[compiler][be] Move e2e tests to BabelPlugin transformer (#32706)
Clean up jest-e2e setup since https://github.com/facebook/react/pull/32663 and other features need program context (e.g. changing imports) --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32706). * #32663 * __->__ #32706
mofeiZ committed
Mar 21, 2025 at 20:05 UTC
da996a15be4f14aeb9726037f4559ff1cb3c2600
6 files changed
+26
-135
compiler/packages/babel-plugin-react-compiler/scripts/jest/e2e-forget.config.js
+1
-4
@@ -7,7 +7,4 @@
7
8
const makeE2EConfig = require('../jest/makeE2EConfig');
9
10
-const config = makeE2EConfig('e2e with forget', true);
11
-config.setupFilesAfterEnv = ['<rootDir>/../scripts/jest/setupEnvE2E.js'];
12
-
13
-module.exports = config;
10
+module.exports = makeE2EConfig('e2e with forget', true);
compiler/packages/babel-plugin-react-compiler/scripts/jest/makeTransform.ts
+19
-115
@@ -5,19 +5,16 @@
5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import {jsx} from '@babel/plugin-syntax-jsx';
8
import babelJest from 'babel-jest';
10
-import {compile} from 'babel-plugin-react-compiler';
11
-import {execSync} from 'child_process';
12
-
13
-import type {NodePath, Visitor} from '@babel/traverse';
14
-import type {CallExpression, FunctionDeclaration} from '@babel/types';
15
-import * as t from '@babel/types';
9
import {
17
- EnvironmentConfig,
10
validateEnvironmentConfig,
11
+ EnvironmentConfig,
12
} from 'babel-plugin-react-compiler';
20
-import {basename} from 'path';
13
+import {execSync} from 'child_process';
14
+
15
+import type {NodePath, Visitor} from '@babel/traverse';
16
+import type {CallExpression} from '@babel/types';
17
+import BabelPluginReactCompiler from 'babel-plugin-react-compiler';
18
19
/**
20
* -- IMPORTANT --
@@ -28,10 +25,19 @@ import {basename} from 'path';
25
const e2eTransformerCacheKey = 1;
26
const forgetOptions: EnvironmentConfig = validateEnvironmentConfig({
27
enableAssumeHooksFollowRulesOfReact: true,
31
- enableFunctionOutlining: false,
28
});
29
const debugMode = process.env['DEBUG_FORGET_COMPILER'] != null;
30
31
+const compilerCacheKey = execSync(
32
+ 'yarn --silent --cwd ../.. hash packages/babel-plugin-react-compiler/dist',
33
+)
34
+ .toString()
35
+ .trim();
36
+
37
+if (debugMode) {
38
+ console.log('cachebreaker', compilerCacheKey);
39
+}
40
+
41
module.exports = (useForget: boolean) => {
42
function createTransformer() {
43
return babelJest.createTransformer({
@@ -42,15 +48,14 @@ module.exports = (useForget: boolean) => {
48
plugins: [
49
useForget
50
? [
45
- ReactForgetFunctionTransform,
51
+ BabelPluginReactCompiler,
52
{
53
+ environment: forgetOptions,
54
/*
55
* Jest hashes the babel config as a cache breaker.
56
* (see https://github.com/jestjs/jest/blob/v29.6.2/packages/babel-jest/src/index.ts#L84)
57
*/
51
- compilerCacheKey: execSync(
52
- 'yarn --silent --cwd ../.. hash packages/babel-plugin-react-compiler/dist',
53
- ).toString(),
58
+ compilerCacheKey,
59
transformOptionsCacheKey: forgetOptions,
60
e2eTransformerCacheKey,
61
},
@@ -105,104 +110,3 @@ module.exports = (useForget: boolean) => {
110
createTransformer,
111
};
112
};
108
-
109
-// Mostly copied from react/scripts/babel/transform-forget.js
110
-function isReactComponentLike(fn: NodePath<FunctionDeclaration>): boolean {
111
- let isReactComponent = false;
112
- let hasNoUseForgetDirective = false;
113
-
114
- /*
115
- * React components start with an upper case letter,
116
- * React hooks start with `use`
117
- */
118
- if (
119
- fn.node.id == null ||
120
- (fn.node.id.name[0].toUpperCase() !== fn.node.id.name[0] &&
121
- !/^use[A-Z0-9]/.test(fn.node.id.name))
122
- ) {
123
- return false;
124
- }
125
-
126
- fn.traverse({
127
- DirectiveLiteral(path) {
128
- if (path.node.value === 'use no forget') {
129
- hasNoUseForgetDirective = true;
130
- }
131
- },
132
-
133
- JSX(path) {
134
- // Is there is a JSX node created in the current function context?
135
- if (path.scope.getFunctionParent()?.path.node === fn.node) {
136
- isReactComponent = true;
137
- }
138
- },
139
-
140
- CallExpression(path) {
141
- // Is there hook usage?
142
- if (
143
- path.node.callee.type === 'Identifier' &&
144
- !/^use[A-Z0-9]/.test(path.node.callee.name)
145
- ) {
146
- isReactComponent = true;
147
- }
148
- },
149
- });
150
-
151
- if (hasNoUseForgetDirective) {
152
- return false;
153
- }
154
-
155
- return isReactComponent;
156
-}
157
-
158
-function ReactForgetFunctionTransform() {
159
- const compiledFns = new Set();
160
- const visitor = {
161
- FunctionDeclaration(fn: NodePath<FunctionDeclaration>, state: any): void {
162
- if (compiledFns.has(fn.node)) {
163
- return;
164
- }
165
-
166
- if (!isReactComponentLike(fn)) {
167
- return;
168
- }
169
- if (debugMode) {
170
- const filename = basename(state.file.opts.filename);
171
- if (fn.node.loc && fn.node.id) {
172
- console.log(
173
- ` Compiling ${filename}:${fn.node.loc.start.line}:${fn.node.loc.start.column} ${fn.node.id.name}`,
174
- );
175
- } else {
176
- console.log(` Compiling ${filename} ${fn.node.id?.name}`);
177
- }
178
- }
179
-
180
- const compiled = compile(
181
- fn,
182
- forgetOptions,
183
- 'Other',
184
- 'all_features',
185
- '_c',
186
- null,
187
- null,
188
- null,
189
- );
190
- compiledFns.add(compiled);
191
-
192
- const fun = t.functionDeclaration(
193
- compiled.id,
194
- compiled.params,
195
- compiled.body,
196
- compiled.generator,
197
- compiled.async,
198
- );
199
- fn.replaceWith(fun);
200
- fn.skip();
201
- },
202
- };
203
- return {
204
- name: 'react-forget-e2e',
205
- inherits: jsx,
206
- visitor,
207
- };
208
-}
compiler/packages/babel-plugin-react-compiler/scripts/jest/setupEnvE2E.js
deleted
-16
@@ -1,16 +0,0 @@
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
-const ReactCompilerRuntime = require('react/compiler-runtime');
9
-
10
-/*
11
- * Our e2e babel transform currently only compiles functions, not programs.
12
- * As a result, our e2e transpiled code does not contain an import for the
13
- * memo cache function. As a temporary hack, we add a `_c` global, which is
14
- * the name that is used for the import by default.
15
- */
16
-globalThis._c = ReactCompilerRuntime.c;
compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/constant-prop.e2e.js
+4
@@ -12,6 +12,7 @@ globalThis.constantValue = 'global test value';
12
13
test('literal-constant-propagation', () => {
14
function Component() {
15
+ 'use memo';
16
const x = 'test value 1';
17
return <div>{x}</div>;
18
}
@@ -38,6 +39,7 @@ test('literal-constant-propagation', () => {
39
40
test('global-constant-propagation', () => {
41
function Component() {
42
+ 'use memo';
43
const x = constantValue;
44
45
return <div>{x}</div>;
@@ -65,6 +67,7 @@ test('global-constant-propagation', () => {
67
68
test('lambda-constant-propagation', () => {
69
function Component() {
70
+ 'use memo';
71
const x = 'test value 1';
72
const getDiv = () => <div>{x}</div>;
73
return getDiv();
@@ -92,6 +95,7 @@ test('lambda-constant-propagation', () => {
95
96
test('lambda-constant-propagation-of-phi-node', () => {
97
function Component({noopCallback}) {
98
+ 'use memo';
99
const x = 'test value 1';
100
if (constantValue) {
101
noopCallback();
compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/update-button.e2e.js
+1
@@ -16,6 +16,7 @@ function Button({label}) {
16
17
let currentTheme = 'light';
18
function useTheme() {
19
+ 'use memo';
20
return currentTheme;
21
}
22
compiler/packages/babel-plugin-react-compiler/src/__tests__/e2e/update-expressions.e2e.js
+1
@@ -10,6 +10,7 @@ import * as React from 'react';
10
import {expectLogsAndClear, log} from './expectLogs';
11
12
function Counter(props) {
13
+ 'use memo';
14
let value = props.value;
15
let a = value++;
16
expect(a).toBe(props.value); // postfix