From 0a2a0bb2540949967d172db4ae0a9b0e0462f7f1 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Tue, 17 Oct 2017 11:09:10 -0700 Subject: [PATCH 01/11] Remove the relative babel-core option. --- packages/babel-cli/src/babel/index.js | 1 + packages/babel-core/src/config/option-manager.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-cli/src/babel/index.js b/packages/babel-cli/src/babel/index.js index 530501e926..00fcdfbd77 100755 --- a/packages/babel-cli/src/babel/index.js +++ b/packages/babel-cli/src/babel/index.js @@ -242,6 +242,7 @@ delete opts.quiet; delete opts.configFile; delete opts.deleteDirOnStart; delete opts.keepFileExtension; +delete opts.relative; // Commander will default the "--no-" arguments to true, but we want to leave them undefined so that // @babel/core can handle the default-assignment logic on its own. diff --git a/packages/babel-core/src/config/option-manager.js b/packages/babel-core/src/config/option-manager.js index 787fd28ed1..a743265113 100644 --- a/packages/babel-core/src/config/option-manager.js +++ b/packages/babel-core/src/config/option-manager.js @@ -28,7 +28,6 @@ type MergeOptions = { }; const optionNames = new Set([ - "relative", "filename", "filenameRelative", "inputSourceMap", From 107648cd0bc47acee5ad4c9ce1c8d57f9931dbc7 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Tue, 17 Oct 2017 11:11:05 -0700 Subject: [PATCH 02/11] Remove unused 'mode' option. --- packages/babel-core/src/config/option-manager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/babel-core/src/config/option-manager.js b/packages/babel-core/src/config/option-manager.js index a743265113..5440c3d3d0 100644 --- a/packages/babel-core/src/config/option-manager.js +++ b/packages/babel-core/src/config/option-manager.js @@ -32,7 +32,6 @@ const optionNames = new Set([ "filenameRelative", "inputSourceMap", "env", - "mode", "retainLines", "highlightCode", "suppressDeprecationMessages", From a25942bda99093c73e0f78ff6f8e2dcf44a07c50 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Tue, 17 Oct 2017 11:12:13 -0700 Subject: [PATCH 03/11] Remove unused suppressDeprecationMessages option. --- packages/babel-core/src/config/option-manager.js | 1 - packages/babel-helper-transform-fixture-test-runner/src/index.js | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/babel-core/src/config/option-manager.js b/packages/babel-core/src/config/option-manager.js index 5440c3d3d0..ff3c57faaf 100644 --- a/packages/babel-core/src/config/option-manager.js +++ b/packages/babel-core/src/config/option-manager.js @@ -34,7 +34,6 @@ const optionNames = new Set([ "env", "retainLines", "highlightCode", - "suppressDeprecationMessages", "presets", "plugins", "ignore", diff --git a/packages/babel-helper-transform-fixture-test-runner/src/index.js b/packages/babel-helper-transform-fixture-test-runner/src/index.js index 06586688a0..ea6aa1f498 100644 --- a/packages/babel-helper-transform-fixture-test-runner/src/index.js +++ b/packages/babel-helper-transform-fixture-test-runner/src/index.js @@ -259,7 +259,6 @@ export default function( filenameRelative: task.expect.filename, sourceFileName: task.actual.filename, sourceMapTarget: task.expect.filename, - suppressDeprecationMessages: true, babelrc: false, sourceMap: !!(task.sourceMappings || task.sourceMap), }); From 14901aa74fbe15f75928ce3a2379655713297ad1 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Tue, 17 Oct 2017 15:37:44 -0700 Subject: [PATCH 04/11] Remove the .loc from config items. --- .../src/config/build-config-chain.js | 2 -- .../babel-core/src/config/option-manager.js | 21 +++++++------------ 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/packages/babel-core/src/config/build-config-chain.js b/packages/babel-core/src/config/build-config-chain.js index 56995ea6f2..ec55737bbd 100644 --- a/packages/babel-core/src/config/build-config-chain.js +++ b/packages/babel-core/src/config/build-config-chain.js @@ -16,7 +16,6 @@ type ConfigItem = { options: {}, dirname: string, alias: string, - loc: string, }; type ConfigRaw = { @@ -351,7 +350,6 @@ function flattenOptionsParts( type, options, alias, - loc: alias, dirname, }, ignore, diff --git a/packages/babel-core/src/config/option-manager.js b/packages/babel-core/src/config/option-manager.js index ff3c57faaf..a5596f0c50 100644 --- a/packages/babel-core/src/config/option-manager.js +++ b/packages/babel-core/src/config/option-manager.js @@ -23,7 +23,6 @@ type MergeOptions = { +type: "arguments" | "options" | "preset", options: {}, alias: string, - loc: string, dirname: string, }; @@ -214,7 +213,6 @@ type BasicDescriptor = { options: {} | void, dirname: string, alias: string, - loc: string, }; type LoadedDescriptor = { @@ -222,7 +220,6 @@ type LoadedDescriptor = { options: {}, dirname: string, alias: string, - loc: string, }; /** @@ -250,8 +247,7 @@ const loadConfig = makeWeakCache((config): { ); return { - alias: filepath || `${config.loc}$${index}`, - loc: filepath || config.loc, + alias: filepath || `${config.alias}$${index}`, value, options, dirname: config.dirname, @@ -273,8 +269,7 @@ const loadConfig = makeWeakCache((config): { ); return { - alias: filepath || `${config.loc}$${index}`, - loc: filepath || config.loc, + alias: filepath || `${config.alias}$${index}`, value, options, dirname: config.dirname, @@ -289,7 +284,7 @@ const loadConfig = makeWeakCache((config): { */ const loadDescriptor = makeWeakCache( ( - { value, options = {}, dirname, alias, loc }: BasicDescriptor, + { value, options = {}, dirname, alias }: BasicDescriptor, cache, ): LoadedDescriptor => { let item = value; @@ -313,7 +308,7 @@ const loadDescriptor = makeWeakCache( throw new Error("Plugin/Preset did not return an object."); } - return { value: item, options, dirname, alias, loc }; + return { value: item, options, dirname, alias }; }, ); @@ -336,7 +331,7 @@ function loadPluginDescriptor(descriptor: BasicDescriptor): Plugin { const instantiatePlugin = makeWeakCache( ( - { value: pluginObj, options, dirname, alias, loc }: LoadedDescriptor, + { value: pluginObj, options, dirname, alias }: LoadedDescriptor, cache, ): Plugin => { Object.keys(pluginObj).forEach(key => { @@ -366,8 +361,7 @@ const instantiatePlugin = makeWeakCache( let inherits; if (plugin.inherits) { inheritsDescriptor = { - alias: `${loc}$inherits`, - loc, + alias: `${alias}$inherits`, value: plugin.inherits, options, dirname, @@ -402,12 +396,11 @@ const loadPresetDescriptor = (descriptor: BasicDescriptor): MergeOptions => { }; const instantiatePreset = makeWeakCache( - ({ value, dirname, alias, loc }: LoadedDescriptor): MergeOptions => { + ({ value, dirname, alias }: LoadedDescriptor): MergeOptions => { return { type: "preset", options: value, alias, - loc, dirname, }; }, From 3673fbbd52d9f94c4d3e0629b2d4970ab1d20b25 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Tue, 17 Oct 2017 15:59:08 -0700 Subject: [PATCH 05/11] Refactor config item processing. --- .../src/config/build-config-chain.js | 62 ++++++--------- .../babel-core/src/config/option-manager.js | 8 +- packages/babel-core/test/api.js | 12 +-- packages/babel-core/test/config-chain.js | 76 +++++++------------ 4 files changed, 63 insertions(+), 95 deletions(-) diff --git a/packages/babel-core/src/config/build-config-chain.js b/packages/babel-core/src/config/build-config-chain.js index ec55737bbd..78e97794cf 100644 --- a/packages/babel-core/src/config/build-config-chain.js +++ b/packages/babel-core/src/config/build-config-chain.js @@ -18,12 +18,20 @@ type ConfigItem = { alias: string, }; -type ConfigRaw = { - type: "options" | "arguments", - options: {}, - alias: string, - dirname: string, -}; +type ConfigPart = + | { + part: "config", + config: ConfigItem, + ignore: ?Array, + only: ?Array, + activeEnv: string | null, + } + | { + part: "extends", + path: string, + dirname: string, + activeEnv: string | null, + }; export default function buildConfigChain(opts: {}): Array | null { if (typeof opts.filename !== "string" && opts.filename != null) { @@ -160,7 +168,7 @@ function flattenArgumentsOptionsParts( if (opts.extends != null) { raw.push( ...flattenOptionsParts( - buildArgumentsRawConfig({ extends: opts.extends }, dirname), + buildArgumentsItem({ extends: opts.extends }, dirname), ), ); } @@ -176,7 +184,7 @@ const flattenArgumentsEnvOptionsParts = makeWeakCache((env: {}) => { const options = { env }; return makeStrongCache((dirname: string) => - flattenOptionsPartsLookup(buildArgumentsRawConfig(options, dirname)), + flattenOptionsPartsLookup(buildArgumentsItem(options, dirname)), ); }); @@ -189,7 +197,7 @@ const flattenArgumentsPluginsOptionsParts = makeWeakCache( const options = { plugins }; return makeStrongCache((dirname: string) => - flattenOptionsParts(buildArgumentsRawConfig(options, dirname)), + flattenOptionsParts(buildArgumentsItem(options, dirname)), ); }, ); @@ -207,12 +215,12 @@ const flattenArgumentsPresetsOptionsParts = makeWeakCache( const options = { presets, passPerPreset }; return makeStrongCache((dirname: string) => - flattenOptionsParts(buildArgumentsRawConfig(options, dirname)), + flattenOptionsParts(buildArgumentsItem(options, dirname)), ); }), ); -function buildArgumentsRawConfig(options: {}, dirname: string): ConfigRaw { +function buildArgumentsItem(options: {}, dirname: string): ConfigItem { return { type: "arguments", options, @@ -240,7 +248,7 @@ const flattenFileOptionsParts = makeWeakCache((file: ConfigFile) => { * the environment passed as the first argument. */ function flattenOptionsPartsLookup( - config: ConfigRaw, + config: ConfigItem, ): (string | null) => Array { const parts = flattenOptionsParts(config); @@ -262,30 +270,15 @@ function flattenOptionsPartsLookup( return envKey => lookup.get(envKey) || def; } -type ConfigPart = - | { - part: "config", - config: ConfigItem, - ignore: ?Array, - only: ?Array, - activeEnv: string | null, - } - | { - part: "extends", - path: string, - dirname: string, - activeEnv: string | null, - }; - /** * Given a generic config object, flatten it into its various parts so that * then can be cached and processed later. */ function flattenOptionsParts( - rawConfig: ConfigRaw, + config: ConfigItem, activeEnv: string | null = null, ): Array { - const { type, options: rawOpts, alias, dirname } = rawConfig; + const { type, options: rawOpts, alias, dirname } = config; if (rawOpts.ignore != null && !Array.isArray(rawOpts.ignore)) { throw new Error( @@ -340,18 +333,9 @@ function flattenOptionsParts( } }); - const options = Object.assign({}, rawOpts); - delete options.env; - delete options.extends; - parts.push({ part: "config", - config: { - type, - options, - alias, - dirname, - }, + config, ignore, only, activeEnv, diff --git a/packages/babel-core/src/config/option-manager.js b/packages/babel-core/src/config/option-manager.js index a5596f0c50..ae63a9ba92 100644 --- a/packages/babel-core/src/config/option-manager.js +++ b/packages/babel-core/src/config/option-manager.js @@ -137,7 +137,13 @@ class OptionManager { pass.unshift(...plugins); } - merge(this.options, result.options); + const options = Object.assign({}, result.options); + delete options.extends; + delete options.env; + delete options.plugins; + delete options.presets; + + merge(this.options, options); } init(opts: {}) { diff --git a/packages/babel-core/test/api.js b/packages/babel-core/test/api.js index 1ab0ece980..e66d88d6cf 100644 --- a/packages/babel-core/test/api.js +++ b/packages/babel-core/test/api.js @@ -530,31 +530,31 @@ describe("api", function() { it("default", function() { const result = babel.transform("foo;", { env: { - development: { code: false }, + development: { comments: false }, }, }); - assert.equal(result.code, undefined); + assert.equal(result.options.comments, false); }); it("BABEL_ENV", function() { process.env.BABEL_ENV = "foo"; const result = babel.transform("foo;", { env: { - foo: { code: false }, + foo: { comments: false }, }, }); - assert.equal(result.code, undefined); + assert.equal(result.options.comments, false); }); it("NODE_ENV", function() { process.env.NODE_ENV = "foo"; const result = babel.transform("foo;", { env: { - foo: { code: false }, + foo: { comments: false }, }, }); - assert.equal(result.code, undefined); + assert.equal(result.options.comments, false); }); }); diff --git a/packages/babel-core/test/config-chain.js b/packages/babel-core/test/config-chain.js index 23c8d95a57..e004a8aaa2 100644 --- a/packages/babel-core/test/config-chain.js +++ b/packages/babel-core/test/config-chain.js @@ -72,7 +72,6 @@ describe("buildConfigChain", function() { ], }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -117,7 +116,6 @@ describe("buildConfigChain", function() { ], }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -379,16 +377,15 @@ describe("buildConfigChain", function() { plugins: ["extended"], }, alias: fixture("extended.babelrc.json"), - loc: fixture("extended.babelrc.json"), dirname: fixture(), }, { type: "options", options: { + extends: "./extended.babelrc.json", plugins: ["root"], }, alias: fixture(".babelrc"), - loc: fixture(".babelrc"), dirname: fixture(), }, { @@ -397,7 +394,6 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { @@ -406,7 +402,6 @@ describe("buildConfigChain", function() { filename: fixture("dir1", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -426,7 +421,6 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { @@ -435,7 +429,6 @@ describe("buildConfigChain", function() { plugins: ["dir2"], }, alias: fixture("dir2", ".babelrc"), - loc: fixture("dir2", ".babelrc"), dirname: fixture("dir2"), }, { @@ -444,7 +437,6 @@ describe("buildConfigChain", function() { filename: fixture("dir2", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -464,16 +456,15 @@ describe("buildConfigChain", function() { plugins: ["extended"], }, alias: fixture("extended.babelrc.json"), - loc: fixture("extended.babelrc.json"), dirname: fixture(), }, { type: "options", options: { + extends: "./extended.babelrc.json", plugins: ["root"], }, alias: fixture(".babelrc"), - loc: fixture(".babelrc"), dirname: fixture(), }, { @@ -482,7 +473,6 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { @@ -491,7 +481,6 @@ describe("buildConfigChain", function() { filename: fixture("dir3", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -511,16 +500,22 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { type: "options", options: { + env: { + bar: { + plugins: ["env-bar"], + }, + foo: { + plugins: ["env-foo"], + }, + }, plugins: ["env-base"], }, alias: fixture("env", ".babelrc"), - loc: fixture("env", ".babelrc"), dirname: fixture("env"), }, { @@ -529,7 +524,6 @@ describe("buildConfigChain", function() { filename: fixture("env", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -551,16 +545,22 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { type: "options", options: { + env: { + bar: { + plugins: ["env-bar"], + }, + foo: { + plugins: ["env-foo"], + }, + }, plugins: ["env-base"], }, alias: fixture("env", ".babelrc"), - loc: fixture("env", ".babelrc"), dirname: fixture("env"), }, { @@ -569,7 +569,6 @@ describe("buildConfigChain", function() { plugins: ["env-foo"], }, alias: fixture("env", ".babelrc.env.foo"), - loc: fixture("env", ".babelrc.env.foo"), dirname: fixture("env"), }, { @@ -578,7 +577,6 @@ describe("buildConfigChain", function() { filename: fixture("env", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -601,16 +599,22 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { type: "options", options: { + env: { + bar: { + plugins: ["env-bar"], + }, + foo: { + plugins: ["env-foo"], + }, + }, plugins: ["env-base"], }, alias: fixture("env", ".babelrc"), - loc: fixture("env", ".babelrc"), dirname: fixture("env"), }, { @@ -619,7 +623,6 @@ describe("buildConfigChain", function() { plugins: ["env-bar"], }, alias: fixture("env", ".babelrc.env.bar"), - loc: fixture("env", ".babelrc.env.bar"), dirname: fixture("env"), }, { @@ -628,7 +631,6 @@ describe("buildConfigChain", function() { filename: fixture("env", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -650,7 +652,6 @@ describe("buildConfigChain", function() { plugins: ["pkg-plugin"], }, alias: fixture("pkg", "package.json"), - loc: fixture("pkg", "package.json"), dirname: fixture("pkg"), }, { @@ -659,7 +660,6 @@ describe("buildConfigChain", function() { ignore: ["pkg-ignore"], }, alias: fixture("pkg", ".babelignore"), - loc: fixture("pkg", ".babelignore"), dirname: fixture("pkg"), }, { @@ -668,7 +668,6 @@ describe("buildConfigChain", function() { filename: fixture("pkg", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -688,7 +687,6 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { @@ -697,7 +695,6 @@ describe("buildConfigChain", function() { plugins: ["foo", "bar"], }, alias: fixture("js-config", ".babelrc.js"), - loc: fixture("js-config", ".babelrc.js"), dirname: fixture("js-config"), }, { @@ -706,7 +703,6 @@ describe("buildConfigChain", function() { filename: fixture("js-config", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -726,7 +722,6 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { @@ -735,7 +730,6 @@ describe("buildConfigChain", function() { compact: true, }, alias: fixture("js-config-function", ".babelrc.js"), - loc: fixture("js-config-function", ".babelrc.js"), dirname: fixture("js-config-function"), }, { @@ -744,7 +738,6 @@ describe("buildConfigChain", function() { filename: fixture("js-config-function", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -764,7 +757,6 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { @@ -773,7 +765,6 @@ describe("buildConfigChain", function() { plugins: ["foo", "bar"], }, alias: fixture("js-config-default", ".babelrc.js"), - loc: fixture("js-config-default", ".babelrc.js"), dirname: fixture("js-config-default"), }, { @@ -782,7 +773,6 @@ describe("buildConfigChain", function() { filename: fixture("js-config-default", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -801,7 +791,6 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { @@ -810,16 +799,15 @@ describe("buildConfigChain", function() { plugins: ["extended"], }, alias: fixture("extended.babelrc.json"), - loc: fixture("extended.babelrc.json"), dirname: fixture(), }, { type: "options", options: { + extends: "../extended.babelrc.json", plugins: ["foo", "bar"], }, alias: fixture("js-config-extended", ".babelrc.js"), - loc: fixture("js-config-extended", ".babelrc.js"), dirname: fixture("js-config-extended"), }, { @@ -828,7 +816,6 @@ describe("buildConfigChain", function() { filename: fixture("js-config-extended", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -851,7 +838,6 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { @@ -860,7 +846,6 @@ describe("buildConfigChain", function() { plugins: ["json"], }, alias: fixture("json-pkg-config-no-babel", ".babelrc"), - loc: fixture("json-pkg-config-no-babel", ".babelrc"), dirname: fixture("json-pkg-config-no-babel"), }, { @@ -869,7 +854,6 @@ describe("buildConfigChain", function() { filename: fixture("json-pkg-config-no-babel", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -890,7 +874,6 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { @@ -899,7 +882,6 @@ describe("buildConfigChain", function() { ignore: ["*", "!src.js"], }, alias: fixture("ignore-negate", ".babelrc"), - loc: fixture("ignore-negate", ".babelrc"), dirname: fixture("ignore-negate"), }, { @@ -908,7 +890,6 @@ describe("buildConfigChain", function() { filename: fixture("ignore-negate", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; @@ -934,7 +915,6 @@ describe("buildConfigChain", function() { ignore: ["root-ignore"], }, alias: fixture(".babelignore"), - loc: fixture(".babelignore"), dirname: fixture(), }, { @@ -943,7 +923,6 @@ describe("buildConfigChain", function() { ignore: ["*", "!folder"], }, alias: fixture("ignore-negate-folder", ".babelrc"), - loc: fixture("ignore-negate-folder", ".babelrc"), dirname: fixture("ignore-negate-folder"), }, { @@ -952,7 +931,6 @@ describe("buildConfigChain", function() { filename: fixture("ignore-negate-folder", "folder", "src.js"), }, alias: "base", - loc: "base", dirname: base(), }, ]; From 64abf75d1f70a7115532fca854cd6159078a89b8 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Mon, 16 Oct 2017 18:33:32 -0700 Subject: [PATCH 06/11] Perform option validation up front to avoid repeating assertions. --- .../src/config/build-config-chain.js | 186 +++++-------- packages/babel-core/src/config/caching.js | 2 +- packages/babel-core/src/config/index.js | 2 +- .../src/config/option-assertions.js | 174 ++++++++++++ .../babel-core/src/config/option-manager.js | 185 +++---------- packages/babel-core/src/config/options.js | 255 ++++++++++++++++++ packages/babel-core/test/api.js | 2 +- packages/babel-core/test/config-chain.js | 66 ++--- packages/babel-core/test/option-manager.js | 6 +- 9 files changed, 571 insertions(+), 307 deletions(-) create mode 100644 packages/babel-core/src/config/option-assertions.js create mode 100644 packages/babel-core/src/config/options.js diff --git a/packages/babel-core/src/config/build-config-chain.js b/packages/babel-core/src/config/build-config-chain.js index 78e97794cf..c5f738cc11 100644 --- a/packages/babel-core/src/config/build-config-chain.js +++ b/packages/babel-core/src/config/build-config-chain.js @@ -4,6 +4,12 @@ import { getEnv } from "./helpers/environment"; import path from "path"; import micromatch from "micromatch"; import buildDebug from "debug"; +import { + validate, + type ValidatedOptions, + type PluginList, + type IgnoreList, +} from "./options"; const debug = buildDebug("babel:config:config-chain"); @@ -11,19 +17,19 @@ import { findConfigs, loadConfig, type ConfigFile } from "./loading/files"; import { makeWeakCache, makeStrongCache } from "./caching"; -type ConfigItem = { - type: "options" | "arguments", - options: {}, - dirname: string, +export type ConfigItem = { + type: "arguments" | "env" | "file", + options: ValidatedOptions, alias: string, + dirname: string, }; type ConfigPart = | { part: "config", config: ConfigItem, - ignore: ?Array, - only: ?Array, + ignore: ?IgnoreList, + only: ?IgnoreList, activeEnv: string | null, } | { @@ -33,11 +39,9 @@ type ConfigPart = activeEnv: string | null, }; -export default function buildConfigChain(opts: {}): Array | null { - if (typeof opts.filename !== "string" && opts.filename != null) { - throw new Error(".filename must be a string, null, or undefined"); - } - +export default function buildConfigChain( + opts: ValidatedOptions, +): Array | null { const filename = opts.filename ? path.resolve(opts.filename) : null; const builder = new ConfigChainBuilder( filename ? new LoadedFile(filename) : null, @@ -70,7 +74,11 @@ class ConfigChainBuilder { this.file = file; } - mergeConfigArguments(opts: {}, dirname: string, envKey: string) { + mergeConfigArguments( + opts: ValidatedOptions, + dirname: string, + envKey: string, + ) { flattenArgumentsOptionsParts(opts, dirname, envKey).forEach(part => this._processConfigPart(part, envKey), ); @@ -117,43 +125,26 @@ class ConfigChainBuilder { * object identity preserved between calls so that they can be used for caching. */ function flattenArgumentsOptionsParts( - opts: {}, + opts: ValidatedOptions, dirname: string, envKey: string, ): Array { + const { + env, + plugins, + presets, + passPerPreset, + extends: extendsPath, + ...options + } = opts; + const raw = []; - - const env = typeof opts.env === "object" ? opts.env : null; - const plugins = Array.isArray(opts.plugins) ? opts.plugins : null; - const presets = Array.isArray(opts.presets) ? opts.presets : null; - const passPerPreset = - typeof opts.passPerPreset === "boolean" ? opts.passPerPreset : false; - if (env) { raw.push(...flattenArgumentsEnvOptionsParts(env)(dirname)(envKey)); } - const innerOpts = Object.assign({}, opts); - // If the env, plugins, and presets values on the object aren't arrays or - // objects, leave them in the base opts so that normal options validation - // will throw errors on them later. - if (env) delete innerOpts.env; - if (plugins) delete innerOpts.plugins; - if (presets) { - delete innerOpts.presets; - delete innerOpts.passPerPreset; - } - delete innerOpts.extends; - - if (Object.keys(innerOpts).length > 0) { - raw.push( - ...flattenOptionsParts({ - type: "arguments", - options: innerOpts, - alias: "base", - dirname, - }), - ); + if (Object.keys(options).length > 0) { + raw.push(...flattenOptionsParts(buildArgumentsItem(options, dirname))); } if (plugins) { @@ -161,14 +152,14 @@ function flattenArgumentsOptionsParts( } if (presets) { raw.push( - ...flattenArgumentsPresetsOptionsParts(presets)(passPerPreset)(dirname), + ...flattenArgumentsPresetsOptionsParts(presets)(!!passPerPreset)(dirname), ); } - if (opts.extends != null) { + if (extendsPath != null) { raw.push( ...flattenOptionsParts( - buildArgumentsItem({ extends: opts.extends }, dirname), + buildArgumentsItem({ extends: extendsPath }, dirname), ), ); } @@ -181,7 +172,7 @@ function flattenArgumentsOptionsParts( * the object identity of the 'env' object. */ const flattenArgumentsEnvOptionsParts = makeWeakCache((env: {}) => { - const options = { env }; + const options: ValidatedOptions = { env }; return makeStrongCache((dirname: string) => flattenOptionsPartsLookup(buildArgumentsItem(options, dirname)), @@ -193,8 +184,8 @@ const flattenArgumentsEnvOptionsParts = makeWeakCache((env: {}) => { * the object identity of the 'plugins' object. */ const flattenArgumentsPluginsOptionsParts = makeWeakCache( - (plugins: Array) => { - const options = { plugins }; + (plugins: PluginList) => { + const options: ValidatedOptions = { plugins }; return makeStrongCache((dirname: string) => flattenOptionsParts(buildArgumentsItem(options, dirname)), @@ -207,8 +198,8 @@ const flattenArgumentsPluginsOptionsParts = makeWeakCache( * the object identity of the 'presets' object. */ const flattenArgumentsPresetsOptionsParts = makeWeakCache( - (presets: Array) => - makeStrongCache((passPerPreset: ?boolean) => { + (presets: PluginList) => + makeStrongCache((passPerPreset: boolean) => { // The concept of passPerPreset is integrally tied to the preset list // so unfortunately we need to copy both values here, adding an extra // layer of caching functions. @@ -220,7 +211,10 @@ const flattenArgumentsPresetsOptionsParts = makeWeakCache( }), ); -function buildArgumentsItem(options: {}, dirname: string): ConfigItem { +function buildArgumentsItem( + options: ValidatedOptions, + dirname: string, +): ConfigItem { return { type: "arguments", options, @@ -236,8 +230,8 @@ function buildArgumentsItem(options: {}, dirname: string): ConfigItem { */ const flattenFileOptionsParts = makeWeakCache((file: ConfigFile) => { return flattenOptionsPartsLookup({ - type: "options", - options: file.options, + type: "file", + options: validate("file", file.options), alias: file.filepath, dirname: file.dirname, }); @@ -278,74 +272,37 @@ function flattenOptionsParts( config: ConfigItem, activeEnv: string | null = null, ): Array { - const { type, options: rawOpts, alias, dirname } = config; - - if (rawOpts.ignore != null && !Array.isArray(rawOpts.ignore)) { - throw new Error( - `.ignore should be an array, ${JSON.stringify(rawOpts.ignore)} given`, - ); - } - if (rawOpts.only != null && !Array.isArray(rawOpts.only)) { - throw new Error( - `.only should be an array, ${JSON.stringify(rawOpts.only)} given`, - ); - } - const ignore = rawOpts.ignore || null; - const only = rawOpts.only || null; + const { options: rawOpts, alias, dirname } = config; const parts = []; - if ( - rawOpts.env != null && - (typeof rawOpts.env !== "object" || Array.isArray(rawOpts.env)) - ) { - throw new Error(".env block must be an object, null, or undefined"); + if (rawOpts.env) { + for (const envKey of Object.keys(rawOpts.env)) { + if (rawOpts.env[envKey]) { + parts.push( + ...flattenOptionsParts( + { + type: "env", + options: rawOpts.env[envKey], + alias: alias + `.env.${envKey}`, + dirname, + }, + envKey, + ), + ); + } + } } - const rawEnv = rawOpts.env || {}; - - Object.keys(rawEnv).forEach(envKey => { - const envOpts = rawEnv[envKey]; - - if (envOpts !== undefined && activeEnv !== null && activeEnv !== envKey) { - throw new Error(`Unreachable .env[${envKey}] block detected`); - } - - if ( - envOpts != null && - (typeof envOpts !== "object" || Array.isArray(envOpts)) - ) { - throw new Error(".env[...] block must be an object, null, or undefined"); - } - - if (envOpts) { - parts.push( - ...flattenOptionsParts( - { - type, - options: envOpts, - alias: alias + `.env.${envKey}`, - dirname, - }, - envKey, - ), - ); - } - }); - parts.push({ part: "config", config, - ignore, - only, + ignore: rawOpts.ignore, + only: rawOpts.only, activeEnv, }); if (rawOpts.extends != null) { - if (typeof rawOpts.extends !== "string") { - throw new Error(".extends must be a string"); - } - parts.push({ part: "extends", path: rawOpts.extends, @@ -372,8 +329,8 @@ class LoadedFile { * Tests if a filename should be ignored based on "ignore" and "only" options. */ shouldIgnore( - ignore: ?Array, - only: ?Array, + ignore: ?IgnoreList, + only: ?IgnoreList, dirname: string, ): boolean { if (ignore) { @@ -407,7 +364,7 @@ class LoadedFile { * Returns result of calling function with filename if pattern is a function. * Otherwise returns result of matching pattern Regex with filename. */ - _matchesPatterns(patterns: Array, dirname: string): boolean { + _matchesPatterns(patterns: IgnoreList, dirname: string): boolean { const res = []; const strings = []; const fns = []; @@ -415,12 +372,7 @@ class LoadedFile { patterns.forEach(pattern => { if (typeof pattern === "string") strings.push(pattern); else if (typeof pattern === "function") fns.push(pattern); - else if (pattern instanceof RegExp) res.push(pattern); - else { - throw new Error( - "Patterns must be a string, function, or regular expression", - ); - } + else res.push(pattern); }); const filename = this.filename; diff --git a/packages/babel-core/src/config/caching.js b/packages/babel-core/src/config/caching.js index 052e2803c3..bd12bc31ac 100644 --- a/packages/babel-core/src/config/caching.js +++ b/packages/babel-core/src/config/caching.js @@ -31,7 +31,7 @@ export function makeStrongCache( * configures its caching behavior. Cached values are stored weakly and the function argument must be * an object type. */ -export function makeWeakCache, ResultT>( +export function makeWeakCache | $ReadOnlyArray<*>, ResultT>( handler: (ArgT, CacheConfigurator) => ResultT, autoPermacache?: boolean, ): ArgT => ResultT { diff --git a/packages/babel-core/src/config/index.js b/packages/babel-core/src/config/index.js index 2676d24047..8e458cdf7d 100644 --- a/packages/babel-core/src/config/index.js +++ b/packages/babel-core/src/config/index.js @@ -16,7 +16,7 @@ export type PluginPasses = Array; * Standard API for loading Babel configuration data. Not for public consumption. */ export default function loadConfig(opts: mixed): ResolvedConfig | null { - if (opts != null && typeof opts !== "object") { + if (opts != null && (typeof opts !== "object" || Array.isArray(opts))) { throw new Error("Babel options must be an object, null, or undefined"); } diff --git a/packages/babel-core/src/config/option-assertions.js b/packages/babel-core/src/config/option-assertions.js new file mode 100644 index 0000000000..12aa4460c6 --- /dev/null +++ b/packages/babel-core/src/config/option-assertions.js @@ -0,0 +1,174 @@ +// @flow + +import type { + IgnoreList, + IgnoreItem, + PluginList, + PluginItem, + PluginTarget, + SourceMapsOption, + SourceTypeOption, + CompactOption, + RootInputSourceMapOption, +} from "./options"; + +export function assertSourceMaps(key: string, value: mixed): ?SourceMapsOption { + if ( + value != null && + typeof value !== "boolean" && + value !== "inline" && + value !== "both" + ) { + throw new Error( + `.${key} must be a boolean, "inline", "both", null, or undefined`, + ); + } + return value; +} + +export function assertCompact(key: string, value: mixed): ?CompactOption { + if (value != null && typeof value !== "boolean" && value !== "auto") { + throw new Error(`.${key} must be a boolean, "auto", null, or undefined`); + } + return value; +} + +export function assertSourceType(key: string, value: mixed): ?SourceTypeOption { + if (value != null && value !== "module" && value !== "script") { + throw new Error(`.${key} must be "module", "script", null, or undefined`); + } + return value; +} + +export function assertInputSourceMap( + key: string, + value: mixed, +): ?RootInputSourceMapOption { + if ( + value != null && + typeof value !== "boolean" && + typeof value !== "object" + ) { + throw new Error( + ".inputSourceMap must be a boolean, object, null, or undefined", + ); + } + return value; +} + +export function assertString(key: string, value: mixed): ?string { + if (value != null && typeof value !== "string") { + throw new Error(`.${key} must be a string, null, or undefined`); + } + return value; +} + +export function assertFunction(key: string, value: mixed): ?Function { + if (value != null && typeof value !== "function") { + throw new Error(`.${key} must be a function, null, or undefined`); + } + return value; +} + +export function assertBoolean(key: string, value: mixed): ?boolean { + if (value != null && typeof value !== "boolean") { + throw new Error(`.${key} must be a boolean, null, or undefined`); + } + return value; +} + +export function assertObject(key: string, value: mixed): ?{} { + if (value != null && (typeof value !== "object" || Array.isArray(value))) { + throw new Error(`.${key} must be an object, null, or undefined`); + } + return value; +} + +export function assertIgnoreList(key: string, value: mixed): ?IgnoreList { + const arr = assertArray(key, value); + if (arr) { + arr.forEach((item, i) => assertIgnoreItem(key, i, item)); + } + return (arr: any); +} +function assertIgnoreItem( + key: string, + index: number, + value: mixed, +): IgnoreItem { + if ( + typeof value !== "string" && + typeof value !== "function" && + !(value instanceof RegExp) + ) { + throw new Error( + `.${key}[${index}] must be an array of string/Funtion/RegExp values, or null, or undefined`, + ); + } + return value; +} + +export function assertPluginList(key: string, value: mixed): ?PluginList { + const arr = assertArray(key, value); + if (arr) { + // Loop instead of using `.map` in order to preserve object identity + // for plugin array for use during config chain processing. + arr.forEach((item, i) => assertPluginItem(key, i, item)); + } + return (arr: any); +} +function assertPluginItem( + key: string, + index: number, + value: mixed, +): PluginItem { + if (Array.isArray(value)) { + if (value.length === 0) { + throw new Error(`.${key}[${index}] must include an object`); + } + if (value.length > 2) { + throw new Error(`.${key}[${index}] may only be a two-tuple`); + } + + assertPluginTarget(key, index, true, value[0]); + + if (value.length === 2) { + const opts = value[1]; + if (opts != null && (typeof opts !== "object" || Array.isArray(opts))) { + throw new Error( + `.${key}[${index}][1] must be an object, null, or undefined`, + ); + } + } + } else { + assertPluginTarget(key, index, false, value); + } + + return (value: any); +} +function assertPluginTarget( + key: string, + index: number, + inArray: boolean, + value: mixed, +): PluginTarget { + if ( + (typeof value !== "object" || !value) && + typeof value !== "string" && + typeof value !== "function" + ) { + throw new Error( + `.${key}[${index}]${inArray + ? `[0]` + : ""} must be a string, object, function`, + ); + } + return value; +} + +function assertArray(key: string, value: mixed): ?$ReadOnlyArray { + if (value != null && !Array.isArray(value)) { + throw new Error(`.${key} must be an array, null, or undefined`); + } + return value; +} diff --git a/packages/babel-core/src/config/option-manager.js b/packages/babel-core/src/config/option-manager.js index ae63a9ba92..8a4537a272 100644 --- a/packages/babel-core/src/config/option-manager.js +++ b/packages/babel-core/src/config/option-manager.js @@ -4,13 +4,13 @@ import * as context from "../index"; import Plugin from "./plugin"; import defaults from "lodash/defaults"; import merge from "lodash/merge"; -import removed from "./removed"; -import buildConfigChain from "./build-config-chain"; +import buildConfigChain, { type ConfigItem } from "./build-config-chain"; import path from "path"; import traverse from "@babel/traverse"; import clone from "lodash/clone"; import { makeWeakCache } from "./caching"; import { getEnv } from "./helpers/environment"; +import { validate, type ValidatedOptions, type PluginItem } from "./options"; import { loadPlugin, @@ -19,50 +19,14 @@ import { loadGenerator, } from "./loading/files"; -type MergeOptions = { - +type: "arguments" | "options" | "preset", - options: {}, - alias: string, - dirname: string, -}; - -const optionNames = new Set([ - "filename", - "filenameRelative", - "inputSourceMap", - "env", - "retainLines", - "highlightCode", - "presets", - "plugins", - "ignore", - "only", - "code", - "ast", - "extends", - "comments", - "shouldPrintComment", - "wrapPluginVisitorMethod", - "compact", - "minified", - "sourceMaps", - "sourceMapTarget", - "sourceFileName", - "sourceRoot", - "babelrc", - "sourceType", - "auxiliaryCommentBefore", - "auxiliaryCommentAfter", - "getModuleId", - "moduleRoot", - "moduleIds", - "moduleId", - "passPerPreset", - // Deprecate top level parserOpts - "parserOpts", - // Deprecate top level generatorOpts - "generatorOpts", -]); +type MergeOptions = + | ConfigItem + | { + type: "preset", + options: ValidatedOptions, + alias: string, + dirname: string, + }; const ALLOWED_PLUGIN_KEYS = new Set([ "name", @@ -82,11 +46,11 @@ export default function manageOptions(opts: {}): { class OptionManager { constructor() { - this.options = createInitialOptions(); + this.options = {}; this.passes = [[]]; } - options: Object; + options: ValidatedOptions; passes: Array>; /** @@ -108,12 +72,6 @@ class OptionManager { loadPresetDescriptor(descriptor), ); - if ( - config.options.passPerPreset != null && - typeof config.options.passPerPreset !== "boolean" - ) { - throw new Error(".passPerPreset must be a boolean or undefined"); - } const passPerPreset = config.options.passPerPreset; pass = pass || this.passes[0]; @@ -142,12 +100,22 @@ class OptionManager { delete options.env; delete options.plugins; delete options.presets; + delete options.passPerPreset; + + // "sourceMap" is just aliased to sourceMap, so copy it over as + // we merge the options together. + if (options.sourceMap) { + options.sourceMaps = options.sourceMap; + delete options.sourceMap; + } merge(this.options, options); } - init(opts: {}) { - const configChain = buildConfigChain(opts); + init(inputOpts: {}) { + const args = validate("arguments", inputOpts); + + const configChain = buildConfigChain(args); if (!configChain) return null; try { @@ -158,15 +126,13 @@ class OptionManager { // There are a few case where thrown errors will try to annotate themselves multiple times, so // to keep things simple we just bail out if re-wrapping the message. if (!/^\[BABEL\]/.test(e.message)) { - const filename = - typeof opts.filename === "string" ? opts.filename : null; - e.message = `[BABEL] ${filename || "unknown"}: ${e.message}`; + e.message = `[BABEL] ${args.filename || "unknown"}: ${e.message}`; } throw e; } - opts = this.options; + const opts: Object = merge(createInitialOptions(), this.options); // Tack the passes onto the object itself so that, if this object is passed back to Babel a second time, // it will be in the right structure to not change behavior. @@ -175,6 +141,7 @@ class OptionManager { .slice(1) .filter(plugins => plugins.length > 0) .map(plugins => ({ plugins })); + opts.passPerPreset = opts.presets.length > 0; if (opts.inputSourceMap) { opts.sourceMaps = true; @@ -231,20 +198,13 @@ type LoadedDescriptor = { /** * Load and validate the given config into a set of options, plugins, and presets. */ -const loadConfig = makeWeakCache((config): { +const loadConfig = makeWeakCache((config: MergeOptions): { options: {}, plugins: Array, presets: Array, } => { const options = normalizeOptions(config); - if ( - config.options.plugins != null && - !Array.isArray(config.options.plugins) - ) { - throw new Error(".plugins should be an array, null, or undefined"); - } - const plugins = (config.options.plugins || []).map((plugin, index) => { const { filepath, value, options } = normalizePair( plugin, @@ -260,13 +220,6 @@ const loadConfig = makeWeakCache((config): { }; }); - if ( - config.options.presets != null && - !Array.isArray(config.options.presets) - ) { - throw new Error(".presets should be an array, null, or undefined"); - } - const presets = (config.options.presets || []).map((preset, index) => { const { filepath, value, options } = normalizePair( preset, @@ -405,7 +358,7 @@ const instantiatePreset = makeWeakCache( ({ value, dirname, alias }: LoadedDescriptor): MergeOptions => { return { type: "preset", - options: value, + options: validate("preset", value), alias, dirname, }; @@ -416,72 +369,12 @@ const instantiatePreset = makeWeakCache( * Validate and return the options object for the config. */ function normalizeOptions(config) { - const alias = config.alias || "foreign"; - const type = config.type; - - // - if (typeof config.options !== "object" || Array.isArray(config.options)) { - throw new TypeError(`Invalid options type for ${alias}`); - } - // const options = Object.assign({}, config.options); - if (type !== "arguments") { - if (options.filename !== undefined) { - throw new Error(`${alias}.filename is only allowed as a root argument`); - } - - if (options.babelrc !== undefined) { - throw new Error(`${alias}.babelrc is only allowed as a root argument`); - } - } - - if (type === "preset") { - if (options.only !== undefined) { - throw new Error(`${alias}.only is not supported in a preset`); - } - if (options.ignore !== undefined) { - throw new Error(`${alias}.ignore is not supported in a preset`); - } - if (options.extends !== undefined) { - throw new Error(`${alias}.extends is not supported in a preset`); - } - if (options.env !== undefined) { - throw new Error(`${alias}.env is not supported in a preset`); - } - } - - if (options.sourceMap !== undefined) { - if (options.sourceMaps !== undefined) { - throw new Error(`Both ${alias}.sourceMap and .sourceMaps have been set`); - } - - options.sourceMaps = options.sourceMap; - delete options.sourceMap; - } - - for (const key in options) { - // check for an unknown option - if (!optionNames.has(key)) { - if (removed[key]) { - const { message, version = 5 } = removed[key]; - - throw new ReferenceError( - `Using removed Babel ${version} option: ${alias}.${key} - ${message}`, - ); - } else { - // eslint-disable-next-line max-len - const unknownOptErr = `Unknown option: ${alias}.${key}. Check out http://babeljs.io/docs/usage/options/ for more information about options.`; - - throw new ReferenceError(unknownOptErr); - } - } - } - if (options.parserOpts && typeof options.parserOpts.parser === "string") { options.parserOpts = Object.assign({}, options.parserOpts); - options.parserOpts.parser = loadParser( + (options.parserOpts: any).parser = loadParser( options.parserOpts.parser, config.dirname, ).value; @@ -492,16 +385,12 @@ function normalizeOptions(config) { typeof options.generatorOpts.generator === "string" ) { options.generatorOpts = Object.assign({}, options.generatorOpts); - options.generatorOpts.generator = loadGenerator( + (options.generatorOpts: any).generator = loadGenerator( options.generatorOpts.generator, config.dirname, ).value; } - delete options.passPerPreset; - delete options.plugins; - delete options.presets; - return options; } @@ -509,7 +398,7 @@ function normalizeOptions(config) { * Given a plugin/preset item, resolve it into a standard format. */ function normalizePair( - pair: mixed, + pair: PluginItem, resolver, dirname, ): { @@ -519,14 +408,8 @@ function normalizePair( } { let options; let value = pair; - if (Array.isArray(pair)) { - if (pair.length > 2) { - throw new Error( - `Unexpected extra options ${JSON.stringify(pair.slice(2))}.`, - ); - } - - [value, options] = pair; + if (Array.isArray(value)) { + [value, options] = value; } let filepath = null; diff --git a/packages/babel-core/src/config/options.js b/packages/babel-core/src/config/options.js new file mode 100644 index 0000000000..75a91388f9 --- /dev/null +++ b/packages/babel-core/src/config/options.js @@ -0,0 +1,255 @@ +// @flow + +import removed from "./removed"; +import { + assertString, + assertBoolean, + assertObject, + assertInputSourceMap, + assertIgnoreList, + assertPluginList, + assertFunction, + assertSourceMaps, + assertCompact, + assertSourceType, +} from "./option-assertions"; + +type ValidatorSet = { + [string]: Validator, +}; + +type Validator = (string, mixed) => T; + +const ROOT_VALIDATORS: ValidatorSet = { + filename: (assertString: Validator< + $PropertyType, + >), + filenameRelative: (assertString: Validator< + $PropertyType, + >), + babelrc: (assertBoolean: Validator< + $PropertyType, + >), + code: (assertBoolean: Validator<$PropertyType>), + ast: (assertBoolean: Validator<$PropertyType>), +}; + +const NONPRESET_VALIDATORS: ValidatorSet = { + extends: (assertString: Validator< + $PropertyType, + >), + env: (assertEnvSet: Validator<$PropertyType>), + ignore: (assertIgnoreList: Validator< + $PropertyType, + >), + only: (assertIgnoreList: Validator<$PropertyType>), +}; + +const COMMON_VALIDATORS: ValidatorSet = { + // TODO: Should 'inputSourceMap' be moved to be a root-only option? + // We may want a boolean-only version to be a common option, with the + // object only allowed as a root config argument. + inputSourceMap: (assertInputSourceMap: Validator< + $PropertyType, + >), + presets: (assertPluginList: Validator< + $PropertyType, + >), + plugins: (assertPluginList: Validator< + $PropertyType, + >), + passPerPreset: (assertBoolean: Validator< + $PropertyType, + >), + retainLines: (assertBoolean: Validator< + $PropertyType, + >), + comments: (assertBoolean: Validator< + $PropertyType, + >), + shouldPrintComment: (assertFunction: Validator< + $PropertyType, + >), + compact: (assertCompact: Validator< + $PropertyType, + >), + minified: (assertBoolean: Validator< + $PropertyType, + >), + auxiliaryCommentBefore: (assertString: Validator< + $PropertyType, + >), + auxiliaryCommentAfter: (assertString: Validator< + $PropertyType, + >), + sourceType: (assertSourceType: Validator< + $PropertyType, + >), + wrapPluginVisitorMethod: (assertFunction: Validator< + $PropertyType, + >), + highlightCode: (assertBoolean: Validator< + $PropertyType, + >), + sourceMaps: (assertSourceMaps: Validator< + $PropertyType, + >), + sourceMap: (assertSourceMaps: Validator< + $PropertyType, + >), + sourceMapTarget: (assertString: Validator< + $PropertyType, + >), + sourceFileName: (assertString: Validator< + $PropertyType, + >), + sourceRoot: (assertString: Validator< + $PropertyType, + >), + getModuleId: (assertFunction: Validator< + $PropertyType, + >), + moduleRoot: (assertString: Validator< + $PropertyType, + >), + moduleIds: (assertBoolean: Validator< + $PropertyType, + >), + moduleId: (assertString: Validator< + $PropertyType, + >), + parserOpts: (assertObject: Validator< + $PropertyType, + >), + generatorOpts: (assertObject: Validator< + $PropertyType, + >), +}; +export type ValidatedOptions = { + filename?: ?string, + filenameRelative?: ?string, + babelrc?: ?boolean, + code?: ?boolean, + ast?: ?boolean, + inputSourceMap?: ?RootInputSourceMapOption, + + extends?: ?string, + env?: ?EnvSet, + ignore?: ?IgnoreList, + only?: ?IgnoreList, + + presets?: ?PluginList, + plugins?: ?PluginList, + passPerPreset?: ?boolean, + + // Options for @babel/generator + retainLines?: ?boolean, + comments?: ?boolean, + shouldPrintComment?: ?Function, + compact?: ?CompactOption, + minified?: ?boolean, + auxiliaryCommentBefore?: ?string, + auxiliaryCommentAfter?: ?string, + + // Parser + sourceType?: ?SourceTypeOption, + + wrapPluginVisitorMethod?: ?Function, + highlightCode?: ?boolean, + + // Sourcemap generation options. + sourceMaps?: ?SourceMapsOption, + sourceMap?: ?SourceMapsOption, + sourceMapTarget?: ?string, + sourceFileName?: ?string, + sourceRoot?: ?string, + + // AMD/UMD/SystemJS module naming options. + getModuleId?: ?Function, + moduleRoot?: ?string, + moduleIds?: ?boolean, + moduleId?: ?string, + + // Deprecate top level parserOpts + parserOpts?: ?{}, + // Deprecate top level generatorOpts + generatorOpts?: ?{}, +}; + +export type EnvSet = { + [string]: ?T, +}; +export type IgnoreItem = string | Function | RegExp; +export type IgnoreList = $ReadOnlyArray; + +export type PluginTarget = string | {} | Function; +export type PluginItem = PluginTarget | [PluginTarget, {} | void]; +export type PluginList = $ReadOnlyArray; + +export type SourceMapsOption = boolean | "inline" | "both"; +export type SourceTypeOption = "module" | "script"; +export type CompactOption = boolean | "auto"; +export type RootInputSourceMapOption = {} | boolean; + +export type OptionsType = "arguments" | "file" | "env" | "preset"; + +export function validate(type: OptionsType, opts: {}): ValidatedOptions { + assertNoDuplicateSourcemap(opts); + + Object.keys(opts).forEach(key => { + if (type === "preset" && NONPRESET_VALIDATORS[key]) { + throw new Error(`.${key} is not allowed in preset options`); + } + if (type !== "arguments" && ROOT_VALIDATORS[key]) { + throw new Error(`.${key} is only allowed in root programmatic options`); + } + + const validator = + COMMON_VALIDATORS[key] || + NONPRESET_VALIDATORS[key] || + ROOT_VALIDATORS[key]; + + if (validator) validator(key, opts[key]); + else throw buildUnknownError(key); + }); + + return (opts: any); +} + +function buildUnknownError(key: string) { + if (removed[key]) { + const { message, version = 5 } = removed[key]; + + throw new ReferenceError( + `Using removed Babel ${version} option: .${key} - ${message}`, + ); + } else { + // eslint-disable-next-line max-len + const unknownOptErr = `Unknown option: .${key}. Check out http://babeljs.io/docs/usage/options/ for more information about options.`; + + throw new ReferenceError(unknownOptErr); + } +} + +function has(obj: {}, key: string) { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +function assertNoDuplicateSourcemap(opts: {}): void { + if (has(opts, "sourceMap") && has(opts, "sourceMaps")) { + throw new Error(".sourceMap is an alias for .sourceMaps, cannot use both"); + } +} + +function assertEnvSet(key: string, value: mixed): ?EnvSet { + const obj = assertObject(key, value); + if (obj) { + // Validate but don't copy the .env object in order to preserve + // object identity for use during config chain processing. + for (const key of Object.keys(obj)) { + const env = assertObject(key, obj[key]); + if (env) validate("env", env); + } + } + return (obj: any); +} diff --git a/packages/babel-core/test/api.js b/packages/babel-core/test/api.js index e66d88d6cf..cad0308862 100644 --- a/packages/babel-core/test/api.js +++ b/packages/babel-core/test/api.js @@ -145,7 +145,7 @@ describe("api", function() { babel.transform("", { plugins: [__dirname + "/../../babel-plugin-syntax-jsx", false], }); - }, /Error: \[BABEL\] unknown: Unexpected falsy value: false/); + }, /.plugins\[1\] must be a string, object, function/); }); it("options merge backwards", function() { diff --git a/packages/babel-core/test/config-chain.js b/packages/babel-core/test/config-chain.js index e004a8aaa2..097112cb07 100644 --- a/packages/babel-core/test/config-chain.js +++ b/packages/babel-core/test/config-chain.js @@ -372,7 +372,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { plugins: ["extended"], }, @@ -380,7 +380,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { extends: "./extended.babelrc.json", plugins: ["root"], @@ -389,7 +389,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -416,7 +416,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -424,7 +424,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { plugins: ["dir2"], }, @@ -451,7 +451,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { plugins: ["extended"], }, @@ -459,7 +459,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { extends: "./extended.babelrc.json", plugins: ["root"], @@ -468,7 +468,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -495,7 +495,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -503,7 +503,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { env: { bar: { @@ -540,7 +540,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -548,7 +548,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { env: { bar: { @@ -564,7 +564,7 @@ describe("buildConfigChain", function() { dirname: fixture("env"), }, { - type: "options", + type: "env", options: { plugins: ["env-foo"], }, @@ -594,7 +594,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -602,7 +602,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { env: { bar: { @@ -618,7 +618,7 @@ describe("buildConfigChain", function() { dirname: fixture("env"), }, { - type: "options", + type: "env", options: { plugins: ["env-bar"], }, @@ -647,7 +647,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { plugins: ["pkg-plugin"], }, @@ -655,7 +655,7 @@ describe("buildConfigChain", function() { dirname: fixture("pkg"), }, { - type: "options", + type: "file", options: { ignore: ["pkg-ignore"], }, @@ -682,7 +682,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -690,7 +690,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { plugins: ["foo", "bar"], }, @@ -717,7 +717,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -725,7 +725,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { compact: true, }, @@ -752,7 +752,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -760,7 +760,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { plugins: ["foo", "bar"], }, @@ -786,7 +786,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -794,7 +794,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { plugins: ["extended"], }, @@ -802,7 +802,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { extends: "../extended.babelrc.json", plugins: ["foo", "bar"], @@ -833,7 +833,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -841,7 +841,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { plugins: ["json"], }, @@ -869,7 +869,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -877,7 +877,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { ignore: ["*", "!src.js"], }, @@ -910,7 +910,7 @@ describe("buildConfigChain", function() { const expected = [ { - type: "options", + type: "file", options: { ignore: ["root-ignore"], }, @@ -918,7 +918,7 @@ describe("buildConfigChain", function() { dirname: fixture(), }, { - type: "options", + type: "file", options: { ignore: ["*", "!folder"], }, diff --git a/packages/babel-core/test/option-manager.js b/packages/babel-core/test/option-manager.js index 7e233a2a67..f6429615f5 100644 --- a/packages/babel-core/test/option-manager.js +++ b/packages/babel-core/test/option-manager.js @@ -17,7 +17,7 @@ describe("option-manager", () => { manageOptions({ randomOption: true, }); - }, /Unknown option: base.randomOption/); + }, /Unknown option: .randomOption/); }); it("throws for removed babel 5 options", () => { @@ -29,7 +29,7 @@ describe("option-manager", () => { }); }, // eslint-disable-next-line max-len - /Using removed Babel 5 option: base.auxiliaryComment - Use `auxiliaryCommentBefore` or `auxiliaryCommentAfter`/, + /Using removed Babel 5 option: .auxiliaryComment - Use `auxiliaryCommentBefore` or `auxiliaryCommentAfter`/, ); }); @@ -47,7 +47,7 @@ describe("option-manager", () => { describe("source type", function() { it("should set module for .mjs extension", () => { const config = manageOptions({ - sourceType: "program", + sourceType: "script", filename: "foo.mjs", }); From 0f31ce568695aba61af0a41c3c89db6f2aa0dc01 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Wed, 18 Oct 2017 00:19:09 -0700 Subject: [PATCH 07/11] Disallow 'null' as a general blank option placeholder. --- .../src/config/option-assertions.js | 73 ++++++++++--------- packages/babel-core/src/config/options.js | 70 +++++++++--------- 2 files changed, 74 insertions(+), 69 deletions(-) diff --git a/packages/babel-core/src/config/option-assertions.js b/packages/babel-core/src/config/option-assertions.js index 12aa4460c6..01c65e4e14 100644 --- a/packages/babel-core/src/config/option-assertions.js +++ b/packages/babel-core/src/config/option-assertions.js @@ -12,30 +12,36 @@ import type { RootInputSourceMapOption, } from "./options"; -export function assertSourceMaps(key: string, value: mixed): ?SourceMapsOption { +export function assertSourceMaps( + key: string, + value: mixed, +): SourceMapsOption | void { if ( - value != null && + value !== undefined && typeof value !== "boolean" && value !== "inline" && value !== "both" ) { throw new Error( - `.${key} must be a boolean, "inline", "both", null, or undefined`, + `.${key} must be a boolean, "inline", "both", or undefined`, ); } return value; } -export function assertCompact(key: string, value: mixed): ?CompactOption { - if (value != null && typeof value !== "boolean" && value !== "auto") { - throw new Error(`.${key} must be a boolean, "auto", null, or undefined`); +export function assertCompact(key: string, value: mixed): CompactOption | void { + if (value !== undefined && typeof value !== "boolean" && value !== "auto") { + throw new Error(`.${key} must be a boolean, "auto", or undefined`); } return value; } -export function assertSourceType(key: string, value: mixed): ?SourceTypeOption { - if (value != null && value !== "module" && value !== "script") { - throw new Error(`.${key} must be "module", "script", null, or undefined`); +export function assertSourceType( + key: string, + value: mixed, +): SourceTypeOption | void { + if (value !== undefined && value !== "module" && value !== "script") { + throw new Error(`.${key} must be "module", "script", or undefined`); } return value; } @@ -43,48 +49,49 @@ export function assertSourceType(key: string, value: mixed): ?SourceTypeOption { export function assertInputSourceMap( key: string, value: mixed, -): ?RootInputSourceMapOption { +): RootInputSourceMapOption | void { if ( - value != null && + value !== undefined && typeof value !== "boolean" && - typeof value !== "object" + (typeof value !== "object" || !value) ) { - throw new Error( - ".inputSourceMap must be a boolean, object, null, or undefined", - ); + throw new Error(".inputSourceMap must be a boolean, object, or undefined"); } return value; } -export function assertString(key: string, value: mixed): ?string { - if (value != null && typeof value !== "string") { - throw new Error(`.${key} must be a string, null, or undefined`); +export function assertString(key: string, value: mixed): string | void { + if (value !== undefined && typeof value !== "string") { + throw new Error(`.${key} must be a string, or undefined`); } return value; } -export function assertFunction(key: string, value: mixed): ?Function { - if (value != null && typeof value !== "function") { - throw new Error(`.${key} must be a function, null, or undefined`); +export function assertFunction(key: string, value: mixed): Function | void { + if (value !== undefined && typeof value !== "function") { + throw new Error(`.${key} must be a function, or undefined`); } return value; } -export function assertBoolean(key: string, value: mixed): ?boolean { - if (value != null && typeof value !== "boolean") { - throw new Error(`.${key} must be a boolean, null, or undefined`); +export function assertBoolean(key: string, value: mixed): boolean | void { + if (value !== undefined && typeof value !== "boolean") { + throw new Error(`.${key} must be a boolean, or undefined`); } return value; } -export function assertObject(key: string, value: mixed): ?{} { - if (value != null && (typeof value !== "object" || Array.isArray(value))) { - throw new Error(`.${key} must be an object, null, or undefined`); +export function assertObject(key: string, value: mixed): {} | void { + if ( + value !== undefined && + (typeof value !== "object" || Array.isArray(value) || !value) + ) { + throw new Error(`.${key} must be an object, or undefined`); } return value; } -export function assertIgnoreList(key: string, value: mixed): ?IgnoreList { +export function assertIgnoreList(key: string, value: mixed): IgnoreList | void { const arr = assertArray(key, value); if (arr) { arr.forEach((item, i) => assertIgnoreItem(key, i, item)); @@ -102,13 +109,13 @@ function assertIgnoreItem( !(value instanceof RegExp) ) { throw new Error( - `.${key}[${index}] must be an array of string/Funtion/RegExp values, or null, or undefined`, + `.${key}[${index}] must be an array of string/Funtion/RegExp values, or or undefined`, ); } return value; } -export function assertPluginList(key: string, value: mixed): ?PluginList { +export function assertPluginList(key: string, value: mixed): PluginList | void { const arr = assertArray(key, value); if (arr) { // Loop instead of using `.map` in order to preserve object identity @@ -135,9 +142,7 @@ function assertPluginItem( if (value.length === 2) { const opts = value[1]; if (opts != null && (typeof opts !== "object" || Array.isArray(opts))) { - throw new Error( - `.${key}[${index}][1] must be an object, null, or undefined`, - ); + throw new Error(`.${key}[${index}][1] must be an object, or undefined`); } } } else { @@ -168,7 +173,7 @@ function assertPluginTarget( function assertArray(key: string, value: mixed): ?$ReadOnlyArray { if (value != null && !Array.isArray(value)) { - throw new Error(`.${key} must be an array, null, or undefined`); + throw new Error(`.${key} must be an array, or undefined`); } return value; } diff --git a/packages/babel-core/src/config/options.js b/packages/babel-core/src/config/options.js index 75a91388f9..c46ca0d3df 100644 --- a/packages/babel-core/src/config/options.js +++ b/packages/babel-core/src/config/options.js @@ -126,54 +126,54 @@ const COMMON_VALIDATORS: ValidatorSet = { >), }; export type ValidatedOptions = { - filename?: ?string, - filenameRelative?: ?string, - babelrc?: ?boolean, - code?: ?boolean, - ast?: ?boolean, - inputSourceMap?: ?RootInputSourceMapOption, + filename?: string, + filenameRelative?: string, + babelrc?: boolean, + code?: boolean, + ast?: boolean, + inputSourceMap?: RootInputSourceMapOption, - extends?: ?string, - env?: ?EnvSet, - ignore?: ?IgnoreList, - only?: ?IgnoreList, + extends?: string, + env?: EnvSet, + ignore?: IgnoreList, + only?: IgnoreList, - presets?: ?PluginList, - plugins?: ?PluginList, - passPerPreset?: ?boolean, + presets?: PluginList, + plugins?: PluginList, + passPerPreset?: boolean, // Options for @babel/generator - retainLines?: ?boolean, - comments?: ?boolean, - shouldPrintComment?: ?Function, - compact?: ?CompactOption, - minified?: ?boolean, - auxiliaryCommentBefore?: ?string, - auxiliaryCommentAfter?: ?string, + retainLines?: boolean, + comments?: boolean, + shouldPrintComment?: Function, + compact?: CompactOption, + minified?: boolean, + auxiliaryCommentBefore?: string, + auxiliaryCommentAfter?: string, // Parser - sourceType?: ?SourceTypeOption, + sourceType?: SourceTypeOption, - wrapPluginVisitorMethod?: ?Function, - highlightCode?: ?boolean, + wrapPluginVisitorMethod?: Function, + highlightCode?: boolean, // Sourcemap generation options. - sourceMaps?: ?SourceMapsOption, - sourceMap?: ?SourceMapsOption, - sourceMapTarget?: ?string, - sourceFileName?: ?string, - sourceRoot?: ?string, + sourceMaps?: SourceMapsOption, + sourceMap?: SourceMapsOption, + sourceMapTarget?: string, + sourceFileName?: string, + sourceRoot?: string, // AMD/UMD/SystemJS module naming options. - getModuleId?: ?Function, - moduleRoot?: ?string, - moduleIds?: ?boolean, - moduleId?: ?string, + getModuleId?: Function, + moduleRoot?: string, + moduleIds?: boolean, + moduleId?: string, // Deprecate top level parserOpts - parserOpts?: ?{}, + parserOpts?: {}, // Deprecate top level generatorOpts - generatorOpts?: ?{}, + generatorOpts?: {}, }; export type EnvSet = { @@ -241,7 +241,7 @@ function assertNoDuplicateSourcemap(opts: {}): void { } } -function assertEnvSet(key: string, value: mixed): ?EnvSet { +function assertEnvSet(key: string, value: mixed): EnvSet { const obj = assertObject(key, value); if (obj) { // Validate but don't copy the .env object in order to preserve From fed2a14da13b75c9b38912bb4412b490ce747d92 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Wed, 18 Oct 2017 10:44:58 -0700 Subject: [PATCH 08/11] Fail if there are cycles in config files. --- .../src/config/build-config-chain.js | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/babel-core/src/config/build-config-chain.js b/packages/babel-core/src/config/build-config-chain.js index c5f738cc11..5b4b8cee24 100644 --- a/packages/babel-core/src/config/build-config-chain.js +++ b/packages/babel-core/src/config/build-config-chain.js @@ -69,6 +69,7 @@ export default function buildConfigChain( class ConfigChainBuilder { file: LoadedFile | null; configs: Array = []; + seenFiles: Set = new Set(); constructor(file: LoadedFile | null) { this.file = file; @@ -85,9 +86,17 @@ class ConfigChainBuilder { } mergeConfigFile(file: ConfigFile, envKey: string) { - flattenFileOptionsParts(file)(envKey).forEach(part => - this._processConfigPart(part, envKey), - ); + if (this.seenFiles.has(file)) { + throw new Error( + `Cycle detected in Babel configuration file through "${file.filepath}".`, + ); + } + + const parts = flattenFileOptionsParts(file)(envKey); + + this.seenFiles.add(file); + parts.forEach(part => this._processConfigPart(part, envKey)); + this.seenFiles.delete(file); } _processConfigPart(part: ConfigPart, envKey: string) { @@ -107,14 +116,7 @@ class ConfigChainBuilder { this.configs.push(part.config); } else { - const extendsConfig = loadConfig(part.path, part.dirname); - - const existingConfig = this.configs.some(config => { - return config.alias === extendsConfig.filepath; - }); - if (!existingConfig) { - this.mergeConfigFile(extendsConfig, envKey); - } + this.mergeConfigFile(loadConfig(part.path, part.dirname), envKey); } } } From 1d6cbd8dce563fdf572dce44a4f22fd6cd613185 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Thu, 19 Oct 2017 01:30:50 -0700 Subject: [PATCH 09/11] Standardize descriptor creation. --- .../babel-core/src/config/option-manager.js | 62 ++++++++----------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/packages/babel-core/src/config/option-manager.js b/packages/babel-core/src/config/option-manager.js index 8a4537a272..641440c353 100644 --- a/packages/babel-core/src/config/option-manager.js +++ b/packages/babel-core/src/config/option-manager.js @@ -205,35 +205,19 @@ const loadConfig = makeWeakCache((config: MergeOptions): { } => { const options = normalizeOptions(config); - const plugins = (config.options.plugins || []).map((plugin, index) => { - const { filepath, value, options } = normalizePair( - plugin, - loadPlugin, - config.dirname, - ); + const plugins = (config.options.plugins || []).map((plugin, index) => + createDescriptor(plugin, loadPlugin, config.dirname, { + index, + alias: config.alias, + }), + ); - return { - alias: filepath || `${config.alias}$${index}`, - value, - options, - dirname: config.dirname, - }; - }); - - const presets = (config.options.presets || []).map((preset, index) => { - const { filepath, value, options } = normalizePair( - preset, - loadPreset, - config.dirname, - ); - - return { - alias: filepath || `${config.alias}$${index}`, - value, - options, - dirname: config.dirname, - }; - }); + const presets = (config.options.presets || []).map((preset, index) => + createDescriptor(preset, loadPreset, config.dirname, { + index, + alias: config.alias, + }), + ); return { options, plugins, presets }; }); @@ -397,15 +381,18 @@ function normalizeOptions(config) { /** * Given a plugin/preset item, resolve it into a standard format. */ -function normalizePair( +function createDescriptor( pair: PluginItem, resolver, dirname, -): { - filepath: string | null, - value: {} | Function, - options: {} | void, -} { + { + index, + alias, + }: { + index: number, + alias: string, + }, +): BasicDescriptor { let options; let value = pair; if (Array.isArray(value)) { @@ -451,7 +438,12 @@ function normalizePair( } options = options || undefined; - return { filepath, value, options }; + return { + alias: filepath || `${alias}$${index}`, + value, + options, + dirname, + }; } function chain(a, b) { From 2d0211a0856c15a7c508e426d109e0a1b4caa876 Mon Sep 17 00:00:00 2001 From: Raja Sekar Date: Thu, 2 Nov 2017 21:26:27 +0530 Subject: [PATCH 10/11] Fix parsing arrow with existential return type (#6726) --- packages/babylon/src/tokenizer/index.js | 3 +- .../good_13/actual.js | 1 + .../good_13/expected.json | 152 ++++++++++++++++ .../fixtures/flow/classes/good_01/actual.js | 1 + .../flow/classes/good_01/expected.json | 164 ++++++++++++++++++ .../flow/classes/good_01/options.json | 3 + 6 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 packages/babylon/test/fixtures/flow/anonymous-function-no-parens-types/good_13/actual.js create mode 100644 packages/babylon/test/fixtures/flow/anonymous-function-no-parens-types/good_13/expected.json create mode 100644 packages/babylon/test/fixtures/flow/classes/good_01/actual.js create mode 100644 packages/babylon/test/fixtures/flow/classes/good_01/expected.json create mode 100644 packages/babylon/test/fixtures/flow/classes/good_01/options.json diff --git a/packages/babylon/src/tokenizer/index.js b/packages/babylon/src/tokenizer/index.js index fa484ed017..26d003094f 100644 --- a/packages/babylon/src/tokenizer/index.js +++ b/packages/babylon/src/tokenizer/index.js @@ -437,6 +437,7 @@ export default class Tokenizer extends LocationParser { let type = code === 42 ? tt.star : tt.modulo; let width = 1; let next = this.input.charCodeAt(this.state.pos + 1); + const exprAllowed = this.state.exprAllowed; // Exponentiation operator ** if (code === 42 && next === 42) { @@ -445,7 +446,7 @@ export default class Tokenizer extends LocationParser { type = tt.exponent; } - if (next === 61) { + if (next === 61 && !exprAllowed) { width++; type = tt.assign; } diff --git a/packages/babylon/test/fixtures/flow/anonymous-function-no-parens-types/good_13/actual.js b/packages/babylon/test/fixtures/flow/anonymous-function-no-parens-types/good_13/actual.js new file mode 100644 index 0000000000..feb53f2b4b --- /dev/null +++ b/packages/babylon/test/fixtures/flow/anonymous-function-no-parens-types/good_13/actual.js @@ -0,0 +1 @@ +const x = ():*=>{} diff --git a/packages/babylon/test/fixtures/flow/anonymous-function-no-parens-types/good_13/expected.json b/packages/babylon/test/fixtures/flow/anonymous-function-no-parens-types/good_13/expected.json new file mode 100644 index 0000000000..ad9ad5c630 --- /dev/null +++ b/packages/babylon/test/fixtures/flow/anonymous-function-no-parens-types/good_13/expected.json @@ -0,0 +1,152 @@ +{ + "type": "File", + "start": 0, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "sourceType": "module", + "body": [ + { + "type": "VariableDeclaration", + "start": 0, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 6, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "id": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "x" + }, + "name": "x" + }, + "init": { + "type": "ArrowFunctionExpression", + "start": 10, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "predicate": null, + "returnType": { + "type": "TypeAnnotation", + "start": 12, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 14 + } + }, + "typeAnnotation": { + "type": "ExistsTypeAnnotation", + "start": 13, + "end": 14, + "loc": { + "start": { + "line": 1, + "column": 13 + }, + "end": { + "line": 1, + "column": 14 + } + } + } + }, + "id": null, + "generator": false, + "expression": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 16, + "end": 18, + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 18 + } + }, + "body": [], + "directives": [] + } + } + } + ], + "kind": "const" + } + ], + "directives": [] + } +} diff --git a/packages/babylon/test/fixtures/flow/classes/good_01/actual.js b/packages/babylon/test/fixtures/flow/classes/good_01/actual.js new file mode 100644 index 0000000000..aabb328bdf --- /dev/null +++ b/packages/babylon/test/fixtures/flow/classes/good_01/actual.js @@ -0,0 +1 @@ +class C { field:*=null } diff --git a/packages/babylon/test/fixtures/flow/classes/good_01/expected.json b/packages/babylon/test/fixtures/flow/classes/good_01/expected.json new file mode 100644 index 0000000000..85cf0ca075 --- /dev/null +++ b/packages/babylon/test/fixtures/flow/classes/good_01/expected.json @@ -0,0 +1,164 @@ +{ + "type": "File", + "start": 0, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 24 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 24 + } + }, + "sourceType": "module", + "body": [ + { + "type": "ClassDeclaration", + "start": 0, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 24 + } + }, + "id": { + "type": "Identifier", + "start": 6, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 7 + }, + "identifierName": "C" + }, + "name": "C" + }, + "superClass": null, + "body": { + "type": "ClassBody", + "start": 8, + "end": 24, + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 24 + } + }, + "body": [ + { + "type": "ClassProperty", + "start": 10, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "static": false, + "key": { + "type": "Identifier", + "start": 10, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 15 + }, + "identifierName": "field" + }, + "name": "field" + }, + "computed": false, + "variance": null, + "typeAnnotation": { + "type": "TypeAnnotation", + "start": 15, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "typeAnnotation": { + "type": "ExistsTypeAnnotation", + "start": 16, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 17 + } + } + } + }, + "value": { + "type": "NullLiteral", + "start": 18, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 22 + } + } + } + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babylon/test/fixtures/flow/classes/good_01/options.json b/packages/babylon/test/fixtures/flow/classes/good_01/options.json new file mode 100644 index 0000000000..65c79d81c7 --- /dev/null +++ b/packages/babylon/test/fixtures/flow/classes/good_01/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["flow", "classProperties"] +} From ce53c7668a2ed32457a275fdb97c5fb7927de279 Mon Sep 17 00:00:00 2001 From: MICHAEL JACKSON Date: Thu, 2 Nov 2017 10:36:05 -0700 Subject: [PATCH 11/11] Fix unpkg link (#6730) [skip ci] --- packages/babel-standalone/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-standalone/README.md b/packages/babel-standalone/README.md index 81681ef825..4743209f7f 100644 --- a/packages/babel-standalone/README.md +++ b/packages/babel-standalone/README.md @@ -18,7 +18,7 @@ Installation There are several ways to get a copy of @babel/standalone. Pick whichever one you like: -- Use it via UNPKG: https://unpkg.com/@babel/standalone@6/babel.min.js. This is a simple way to embed it on a webpage without having to do any other setup. +- Use it via UNPKG: https://unpkg.com/@babel/standalone/babel.min.js. This is a simple way to embed it on a webpage without having to do any other setup. - Install via Bower: `bower install @babel/standalone` - Install via NPM: `npm install --save @babel/standalone` - Manually grab `babel.js` and/or `babel.min.js` from the [GitHub releases page](https://github.com/Daniel15/babel-standalone/releases). Every release includes these files.