From f770666fbd2d347e78e0af38423ff235f3acf782 Mon Sep 17 00:00:00 2001 From: Emily Marigold Klassen Date: Fri, 15 Oct 2021 17:42:33 -0700 Subject: [PATCH 1/5] chore(plugin-transform-typescript): improve typings --- .../src/const-enum.ts | 3 +- .../src/index.ts | 56 +++++++++++++++---- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/src/const-enum.ts b/packages/babel-plugin-transform-typescript/src/const-enum.ts index 171fa56b6b7f..7d241997f1f1 100644 --- a/packages/babel-plugin-transform-typescript/src/const-enum.ts +++ b/packages/babel-plugin-transform-typescript/src/const-enum.ts @@ -3,8 +3,9 @@ import type { NodePath } from "@babel/traverse"; import { translateEnumValues } from "./enum"; +export type NodePathConstEnum = NodePath; export default function transpileConstEnum( - path: NodePath, + path: NodePathConstEnum, t: typeof import("@babel/types"), ) { const { name } = path.node.id; diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts index b79d6deb96ad..86d5e9076868 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -1,12 +1,13 @@ import { declare } from "@babel/helper-plugin-utils"; import syntaxTypeScript from "@babel/plugin-syntax-typescript"; import { types as t, template } from "@babel/core"; +import type { File as BabelFile } from "@babel/core"; import { injectInitialization } from "@babel/helper-create-class-features-plugin"; -import transpileConstEnum from "./const-enum"; +import transpileConstEnum, { NodePathConstEnum } from "./const-enum"; import transpileEnum from "./enum"; import transpileNamespace from "./namespace"; -import type { NodePath } from "@babel/traverse"; +import type { NodePath, Visitor } from "@babel/traverse"; function isInType(path) { switch (path.parent.type) { @@ -50,8 +51,31 @@ function isGlobalType(path, name) { function registerGlobalType(programNode, name) { GLOBAL_TYPES.get(programNode).add(name); } - -export default declare((api, opts) => { +export interface Options { + /** @default true */ + allowNamespaces?: boolean; + /** @default "React.createElement" */ + jsxPragma?: string; + /** @default "React.Fragment" */ + jsxPragmaFrag?: string; + onlyRemoveTypeImports?: boolean; + optimizeConstEnums?: boolean; + allowDeclareFields?: boolean; +} +type ConfigAPI = { assertVersion: (range: string | number) => void }; +interface Plugin { + name: string; + visitor: Visitor<{ file: BabelFile }>; + inherits: typeof syntaxTypeScript; +} +type ExtraNodeProps = { + declare?: unknown; + accessibility?: unknown; + abstract?: unknown; + optional?: unknown; + override?: unknown; +}; +export default declare((api: ConfigAPI, opts: Options): Plugin => { api.assertVersion(7); const JSX_PRAGMA_REGEX = /\*?\s*@jsx((?:Frag)?)\s+([^\s]+)/; @@ -70,7 +94,11 @@ export default declare((api, opts) => { } const classMemberVisitors = { - field(path) { + field( + path: NodePath< + (t.ClassPrivateProperty | t.ClassProperty) & ExtraNodeProps + >, + ) { const { node } = path; if (!process.env.BABEL_8_BREAKING) { @@ -302,7 +330,10 @@ export default declare((api, opts) => { (stmt.isTSModuleDeclaration({ declare: true }) && stmt.get("id").isIdentifier()) ) { - registerGlobalType(programNode, stmt.node.id.name); + registerGlobalType( + programNode, + (stmt.node.id as t.Identifier).name, + ); } } }, @@ -336,10 +367,11 @@ export default declare((api, opts) => { // Also, currently @babel/parser sets exportKind to "value" for // export interface A {} // etc. + type S = typeof path.node.specifiers[number] & { local?: t.Identifier }; if ( !path.node.source && path.node.specifiers.length > 0 && - path.node.specifiers.every(({ local }) => + path.node.specifiers.every(({ local }: S) => isGlobalType(path, local.name), ) ) { @@ -352,7 +384,9 @@ export default declare((api, opts) => { ExportSpecifier(path) { // remove type exports - if (!path.parent.source && isGlobalType(path, path.node.local.name)) { + type Parent = t.ExportDeclaration & { source?: t.StringLiteral }; + const parent = path.parent as Parent; + if (!parent.source && isGlobalType(path, path.node.local.name)) { path.remove(); } }, @@ -406,7 +440,7 @@ export default declare((api, opts) => { }, Class(path) { - const { node } = path; + const { node }: { node: typeof path.node & ExtraNodeProps } = path; if (node.typeParameters) node.typeParameters = null; if (node.superTypeParameters) node.superTypeParameters = null; @@ -469,7 +503,7 @@ export default declare((api, opts) => { TSEnumDeclaration(path) { if (optimizeConstEnums && path.node.const) { - transpileConstEnum(path, t); + transpileConstEnum(path as NodePathConstEnum, t); } else { transpileEnum(path, t); } @@ -510,7 +544,7 @@ export default declare((api, opts) => { }, TSAsExpression(path) { - let { node } = path; + let { node }: { node: t.Expression } = path; do { node = node.expression; } while (t.isTSAsExpression(node)); From 518faf9eb59271ab0cbedbab0ab740a1487144a9 Mon Sep 17 00:00:00 2001 From: Emily Marigold Klassen Date: Fri, 15 Oct 2021 18:00:38 -0700 Subject: [PATCH 2/5] fix(plugin-proposal-class-properties): skip ambient classes --- .../babel-helper-create-class-features-plugin/src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/babel-helper-create-class-features-plugin/src/index.ts b/packages/babel-helper-create-class-features-plugin/src/index.ts index 57c7595a158d..b9a171ae32d8 100644 --- a/packages/babel-helper-create-class-features-plugin/src/index.ts +++ b/packages/babel-helper-create-class-features-plugin/src/index.ts @@ -98,6 +98,11 @@ export function createClassFeaturePlugin({ verifyUsedFeatures(path, this.file); + if ((path.node as t.ClassDeclaration).declare) { + // TypeScript ambient declaration; dont transform + return; + } + const loose = isLoose(this.file, feature); let constructor: NodePath; From 168e726fef81babd103267f0afeaa16d9b7ea528 Mon Sep 17 00:00:00 2001 From: Emily Marigold Klassen Date: Fri, 15 Oct 2021 19:02:04 -0700 Subject: [PATCH 3/5] test: add test for #13853 --- packages/babel-core/src/config/full.ts | 2 +- .../typescript-declare-class-property/input.ts | 5 +++++ .../typescript-declare-class-property/options.json | 4 ++++ .../typescript-declare-class-property/output.js | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/input.ts create mode 100644 packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/options.json create mode 100644 packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/output.js diff --git a/packages/babel-core/src/config/full.ts b/packages/babel-core/src/config/full.ts index 286b16e79107..64ecd6e75fe2 100644 --- a/packages/babel-core/src/config/full.ts +++ b/packages/babel-core/src/config/full.ts @@ -92,7 +92,7 @@ export default gensync<(inputOpts: unknown) => ResolvedConfig | null>( function* recursePresetDescriptors( rawPresets: Array, pluginDescriptorsPass: Array, - ) { + ): Handler { const presets: Array<{ preset: ConfigChain | null; pass: Array; diff --git a/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/input.ts b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/input.ts new file mode 100644 index 000000000000..73dbff94af83 --- /dev/null +++ b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/input.ts @@ -0,0 +1,5 @@ +noop; + +declare class Foo { + static bar: string; +} diff --git a/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/options.json b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/options.json new file mode 100644 index 000000000000..a9900a1accaa --- /dev/null +++ b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/options.json @@ -0,0 +1,4 @@ +{ + "presets": ["typescript"], + "plugins": ["proposal-class-properties"] +} diff --git a/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/output.js b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/output.js new file mode 100644 index 000000000000..3583c5a20ea2 --- /dev/null +++ b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/output.js @@ -0,0 +1 @@ +noop; From 658f588bb25bc177b1c17cce244d39a8094b8076 Mon Sep 17 00:00:00 2001 From: Emily Marigold Klassen Date: Fri, 15 Oct 2021 19:32:29 -0700 Subject: [PATCH 4/5] fix: address review comments --- .../src/index.ts | 6 ++--- .../src/typescript.ts | 4 ++- .../input.ts | 2 -- .../options.json | 3 ++- .../output.js | 1 - .../src/index.ts | 27 +++++++++++-------- 6 files changed, 23 insertions(+), 20 deletions(-) delete mode 100644 packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/output.js diff --git a/packages/babel-helper-create-class-features-plugin/src/index.ts b/packages/babel-helper-create-class-features-plugin/src/index.ts index b9a171ae32d8..df7046213ee0 100644 --- a/packages/babel-helper-create-class-features-plugin/src/index.ts +++ b/packages/babel-helper-create-class-features-plugin/src/index.ts @@ -18,6 +18,7 @@ import { FEATURES, isLoose, } from "./features"; +import { assertFieldTransformed } from "./typescript"; export { FEATURES, enableFeature, injectInitialization }; @@ -98,10 +99,7 @@ export function createClassFeaturePlugin({ verifyUsedFeatures(path, this.file); - if ((path.node as t.ClassDeclaration).declare) { - // TypeScript ambient declaration; dont transform - return; - } + if (path.isClassDeclaration()) assertFieldTransformed(path); const loose = isLoose(this.file, feature); diff --git a/packages/babel-helper-create-class-features-plugin/src/typescript.ts b/packages/babel-helper-create-class-features-plugin/src/typescript.ts index 364ccd15e932..d4ca0fca89a1 100644 --- a/packages/babel-helper-create-class-features-plugin/src/typescript.ts +++ b/packages/babel-helper-create-class-features-plugin/src/typescript.ts @@ -1,7 +1,9 @@ import type { NodePath } from "@babel/traverse"; import type * as t from "@babel/types"; -export function assertFieldTransformed(path: NodePath) { +export function assertFieldTransformed( + path: NodePath, +) { // TODO (Babel 8): Also check path.node.definite if (path.node.declare) { diff --git a/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/input.ts b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/input.ts index 73dbff94af83..4aec71c8add3 100644 --- a/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/input.ts +++ b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/input.ts @@ -1,5 +1,3 @@ -noop; - declare class Foo { static bar: string; } diff --git a/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/options.json b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/options.json index a9900a1accaa..9fdc078bd4bb 100644 --- a/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/options.json +++ b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/options.json @@ -1,4 +1,5 @@ { "presets": ["typescript"], - "plugins": ["proposal-class-properties"] + "plugins": ["proposal-class-properties"], + "throws": "TypeScript 'declare' fields must first be transformed by @babel/plugin-transform-typescript." } diff --git a/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/output.js b/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/output.js deleted file mode 100644 index 3583c5a20ea2..000000000000 --- a/packages/babel-helper-create-class-features-plugin/test/fixtures/plugin-proposal-class-properties/typescript-declare-class-property/output.js +++ /dev/null @@ -1 +0,0 @@ -noop; diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts index 86d5e9076868..7747bcd36e54 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -1,15 +1,15 @@ import { declare } from "@babel/helper-plugin-utils"; import syntaxTypeScript from "@babel/plugin-syntax-typescript"; import { types as t, template } from "@babel/core"; -import type { File as BabelFile } from "@babel/core"; +import type { PluginPass } from "@babel/core"; import { injectInitialization } from "@babel/helper-create-class-features-plugin"; +import type { NodePath, Visitor } from "@babel/traverse"; import transpileConstEnum, { NodePathConstEnum } from "./const-enum"; import transpileEnum from "./enum"; import transpileNamespace from "./namespace"; -import type { NodePath, Visitor } from "@babel/traverse"; -function isInType(path) { +function isInType(path: NodePath) { switch (path.parent.type) { case "TSTypeReference": case "TSQualifiedName": @@ -17,7 +17,10 @@ function isInType(path) { case "TSTypeQuery": return true; case "ExportSpecifier": - return path.parentPath.parent.exportKind === "type"; + return ( + (path.parentPath.parent as t.ExportNamedDeclaration).exportKind === + "type" + ); default: return false; } @@ -30,7 +33,7 @@ const GLOBAL_TYPES = new WeakMap(); const NEEDS_EXPLICIT_ESM = new WeakMap(); const PARSED_PARAMS = new WeakSet(); -function isGlobalType(path, name) { +function isGlobalType(path: NodePath, name: string) { const program = path.find(path => path.isProgram()).node; if (path.scope.hasOwnBinding(name)) return false; if (GLOBAL_TYPES.get(program).has(name)) return true; @@ -48,7 +51,7 @@ function isGlobalType(path, name) { return false; } -function registerGlobalType(programNode, name) { +function registerGlobalType(programNode: t.Program, name: string) { GLOBAL_TYPES.get(programNode).add(name); } export interface Options { @@ -65,7 +68,7 @@ export interface Options { type ConfigAPI = { assertVersion: (range: string | number) => void }; interface Plugin { name: string; - visitor: Visitor<{ file: BabelFile }>; + visitor: Visitor; inherits: typeof syntaxTypeScript; } type ExtraNodeProps = { @@ -332,7 +335,8 @@ export default declare((api: ConfigAPI, opts: Options): Plugin => { ) { registerGlobalType( programNode, - (stmt.node.id as t.Identifier).name, + //@ts-expect-error + stmt.node.id.name, ); } } @@ -367,12 +371,13 @@ export default declare((api: ConfigAPI, opts: Options): Plugin => { // Also, currently @babel/parser sets exportKind to "value" for // export interface A {} // etc. - type S = typeof path.node.specifiers[number] & { local?: t.Identifier }; if ( !path.node.source && path.node.specifiers.length > 0 && - path.node.specifiers.every(({ local }: S) => - isGlobalType(path, local.name), + path.node.specifiers.every( + specifier => + t.isExportSpecifier(specifier) && + isGlobalType(path, specifier.local.name), ) ) { path.remove(); From 9578e3e3c55628538fa7a2ff07d06bb13e531479 Mon Sep 17 00:00:00 2001 From: Emily Marigold Klassen <760204+forivall@users.noreply.github.com> Date: Sat, 16 Oct 2021 14:45:45 -0700 Subject: [PATCH 5/5] Update packages/babel-plugin-transform-typescript/src/index.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolò Ribaudo --- packages/babel-plugin-transform-typescript/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts index 7747bcd36e54..8bad5c22cf23 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -5,7 +5,8 @@ import type { PluginPass } from "@babel/core"; import { injectInitialization } from "@babel/helper-create-class-features-plugin"; import type { NodePath, Visitor } from "@babel/traverse"; -import transpileConstEnum, { NodePathConstEnum } from "./const-enum"; +import transpileConstEnum from "./const-enum"; +import type { NodePathConstEnum } from "./const-enum"; import transpileEnum from "./enum"; import transpileNamespace from "./namespace";