diff --git a/packages/babel-helper-module-imports/src/import-builder.ts b/packages/babel-helper-module-imports/src/import-builder.ts index 55c7d09c8cac..da78c1fcf3ca 100644 --- a/packages/babel-helper-module-imports/src/import-builder.ts +++ b/packages/babel-helper-module-imports/src/import-builder.ts @@ -13,20 +13,23 @@ import { variableDeclaration, variableDeclarator, } from "@babel/types"; +import type * as t from "@babel/types"; +import type { Scope } from "@babel/traverse"; +import type { File } from "@babel/core"; /** * A class to track and accumulate mutations to the AST that will eventually * output a new require/import statement list. */ export default class ImportBuilder { - _statements = []; - _resultName = null; + private _statements: t.Statement[] = []; + private _resultName: t.Identifier | t.MemberExpression = null; - _scope = null; - _hub = null; - private _importedSource: any; + declare _scope: Scope; + declare _hub: File["hub"]; + private _importedSource: string; - constructor(importedSource, scope, hub) { + constructor(importedSource: string, scope: Scope, hub: File["hub"]) { this._scope = scope; this._hub = hub; this._importedSource = importedSource; @@ -67,29 +70,29 @@ export default class ImportBuilder { this._resultName = cloneNode(local); return this; } - default(name) { - name = this._scope.generateUidIdentifier(name); + default(name: string) { + const id = this._scope.generateUidIdentifier(name); const statement = this._statements[this._statements.length - 1]; assert(statement.type === "ImportDeclaration"); assert(statement.specifiers.length === 0); - statement.specifiers = [importDefaultSpecifier(name)]; - this._resultName = cloneNode(name); + statement.specifiers = [importDefaultSpecifier(id)]; + this._resultName = cloneNode(id); return this; } - named(name, importName) { + named(name: string, importName: string) { if (importName === "default") return this.default(name); - name = this._scope.generateUidIdentifier(name); + const id = this._scope.generateUidIdentifier(name); const statement = this._statements[this._statements.length - 1]; assert(statement.type === "ImportDeclaration"); assert(statement.specifiers.length === 0); - statement.specifiers = [importSpecifier(name, identifier(importName))]; - this._resultName = cloneNode(name); + statement.specifiers = [importSpecifier(id, identifier(importName))]; + this._resultName = cloneNode(id); return this; } - var(name) { - name = this._scope.generateUidIdentifier(name); + var(name: string) { + const id = this._scope.generateUidIdentifier(name); let statement = this._statements[this._statements.length - 1]; if (statement.type !== "ExpressionStatement") { assert(this._resultName); @@ -97,9 +100,9 @@ export default class ImportBuilder { this._statements.push(statement); } this._statements[this._statements.length - 1] = variableDeclaration("var", [ - variableDeclarator(name, statement.expression), + variableDeclarator(id, statement.expression), ]); - this._resultName = cloneNode(name); + this._resultName = cloneNode(id); return this; } @@ -110,7 +113,7 @@ export default class ImportBuilder { return this._interop(this._hub.addHelper("interopRequireWildcard")); } - _interop(callee) { + _interop(callee: t.Expression) { const statement = this._statements[this._statements.length - 1]; if (statement.type === "ExpressionStatement") { statement.expression = callExpression(callee, [statement.expression]); @@ -125,7 +128,7 @@ export default class ImportBuilder { return this; } - prop(name) { + prop(name: string) { const statement = this._statements[this._statements.length - 1]; if (statement.type === "ExpressionStatement") { statement.expression = memberExpression( @@ -144,7 +147,7 @@ export default class ImportBuilder { return this; } - read(name) { + read(name: string) { this._resultName = memberExpression(this._resultName, identifier(name)); } } diff --git a/packages/babel-helper-module-imports/src/import-injector.ts b/packages/babel-helper-module-imports/src/import-injector.ts index 2b1d78709795..7e439c44accd 100644 --- a/packages/babel-helper-module-imports/src/import-injector.ts +++ b/packages/babel-helper-module-imports/src/import-injector.ts @@ -1,7 +1,8 @@ import assert from "assert"; import { numericLiteral, sequenceExpression } from "@babel/types"; import type * as t from "@babel/types"; -import type { NodePath, Scope, HubInterface } from "@babel/traverse"; +import type { NodePath, Scope } from "@babel/traverse"; +import type { File } from "@babel/core"; import ImportBuilder from "./import-builder"; import isModule from "./is-module"; @@ -93,8 +94,8 @@ export type ImportOptions = { */ importPosition: "before" | "after"; - nameHint?; - blockHoist?; + nameHint?: string; + blockHoist?: number; }; /** @@ -114,7 +115,7 @@ export default class ImportInjector { /** * The file used to inject helpers and resolve paths. */ - declare _hub: HubInterface; + declare _hub: File["hub"]; /** * The default options to use with this instance when imports are added. @@ -129,21 +130,29 @@ export default class ImportInjector { importPosition: "before", }; - constructor(path: NodePath, importedSource?, opts?) { + constructor( + path: NodePath, + importedSource?: string, + opts?: Partial, + ) { const programPath = path.find(p => p.isProgram()) as NodePath; this._programPath = programPath; this._programScope = programPath.scope; - this._hub = programPath.hub; + this._hub = programPath.hub as File["hub"]; this._defaultOpts = this._applyDefaults(importedSource, opts, true); } - addDefault(importedSourceIn, opts) { + addDefault(importedSourceIn: string, opts: Partial) { return this.addNamed("default", importedSourceIn, opts); } - addNamed(importName, importedSourceIn, opts) { + addNamed( + importName: string, + importedSourceIn: string, + opts: Partial, + ) { assert(typeof importName === "string"); return this._generateImport( @@ -152,49 +161,44 @@ export default class ImportInjector { ); } - addNamespace(importedSourceIn, opts) { + addNamespace(importedSourceIn: string, opts: Partial) { return this._generateImport( this._applyDefaults(importedSourceIn, opts), null, ); } - addSideEffect(importedSourceIn, opts) { + addSideEffect(importedSourceIn: string, opts: Partial) { return this._generateImport( this._applyDefaults(importedSourceIn, opts), - false, + void 0, ); } - _applyDefaults(importedSource, opts, isInit = false) { - const optsList = []; + _applyDefaults( + importedSource: string | Partial, + opts: Partial | undefined, + isInit = false, + ) { + let newOpts: ImportOptions; if (typeof importedSource === "string") { - optsList.push({ importedSource }); - optsList.push(opts); + newOpts = { ...this._defaultOpts, importedSource, ...opts }; } else { assert(!opts, "Unexpected secondary arguments."); - - optsList.push(importedSource); + newOpts = { ...this._defaultOpts, ...importedSource }; } - const newOpts: ImportOptions = { - ...this._defaultOpts, - }; - for (const opts of optsList) { - if (!opts) continue; - Object.keys(newOpts).forEach(key => { - if (opts[key] !== undefined) newOpts[key] = opts[key]; - }); - - if (!isInit) { - if (opts.nameHint !== undefined) newOpts.nameHint = opts.nameHint; - if (opts.blockHoist !== undefined) newOpts.blockHoist = opts.blockHoist; - } + if (!isInit && opts) { + if (opts.nameHint !== undefined) newOpts.nameHint = opts.nameHint; + if (opts.blockHoist !== undefined) newOpts.blockHoist = opts.blockHoist; } return newOpts; } - _generateImport(opts, importName) { + _generateImport( + opts: Partial, + importName: string | null | undefined, + ) { const isDefault = importName === "default"; const isNamed = !!importName && !isDefault; const isNamespace = importName === null; @@ -422,7 +426,11 @@ export default class ImportInjector { return resultName; } - _insertStatements(statements, importPosition = "before", blockHoist = 3) { + _insertStatements( + statements: t.Statement[], + importPosition = "before", + blockHoist = 3, + ) { const body = this._programPath.get("body"); if (importPosition === "after") { @@ -434,6 +442,7 @@ export default class ImportInjector { } } else { statements.forEach(node => { + // @ts-expect-error handle _blockHoist node._blockHoist = blockHoist; }); diff --git a/packages/babel-helper-module-imports/src/index.ts b/packages/babel-helper-module-imports/src/index.ts index 44b98d5c2054..183aaabd6fe0 100644 --- a/packages/babel-helper-module-imports/src/index.ts +++ b/packages/babel-helper-module-imports/src/index.ts @@ -1,21 +1,77 @@ -import ImportInjector from "./import-injector"; +import ImportInjector, { type ImportOptions } from "./import-injector"; +import type { NodePath } from "@babel/traverse"; +import type * as t from "@babel/types"; export { ImportInjector }; export { default as isModule } from "./is-module"; -export function addDefault(path, importedSource, opts?) { +export function addDefault( + path: NodePath, + importedSource: string, + opts?: Partial, +) { return new ImportInjector(path).addDefault(importedSource, opts); } -export function addNamed(path, name, importedSource, opts?) { +function addNamed( + path: NodePath, + name: string, + importedSource: string, + opts?: Omit< + Partial, + "ensureLiveReference" | "ensureNoContext" + >, +): t.Identifier; +function addNamed( + path: NodePath, + name: string, + importedSource: string, + opts?: Omit, "ensureLiveReference"> & { + ensureLiveReference: true; + }, +): t.MemberExpression; +function addNamed( + path: NodePath, + name: string, + importedSource: string, + opts?: Omit, "ensureNoContext"> & { + ensureNoContext: true; + }, +): t.SequenceExpression; +/** + * add a named import to the program path of given path + * + * @export + * @param {NodePath} path The starting path to find a program path + * @param {string} name The name of the generated binding. Babel will prefix it with `_` + * @param {string} importedSource The source of the import + * @param {Partial} [opts] + * @returns {t.Identifier | t.MemberExpression | t.SequenceExpression} If opts.ensureNoContext is true, returns a SequenceExpression, + * else if opts.ensureLiveReference is true, returns a MemberExpression, else returns an Identifier + */ +function addNamed( + path: NodePath, + name: string, + importedSource: string, + opts?: Partial, +) { return new ImportInjector(path).addNamed(name, importedSource, opts); } +export { addNamed }; -export function addNamespace(path, importedSource, opts?) { +export function addNamespace( + path: NodePath, + importedSource: string, + opts?: Partial, +) { return new ImportInjector(path).addNamespace(importedSource, opts); } -export function addSideEffect(path, importedSource, opts?) { +export function addSideEffect( + path: NodePath, + importedSource: string, + opts?: Partial, +) { return new ImportInjector(path).addSideEffect(importedSource, opts); } diff --git a/scripts/generators/tsconfig.js b/scripts/generators/tsconfig.js index c56c455db0a5..3c186aee1b00 100644 --- a/scripts/generators/tsconfig.js +++ b/scripts/generators/tsconfig.js @@ -116,6 +116,7 @@ fs.writeFileSync( "babel-plugin-dynamic-import-node/utils", ["./lib/babel-plugin-dynamic-import-node.d.ts"], ], + ["globals", ["./node_modules/globals-BABEL_8_BREAKING-true"]], ]), }, }, diff --git a/tsconfig.json b/tsconfig.json index 17553efa7cab..86f6aec802c9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -698,6 +698,9 @@ ], "babel-plugin-dynamic-import-node/utils": [ "./lib/babel-plugin-dynamic-import-node.d.ts" + ], + "globals": [ + "./node_modules/globals-BABEL_8_BREAKING-true" ] } }