[compiler][ez] Option to bail out on blocklisted imports
ghstack-source-id: 540d154b25e49a83683a05fc9326dbd0ad59a6bd Pull Request resolved: https://github.com/facebook/react/pull/30643
Mofei Zhang committed
Aug 8, 2024 at 15:47 UTC
2d2cc042d7812499baf992804fbf83c20caa7436
6 files changed
+83
-2
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Imports.ts
+25
-1
@@ -8,9 +8,33 @@
8
import {NodePath} from '@babel/core';
9
import * as t from '@babel/types';
10
import {CompilerError} from '../CompilerError';
11
-import {ExternalFunction, GeneratedSource} from '../HIR';
11
+import {EnvironmentConfig, ExternalFunction, GeneratedSource} from '../HIR';
12
import {getOrInsertDefault} from '../Utils/utils';
13
14
+export function validateRestrictedImports(
15
+ path: NodePath<t.Program>,
16
+ {validateBlocklistedImports}: EnvironmentConfig,
17
+): void {
18
+ if (
19
+ validateBlocklistedImports == null ||
20
+ validateBlocklistedImports.length === 0
21
+ ) {
22
+ return;
23
+ }
24
+ const restrictedImports = new Set(validateBlocklistedImports);
25
+ path.traverse({
26
+ ImportDeclaration(importDeclPath) {
27
+ if (restrictedImports.has(importDeclPath.node.source.value)) {
28
+ CompilerError.throwTodo({
29
+ reason: 'Bailing out due to blocklisted import',
30
+ description: `Import from module ${importDeclPath.node.source.value}`,
31
+ loc: importDeclPath.node.loc ?? null,
32
+ });
33
+ }
34
+ },
35
+ });
36
+}
37
+
38
export function addImportsToProgram(
39
path: NodePath<t.Program>,
40
importList: Array<ExternalFunction>,
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts
+6
-1
@@ -24,7 +24,11 @@ import {isComponentDeclaration} from '../Utils/ComponentDeclaration';
24
import {isHookDeclaration} from '../Utils/HookDeclaration';
25
import {assertExhaustive} from '../Utils/utils';
26
import {insertGatedFunctionDeclaration} from './Gating';
27
-import {addImportsToProgram, updateMemoCacheFunctionImport} from './Imports';
27
+import {
28
+ addImportsToProgram,
29
+ updateMemoCacheFunctionImport,
30
+ validateRestrictedImports,
31
+} from './Imports';
32
import {PluginOptions} from './Options';
33
import {compileFn} from './Pipeline';
34
import {
@@ -296,6 +300,7 @@ export function compileProgram(
300
});
301
}
302
const environment = environmentResult.unwrap();
303
+ validateRestrictedImports(program, environment);
304
const useMemoCacheIdentifier = program.scope.generateUidIdentifier('c');
305
const moduleName = pass.opts.runtimeModule ?? 'react/compiler-runtime';
306
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+1
@@ -238,6 +238,7 @@ const EnvironmentConfigSchema = z.object({
238
* this option to the empty array.
239
*/
240
validateNoCapitalizedCalls: z.nullable(z.array(z.string())).default(null),
241
+ validateBlocklistedImports: z.nullable(z.array(z.string())).default(null),
242
243
/*
244
* When enabled, the compiler assumes that hooks follow the Rules of React:
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-blocklisted-imports.expect.md
new
+28
@@ -0,0 +1,28 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @validateBlocklistedImports(DangerousImport)
6
+import {foo} from 'DangerousImport';
7
+import {useIdentity} from 'shared-runtime';
8
+
9
+function useHook() {
10
+ useIdentity(foo);
11
+ return;
12
+}
13
+
14
+```
15
+
16
+
17
+## Error
18
+
19
+```
20
+ 1 | // @validateBlocklistedImports(DangerousImport)
21
+> 2 | import {foo} from 'DangerousImport';
22
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Todo: Bailing out due to blocklisted import. Import from module DangerousImport (2:2)
23
+ 3 | import {useIdentity} from 'shared-runtime';
24
+ 4 |
25
+ 5 | function useHook() {
26
+```
27
+
28
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.validate-blocklisted-imports.ts
new
+8
@@ -0,0 +1,8 @@
1
+// @validateBlocklistedImports(DangerousImport)
2
+import {foo} from 'DangerousImport';
3
+import {useIdentity} from 'shared-runtime';
4
+
5
+function useHook() {
6
+ useIdentity(foo);
7
+ return;
8
+}
compiler/packages/snap/src/compiler.ts
+15
@@ -47,6 +47,7 @@ function makePluginOptions(
47
let validatePreserveExistingMemoizationGuarantees = false;
48
let enableChangeDetectionForDebugging = null;
49
let customMacros = null;
50
+ let validateBlocklistedImports = null;
51
52
if (firstLine.indexOf('@compilationMode(annotation)') !== -1) {
53
assert(
@@ -155,6 +156,19 @@ function makePluginOptions(
156
.filter(s => s.length > 0);
157
}
158
159
+ const validateBlocklistedImportsMatch =
160
+ /@validateBlocklistedImports\(([^)]+)\)/.exec(firstLine);
161
+ if (
162
+ validateBlocklistedImportsMatch &&
163
+ validateBlocklistedImportsMatch.length > 1 &&
164
+ validateBlocklistedImportsMatch[1].trim().length > 0
165
+ ) {
166
+ validateBlocklistedImports = validateBlocklistedImportsMatch[1]
167
+ .split(' ')
168
+ .map(s => s.trim())
169
+ .filter(s => s.length > 0);
170
+ }
171
+
172
let lowerContextAccess = null;
173
if (firstLine.includes('@lowerContextAccess')) {
174
lowerContextAccess = {
@@ -216,6 +230,7 @@ function makePluginOptions(
230
validatePreserveExistingMemoizationGuarantees,
231
enableChangeDetectionForDebugging,
232
lowerContextAccess,
233
+ validateBlocklistedImports,
234
},
235
compilationMode,
236
logger,