@samitouri / QOS-React-2 / commits / d2990f1592

[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";