convert @babel/helper-module-transforms to typescript (#12928)

Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
This commit is contained in:
Bogdan Savluk 2021-03-27 01:40:32 +01:00 committed by GitHub
parent e5e37b94a5
commit 6ac07a1647
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 124 additions and 72 deletions

View File

@ -208,3 +208,7 @@ declare module "@babel/highlight" {
declare module "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" { declare module "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" {
declare module.exports: any; declare module.exports: any;
} }
declare module "@babel/helper-module-transforms" {
declare module.exports: any;
}

View File

@ -1,9 +1,7 @@
// @flow
export default function getModuleName( export default function getModuleName(
rootOpts: Object, rootOpts: any,
pluginOpts: Object, pluginOpts: any,
): ?string { ): string | undefined | null {
const { const {
filename, filename,
filenameRelative = filename, filenameRelative = filename,

View File

@ -9,9 +9,13 @@ import rewriteLiveReferences from "./rewrite-live-references";
import normalizeAndLoadModuleMetadata, { import normalizeAndLoadModuleMetadata, {
hasExports, hasExports,
isSideEffectImport, isSideEffectImport,
type ModuleMetadata,
type SourceModuleMetadata,
} from "./normalize-and-load-metadata"; } from "./normalize-and-load-metadata";
import type {
InteropType,
ModuleMetadata,
SourceModuleMetadata,
} from "./normalize-and-load-metadata";
import type { NodePath } from "@babel/traverse";
export { default as getModuleName } from "./get-module-name"; export { default as getModuleName } from "./get-module-name";
@ -24,7 +28,7 @@ export { hasExports, isSideEffectImport, isModule, rewriteThis };
* and returns a list of statements for use when initializing the module. * and returns a list of statements for use when initializing the module.
*/ */
export function rewriteModuleStatementsAndPrepareHeader( export function rewriteModuleStatementsAndPrepareHeader(
path: NodePath, path: NodePath<t.Program>,
{ {
// TODO(Babel 8): Remove this // TODO(Babel 8): Remove this
loose, loose,
@ -39,6 +43,17 @@ export function rewriteModuleStatementsAndPrepareHeader(
constantReexports = loose, constantReexports = loose,
enumerableModuleMeta = loose, enumerableModuleMeta = loose,
}: {
exportName?;
strict;
allowTopLevelThis?;
strictMode;
loose?;
noInterop?;
lazy?;
esNamespaceOnly?;
constantReexports?;
enumerableModuleMeta?;
}, },
) { ) {
assert(isModule(path), "Cannot process module statements in a script"); assert(isModule(path), "Cannot process module statements in a script");
@ -106,9 +121,9 @@ export function ensureStatementsHoisted(statements) {
*/ */
export function wrapInterop( export function wrapInterop(
programPath: NodePath, programPath: NodePath,
expr: Node, expr: t.Expression,
type: InteropType, type: InteropType,
): Node { ): t.CallExpression {
if (type === "none") { if (type === "none") {
return null; return null;
} }
@ -138,7 +153,7 @@ export function buildNamespaceInitStatements(
) { ) {
const statements = []; const statements = [];
let srcNamespace = t.identifier(sourceMetadata.name); let srcNamespace: t.Node = t.identifier(sourceMetadata.name);
if (sourceMetadata.lazy) srcNamespace = t.callExpression(srcNamespace, []); if (sourceMetadata.lazy) srcNamespace = t.callExpression(srcNamespace, []);
for (const localName of sourceMetadata.importsNamespace) { for (const localName of sourceMetadata.importsNamespace) {
@ -329,7 +344,7 @@ function buildExportNameListDeclaration(
exportedVars[exportName] = true; exportedVars[exportName] = true;
} }
hasReexport = hasReexport || data.reexportAll; hasReexport = hasReexport || !!data.reexportAll;
} }
if (!hasReexport || Object.keys(exportedVars).length === 0) return null; if (!hasReexport || Object.keys(exportedVars).length === 0) return null;

View File

@ -1,61 +1,52 @@
import { basename, extname } from "path"; import { basename, extname } from "path";
import type * as t from "@babel/types";
import { isIdentifierName } from "@babel/helper-validator-identifier"; import { isIdentifierName } from "@babel/helper-validator-identifier";
import splitExportDeclaration from "@babel/helper-split-export-declaration"; import splitExportDeclaration from "@babel/helper-split-export-declaration";
import type { NodePath } from "@babel/traverse";
export type ModuleMetadata = { export interface ModuleMetadata {
exportName: string, exportName: string;
// The name of the variable that will reference an object containing export names. // The name of the variable that will reference an object containing export names.
exportNameListName: null | string, exportNameListName: null | string;
hasExports: boolean;
hasExports: boolean,
// Lookup from local binding to export information. // Lookup from local binding to export information.
local: Map<string, LocalExportMetadata>, local: Map<string, LocalExportMetadata>;
// Lookup of source file to source file metadata. // Lookup of source file to source file metadata.
source: Map<string, SourceModuleMetadata>, source: Map<string, SourceModuleMetadata>;
// List of names that should only be printed as string literals. // List of names that should only be printed as string literals.
// i.e. `import { "any unicode" as foo } from "some-module"` // i.e. `import { "any unicode" as foo } from "some-module"`
// `stringSpecifiers` is Set(1) ["any unicode"] // `stringSpecifiers` is Set(1) ["any unicode"]
// In most cases `stringSpecifiers` is an empty Set // In most cases `stringSpecifiers` is an empty Set
stringSpecifiers: Set<string>, stringSpecifiers: Set<string>;
}; }
export type InteropType = "default" | "namespace" | "none"; export type InteropType = "default" | "namespace" | "none";
export type SourceModuleMetadata = { export interface SourceModuleMetadata {
// A unique variable name to use for this namespace object. Centralized for simplicity. // A unique variable name to use for this namespace object. Centralized for simplicity.
name: string, name: string;
loc: t.SourceLocation | undefined | null;
loc: ?BabelNodeSourceLocation, interop: InteropType;
interop: InteropType,
// Local binding to reference from this source namespace. Key: Local name, value: Import name // Local binding to reference from this source namespace. Key: Local name, value: Import name
imports: Map<string, string>, imports: Map<string, string>;
// Local names that reference namespace object. // Local names that reference namespace object.
importsNamespace: Set<string>, importsNamespace: Set<string>;
// Reexports to create for namespace. Key: Export name, value: Import name // Reexports to create for namespace. Key: Export name, value: Import name
reexports: Map<string, string>, reexports: Map<string, string>;
// List of names to re-export namespace as. // List of names to re-export namespace as.
reexportNamespace: Set<string>, reexportNamespace: Set<string>;
// Tracks if the source should be re-exported. // Tracks if the source should be re-exported.
reexportAll: null | { reexportAll: null | {
loc: ?BabelNodeSourceLocation, loc: t.SourceLocation | undefined | null;
}, };
}; lazy?;
}
export type LocalExportMetadata = { export interface LocalExportMetadata {
name: Array<string>, // names of exports names: Array<string>; // names of exports,
kind: "import" | "hoisted" | "block" | "var", kind: "import" | "hoisted" | "block" | "var";
}; }
/** /**
* Check if the module has any exports that need handling. * Check if the module has any exports that need handling.
@ -82,7 +73,7 @@ export function isSideEffectImport(source: SourceModuleMetadata) {
* needed to reconstruct the module's behavior. * needed to reconstruct the module's behavior.
*/ */
export default function normalizeModuleAndLoadMetadata( export default function normalizeModuleAndLoadMetadata(
programPath: NodePath, programPath: NodePath<t.Program>,
exportName?: string, exportName?: string,
{ {
noInterop = false, noInterop = false,
@ -94,7 +85,7 @@ export default function normalizeModuleAndLoadMetadata(
if (!exportName) { if (!exportName) {
exportName = programPath.scope.generateUidIdentifier("exports").name; exportName = programPath.scope.generateUidIdentifier("exports").name;
} }
const stringSpecifiers = new Set(); const stringSpecifiers = new Set<string>();
nameAnonymousExports(programPath); nameAnonymousExports(programPath);
@ -166,11 +157,15 @@ function getExportSpecifierName(
* Get metadata about the imports and exports present in this module. * Get metadata about the imports and exports present in this module.
*/ */
function getModuleMetadata( function getModuleMetadata(
programPath: NodePath, programPath: NodePath<t.Program>,
{ {
lazy, lazy,
initializeReexports, initializeReexports,
}: { lazy: boolean, initializeReexports: boolean }, }: {
// todo(flow-ts) changed from boolean, to match expected usage inside the function
lazy: boolean | string[] | Function;
initializeReexports: boolean;
},
stringSpecifiers: Set<string>, stringSpecifiers: Set<string>,
) { ) {
const localData = getLocalExportMetadata( const localData = getLocalExportMetadata(
@ -289,7 +284,9 @@ function getModuleMetadata(
data.reexports.set(exportName, importName); data.reexports.set(exportName, importName);
if (exportName === "__esModule") { if (exportName === "__esModule") {
throw exportName.buildCodeFrameError('Illegal export "__esModule".'); throw spec
.get("exported")
.buildCodeFrameError('Illegal export "__esModule".');
} }
}); });
} else if ( } else if (
@ -360,13 +357,13 @@ function getModuleMetadata(
* Get metadata about local variables that are exported. * Get metadata about local variables that are exported.
*/ */
function getLocalExportMetadata( function getLocalExportMetadata(
programPath: NodePath, programPath: NodePath<t.Program>,
initializeReexports: boolean, initializeReexports: boolean,
stringSpecifiers: Set<string>, stringSpecifiers: Set<string>,
): Map<string, LocalExportMetadata> { ): Map<string, LocalExportMetadata> {
const bindingKindLookup = new Map(); const bindingKindLookup = new Map();
programPath.get("body").forEach(child => { programPath.get("body").forEach((child: any) => {
let kind; let kind;
if (child.isImportDeclaration()) { if (child.isImportDeclaration()) {
kind = "import"; kind = "import";
@ -434,7 +431,8 @@ function getLocalExportMetadata(
(initializeReexports || !child.node.source) (initializeReexports || !child.node.source)
) { ) {
if (child.node.declaration) { if (child.node.declaration) {
const declaration = child.get("declaration"); // todo: flow->ts babel-types node field types
const declaration = child.get("declaration") as NodePath;
const ids = declaration.getOuterBindingIdentifierPaths(); const ids = declaration.getOuterBindingIdentifierPaths();
Object.keys(ids).forEach(name => { Object.keys(ids).forEach(name => {
if (name === "__esModule") { if (name === "__esModule") {
@ -463,6 +461,7 @@ function getLocalExportMetadata(
declaration.isFunctionDeclaration() || declaration.isFunctionDeclaration() ||
declaration.isClassDeclaration() declaration.isClassDeclaration()
) { ) {
// @ts-expect-error todo(flow->ts): improve babel-types
getLocalMetadata(declaration.get("id")).names.push("default"); getLocalMetadata(declaration.get("id")).names.push("default");
} else { } else {
// These should have been removed by the nameAnonymousExports() call. // These should have been removed by the nameAnonymousExports() call.
@ -478,7 +477,7 @@ function getLocalExportMetadata(
/** /**
* Ensure that all exported values have local binding names. * Ensure that all exported values have local binding names.
*/ */
function nameAnonymousExports(programPath: NodePath) { function nameAnonymousExports(programPath: NodePath<t.Program>) {
// Name anonymous exported locals. // Name anonymous exported locals.
programPath.get("body").forEach(child => { programPath.get("body").forEach(child => {
if (!child.isExportDefaultDeclaration()) return; if (!child.isExportDefaultDeclaration()) return;
@ -486,12 +485,13 @@ function nameAnonymousExports(programPath: NodePath) {
}); });
} }
function removeModuleDeclarations(programPath: NodePath) { function removeModuleDeclarations(programPath: NodePath<t.Program>) {
programPath.get("body").forEach(child => { programPath.get("body").forEach(child => {
if (child.isImportDeclaration()) { if (child.isImportDeclaration()) {
child.remove(); child.remove();
} else if (child.isExportNamedDeclaration()) { } else if (child.isExportNamedDeclaration()) {
if (child.node.declaration) { if (child.node.declaration) {
// @ts-expect-error todo(flow->ts): avoid mutations
child.node.declaration._blockHoist = child.node._blockHoist; child.node.declaration._blockHoist = child.node._blockHoist;
child.replaceWith(child.node.declaration); child.replaceWith(child.node.declaration);
} else { } else {
@ -504,6 +504,7 @@ function removeModuleDeclarations(programPath: NodePath) {
declaration.isFunctionDeclaration() || declaration.isFunctionDeclaration() ||
declaration.isClassDeclaration() declaration.isClassDeclaration()
) { ) {
// @ts-expect-error todo(flow->ts): avoid mutations
declaration._blockHoist = child.node._blockHoist; declaration._blockHoist = child.node._blockHoist;
child.replaceWith(declaration); child.replaceWith(declaration);
} else { } else {

View File

@ -1,12 +1,33 @@
import assert from "assert"; import assert from "assert";
import * as t from "@babel/types"; import * as t from "@babel/types";
import template from "@babel/template"; import template from "@babel/template";
import type { NodePath, Visitor, Scope } from "@babel/traverse";
import simplifyAccess from "@babel/helper-simple-access"; import simplifyAccess from "@babel/helper-simple-access";
import type { ModuleMetadata } from "./normalize-and-load-metadata"; import type { ModuleMetadata } from "./normalize-and-load-metadata";
interface RewriteReferencesVisitorState {
exported: Map<any, any>;
metadata: ModuleMetadata;
requeueInParent: (path) => void;
scope: Scope;
imported: Map<any, any>;
buildImportReference: (
[source, importName, localName]: readonly [any, any, any],
identNode,
) => any;
seen: WeakSet<object>;
}
interface RewriteBindingInitVisitorState {
exported: Map<any, any>;
metadata: ModuleMetadata;
requeueInParent: (path) => void;
scope: Scope;
}
export default function rewriteLiveReferences( export default function rewriteLiveReferences(
programPath: NodePath, programPath: NodePath<t.Program>,
metadata: ModuleMetadata, metadata: ModuleMetadata,
) { ) {
const imported = new Map(); const imported = new Map();
@ -39,12 +60,16 @@ export default function rewriteLiveReferences(
} }
// Rewrite initialization of bindings to update exports. // Rewrite initialization of bindings to update exports.
programPath.traverse(rewriteBindingInitVisitor, { const rewriteBindingInitVisitorState: RewriteBindingInitVisitorState = {
metadata, metadata,
requeueInParent, requeueInParent,
scope: programPath.scope, scope: programPath.scope,
exported, // local name => exported name list exported, // local name => exported name list
}); };
programPath.traverse(
rewriteBindingInitVisitor,
rewriteBindingInitVisitorState,
);
simplifyAccess( simplifyAccess(
programPath, programPath,
@ -53,7 +78,7 @@ export default function rewriteLiveReferences(
); );
// Rewrite reads/writes from imports and exports to have the correct behavior. // Rewrite reads/writes from imports and exports to have the correct behavior.
programPath.traverse(rewriteReferencesVisitor, { const rewriteReferencesVisitorState: RewriteReferencesVisitorState = {
seen: new WeakSet(), seen: new WeakSet(),
metadata, metadata,
requeueInParent, requeueInParent,
@ -68,7 +93,7 @@ export default function rewriteLiveReferences(
return identNode; return identNode;
} }
let namespace = t.identifier(meta.name); let namespace: t.Expression = t.identifier(meta.name);
if (meta.lazy) namespace = t.callExpression(namespace, []); if (meta.lazy) namespace = t.callExpression(namespace, []);
const computed = metadata.stringSpecifiers.has(importName); const computed = metadata.stringSpecifiers.has(importName);
@ -79,13 +104,14 @@ export default function rewriteLiveReferences(
computed, computed,
); );
}, },
}); };
programPath.traverse(rewriteReferencesVisitor, rewriteReferencesVisitorState);
} }
/** /**
* A visitor to inject export update statements during binding initialization. * A visitor to inject export update statements during binding initialization.
*/ */
const rewriteBindingInitVisitor = { const rewriteBindingInitVisitor: Visitor<RewriteBindingInitVisitorState> = {
Scope(path) { Scope(path) {
path.skip(); path.skip();
}, },
@ -105,6 +131,7 @@ const rewriteBindingInitVisitor = {
t.identifier(localName), t.identifier(localName),
), ),
); );
// @ts-expect-error todo(flow->ts): avoid mutations
statement._blockHoist = path.node._blockHoist; statement._blockHoist = path.node._blockHoist;
requeueInParent(path.insertAfter(statement)[0]); requeueInParent(path.insertAfter(statement)[0]);
@ -124,6 +151,7 @@ const rewriteBindingInitVisitor = {
t.identifier(localName), t.identifier(localName),
), ),
); );
// @ts-expect-error todo(flow->ts): avoid mutations
statement._blockHoist = path.node._blockHoist; statement._blockHoist = path.node._blockHoist;
requeueInParent(path.insertAfter(statement)[0]); requeueInParent(path.insertAfter(statement)[0]);
@ -163,7 +191,7 @@ const buildImportThrow = localName => {
`; `;
}; };
const rewriteReferencesVisitor = { const rewriteReferencesVisitor: Visitor<RewriteReferencesVisitorState> = {
ReferencedIdentifier(path) { ReferencedIdentifier(path) {
const { const {
seen, seen,
@ -200,9 +228,11 @@ const rewriteReferencesVisitor = {
} else if (path.isJSXIdentifier() && t.isMemberExpression(ref)) { } else if (path.isJSXIdentifier() && t.isMemberExpression(ref)) {
const { object, property } = ref; const { object, property } = ref;
path.replaceWith( path.replaceWith(
t.JSXMemberExpression( t.jsxMemberExpression(
t.JSXIdentifier(object.name), // @ts-expect-error todo(flow->ts): possible bug `object` might not have a name
t.JSXIdentifier(property.name), t.jsxIdentifier(object.name),
// @ts-expect-error todo(flow->ts): possible bug `property` might not have a name
t.jsxIdentifier(property.name),
), ),
); );
} else { } else {
@ -303,9 +333,10 @@ const rewriteReferencesVisitor = {
}); });
if (items.length > 0) { if (items.length > 0) {
let node = t.sequenceExpression(items); let node: t.Node = t.sequenceExpression(items);
if (path.parentPath.isExpressionStatement()) { if (path.parentPath.isExpressionStatement()) {
node = t.expressionStatement(node); node = t.expressionStatement(node);
// @ts-expect-error todo(flow->ts): avoid mutations
node._blockHoist = path.parentPath.node._blockHoist; node._blockHoist = path.parentPath.node._blockHoist;
} }
@ -315,7 +346,9 @@ const rewriteReferencesVisitor = {
} }
}, },
}, },
"ForOfStatement|ForInStatement"(path) { "ForOfStatement|ForInStatement"(
path: NodePath<t.ForOfStatement | t.ForInStatement>,
) {
const { scope, node } = path; const { scope, node } = path;
const { left } = node; const { left } = node;
const { exported, scope: programScope } = this; const { exported, scope: programScope } = this;

View File

@ -2,6 +2,7 @@ import { environmentVisitor } from "@babel/helper-replace-supers";
import traverse from "@babel/traverse"; import traverse from "@babel/traverse";
import * as t from "@babel/types"; import * as t from "@babel/types";
import type { NodePath, Visitor } from "@babel/traverse";
export default function rewriteThis(programPath: NodePath) { export default function rewriteThis(programPath: NodePath) {
// Rewrite "this" to be "undefined". // Rewrite "this" to be "undefined".
traverse(programPath.node, { ...rewriteThisVisitor, noScope: true }); traverse(programPath.node, { ...rewriteThisVisitor, noScope: true });
@ -11,10 +12,10 @@ export default function rewriteThis(programPath: NodePath) {
* A visitor to walk the tree, rewriting all `this` references in the top-level scope to be * A visitor to walk the tree, rewriting all `this` references in the top-level scope to be
* `void 0` (undefined). * `void 0` (undefined).
*/ */
const rewriteThisVisitor = traverse.visitors.merge([ const rewriteThisVisitor: Visitor = traverse.visitors.merge([
environmentVisitor, environmentVisitor,
{ {
ThisExpression(path) { ThisExpression(path: NodePath<t.ThisExpression>) {
path.replaceWith(t.unaryExpression("void", t.numericLiteral(0), true)); path.replaceWith(t.unaryExpression("void", t.numericLiteral(0), true));
}, },
}, },