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.exports: any;
}
declare module "@babel/helper-module-transforms" {
declare module.exports: any;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@ import { environmentVisitor } from "@babel/helper-replace-supers";
import traverse from "@babel/traverse";
import * as t from "@babel/types";
import type { NodePath, Visitor } from "@babel/traverse";
export default function rewriteThis(programPath: NodePath) {
// Rewrite "this" to be "undefined".
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
* `void 0` (undefined).
*/
const rewriteThisVisitor = traverse.visitors.merge([
const rewriteThisVisitor: Visitor = traverse.visitors.merge([
environmentVisitor,
{
ThisExpression(path) {
ThisExpression(path: NodePath<t.ThisExpression>) {
path.replaceWith(t.unaryExpression("void", t.numericLiteral(0), true));
},
},