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/src/index.ts b/packages/babel-helper-create-class-features-plugin/src/index.ts index 57c7595a158d..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,6 +99,8 @@ export function createClassFeaturePlugin({ verifyUsedFeatures(path, this.file); + if (path.isClassDeclaration()) assertFieldTransformed(path); + const loose = isLoose(this.file, feature); let constructor: NodePath; 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 new file mode 100644 index 000000000000..4aec71c8add3 --- /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,3 @@ +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..9fdc078bd4bb --- /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,5 @@ +{ + "presets": ["typescript"], + "plugins": ["proposal-class-properties"], + "throws": "TypeScript 'declare' fields must first be transformed by @babel/plugin-transform-typescript." +} 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..8bad5c22cf23 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -1,14 +1,16 @@ import { declare } from "@babel/helper-plugin-utils"; import syntaxTypeScript from "@babel/plugin-syntax-typescript"; import { types as t, template } 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 from "./const-enum"; +import type { NodePathConstEnum } from "./const-enum"; import transpileEnum from "./enum"; import transpileNamespace from "./namespace"; -import type { NodePath } from "@babel/traverse"; -function isInType(path) { +function isInType(path: NodePath) { switch (path.parent.type) { case "TSTypeReference": case "TSQualifiedName": @@ -16,7 +18,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; } @@ -29,7 +34,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; @@ -47,11 +52,34 @@ function isGlobalType(path, name) { return false; } -function registerGlobalType(programNode, name) { +function registerGlobalType(programNode: t.Program, name: string) { 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; + 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 +98,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 +334,11 @@ export default declare((api, opts) => { (stmt.isTSModuleDeclaration({ declare: true }) && stmt.get("id").isIdentifier()) ) { - registerGlobalType(programNode, stmt.node.id.name); + registerGlobalType( + programNode, + //@ts-expect-error + stmt.node.id.name, + ); } } }, @@ -339,8 +375,10 @@ export default declare((api, opts) => { if ( !path.node.source && path.node.specifiers.length > 0 && - path.node.specifiers.every(({ local }) => - isGlobalType(path, local.name), + path.node.specifiers.every( + specifier => + t.isExportSpecifier(specifier) && + isGlobalType(path, specifier.local.name), ) ) { path.remove(); @@ -352,7 +390,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 +446,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 +509,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 +550,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));