@samitouri / QOS-React / commits / 2d2cc042d7

[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,