From ff52acee795fafacd023e9eeed105e013b629395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 7 Jan 2021 14:58:58 -0500 Subject: [PATCH] [babel 8] Type checking preset-typescript options (#12460) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: extract option normalization in preset-typescript * breaking: type checking preset-typescript options * chore: bundle preset-typescript into a single lib * test: disable Babel 7 test on Babel 8 breaking change test * workaround Jest 24 error snapshot dedent issue * skip Babel 8 test when the Breaking ENV is not available * chore: update test snapshot * Update packages/babel-preset-typescript/test/normalize-options.spec.js Co-authored-by: Nicolò Ribaudo * update test fixtures Co-authored-by: Nicolò Ribaudo --- Gulpfile.js | 1 + packages/babel-preset-typescript/src/index.js | 26 +----- .../src/normalize-options.js | 72 ++++++++++++++ .../test/normalize-options.spec.js | 93 +++++++++++++++++++ 4 files changed, 171 insertions(+), 21 deletions(-) create mode 100644 packages/babel-preset-typescript/src/normalize-options.js create mode 100644 packages/babel-preset-typescript/test/normalize-options.spec.js diff --git a/Gulpfile.js b/Gulpfile.js index 25c74e2801..0887d1ece5 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -361,6 +361,7 @@ function copyDts(packages) { const libBundles = [ "packages/babel-parser", "packages/babel-plugin-proposal-optional-chaining", + "packages/babel-preset-typescript", "packages/babel-helper-member-expression-to-functions", ].map(src => ({ src, diff --git a/packages/babel-preset-typescript/src/index.js b/packages/babel-preset-typescript/src/index.js index 36e20e63ac..f1827205ed 100644 --- a/packages/babel-preset-typescript/src/index.js +++ b/packages/babel-preset-typescript/src/index.js @@ -1,35 +1,19 @@ import { declare } from "@babel/helper-plugin-utils"; import transformTypeScript from "@babel/plugin-transform-typescript"; -import { OptionValidator } from "@babel/helper-validator-option"; -const v = new OptionValidator("@babel/preset-typescript"); +import normalizeOptions from "./normalize-options.js"; export default declare((api, opts) => { api.assertVersion(7); const { + allExtensions, allowDeclareFields, allowNamespaces, + isTSX, jsxPragma, + jsxPragmaFrag, onlyRemoveTypeImports, - } = opts; - - const jsxPragmaFrag = v.validateStringOption( - "jsxPragmaFrag", - opts.jsxPragmaFrag, - "React.Fragment", - ); - - const allExtensions = v.validateBooleanOption( - "allExtensions", - opts.allExtensions, - false, - ); - - const isTSX = v.validateBooleanOption("isTSX", opts.isTSX, false); - - if (isTSX) { - v.invariant(allExtensions, "isTSX:true requires allExtensions:true"); - } + } = normalizeOptions(opts); const pluginOptions = isTSX => ({ allowDeclareFields, diff --git a/packages/babel-preset-typescript/src/normalize-options.js b/packages/babel-preset-typescript/src/normalize-options.js new file mode 100644 index 0000000000..14d2c56e32 --- /dev/null +++ b/packages/babel-preset-typescript/src/normalize-options.js @@ -0,0 +1,72 @@ +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; + + if (process.env.BABEL_8_BREAKING) { + const TopLevelOptions = { + allowDeclareFields: "allowDeclareFields", + allExtensions: "allExtensions", + allowNamespaces: "allowNamespaces", + isTSX: "isTSX", + jsxPragma: "jsxPragma", + jsxPragmaFrag: "jsxPragmaFrag", + onlyRemoveTypeImports: "onlyRemoveTypeImports", + }; + v.validateTopLevelOptions(options, TopLevelOptions); + allowDeclareFields = v.validateBooleanOption( + TopLevelOptions.allowDeclareFields, + options.allowDeclareFields, + true, + ); + allowNamespaces = v.validateBooleanOption( + TopLevelOptions.allowNamespaces, + options.allowNamespaces, + true, + ); + jsxPragma = v.validateStringOption( + TopLevelOptions.jsxPragma, + options.jsxPragma, + "React", + ); + onlyRemoveTypeImports = v.validateBooleanOption( + TopLevelOptions.onlyRemoveTypeImports, + options.onlyRemoveTypeImports, + true, + ); + } + + const jsxPragmaFrag = v.validateStringOption( + "jsxPragmaFrag", + options.jsxPragmaFrag, + "React.Fragment", + ); + + const allExtensions = v.validateBooleanOption( + "allExtensions", + options.allExtensions, + false, + ); + + const isTSX = v.validateBooleanOption("isTSX", options.isTSX, false); + + if (isTSX) { + v.invariant(allExtensions, "isTSX:true requires allExtensions:true"); + } + + return { + allExtensions, + allowDeclareFields, + allowNamespaces, + isTSX, + jsxPragma, + jsxPragmaFrag, + onlyRemoveTypeImports, + }; +} diff --git a/packages/babel-preset-typescript/test/normalize-options.spec.js b/packages/babel-preset-typescript/test/normalize-options.spec.js new file mode 100644 index 0000000000..b8875ed05d --- /dev/null +++ b/packages/babel-preset-typescript/test/normalize-options.spec.js @@ -0,0 +1,93 @@ +import normalizeOptions from "../src/normalize-options"; +describe("normalize options", () => { + (process.env.BABEL_8_BREAKING ? describe : describe.skip)("Babel 8", () => { + it("should throw on unknown options", () => { + expect(() => normalizeOptions({ allowNamespace: true })).toThrowError( + "@babel/preset-typescript: 'allowNamespace' is not a valid top-level option.\n- Did you mean 'allowNamespaces'?", + ); + }); + it.each([ + "allowDeclareFields", + "allExtensions", + "allowNamespaces", + "isTSX", + "onlyRemoveTypeImports", + ])("should throw when `%p` is not a boolean", optionName => { + expect(() => normalizeOptions({ [optionName]: 0 })).toThrow( + `@babel/preset-typescript: '${optionName}' option must be a boolean.`, + ); + }); + it.each(["jsxPragma", "jsxPragmaFrag"])( + "should throw when `%p` is not a string", + optionName => { + expect(() => normalizeOptions({ [optionName]: 0 })).toThrow( + `@babel/preset-typescript: '${optionName}' option must be a string.`, + ); + }, + ); + it("should not throw when options is not defined", () => { + expect(() => normalizeOptions()).not.toThrowError(); + }); + it("default values", () => { + expect(normalizeOptions({})).toMatchInlineSnapshot(` + Object { + "allExtensions": false, + "allowDeclareFields": true, + "allowNamespaces": true, + "isTSX": false, + "jsxPragma": "React", + "jsxPragmaFrag": "React.Fragment", + "onlyRemoveTypeImports": true, + } + `); + }); + }); + (process.env.BABEL_8_BREAKING ? describe.skip : describe)("Babel 7", () => { + it("should not throw on unknown options", () => { + expect(() => + normalizeOptions({ allowNamespace: true }), + ).not.toThrowError(); + }); + it.each(["allowDeclareFields", "allowNamespaces", "onlyRemoveTypeImports"])( + "should not throw when `%p` is not a boolean", + optionName => { + expect(() => normalizeOptions({ [optionName]: 0 })).not.toThrowError(); + }, + ); + it.each(["jsxPragma"])( + "should throw when `%p` is not a string", + optionName => { + expect(() => normalizeOptions({ [optionName]: 0 })).not.toThrowError(); + }, + ); + it.each(["allExtensions", "isTSX"])( + "should throw when `%p` is not a boolean", + optionName => { + expect(() => normalizeOptions({ [optionName]: 0 })).toThrow( + `@babel/preset-typescript: '${optionName}' option must be a boolean.`, + ); + }, + ); + it.each(["jsxPragmaFrag"])( + "should throw when `%p` is not a string", + optionName => { + expect(() => normalizeOptions({ [optionName]: 0 })).toThrow( + `@babel/preset-typescript: '${optionName}' option must be a string.`, + ); + }, + ); + it("default values", () => { + expect(normalizeOptions({})).toMatchInlineSnapshot(` + Object { + "allExtensions": false, + "allowDeclareFields": undefined, + "allowNamespaces": undefined, + "isTSX": false, + "jsxPragma": undefined, + "jsxPragmaFrag": "React.Fragment", + "onlyRemoveTypeImports": undefined, + } + `); + }); + }); +});