diff --git a/packages/babel-core/src/transformation/file/options/option-manager.js b/packages/babel-core/src/transformation/file/options/option-manager.js index 3e6209abfc..7a106f8ada 100644 --- a/packages/babel-core/src/transformation/file/options/option-manager.js +++ b/packages/babel-core/src/transformation/file/options/option-manager.js @@ -181,12 +181,10 @@ export default class OptionManager { this.log.error(`Using removed Babel 5 option: ${alias}.${key} - ${removed[key].message}`, ReferenceError); } else { - /* eslint-disable max-len */ + // 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.`; - const presetConfigErr = "A common cause of this error is the presence of a configuration options object without the corresponding preset name. Example:\n\nInvalid:\n `{ presets: [{option: value}] }`\nValid:\n `{ presets: [['presetName', {option: value}]] }`\n\nFor more detailed information on preset configuration, please see http://babeljs.io/docs/plugins/#pluginpresets-options."; - /* eslint-enable max-len */ - this.log.error(`${unknownOptErr}\n\n${presetConfigErr}`, ReferenceError); + this.log.error(unknownOptErr, ReferenceError); } } } @@ -250,72 +248,65 @@ export default class OptionManager { * or a module name to require. */ resolvePresets(presets: Array, dirname: string, onResolve?) { - return presets.map((val) => { + return presets.map((preset) => { let options; - if (Array.isArray(val)) { - if (val.length > 2) { - throw new Error(`Unexpected extra options ${JSON.stringify(val.slice(2))} passed to preset.`); + if (Array.isArray(preset)) { + if (preset.length > 2) { + throw new Error(`Unexpected extra options ${JSON.stringify(preset.slice(2))} passed to preset.`); } - [val, options] = val; + [preset, options] = preset; } let presetLoc; try { - if (typeof val === "string") { - presetLoc = resolvePreset(val, dirname); + if (typeof preset === "string") { + presetLoc = resolvePreset(preset, dirname); if (!presetLoc) { - throw new Error(`Couldn't find preset ${JSON.stringify(val)} relative to directory ` + + throw new Error(`Couldn't find preset ${JSON.stringify(preset)} relative to directory ` + JSON.stringify(dirname)); } - - val = require(presetLoc); } + const presetFactory = this.getPresetFactoryForPreset(presetLoc || preset); - // If the imported preset is a transpiled ES2015 module - if (typeof val === "object" && val.__esModule) { - // Try to grab the default export. - if (val.default) { - val = val.default; - } else { - // If there is no default export we treat all named exports as options - // and just remove the __esModule. This is to support presets that have been - // exporting named exports in the past, although we definitely want presets to - // only use the default export (with either an object or a function) - const { __esModule, ...rest } = val; // eslint-disable-line no-unused-vars - val = rest; - } - } + preset = presetFactory(context, options); - // For compatibility with babel-core < 6.13.x, allow presets to export an object with a - // a 'buildPreset' function that will return the preset itself, while still exporting a - // simple object (rather than a function), for supporting old Babel versions. - if (typeof val === "object" && val.buildPreset) val = val.buildPreset; - - - if (typeof val !== "function" && options !== undefined) { - throw new Error(`Options ${JSON.stringify(options)} passed to ` + - (presetLoc || "a preset") + " which does not accept options."); - } - - if (typeof val === "function") val = val(context, options); - - if (typeof val !== "object") { - throw new Error(`Unsupported preset format: ${val}.`); - } - - onResolve && onResolve(val, presetLoc); + if (onResolve) onResolve(preset, presetLoc); } catch (e) { if (presetLoc) { e.message += ` (While processing preset: ${JSON.stringify(presetLoc)})`; } throw e; } - return val; + + return preset; }); } + getPresetFactoryForPreset(preset) { + let presetFactory = preset; + if (typeof presetFactory === "string") { + presetFactory = require(presetFactory); + } + + // If the imported preset is a transpiled ES2015 module + if (typeof presetFactory === "object" && presetFactory.__esModule) { + if (presetFactory.default) { + presetFactory = presetFactory.default; + } else { + throw new Error("Preset must export a default export when using ES6 modules."); + } + } + + if (typeof presetFactory !== "function") { + // eslint-disable-next-line max-len + throw new Error(`Unsupported preset format: ${typeof presetFactory}. Expected preset to return a function.`); + } + + return presetFactory; + } + normaliseOptions() { const opts = this.options; diff --git a/packages/babel-core/test/api.js b/packages/babel-core/test/api.js index 7cd5157afe..8344bacd54 100644 --- a/packages/babel-core/test/api.js +++ b/packages/babel-core/test/api.js @@ -194,38 +194,42 @@ describe("api", function () { passPerPreset: passPerPreset, presets: [ // First preset with our plugin, "before" - { - plugins: [ - new Plugin({ - visitor: { - Function: function(path) { - const alias = path.scope.getProgramParent().path.get("body")[0].node; - if (!babel.types.isTypeAlias(alias)) return; + function () { + return { + plugins: [ + new Plugin({ + visitor: { + Function: function (path) { + const alias = path.scope.getProgramParent().path.get("body")[0].node; + if (!babel.types.isTypeAlias(alias)) return; - // In case of `passPerPreset` being `false`, the - // alias node is already removed by Flow plugin. - if (!alias) { - return; + // In case of `passPerPreset` being `false`, the + // alias node is already removed by Flow plugin. + if (!alias) { + return; + } + + // In case of `passPerPreset` being `true`, the + // alias node should still exist. + aliasBaseType = alias.right.type; // NumberTypeAnnotation } - - // In case of `passPerPreset` being `true`, the - // alias node should still exist. - aliasBaseType = alias.right.type; // NumberTypeAnnotation } - } - }) - ] + }) + ] + }; }, // ES2015 preset require(__dirname + "/../../babel-preset-es2015"), // Third preset for Flow. - { - plugins: [ - require(__dirname + "/../../babel-plugin-syntax-flow"), - require(__dirname + "/../../babel-plugin-transform-flow-strip-types"), - ] + function () { + return { + plugins: [ + require(__dirname + "/../../babel-plugin-syntax-flow"), + require(__dirname + "/../../babel-plugin-transform-flow-strip-types"), + ] + }; } ], }); diff --git a/packages/babel-core/test/fixtures/option-manager/not-a-preset.js b/packages/babel-core/test/fixtures/option-manager/not-a-preset.js index 7059dfdbe3..67e7ffccf5 100644 --- a/packages/babel-core/test/fixtures/option-manager/not-a-preset.js +++ b/packages/babel-core/test/fixtures/option-manager/not-a-preset.js @@ -1,3 +1,3 @@ module.exports = function () { throw new Error('Not a real preset'); -} +}; diff --git a/packages/babel-core/test/fixtures/option-manager/presets/es2015_default_object_function.js b/packages/babel-core/test/fixtures/option-manager/presets/es2015_default_object_function.js deleted file mode 100644 index b5f1eef249..0000000000 --- a/packages/babel-core/test/fixtures/option-manager/presets/es2015_default_object_function.js +++ /dev/null @@ -1,18 +0,0 @@ -// from code: -// export default { -// buildPreset: function() { -// return { -// plugins: [require('../../../../../babel-plugin-syntax-decorators'),] -// }; -// } -// } -'use strict'; - -exports.__esModule = true; -exports.default = { - buildPreset: function buildPreset() { - return { - plugins: [require('../../../../../babel-plugin-syntax-decorators')] - }; - } -}; diff --git a/packages/babel-core/test/fixtures/option-manager/presets/es2015_function.js b/packages/babel-core/test/fixtures/option-manager/presets/es2015_function.js deleted file mode 100644 index eddcf7e294..0000000000 --- a/packages/babel-core/test/fixtures/option-manager/presets/es2015_function.js +++ /dev/null @@ -1,17 +0,0 @@ -// from code: -// export const buildPreset = function() { -// return { -// plugins: [require('../../../../../babel-plugin-syntax-decorators'),] -// }; -// } -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var buildPreset = exports.buildPreset = function buildPreset() { - return { - plugins: [require('../../../../../babel-plugin-syntax-decorators')] - }; -}; diff --git a/packages/babel-core/test/fixtures/option-manager/presets/es2015_function_fallback.js b/packages/babel-core/test/fixtures/option-manager/presets/es2015_function_fallback.js deleted file mode 100644 index d0c11d0258..0000000000 --- a/packages/babel-core/test/fixtures/option-manager/presets/es2015_function_fallback.js +++ /dev/null @@ -1,50 +0,0 @@ -// from code: -// function preset() { -// return { -// plugins: [ -// require('../../../../../babel-plugin-syntax-decorators'), -// ] -// }; -// } -// -// const oldConfig = preset(); -// -// export default oldConfig; -// -// // However, for backward compatibility with babel-core < v6.13.x, we use the 'buildPreset' -// // property of the preset object for the preset creation function with the enumerability -// // caveat mentioned below. -// Object.defineProperty(oldConfig, "buildPreset", { -// configurable: true, -// writable: true, -// // We make this non-enumerable so old versions of babel-core won't see it as an unknown property, -// // while allowing new versions to see it as a preset builder function. -// enumerable: false, -// value: preset, -// }); -// - -"use strict"; - -exports.__esModule = true; -function preset() { - return { - plugins: [require('../../../../../babel-plugin-syntax-decorators')] - }; -} - -var oldConfig = preset(); - -exports.default = oldConfig; - -// However, for backward compatibility with babel-core < v6.13.x, we use the 'buildPreset' -// property of the preset object for the preset creation function with the enumerability -// caveat mentioned below. -Object.defineProperty(oldConfig, "buildPreset", { - configurable: true, - writable: true, - // We make this non-enumerable so old versions of babel-core won't see it as an unknown property, - // while allowing new versions to see it as a preset builder function. - enumerable: false, - value: preset -}); diff --git a/packages/babel-core/test/fixtures/option-manager/presets/es5_function.js b/packages/babel-core/test/fixtures/option-manager/presets/es5_function.js index 31648d0600..71c789f9df 100644 --- a/packages/babel-core/test/fixtures/option-manager/presets/es5_function.js +++ b/packages/babel-core/test/fixtures/option-manager/presets/es5_function.js @@ -1,9 +1,7 @@ -module.exports = { - buildPreset: function () { - return { - plugins: [ - require('../../../../../babel-plugin-syntax-decorators'), - ] - }; - } +module.exports = function () { + return { + plugins: [ + require('../../../../../babel-plugin-syntax-decorators'), + ] + }; }; diff --git a/packages/babel-core/test/fixtures/option-manager/presets/es5_invalid.js b/packages/babel-core/test/fixtures/option-manager/presets/es5_invalid.js new file mode 100644 index 0000000000..258dbaf2cc --- /dev/null +++ b/packages/babel-core/test/fixtures/option-manager/presets/es5_invalid.js @@ -0,0 +1,2 @@ +'use strict'; +module.exports = "invalid"; diff --git a/packages/babel-core/test/fixtures/resolution/resolve-addons-relative-to-file/node_modules/addons/preset.js b/packages/babel-core/test/fixtures/resolution/resolve-addons-relative-to-file/node_modules/addons/preset.js index a1af16e41f..b2f7d13c73 100644 --- a/packages/babel-core/test/fixtures/resolution/resolve-addons-relative-to-file/node_modules/addons/preset.js +++ b/packages/babel-core/test/fixtures/resolution/resolve-addons-relative-to-file/node_modules/addons/preset.js @@ -1,5 +1,7 @@ -module.exports = { - plugins: [plugin], +module.exports = function () { + return { + plugins: [plugin], + } }; function plugin () { diff --git a/packages/babel-core/test/option-manager.js b/packages/babel-core/test/option-manager.js index 2ca432dd20..582b61d8be 100644 --- a/packages/babel-core/test/option-manager.js +++ b/packages/babel-core/test/option-manager.js @@ -51,19 +51,6 @@ describe("option-manager", () => { /While processing preset: .*option-manager(?:\/|\\\\)not-a-preset\.js/ ); }); - - it("throws for invalid preset configuration", function() { - return assert.throws( - function () { - const opt = new OptionManager(new Logger(null, "unknown")); - opt.init({ - "presets": [{ option: "value" }] - }); - }, - // eslint-disable-next-line max-len - /Unknown option: foreign.option\.(?:.|\n)+A common cause of this error is the presence of a configuration options object without the corresponding preset name/ - ); - }); }); describe("presets", function () { @@ -79,14 +66,21 @@ describe("option-manager", () => { }); } - presetTest("es5"); - presetTest("es5_function"); - presetTest("es2015_default"); - presetTest("es2015_default_function"); - presetTest("es2015_default_object_function"); - presetTest("es2015_function"); - presetTest("es2015_function_fallback"); - presetTest("es2015_named"); + function presetThrowsTest(name, msg) { + it(name, function () { + const opt = new OptionManager(new Logger(null, "unknown")); + assert.throws(() => opt.init({ + "presets": [path.join(__dirname, "fixtures/option-manager/presets", name)] + }), msg); + }); + } + presetTest("es5_function"); + presetTest("es2015_default_function"); + + presetThrowsTest("es5", /Expected preset to return a function./); + presetThrowsTest("es2015_default", /Expected preset to return a function./); + presetThrowsTest("es2015_named", /Preset must export a default export when using ES6 modules/); + presetThrowsTest("es5_invalid", /Unsupported preset format: string/); }); }); diff --git a/packages/babel-preset-es2015/src/index.js b/packages/babel-preset-es2015/src/index.js index 57d96082d1..dcff99017a 100644 --- a/packages/babel-preset-es2015/src/index.js +++ b/packages/babel-preset-es2015/src/index.js @@ -1,11 +1,3 @@ -/** - * This file is not the canonical way of writing a Babel preset since it strives for - * backward compatibility with babel-core < v6.13.x. If you're looking at it as a - * reference for how to write a preset, it's probably best to look at the other presets - * such as babel-preset-es2016 & babel-preset-latest noting that the former example - * exports via a default object and the latter via a default function. - */ - import transformES2015TemplateLiterals from "babel-plugin-transform-es2015-template-literals"; import transformES2015Literals from "babel-plugin-transform-es2015-literals"; import transformES2015FunctionName from "babel-plugin-transform-es2015-function-name"; @@ -31,7 +23,7 @@ import transformES2015ModulesAMD from "babel-plugin-transform-es2015-modules-amd import transformES2015ModulesUMD from "babel-plugin-transform-es2015-modules-umd"; import transformRegenerator from "babel-plugin-transform-regenerator"; -function preset(context, opts = {}) { +export default function (context, opts = {}) { const moduleTypes = ["commonjs", "amd", "umd", "systemjs"]; let loose = false; let modules = "commonjs"; @@ -82,24 +74,3 @@ function preset(context, opts = {}) { ].filter(Boolean) // filter out falsy values }; } - -/** - * This preset was originally an object, before function-based configurable presets were introduced. - * For backward-compatibility with anything that may have been loading this preset and expecting - * it to be a simple Babel config object, we export the old config here via default object. - */ -const oldConfig = preset({}); - -export default oldConfig; - -// However, for backward compatibility with babel-core < v6.13.x, we use the 'buildPreset' -// property of the preset object for the preset creation function with the enumerability -// caveat mentioned below. -Object.defineProperty(oldConfig, "buildPreset", { - configurable: true, - writable: true, - // We make this non-enumerable so old versions of babel-core won't see it as an unknown property, - // while allowing new versions to see it as a preset builder function. - enumerable: false, - value: preset, -}); diff --git a/packages/babel-preset-es2015/test/index.js b/packages/babel-preset-es2015/test/index.js index b7847d1b83..99ac458986 100644 --- a/packages/babel-preset-es2015/test/index.js +++ b/packages/babel-preset-es2015/test/index.js @@ -2,18 +2,10 @@ import es2015 from "../lib"; import { expect } from "chai"; describe("es2015 preset", function () { - it("exposes an object", function () { - // Changing this will break compatibility with babel-core < 6.13.x. - expect(typeof es2015).to.equal("object"); - }); - - it("exposes a separate list of plugins", function () { - expect(Array.isArray(es2015.plugins)).to.equal(true); - }); it("doesn't throw with no options passed", function () { expect(function () { - es2015.buildPreset(null); + es2015(null); }).not.to.throw(); }); @@ -21,7 +13,7 @@ describe("es2015 preset", function () { describe("loose", function () { it("throws on non-boolean value", function () { expect(function () { - es2015.buildPreset(null, { loose: 1 }); + es2015(null, { loose: 1 }); }).to.throw(/must be a boolean/); }); }); @@ -29,25 +21,25 @@ describe("es2015 preset", function () { describe("modules", function () { it("doesn't throw when passing one false", function () { expect(function () { - es2015.buildPreset(null, { modules: false }); + es2015(null, { modules: false }); }).not.to.throw(); }); it("doesn't throw when passing one of: 'commonjs', 'amd', 'umd', 'systemjs", function () { expect(function () { - es2015.buildPreset(null, { modules: "commonjs" }); + es2015(null, { modules: "commonjs" }); }).not.to.throw(); expect(function () { - es2015.buildPreset(null, { modules: "amd" }); + es2015(null, { modules: "amd" }); }).not.to.throw(); expect(function () { - es2015.buildPreset(null, { modules: "umd" }); + es2015(null, { modules: "umd" }); }).not.to.throw(); expect(function () { - es2015.buildPreset(null, { modules: "systemjs" }); + es2015(null, { modules: "systemjs" }); }).not.to.throw(); }); }); diff --git a/packages/babel-preset-es2016/src/index.js b/packages/babel-preset-es2016/src/index.js index 3ce3c75985..e0857e59c2 100644 --- a/packages/babel-preset-es2016/src/index.js +++ b/packages/babel-preset-es2016/src/index.js @@ -1,7 +1,9 @@ import transformExponentiationOperator from "babel-plugin-transform-exponentiation-operator"; -export default { - plugins: [ - transformExponentiationOperator - ] -}; +export default function () { + return { + plugins: [ + transformExponentiationOperator + ] + }; +} diff --git a/packages/babel-preset-es2017/src/index.js b/packages/babel-preset-es2017/src/index.js index 8c83b453c2..599186b002 100644 --- a/packages/babel-preset-es2017/src/index.js +++ b/packages/babel-preset-es2017/src/index.js @@ -1,9 +1,11 @@ import syntaxTrailingFunctionCommas from "babel-plugin-syntax-trailing-function-commas"; import transformAsyncToGenerator from "babel-plugin-transform-async-to-generator"; -export default { - plugins: [ - syntaxTrailingFunctionCommas, - transformAsyncToGenerator - ] -}; +export default function () { + return { + plugins: [ + syntaxTrailingFunctionCommas, + transformAsyncToGenerator + ] + }; +} diff --git a/packages/babel-preset-flow/src/index.js b/packages/babel-preset-flow/src/index.js index 8251ca8270..ef847d5f57 100644 --- a/packages/babel-preset-flow/src/index.js +++ b/packages/babel-preset-flow/src/index.js @@ -1,7 +1,9 @@ import transformFlowStripTypes from "babel-plugin-transform-flow-strip-types"; -export default { - plugins: [ - transformFlowStripTypes - ] -}; +export default function () { + return { + plugins: [ + transformFlowStripTypes + ] + }; +} diff --git a/packages/babel-preset-latest/src/index.js b/packages/babel-preset-latest/src/index.js index 8571beea98..5469c2146d 100644 --- a/packages/babel-preset-latest/src/index.js +++ b/packages/babel-preset-latest/src/index.js @@ -2,13 +2,10 @@ import presetES2015 from "babel-preset-es2015"; import presetES2016 from "babel-preset-es2016"; import presetES2017 from "babel-preset-es2017"; -// Rather than exporting a default object to represent the preset, we can -// also export a default function instead, as this preset demonstrates. -// This allows one to further configure a preset by way of specific options. export default function (context, opts = {}) { return { presets: [ - opts.es2015 !== false && [presetES2015.buildPreset, opts.es2015], + opts.es2015 !== false && [presetES2015, opts.es2015], opts.es2016 !== false && presetES2016, opts.es2017 !== false && presetES2017 ].filter(Boolean) // filter out falsy values diff --git a/packages/babel-preset-react/src/index.js b/packages/babel-preset-react/src/index.js index 361238658e..7f30145f70 100644 --- a/packages/babel-preset-react/src/index.js +++ b/packages/babel-preset-react/src/index.js @@ -7,21 +7,23 @@ import transformReactDisplayName from "babel-plugin-transform-react-display-name // import transformReactJSXSource from "babel-plugin-transform-react-jsx-source"; // import transformReactJSXSelf from "babel-plugin-transform-react-jsx-self"; -export default { - presets: [ - presetFlow - ], - plugins: [ - transformReactJSX, - transformSyntaxJSX, - transformReactDisplayName - ], - env: { - development: { - plugins: [ - // transformReactJSXSource, - // transformReactJSXSelf - ] +export default function () { + return { + presets: [ + presetFlow + ], + plugins: [ + transformReactJSX, + transformSyntaxJSX, + transformReactDisplayName + ], + env: { + development: { + plugins: [ + // transformReactJSXSource, + // transformReactJSXSelf + ] + } } - } -}; + }; +} diff --git a/packages/babel-preset-stage-0/src/index.js b/packages/babel-preset-stage-0/src/index.js index b9b65f10a0..f134ea884c 100644 --- a/packages/babel-preset-stage-0/src/index.js +++ b/packages/babel-preset-stage-0/src/index.js @@ -3,12 +3,14 @@ import presetStage1 from "babel-preset-stage-1"; import transformDoExpressions from "babel-plugin-transform-do-expressions"; import transformFunctionBind from "babel-plugin-transform-function-bind"; -export default { - presets: [ - presetStage1 - ], - plugins: [ - transformDoExpressions, - transformFunctionBind - ] -}; +export default function () { + return { + presets: [ + presetStage1 + ], + plugins: [ + transformDoExpressions, + transformFunctionBind + ] + }; +} diff --git a/packages/babel-preset-stage-1/src/index.js b/packages/babel-preset-stage-1/src/index.js index 11ed6e011c..9ab261e3c9 100644 --- a/packages/babel-preset-stage-1/src/index.js +++ b/packages/babel-preset-stage-1/src/index.js @@ -3,12 +3,14 @@ import presetStage2 from "babel-preset-stage-2"; import transformDecorators from "babel-plugin-transform-decorators"; import transformExportExtensions from "babel-plugin-transform-export-extensions"; -export default { - presets: [ - presetStage2 - ], - plugins: [ - transformDecorators, - transformExportExtensions - ] -}; +export default function () { + return { + presets: [ + presetStage2 + ], + plugins: [ + transformDecorators, + transformExportExtensions + ] + }; +} diff --git a/packages/babel-preset-stage-2/src/index.js b/packages/babel-preset-stage-2/src/index.js index efdd6ac729..9f251fc38c 100644 --- a/packages/babel-preset-stage-2/src/index.js +++ b/packages/babel-preset-stage-2/src/index.js @@ -4,13 +4,15 @@ import syntaxDynamicImport from "babel-plugin-syntax-dynamic-import"; import transformClassProperties from "babel-plugin-transform-class-properties"; import transformUnicodePropertyRegex from "babel-plugin-transform-unicode-property-regex"; -export default { - presets: [ - presetStage3 - ], - plugins: [ - syntaxDynamicImport, - transformClassProperties, - transformUnicodePropertyRegex - ] -}; +export default function () { + return { + presets: [ + presetStage3 + ], + plugins: [ + syntaxDynamicImport, + transformClassProperties, + transformUnicodePropertyRegex + ] + }; +} diff --git a/packages/babel-preset-stage-3/src/index.js b/packages/babel-preset-stage-3/src/index.js index dd6e2edbe9..20c6c49393 100644 --- a/packages/babel-preset-stage-3/src/index.js +++ b/packages/babel-preset-stage-3/src/index.js @@ -1,9 +1,11 @@ import transformObjectRestSpread from "babel-plugin-transform-object-rest-spread"; import transformAsyncGeneratorFunctions from "babel-plugin-transform-async-generator-functions"; -export default { - plugins: [ - transformAsyncGeneratorFunctions, - transformObjectRestSpread - ] -}; +export default function () { + return { + plugins: [ + transformAsyncGeneratorFunctions, + transformObjectRestSpread + ] + }; +}