From 1ee71c26b1812dc60245fc3138c9c4c343bb0a6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 26 May 2022 09:01:40 -0400 Subject: [PATCH 01/22] enable noImplicitAny --- tsconfig.base.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index af2501b34cc3..64b95c92c311 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -12,6 +12,7 @@ "isolatedModules": true, "skipLibCheck": true, "resolveJsonModule": true, - "noImplicitThis": true + "noImplicitThis": true, + "noImplicitAny": true } } From 65a952359585ca32d43283add06ffa7a168fc42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 26 May 2022 11:51:19 -0400 Subject: [PATCH 02/22] codemod (#14602) --- .../src/index.ts | 12 +++++++++--- .../src/index.ts | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/codemods/babel-plugin-codemod-object-assign-to-object-spread/src/index.ts b/codemods/babel-plugin-codemod-object-assign-to-object-spread/src/index.ts index cb7ae05910d8..50a18d9bd0b0 100644 --- a/codemods/babel-plugin-codemod-object-assign-to-object-spread/src/index.ts +++ b/codemods/babel-plugin-codemod-object-assign-to-object-spread/src/index.ts @@ -1,6 +1,7 @@ import syntaxObjectRestSpread from "@babel/plugin-syntax-object-rest-spread"; +import type { PluginAPI, PluginObject } from "@babel/core"; -export default function ({ types: t }) { +export default function ({ types: t }: PluginAPI): PluginObject { return { inherits: syntaxObjectRestSpread.default, @@ -21,10 +22,15 @@ export default function ({ types: t }) { const arg = args[i]; const { node } = arg; - if (arg.isObjectExpression()) { + if (t.isObjectExpression(node)) { properties.push(...node.properties); } else { - properties.push(t.spreadElement(node)); + properties.push( + t.spreadElement( + // @ts-expect-error fixme + node, + ), + ); } } diff --git a/codemods/babel-plugin-codemod-optional-catch-binding/src/index.ts b/codemods/babel-plugin-codemod-optional-catch-binding/src/index.ts index d4873469bb7a..9cf2954e0e58 100644 --- a/codemods/babel-plugin-codemod-optional-catch-binding/src/index.ts +++ b/codemods/babel-plugin-codemod-optional-catch-binding/src/index.ts @@ -1,6 +1,7 @@ import syntaxOptionalCatchBinding from "@babel/plugin-syntax-optional-catch-binding"; +import type { PluginAPI, PluginObject } from "@babel/core"; -export default function ({ types: t }) { +export default function ({ types: t }: PluginAPI): PluginObject { return { inherits: syntaxOptionalCatchBinding.default, From ee06d122f9b88ac9f78c49bc7adfcb69bbaab384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 26 May 2022 15:07:00 -0400 Subject: [PATCH 03/22] Improve preset/plugin-typescript typings (#14603) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * preset-typescript * transform-typescript * Update packages/babel-plugin-transform-typescript/src/enum.ts Co-authored-by: Nicolò Ribaudo * prettier Co-authored-by: Nicolò Ribaudo --- .../src/enum.ts | 10 ++-- .../src/index.ts | 23 +++++++--- .../src/namespace.ts | 46 ++++++++++--------- packages/babel-preset-typescript/src/index.ts | 4 +- 4 files changed, 48 insertions(+), 35 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/src/enum.ts b/packages/babel-plugin-transform-typescript/src/enum.ts index 79368a06656a..9a3234286f69 100644 --- a/packages/babel-plugin-transform-typescript/src/enum.ts +++ b/packages/babel-plugin-transform-typescript/src/enum.ts @@ -39,7 +39,7 @@ export default function transpileEnum( throw new Error(`Unexpected enum parent '${path.parent.type}`); } - function seen(parentPath: NodePath) { + function seen(parentPath: NodePath): boolean { if (parentPath.isExportDeclaration()) { return seen(parentPath.parentPath); } @@ -193,12 +193,12 @@ export function translateEnumValues( // Based on the TypeScript repository's `evalConstant` in `checker.ts`. function evaluate( - expr, + expr: t.Node, seen: PreviousEnumMembers, ): number | string | typeof undefined { return evalConstant(expr); - function evalConstant(expr): number | typeof undefined { + function evalConstant(expr: t.Node): number | typeof undefined { switch (expr.type) { case "StringLiteral": return expr.value; @@ -225,7 +225,7 @@ function evaluate( function evalUnaryExpression({ argument, operator, - }): number | typeof undefined { + }: t.UnaryExpression): number | typeof undefined { const value = evalConstant(argument); if (value === undefined) { return undefined; @@ -243,7 +243,7 @@ function evaluate( } } - function evalBinaryExpression(expr): number | typeof undefined { + function evalBinaryExpression(expr: t.BinaryExpression): number | undefined { const left = evalConstant(expr.left); if (left === undefined) { return undefined; diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts index 0468c6e04d6b..2ab07a6dd016 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -2,7 +2,7 @@ import { declare } from "@babel/helper-plugin-utils"; import syntaxTypeScript from "@babel/plugin-syntax-typescript"; import { types as t, template } from "@babel/core"; import { injectInitialization } from "@babel/helper-create-class-features-plugin"; -import type { NodePath } from "@babel/traverse"; +import type { Binding, NodePath } from "@babel/traverse"; import type { Options as SyntaxOptions } from "@babel/plugin-syntax-typescript"; import transpileConstEnum from "./const-enum"; @@ -149,7 +149,7 @@ export default declare((api, opts: Options) => { if (node.declare) node.declare = null; if (node.override) node.override = null; }, - method({ node }) { + method({ node }: NodePath) { if (node.accessibility) node.accessibility = null; if (node.abstract) node.abstract = null; if (node.optional) node.optional = null; @@ -157,7 +157,7 @@ export default declare((api, opts: Options) => { // Rest handled by Function visitor }, - constructor(path, classPath) { + constructor(path: NodePath, classPath: NodePath) { if (path.node.accessibility) path.node.accessibility = null; // Collects parameter properties so that we can add an assignment // for each of them in the constructor body @@ -487,7 +487,11 @@ export default declare((api, opts: Options) => { path.get("body.body").forEach(child => { if (child.isClassMethod() || child.isClassPrivateMethod()) { if (child.node.kind === "constructor") { - classMemberVisitors.constructor(child, path); + classMemberVisitors.constructor( + // @ts-expect-error A constructor must not be a private method + child, + path, + ); } else { classMemberVisitors.method(child); } @@ -512,7 +516,7 @@ export default declare((api, opts: Options) => { }, TSModuleDeclaration(path) { - transpileNamespace(path, t, allowNamespaces); + transpileNamespace(path, allowNamespaces); }, TSInterfaceDeclaration(path) { @@ -622,7 +626,9 @@ export default declare((api, opts: Options) => { return node; } - function visitPattern({ node }) { + function visitPattern({ + node, + }: NodePath) { if (node.typeAnnotation) node.typeAnnotation = null; if (t.isIdentifier(node) && node.optional) node.optional = null; // 'access' and 'readonly' are only for parameter properties, so constructor visitor will handle them. @@ -633,6 +639,11 @@ export default declare((api, opts: Options) => { programPath, pragmaImportName, pragmaFragImportName, + }: { + binding: Binding; + programPath: NodePath; + pragmaImportName: string; + pragmaFragImportName: string; }) { for (const path of binding.referencePaths) { if (!isInType(path)) { diff --git a/packages/babel-plugin-transform-typescript/src/namespace.ts b/packages/babel-plugin-transform-typescript/src/namespace.ts index 186da7c3c77c..74532eca9412 100644 --- a/packages/babel-plugin-transform-typescript/src/namespace.ts +++ b/packages/babel-plugin-transform-typescript/src/namespace.ts @@ -1,29 +1,33 @@ import { template, types as t } from "@babel/core"; import type { NodePath } from "@babel/traverse"; -export default function transpileNamespace(path, t, allowNamespaces) { +export default function transpileNamespace( + path: NodePath, + allowNamespaces: boolean, +) { if (path.node.declare || path.node.id.type === "StringLiteral") { path.remove(); return; } if (!allowNamespaces) { - throw path.hub.file.buildCodeFrameError( - path.node.id, - "Namespace not marked type-only declare." + - " Non-declarative namespaces are only supported experimentally in Babel." + - " To enable and review caveats see:" + - " https://babeljs.io/docs/en/babel-plugin-transform-typescript", - ); + throw path + .get("id") + .buildCodeFrameError( + "Namespace not marked type-only declare." + + " Non-declarative namespaces are only supported experimentally in Babel." + + " To enable and review caveats see:" + + " https://babeljs.io/docs/en/babel-plugin-transform-typescript", + ); } const name = path.node.id.name; - const value = handleNested(path, t, t.cloneDeep(path.node)); + const value = handleNested(path, t.cloneNode(path.node, true)); const bound = path.scope.hasOwnBinding(name); if (path.parent.type === "ExportNamedDeclaration") { if (!bound) { path.parentPath.insertAfter(value); - path.replaceWith(getDeclaration(t, name)); + path.replaceWith(getDeclaration(name)); path.scope.registerDeclaration(path.parentPath); } else { path.parentPath.replaceWith(value); @@ -32,18 +36,18 @@ export default function transpileNamespace(path, t, allowNamespaces) { path.replaceWith(value); } else { path.scope.registerDeclaration( - path.replaceWithMultiple([getDeclaration(t, name), value])[0], + path.replaceWithMultiple([getDeclaration(name), value])[0], ); } } -function getDeclaration(t, name) { +function getDeclaration(name: string) { return t.variableDeclaration("let", [ t.variableDeclarator(t.identifier(name)), ]); } -function getMemberExpression(t, name, itemName) { +function getMemberExpression(name: string, itemName: string) { return t.memberExpression(t.identifier(name), t.identifier(itemName)); } @@ -79,7 +83,7 @@ function handleVariableDeclaration( for (const declarator of declarations) { declarator.init = t.assignmentExpression( "=", - getMemberExpression(t, name, declarator.id.name), + getMemberExpression(name, declarator.id.name), declarator.init, ); } @@ -95,7 +99,7 @@ function handleVariableDeclaration( assignments.push( t.assignmentExpression( "=", - getMemberExpression(t, name, idName), + getMemberExpression(name, idName), t.cloneNode(bindingIdentifiers[idName]), ), ); @@ -113,9 +117,8 @@ function buildNestedAmbiendModuleError(path: NodePath, node: t.Node) { function handleNested( path: NodePath, - t: typeof import("@babel/types"), node: t.TSModuleDeclaration, - parentExport?, + parentExport?: t.Expression, ) { const names = new Set(); const realName = node.id; @@ -142,7 +145,7 @@ function handleNested( throw buildNestedAmbiendModuleError(path, subNode); } - const transformed = handleNested(path, t, subNode); + const transformed = handleNested(path, subNode); const moduleName = subNode.id.name; if (names.has(moduleName)) { namespaceTopLevel[i] = transformed; @@ -151,7 +154,7 @@ function handleNested( namespaceTopLevel.splice( i++, 1, - getDeclaration(t, moduleName), + getDeclaration(moduleName), transformed, ); } @@ -191,7 +194,7 @@ function handleNested( t.expressionStatement( t.assignmentExpression( "=", - getMemberExpression(t, name, itemName), + getMemberExpression(name, itemName), t.identifier(itemName), ), ), @@ -215,7 +218,6 @@ function handleNested( const transformed = handleNested( path, - t, subNode.declaration, t.identifier(name), ); @@ -227,7 +229,7 @@ function handleNested( namespaceTopLevel.splice( i++, 1, - getDeclaration(t, moduleName), + getDeclaration(moduleName), transformed, ); } diff --git a/packages/babel-preset-typescript/src/index.ts b/packages/babel-preset-typescript/src/index.ts index e9fb7b515204..5fce63166f9e 100644 --- a/packages/babel-preset-typescript/src/index.ts +++ b/packages/babel-preset-typescript/src/index.ts @@ -18,7 +18,7 @@ export default declarePreset((api, opts: Options) => { } = normalizeOptions(opts); const pluginOptions = process.env.BABEL_8_BREAKING - ? (isTSX, disallowAmbiguousJSXLike) => ({ + ? (isTSX: boolean, disallowAmbiguousJSXLike: boolean) => ({ allowNamespaces, disallowAmbiguousJSXLike, isTSX, @@ -27,7 +27,7 @@ export default declarePreset((api, opts: Options) => { onlyRemoveTypeImports, optimizeConstEnums, }) - : (isTSX, disallowAmbiguousJSXLike) => ({ + : (isTSX: boolean, disallowAmbiguousJSXLike: boolean) => ({ allowDeclareFields: opts.allowDeclareFields, allowNamespaces, disallowAmbiguousJSXLike, From 0c0741d90b33a35700f9a67ebdd61be273655db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 26 May 2022 16:22:02 -0400 Subject: [PATCH 04/22] Improve transform-runtime typings (#14605) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enable noImplicitAny * codemod (#14602) * Improve preset/plugin-typescript typings (#14603) * preset-typescript * transform-typescript * Update packages/babel-plugin-transform-typescript/src/enum.ts Co-authored-by: Nicolò Ribaudo * prettier Co-authored-by: Nicolò Ribaudo * improve transform-runtime typings Co-authored-by: Nicolò Ribaudo --- packages/babel-core/src/config/index.ts | 5 +- packages/babel-core/src/index.ts | 2 + .../src/get-runtime-path/browser.ts | 6 +- .../src/get-runtime-path/index.ts | 8 +- .../src/helpers.ts | 5 +- .../src/index.ts | 73 ++++++++++++++++--- 6 files changed, 83 insertions(+), 16 deletions(-) diff --git a/packages/babel-core/src/config/index.ts b/packages/babel-core/src/config/index.ts index d231a8ec6d59..aff0cc5b7fd2 100644 --- a/packages/babel-core/src/config/index.ts +++ b/packages/babel-core/src/config/index.ts @@ -18,7 +18,10 @@ type PluginAPI = basePluginAPI & typeof import(".."); type PresetAPI = basePresetAPI & typeof import(".."); export type { PluginAPI, PresetAPI }; // todo: may need to refine PresetObject to be a subset of ValidatedOptions -export type { ValidatedOptions as PresetObject } from "./validation/options"; +export type { + CallerMetadata, + ValidatedOptions as PresetObject, +} from "./validation/options"; import loadFullConfig from "./full"; import { loadPartialConfig as loadPartialConfigRunner } from "./partial"; diff --git a/packages/babel-core/src/index.ts b/packages/babel-core/src/index.ts index 3a05f67240ce..81ea29c99e66 100644 --- a/packages/babel-core/src/index.ts +++ b/packages/babel-core/src/index.ts @@ -32,6 +32,8 @@ export { } from "./config"; export type { + CallerMetadata, + InputOptions, PluginAPI, PluginObject, PresetAPI, diff --git a/packages/babel-plugin-transform-runtime/src/get-runtime-path/browser.ts b/packages/babel-plugin-transform-runtime/src/get-runtime-path/browser.ts index 94af79f4ce4d..53cc0f75469c 100644 --- a/packages/babel-plugin-transform-runtime/src/get-runtime-path/browser.ts +++ b/packages/babel-plugin-transform-runtime/src/get-runtime-path/browser.ts @@ -1,4 +1,8 @@ -export default function (moduleName, dirname, absoluteRuntime) { +export default function ( + moduleName: string, + dirname: string, + absoluteRuntime: string | boolean, +) { if (absoluteRuntime === false) return moduleName; resolveFSPath(); diff --git a/packages/babel-plugin-transform-runtime/src/get-runtime-path/index.ts b/packages/babel-plugin-transform-runtime/src/get-runtime-path/index.ts index 37fdc83a6827..ac92873a8e05 100644 --- a/packages/babel-plugin-transform-runtime/src/get-runtime-path/index.ts +++ b/packages/babel-plugin-transform-runtime/src/get-runtime-path/index.ts @@ -3,7 +3,11 @@ import path from "path"; import { createRequire } from "module"; const require = createRequire(import.meta.url); -export default function (moduleName, dirname, absoluteRuntime) { +export default function ( + moduleName: string, + dirname: string, + absoluteRuntime: string | boolean, +) { if (absoluteRuntime === false) return moduleName; return resolveAbsoluteRuntime( @@ -33,6 +37,6 @@ function resolveAbsoluteRuntime(moduleName: string, dirname: string) { } } -export function resolveFSPath(path) { +export function resolveFSPath(path: string) { return require.resolve(path).replace(/\\/g, "/"); } diff --git a/packages/babel-plugin-transform-runtime/src/helpers.ts b/packages/babel-plugin-transform-runtime/src/helpers.ts index ad77e524f9ae..bea33ed07af0 100644 --- a/packages/babel-plugin-transform-runtime/src/helpers.ts +++ b/packages/babel-plugin-transform-runtime/src/helpers.ts @@ -1,6 +1,9 @@ import semver from "semver"; -export function hasMinVersion(minVersion, runtimeVersion) { +export function hasMinVersion( + minVersion: string, + runtimeVersion: string | void, +) { // If the range is unavailable, we're running the script during Babel's // build process, and we want to assume that all versions are satisfied so // that the built output will include all definitions. diff --git a/packages/babel-plugin-transform-runtime/src/index.ts b/packages/babel-plugin-transform-runtime/src/index.ts index b734e4b6e3fc..d16ea9acae92 100644 --- a/packages/babel-plugin-transform-runtime/src/index.ts +++ b/packages/babel-plugin-transform-runtime/src/index.ts @@ -4,6 +4,7 @@ import { types as t } from "@babel/core"; import { hasMinVersion } from "./helpers"; import getRuntimePath, { resolveFSPath } from "./get-runtime-path"; +import type { PluginAPI, PluginObject, CallerMetadata } from "@babel/core"; import _pluginCorejs2 from "babel-plugin-polyfill-corejs2"; import _pluginCorejs3 from "babel-plugin-polyfill-corejs3"; @@ -17,7 +18,8 @@ const pluginRegenerator = (_pluginRegenerator.default || const pluginsCompat = "#__secret_key__@babel/runtime__compatibility"; -function supportsStaticESM(caller) { +function supportsStaticESM(caller: CallerMetadata | void) { + // @ts-ignore TS does not narrow down optional chaining return !!caller?.supportsStaticESM; } @@ -30,6 +32,35 @@ export interface Options { version?: string; } +interface CoreJS2PluginOptions { + absoluteImports: string | false; + method: "usage-pure"; + [pluginsCompat]: { + runtimeVersion: string; + useBabelRuntime: string | false; + ext: string; + }; +} + +interface RegeneratorPluginOptions { + absoluteImports: string | false; + method: "usage-pure"; + [pluginsCompat]: { + useBabelRuntime: string | false; + }; +} + +interface CoreJS3PluginOptions { + absoluteImports: string | false; + method: "usage-pure"; + proposals: boolean; + version: number; + [pluginsCompat]: { + useBabelRuntime: string | false; + ext: string; + }; +} + export default declare((api, options: Options, dirname) => { api.assertVersion(7); @@ -106,11 +137,12 @@ export default declare((api, options: Options, dirname) => { var supportsCJSDefault = hasMinVersion(DUAL_MODE_RUNTIME, runtimeVersion); } - function has(obj, key) { + function has(obj: {}, key: string) { return Object.prototype.hasOwnProperty.call(obj, key); } if (has(options, "useBuiltIns")) { + // @ts-expect-error deprecated options if (options["useBuiltIns"]) { throw new Error( "The 'useBuiltIns' option has been removed. The @babel/runtime " + @@ -125,6 +157,7 @@ export default declare((api, options: Options, dirname) => { } if (has(options, "polyfill")) { + // @ts-expect-error deprecated options if (options["polyfill"] === false) { throw new Error( "The 'polyfill' option has been removed. The @babel/runtime " + @@ -163,8 +196,20 @@ export default declare((api, options: Options, dirname) => { const modulePath = getRuntimePath(moduleName, dirname, absoluteRuntime); - function createCorejsPlgin(plugin, options, regeneratorPlugin) { - return (api, _, filename) => { + function createCorejsPlgin( + plugin: ( + api: PluginAPI, + options: Options, + filename: string, + ) => PluginObject, + options: Options, + regeneratorPlugin: ( + api: PluginAPI, + options: RegeneratorPluginOptions, + filename: string, + ) => PluginObject, + ): (api: PluginAPI, options: {}, filename: string) => PluginObject { + return (api: PluginAPI, _: {}, filename: string) => { return { ...plugin(api, options, filename), inherits: regeneratorPlugin, @@ -173,7 +218,13 @@ export default declare((api, options: Options, dirname) => { } // TODO: Remove this in Babel 8 - function createRegeneratorPlugin(options) { + function createRegeneratorPlugin( + options: RegeneratorPluginOptions, + ): ( + api: PluginAPI, + options: RegeneratorPluginOptions, + filename: string, + ) => PluginObject { if (!useRuntimeRegenerator) return undefined; return (api, _, filename) => { return pluginRegenerator(api, options, filename); @@ -184,7 +235,7 @@ export default declare((api, options: Options, dirname) => { name: "transform-runtime", inherits: injectCoreJS2 - ? createCorejsPlgin( + ? createCorejsPlgin( pluginCorejs2, { method: "usage-pure", @@ -202,7 +253,7 @@ export default declare((api, options: Options, dirname) => { }), ) : injectCoreJS3 - ? createCorejsPlgin( + ? createCorejsPlgin( pluginCorejs3, { method: "usage-pure", @@ -226,7 +277,7 @@ export default declare((api, options: Options, dirname) => { pre(file) { if (!useRuntimeHelpers) return; - file.set("helperGenerator", name => { + file.set("helperGenerator", (name: string) => { // If the helper didn't exist yet at the version given, we bail // out and let Babel either insert it directly, or throw an error // so that plugins can handle that case properly. @@ -271,9 +322,9 @@ export default declare((api, options: Options, dirname) => { const cache = new Map(); function addDefaultImport( - source, - nameHint, - blockHoist, + source: string, + nameHint: string, + blockHoist: number, isHelper = false, ) { // If something on the page adds a helper when the file is an ES6 From 7623b47a0ade42fe74e8d99e733bccd29260c4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 27 May 2022 17:52:38 -0400 Subject: [PATCH 05/22] jsx (#14607) --- .../src/index.ts | 119 ++++++++++++------ .../src/index.ts | 18 ++- .../src/index.ts | 9 +- .../src/index.ts | 2 +- .../src/index.ts | 25 ++-- .../src/create-plugin.ts | 30 +++-- 6 files changed, 137 insertions(+), 66 deletions(-) diff --git a/packages/babel-helper-builder-react-jsx/src/index.ts b/packages/babel-helper-builder-react-jsx/src/index.ts index e9f436afb6fa..f51975543fbc 100644 --- a/packages/babel-helper-builder-react-jsx/src/index.ts +++ b/packages/babel-helper-builder-react-jsx/src/index.ts @@ -9,7 +9,6 @@ import { isJSXMemberExpression, isJSXNamespacedName, isJSXSpreadAttribute, - isLiteral, isObjectExpression, isReferenced, isStringLiteral, @@ -24,10 +23,12 @@ import { thisExpression, } from "@babel/types"; import annotateAsPure from "@babel/helper-annotate-as-pure"; -import type { Visitor } from "@babel/traverse"; +import type { NodePath, Visitor } from "@babel/traverse"; +import type { PluginPass, File } from "@babel/core"; +import type * as t from "@babel/types"; type ElementState = { - tagExpr: any; // tag node, + tagExpr: t.Expression; // tag node, tagName: string | undefined | null; // raw string tag name, args: Array; // array of call arguments, call?: any; // optional call property that can be set to override the call expression returned, @@ -35,8 +36,17 @@ type ElementState = { callee?: any; }; -export default function (opts) { - const visitor: Visitor = {}; +export interface Options { + filter?: (node: t.Node, file: File) => boolean; + pre?: (state: ElementState, file: File) => void; + post?: (state: ElementState, file: File) => void; + compat?: boolean; + pure?: string; + throwIfNamespace?: boolean; +} + +export default function (opts: Options) { + const visitor: Visitor = {}; visitor.JSXNamespacedName = function (path) { if (opts.throwIfNamespace) { @@ -54,8 +64,8 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, }; visitor.JSXElement = { - exit(path, file) { - const callExpr = buildElementCall(path, file); + exit(path, state) { + const callExpr = buildElementCall(path, state.file); if (callExpr) { path.replaceWith(inherits(callExpr, path.node)); } @@ -63,13 +73,13 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, }; visitor.JSXFragment = { - exit(path, file) { + exit(path, state) { if (opts.compat) { throw path.buildCodeFrameError( "Fragment tags are only supported in React 16 and up.", ); } - const callExpr = buildFragmentCall(path, file); + const callExpr = buildFragmentCall(path, state.file); if (callExpr) { path.replaceWith(inherits(callExpr, path.node)); } @@ -78,7 +88,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, return visitor; - function convertJSXIdentifier(node, parent) { + function convertJSXIdentifier( + node: t.JSXIdentifier | t.JSXMemberExpression | t.JSXNamespacedName, + parent: t.JSXOpeningElement | t.JSXMemberExpression, + ): t.ThisExpression | t.StringLiteral | t.MemberExpression | t.Identifier { if (isJSXIdentifier(node)) { if (node.name === "this" && isReferenced(node, parent)) { return thisExpression(); @@ -101,10 +114,13 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, return stringLiteral(`${node.namespace.name}:${node.name.name}`); } + // @ts-expect-error return node; } - function convertAttributeValue(node) { + function convertAttributeValue( + node: t.JSXAttribute["value"] | t.BooleanLiteral, + ) { if (isJSXExpressionContainer(node)) { return node.expression; } else { @@ -112,12 +128,11 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, } } - function convertAttribute(node) { - const value = convertAttributeValue(node.value || booleanLiteral(true)); - + function convertAttribute(node: t.JSXAttribute | t.JSXSpreadAttribute) { if (isJSXSpreadAttribute(node)) { return spreadElement(node.argument); } + const value = convertAttributeValue(node.value || booleanLiteral(true)); if (isStringLiteral(value) && !isJSXExpressionContainer(node.value)) { value.value = value.value.replace(/\n\s+/g, " "); @@ -127,35 +142,45 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, } if (isJSXNamespacedName(node.name)) { + // @ts-expect-error Mutating AST nodes node.name = stringLiteral( node.name.namespace.name + ":" + node.name.name.name, ); } else if (isValidIdentifier(node.name.name, false)) { + // @ts-expect-error Mutating AST nodes node.name.type = "Identifier"; } else { + // @ts-expect-error Mutating AST nodes node.name = stringLiteral(node.name.name); } - return inherits(objectProperty(node.name, value), node); + return inherits( + objectProperty( + // @ts-expect-error Mutating AST nodes + node.name, + value, + ), + node, + ); } - function buildElementCall(path, file) { + function buildElementCall(path: NodePath, file: File) { if (opts.filter && !opts.filter(path.node, file)) return; const openingPath = path.get("openingElement"); - openingPath.parent.children = react.buildChildren(openingPath.parent); + // @ts-expect-error mutating AST nodes + path.node.children = react.buildChildren(path.node); const tagExpr = convertJSXIdentifier( openingPath.node.name, openingPath.node, ); - const args = []; + const args: (t.Expression | t.JSXElement | t.JSXFragment)[] = []; - let tagName; + let tagName: string; if (isIdentifier(tagExpr)) { tagName = tagExpr.name; - } else if (isLiteral(tagExpr)) { - // @ts-expect-error todo(flow->ts) NullLiteral + } else if (isStringLiteral(tagExpr)) { tagName = tagExpr.value; } @@ -170,18 +195,23 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, opts.pre(state, file); } - let attribs = openingPath.node.attributes; + const attribs = openingPath.node.attributes; + let convertedAttributes: t.Expression; if (attribs.length) { if (process.env.BABEL_8_BREAKING) { - attribs = objectExpression(attribs.map(convertAttribute)); + convertedAttributes = objectExpression(attribs.map(convertAttribute)); } else { - attribs = buildOpeningElementAttributes(attribs, file); + convertedAttributes = buildOpeningElementAttributes(attribs, file); } } else { - attribs = nullLiteral(); + convertedAttributes = nullLiteral(); } - args.push(attribs, ...path.node.children); + args.push( + convertedAttributes, + // @ts-expect-error JSXExpressionContainer has been transformed by convertAttributeValue + ...path.node.children, + ); if (opts.post) { opts.post(state, file); @@ -193,7 +223,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, return call; } - function pushProps(_props, objs) { + function pushProps( + _props: (t.ObjectProperty | t.SpreadElement)[], + objs: t.Expression[], + ) { if (!_props.length) return _props; objs.push(objectExpression(_props)); @@ -207,9 +240,12 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, * all prior attributes to an array for later processing. */ - function buildOpeningElementAttributes(attribs, file) { - let _props = []; - const objs = []; + function buildOpeningElementAttributes( + attribs: (t.JSXAttribute | t.JSXSpreadAttribute)[], + file: File, + ): t.Expression { + let _props: (t.ObjectProperty | t.SpreadElement)[] = []; + const objs: t.Expression[] = []; const { useSpread = false } = file.opts; if (typeof useSpread !== "boolean") { @@ -250,10 +286,11 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, } pushProps(_props, objs); + let convertedAttribs: t.Expression; if (objs.length === 1) { // only one object - attribs = objs[0]; + convertedAttribs = objs[0]; } else { // looks like we have multiple objects if (!isObjectExpression(objs[0])) { @@ -265,20 +302,20 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, : file.addHelper("extends"); // spread it - attribs = callExpression(helper, objs); + convertedAttribs = callExpression(helper, objs); } - return attribs; + return convertedAttribs; } - function buildFragmentCall(path, file) { + function buildFragmentCall(path: NodePath, file: File) { if (opts.filter && !opts.filter(path.node, file)) return; - const openingPath = path.get("openingElement"); - openingPath.parent.children = react.buildChildren(openingPath.parent); + // @ts-expect-error mutating AST nodes + path.node.children = react.buildChildren(path.node); - const args = []; - const tagName = null; + const args: t.Expression[] = []; + const tagName: null = null; const tagExpr = file.get("jsxFragIdentifier")(); const state: ElementState = { @@ -293,7 +330,11 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, } // no attributes are allowed with <> syntax - args.push(nullLiteral(), ...path.node.children); + args.push( + nullLiteral(), + // @ts-expect-error JSXExpressionContainer has been transformed by convertAttributeValue + ...path.node.children, + ); if (opts.post) { opts.post(state, file); diff --git a/packages/babel-plugin-transform-react-display-name/src/index.ts b/packages/babel-plugin-transform-react-display-name/src/index.ts index f19c4dd0555f..91d9c85f5a27 100644 --- a/packages/babel-plugin-transform-react-display-name/src/index.ts +++ b/packages/babel-plugin-transform-react-display-name/src/index.ts @@ -2,17 +2,24 @@ import { declare } from "@babel/helper-plugin-utils"; import path from "path"; import { types as t } from "@babel/core"; +type ReactCreateClassCall = t.CallExpression & { + arguments: [t.ObjectExpression]; +}; + export default declare(api => { api.assertVersion(7); - function addDisplayName(id, call) { + function addDisplayName(id: string, call: ReactCreateClassCall) { const props = call.arguments[0].properties; let safe = true; for (let i = 0; i < props.length; i++) { const prop = props[i]; + if (t.isSpreadElement(prop)) { + continue; + } const key = t.toComputedKey(prop); - if (t.isLiteral(key, { value: "displayName" })) { + if (t.isStringLiteral(key, { value: "displayName" })) { safe = false; break; } @@ -27,9 +34,10 @@ export default declare(api => { const isCreateClassCallExpression = t.buildMatchMemberExpression("React.createClass"); - const isCreateClassAddon = callee => callee.name === "createReactClass"; + const isCreateClassAddon = (callee: t.CallExpression["callee"]) => + t.isIdentifier(callee, { name: "createReactClass" }); - function isCreateClass(node) { + function isCreateClass(node?: t.Node): node is ReactCreateClassCall { if (!node || !t.isCallExpression(node)) return false; // not createReactClass nor React.createClass call member object @@ -74,7 +82,7 @@ export default declare(api => { const { node } = path; if (!isCreateClass(node)) return; - let id; + let id: t.LVal | t.Expression | t.PrivateName | null; // crawl up the ancestry looking for possible candidates for displayName inference path.find(function (path) { diff --git a/packages/babel-plugin-transform-react-inline-elements/src/index.ts b/packages/babel-plugin-transform-react-inline-elements/src/index.ts index 46199b940b2b..119971a4f392 100644 --- a/packages/babel-plugin-transform-react-inline-elements/src/index.ts +++ b/packages/babel-plugin-transform-react-inline-elements/src/index.ts @@ -5,7 +5,7 @@ import { types as t } from "@babel/core"; export default declare(api => { api.assertVersion(7); - function hasRefOrSpread(attrs) { + function hasRefOrSpread(attrs: t.JSXOpeningElement["attributes"]) { for (let i = 0; i < attrs.length; i++) { const attr = attrs[i]; if (t.isJSXSpreadAttribute(attr)) return true; @@ -14,7 +14,7 @@ export default declare(api => { return false; } - function isJSXAttributeOfName(attr, name) { + function isJSXAttributeOfName(attr: t.JSXAttribute, name: string) { return ( t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: name }) ); @@ -23,9 +23,8 @@ export default declare(api => { const visitor = helper({ filter(node) { return ( - // Regular JSX nodes have an `openingElement`. JSX fragments, however, don't have an - // `openingElement` which causes `node.openingElement.attributes` to throw. - node.openingElement && !hasRefOrSpread(node.openingElement.attributes) + node.type === "JSXElement" && + !hasRefOrSpread(node.openingElement.attributes) ); }, pre(state) { diff --git a/packages/babel-plugin-transform-react-jsx-compat/src/index.ts b/packages/babel-plugin-transform-react-jsx-compat/src/index.ts index 294607ba79ec..510f20480628 100644 --- a/packages/babel-plugin-transform-react-jsx-compat/src/index.ts +++ b/packages/babel-plugin-transform-react-jsx-compat/src/index.ts @@ -8,7 +8,7 @@ export default declare(api => { return { name: "transform-react-jsx-compat", - manipulateOptions(opts, parserOpts) { + manipulateOptions(_, parserOpts) { parserOpts.plugins.push("jsx"); }, diff --git a/packages/babel-plugin-transform-react-jsx-source/src/index.ts b/packages/babel-plugin-transform-react-jsx-source/src/index.ts index 0f5bee8a3bf9..08602e94a1ee 100644 --- a/packages/babel-plugin-transform-react-jsx-source/src/index.ts +++ b/packages/babel-plugin-transform-react-jsx-source/src/index.ts @@ -13,15 +13,23 @@ * */ import { declare } from "@babel/helper-plugin-utils"; -import { types as t } from "@babel/core"; +import { type PluginPass, types as t } from "@babel/core"; +import type { Visitor } from "@babel/traverse"; const TRACE_ID = "__source"; const FILE_NAME_VAR = "_jsxFileName"; -export default declare(api => { +type State = { + fileNameIdentifier: t.Identifier; +}; +export default declare(api => { api.assertVersion(7); - function makeTrace(fileNameIdentifier, lineNumber, column0Based) { + function makeTrace( + fileNameIdentifier: t.Identifier, + lineNumber: number, + column0Based: number, + ) { const fileLineLiteral = lineNumber != null ? t.numericLiteral(lineNumber) : t.nullLiteral(); const fileColumnLiteral = @@ -47,18 +55,21 @@ export default declare(api => { ]); } - const visitor = { + const visitor: Visitor = { JSXOpeningElement(path, state) { const id = t.jsxIdentifier(TRACE_ID); - const location = path.container.openingElement.loc; + const location = (path.container as t.JSXElement).openingElement.loc; if (!location) { // the element was generated and doesn't have location information return; } - const attributes = path.container.openingElement.attributes; + const attributes = (path.container as t.JSXElement).openingElement + .attributes; for (let i = 0; i < attributes.length; i++) { - const name = attributes[i].name; + // @ts-expect-error .name is not defined in JSXSpreadElement + const name = attributes[i].name as t.JSXAttribute["name"] | void; + // @ts-expect-error TS can not narrow down optional chain if (name?.name === TRACE_ID) { // The __source attribute already exists return; diff --git a/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts b/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts index 2fb18e6ddb9c..3e3a93963030 100644 --- a/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts +++ b/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts @@ -50,8 +50,14 @@ export interface Options { useBuiltIns: boolean; useSpread?: boolean; } -export default function createPlugin({ name, development }) { - return declare((api, options: Options) => { +export default function createPlugin({ + name, + development, +}: { + name: string; + development: boolean; +}) { + return declare((_, options: Options) => { const { pure: PURE_ANNOTATION, @@ -369,7 +375,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, return false; } - function convertJSXIdentifier(node, parent) { + function convertJSXIdentifier( + node: t.JSXIdentifier | t.JSXMemberExpression | t.JSXNamespacedName, + parent: t.JSXOpeningElement | t.JSXMemberExpression, + ): t.ThisExpression | t.StringLiteral | t.MemberExpression | t.Identifier { if (t.isJSXIdentifier(node)) { if (node.name === "this" && t.isReferenced(node, parent)) { return t.thisExpression(); @@ -392,10 +401,13 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, return t.stringLiteral(`${node.namespace.name}:${node.name.name}`); } + // @ts-expect-error return node; } - function convertAttributeValue(node) { + function convertAttributeValue( + node: t.JSXAttribute["value"] | t.BooleanLiteral, + ) { if (t.isJSXExpressionContainer(node)) { return node.expression; } else { @@ -486,7 +498,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, // Development: React.jsxDEV(type, arguments, key, isStaticChildren, source, self) function buildJSXElementCall(path: NodePath, file: PluginPass) { const openingPath = path.get("openingElement"); - const args = [getTag(openingPath)]; + const args: t.Expression[] = [getTag(openingPath)]; const attribsArray = []; const extracted = Object.create(null); @@ -640,6 +652,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, path, openingPath.get("attributes"), ), + // @ts-expect-error JSXSpreadChild has been transformed in convertAttributeValue ...t.react.buildChildren(path.node), ]); } @@ -650,11 +663,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, openingPath.node, ); - let tagName; + let tagName: string; if (t.isIdentifier(tagExpr)) { tagName = tagExpr.name; - } else if (t.isLiteral(tagExpr)) { - // @ts-expect-error todo(flow->ts) value in missing for NullLiteral + } else if (t.isStringLiteral(tagExpr)) { tagName = tagExpr.value; } @@ -723,7 +735,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, } } - const props = []; + const props: ObjectExpression["properties"] = []; const found = Object.create(null); for (const attr of attribs) { From 000086ddba096cd624d876c0114ed26511ac4e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Mon, 30 May 2022 13:02:49 -0400 Subject: [PATCH 06/22] Improve module transform packages typings (#14608) --- .../src/index.ts | 55 +++++++----- .../src/normalize-and-load-metadata.ts | 42 ++++++--- .../src/rewrite-live-references.ts | 40 ++++++--- .../src/rewrite-this.ts | 3 +- .../src/index.ts | 33 ++++--- .../src/index.ts | 13 +-- .../src/index.ts | 89 ++++++++++--------- .../src/index.ts | 35 +++++--- packages/babel-traverse/src/path/family.ts | 24 ++--- 9 files changed, 204 insertions(+), 130 deletions(-) diff --git a/packages/babel-helper-module-transforms/src/index.ts b/packages/babel-helper-module-transforms/src/index.ts index 5001699b619e..241dac12e110 100644 --- a/packages/babel-helper-module-transforms/src/index.ts +++ b/packages/babel-helper-module-transforms/src/index.ts @@ -27,7 +27,9 @@ import normalizeModuleAndLoadMetadata, { validateImportInteropOption, } from "./normalize-and-load-metadata"; import type { + ImportInterop, InteropType, + Lazy, ModuleMetadata, SourceModuleMetadata, } from "./normalize-and-load-metadata"; @@ -38,6 +40,22 @@ export type { PluginOptions } from "./get-module-name"; export { hasExports, isSideEffectImport, isModule, rewriteThis }; +export interface RewriteModuleStatementsAndPrepareHeaderOptions { + exportName?: string; + strict: boolean; + allowTopLevelThis?: boolean; + strictMode: boolean; + loose?: boolean; + importInterop?: ImportInterop; + noInterop?: boolean; + lazy?: Lazy; + esNamespaceOnly?: boolean; + filename: string | undefined; + constantReexports?: boolean | void; + enumerableModuleMeta?: boolean | void; + noIncompleteNsImportDetection?: boolean | void; +} + /** * Perform all of the generic ES6 module rewriting needed to handle initial * module processing. This function will rewrite the majority of the given @@ -63,21 +81,7 @@ export function rewriteModuleStatementsAndPrepareHeader( constantReexports = loose, enumerableModuleMeta = loose, noIncompleteNsImportDetection, - }: { - exportName?; - strict; - allowTopLevelThis?; - strictMode; - loose?; - importInterop?: "none" | "babel" | "node"; - noInterop?; - lazy?; - esNamespaceOnly?; - filename: string | undefined; - constantReexports?; - enumerableModuleMeta?; - noIncompleteNsImportDetection?: boolean; - }, + }: RewriteModuleStatementsAndPrepareHeaderOptions, ) { validateImportInteropOption(importInterop); assert(isModule(path), "Cannot process module statements in a script"); @@ -138,9 +142,10 @@ export function rewriteModuleStatementsAndPrepareHeader( * Flag a set of statements as hoisted above all else so that module init * statements all run before user code. */ -export function ensureStatementsHoisted(statements) { +export function ensureStatementsHoisted(statements: t.Statement[]) { // Force all of the header fields to be at the top of the file. statements.forEach(header => { + // @ts-ignore Fixme: handle _blockHoist property header._blockHoist = 3; }); } @@ -303,7 +308,7 @@ const buildReexportsFromMeta = ( */ function buildESModuleHeader( metadata: ModuleMetadata, - enumerableModuleMeta: boolean = false, + enumerableModuleMeta: boolean | void = false, ) { return ( enumerableModuleMeta @@ -321,7 +326,11 @@ function buildESModuleHeader( /** * Create a re-export initialization loop for a specific imported namespace. */ -function buildNamespaceReexport(metadata, namespace, constantReexports) { +function buildNamespaceReexport( + metadata: ModuleMetadata, + namespace: t.Identifier | t.CallExpression, + constantReexports: boolean | void, +) { return ( constantReexports ? template.statement` @@ -413,8 +422,8 @@ function buildExportNameListDeclaration( function buildExportInitializationStatements( programPath: NodePath, metadata: ModuleMetadata, - constantReexports: boolean = false, - noIncompleteNsImportDetection = false, + constantReexports: boolean | void = false, + noIncompleteNsImportDetection: boolean | void = false, ) { const initStatements: Array<[string, t.Statement | null]> = []; @@ -514,7 +523,11 @@ const InitTemplate = { default: template.expression`EXPORTS.NAME = VALUE`, }; -function buildInitStatement(metadata: ModuleMetadata, exportNames, initExpr) { +function buildInitStatement( + metadata: ModuleMetadata, + exportNames: string[], + initExpr: t.Expression, +) { const { stringSpecifiers, exportName: EXPORTS } = metadata; return expressionStatement( exportNames.reduce((acc, exportName) => { diff --git a/packages/babel-helper-module-transforms/src/normalize-and-load-metadata.ts b/packages/babel-helper-module-transforms/src/normalize-and-load-metadata.ts index bd2941fba21f..e4a263831784 100644 --- a/packages/babel-helper-module-transforms/src/normalize-and-load-metadata.ts +++ b/packages/babel-helper-module-transforms/src/normalize-and-load-metadata.ts @@ -28,6 +28,14 @@ export type InteropType = | "node-namespace" // Node.js interop for namespace or default+named imports | "none"; // No interop, or named-only imports +export type ImportInterop = + | "none" + | "babel" + | "node" + | ((source: string, filename?: string) => "none" | "babel" | "node"); + +export type Lazy = boolean | string[] | ((source: string) => boolean); + export interface SourceModuleMetadata { // A unique variable name to use for this namespace object. Centralized for simplicity. name: string; @@ -45,7 +53,7 @@ export interface SourceModuleMetadata { reexportAll: null | { loc: t.SourceLocation | undefined | null; }; - lazy?; + lazy?: Lazy; } export interface LocalExportMetadata { @@ -75,7 +83,7 @@ export function isSideEffectImport(source: SourceModuleMetadata) { export function validateImportInteropOption( importInterop: any, -): importInterop is "none" | "babel" | "node" | Function { +): importInterop is ImportInterop { if ( typeof importInterop !== "function" && importInterop !== "none" && @@ -90,8 +98,8 @@ export function validateImportInteropOption( } function resolveImportInterop( - importInterop, - source, + importInterop: ImportInterop, + source: string, filename: string | undefined, ) { if (typeof importInterop === "function") { @@ -113,6 +121,12 @@ export default function normalizeModuleAndLoadMetadata( lazy = false, esNamespaceOnly = false, filename, + }: { + importInterop: ImportInterop; + initializeReexports: boolean | void; + lazy: Lazy; + esNamespaceOnly: boolean; + filename: string; }, ): ModuleMetadata { if (!exportName) { @@ -219,8 +233,8 @@ function getModuleMetadata( initializeReexports, }: { // todo(flow-ts) changed from boolean, to match expected usage inside the function - lazy: boolean | string[] | Function; - initializeReexports: boolean; + lazy: boolean | string[] | ((source: string) => boolean); + initializeReexports: boolean | void; }, stringSpecifiers: Set, ) { @@ -231,7 +245,7 @@ function getModuleMetadata( ); const sourceData = new Map(); - const getData = sourceNode => { + const getData = (sourceNode: t.StringLiteral) => { const source = sourceNode.value; let data = sourceData.get(source); @@ -409,22 +423,26 @@ function getModuleMetadata( }; } +type ModuleBindingKind = "import" | "hoisted" | "block" | "var"; /** * Get metadata about local variables that are exported. */ function getLocalExportMetadata( programPath: NodePath, - initializeReexports: boolean, + initializeReexports: boolean | void, stringSpecifiers: Set, ): Map { const bindingKindLookup = new Map(); - programPath.get("body").forEach((child: any) => { - let kind; + programPath.get("body").forEach(child => { + let kind: ModuleBindingKind; if (child.isImportDeclaration()) { kind = "import"; } else { - if (child.isExportDefaultDeclaration()) child = child.get("declaration"); + if (child.isExportDefaultDeclaration()) { + // @ts-ignore + child = child.get("declaration"); + } if (child.isExportNamedDeclaration()) { if (child.node.declaration) { child = child.get("declaration"); @@ -460,7 +478,7 @@ function getLocalExportMetadata( }); const localMetadata = new Map(); - const getLocalMetadata = idPath => { + const getLocalMetadata = (idPath: NodePath) => { const localName = idPath.node.name; let metadata = localMetadata.get(localName); diff --git a/packages/babel-helper-module-transforms/src/rewrite-live-references.ts b/packages/babel-helper-module-transforms/src/rewrite-live-references.ts index 9ba5d3063182..33c7eb3af37b 100644 --- a/packages/babel-helper-module-transforms/src/rewrite-live-references.ts +++ b/packages/babel-helper-module-transforms/src/rewrite-live-references.ts @@ -27,12 +27,12 @@ import type { ModuleMetadata } from "./normalize-and-load-metadata"; interface RewriteReferencesVisitorState { exported: Map; metadata: ModuleMetadata; - requeueInParent: (path) => void; + requeueInParent: (path: NodePath) => void; scope: Scope; imported: Map; buildImportReference: ( - [source, importName, localName]: readonly [any, any, any], - identNode, + [source, importName, localName]: readonly [string, string, string], + identNode: t.Identifier | t.CallExpression | t.JSXIdentifier, ) => any; seen: WeakSet; } @@ -40,11 +40,11 @@ interface RewriteReferencesVisitorState { interface RewriteBindingInitVisitorState { exported: Map; metadata: ModuleMetadata; - requeueInParent: (path) => void; + requeueInParent: (path: NodePath) => void; scope: Scope; } -function isInType(path) { +function isInType(path: NodePath) { do { switch (path.parent.type) { case "TSTypeAnnotation": @@ -54,7 +54,13 @@ function isInType(path) { case "TypeAlias": return true; case "ExportSpecifier": - return path.parentPath.parent.exportKind === "type"; + return ( + ( + path.parentPath.parent as + | t.ExportDefaultDeclaration + | t.ExportNamedDeclaration + ).exportKind === "type" + ); default: if (path.parentPath.isStatement() || path.parentPath.isExpression()) { return false; @@ -69,7 +75,7 @@ export default function rewriteLiveReferences( ) { const imported = new Map(); const exported = new Map(); - const requeueInParent = path => { + const requeueInParent = (path: NodePath) => { // Manually re-queue `exports.default =` expressions so that the ES3 // transform has an opportunity to convert them. Ideally this would // happen automatically from the replaceWith above. See #4140 for @@ -127,7 +133,13 @@ export default function rewriteLiveReferences( const meta = metadata.source.get(source); if (localName) { - if (meta.lazy) identNode = callExpression(identNode, []); + if (meta.lazy) { + identNode = callExpression( + // @ts-expect-error Fixme: we should handle the case when identNode is a JSXIdentifier + identNode, + [], + ); + } return identNode; } @@ -203,9 +215,9 @@ const rewriteBindingInitVisitor: Visitor = { }; const buildBindingExportAssignmentExpression = ( - metadata, - exportNames, - localExpr, + metadata: ModuleMetadata, + exportNames: string[], + localExpr: t.Expression, ) => { return (exportNames || []).reduce((expr, exportName) => { // class Foo {} export { Foo, Foo as Bar }; @@ -225,7 +237,7 @@ const buildBindingExportAssignmentExpression = ( }, localExpr); }; -const buildImportThrow = localName => { +const buildImportThrow = (localName: string) => { return template.expression.ast` (function() { throw new Error('"' + '${localName}' + '" is read-only.'); @@ -403,7 +415,7 @@ const rewriteReferencesVisitor: Visitor = { const assignment = path.node; if (importData) { - assignment.left = buildImportReference(importData, assignment.left); + assignment.left = buildImportReference(importData, left.node); assignment.right = sequenceExpression([ assignment.right, @@ -437,7 +449,7 @@ const rewriteReferencesVisitor: Visitor = { // Complex ({a, b, c} = {}); export { a, c }; // => ({a, b, c} = {}), (exports.a = a, exports.c = c); - const items = []; + const items: t.Expression[] = []; programScopeIds.forEach(localName => { const exportedNames = exported.get(localName) || []; if (exportedNames.length > 0) { diff --git a/packages/babel-helper-module-transforms/src/rewrite-this.ts b/packages/babel-helper-module-transforms/src/rewrite-this.ts index 92d9dd233968..cfd6aa9c24a2 100644 --- a/packages/babel-helper-module-transforms/src/rewrite-this.ts +++ b/packages/babel-helper-module-transforms/src/rewrite-this.ts @@ -1,7 +1,6 @@ import environmentVisitor from "@babel/helper-environment-visitor"; import traverse from "@babel/traverse"; import { numericLiteral, unaryExpression } from "@babel/types"; -import type * as t from "@babel/types"; import type { NodePath, Visitor } from "@babel/traverse"; export default function rewriteThis(programPath: NodePath) { @@ -16,7 +15,7 @@ export default function rewriteThis(programPath: NodePath) { const rewriteThisVisitor: Visitor = traverse.visitors.merge([ environmentVisitor, { - ThisExpression(path: NodePath) { + ThisExpression(path) { path.replaceWith(unaryExpression("void", numericLiteral(0), true)); }, }, diff --git a/packages/babel-plugin-transform-modules-amd/src/index.ts b/packages/babel-plugin-transform-modules-amd/src/index.ts index a22064354549..04e20d9c779c 100644 --- a/packages/babel-plugin-transform-modules-amd/src/index.ts +++ b/packages/babel-plugin-transform-modules-amd/src/index.ts @@ -2,6 +2,7 @@ import { declare } from "@babel/helper-plugin-utils"; import { isModule, rewriteModuleStatementsAndPrepareHeader, + type RewriteModuleStatementsAndPrepareHeaderOptions, hasExports, isSideEffectImport, buildNamespaceInitStatements, @@ -12,33 +13,41 @@ import { import { template, types as t } from "@babel/core"; import { getImportSource } from "babel-plugin-dynamic-import-node/utils"; import type { PluginOptions } from "@babel/helper-module-transforms"; +import type { NodePath } from "@babel/traverse"; -const buildWrapper = template(` +const buildWrapper = template.statement(` define(MODULE_NAME, AMD_ARGUMENTS, function(IMPORT_NAMES) { }) `); -const buildAnonymousWrapper = template(` +const buildAnonymousWrapper = template.statement(` define(["require"], function(REQUIRE) { }) `); -function injectWrapper(path, wrapper) { +function injectWrapper( + path: NodePath, + wrapper: t.ExpressionStatement, +) { const { body, directives } = path.node; path.node.directives = []; path.node.body = []; - const amdWrapper = path.pushContainer("body", wrapper)[0]; - const amdFactory = amdWrapper - .get("expression.arguments") - .filter(arg => arg.isFunctionExpression())[0] - .get("body"); + const amdFactoryCall = ( + path.pushContainer("body", wrapper)[0] as NodePath + ).get("expression") as NodePath; + const amdFactoryCallArgs = amdFactoryCall.get("arguments"); + const amdFactory = ( + amdFactoryCallArgs[ + amdFactoryCallArgs.length - 1 + ] as NodePath + ).get("body") as NodePath; amdFactory.pushContainer("directives", directives); amdFactory.pushContainer("body", body); } export interface Options extends PluginOptions { allowTopLevelThis?: boolean; - importInterop?: "babel" | "node"; + importInterop?: RewriteModuleStatementsAndPrepareHeaderOptions["importInterop"]; loose?: boolean; noInterop?: boolean; strict?: boolean; @@ -107,7 +116,9 @@ export default declare((api, options: Options) => { if (requireId) { injectWrapper( path, - buildAnonymousWrapper({ REQUIRE: t.cloneNode(requireId) }), + buildAnonymousWrapper({ + REQUIRE: t.cloneNode(requireId), + }) as t.ExpressionStatement, ); } return; @@ -186,7 +197,7 @@ export default declare((api, options: Options) => { AMD_ARGUMENTS: t.arrayExpression(amdArgs), IMPORT_NAMES: importNames, - }), + }) as t.ExpressionStatement, ); }, }, diff --git a/packages/babel-plugin-transform-modules-commonjs/src/index.ts b/packages/babel-plugin-transform-modules-commonjs/src/index.ts index e3de86a846b6..d15f31c4e0a6 100644 --- a/packages/babel-plugin-transform-modules-commonjs/src/index.ts +++ b/packages/babel-plugin-transform-modules-commonjs/src/index.ts @@ -2,6 +2,7 @@ import { declare } from "@babel/helper-plugin-utils"; import { isModule, rewriteModuleStatementsAndPrepareHeader, + type RewriteModuleStatementsAndPrepareHeaderOptions, isSideEffectImport, buildNamespaceInitStatements, ensureStatementsHoisted, @@ -18,8 +19,8 @@ import { createDynamicImportTransform } from "babel-plugin-dynamic-import-node/u export interface Options extends PluginOptions { allowCommonJSExports?: boolean; allowTopLevelThis?: boolean; - importInterop?: "babel" | "node"; - lazy?: boolean | string[] | ((string) => boolean); + importInterop?: RewriteModuleStatementsAndPrepareHeaderOptions["importInterop"]; + lazy?: RewriteModuleStatementsAndPrepareHeaderOptions["lazy"]; loose?: boolean; mjsStrictNamespace?: boolean; noInterop?: boolean; @@ -74,7 +75,7 @@ export default declare((api, options: Options) => { throw new Error(`.mjsStrictNamespace must be a boolean, or undefined`); } - const getAssertion = localName => template.expression.ast` + const getAssertion = (localName: string) => template.expression.ast` (function(){ throw new Error( "The CommonJS '" + "${localName}" + "' variable is not available in ES6 modules." + @@ -236,7 +237,7 @@ export default declare((api, options: Options) => { t.stringLiteral(source), ]); - let header; + let header: t.Statement; if (isSideEffectImport(metadata)) { if (metadata.lazy) throw new Error("Assertion failure"); @@ -246,7 +247,7 @@ export default declare((api, options: Options) => { wrapInterop(path, loadExpr, metadata.interop) || loadExpr; if (metadata.lazy) { - header = template.ast` + header = template.statement.ast` function ${metadata.name}() { const data = ${init}; ${metadata.name} = function(){ return data; }; @@ -254,7 +255,7 @@ export default declare((api, options: Options) => { } `; } else { - header = template.ast` + header = template.statement.ast` var ${metadata.name} = ${init}; `; } diff --git a/packages/babel-plugin-transform-modules-systemjs/src/index.ts b/packages/babel-plugin-transform-modules-systemjs/src/index.ts index 30832c80f833..1453fb694540 100644 --- a/packages/babel-plugin-transform-modules-systemjs/src/index.ts +++ b/packages/babel-plugin-transform-modules-systemjs/src/index.ts @@ -72,12 +72,18 @@ type PluginState = { stringSpecifiers: Set; }; +type ModuleMetadata = { + key: string; + imports: any[]; + exports: any[]; +}; + function constructExportCall( - path, - exportIdent, - exportNames, - exportValues, - exportStarTarget, + path: NodePath, + exportIdent: t.Identifier, + exportNames: string[], + exportValues: t.Expression[], + exportStarTarget: t.Identifier | null, stringSpecifiers: Set, ) { const statements = []; @@ -312,27 +318,31 @@ export default declare((api, options: Options) => { rewriteThis(path); } }, - exit(path: NodePath, state: PluginState) { + exit(path, state) { const scope = path.scope; const exportIdent = scope.generateUid("export"); const { contextIdent, stringSpecifiers } = state; - const exportMap = Object.create(null); - const modules = []; + const exportMap: Record = Object.create(null); + const modules: ModuleMetadata[] = []; const beforeBody = []; - const setters = []; - const sources = []; + const setters: t.Expression[] = []; + const sources: t.StringLiteral[] = []; const variableIds = []; const removedPaths = []; - function addExportName(key, val) { + function addExportName(key: string, val: string) { exportMap[key] = exportMap[key] || []; exportMap[key].push(val); } - function pushModule(source, key, specifiers) { - let module; + function pushModule( + source: string, + key: "imports" | "exports", + specifiers: t.ModuleSpecifier[] | t.ExportAllDeclaration, + ) { + let module: ModuleMetadata; modules.forEach(function (m) { if (m.key === source) { module = m; @@ -346,7 +356,7 @@ export default declare((api, options: Options) => { module[key] = module[key].concat(specifiers); } - function buildExportCall(name, val) { + function buildExportCall(name: string, val: t.Expression) { return t.expressionStatement( t.callExpression(t.identifier(exportIdent), [ t.stringLiteral(name), @@ -356,7 +366,7 @@ export default declare((api, options: Options) => { } const exportNames = []; - const exportValues = []; + const exportValues: t.Expression[] = []; const body = path.get("body"); @@ -391,9 +401,9 @@ export default declare((api, options: Options) => { pushModule(path.node.source.value, "exports", path.node); path.remove(); } else if (path.isExportDefaultDeclaration()) { - const declar = path.get("declaration"); - if (declar.isClassDeclaration()) { - const id = declar.node.id; + const declar = path.node.declaration; + if (t.isClassDeclaration(declar)) { + const id = declar.id; if (id) { exportNames.push("default"); exportValues.push(scope.buildUndefinedNode()); @@ -404,67 +414,66 @@ export default declare((api, options: Options) => { t.assignmentExpression( "=", t.cloneNode(id), - t.toExpression(declar.node), + t.toExpression(declar), ), ), ); } else { exportNames.push("default"); - exportValues.push(t.toExpression(declar.node)); + exportValues.push(t.toExpression(declar)); removedPaths.push(path); } - } else if (declar.isFunctionDeclaration()) { - const id = declar.node.id; + } else if (t.isFunctionDeclaration(declar)) { + const id = declar.id; if (id) { - beforeBody.push(declar.node); + beforeBody.push(declar); exportNames.push("default"); exportValues.push(t.cloneNode(id)); addExportName(id.name, "default"); } else { exportNames.push("default"); - exportValues.push(t.toExpression(declar.node)); + exportValues.push(t.toExpression(declar)); } removedPaths.push(path); } else { - path.replaceWith(buildExportCall("default", declar.node)); + path.replaceWith(buildExportCall("default", declar)); } } else if (path.isExportNamedDeclaration()) { - const declar = path.get("declaration"); + const declar = path.node.declaration; - if (declar.node) { + if (declar) { path.replaceWith(declar); - if (declar.isFunction()) { - const node = declar.node; - const name = node.id.name; + if (t.isFunction(declar)) { + const name = declar.id.name; addExportName(name, name); - beforeBody.push(node); + beforeBody.push(declar); exportNames.push(name); - exportValues.push(t.cloneNode(node.id)); + exportValues.push(t.cloneNode(declar.id)); removedPaths.push(path); - } else if (declar.isClass()) { - const name = declar.node.id.name; + } else if (t.isClass(declar)) { + const name = declar.id.name; exportNames.push(name); exportValues.push(scope.buildUndefinedNode()); - variableIds.push(t.cloneNode(declar.node.id)); + variableIds.push(t.cloneNode(declar.id)); path.replaceWith( t.expressionStatement( t.assignmentExpression( "=", - t.cloneNode(declar.node.id), - t.toExpression(declar.node), + t.cloneNode(declar.id), + t.toExpression(declar), ), ), ); addExportName(name, name); } else { - if (declar.isVariableDeclaration()) { + if (t.isVariableDeclaration(declar)) { // Convert top-level variable declarations to "var", // because they must be hoisted - declar.node.kind = "var"; + declar.kind = "var"; } for (const name of Object.keys( - declar.getBindingIdentifiers(), + t.getBindingIdentifiers(declar), )) { addExportName(name, name); } diff --git a/packages/babel-plugin-transform-modules-umd/src/index.ts b/packages/babel-plugin-transform-modules-umd/src/index.ts index 955e23ac9ee4..072987e16db0 100644 --- a/packages/babel-plugin-transform-modules-umd/src/index.ts +++ b/packages/babel-plugin-transform-modules-umd/src/index.ts @@ -3,6 +3,7 @@ import { basename, extname } from "path"; import { isModule, rewriteModuleStatementsAndPrepareHeader, + type RewriteModuleStatementsAndPrepareHeaderOptions, hasExports, isSideEffectImport, buildNamespaceInitStatements, @@ -44,7 +45,7 @@ export interface Options extends PluginOptions { allowTopLevelThis?: boolean; exactGlobals?: boolean; globals?: Record; - importInterop?: "babel" | "node"; + importInterop?: RewriteModuleStatementsAndPrepareHeaderOptions["importInterop"]; loose?: boolean; noInterop?: boolean; strict?: boolean; @@ -73,10 +74,10 @@ export default declare((api, options: Options) => { * Build the assignment statements that initialize the UMD global. */ function buildBrowserInit( - browserGlobals, - exactGlobals, - filename, - moduleName, + browserGlobals: Record, + exactGlobals: boolean, + filename: string, + moduleName: t.StringLiteral | void, ) { const moduleNameOrBasename = moduleName ? moduleName.value @@ -121,17 +122,22 @@ export default declare((api, options: Options) => { /** * Build the member expression that reads from a global for a given source. */ - function buildBrowserArg(browserGlobals, exactGlobals, source) { - let memberExpression; + function buildBrowserArg( + browserGlobals: Record, + exactGlobals: boolean, + source: string, + ) { + let memberExpression: t.MemberExpression; if (exactGlobals) { const globalRef = browserGlobals[source]; if (globalRef) { memberExpression = globalRef .split(".") .reduce( - (accum, curr) => t.memberExpression(accum, t.identifier(curr)), + (accum: t.Identifier | t.MemberExpression, curr) => + t.memberExpression(accum, t.identifier(curr)), t.identifier("global"), - ); + ) as t.MemberExpression; } else { memberExpression = t.memberExpression( t.identifier("global"), @@ -159,9 +165,9 @@ export default declare((api, options: Options) => { const browserGlobals = globals || {}; - let moduleName = getModuleName(this.file.opts, options); - // @ts-expect-error todo(flow->ts): do not reuse variables - if (moduleName) moduleName = t.stringLiteral(moduleName); + const moduleName = getModuleName(this.file.opts, options); + let moduleNameLiteral: void | t.StringLiteral; + if (moduleName) moduleNameLiteral = t.stringLiteral(moduleName); const { meta, headers } = rewriteModuleStatementsAndPrepareHeader( path, @@ -240,7 +246,8 @@ export default declare((api, options: Options) => { path.node.body = []; const umdWrapper = path.pushContainer("body", [ buildWrapper({ - MODULE_NAME: moduleName, + //todo: buildWrapper does not handle void moduleNameLiteral + MODULE_NAME: moduleNameLiteral, AMD_ARGUMENTS: t.arrayExpression(amdArgs), COMMONJS_ARGUMENTS: commonjsArgs, @@ -251,7 +258,7 @@ export default declare((api, options: Options) => { browserGlobals, exactGlobals, this.filename || "unknown", - moduleName, + moduleNameLiteral, ), }) as t.Statement, ])[0] as NodePath; diff --git a/packages/babel-traverse/src/path/family.ts b/packages/babel-traverse/src/path/family.ts index d50bacf908e9..e292e03e9d5a 100644 --- a/packages/babel-traverse/src/path/family.ts +++ b/packages/babel-traverse/src/path/family.ts @@ -11,8 +11,8 @@ import { } from "@babel/types"; import type * as t from "@babel/types"; -const NORMAL_COMPLETION = 0; -const BREAK_COMPLETION = 1; +const NORMAL_COMPLETION = 0 as const; +const BREAK_COMPLETION = 1 as const; type Completion = { path: NodePath; @@ -65,7 +65,7 @@ function completionRecordForSwitch( context: CompletionContext, ): Completion[] { // https://tc39.es/ecma262/#sec-runtime-semantics-caseblockevaluation - let lastNormalCompletions = []; + let lastNormalCompletions: Completion[] = []; for (let i = 0; i < cases.length; i++) { const casePath = cases[i]; const caseCompletions = _getCompletionRecords(casePath, context); @@ -227,7 +227,7 @@ function _getCompletionRecords( path: NodePath, context: CompletionContext, ): Completion[] { - let records = []; + let records: Completion[] = []; if (path.isIfStatement()) { records = addCompletionRecords(path.get("consequent"), records, context); records = addCompletionRecords(path.get("alternate"), records, context); @@ -413,7 +413,9 @@ export function _getKey( context?: TraversalContext, ): NodePath | NodePath[] { const node = this.node; - const container = node[key]; + const container = + // @ts-ignore + node[key]; if (Array.isArray(container)) { // requested a container so give them all the paths @@ -448,6 +450,7 @@ export function _getPattern( path = path.parentPath; } else { if (Array.isArray(path)) { + // @ts-ignore path = path[part]; } else { path = path.get(part, context); @@ -503,7 +506,8 @@ export function getBindingIdentifierPaths( duplicates: boolean = false, outerOnly: boolean = false, ): { - [x: string]: NodePath; + // todo: returns NodePath[] when duplicates is true + [x: string]: NodePath; } { const path = this; const search = [path]; @@ -514,7 +518,9 @@ export function getBindingIdentifierPaths( if (!id) continue; if (!id.node) continue; - const keys = _getBindingIdentifiers.keys[id.node.type]; + const keys = + // @ts-ignore + _getBindingIdentifiers.keys[id.node.type]; if (id.isIdentifier()) { if (duplicates) { @@ -564,8 +570,6 @@ export function getBindingIdentifierPaths( export function getOuterBindingIdentifierPaths( this: NodePath, duplicates?: boolean, -): { - [x: string]: NodePath; -} { +) { return this.getBindingIdentifierPaths(duplicates, true); } From 88378e2d53d40fa998913d5bee3c1ae7ec87e982 Mon Sep 17 00:00:00 2001 From: liuxingbaoyu <30521560+liuxingbaoyu@users.noreply.github.com> Date: Tue, 31 May 2022 07:05:39 +0800 Subject: [PATCH 07/22] improve babel-template typings (#14621) --- packages/babel-template/src/builder.ts | 6 +++--- packages/babel-template/src/literal.ts | 2 +- packages/babel-template/src/parse.ts | 10 +++++----- packages/babel-template/src/string.ts | 3 ++- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/babel-template/src/builder.ts b/packages/babel-template/src/builder.ts index 7040f81595e2..e3acc3878da4 100644 --- a/packages/babel-template/src/builder.ts +++ b/packages/babel-template/src/builder.ts @@ -13,7 +13,7 @@ export type TemplateBuilder = { (tpl: string, opts?: PublicOpts): (replacements?: PublicReplacements) => T; // Building from a template literal produces an AST builder function by default. - (tpl: TemplateStringsArray, ...args: Array): ( + (tpl: TemplateStringsArray, ...args: Array): ( replacements?: PublicReplacements, ) => T; @@ -21,7 +21,7 @@ export type TemplateBuilder = { // the need for an intermediate function. ast: { (tpl: string, opts?: PublicOpts): T; - (tpl: TemplateStringsArray, ...args: Array): T; + (tpl: TemplateStringsArray, ...args: Array): T; }; }; @@ -66,7 +66,7 @@ export default function createTemplateBuilder( throw new Error(`Unexpected template param ${typeof tpl}`); }) as TemplateBuilder, { - ast: (tpl, ...args) => { + ast: (tpl: string | Array, ...args: Array) => { if (typeof tpl === "string") { if (args.length > 1) throw new Error("Unexpected extra params."); return stringTemplate( diff --git a/packages/babel-template/src/literal.ts b/packages/babel-template/src/literal.ts index c436edcbd8db..c6e79a1fcc8f 100644 --- a/packages/babel-template/src/literal.ts +++ b/packages/babel-template/src/literal.ts @@ -46,7 +46,7 @@ function buildLiteralData( opts: TemplateOpts, ) { let names; - let nameSet; + let nameSet: Set; let metadata; let prefix = ""; diff --git a/packages/babel-template/src/parse.ts b/packages/babel-template/src/parse.ts index e57d5b87a273..2fd61025ac2f 100644 --- a/packages/babel-template/src/parse.ts +++ b/packages/babel-template/src/parse.ts @@ -54,15 +54,15 @@ export default function parseAndBuildMetadata( formatter.validate(ast); - const syntactic = { + const syntactic: MetadataState["syntactic"] = { placeholders: [], - placeholderNames: new Set(), + placeholderNames: new Set(), }; - const legacy = { + const legacy: MetadataState["legacy"] = { placeholders: [], - placeholderNames: new Set(), + placeholderNames: new Set(), }; - const isLegacyRef = { value: undefined }; + const isLegacyRef: MetadataState["isLegacyRef"] = { value: undefined }; traverse(ast, placeholderVisitorHandler as TraversalHandler, { syntactic, diff --git a/packages/babel-template/src/string.ts b/packages/babel-template/src/string.ts index 1f8ad4c9f783..10f26fb64ebe 100644 --- a/packages/babel-template/src/string.ts +++ b/packages/babel-template/src/string.ts @@ -1,5 +1,6 @@ import type { Formatter } from "./formatters"; import type { TemplateOpts } from "./options"; +import type { Metadata } from "./parse"; import { normalizeReplacements } from "./options"; import parseAndBuildMetadata from "./parse"; import populatePlaceholders from "./populate"; @@ -11,7 +12,7 @@ export default function stringTemplate( ): (arg?: unknown) => T { code = formatter.code(code); - let metadata; + let metadata: Metadata; return (arg?: unknown) => { const replacements = normalizeReplacements(arg); From 2ea5c459fd5429a6c314b43871d1fcfe244ee295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Mon, 30 May 2022 21:16:03 -0400 Subject: [PATCH 08/22] Improve @babel/helper-* typings (#14620) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improve helper-function-name typings * helper hoist variables * helper-member-expression-to-functions * helper-simple-access * remap-async-to-generator * helper-annotate-as-pure * helper-builder-binary-assignment-operator-visitor * helper-check-duplicate-nodes * early return when export declaration is export all * split-export-declaration * helper-define-map * define-map * Update packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts Co-authored-by: Nicolò Ribaudo * review comments Co-authored-by: Nicolò Ribaudo --- .../src/index.ts | 12 ++--- .../src/index.ts | 3 +- .../src/index.ts | 15 ++++-- packages/babel-helper-define-map/src/index.ts | 36 +++++++++++-- .../babel-helper-function-name/src/index.ts | 52 ++++++++++++++----- .../babel-helper-hoist-variables/src/index.ts | 10 ++-- .../src/index.ts | 22 +++++--- .../src/index.ts | 9 ++-- .../babel-helper-simple-access/src/index.ts | 2 +- .../src/index.ts | 40 +++++++++----- .../src/index.ts | 4 +- packages/babel-traverse/src/scope/index.ts | 9 ++-- 12 files changed, 149 insertions(+), 65 deletions(-) diff --git a/packages/babel-helper-annotate-as-pure/src/index.ts b/packages/babel-helper-annotate-as-pure/src/index.ts index e2644da46e68..f6a92c4fb7ba 100644 --- a/packages/babel-helper-annotate-as-pure/src/index.ts +++ b/packages/babel-helper-annotate-as-pure/src/index.ts @@ -1,5 +1,5 @@ -import { addComment } from "@babel/types"; -import type { Node } from "@babel/types"; +import { addComment, type Node } from "@babel/types"; +import type { NodePath } from "@babel/traverse"; const PURE_ANNOTATION = "#__PURE__"; @@ -7,10 +7,10 @@ const isPureAnnotated = ({ leadingComments }: Node): boolean => !!leadingComments && leadingComments.some(comment => /[@#]__PURE__/.test(comment.value)); -export default function annotateAsPure( - pathOrNode: Node | { node: Node }, -): void { - const node = pathOrNode["node"] || pathOrNode; +export default function annotateAsPure(pathOrNode: Node | NodePath): void { + const node = + // @ts-expect-error Node will not have `node` property + (pathOrNode["node"] || pathOrNode) as Node; if (isPureAnnotated(node)) { return; } diff --git a/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts b/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts index c498d70369d7..7cc8513ac3ed 100644 --- a/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts +++ b/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts @@ -1,6 +1,7 @@ import explode from "@babel/helper-explode-assignable-expression"; import { assignmentExpression, sequenceExpression } from "@babel/types"; import type { Visitor } from "@babel/traverse"; +import type * as t from "@babel/types"; export default function (opts: { build: Function; operator: string }) { const { build, operator } = opts; @@ -10,7 +11,7 @@ export default function (opts: { build: Function; operator: string }) { const { node, scope } = path; if (node.operator !== operator + "=") return; - const nodes = []; + const nodes: t.AssignmentExpression[] = []; // @ts-expect-error todo(flow->ts) const exploded = explode(node.left, nodes, this, scope); nodes.push( diff --git a/packages/babel-helper-check-duplicate-nodes/src/index.ts b/packages/babel-helper-check-duplicate-nodes/src/index.ts index 949c5c2a7871..2f44f4215c79 100644 --- a/packages/babel-helper-check-duplicate-nodes/src/index.ts +++ b/packages/babel-helper-check-duplicate-nodes/src/index.ts @@ -1,20 +1,25 @@ import { VISITOR_KEYS } from "@babel/types"; +import type * as t from "@babel/types"; -export default function checkDuplicateNodes(ast) { +type StackItem = { + node: t.Node; + parent: t.Node | null; +}; +export default function checkDuplicateNodes(ast: t.Node) { if (arguments.length !== 1) { throw new Error("checkDuplicateNodes accepts only one argument: ast"); } // A Map from node to its parent const parentsMap = new Map(); - const hidePrivateProperties = (key, val) => { + const hidePrivateProperties = (key: string, val: unknown) => { // Hides properties like _shadowedFunctionLiteral, // which makes the AST circular if (key[0] === "_") return "[Private]"; return val; }; - const stack = [{ node: ast, parent: null }]; + const stack: StackItem[] = [{ node: ast, parent: null }]; let item; while ((item = stack.pop()) !== undefined) { @@ -36,7 +41,9 @@ export default function checkDuplicateNodes(ast) { parentsMap.set(node, parent); for (const key of keys) { - const subNode = node[key]; + const subNode = + // @ts-expect-error visitor keys must present in node + node[key]; if (Array.isArray(subNode)) { for (const child of subNode) { diff --git a/packages/babel-helper-define-map/src/index.ts b/packages/babel-helper-define-map/src/index.ts index 0653c7991744..c303e022632f 100644 --- a/packages/babel-helper-define-map/src/index.ts +++ b/packages/babel-helper-define-map/src/index.ts @@ -17,8 +17,11 @@ import { toComputedKey, toKeyAlias, } from "@babel/types"; +import type { File } from "@babel/core"; +import type * as t from "@babel/types"; +import type { Scope } from "@babel/traverse"; -function toKind(node: any) { +function toKind(node: t.Property | t.Method) { if (isClassMethod(node) || isObjectMethod(node)) { if (node.kind === "get" || node.kind === "set") { return node.kind; @@ -30,12 +33,32 @@ function toKind(node: any) { const has = Function.prototype.call.bind(Object.prototype.hasOwnProperty); -export function push(mutatorMap: any, node: any, kind: string, file, scope?) { +type DefineMap = { + decorators: t.ArrayExpression; + _computed: boolean; + _inherits: t.Node[]; + _key: t.Expression | t.PrivateName; + value?: t.Expression; + initializer?: t.Expression; + get?: t.Expression; + set?: t.Expression; + kind: "get" | "set" | "value" | "initializer"; +}; + +export type MutatorMap = Record; + +export function push( + mutatorMap: MutatorMap, + node: t.Property | t.Method, + kind: DefineMap["kind"], + file: File, + scope?: Scope, +) { const alias = toKeyAlias(node); // - let map = {} as any; + let map = {} as DefineMap; if (has(mutatorMap, alias)) map = mutatorMap[alias]; mutatorMap[alias] = map; @@ -46,7 +69,10 @@ export function push(mutatorMap: any, node: any, kind: string, file, scope?) { map._key = node.key; - if (node.computed) { + if ( + // @ts-expect-error computed is not in private property + node.computed + ) { map._computed = true; } @@ -69,7 +95,7 @@ export function push(mutatorMap: any, node: any, kind: string, file, scope?) { } if (isProperty(node)) { - value = node.value; + value = node.value as t.Expression; } else if (isObjectMethod(node) || isClassMethod(node)) { value = functionExpression( null, diff --git a/packages/babel-helper-function-name/src/index.ts b/packages/babel-helper-function-name/src/index.ts index fc157b28be35..623b541e4ad1 100644 --- a/packages/babel-helper-function-name/src/index.ts +++ b/packages/babel-helper-function-name/src/index.ts @@ -18,6 +18,7 @@ import { toBindingIdentifierName, } from "@babel/types"; import type * as t from "@babel/types"; +import type { NodePath, Scope, Visitor } from "@babel/traverse"; function getFunctionArity(node: t.Function): number { const count = node.params.findIndex( @@ -26,7 +27,7 @@ function getFunctionArity(node: t.Function): number { return count === -1 ? node.params.length : count; } -const buildPropertyMethodAssignmentWrapper = template(` +const buildPropertyMethodAssignmentWrapper = template.statement(` (function (FUNCTION_KEY) { function FUNCTION_ID() { return FUNCTION_KEY.apply(this, arguments); @@ -40,7 +41,7 @@ const buildPropertyMethodAssignmentWrapper = template(` })(FUNCTION) `); -const buildGeneratorPropertyMethodAssignmentWrapper = template(` +const buildGeneratorPropertyMethodAssignmentWrapper = template.statement(` (function (FUNCTION_KEY) { function* FUNCTION_ID() { return yield* FUNCTION_KEY.apply(this, arguments); @@ -54,8 +55,18 @@ const buildGeneratorPropertyMethodAssignmentWrapper = template(` })(FUNCTION) `); -const visitor = { - "ReferencedIdentifier|BindingIdentifier"(path, state) { +type State = { + name: string; + outerDeclar: t.Identifier; + selfAssignment: boolean; + selfReference: boolean; +}; + +const visitor: Visitor = { + "ReferencedIdentifier|BindingIdentifier"( + path: NodePath, + state, + ) { // check if this node matches our function id if (path.node.name !== state.name) return; @@ -69,7 +80,7 @@ const visitor = { }, }; -function getNameFromLiteralId(id) { +function getNameFromLiteralId(id: t.Literal) { if (isNullLiteral(id)) { return "null"; } @@ -89,7 +100,12 @@ function getNameFromLiteralId(id) { return ""; } -function wrap(state, method, id, scope) { +function wrap( + state: State, + method: t.FunctionExpression | t.ClassExpression, + id: t.Identifier, + scope: Scope, +) { if (state.selfReference) { if (scope.hasBinding(id.name) && !scope.hasGlobal(id.name)) { // we can just munge the local binding @@ -131,12 +147,15 @@ function wrap(state, method, id, scope) { scope.getProgramParent().references[id.name] = true; } -function visit(node, name, scope) { - const state = { +function visit( + node: t.FunctionExpression | t.ClassExpression, + name: string, + scope: Scope, +) { + const state: State = { selfAssignment: false, selfReference: false, outerDeclar: scope.getBindingIdentifier(name), - references: [], name: name, }; @@ -188,7 +207,12 @@ export default function ( parent, scope, id, - }: { node: any; parent?: any; scope: any; id?: any }, + }: { + node: t.FunctionExpression | t.ClassExpression; + parent?: t.Node; + scope: Scope; + id?: any; + }, localBinding = false, supportUnicodeId = false, ) { @@ -215,6 +239,7 @@ export default function ( ) { // always going to reference this method node.id = cloneNode(id); + // @ts-expect-error Fixme: avoid mutating AST nodes node.id[NOT_LOCAL_BINDING] = true; return; } @@ -242,13 +267,14 @@ export default function ( } name = toBindingIdentifierName(name); - id = identifier(name); + const newId = identifier(name); // The id shouldn't be considered a local binding to the function because // we are simply trying to set the function name and not actually create // a local binding. - id[NOT_LOCAL_BINDING] = true; + // @ts-ignore Fixme: avoid mutating AST nodes + newId[NOT_LOCAL_BINDING] = true; const state = visit(node, name, scope); - return wrap(state, node, id, scope) || node; + return wrap(state, node, newId, scope) || node; } diff --git a/packages/babel-helper-hoist-variables/src/index.ts b/packages/babel-helper-hoist-variables/src/index.ts index 1cb3b8a3c4c5..89653ac33af8 100644 --- a/packages/babel-helper-hoist-variables/src/index.ts +++ b/packages/babel-helper-hoist-variables/src/index.ts @@ -4,7 +4,7 @@ import { identifier, } from "@babel/types"; import type * as t from "@babel/types"; -import type { NodePath } from "@babel/traverse"; +import type { NodePath, Visitor } from "@babel/traverse"; export type EmitFunction = ( id: t.Identifier, @@ -19,16 +19,16 @@ type State = { type Unpacked = T extends (infer U)[] ? U : T; -const visitor = { - Scope(path: NodePath, state: State) { +const visitor: Visitor = { + Scope(path, state) { if (state.kind === "let") path.skip(); }, - FunctionParent(path: NodePath) { + FunctionParent(path) { path.skip(); }, - VariableDeclaration(path: NodePath, state: State) { + VariableDeclaration(path, state) { if (state.kind && path.node.kind !== state.kind) return; const nodes = []; diff --git a/packages/babel-helper-member-expression-to-functions/src/index.ts b/packages/babel-helper-member-expression-to-functions/src/index.ts index fd629d54de30..3b8867711a50 100644 --- a/packages/babel-helper-member-expression-to-functions/src/index.ts +++ b/packages/babel-helper-member-expression-to-functions/src/index.ts @@ -95,7 +95,13 @@ function isInDetachedTree(path: NodePath) { const { parentPath, container, listKey } = path; const parentNode = parentPath.node; if (listKey) { - if (container !== parentNode[listKey]) return true; + if ( + container !== + // @ts-expect-error listKey must be a valid parent node key + parentNode[listKey] + ) { + return true; + } } else { if (container !== parentNode) return true; } @@ -208,10 +214,9 @@ const handle = { ); } - const startingProp = startingOptional.isOptionalMemberExpression() - ? "object" - : "callee"; - const startingNode = startingOptional.node[startingProp]; + const startingNode = startingOptional.isOptionalMemberExpression() + ? startingOptional.node.object + : startingOptional.node.callee; const baseNeedsMemoised = scope.maybeGenerateMemoised(startingNode); const baseRef = baseNeedsMemoised ?? startingNode; @@ -281,7 +286,12 @@ const handle = { } const baseMemoised = baseNeedsMemoised - ? assignmentExpression("=", cloneNode(baseRef), cloneNode(startingNode)) + ? assignmentExpression( + "=", + // When base needs memoised, the baseRef must be an identifier + cloneNode(baseRef as t.Identifier), + cloneNode(startingNode), + ) : cloneNode(baseRef); if (willEndPathCastToBoolean) { diff --git a/packages/babel-helper-remap-async-to-generator/src/index.ts b/packages/babel-helper-remap-async-to-generator/src/index.ts index a4df21c8e60e..d15b95842791 100644 --- a/packages/babel-helper-remap-async-to-generator/src/index.ts +++ b/packages/babel-helper-remap-async-to-generator/src/index.ts @@ -1,6 +1,6 @@ /* @noflow */ -import type { NodePath } from "@babel/traverse"; +import type { NodePath, Visitor } from "@babel/traverse"; import wrapFunction from "@babel/helper-wrap-function"; import annotateAsPure from "@babel/helper-annotate-as-pure"; import { @@ -10,8 +10,9 @@ import { isThisExpression, yieldExpression, } from "@babel/types"; +import type * as t from "@babel/types"; -const awaitVisitor = { +const awaitVisitor: Visitor<{ wrapAwait: t.Expression }> = { Function(path) { path.skip(); }, @@ -32,8 +33,8 @@ const awaitVisitor = { export default function ( path: NodePath, helpers: { - wrapAsync: any; - wrapAwait?: any; + wrapAsync: t.Expression; + wrapAwait?: t.Expression; }, noNewArrows?: boolean, ignoreFunctionLength?: boolean, diff --git a/packages/babel-helper-simple-access/src/index.ts b/packages/babel-helper-simple-access/src/index.ts index 2b198edfd961..6e460c069b8b 100644 --- a/packages/babel-helper-simple-access/src/index.ts +++ b/packages/babel-helper-simple-access/src/index.ts @@ -20,7 +20,7 @@ type State = { }; export default function simplifyAccess( path: NodePath, - bindingNames, + bindingNames: Set, // TODO(Babel 8): Remove this includeUpdateExpression: boolean = true, ) { diff --git a/packages/babel-helper-split-export-declaration/src/index.ts b/packages/babel-helper-split-export-declaration/src/index.ts index 25ab91b6a9c1..5737527cece5 100644 --- a/packages/babel-helper-split-export-declaration/src/index.ts +++ b/packages/babel-helper-split-export-declaration/src/index.ts @@ -6,25 +6,33 @@ import { variableDeclaration, variableDeclarator, } from "@babel/types"; - -export default function splitExportDeclaration(exportDeclaration) { - if (!exportDeclaration.isExportDeclaration()) { - throw new Error("Only export declarations can be split."); +import type * as t from "@babel/types"; +import type { NodePath } from "@babel/traverse"; + +export default function splitExportDeclaration( + exportDeclaration: NodePath< + t.ExportDefaultDeclaration | t.ExportNamedDeclaration + >, +) { + if ( + !exportDeclaration.isExportDeclaration() || + exportDeclaration.isExportAllDeclaration() + ) { + throw new Error("Only default and named export declarations can be split."); } // build specifiers that point back to this export declaration - const isDefault = exportDeclaration.isExportDefaultDeclaration(); - const declaration = exportDeclaration.get("declaration"); - const isClassDeclaration = declaration.isClassDeclaration(); - if (isDefault) { + if (exportDeclaration.isExportDefaultDeclaration()) { + const declaration = exportDeclaration.get("declaration"); const standaloneDeclaration = - declaration.isFunctionDeclaration() || isClassDeclaration; + declaration.isFunctionDeclaration() || declaration.isClassDeclaration(); const scope = declaration.isScope() ? declaration.scope.parent : declaration.scope; + // @ts-expect-error id is not defined in expressions other than function/class let id = declaration.node.id; let needBindingRegistration = false; @@ -45,7 +53,11 @@ export default function splitExportDeclaration(exportDeclaration) { const updatedDeclaration = standaloneDeclaration ? declaration : variableDeclaration("var", [ - variableDeclarator(cloneNode(id), declaration.node), + variableDeclarator( + cloneNode(id), + // @ts-expect-error When `standaloneDeclaration` is false, declaration must not be a Function/ClassDeclaration + declaration.node, + ), ]); const updatedExportDeclaration = exportNamedDeclaration(null, [ @@ -60,12 +72,14 @@ export default function splitExportDeclaration(exportDeclaration) { } return exportDeclaration; - } - - if (exportDeclaration.get("specifiers").length > 0) { + } else if ( + // @ts-expect-error TS can not narrow down to NodePath + exportDeclaration.get("specifiers").length > 0 + ) { throw new Error("It doesn't make sense to split exported specifiers."); } + const declaration = exportDeclaration.get("declaration"); const bindingIdentifiers = declaration.getOuterBindingIdentifiers(); const specifiers = Object.keys(bindingIdentifiers).map(name => { diff --git a/packages/babel-plugin-transform-property-mutators/src/index.ts b/packages/babel-plugin-transform-property-mutators/src/index.ts index 8323bda0acfe..abfff41b10c1 100644 --- a/packages/babel-plugin-transform-property-mutators/src/index.ts +++ b/packages/babel-plugin-transform-property-mutators/src/index.ts @@ -9,9 +9,9 @@ export default declare(api => { name: "transform-property-mutators", visitor: { - ObjectExpression(path, file) { + ObjectExpression(path, { file }) { const { node } = path; - let mutatorMap; + let mutatorMap: defineMap.MutatorMap | void; const newProperties = node.properties.filter(function (prop) { if (t.isObjectMethod(prop)) { if (prop.kind === "get" || prop.kind === "set") { diff --git a/packages/babel-traverse/src/scope/index.ts b/packages/babel-traverse/src/scope/index.ts index 06504a5df426..1cfa5add854f 100644 --- a/packages/babel-traverse/src/scope/index.ts +++ b/packages/babel-traverse/src/scope/index.ts @@ -380,9 +380,9 @@ export default class Scope { inited; bindings: { [name: string]: Binding }; - references: object; - globals: object; - uids: object; + references: { [name: string]: true }; + globals: { [name: string]: t.Identifier | t.JSXIdentifier }; + uids: { [name: string]: true }; data: object; crawling: boolean; @@ -798,8 +798,7 @@ export default class Scope { } } - // todo: flow->ts maybe add more specific type - addGlobal(node: Extract) { + addGlobal(node: t.Identifier | t.JSXIdentifier) { this.globals[node.name] = node; } From a32418b2baffdecfd3a0a338f11883f588f765f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 31 May 2022 13:59:41 -0400 Subject: [PATCH 09/22] improve helper-module-imports typings (#14623) * refactor: simplify ImportInjector._applyDefaults * helper-module-imports * map globals to the one used in Babel 8 --- .../src/import-builder.ts | 45 ++++++------ .../src/import-injector.ts | 73 +++++++++++-------- .../babel-helper-module-imports/src/index.ts | 66 +++++++++++++++-- scripts/generators/tsconfig.js | 1 + tsconfig.json | 3 + 5 files changed, 130 insertions(+), 58 deletions(-) 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" ] } } From 7456f50478e80efe07135975a668f85ba582178d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Wed, 1 Jun 2022 08:57:28 -0400 Subject: [PATCH 10/22] Improve fixture-test-runner typings (#14625) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * plugin-test-runner * fixture-test-runner * Update packages/babel-helper-fixtures/src/index.ts Co-authored-by: Nicolò Ribaudo Co-authored-by: Nicolò Ribaudo --- packages/babel-core/src/index.ts | 7 +- packages/babel-core/src/transform.ts | 2 + .../babel-core/src/transformation/index.ts | 2 +- packages/babel-helper-fixtures/src/index.ts | 84 +++++++++++++------ .../src/index.ts | 2 +- .../src/helpers.ts | 4 +- .../src/index.ts | 63 +++++++++----- 7 files changed, 113 insertions(+), 51 deletions(-) diff --git a/packages/babel-core/src/index.ts b/packages/babel-core/src/index.ts index 81ea29c99e66..9698e3fd1a6f 100644 --- a/packages/babel-core/src/index.ts +++ b/packages/babel-core/src/index.ts @@ -40,7 +40,12 @@ export type { PresetObject, } from "./config"; -export { transform, transformSync, transformAsync } from "./transform"; +export { + transform, + transformSync, + transformAsync, + type FileResult, +} from "./transform"; export { transformFile, transformFileSync, diff --git a/packages/babel-core/src/transform.ts b/packages/babel-core/src/transform.ts index 668b20de5ab7..c44e62994a9d 100644 --- a/packages/babel-core/src/transform.ts +++ b/packages/babel-core/src/transform.ts @@ -6,6 +6,8 @@ import { run } from "./transformation"; import type { FileResult, FileResultCallback } from "./transformation"; +export type { FileResult } from "./transformation"; + type Transform = { (code: string, callback: FileResultCallback): void; ( diff --git a/packages/babel-core/src/transformation/index.ts b/packages/babel-core/src/transformation/index.ts index 4d83285e9735..b6d6875705b1 100644 --- a/packages/babel-core/src/transformation/index.ts +++ b/packages/babel-core/src/transformation/index.ts @@ -23,7 +23,7 @@ export type FileResultCallback = { export type FileResult = { metadata: {}; options: {}; - ast: {} | null; + ast: t.File | null; code: string | null; map: SourceMap | null; sourceType: "string" | "module"; diff --git a/packages/babel-helper-fixtures/src/index.ts b/packages/babel-helper-fixtures/src/index.ts index 8f3189881b0e..b30a2cf73e4d 100644 --- a/packages/babel-helper-fixtures/src/index.ts +++ b/packages/babel-helper-fixtures/src/index.ts @@ -3,56 +3,81 @@ import path from "path"; import fs from "fs"; import { fileURLToPath } from "url"; import { createRequire } from "module"; +import type { InputOptions } from "@babel/core"; +import type { EncodedSourceMap, Mapping } from "@jridgewell/gen-mapping"; const require = createRequire(import.meta.url); const nodeVersion = semver.clean(process.version.slice(1)); -function humanize(val, noext?) { +function humanize(val: string, noext?: boolean) { if (noext) val = path.basename(val, path.extname(val)); return val.replace(/-/g, " "); } -type TestFile = { +interface TestIO { loc: string; code: string; +} + +export interface TestFile extends TestIO { filename: string; -}; +} -type Test = { +export interface Test { + taskDir: string; title: string; disabled: boolean; - options: any; + options: TaskOptions; + optionsDir: string; + doNotSetSourceType: boolean; + externalHelpers: boolean; + ignoreOutput: boolean; + stdout: TestIO; + stderr: TestIO; exec: TestFile; actual: TestFile; - expected: TestFile; - // todo(flow->ts): improve types here - sourceMappings; - sourceMap; + expect: TestFile; + inputSourceMap?: EncodedSourceMap; + sourceMappings?: Mapping[]; + sourceMap: string; sourceMapFile: TestFile; -}; + validateLogs: boolean; +} + +interface TaskOptions extends InputOptions { + BABEL_8_BREAKING?: boolean; + DO_NOT_SET_SOURCE_TYPE?: boolean; + externalHelpers?: boolean; + ignoreOutput?: boolean; + minNodeVersion?: string; + sourceMap?: boolean; + os?: string | string[]; + validateLogs?: boolean; + throws?: boolean | string; +} type Suite = { - options: any; + options: TaskOptions; tests: Array; title: string; filename: string; }; -function tryResolve(module) { +function tryResolve(module: string) { try { return require.resolve(module); } catch (e) { return null; } } -function assertDirectory(loc) { +function assertDirectory(loc: string) { if (!fs.statSync(loc).isDirectory()) { throw new Error(`Expected ${loc} to be a directory.`); } } -function shouldIgnore(name, ignore?: Array) { +function shouldIgnore(name: string, ignore?: Array) { if (ignore && ignore.indexOf(name) >= 0) { return true; } @@ -87,7 +112,12 @@ function findFile(filepath: string, allowJSON?: boolean) { return matches[0]; } -function pushTask(taskName, taskDir, suite, suiteName) { +function pushTask( + taskName: string, + taskDir: string, + suite: Suite, + suiteName: string, +) { const taskDirStats = fs.statSync(taskDir); let actualLoc = findFile(taskDir + "/input"); let execLoc = findFile(taskDir + "/exec"); @@ -126,12 +156,12 @@ function pushTask(taskName, taskDir, suite, suiteName) { execLocAlias = suiteName + "/" + taskName; } - const taskOpts = JSON.parse(JSON.stringify(suite.options)); + const taskOpts: TaskOptions = JSON.parse(JSON.stringify(suite.options)); const taskOptsLoc = tryResolve(taskDir + "/options"); if (taskOptsLoc) Object.assign(taskOpts, require(taskOptsLoc)); - const test = { + const test: Test = { taskDir, optionsDir: taskOptsLoc ? path.dirname(taskOptsLoc) : null, title: humanize(taskName, true), @@ -264,7 +294,7 @@ function pushTask(taskName, taskDir, suite, suiteName) { (test.stdout.code ? stdoutLoc : stderrLoc), ); } - if (test.options.ignoreOutput) { + if (test.ignoreOutput) { if (test.expect.code) { throw new Error( "Test cannot ignore its output and also validate it: " + expectLoc, @@ -284,7 +314,11 @@ function pushTask(taskName, taskDir, suite, suiteName) { delete test.options.externalHelpers; } -function wrapPackagesArray(type, names, optionsDir) { +function wrapPackagesArray( + type: "plugin" | "preset", + names: (string | [string, object?, string?])[], + optionsDir: string, +) { return names.map(function (val) { if (typeof val === "string") val = [val]; @@ -362,10 +396,10 @@ export function resolveOptionPluginOrPreset( return options; } -export default function get(entryLoc): Array { +export default function get(entryLoc: string): Array { const suites = []; - let rootOpts = {}; + let rootOpts: TaskOptions = {}; const rootOptsLoc = tryResolve(entryLoc + "/options"); if (rootOptsLoc) rootOpts = require(rootOptsLoc); @@ -374,7 +408,7 @@ export default function get(entryLoc): Array { const suite = { options: { ...rootOpts }, - tests: [], + tests: [] as Test[], title: humanize(suiteName), filename: entryLoc + "/" + suiteName, }; @@ -398,8 +432,8 @@ export default function get(entryLoc): Array { return suites; } -export function multiple(entryLoc, ignore?: Array) { - const categories = {}; +export function multiple(entryLoc: string, ignore?: Array) { + const categories: Record = {}; for (const name of fs.readdirSync(entryLoc)) { if (shouldIgnore(name, ignore)) continue; @@ -413,7 +447,7 @@ export function multiple(entryLoc, ignore?: Array) { return categories; } -export function readFile(filename) { +export function readFile(filename: string) { if (fs.existsSync(filename)) { let file = fs.readFileSync(filename, "utf8").trimRight(); file = file.replace(/\r\n/g, "\n"); diff --git a/packages/babel-helper-plugin-test-runner/src/index.ts b/packages/babel-helper-plugin-test-runner/src/index.ts index 44e5a7d56f32..f5a43239e7a2 100644 --- a/packages/babel-helper-plugin-test-runner/src/index.ts +++ b/packages/babel-helper-plugin-test-runner/src/index.ts @@ -2,7 +2,7 @@ import testRunner from "@babel/helper-transform-fixture-test-runner"; import path from "path"; import { URL } from "url"; -export default function (loc) { +export default function (loc: string) { if (!process.env.BABEL_8_BREAKING) { if (!loc.startsWith("file://")) { const name = path.basename(path.dirname(loc)); diff --git a/packages/babel-helper-transform-fixture-test-runner/src/helpers.ts b/packages/babel-helper-transform-fixture-test-runner/src/helpers.ts index 4df3ae81e847..7c15a524432e 100644 --- a/packages/babel-helper-transform-fixture-test-runner/src/helpers.ts +++ b/packages/babel-helper-transform-fixture-test-runner/src/helpers.ts @@ -1,7 +1,7 @@ -export function assertNoOwnProperties(obj) { +export function assertNoOwnProperties(obj: {}) { expect(Object.getOwnPropertyNames(obj)).toHaveLength(0); } -export function multiline(arr) { +export function multiline(arr: string[]) { return arr.join("\n"); } diff --git a/packages/babel-helper-transform-fixture-test-runner/src/index.ts b/packages/babel-helper-transform-fixture-test-runner/src/index.ts index 45c1f1b135cc..49ae396c4d69 100644 --- a/packages/babel-helper-transform-fixture-test-runner/src/index.ts +++ b/packages/babel-helper-transform-fixture-test-runner/src/index.ts @@ -1,9 +1,15 @@ /* eslint-env jest */ import * as babel from "@babel/core"; -import { buildExternalHelpers } from "@babel/core"; +import { + buildExternalHelpers, + type InputOptions, + type FileResult, +} from "@babel/core"; import { default as getFixtures, resolveOptionPluginOrPreset, + type Test, + type TestFile, } from "@babel/helper-fixtures"; import { codeFrameColumns } from "@babel/code-frame"; import { TraceMap, originalPositionFor } from "@jridgewell/trace-mapping"; @@ -20,6 +26,11 @@ const require = createRequire(import.meta.url); import checkDuplicateNodes from "@babel/helper-check-duplicate-nodes"; +type Module = { + id: string; + exports: Record; +}; + if (!process.env.BABEL_8_BREAKING) { // Introduced in Node.js 8 if (!assert.rejects) { @@ -51,14 +62,14 @@ const sharedTestContext = createContext(); // babel.config.js file, so we disable config loading by // default. Tests can still set `configFile: true | string` // to re-enable config loading. -function transformWithoutConfigFile(code, opts) { +function transformWithoutConfigFile(code: string, opts: InputOptions) { return babel.transformSync(code, { configFile: false, babelrc: false, ...opts, }); } -function transformAsyncWithoutConfigFile(code, opts) { +function transformAsyncWithoutConfigFile(code: string, opts: InputOptions) { return babel.transformAsync(code, { configFile: false, babelrc: false, @@ -100,7 +111,7 @@ function runCacheableScriptInTestContext( srcFn: () => string, context: vm.Context, moduleCache: any, -) { +): Module { let cached = cachedScripts.get(filename); if (!cached) { const code = `(function (exports, require, module, __filename, __dirname) {\n${srcFn()}\n});`; @@ -137,7 +148,8 @@ function runCacheableScriptInTestContext( id: filename, exports: {}, }; - const req = id => runModuleInTestContext(id, filename, context, moduleCache); + const req = (id: string) => + runModuleInTestContext(id, filename, context, moduleCache); const dirname = path.dirname(filename); script @@ -195,9 +207,10 @@ export function runCodeInTestContext( const filename = opts.filename; const dirname = path.dirname(filename); const moduleCache = contextModuleCache.get(context); - const req = id => runModuleInTestContext(id, filename, context, moduleCache); + const req = (id: string) => + runModuleInTestContext(id, filename, context, moduleCache); - const module = { + const module: Module = { id: filename, exports: {}, }; @@ -220,7 +233,7 @@ export function runCodeInTestContext( } } -async function maybeMockConsole(validateLogs, run) { +async function maybeMockConsole(validateLogs: boolean, run: () => R) { const actualLogs = { stdout: "", stderr: "" }; if (!validateLogs) return { result: await run(), actualLogs }; @@ -240,7 +253,7 @@ async function maybeMockConsole(validateLogs, run) { } } -async function run(task) { +async function run(task: Test) { const { actual, expect: expected, @@ -255,7 +268,7 @@ async function run(task) { } = task; // todo(flow->ts) add proper return type (added any, because empty object is inferred) - function getOpts(self): any { + function getOpts(self: TestFile): any { const newOpts = { ast: true, cwd: path.dirname(self.loc), @@ -273,7 +286,7 @@ async function run(task) { } let execCode = exec.code; - let result; + let result: FileResult; let resultExec; if (execCode) { @@ -387,7 +400,11 @@ async function run(task) { } } -function validateFile(actualCode, expectedLoc, expectedCode) { +function validateFile( + actualCode: string, + expectedLoc: string, + expectedCode: string, +) { try { expect(actualCode).toEqualFile({ filename: expectedLoc, @@ -401,11 +418,11 @@ function validateFile(actualCode, expectedLoc, expectedCode) { } } -function escapeRegExp(string) { +function escapeRegExp(string: string) { return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); } -function normalizeOutput(code, normalizePathSeparator?) { +function normalizeOutput(code: string, normalizePathSeparator?: boolean) { const projectRoot = path.resolve( path.dirname(fileURLToPath(import.meta.url)), "../../../", @@ -438,7 +455,7 @@ function normalizeOutput(code, normalizePathSeparator?) { } expect.extend({ - toEqualFile(actual, { filename, code }) { + toEqualFile(actual, { filename, code }: Pick) { if (this.isNot) { throw new Error(".toEqualFile does not support negation"); } @@ -465,7 +482,10 @@ declare global { namespace jest { // eslint-disable-next-line @typescript-eslint/no-unused-vars interface Matchers { - toEqualFile({ filename, code }): jest.CustomMatcherResult; + toEqualFile({ + filename, + code, + }: Pick): jest.CustomMatcherResult; } } } @@ -508,12 +528,13 @@ export default function ( if (dynamicOpts) dynamicOpts(task.options, task); - // @ts-expect-error todo(flow->ts) missing property if (task.externalHelpers) { - (task.options.plugins ??= []).push([ - "external-helpers", - { helperVersion: EXTERNAL_HELPERS_VERSION }, - ]); + (task.options.plugins ??= []) + // @ts-ignore manipulating input options + .push([ + "external-helpers", + { helperVersion: EXTERNAL_HELPERS_VERSION }, + ]); } const throwMsg = task.options.throws; From 54e2e60b734d6a748d427d5aea3862be53fedcd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Wed, 1 Jun 2022 09:25:05 -0400 Subject: [PATCH 11/22] Improve preset-env typings (#14606) * refactor: move shipped-proposals to ts * preset-env * address review comments --- .../babel-core/src/config/cache-contexts.ts | 2 +- .../babel-core/src/config/config-chain.ts | 2 +- .../src/config/files/configuration.ts | 10 ++--- .../src/config/files/index-browser.ts | 6 +-- .../src/config/helpers/config-api.ts | 2 +- .../config/validation/option-assertions.ts | 2 +- .../src/rest.ts | 44 +++++++++++++++++-- .../src/index.ts | 2 +- .../data/shipped-proposals.js | 36 +-------------- packages/babel-preset-env/src/debug.ts | 1 + packages/babel-preset-env/src/filter-items.ts | 11 ++++- packages/babel-preset-env/src/index.ts | 29 +++++++----- .../babel-preset-env/src/normalize-options.ts | 8 +++- .../src/plugins-compat-data.ts | 2 + .../src/polyfills/regenerator.ts | 2 +- .../babel-preset-env/src/shipped-proposals.ts | 41 +++++++++++++++++ 16 files changed, 134 insertions(+), 66 deletions(-) create mode 100644 packages/babel-preset-env/src/shipped-proposals.ts diff --git a/packages/babel-core/src/config/cache-contexts.ts b/packages/babel-core/src/config/cache-contexts.ts index b487d22dfcb3..1c7e1553bac3 100644 --- a/packages/babel-core/src/config/cache-contexts.ts +++ b/packages/babel-core/src/config/cache-contexts.ts @@ -16,7 +16,7 @@ export type FullPlugin = { // process 'ignore'/'only' and other filename-based logic. export type SimpleConfig = { envName: string; - caller: CallerMetadata | void; + caller: CallerMetadata | undefined; }; export type SimplePreset = { targets: Targets; diff --git a/packages/babel-core/src/config/config-chain.ts b/packages/babel-core/src/config/config-chain.ts index 059d38034b52..3e8b45005a8b 100644 --- a/packages/babel-core/src/config/config-chain.ts +++ b/packages/babel-core/src/config/config-chain.ts @@ -55,7 +55,7 @@ export type ConfigContext = { cwd: string; root: string; envName: string; - caller: CallerMetadata | void; + caller: CallerMetadata | undefined; showConfig: boolean; }; diff --git a/packages/babel-core/src/config/files/configuration.ts b/packages/babel-core/src/config/files/configuration.ts index 713e0c355ab2..99f49c0ce2f7 100644 --- a/packages/babel-core/src/config/files/configuration.ts +++ b/packages/babel-core/src/config/files/configuration.ts @@ -57,7 +57,7 @@ export function findConfigUpwards(rootDir: string): string | null { export function* findRelativeConfig( packageData: FilePackageData, envName: string, - caller: CallerMetadata | void, + caller: CallerMetadata | undefined, ): Handler { let config = null; let ignore = null; @@ -93,7 +93,7 @@ export function* findRelativeConfig( export function findRootConfig( dirname: string, envName: string, - caller: CallerMetadata | void, + caller: CallerMetadata | undefined, ): Handler { return loadOneConfig(ROOT_CONFIG_FILENAMES, dirname, envName, caller); } @@ -102,7 +102,7 @@ function* loadOneConfig( names: string[], dirname: string, envName: string, - caller: CallerMetadata | void, + caller: CallerMetadata | undefined, previousConfig: ConfigFile | null = null, ): Handler { const configs = yield* gensync.all( @@ -133,7 +133,7 @@ export function* loadConfig( name: string, dirname: string, envName: string, - caller: CallerMetadata | void, + caller: CallerMetadata | undefined, ): Handler { const filepath = require.resolve(name, { paths: [dirname] }); @@ -163,7 +163,7 @@ const readConfigJS = makeStrongCache(function* readConfigJS( filepath: string, cache: CacheConfigurator<{ envName: string; - caller: CallerMetadata | void; + caller: CallerMetadata | undefined; }>, ): Handler { if (!nodeFs.existsSync(filepath)) { diff --git a/packages/babel-core/src/config/files/index-browser.ts b/packages/babel-core/src/config/files/index-browser.ts index ac615d9a1583..9acbcb6be68f 100644 --- a/packages/babel-core/src/config/files/index-browser.ts +++ b/packages/babel-core/src/config/files/index-browser.ts @@ -35,7 +35,7 @@ export function* findRelativeConfig( // eslint-disable-next-line @typescript-eslint/no-unused-vars envName: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars - caller: CallerMetadata | void, + caller: CallerMetadata | undefined, ): Handler { return { config: null, ignore: null }; } @@ -47,7 +47,7 @@ export function* findRootConfig( // eslint-disable-next-line @typescript-eslint/no-unused-vars envName: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars - caller: CallerMetadata | void, + caller: CallerMetadata | undefined, ): Handler { return null; } @@ -59,7 +59,7 @@ export function* loadConfig( // eslint-disable-next-line @typescript-eslint/no-unused-vars envName: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars - caller: CallerMetadata | void, + caller: CallerMetadata | undefined, ): Handler { throw new Error(`Cannot load ${name} relative to ${dirname} in a browser`); } diff --git a/packages/babel-core/src/config/helpers/config-api.ts b/packages/babel-core/src/config/helpers/config-api.ts index b8f8ba833c3b..2b87b705dcb6 100644 --- a/packages/babel-core/src/config/helpers/config-api.ts +++ b/packages/babel-core/src/config/helpers/config-api.ts @@ -21,7 +21,7 @@ type EnvFunction = { }; type CallerFactory = ( - extractor: (callerMetadata: CallerMetadata | void) => unknown, + extractor: (callerMetadata: CallerMetadata | undefined) => unknown, ) => SimpleType; type TargetsFunction = () => Targets; type AssumptionFunction = (name: AssumptionName) => boolean | void; diff --git a/packages/babel-core/src/config/validation/option-assertions.ts b/packages/babel-core/src/config/validation/option-assertions.ts index 8f141a1c160a..9abc05582227 100644 --- a/packages/babel-core/src/config/validation/option-assertions.ts +++ b/packages/babel-core/src/config/validation/option-assertions.ts @@ -135,7 +135,7 @@ export function assertSourceType( export function assertCallerMetadata( loc: OptionPath, value: unknown, -): CallerMetadata | void { +): CallerMetadata | undefined { const obj = assertObject(loc, value); if (obj) { if (typeof obj.name !== "string") { diff --git a/packages/babel-plugin-transform-parameters/src/rest.ts b/packages/babel-plugin-transform-parameters/src/rest.ts index 3d784f58b024..3699cd823ba8 100644 --- a/packages/babel-plugin-transform-parameters/src/rest.ts +++ b/packages/babel-plugin-transform-parameters/src/rest.ts @@ -1,4 +1,39 @@ import { template, types as t } from "@babel/core"; +import type { NodePath, Visitor } from "@babel/traverse"; + +type Candidate = { + cause: "indexGetter" | "lengthGetter" | "argSpread"; + path: NodePath; +}; + +type State = { + references: []; + offset: number; + + argumentsNode: t.Identifier; + outerBinding: t.Identifier; + + // candidate member expressions we could optimise if there are no other references + candidates: Candidate[]; + + // local rest binding name + name: string; + + /* + It may be possible to optimize the output code in certain ways, such as + not generating code to initialize an array (perhaps substituting direct + references to arguments[i] or arguments.length for reads of the + corresponding rest parameter property) or positioning the initialization + code so that it may not have to execute depending on runtime conditions. + + This property tracks eligibility for optimization. "deopted" means give up + and don't perform optimization. For example, when any of rest's elements / + properties is assigned to at the top level, or referenced at all in a + nested function. + */ + deopted: boolean; + noOptimise: boolean; +}; const buildRest = template(` for (var LEN = ARGUMENTS.length, @@ -22,7 +57,10 @@ const restLength = template(` ARGUMENTS.length <= OFFSET ? 0 : ARGUMENTS.length - OFFSET `); -function referencesRest(path, state) { +function referencesRest( + path: NodePath, + state: State, +) { if (path.node.name === state.name) { // Check rest parameter is not shadowed by a binding in another scope. return path.scope.bindingIdentifierEquals(state.name, state.outerBinding); @@ -31,7 +69,7 @@ function referencesRest(path, state) { return false; } -const memberExpressionOptimisationVisitor = { +const memberExpressionOptimisationVisitor: Visitor = { Scope(path, state) { // check if this scope has a local binding that will shadow the rest parameter if (!path.scope.bindingIdentifierEquals(state.name, state.outerBinding)) { @@ -39,7 +77,7 @@ const memberExpressionOptimisationVisitor = { } }, - Flow(path) { + Flow(path: NodePath) { // Do not skip TypeCastExpressions as the contain valid non flow code if (path.isTypeCastExpression()) return; // don't touch reference in type annotations diff --git a/packages/babel-plugin-transform-runtime/src/index.ts b/packages/babel-plugin-transform-runtime/src/index.ts index d16ea9acae92..b63c58813828 100644 --- a/packages/babel-plugin-transform-runtime/src/index.ts +++ b/packages/babel-plugin-transform-runtime/src/index.ts @@ -18,7 +18,7 @@ const pluginRegenerator = (_pluginRegenerator.default || const pluginsCompat = "#__secret_key__@babel/runtime__compatibility"; -function supportsStaticESM(caller: CallerMetadata | void) { +function supportsStaticESM(caller: CallerMetadata | undefined) { // @ts-ignore TS does not narrow down optional chaining return !!caller?.supportsStaticESM; } diff --git a/packages/babel-preset-env/data/shipped-proposals.js b/packages/babel-preset-env/data/shipped-proposals.js index ef9fe775dfd8..d583f9a3c8b8 100644 --- a/packages/babel-preset-env/data/shipped-proposals.js +++ b/packages/babel-preset-env/data/shipped-proposals.js @@ -1,36 +1,2 @@ -/* eslint sort-keys: "error" */ -// These mappings represent the transform plugins that have been -// shipped by browsers, and are enabled by the `shippedProposals` option. - -const proposalPlugins = new Set(); - -// proposal syntax plugins enabled by the `shippedProposals` option. -// Unlike proposalPlugins above, they are independent of compiler targets. -const proposalSyntaxPlugins = [ - "syntax-import-assertions" -] - -// use intermediary object to enforce alphabetical key order -const pluginSyntaxObject = { - "proposal-async-generator-functions": "syntax-async-generators", - "proposal-class-properties": "syntax-class-properties", - "proposal-class-static-block": "syntax-class-static-block", - "proposal-json-strings": "syntax-json-strings", - "proposal-nullish-coalescing-operator": "syntax-nullish-coalescing-operator", - "proposal-numeric-separator": "syntax-numeric-separator", - "proposal-object-rest-spread": "syntax-object-rest-spread", - "proposal-optional-catch-binding": "syntax-optional-catch-binding", - "proposal-optional-chaining": "syntax-optional-chaining", - // note: we don't have syntax-private-methods - "proposal-private-methods": "syntax-class-properties", - "proposal-private-property-in-object": "syntax-private-property-in-object", - "proposal-unicode-property-regex": null, -}; - -const pluginSyntaxEntries = Object.keys(pluginSyntaxObject).map(function (key) { - return [key, pluginSyntaxObject[key]]; -}); - -const pluginSyntaxMap = new Map(pluginSyntaxEntries); - +const { pluginSyntaxMap, proposalPlugins, proposalSyntaxPlugins } = require("../lib/shipped-proposals"); module.exports = { pluginSyntaxMap, proposalPlugins, proposalSyntaxPlugins }; diff --git a/packages/babel-preset-env/src/debug.ts b/packages/babel-preset-env/src/debug.ts index 7299f62fc228..97db94e2c123 100644 --- a/packages/babel-preset-env/src/debug.ts +++ b/packages/babel-preset-env/src/debug.ts @@ -24,6 +24,7 @@ export const logPlugin = ( if (!first) formattedTargets += `,`; first = false; formattedTargets += ` ${target}`; + // @ts-ignore if (support[target]) formattedTargets += ` < ${support[target]}`; } formattedTargets += ` }`; diff --git a/packages/babel-preset-env/src/filter-items.ts b/packages/babel-preset-env/src/filter-items.ts index cb5b4c0f6341..692c67bdbead 100644 --- a/packages/babel-preset-env/src/filter-items.ts +++ b/packages/babel-preset-env/src/filter-items.ts @@ -6,7 +6,7 @@ const has = Function.call.bind(Object.hasOwnProperty); export function addProposalSyntaxPlugins( items: Set, - proposalSyntaxPlugins: string[], + proposalSyntaxPlugins: readonly string[], ) { proposalSyntaxPlugins.forEach(plugin => { items.add(plugin); @@ -25,7 +25,14 @@ export function removeUnsupportedItems( babelVersion: string, ) { items.forEach(item => { - if (has(minVersions, item) && semver.lt(babelVersion, minVersions[item])) { + if ( + has(minVersions, item) && + semver.lt( + babelVersion, + // @ts-ignore we have checked minVersions[item] in has call + minVersions[item], + ) + ) { items.delete(item); } }); diff --git a/packages/babel-preset-env/src/index.ts b/packages/babel-preset-env/src/index.ts index 9eb8bc328981..bfd7ff2c1fae 100644 --- a/packages/babel-preset-env/src/index.ts +++ b/packages/babel-preset-env/src/index.ts @@ -12,7 +12,7 @@ import { pluginSyntaxMap, proposalPlugins, proposalSyntaxPlugins, -} from "../data/shipped-proposals"; +} from "./shipped-proposals"; import { plugins as pluginsList, pluginsBugfixes as pluginsBugfixesList, @@ -22,6 +22,8 @@ import overlappingPlugins from "@babel/compat-data/overlapping-plugins"; import removeRegeneratorEntryPlugin from "./polyfills/regenerator"; import legacyBabelPolyfillPlugin from "./polyfills/babel-polyfill"; +import type { CallerMetadata } from "@babel/core"; + import _pluginCoreJS2 from "babel-plugin-polyfill-corejs2"; import _pluginCoreJS3 from "babel-plugin-polyfill-corejs3"; import _pluginRegenerator from "babel-plugin-polyfill-regenerator"; @@ -55,6 +57,7 @@ function filterStageFromList( ) { return Object.keys(list).reduce((result, item) => { if (!stageList.has(item)) { + // @ts-ignore result[item] = list[item]; } @@ -87,7 +90,9 @@ function getPluginList(proposals: boolean, bugfixes: boolean) { } const getPlugin = (pluginName: string) => { - const plugin = availablePlugins[pluginName](); + const plugin = + // @ts-ignore plugin name is constructed from available plugin list + availablePlugins[pluginName](); if (!plugin) { throw new Error( @@ -243,10 +248,10 @@ export const getPolyfillPlugins = ({ }; function getLocalTargets( - optionsTargets, - ignoreBrowserslistConfig, - configPath, - browserslistEnv, + optionsTargets: Options["targets"], + ignoreBrowserslistConfig: boolean, + configPath: string, + browserslistEnv: string, ) { if (optionsTargets?.esmodules && optionsTargets.browsers) { console.warn(` @@ -262,19 +267,23 @@ function getLocalTargets( }); } -function supportsStaticESM(caller) { +function supportsStaticESM(caller: CallerMetadata | undefined) { + // @ts-expect-error supportsStaticESM is not defined in CallerMetadata return !!caller?.supportsStaticESM; } -function supportsDynamicImport(caller) { +function supportsDynamicImport(caller: CallerMetadata | undefined) { + // @ts-expect-error supportsDynamicImport is not defined in CallerMetadata return !!caller?.supportsDynamicImport; } -function supportsExportNamespaceFrom(caller) { +function supportsExportNamespaceFrom(caller: CallerMetadata | undefined) { + // @ts-expect-error supportsExportNamespaceFrom is not defined in CallerMetadata return !!caller?.supportsExportNamespaceFrom; } -function supportsTopLevelAwait(caller) { +function supportsTopLevelAwait(caller: CallerMetadata | undefined) { + // @ts-expect-error supportsTopLevelAwait is not defined in CallerMetadata return !!caller?.supportsTopLevelAwait; } diff --git a/packages/babel-preset-env/src/normalize-options.ts b/packages/babel-preset-env/src/normalize-options.ts index 4f1e9288e33a..babae634c7d9 100644 --- a/packages/babel-preset-env/src/normalize-options.ts +++ b/packages/babel-preset-env/src/normalize-options.ts @@ -112,7 +112,9 @@ export const checkDuplicateIncludeExcludes = ( ); }; -const normalizeTargets = (targets): Options["targets"] => { +const normalizeTargets = ( + targets: string | string[] | Options["targets"], +): Options["targets"] => { // TODO: Allow to use only query or strings as a targets from next breaking change. if (typeof targets === "string" || Array.isArray(targets)) { return { browsers: targets }; @@ -124,6 +126,7 @@ export const validateModulesOption = ( modulesOpt: ModuleOption = ModulesOption.auto, ) => { v.invariant( + // @ts-ignore we have provided fallback for undefined keys ModulesOption[modulesOpt.toString()] || modulesOpt === ModulesOption.false, `The 'modules' option must be one of \n` + ` - 'false' to indicate no module processing\n` + @@ -139,6 +142,7 @@ export const validateUseBuiltInsOption = ( builtInsOpt: BuiltInsOption = false, ) => { v.invariant( + // @ts-ignore we have provided fallback for undefined keys UseBuiltInsOption[builtInsOpt.toString()] || builtInsOpt === UseBuiltInsOption.false, `The 'useBuiltIns' option must be either @@ -260,7 +264,7 @@ export default function normalizeOptions(opts: Options) { spec: v.validateBooleanOption(TopLevelOptions.spec, opts.spec, false), targets: normalizeTargets(opts.targets), useBuiltIns: useBuiltIns, - browserslistEnv: v.validateStringOption( + browserslistEnv: v.validateStringOption( TopLevelOptions.browserslistEnv, opts.browserslistEnv, ), diff --git a/packages/babel-preset-env/src/plugins-compat-data.ts b/packages/babel-preset-env/src/plugins-compat-data.ts index 583395499dd0..2324c6c78919 100644 --- a/packages/babel-preset-env/src/plugins-compat-data.ts +++ b/packages/babel-preset-env/src/plugins-compat-data.ts @@ -7,12 +7,14 @@ const bugfixPluginsFiltered = {}; for (const plugin of Object.keys(plugins)) { if (Object.hasOwnProperty.call(availablePlugins, plugin)) { + // @ts-ignore pluginsFiltered[plugin] = plugins[plugin]; } } for (const plugin of Object.keys(bugfixPlugins)) { if (Object.hasOwnProperty.call(availablePlugins, plugin)) { + // @ts-ignore bugfixPluginsFiltered[plugin] = bugfixPlugins[plugin]; } } diff --git a/packages/babel-preset-env/src/polyfills/regenerator.ts b/packages/babel-preset-env/src/polyfills/regenerator.ts index 046dfdcace42..e9b5111dc8b2 100644 --- a/packages/babel-preset-env/src/polyfills/regenerator.ts +++ b/packages/babel-preset-env/src/polyfills/regenerator.ts @@ -2,7 +2,7 @@ import { getImportSource, getRequireSource } from "./utils"; import type { Visitor } from "@babel/traverse"; import type { PluginObject, PluginPass } from "@babel/core"; -function isRegeneratorSource(source) { +function isRegeneratorSource(source: string) { return ( source === "regenerator-runtime/runtime" || source === "regenerator-runtime/runtime.js" diff --git a/packages/babel-preset-env/src/shipped-proposals.ts b/packages/babel-preset-env/src/shipped-proposals.ts new file mode 100644 index 000000000000..fb77edc566e7 --- /dev/null +++ b/packages/babel-preset-env/src/shipped-proposals.ts @@ -0,0 +1,41 @@ +// TODO(Babel 8): Remove this file +/* eslint sort-keys: "error" */ +// These mappings represent the transform plugins that have been +// shipped by browsers, and are enabled by the `shippedProposals` option. + +const proposalPlugins = new Set(); + +// proposal syntax plugins enabled by the `shippedProposals` option. +// Unlike proposalPlugins above, they are independent of compiler targets. +const proposalSyntaxPlugins = ["syntax-import-assertions"] as const; + +// use intermediary object to enforce alphabetical key order +const pluginSyntaxObject = { + "proposal-async-generator-functions": "syntax-async-generators", + "proposal-class-properties": "syntax-class-properties", + "proposal-class-static-block": "syntax-class-static-block", + "proposal-json-strings": "syntax-json-strings", + "proposal-nullish-coalescing-operator": "syntax-nullish-coalescing-operator", + "proposal-numeric-separator": "syntax-numeric-separator", + "proposal-object-rest-spread": "syntax-object-rest-spread", + "proposal-optional-catch-binding": "syntax-optional-catch-binding", + "proposal-optional-chaining": "syntax-optional-chaining", + // note: we don't have syntax-private-methods + "proposal-private-methods": "syntax-class-properties", + "proposal-private-property-in-object": "syntax-private-property-in-object", + "proposal-unicode-property-regex": null as null, +} as const; + +const pluginSyntaxEntries = Object.keys(pluginSyntaxObject).map< + [string, string | null] +>(function (key) { + return [ + key, + // @ts-expect-error key has been guarded + pluginSyntaxObject[key], + ]; +}); + +const pluginSyntaxMap = new Map(pluginSyntaxEntries); + +export { proposalPlugins, proposalSyntaxPlugins, pluginSyntaxMap }; From 143bf3653cd79306733f6c240a08b873f73b474c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 2 Jun 2022 09:58:17 -0400 Subject: [PATCH 12/22] Improve pipeline-operator typings (#14629) * refactor: simplify buildOptimizedSequenceExpression * proposal-pipeline-operator * address review comments --- .../src/buildOptimizedSequenceExpression.ts | 67 ++++++++++++------- .../src/fsharpVisitor.ts | 7 +- .../src/hackVisitor.ts | 8 ++- .../src/minimalVisitor.ts | 7 +- 4 files changed, 54 insertions(+), 35 deletions(-) diff --git a/packages/babel-plugin-proposal-pipeline-operator/src/buildOptimizedSequenceExpression.ts b/packages/babel-plugin-proposal-pipeline-operator/src/buildOptimizedSequenceExpression.ts index 44909eac757a..e15e7eb3801b 100644 --- a/packages/babel-plugin-proposal-pipeline-operator/src/buildOptimizedSequenceExpression.ts +++ b/packages/babel-plugin-proposal-pipeline-operator/src/buildOptimizedSequenceExpression.ts @@ -1,58 +1,73 @@ import { types as t } from "@babel/core"; +import type { NodePath } from "@babel/traverse"; // tries to optimize sequence expressions in the format // (a = b, (c => c + e)(a)) // to // (a = b, a + e) -const buildOptimizedSequenceExpression = ({ call, path, placeholder }) => { + +type Options = { + call: t.CallExpression | t.AwaitExpression; + path: NodePath" }>; + placeholder: t.Identifier; +}; + +function isConciseArrowExpression( + node: t.Node, +): node is t.ArrowFunctionExpression & { body: t.Expression } { + return ( + t.isArrowFunctionExpression(node) && + t.isExpression(node.body) && + !node.async + ); +} + +const buildOptimizedSequenceExpression = ({ + call, + path, + placeholder, +}: Options) => { + // @ts-expect-error AwaitExpression does not have callee property const { callee: calledExpression } = call; - const pipelineLeft = path.node.left; + // pipelineLeft must not be a PrivateName + const pipelineLeft = path.node.left as t.Expression; const assign = t.assignmentExpression( "=", t.cloneNode(placeholder), pipelineLeft, ); - let optimizeArrow = - t.isArrowFunctionExpression(calledExpression) && - t.isExpression(calledExpression.body) && - !calledExpression.async && - !calledExpression.generator; - let param; + const expressionIsArrow = isConciseArrowExpression(calledExpression); - if (optimizeArrow) { + if (expressionIsArrow) { + let param; + let optimizeArrow = true; const { params } = calledExpression; if (params.length === 1 && t.isIdentifier(params[0])) { param = params[0]; } else if (params.length > 0) { optimizeArrow = false; } + if (optimizeArrow && !param) { + // fixme: arrow function with 1 pattern argument will also go into this branch + // Arrow function with 0 arguments + return t.sequenceExpression([pipelineLeft, calledExpression.body]); + } else if (param) { + path.scope.push({ id: t.cloneNode(placeholder) }); + path.get("right").scope.rename(param.name, placeholder.name); + + return t.sequenceExpression([assign, calledExpression.body]); + } } else if (t.isIdentifier(calledExpression, { name: "eval" })) { const evalSequence = t.sequenceExpression([ t.numericLiteral(0), calledExpression, ]); - call.callee = evalSequence; - - path.scope.push({ id: t.cloneNode(placeholder) }); - - return t.sequenceExpression([assign, call]); - } - - if (optimizeArrow && !param) { - // Arrow function with 0 arguments - return t.sequenceExpression([pipelineLeft, calledExpression.body]); + (call as t.CallExpression).callee = evalSequence; } - path.scope.push({ id: t.cloneNode(placeholder) }); - if (param) { - path.get("right").scope.rename(param.name, placeholder.name); - - return t.sequenceExpression([assign, calledExpression.body]); - } - return t.sequenceExpression([assign, call]); }; diff --git a/packages/babel-plugin-proposal-pipeline-operator/src/fsharpVisitor.ts b/packages/babel-plugin-proposal-pipeline-operator/src/fsharpVisitor.ts index d285588f37df..5b1b5cac24cd 100644 --- a/packages/babel-plugin-proposal-pipeline-operator/src/fsharpVisitor.ts +++ b/packages/babel-plugin-proposal-pipeline-operator/src/fsharpVisitor.ts @@ -1,7 +1,8 @@ -import { types as t } from "@babel/core"; +import { types as t, type PluginObject } from "@babel/core"; +import type { NodePath } from "@babel/traverse"; import buildOptimizedSequenceExpression from "./buildOptimizedSequenceExpression"; -const fsharpVisitor = { +const fsharpVisitor: PluginObject["visitor"] = { BinaryExpression(path) { const { scope, node } = path; const { operator, left, right } = node; @@ -16,7 +17,7 @@ const fsharpVisitor = { const sequence = buildOptimizedSequenceExpression({ placeholder, call, - path, + path: path as NodePath" }>, }); path.replaceWith(sequence); }, diff --git a/packages/babel-plugin-proposal-pipeline-operator/src/hackVisitor.ts b/packages/babel-plugin-proposal-pipeline-operator/src/hackVisitor.ts index 36af34d537d5..c7e9d8d865c4 100644 --- a/packages/babel-plugin-proposal-pipeline-operator/src/hackVisitor.ts +++ b/packages/babel-plugin-proposal-pipeline-operator/src/hackVisitor.ts @@ -33,7 +33,7 @@ const topicReferenceVisitor: Visitor = { // with sequence expressions containing assignment expressions // with automatically generated variables, // from inside to outside, from left to right. -export default { +const visitor: Visitor = { BinaryExpression: { exit(path) { const { scope, node } = path; @@ -52,7 +52,7 @@ export default { return; } - const visitorState = { + const visitorState: State = { topicReferences: [], // pipeBodyPath might be a function, and it won't be visited by // topicReferenceVisitor because traverse() skips the top-level @@ -93,4 +93,6 @@ export default { ); }, }, -} as Visitor; +}; + +export default visitor; diff --git a/packages/babel-plugin-proposal-pipeline-operator/src/minimalVisitor.ts b/packages/babel-plugin-proposal-pipeline-operator/src/minimalVisitor.ts index 66d3bff98af0..a7a0e3e72464 100644 --- a/packages/babel-plugin-proposal-pipeline-operator/src/minimalVisitor.ts +++ b/packages/babel-plugin-proposal-pipeline-operator/src/minimalVisitor.ts @@ -1,7 +1,8 @@ -import { types as t } from "@babel/core"; +import { types as t, type PluginPass } from "@babel/core"; +import type { NodePath, Visitor } from "@babel/traverse"; import buildOptimizedSequenceExpression from "./buildOptimizedSequenceExpression"; -const minimalVisitor = { +const minimalVisitor: Visitor = { BinaryExpression(path) { const { scope, node } = path; const { operator, left, right } = node; @@ -14,7 +15,7 @@ const minimalVisitor = { buildOptimizedSequenceExpression({ placeholder, call, - path, + path: path as NodePath" }>, }), ); }, From 77e5aea9ed75f404f9fb1fbabf9047aba287c95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 2 Jun 2022 09:58:46 -0400 Subject: [PATCH 13/22] improve standalone typings (#14630) * babel-standalone * address review comment --- packages/babel-standalone/src/index.ts | 26 ++++++---- .../src/transformScriptTags.ts | 52 +++++++++++++++---- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/packages/babel-standalone/src/index.ts b/packages/babel-standalone/src/index.ts index 1b2ae5ee9b17..f355b5f575ee 100644 --- a/packages/babel-standalone/src/index.ts +++ b/packages/babel-standalone/src/index.ts @@ -26,6 +26,7 @@ import presetEnv from "@babel/preset-env"; import presetFlow from "@babel/preset-flow"; import presetReact from "@babel/preset-react"; import presetTypescript from "@babel/preset-typescript"; +import type { InputOptions } from "@babel/core"; import { runScripts } from "./transformScriptTags"; @@ -40,7 +41,7 @@ const isArray = * Returns undefined if the preset or plugin is not available; passes through * name unmodified if it (or the first element of the pair) is not a string. */ -function loadBuiltin(builtinTable, name) { +function loadBuiltin(builtinTable: Record, name: any) { if (isArray(name) && typeof name[0] === "string") { if (Object.prototype.hasOwnProperty.call(builtinTable, name[0])) { return [builtinTable[name[0]]].concat(name.slice(1)); @@ -56,7 +57,7 @@ function loadBuiltin(builtinTable, name) { /** * Parses plugin names and presets from the specified options. */ -function processOptions(options) { +function processOptions(options: InputOptions) { // Parse preset names const presets = (options.presets || []).map(presetName => { const preset = loadBuiltin(availablePresets, presetName); @@ -100,15 +101,19 @@ function processOptions(options) { }; } -export function transform(code: string, options: any) { +export function transform(code: string, options: InputOptions) { return babelTransformSync(code, processOptions(options)); } -export function transformFromAst(ast: any, code: string, options: any) { +export function transformFromAst( + ast: Parameters[0], + code: string, + options: InputOptions, +) { return babelTransformFromAstSync(ast, code, processOptions(options)); } -export const availablePlugins = {}; -export const availablePresets = {}; +export const availablePlugins: typeof all = {}; + export const buildExternalHelpers = babelBuildExternalHelpers; /** * Registers a named plugin for use with Babel. @@ -148,6 +153,7 @@ export function registerPreset(name: string, preset: any | Function): void { ); } } + // @ts-expect-error mutating available presets availablePresets[name] = preset; } /** @@ -170,7 +176,7 @@ registerPlugins(all); // All the presets we should bundle // Want to get rid of this list of allowed presets? // Wait! Please read https://github.com/babel/babel/pull/6177 first. -registerPresets({ +export const availablePresets = { env: presetEnv, es2015: preset2015, es2016: () => { @@ -197,7 +203,7 @@ registerPresets({ }, typescript: presetTypescript, flow: presetFlow, -}); +}; // @ts-ignore VERSION is to be replaced by rollup export const version: string = VERSION; @@ -216,7 +222,9 @@ if (typeof window !== "undefined" && window?.addEventListener) { * Transform