From 50462eb5e4f61c8da49e4b8347e12c0aecf77c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Fri, 8 Jan 2021 01:28:20 +0100 Subject: [PATCH] [babel 8] Enable `allowDeclareFields` option by default with TS (#12461) --- .circleci/config.yml | 24 +- .../input.js | 6 + .../options.json | 4 + .../output.js | 7 +- .../input.js | 6 + .../options.json | 7 + .../output.js | 7 + .../options.json | 1 + .../output.js | 1 + .../options.json | 1 + .../output.js | 2 + .../src/index.js | 807 +++++++++--------- .../fixtures/class/declare-babel-7/input.ts | 4 + .../options.json | 3 +- .../input.ts => declare-babel-7/output.js} | 4 +- .../input.ts | 0 .../options.json | 1 + .../declare-not-initialized/options.json | 3 - .../test/fixtures/class/declare/options.json | 3 +- .../input.ts | 0 .../options.json | 4 + .../output.js | 0 .../class/field-not-initialized/input.ts | 3 + .../class/field-not-initialized/options.json | 4 + .../output.js | 0 .../class/properties-babel-7/input.ts | 11 + .../class/properties-babel-7/options.json | 8 + .../class/properties-babel-7/output.js | 12 + .../fixtures/class/properties/options.json | 1 + .../test/fixtures/class/properties/output.js | 2 + .../options.json | 2 +- .../input.ts | 0 .../options.json | 1 + .../output.js | 0 .../input.ts | 0 .../options.json | 1 + .../output.js | 1 + .../class/uninitialized-definite/input.ts | 3 + .../class/uninitialized-definite/options.json | 4 + .../class/uninitialized-definite/output.js | 3 + .../namespace/module-nested-export/input.ts | 4 +- .../namespace/module-nested-export/output.mjs | 8 +- .../fixtures/namespace/module-nested/input.ts | 4 +- .../namespace/module-nested/output.mjs | 8 +- packages/babel-preset-typescript/src/index.js | 25 +- .../src/normalize-options.js | 14 +- .../test/normalize-options.spec.js | 3 - scripts/integration-tests/e2e-babel.sh | 5 + scripts/integration-tests/e2e-jest.sh | 15 +- 49 files changed, 584 insertions(+), 453 deletions(-) create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/input.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/options.json rename packages/{babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare => babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7}/output.js (66%) create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/input.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/options.json create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/output.js create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/declare-babel-7/input.ts rename packages/babel-plugin-transform-typescript/test/fixtures/class/{transform-properties-declare => declare-babel-7}/options.json (53%) rename packages/babel-plugin-transform-typescript/test/fixtures/class/{transform-properties-declare/input.ts => declare-babel-7/output.js} (51%) rename packages/babel-plugin-transform-typescript/test/fixtures/class/{declare-not-enabled => declare-not-enabled-babel-7}/input.ts (100%) rename packages/babel-plugin-transform-typescript/test/fixtures/class/{declare-not-enabled => declare-not-enabled-babel-7}/options.json (87%) delete mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-initialized/options.json rename packages/babel-plugin-transform-typescript/test/fixtures/class/{declare-not-initialized => field-not-initialized-babel-7}/input.ts (100%) create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized-babel-7/options.json rename packages/babel-plugin-transform-typescript/test/fixtures/class/{uninitialized-definite-with-declare-disabled => field-not-initialized-babel-7}/output.js (100%) create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized/options.json rename packages/babel-plugin-transform-typescript/test/fixtures/class/{declare-not-initialized => field-not-initialized}/output.js (100%) create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/output.js rename packages/babel-plugin-transform-typescript/test/fixtures/class/{uninitialized-definite-with-declare-disabled => uninitialized-definite-babel-7}/input.ts (100%) rename packages/babel-plugin-transform-typescript/test/fixtures/class/{uninitialized-definite-with-declare-enabled => uninitialized-definite-babel-7}/options.json (72%) rename packages/babel-plugin-transform-typescript/test/fixtures/class/{uninitialized-definite-with-declare-enabled => uninitialized-definite-babel-7}/output.js (100%) rename packages/babel-plugin-transform-typescript/test/fixtures/class/{uninitialized-definite-with-declare-enabled => uninitialized-definite-with-declare-disabled-babel-7}/input.ts (100%) rename packages/babel-plugin-transform-typescript/test/fixtures/class/{uninitialized-definite-with-declare-disabled => uninitialized-definite-with-declare-disabled-babel-7}/options.json (72%) create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled-babel-7/output.js create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/output.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 0f6dc2fe18..aa6826594b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -149,6 +149,14 @@ jobs: at: /tmp/verdaccio-workspace - run: ./scripts/integration-tests/e2e-babel.sh + e2e-babel-breaking: + executor: node-executor + steps: + - checkout + - attach_workspace: + at: /tmp/verdaccio-workspace + - run: BABEL_8_BREAKING=true ./scripts/integration-tests/e2e-babel.sh + e2e-babel-old-version: executor: node-executor steps: @@ -185,6 +193,14 @@ jobs: at: /tmp/verdaccio-workspace - run: ./scripts/integration-tests/e2e-jest.sh + e2e-jest-breaking: + executor: node-python-executor + steps: + - checkout + - attach_workspace: + at: /tmp/verdaccio-workspace + - run: BABEL_8_BREAKING=true ./scripts/integration-tests/e2e-jest.sh + workflows: version: 2 build-standalone: @@ -247,7 +263,7 @@ workflows: filters: branches: only: [main, next-8-dev, next-8-rebased] - - e2e-babel: + - e2e-babel-breaking: requires: - publish-verdaccio-babel-8-breaking - e2e-create-react-app: @@ -256,7 +272,7 @@ workflows: - e2e-vue-cli: requires: - publish-verdaccio-babel-8-breaking - - e2e-jest: + - e2e-jest-breaking: requires: - publish-verdaccio-babel-8-breaking @@ -270,7 +286,7 @@ workflows: - publish-verdaccio-babel-8-breaking: requires: - approve-e2e-breaking-run - - e2e-babel: + - e2e-babel-breaking: requires: - publish-verdaccio-babel-8-breaking - e2e-create-react-app: @@ -279,7 +295,7 @@ workflows: - e2e-vue-cli: requires: - publish-verdaccio-babel-8-breaking - - e2e-jest: + - e2e-jest-breaking: requires: - publish-verdaccio-babel-8-breaking diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/input.js new file mode 100644 index 0000000000..593242cde2 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/input.js @@ -0,0 +1,6 @@ +class C { + // Output should not use `_initialiseProps` + x: T; + y = 0; + constructor(T) {} +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/options.json new file mode 100644 index 0000000000..ff538cdb55 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/options.json @@ -0,0 +1,4 @@ +{ + "BABEL_8_BREAKING": false, + "plugins": ["transform-typescript", "proposal-class-properties"] +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/output.js similarity index 66% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare/output.js rename to packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/output.js index 44b56f1600..c85b2477ed 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-babel-7/output.js @@ -1,8 +1,9 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } -class A { - constructor() { - _defineProperty(this, "y", void 0); +class C { + // Output should not use `_initialiseProps` + constructor(T) { + _defineProperty(this, "y", 0); } } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/input.js new file mode 100644 index 0000000000..593242cde2 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/input.js @@ -0,0 +1,6 @@ +class C { + // Output should not use `_initialiseProps` + x: T; + y = 0; + constructor(T) {} +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/options.json new file mode 100644 index 0000000000..044dbac646 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/options.json @@ -0,0 +1,7 @@ +{ + "BABEL_8_BREAKING": false, + "plugins": [ + "transform-typescript", + ["proposal-class-properties", { "loose": true }] + ] +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/output.js new file mode 100644 index 0000000000..18cbcb0e7e --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose-babel-7/output.js @@ -0,0 +1,7 @@ +class C { + // Output should not use `_initialiseProps` + constructor(T) { + this.y = 0; + } + +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose/options.json index 8dcbd70928..5de86d9ab4 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose/options.json +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose/options.json @@ -1,4 +1,5 @@ { + "BABEL_8_BREAKING": true, "plugins": [ "transform-typescript", ["proposal-class-properties", { "loose": true }] diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose/output.js index 18cbcb0e7e..688eb31db9 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types-loose/output.js @@ -1,6 +1,7 @@ class C { // Output should not use `_initialiseProps` constructor(T) { + this.x = void 0; this.y = 0; } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types/options.json index 08e180158b..ba0253c472 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types/options.json +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types/options.json @@ -1,3 +1,4 @@ { + "BABEL_8_BREAKING": true, "plugins": ["transform-typescript", "proposal-class-properties"] } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types/output.js index c85b2477ed..e6d715c07a 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/compile-to-class/constructor-collision-ignores-types/output.js @@ -3,6 +3,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope class C { // Output should not use `_initialiseProps` constructor(T) { + _defineProperty(this, "x", void 0); + _defineProperty(this, "y", 0); } diff --git a/packages/babel-plugin-transform-typescript/src/index.js b/packages/babel-plugin-transform-typescript/src/index.js index 668690219a..89b24c8104 100644 --- a/packages/babel-plugin-transform-typescript/src/index.js +++ b/packages/babel-plugin-transform-typescript/src/index.js @@ -45,52 +45,59 @@ function registerGlobalType(programScope, name) { GLOBAL_TYPES.get(programScope.path.node).add(name); } -export default declare( - ( - api, - { - jsxPragma = "React.createElement", - jsxPragmaFrag = "React.Fragment", - allowNamespaces = false, - allowDeclareFields = false, - onlyRemoveTypeImports = false, - }, - ) => { - api.assertVersion(7); +export default declare((api, opts) => { + api.assertVersion(7); - const JSX_PRAGMA_REGEX = /\*?\s*@jsx((?:Frag)?)\s+([^\s]+)/; + const JSX_PRAGMA_REGEX = /\*?\s*@jsx((?:Frag)?)\s+([^\s]+)/; - const classMemberVisitors = { - field(path) { - const { node } = path; + const { + jsxPragma = "React.createElement", + jsxPragmaFrag = "React.Fragment", + allowNamespaces = false, + onlyRemoveTypeImports = false, + } = opts; + if (!process.env.BABEL_8_BREAKING) { + // eslint-disable-next-line no-var + var { allowDeclareFields = false } = opts; + } + + const classMemberVisitors = { + field(path) { + const { node } = path; + + if (!process.env.BABEL_8_BREAKING) { if (!allowDeclareFields && node.declare) { throw path.buildCodeFrameError( `The 'declare' modifier is only allowed when the 'allowDeclareFields' option of ` + `@babel/plugin-transform-typescript or @babel/preset-typescript is enabled.`, ); } - if (node.declare) { - if (node.value) { - throw path.buildCodeFrameError( - `Fields with the 'declare' modifier cannot be initialized here, but only in the constructor`, - ); - } - if (!node.decorators) { - path.remove(); - } - } else if (node.definite) { - if (node.value) { - throw path.buildCodeFrameError( - `Definitely assigned fields cannot be initialized here, but only in the constructor`, - ); - } + } + if (node.declare) { + if (node.value) { + throw path.buildCodeFrameError( + `Fields with the 'declare' modifier cannot be initialized here, but only in the constructor`, + ); + } + if (!node.decorators) { + path.remove(); + } + } else if (node.definite) { + if (node.value) { + throw path.buildCodeFrameError( + `Definitely assigned fields cannot be initialized here, but only in the constructor`, + ); + } + if (!process.env.BABEL_8_BREAKING) { // keep the definitely assigned fields only when `allowDeclareFields` (equivalent of // Typescript's `useDefineForClassFields`) is true if (!allowDeclareFields && !node.decorators) { path.remove(); } - } else if ( + } + } else if (!process.env.BABEL_8_BREAKING) { + if ( !allowDeclareFields && !node.value && !node.decorators && @@ -98,405 +105,405 @@ export default declare( ) { path.remove(); } + } - if (node.accessibility) node.accessibility = null; - if (node.abstract) node.abstract = null; - if (node.readonly) node.readonly = null; - if (node.optional) node.optional = null; - if (node.typeAnnotation) node.typeAnnotation = null; - if (node.definite) node.definite = null; - if (node.declare) node.declare = null; - }, - method({ node }) { - if (node.accessibility) node.accessibility = null; - if (node.abstract) node.abstract = null; - if (node.optional) node.optional = null; + if (node.accessibility) node.accessibility = null; + if (node.abstract) node.abstract = null; + if (node.readonly) node.readonly = null; + if (node.optional) node.optional = null; + if (node.typeAnnotation) node.typeAnnotation = null; + if (node.definite) node.definite = null; + if (node.declare) node.declare = null; + }, + method({ node }) { + if (node.accessibility) node.accessibility = null; + if (node.abstract) node.abstract = null; + if (node.optional) node.optional = null; - // Rest handled by Function visitor - }, - constructor(path, classPath) { - 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 - // - // We use a WeakSet to ensure an assignment for a parameter - // property is only added once. This is necessary for cases like - // using `transform-classes`, which causes this visitor to run - // twice. - const parameterProperties = []; - for (const param of path.node.params) { - if ( - param.type === "TSParameterProperty" && - !PARSED_PARAMS.has(param.parameter) - ) { - PARSED_PARAMS.add(param.parameter); - parameterProperties.push(param.parameter); - } + // Rest handled by Function visitor + }, + constructor(path, classPath) { + 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 + // + // We use a WeakSet to ensure an assignment for a parameter + // property is only added once. This is necessary for cases like + // using `transform-classes`, which causes this visitor to run + // twice. + const parameterProperties = []; + for (const param of path.node.params) { + if ( + param.type === "TSParameterProperty" && + !PARSED_PARAMS.has(param.parameter) + ) { + PARSED_PARAMS.add(param.parameter); + parameterProperties.push(param.parameter); } + } - if (parameterProperties.length) { - const assigns = parameterProperties.map(p => { - let id; - if (t.isIdentifier(p)) { - id = p; - } else if (t.isAssignmentPattern(p) && t.isIdentifier(p.left)) { - id = p.left; - } else { - throw path.buildCodeFrameError( - "Parameter properties can not be destructuring patterns.", - ); - } + if (parameterProperties.length) { + const assigns = parameterProperties.map(p => { + let id; + if (t.isIdentifier(p)) { + id = p; + } else if (t.isAssignmentPattern(p) && t.isIdentifier(p.left)) { + id = p.left; + } else { + throw path.buildCodeFrameError( + "Parameter properties can not be destructuring patterns.", + ); + } - return template.statement.ast` + return template.statement.ast` this.${t.cloneNode(id)} = ${t.cloneNode(id)}`; - }); + }); - injectInitialization(classPath, path, assigns); + injectInitialization(classPath, path, assigns); + } + }, + }; + + return { + name: "transform-typescript", + inherits: syntaxTypeScript, + + visitor: { + //"Pattern" alias doesn't include Identifier or RestElement. + Pattern: visitPattern, + Identifier: visitPattern, + RestElement: visitPattern, + + Program(path, state) { + const { file } = state; + let fileJsxPragma = null; + let fileJsxPragmaFrag = null; + + if (!GLOBAL_TYPES.has(path.node)) { + GLOBAL_TYPES.set(path.node, new Set()); } - }, - }; - return { - name: "transform-typescript", - inherits: syntaxTypeScript, - - visitor: { - //"Pattern" alias doesn't include Identifier or RestElement. - Pattern: visitPattern, - Identifier: visitPattern, - RestElement: visitPattern, - - Program(path, state) { - const { file } = state; - let fileJsxPragma = null; - let fileJsxPragmaFrag = null; - - if (!GLOBAL_TYPES.has(path.node)) { - GLOBAL_TYPES.set(path.node, new Set()); - } - - if (file.ast.comments) { - for (const comment of (file.ast.comments: Array)) { - const jsxMatches = JSX_PRAGMA_REGEX.exec(comment.value); - if (jsxMatches) { - if (jsxMatches[1]) { - // isFragment - fileJsxPragmaFrag = jsxMatches[2]; - } else { - fileJsxPragma = jsxMatches[2]; - } + if (file.ast.comments) { + for (const comment of (file.ast.comments: Array)) { + const jsxMatches = JSX_PRAGMA_REGEX.exec(comment.value); + if (jsxMatches) { + if (jsxMatches[1]) { + // isFragment + fileJsxPragmaFrag = jsxMatches[2]; + } else { + fileJsxPragma = jsxMatches[2]; } } } + } - let pragmaImportName = fileJsxPragma || jsxPragma; - if (pragmaImportName) { - [pragmaImportName] = pragmaImportName.split("."); - } + let pragmaImportName = fileJsxPragma || jsxPragma; + if (pragmaImportName) { + [pragmaImportName] = pragmaImportName.split("."); + } - let pragmaFragImportName = fileJsxPragmaFrag || jsxPragmaFrag; - if (pragmaFragImportName) { - [pragmaFragImportName] = pragmaFragImportName.split("."); - } - - // remove type imports - for (let stmt of path.get("body")) { - if (t.isImportDeclaration(stmt)) { - if (stmt.node.importKind === "type") { - stmt.remove(); - continue; - } - - // If onlyRemoveTypeImports is `true`, only remove type-only imports - // and exports introduced in TypeScript 3.8. - if (!onlyRemoveTypeImports) { - // Note: this will allow both `import { } from "m"` and `import "m";`. - // In TypeScript, the former would be elided. - if (stmt.node.specifiers.length === 0) { - continue; - } - - let allElided = true; - const importsToRemove: Path[] = []; - - for (const specifier of stmt.node.specifiers) { - const binding = stmt.scope.getBinding(specifier.local.name); - - // The binding may not exist if the import node was explicitly - // injected by another plugin. Currently core does not do a good job - // of keeping scope bindings synchronized with the AST. For now we - // just bail if there is no binding, since chances are good that if - // the import statement was injected then it wasn't a typescript type - // import anyway. - if ( - binding && - isImportTypeOnly({ - binding, - programPath: path, - pragmaImportName, - pragmaFragImportName, - }) - ) { - importsToRemove.push(binding.path); - } else { - allElided = false; - } - } - - if (allElided) { - stmt.remove(); - } else { - for (const importPath of importsToRemove) { - importPath.remove(); - } - } - } + let pragmaFragImportName = fileJsxPragmaFrag || jsxPragmaFrag; + if (pragmaFragImportName) { + [pragmaFragImportName] = pragmaFragImportName.split("."); + } + // remove type imports + for (let stmt of path.get("body")) { + if (t.isImportDeclaration(stmt)) { + if (stmt.node.importKind === "type") { + stmt.remove(); continue; } - if (stmt.isExportDeclaration()) { - stmt = stmt.get("declaration"); - } - - if (stmt.isVariableDeclaration({ declare: true })) { - for (const name of Object.keys(stmt.getBindingIdentifiers())) { - registerGlobalType(path.scope, name); + // If onlyRemoveTypeImports is `true`, only remove type-only imports + // and exports introduced in TypeScript 3.8. + if (!onlyRemoveTypeImports) { + // Note: this will allow both `import { } from "m"` and `import "m";`. + // In TypeScript, the former would be elided. + if (stmt.node.specifiers.length === 0) { + continue; } - } else if ( - stmt.isTSTypeAliasDeclaration() || - stmt.isTSDeclareFunction() || - stmt.isTSInterfaceDeclaration() || - stmt.isClassDeclaration({ declare: true }) || - stmt.isTSEnumDeclaration({ declare: true }) || - (stmt.isTSModuleDeclaration({ declare: true }) && - stmt.get("id").isIdentifier()) - ) { - registerGlobalType(path.scope, stmt.node.id.name); - } - } - }, - ExportNamedDeclaration(path) { - if (path.node.exportKind === "type") { - path.remove(); - return; - } + let allElided = true; + const importsToRemove: Path[] = []; - // remove export declaration if it's exporting only types - // This logic is needed when exportKind is "value", because - // currently the "type" keyword is optional. - // TODO: - // Also, currently @babel/parser sets exportKind to "value" for - // export interface A {} - // etc. - if ( - !path.node.source && - path.node.specifiers.length > 0 && - path.node.specifiers.every(({ local }) => - isGlobalType(path, local.name), - ) - ) { - path.remove(); - } - }, + for (const specifier of stmt.node.specifiers) { + const binding = stmt.scope.getBinding(specifier.local.name); - ExportSpecifier(path) { - // remove type exports - if (!path.parent.source && isGlobalType(path, path.node.local.name)) { - path.remove(); - } - }, + // The binding may not exist if the import node was explicitly + // injected by another plugin. Currently core does not do a good job + // of keeping scope bindings synchronized with the AST. For now we + // just bail if there is no binding, since chances are good that if + // the import statement was injected then it wasn't a typescript type + // import anyway. + if ( + binding && + isImportTypeOnly({ + binding, + programPath: path, + pragmaImportName, + pragmaFragImportName, + }) + ) { + importsToRemove.push(binding.path); + } else { + allElided = false; + } + } - ExportDefaultDeclaration(path) { - // remove whole declaration if it's exporting a TS type - if ( - t.isIdentifier(path.node.declaration) && - isGlobalType(path, path.node.declaration.name) - ) { - path.remove(); - } - }, - - TSDeclareFunction(path) { - path.remove(); - }, - - TSDeclareMethod(path) { - path.remove(); - }, - - VariableDeclaration(path) { - if (path.node.declare) { - path.remove(); - } - }, - - VariableDeclarator({ node }) { - if (node.definite) node.definite = null; - }, - - TSIndexSignature(path) { - path.remove(); - }, - - ClassDeclaration(path) { - const { node } = path; - if (node.declare) { - path.remove(); - return; - } - }, - - Class(path) { - const { node } = path; - - if (node.typeParameters) node.typeParameters = null; - if (node.superTypeParameters) node.superTypeParameters = null; - if (node.implements) node.implements = null; - if (node.abstract) node.abstract = null; - - // Similar to the logic in `transform-flow-strip-types`, we need to - // handle `TSParameterProperty` and `ClassProperty` here because the - // class transform would transform the class, causing more specific - // visitors to not run. - path.get("body.body").forEach(child => { - if (child.isClassMethod() || child.isClassPrivateMethod()) { - if (child.node.kind === "constructor") { - classMemberVisitors.constructor(child, path); + if (allElided) { + stmt.remove(); } else { - classMemberVisitors.method(child, path); + for (const importPath of importsToRemove) { + importPath.remove(); + } } - } else if ( - child.isClassProperty() || - child.isClassPrivateProperty() - ) { - classMemberVisitors.field(child, path); } - }); - }, - Function({ node }) { - if (node.typeParameters) node.typeParameters = null; - if (node.returnType) node.returnType = null; - - const p0 = node.params[0]; - if (p0 && t.isIdentifier(p0) && p0.name === "this") { - node.params.shift(); + continue; } - // We replace `TSParameterProperty` here so that transforms that - // rely on a `Function` visitor to deal with arguments, like - // `transform-parameters`, work properly. - node.params = node.params.map(p => { - return p.type === "TSParameterProperty" ? p.parameter : p; - }); - }, + if (stmt.isExportDeclaration()) { + stmt = stmt.get("declaration"); + } - TSModuleDeclaration(path) { - transpileNamespace(path, t, allowNamespaces); - }, - - TSInterfaceDeclaration(path) { - path.remove(); - }, - - TSTypeAliasDeclaration(path) { - path.remove(); - }, - - TSEnumDeclaration(path) { - transpileEnum(path, t); - }, - - TSImportEqualsDeclaration(path) { - throw path.buildCodeFrameError( - "`import =` is not supported by @babel/plugin-transform-typescript\n" + - "Please consider using " + - "`import from '';` alongside " + - "Typescript's --allowSyntheticDefaultImports option.", - ); - }, - - TSExportAssignment(path) { - throw path.buildCodeFrameError( - "`export =` is not supported by @babel/plugin-transform-typescript\n" + - "Please consider using `export ;`.", - ); - }, - - TSTypeAssertion(path) { - path.replaceWith(path.node.expression); - }, - - TSAsExpression(path) { - let { node } = path; - do { - node = node.expression; - } while (t.isTSAsExpression(node)); - path.replaceWith(node); - }, - - TSNonNullExpression(path) { - path.replaceWith(path.node.expression); - }, - - CallExpression(path) { - path.node.typeParameters = null; - }, - - OptionalCallExpression(path) { - path.node.typeParameters = null; - }, - - NewExpression(path) { - path.node.typeParameters = null; - }, - - JSXOpeningElement(path) { - path.node.typeParameters = null; - }, - - TaggedTemplateExpression(path) { - path.node.typeParameters = null; - }, - }, - }; - - function visitPattern({ node }) { - 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. - } - - function isImportTypeOnly({ - binding, - programPath, - pragmaImportName, - pragmaFragImportName, - }) { - for (const path of binding.referencePaths) { - if (!isInType(path)) { - return false; + if (stmt.isVariableDeclaration({ declare: true })) { + for (const name of Object.keys(stmt.getBindingIdentifiers())) { + registerGlobalType(path.scope, name); + } + } else if ( + stmt.isTSTypeAliasDeclaration() || + stmt.isTSDeclareFunction() || + stmt.isTSInterfaceDeclaration() || + stmt.isClassDeclaration({ declare: true }) || + stmt.isTSEnumDeclaration({ declare: true }) || + (stmt.isTSModuleDeclaration({ declare: true }) && + stmt.get("id").isIdentifier()) + ) { + registerGlobalType(path.scope, stmt.node.id.name); + } } - } + }, - if ( - binding.identifier.name !== pragmaImportName && - binding.identifier.name !== pragmaFragImportName - ) { - return true; - } + ExportNamedDeclaration(path) { + if (path.node.exportKind === "type") { + path.remove(); + return; + } - // "React" or the JSX pragma is referenced as a value if there are any JSX elements/fragments in the code. - let sourceFileHasJsx = false; - programPath.traverse({ - "JSXElement|JSXFragment"(path) { - sourceFileHasJsx = true; - path.stop(); - }, - }); - return !sourceFileHasJsx; + // remove export declaration if it's exporting only types + // This logic is needed when exportKind is "value", because + // currently the "type" keyword is optional. + // TODO: + // Also, currently @babel/parser sets exportKind to "value" for + // export interface A {} + // etc. + if ( + !path.node.source && + path.node.specifiers.length > 0 && + path.node.specifiers.every(({ local }) => + isGlobalType(path, local.name), + ) + ) { + path.remove(); + } + }, + + ExportSpecifier(path) { + // remove type exports + if (!path.parent.source && isGlobalType(path, path.node.local.name)) { + path.remove(); + } + }, + + ExportDefaultDeclaration(path) { + // remove whole declaration if it's exporting a TS type + if ( + t.isIdentifier(path.node.declaration) && + isGlobalType(path, path.node.declaration.name) + ) { + path.remove(); + } + }, + + TSDeclareFunction(path) { + path.remove(); + }, + + TSDeclareMethod(path) { + path.remove(); + }, + + VariableDeclaration(path) { + if (path.node.declare) { + path.remove(); + } + }, + + VariableDeclarator({ node }) { + if (node.definite) node.definite = null; + }, + + TSIndexSignature(path) { + path.remove(); + }, + + ClassDeclaration(path) { + const { node } = path; + if (node.declare) { + path.remove(); + return; + } + }, + + Class(path) { + const { node } = path; + + if (node.typeParameters) node.typeParameters = null; + if (node.superTypeParameters) node.superTypeParameters = null; + if (node.implements) node.implements = null; + if (node.abstract) node.abstract = null; + + // Similar to the logic in `transform-flow-strip-types`, we need to + // handle `TSParameterProperty` and `ClassProperty` here because the + // class transform would transform the class, causing more specific + // visitors to not run. + path.get("body.body").forEach(child => { + if (child.isClassMethod() || child.isClassPrivateMethod()) { + if (child.node.kind === "constructor") { + classMemberVisitors.constructor(child, path); + } else { + classMemberVisitors.method(child, path); + } + } else if ( + child.isClassProperty() || + child.isClassPrivateProperty() + ) { + classMemberVisitors.field(child, path); + } + }); + }, + + Function({ node }) { + if (node.typeParameters) node.typeParameters = null; + if (node.returnType) node.returnType = null; + + const p0 = node.params[0]; + if (p0 && t.isIdentifier(p0) && p0.name === "this") { + node.params.shift(); + } + + // We replace `TSParameterProperty` here so that transforms that + // rely on a `Function` visitor to deal with arguments, like + // `transform-parameters`, work properly. + node.params = node.params.map(p => { + return p.type === "TSParameterProperty" ? p.parameter : p; + }); + }, + + TSModuleDeclaration(path) { + transpileNamespace(path, t, allowNamespaces); + }, + + TSInterfaceDeclaration(path) { + path.remove(); + }, + + TSTypeAliasDeclaration(path) { + path.remove(); + }, + + TSEnumDeclaration(path) { + transpileEnum(path, t); + }, + + TSImportEqualsDeclaration(path) { + throw path.buildCodeFrameError( + "`import =` is not supported by @babel/plugin-transform-typescript\n" + + "Please consider using " + + "`import from '';` alongside " + + "Typescript's --allowSyntheticDefaultImports option.", + ); + }, + + TSExportAssignment(path) { + throw path.buildCodeFrameError( + "`export =` is not supported by @babel/plugin-transform-typescript\n" + + "Please consider using `export ;`.", + ); + }, + + TSTypeAssertion(path) { + path.replaceWith(path.node.expression); + }, + + TSAsExpression(path) { + let { node } = path; + do { + node = node.expression; + } while (t.isTSAsExpression(node)); + path.replaceWith(node); + }, + + TSNonNullExpression(path) { + path.replaceWith(path.node.expression); + }, + + CallExpression(path) { + path.node.typeParameters = null; + }, + + OptionalCallExpression(path) { + path.node.typeParameters = null; + }, + + NewExpression(path) { + path.node.typeParameters = null; + }, + + JSXOpeningElement(path) { + path.node.typeParameters = null; + }, + + TaggedTemplateExpression(path) { + path.node.typeParameters = null; + }, + }, + }; + + function visitPattern({ node }) { + 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. + } + + function isImportTypeOnly({ + binding, + programPath, + pragmaImportName, + pragmaFragImportName, + }) { + for (const path of binding.referencePaths) { + if (!isInType(path)) { + return false; + } } - }, -); + + if ( + binding.identifier.name !== pragmaImportName && + binding.identifier.name !== pragmaFragImportName + ) { + return true; + } + + // "React" or the JSX pragma is referenced as a value if there are any JSX elements/fragments in the code. + let sourceFileHasJsx = false; + programPath.traverse({ + "JSXElement|JSXFragment"(path) { + sourceFileHasJsx = true; + path.stop(); + }, + }); + return !sourceFileHasJsx; + } +}); diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-babel-7/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-babel-7/input.ts new file mode 100644 index 0000000000..396b74629b --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-babel-7/input.ts @@ -0,0 +1,4 @@ +class A { + declare x; + @foo declare y: string; +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-babel-7/options.json similarity index 53% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare/options.json rename to packages/babel-plugin-transform-typescript/test/fixtures/class/declare-babel-7/options.json index 832ece6409..a9701db689 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare/options.json +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-babel-7/options.json @@ -1,6 +1,7 @@ { + "BABEL_8_BREAKING": false, "plugins": [ ["transform-typescript", { "allowDeclareFields": true }], - "proposal-class-properties" + ["syntax-decorators", { "legacy": true }] ] } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-babel-7/output.js similarity index 51% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare/input.ts rename to packages/babel-plugin-transform-typescript/test/fixtures/class/declare-babel-7/output.js index afc2204701..04c54f970b 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare/input.ts +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-babel-7/output.js @@ -1,4 +1,4 @@ class A { - declare x; + @foo y; -} \ No newline at end of file +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled-babel-7/input.ts similarity index 100% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled/input.ts rename to packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled-babel-7/input.ts diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled-babel-7/options.json similarity index 87% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled/options.json rename to packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled-babel-7/options.json index c844b0ad73..f067798c0a 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled/options.json +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-enabled-babel-7/options.json @@ -1,4 +1,5 @@ { + "BABEL_8_BREAKING": false, "plugins": ["transform-typescript"], "throws": "The 'declare' modifier is only allowed when the 'allowDeclareFields' option of @babel/plugin-transform-typescript or @babel/preset-typescript is enabled." } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-initialized/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-initialized/options.json deleted file mode 100644 index a6d406a50e..0000000000 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-initialized/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "plugins": [["transform-typescript", { "allowDeclareFields": true }]] -} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/declare/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare/options.json index 1cc5b96414..1873c2b337 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/declare/options.json +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/declare/options.json @@ -1,6 +1,7 @@ { + "BABEL_8_BREAKING": true, "plugins": [ - ["transform-typescript", { "allowDeclareFields": true }], + "transform-typescript", ["syntax-decorators", { "legacy": true }] ] } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-initialized/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized-babel-7/input.ts similarity index 100% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-initialized/input.ts rename to packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized-babel-7/input.ts diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized-babel-7/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized-babel-7/options.json new file mode 100644 index 0000000000..dfb2331d5c --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized-babel-7/options.json @@ -0,0 +1,4 @@ +{ + "BABEL_8_BREAKING": false, + "plugins": ["transform-typescript"] +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized-babel-7/output.js similarity index 100% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled/output.js rename to packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized-babel-7/output.js diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized/input.ts new file mode 100644 index 0000000000..c7c42323a3 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized/input.ts @@ -0,0 +1,3 @@ +class A { + x; +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized/options.json new file mode 100644 index 0000000000..cc887b70e2 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized/options.json @@ -0,0 +1,4 @@ +{ + "BABEL_8_BREAKING": true, + "plugins": ["transform-typescript"] +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-initialized/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized/output.js similarity index 100% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/declare-not-initialized/output.js rename to packages/babel-plugin-transform-typescript/test/fixtures/class/field-not-initialized/output.js diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/input.ts new file mode 100644 index 0000000000..02769c7faa --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/input.ts @@ -0,0 +1,11 @@ +class C { + public a?: number; + private b: number = 0; + readonly c: number = 1; + @foo d: number; + @foo e: number = 3; + f!: number; + @foo g!: number; + #h: string; + #i: number = 10; +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/options.json new file mode 100644 index 0000000000..c6b2eb80c0 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/options.json @@ -0,0 +1,8 @@ +{ + "BABEL_8_BREAKING": false, + "plugins": [ + "transform-typescript", + ["syntax-decorators", { "legacy": true }], + "syntax-class-properties" + ] +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/output.js new file mode 100644 index 0000000000..139775da65 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties-babel-7/output.js @@ -0,0 +1,12 @@ +class C { + b = 0; + c = 1; + @foo + d; + @foo + e = 3; + @foo + g; + #h; + #i = 10; +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/options.json index a3d66ed683..097b01cfc9 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/options.json +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/options.json @@ -1,4 +1,5 @@ { + "BABEL_8_BREAKING": true, "plugins": [ "transform-typescript", ["syntax-decorators", { "legacy": true }], diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/output.js index 139775da65..9a3665ad3a 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/output.js +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/properties/output.js @@ -1,10 +1,12 @@ class C { + a; b = 0; c = 1; @foo d; @foo e = 3; + f; @foo g; #h; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare-wrong-order/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare-wrong-order/options.json index 8a07f1cc20..bdc1d6238c 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare-wrong-order/options.json +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/transform-properties-declare-wrong-order/options.json @@ -1,7 +1,7 @@ { "plugins": [ "proposal-class-properties", - ["transform-typescript", { "allowDeclareFields": true }] + "transform-typescript" ], "throws": "TypeScript 'declare' fields must first be transformed by @babel/plugin-transform-typescript.\nIf you have already enabled that plugin (or '@babel/preset-typescript'), make sure that it runs before any plugin related to additional class features:\n - @babel/plugin-proposal-class-properties\n - @babel/plugin-proposal-private-methods\n - @babel/plugin-proposal-decorators" } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-babel-7/input.ts similarity index 100% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled/input.ts rename to packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-babel-7/input.ts diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-enabled/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-babel-7/options.json similarity index 72% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-enabled/options.json rename to packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-babel-7/options.json index a6d406a50e..3a3e00d5d4 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-enabled/options.json +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-babel-7/options.json @@ -1,3 +1,4 @@ { + "BABEL_8_BREAKING": false, "plugins": [["transform-typescript", { "allowDeclareFields": true }]] } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-enabled/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-babel-7/output.js similarity index 100% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-enabled/output.js rename to packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-babel-7/output.js diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-enabled/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled-babel-7/input.ts similarity index 100% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-enabled/input.ts rename to packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled-babel-7/input.ts diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled-babel-7/options.json similarity index 72% rename from packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled/options.json rename to packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled-babel-7/options.json index 9373dcbdc3..96e0b99c46 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled/options.json +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled-babel-7/options.json @@ -1,3 +1,4 @@ { + "BABEL_8_BREAKING": false, "plugins": [["transform-typescript", { "allowDeclareFields": false }]] } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled-babel-7/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled-babel-7/output.js new file mode 100644 index 0000000000..a869c28495 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite-with-declare-disabled-babel-7/output.js @@ -0,0 +1 @@ +class A {} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/input.ts new file mode 100644 index 0000000000..9716f73fa6 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/input.ts @@ -0,0 +1,3 @@ +class A { + x!; +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/options.json new file mode 100644 index 0000000000..cc887b70e2 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/options.json @@ -0,0 +1,4 @@ +{ + "BABEL_8_BREAKING": true, + "plugins": ["transform-typescript"] +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/output.js b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/output.js new file mode 100644 index 0000000000..eebf5c5c33 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/uninitialized-definite/output.js @@ -0,0 +1,3 @@ +class A { + x; +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested-export/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested-export/input.ts index 51ddaf01e8..5d3fec99d7 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested-export/input.ts +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested-export/input.ts @@ -1,12 +1,12 @@ export module src { export namespace ns1 { export class foo { - F1: string; + F1: string = ""; } } export namespace ns2 { export class foo { - F1: string; + F1: string = ""; } } } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested-export/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested-export/output.mjs index a3839919a8..0677fb4678 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested-export/output.mjs +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested-export/output.mjs @@ -4,7 +4,9 @@ export let src; let ns1; (function (_ns) { - class foo {} + class foo { + F1 = ""; + } _ns.foo = foo; })(ns1 || (ns1 = _src.ns1 || (_src.ns1 = {}))); @@ -12,7 +14,9 @@ export let src; let ns2; (function (_ns2) { - class foo {} + class foo { + F1 = ""; + } _ns2.foo = foo; })(ns2 || (ns2 = _src.ns2 || (_src.ns2 = {}))); diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested/input.ts index 5a579dbf50..3de673a76c 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested/input.ts +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested/input.ts @@ -1,12 +1,12 @@ module src { export namespace ns1 { export class foo { - F1: string; + F1: string = ""; } } export namespace ns2 { export class foo { - F1: string; + F1: string = ""; } } } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested/output.mjs index 407dcf9ba8..0078a25490 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested/output.mjs +++ b/packages/babel-plugin-transform-typescript/test/fixtures/namespace/module-nested/output.mjs @@ -4,7 +4,9 @@ let src; let ns1; (function (_ns) { - class foo {} + class foo { + F1 = ""; + } _ns.foo = foo; })(ns1 || (ns1 = _src.ns1 || (_src.ns1 = {}))); @@ -12,7 +14,9 @@ let src; let ns2; (function (_ns2) { - class foo {} + class foo { + F1 = ""; + } _ns2.foo = foo; })(ns2 || (ns2 = _src.ns2 || (_src.ns2 = {}))); diff --git a/packages/babel-preset-typescript/src/index.js b/packages/babel-preset-typescript/src/index.js index f1827205ed..099fc24757 100644 --- a/packages/babel-preset-typescript/src/index.js +++ b/packages/babel-preset-typescript/src/index.js @@ -7,7 +7,6 @@ export default declare((api, opts) => { const { allExtensions, - allowDeclareFields, allowNamespaces, isTSX, jsxPragma, @@ -15,14 +14,22 @@ export default declare((api, opts) => { onlyRemoveTypeImports, } = normalizeOptions(opts); - const pluginOptions = isTSX => ({ - allowDeclareFields, - allowNamespaces, - isTSX, - jsxPragma, - jsxPragmaFrag, - onlyRemoveTypeImports, - }); + const pluginOptions = process.env.BABEL_8_BREAKING + ? isTSX => ({ + allowNamespaces, + isTSX, + jsxPragma, + jsxPragmaFrag, + onlyRemoveTypeImports, + }) + : isTSX => ({ + allowDeclareFields: opts.allowDeclareFields, + allowNamespaces, + isTSX, + jsxPragma, + jsxPragmaFrag, + onlyRemoveTypeImports, + }); return { overrides: allExtensions diff --git a/packages/babel-preset-typescript/src/normalize-options.js b/packages/babel-preset-typescript/src/normalize-options.js index 14d2c56e32..dcf7d42612 100644 --- a/packages/babel-preset-typescript/src/normalize-options.js +++ b/packages/babel-preset-typescript/src/normalize-options.js @@ -2,16 +2,10 @@ import { OptionValidator } from "@babel/helper-validator-option"; const v = new OptionValidator("@babel/preset-typescript"); export default function normalizeOptions(options = {}) { - let { - allowDeclareFields, - allowNamespaces, - jsxPragma, - onlyRemoveTypeImports, - } = options; + let { allowNamespaces, jsxPragma, onlyRemoveTypeImports } = options; if (process.env.BABEL_8_BREAKING) { const TopLevelOptions = { - allowDeclareFields: "allowDeclareFields", allExtensions: "allExtensions", allowNamespaces: "allowNamespaces", isTSX: "isTSX", @@ -20,11 +14,6 @@ export default function normalizeOptions(options = {}) { onlyRemoveTypeImports: "onlyRemoveTypeImports", }; v.validateTopLevelOptions(options, TopLevelOptions); - allowDeclareFields = v.validateBooleanOption( - TopLevelOptions.allowDeclareFields, - options.allowDeclareFields, - true, - ); allowNamespaces = v.validateBooleanOption( TopLevelOptions.allowNamespaces, options.allowNamespaces, @@ -62,7 +51,6 @@ export default function normalizeOptions(options = {}) { return { allExtensions, - allowDeclareFields, allowNamespaces, isTSX, jsxPragma, diff --git a/packages/babel-preset-typescript/test/normalize-options.spec.js b/packages/babel-preset-typescript/test/normalize-options.spec.js index b8875ed05d..b84c91c4f4 100644 --- a/packages/babel-preset-typescript/test/normalize-options.spec.js +++ b/packages/babel-preset-typescript/test/normalize-options.spec.js @@ -7,7 +7,6 @@ describe("normalize options", () => { ); }); it.each([ - "allowDeclareFields", "allExtensions", "allowNamespaces", "isTSX", @@ -32,7 +31,6 @@ describe("normalize options", () => { expect(normalizeOptions({})).toMatchInlineSnapshot(` Object { "allExtensions": false, - "allowDeclareFields": true, "allowNamespaces": true, "isTSX": false, "jsxPragma": "React", @@ -80,7 +78,6 @@ describe("normalize options", () => { expect(normalizeOptions({})).toMatchInlineSnapshot(` Object { "allExtensions": false, - "allowDeclareFields": undefined, "allowNamespaces": undefined, "isTSX": false, "jsxPragma": undefined, diff --git a/scripts/integration-tests/e2e-babel.sh b/scripts/integration-tests/e2e-babel.sh index 3ffd49b8d6..a81ee0400d 100755 --- a/scripts/integration-tests/e2e-babel.sh +++ b/scripts/integration-tests/e2e-babel.sh @@ -20,6 +20,11 @@ cd ../.. # TEST # #==============================================================================# +if [ "$BABEL_8_BREAKING" = true ] ; then + # This option is removed in Babel 8 + sed -i 's/allowDeclareFields: true,\?/\/* allowDeclareFields: true *\//g' babel.config.js +fi + startLocalRegistry "$PWD"/scripts/integration-tests/verdaccio-config.yml # We only bump dependencies in the top-level package.json, because workspaces # already use the workspace: protocol so will get the version in the monorepo diff --git a/scripts/integration-tests/e2e-jest.sh b/scripts/integration-tests/e2e-jest.sh index 622b416e1c..8241fa4dc2 100755 --- a/scripts/integration-tests/e2e-jest.sh +++ b/scripts/integration-tests/e2e-jest.sh @@ -37,11 +37,6 @@ python --version # TEST # #==============================================================================# -startLocalRegistry "$root"/verdaccio-config.yml -yarn install -yarn dedupe '@babel/*' -yarn build - # Workaround for https://github.com/babel/babel/pull/12567 node -e ' let snapshots = fs.readFileSync("packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap", "utf8"); @@ -49,6 +44,16 @@ node -e ' fs.writeFileSync("packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap", snapshots); ' +if [ "$BABEL_8_BREAKING" = true ] ; then + # This option is removed in Babel 8 + sed -i 's/allowDeclareFields: true,\?/\/* allowDeclareFields: true *\//g' babel.config.js +fi + +startLocalRegistry "$root"/verdaccio-config.yml +yarn install +yarn dedupe '@babel/*' +yarn build + # The full test suite takes about 20mins on CircleCI. We run only a few of them # to speed it up. # The goals of this e2e test are: