[be] Extract import logic to own module
Extracting logic around inserting import statements into its own module, with more descriptive function names
Lauren Tan committed
Aug 11, 2023 at 16:38 UTC
d2990f15925edb72aac4826f2f6a788e52b12484
3 files changed
+71
-64
compiler/forget/packages/babel-plugin-react-forget/src/Entrypoint/Imports.ts
new
+68
@@ -0,0 +1,68 @@
1
+import { NodePath } from "@babel/core";
2
+import * as t from "@babel/types";
3
+import { CompilerError } from "../CompilerError";
4
+import { GeneratedSource } from "../HIR";
5
+import { getOrInsertDefault } from "../Utils/utils";
6
+import { ExternalFunction } from "./Options";
7
+
8
+export function addImportsToProgram(
9
+ path: NodePath<t.Program>,
10
+ importList: Array<ExternalFunction>
11
+): void {
12
+ const identifiers: Set<string> = new Set();
13
+ const sortedImports: Map<string, Array<string>> = new Map();
14
+ for (const { importSpecifierName, source } of importList) {
15
+ // Codegen currently does not rename import specifiers, so we do additional
16
+ // validation here
17
+ if (identifiers.has(importSpecifierName)) {
18
+ CompilerError.invalidConfig({
19
+ reason: `Encountered conflicting import specifier for ${importSpecifierName} in Forget config.`,
20
+ description: null,
21
+ loc: GeneratedSource,
22
+ suggestions: null,
23
+ });
24
+ }
25
+ if (path.scope.hasBinding(importSpecifierName)) {
26
+ CompilerError.invalidConfig({
27
+ reason: `Encountered conflicting import specifiers for ${importSpecifierName} in generated program.`,
28
+ description: null,
29
+ loc: GeneratedSource,
30
+ suggestions: null,
31
+ });
32
+ }
33
+ identifiers.add(importSpecifierName);
34
+
35
+ const importSpecifierNameList = getOrInsertDefault(
36
+ sortedImports,
37
+ source,
38
+ []
39
+ );
40
+ importSpecifierNameList.push(importSpecifierName);
41
+ }
42
+
43
+ const stmts: Array<t.ImportDeclaration> = [];
44
+ for (const [source, importSpecifierNameList] of sortedImports) {
45
+ const importSpecifiers = importSpecifierNameList.map((name) => {
46
+ const id = t.identifier(name);
47
+ return t.importSpecifier(id, id);
48
+ });
49
+
50
+ stmts.push(t.importDeclaration(importSpecifiers, t.stringLiteral(source)));
51
+ }
52
+ path.unshiftContainer("body", stmts);
53
+}
54
+
55
+/**
56
+ * Matches `import { ... } from 'react';`
57
+ * but not `import * as React from 'react';`
58
+ */
59
+export function isNonNamespacedImportOfReact(
60
+ importDeclPath: NodePath<t.ImportDeclaration>
61
+): boolean {
62
+ return (
63
+ importDeclPath.get("source").node.value === "react" &&
64
+ importDeclPath
65
+ .get("specifiers")
66
+ .every((specifier) => specifier.isImportSpecifier())
67
+ );
68
+}
compiler/forget/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts
+2
-64
@@ -15,10 +15,10 @@ import {
15
} from "../CompilerError";
16
import { GeneratedSource } from "../HIR";
17
import { isComponentDeclaration } from "../Utils/ComponentDeclaration";
18
-import { getOrInsertDefault } from "../Utils/utils";
18
import { insertGatedFunctionDeclaration } from "./Gating";
19
+import { addImportsToProgram, isNonNamespacedImportOfReact } from "./Imports";
20
import { addInstrumentForget } from "./Instrumentation";
21
-import { ExternalFunction, PluginOptions, parsePluginOptions } from "./Options";
21
+import { PluginOptions, parsePluginOptions } from "./Options";
22
import { compileFn } from "./Pipeline";
23
24
export type CompilerPass = {
@@ -428,65 +428,3 @@ function buildBlockStatement(
428
429
return body.node;
430
}
431
-
432
-function addImportsToProgram(
433
- path: NodePath<t.Program>,
434
- importList: Array<ExternalFunction>
435
-): void {
436
- const identifiers: Set<string> = new Set();
437
- const sortedImports: Map<string, Array<string>> = new Map();
438
- for (const { importSpecifierName, source } of importList) {
439
- // Codegen currently does not rename import specifiers, so we do additional
440
- // validation here
441
- if (identifiers.has(importSpecifierName)) {
442
- CompilerError.invalidConfig({
443
- reason: `Encountered conflicting import specifier for ${importSpecifierName} in Forget config.`,
444
- description: null,
445
- loc: GeneratedSource,
446
- suggestions: null,
447
- });
448
- }
449
- if (path.scope.hasBinding(importSpecifierName)) {
450
- CompilerError.invalidConfig({
451
- reason: `Encountered conflicting import specifiers for ${importSpecifierName} in generated program.`,
452
- description: null,
453
- loc: GeneratedSource,
454
- suggestions: null,
455
- });
456
- }
457
- identifiers.add(importSpecifierName);
458
-
459
- const importSpecifierNameList = getOrInsertDefault(
460
- sortedImports,
461
- source,
462
- []
463
- );
464
- importSpecifierNameList.push(importSpecifierName);
465
- }
466
-
467
- const stmts: Array<t.ImportDeclaration> = [];
468
- for (const [source, importSpecifierNameList] of sortedImports) {
469
- const importSpecifiers = importSpecifierNameList.map((name) => {
470
- const id = t.identifier(name);
471
- return t.importSpecifier(id, id);
472
- });
473
-
474
- stmts.push(t.importDeclaration(importSpecifiers, t.stringLiteral(source)));
475
- }
476
- path.unshiftContainer("body", stmts);
477
-}
478
-
479
-/**
480
- * Matches `import { ... } from 'react';`
481
- * but not `import * as React from 'react';`
482
- */
483
-function isNonNamespacedImportOfReact(
484
- importDeclPath: NodePath<t.ImportDeclaration>
485
-): boolean {
486
- return (
487
- importDeclPath.get("source").node.value === "react" &&
488
- importDeclPath
489
- .get("specifiers")
490
- .every((specifier) => specifier.isImportSpecifier())
491
- );
492
-}
compiler/forget/packages/babel-plugin-react-forget/src/Entrypoint/index.ts
+1
@@ -1,4 +1,5 @@
1
export * from "./Gating";
2
+export * from "./Imports";
3
export * from "./Instrumentation";
4
export * from "./Options";
5
export * from "./Pipeline";