1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+import {RuleTester} from 'eslint';
9
+import {allRules} from '../src/shared/ReactCompiler';
10
+
11
+const ESLintTesterV8 = require('eslint-v8').RuleTester;
12
+
13
+/**
14
+ * A string template tag that removes padding from the left side of multi-line strings
15
+ * @param {Array} strings array of code strings (only one expected)
16
+ */
17
+function normalizeIndent(strings: TemplateStringsArray): string {
18
+ const codeLines = strings[0]?.split('\n') ?? [];
19
+ const leftPadding = codeLines[1]?.match(/\s+/)![0] ?? '';
20
+ return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
21
+}
22
+
23
+type CompilerTestCases = {
24
+ valid: RuleTester.ValidTestCase[];
25
+ invalid: RuleTester.InvalidTestCase[];
26
+};
27
+
28
+const tests: CompilerTestCases = {
29
+ valid: [
30
+ // ===========================================
31
+ // Tests for mayContainReactCode heuristic with Flow syntax
32
+ // Files that should be SKIPPED (no React-like function names)
33
+ // These contain code that WOULD trigger errors if compiled,
34
+ // but since the heuristic skips them, no errors are reported.
35
+ // ===========================================
36
+ {
37
+ name: '[Heuristic/Flow] Skips files with only lowercase utility functions',
38
+ filename: 'utils.js',
39
+ code: normalizeIndent`
40
+ function helper(obj) {
41
+ obj.key = 'value';
42
+ return obj;
43
+ }
44
+ `,
45
+ },
46
+ {
47
+ name: '[Heuristic/Flow] Skips lowercase arrow functions even with mutations',
48
+ filename: 'helpers.js',
49
+ code: normalizeIndent`
50
+ const processData = (input) => {
51
+ input.modified = true;
52
+ return input;
53
+ };
54
+ `,
55
+ },
56
+ ],
57
+ invalid: [
58
+ // ===========================================
59
+ // Tests for mayContainReactCode heuristic with Flow component/hook syntax
60
+ // These use Flow's component/hook declarations which should be detected
61
+ // ===========================================
62
+ {
63
+ name: '[Heuristic/Flow] Compiles Flow component declaration - detects prop mutation',
64
+ filename: 'component.js',
65
+ code: normalizeIndent`
66
+ component MyComponent(a: {key: string}) {
67
+ a.key = 'value';
68
+ return <div />;
69
+ }
70
+ `,
71
+ errors: [
72
+ {
73
+ message: /Modifying component props/,
74
+ },
75
+ ],
76
+ },
77
+ {
78
+ name: '[Heuristic/Flow] Compiles exported Flow component declaration - detects prop mutation',
79
+ filename: 'component.js',
80
+ code: normalizeIndent`
81
+ export component MyComponent(a: {key: string}) {
82
+ a.key = 'value';
83
+ return <div />;
84
+ }
85
+ `,
86
+ errors: [
87
+ {
88
+ message: /Modifying component props/,
89
+ },
90
+ ],
91
+ },
92
+ {
93
+ name: '[Heuristic/Flow] Compiles default exported Flow component declaration - detects prop mutation',
94
+ filename: 'component.js',
95
+ code: normalizeIndent`
96
+ export default component MyComponent(a: {key: string}) {
97
+ a.key = 'value';
98
+ return <div />;
99
+ }
100
+ `,
101
+ errors: [
102
+ {
103
+ message: /Modifying component props/,
104
+ },
105
+ ],
106
+ },
107
+ {
108
+ name: '[Heuristic/Flow] Compiles Flow hook declaration - detects argument mutation',
109
+ filename: 'hooks.js',
110
+ code: normalizeIndent`
111
+ hook useMyHook(a: {key: string}) {
112
+ a.key = 'value';
113
+ return a;
114
+ }
115
+ `,
116
+ errors: [
117
+ {
118
+ message: /Modifying component props or hook arguments/,
119
+ },
120
+ ],
121
+ },
122
+ {
123
+ name: '[Heuristic/Flow] Compiles exported Flow hook declaration - detects argument mutation',
124
+ filename: 'hooks.js',
125
+ code: normalizeIndent`
126
+ export hook useMyHook(a: {key: string}) {
127
+ a.key = 'value';
128
+ return a;
129
+ }
130
+ `,
131
+ errors: [
132
+ {
133
+ message: /Modifying component props or hook arguments/,
134
+ },
135
+ ],
136
+ },
137
+ ],
138
+};
139
+
140
+const eslintTester = new ESLintTesterV8({
141
+ parser: require.resolve('hermes-eslint'),
142
+ parserOptions: {
143
+ sourceType: 'module',
144
+ enableExperimentalComponentSyntax: true,
145
+ },
146
+});
147
+eslintTester.run('react-compiler', allRules['immutability'].rule, tests);