diff --git a/experimental/babel-preset-env/.npmignore b/experimental/babel-preset-env/.npmignore index 860048ba15..9f0a3fbddf 100644 --- a/experimental/babel-preset-env/.npmignore +++ b/experimental/babel-preset-env/.npmignore @@ -1,3 +1,4 @@ +coverage src test node_modules @@ -8,5 +9,6 @@ codecov.yml yarn.lock .nyc_output .vscode +.eslintrc babel-preset-env-*.tgz flow-typed diff --git a/experimental/babel-preset-env/CHANGELOG.md b/experimental/babel-preset-env/CHANGELOG.md index 5e0ee9997d..bc9d5f2806 100644 --- a/experimental/babel-preset-env/CHANGELOG.md +++ b/experimental/babel-preset-env/CHANGELOG.md @@ -1,5 +1,33 @@ # Changelog +## v1.4.0 (2017-04-14) + +### :rocket: New Feature + +- Support `spec` option ([#98](https://github.com/babel/babel-preset-env/pull/98)) (@Kovensky) + +Added an option to enable more spec compliant, but potentially slower, transformations for any plugins in this preset that support them. + +- Bump compat-table for Edge 15 support ([#273](https://github.com/babel/babel-preset-env/pull/273)) (@existentialism) + +We updated our mappings so that you can get native support for async/await and other goodies when targeting Edge 15! + +### :bug: Bug Fix + +- Add Android browser to name map ([#270](https://github.com/babel/babel-preset-env/pull/270)) (@existentialism) + +Fixed a bug that was ignoring Android targets in browserslist queries (for example: "Android >= 4"). + +### :memo: Documentation + +- Clarify note about loading polyfills only once ([#282](https://github.com/babel/babel-preset-env/pull/282)) (@darahak) +- Add a reminder about include/exclude options ([#275](https://github.com/babel/babel-preset-env/pull/275)) (@existentialism) + +### :house: Internal + +- Chore: reduce package size. ([#281](https://github.com/babel/babel-preset-env/pull/281)) (@evilebottnawi) +- Remove deprecated comment ([#271](https://github.com/babel/babel-preset-env/pull/271)) (@yavorsky) + ## v1.3.3 (2017-04-07) ### :bug: Bug Fix diff --git a/experimental/babel-preset-env/README.md b/experimental/babel-preset-env/README.md index ee113d742b..08c606cc7b 100644 --- a/experimental/babel-preset-env/README.md +++ b/experimental/babel-preset-env/README.md @@ -130,6 +130,12 @@ To prevent these errors - specify the uglify option, which will enable all plugi > NOTE: Uglify has a work-in-progress "Harmony" branch to address the lack of ES6 support, but it is not yet stable. You can follow its progress in [UglifyJS2 issue #448](https://github.com/mishoo/UglifyJS2/issues/448). If you require an alternative minifier which _does_ support ES6 syntax, we recommend using [Babili](https://github.com/babel/babili). +### `spec` + +`boolean`, defaults to `false`. + +Enable more spec compliant, but potentially slower, transformations for any plugins in this preset that support them. + ### `loose` `boolean`, defaults to `false`. @@ -166,6 +172,8 @@ This option is useful if there is a bug in a native implementation, or a combina For example, Node 4 supports native classes but not spread. If `super` is used with a spread argument, then the `transform-es2015-classes` transform needs to be `include`d, as it is not possible to transpile a spread with `super` otherwise. +> NOTE: The `include` and `exclude` options _only_ work with the [plugins included with this preset](https://github.com/babel/babel-preset-env/blob/master/data/plugin-features.js); so, for example, including `transform-do-expressions` or excluding `transform-function-bind` will throw errors. To use a plugin _not_ included with this preset, add them to your [config](https://babeljs.io/docs/usage/babelrc/) directly. + ### `exclude` `Array`, defaults to `[]`. @@ -228,7 +236,9 @@ var b = new Map(); #### `useBuiltIns: 'entry'` -> NOTE: Only use `require("babel-polyfill");` once in your whole app. One option is to create a single entry file that only contains the require statement. +> NOTE: Only use `require("babel-polyfill");` once in your whole app. +> Multiple imports or requires of `babel-polyfill` will throw an error since it can cause global collisions and other issues that are hard to trace. +> We recommend creating a single entry file that only contains the `require` statement. This option enables a new plugin that replaces the statement `import "babel-polyfill"` or `require("babel-polyfill")` with individual requires for `babel-polyfill` based on environment. diff --git a/experimental/babel-preset-env/data/plugins.json b/experimental/babel-preset-env/data/plugins.json index 5a0d3a54af..81999ab1b0 100644 --- a/experimental/babel-preset-env/data/plugins.json +++ b/experimental/babel-preset-env/data/plugins.json @@ -211,6 +211,7 @@ }, "transform-async-to-generator": { "chrome": "55", + "edge": "15", "firefox": "52", "safari": "10.1", "node": "7.6", diff --git a/experimental/babel-preset-env/package.json b/experimental/babel-preset-env/package.json index e4a2250b75..d7ff6c2581 100644 --- a/experimental/babel-preset-env/package.json +++ b/experimental/babel-preset-env/package.json @@ -67,7 +67,7 @@ "babel-register": "7.0.0-alpha.8", "chai": "^3.5.0", "codecov": "^2.0.1", - "compat-table": "kangax/compat-table#c38f039b8ea7fadf347d3e300fec3611645e31e9", + "compat-table": "kangax/compat-table#861954b6e13d3eaa1ba9ef1a016906c0fc1072db", "electron-to-chromium": "^1.3.2", "eslint": "^3.17.1", "eslint-config-babel": "^6.0.0", diff --git a/experimental/babel-preset-env/src/debug.js b/experimental/babel-preset-env/src/debug.js index f6369b3512..ceb5890cb9 100644 --- a/experimental/babel-preset-env/src/debug.js +++ b/experimental/babel-preset-env/src/debug.js @@ -9,12 +9,15 @@ export const logMessage = (message, context) => { export const logPlugin = (plugin, targets, list, context) => { const envList = list[plugin] || {}; - const filteredList = Object.keys(targets).reduce((a, b) => { - if (!envList[b] || semver.lt(targets[b], semverify(envList[b]))) { - a[b] = prettifyVersion(targets[b]); - } - return a; - }, {}); + const filteredList = Object.keys(targets).reduce( + (a, b) => { + if (!envList[b] || semver.lt(targets[b], semverify(envList[b]))) { + a[b] = prettifyVersion(targets[b]); + } + return a; + }, + {}, + ); logMessage(`${plugin} ${JSON.stringify(filteredList)}`, context); }; diff --git a/experimental/babel-preset-env/src/index.js b/experimental/babel-preset-env/src/index.js index f399c284ee..b48aa77704 100644 --- a/experimental/babel-preset-env/src/index.js +++ b/experimental/babel-preset-env/src/index.js @@ -108,7 +108,7 @@ const filterItems = (list, includes, excludes, targets, defaultItems) => { export default function buildPreset(context, opts = {}) { const validatedOptions = normalizeOptions(opts); - const { debug, loose, moduleType, useBuiltIns } = validatedOptions; + const { debug, loose, moduleType, spec, useBuiltIns } = validatedOptions; const targets = getTargets(validatedOptions.targets); const include = transformIncludesAndExcludes(validatedOptions.include); @@ -145,9 +145,10 @@ export default function buildPreset(context, opts = {}) { ]); } + // NOTE: not giving spec here yet to avoid compatibility issues when + // babel-plugin-transform-es2015-modules-commonjs gets its spec mode transformations.forEach(pluginName => - plugins.push([require(`babel-plugin-${pluginName}`), { loose }]), - ); + plugins.push([require(`babel-plugin-${pluginName}`), { spec, loose }])); const regenerator = transformations.has("transform-regenerator"); @@ -180,8 +181,7 @@ export default function buildPreset(context, opts = {}) { regenerator, onDebug: (polyfills, context) => { polyfills.forEach(polyfill => - logPlugin(polyfill, polyfillTargets, builtInsList, context), - ); + logPlugin(polyfill, polyfillTargets, builtInsList, context)); }, }; diff --git a/experimental/babel-preset-env/src/normalize-options.js b/experimental/babel-preset-env/src/normalize-options.js index 56af88a092..8a409525cf 100644 --- a/experimental/babel-preset-env/src/normalize-options.js +++ b/experimental/babel-preset-env/src/normalize-options.js @@ -43,18 +43,23 @@ export const checkDuplicateIncludeExcludes = (include = [], exclude = []) => { ); }; -// TODO: Allow specifying plugins as either shortened or full name -// babel-plugin-transform-es2015-classes -// transform-es2015-classes -export const validateLooseOption = (looseOpt = false) => { - invariant( - typeof looseOpt === "boolean", - "Invalid Option: The 'loose' option must be a boolean.", - ); +export const validateBoolOption = (name, value, defaultValue) => { + if (typeof value === "undefined") { + value = defaultValue; + } - return looseOpt; + if (typeof value !== "boolean") { + throw new Error(`Preset env: '${name}' option must be a boolean.`); + } + + return value; }; +export const validateLooseOption = looseOpt => + validateBoolOption("loose", looseOpt, false); +export const validateSpecOption = specOpt => + validateBoolOption("spec", specOpt, false); + export const validateModulesOption = (modulesOpt = "commonjs") => { invariant( modulesOpt === false || @@ -95,6 +100,7 @@ export default function normalizeOptions(opts) { include: validateIncludesAndExcludes(opts.include, "include"), loose: validateLooseOption(opts.loose), moduleType: validateModulesOption(opts.modules), + spec: validateSpecOption(opts.spec), targets: opts.targets, useBuiltIns: validateUseBuiltInsOption(opts.useBuiltIns), }; diff --git a/experimental/babel-preset-env/src/targets-parser.js b/experimental/babel-preset-env/src/targets-parser.js index b7025a3fa4..504c8cc76b 100644 --- a/experimental/babel-preset-env/src/targets-parser.js +++ b/experimental/babel-preset-env/src/targets-parser.js @@ -9,6 +9,7 @@ export type Targets = { }; const browserNameMap = { + android: "android", chrome: "chrome", edge: "edge", firefox: "firefox", diff --git a/experimental/babel-preset-env/src/use-built-ins-entry-plugin.js b/experimental/babel-preset-env/src/use-built-ins-entry-plugin.js index 6734abc48c..b88e7ba484 100644 --- a/experimental/babel-preset-env/src/use-built-ins-entry-plugin.js +++ b/experimental/babel-preset-env/src/use-built-ins-entry-plugin.js @@ -16,15 +16,13 @@ export default function({ types: t }) { } function isRequire(path) { - return ( - t.isExpressionStatement(path.node) && + return t.isExpressionStatement(path.node) && t.isCallExpression(path.node.expression) && t.isIdentifier(path.node.expression.callee) && path.node.expression.callee.name === "require" && path.node.expression.arguments.length === 1 && t.isStringLiteral(path.node.expression.arguments[0]) && - isPolyfillSource(path.node.expression.arguments[0].value) - ); + isPolyfillSource(path.node.expression.arguments[0].value); } function createImport(polyfill, requireType, core) { diff --git a/experimental/babel-preset-env/src/use-built-ins-plugin.js b/experimental/babel-preset-env/src/use-built-ins-plugin.js index 41f2660082..f7fc30c972 100644 --- a/experimental/babel-preset-env/src/use-built-ins-plugin.js +++ b/experimental/babel-preset-env/src/use-built-ins-plugin.js @@ -28,9 +28,8 @@ const modulePathMap = { }; const getModulePath = module => { - return ( - modulePathMap[module] || `babel-polyfill/lib/core-js/modules/${module}` - ); + return modulePathMap[module] || + `babel-polyfill/lib/core-js/modules/${module}`; }; export default function({ types: t }) { @@ -64,15 +63,13 @@ export default function({ types: t }) { } function isRequire(path) { - return ( - t.isExpressionStatement(path.node) && + return t.isExpressionStatement(path.node) && t.isCallExpression(path.node.expression) && t.isIdentifier(path.node.expression.callee) && path.node.expression.callee.name === "require" && path.node.expression.arguments.length === 1 && t.isStringLiteral(path.node.expression.arguments[0]) && - isPolyfillSource(path.node.expression.arguments[0].value) - ); + isPolyfillSource(path.node.expression.arguments[0].value); } const addAndRemovePolyfillImports = { diff --git a/experimental/babel-preset-env/test/debug-fixtures.js b/experimental/babel-preset-env/test/debug-fixtures.js index 0d820ff5d9..4f499022da 100644 --- a/experimental/babel-preset-env/test/debug-fixtures.js +++ b/experimental/babel-preset-env/test/debug-fixtures.js @@ -75,6 +75,7 @@ const buildTest = opts => { describe("debug output", () => { fs.readdirSync(fixtureLoc).forEach(testName => { + if (testName.slice(0, 1) === ".") return; const testLoc = path.join(fixtureLoc, testName); const opts = { diff --git a/experimental/babel-preset-env/test/debug-fixtures/android/options.json b/experimental/babel-preset-env/test/debug-fixtures/android/options.json new file mode 100644 index 0000000000..caac961b51 --- /dev/null +++ b/experimental/babel-preset-env/test/debug-fixtures/android/options.json @@ -0,0 +1,11 @@ +{ + "presets": [ + ["../../lib", { + "debug": true, + "targets": { + "browsers": [ "Android >= 4" ] + }, + "useBuiltIns": "entry" + }] + ] +} diff --git a/experimental/babel-preset-env/test/debug-fixtures/android/stdout.txt b/experimental/babel-preset-env/test/debug-fixtures/android/stdout.txt new file mode 100644 index 0000000000..cb5e191e01 --- /dev/null +++ b/experimental/babel-preset-env/test/debug-fixtures/android/stdout.txt @@ -0,0 +1,124 @@ +babel-preset-env: `DEBUG` option + +Using targets: +{ + "android": "4" +} + +Modules transform: commonjs + +Using plugins: + check-es2015-constants {"android":"4"} + transform-es2015-arrow-functions {"android":"4"} + transform-es2015-block-scoped-functions {"android":"4"} + transform-es2015-block-scoping {"android":"4"} + transform-es2015-classes {"android":"4"} + transform-es2015-computed-properties {"android":"4"} + transform-es2015-destructuring {"android":"4"} + transform-es2015-duplicate-keys {"android":"4"} + transform-es2015-for-of {"android":"4"} + transform-es2015-function-name {"android":"4"} + transform-es2015-literals {"android":"4"} + transform-es2015-object-super {"android":"4"} + transform-es2015-parameters {"android":"4"} + transform-es2015-shorthand-properties {"android":"4"} + transform-es2015-spread {"android":"4"} + transform-es2015-sticky-regex {"android":"4"} + transform-es2015-template-literals {"android":"4"} + transform-es2015-typeof-symbol {"android":"4"} + transform-es2015-unicode-regex {"android":"4"} + transform-regenerator {"android":"4"} + transform-exponentiation-operator {"android":"4"} + transform-async-to-generator {"android":"4"} + syntax-trailing-function-commas {"android":"4"} + +Polyfills +========= + +Replaced `babel-polyfill` with the following polyfills: + es6.typed.array-buffer {"android":"4"} + es6.typed.int8-array {"android":"4"} + es6.typed.uint8-array {"android":"4"} + es6.typed.uint8-clamped-array {"android":"4"} + es6.typed.int16-array {"android":"4"} + es6.typed.uint16-array {"android":"4"} + es6.typed.int32-array {"android":"4"} + es6.typed.uint32-array {"android":"4"} + es6.typed.float32-array {"android":"4"} + es6.typed.float64-array {"android":"4"} + es6.map {"android":"4"} + es6.set {"android":"4"} + es6.weak-map {"android":"4"} + es6.weak-set {"android":"4"} + es6.reflect.apply {"android":"4"} + es6.reflect.construct {"android":"4"} + es6.reflect.define-property {"android":"4"} + es6.reflect.delete-property {"android":"4"} + es6.reflect.get {"android":"4"} + es6.reflect.get-own-property-descriptor {"android":"4"} + es6.reflect.get-prototype-of {"android":"4"} + es6.reflect.has {"android":"4"} + es6.reflect.is-extensible {"android":"4"} + es6.reflect.own-keys {"android":"4"} + es6.reflect.prevent-extensions {"android":"4"} + es6.reflect.set {"android":"4"} + es6.reflect.set-prototype-of {"android":"4"} + es6.promise {"android":"4"} + es6.symbol {"android":"4"} + es6.object.assign {"android":"4"} + es6.object.is {"android":"4"} + es6.object.set-prototype-of {"android":"4"} + es6.function.name {"android":"4"} + es6.string.raw {"android":"4"} + es6.string.from-code-point {"android":"4"} + es6.string.code-point-at {"android":"4"} + es6.string.repeat {"android":"4"} + es6.string.starts-with {"android":"4"} + es6.string.ends-with {"android":"4"} + es6.string.includes {"android":"4"} + es6.regexp.flags {"android":"4"} + es6.regexp.match {"android":"4"} + es6.regexp.replace {"android":"4"} + es6.regexp.split {"android":"4"} + es6.regexp.search {"android":"4"} + es6.array.from {"android":"4"} + es6.array.of {"android":"4"} + es6.array.copy-within {"android":"4"} + es6.array.find {"android":"4"} + es6.array.find-index {"android":"4"} + es6.array.fill {"android":"4"} + es6.array.iterator {"android":"4"} + es6.number.is-finite {"android":"4"} + es6.number.is-integer {"android":"4"} + es6.number.is-safe-integer {"android":"4"} + es6.number.is-nan {"android":"4"} + es6.number.epsilon {"android":"4"} + es6.number.min-safe-integer {"android":"4"} + es6.number.max-safe-integer {"android":"4"} + es6.math.acosh {"android":"4"} + es6.math.asinh {"android":"4"} + es6.math.atanh {"android":"4"} + es6.math.cbrt {"android":"4"} + es6.math.clz32 {"android":"4"} + es6.math.cosh {"android":"4"} + es6.math.expm1 {"android":"4"} + es6.math.fround {"android":"4"} + es6.math.hypot {"android":"4"} + es6.math.imul {"android":"4"} + es6.math.log1p {"android":"4"} + es6.math.log10 {"android":"4"} + es6.math.log2 {"android":"4"} + es6.math.sign {"android":"4"} + es6.math.sinh {"android":"4"} + es6.math.tanh {"android":"4"} + es6.math.trunc {"android":"4"} + es7.array.includes {"android":"4"} + es7.object.values {"android":"4"} + es7.object.entries {"android":"4"} + es7.object.get-own-property-descriptors {"android":"4"} + es7.string.pad-start {"android":"4"} + es7.string.pad-end {"android":"4"} + web.timers {"android":"4"} + web.immediate {"android":"4"} + web.dom.iterable {"android":"4"} +src/in.js -> lib/in.js \ No newline at end of file diff --git a/experimental/babel-preset-env/test/fixtures/preset-options/spec/actual.js b/experimental/babel-preset-env/test/fixtures/preset-options/spec/actual.js new file mode 100644 index 0000000000..51403eebdc --- /dev/null +++ b/experimental/babel-preset-env/test/fixtures/preset-options/spec/actual.js @@ -0,0 +1,2 @@ +const bar = "bar"; +const x = () => `foo${bar}`; diff --git a/experimental/babel-preset-env/test/fixtures/preset-options/spec/expected.js b/experimental/babel-preset-env/test/fixtures/preset-options/spec/expected.js new file mode 100644 index 0000000000..67366fe06e --- /dev/null +++ b/experimental/babel-preset-env/test/fixtures/preset-options/spec/expected.js @@ -0,0 +1,10 @@ +"use strict"; + +function _newArrowCheck(innerThis, boundThis) { if (innerThis !== boundThis) { throw new TypeError("Cannot instantiate an arrow function"); } } + +var bar = "bar"; +var x = function () { + _newArrowCheck(undefined, undefined); + + return "foo" + bar; +}.bind(undefined); \ No newline at end of file diff --git a/experimental/babel-preset-env/test/fixtures/preset-options/spec/options.json b/experimental/babel-preset-env/test/fixtures/preset-options/spec/options.json new file mode 100644 index 0000000000..8aefcdc8ed --- /dev/null +++ b/experimental/babel-preset-env/test/fixtures/preset-options/spec/options.json @@ -0,0 +1,7 @@ +{ + "presets": [ + ["../../../../lib", { + "spec": true + }] + ] +} diff --git a/experimental/babel-preset-env/test/normalize-options.spec.js b/experimental/babel-preset-env/test/normalize-options.spec.js index c91401dcc3..4399a6488d 100644 --- a/experimental/babel-preset-env/test/normalize-options.spec.js +++ b/experimental/babel-preset-env/test/normalize-options.spec.js @@ -5,10 +5,11 @@ const assert = require("assert"); const { checkDuplicateIncludeExcludes, + normalizePluginNames, validateIncludesAndExcludes, validateLooseOption, validateModulesOption, - normalizePluginNames, + validateSpecOption, } = normalizeOptions; describe("normalize-options", () => { @@ -36,6 +37,7 @@ describe("normalize-options", () => { assert.throws(normalizeWithSameIncludes, Error); }); }); + describe("validateLooseOption", () => { it("`undefined` option returns false", () => { assert(validateLooseOption() === false); @@ -50,12 +52,23 @@ describe("normalize-options", () => { }); it("array option is invalid", () => { - assert.throws( - () => { - validateLooseOption([]); - }, - Error, - ); + assert.throws(() => { + validateLooseOption([]); + }); + }); + }); + + describe("validateSpecOption", () => { + it("`undefined` option returns false", () => { + assert(validateSpecOption() === false); + }); + + it("`false` option returns false", () => { + assert(validateSpecOption(false) === false); + }); + + it("`true` option returns true", () => { + assert(validateSpecOption(true) === true); }); }); diff --git a/experimental/babel-preset-env/test/utils.spec.js b/experimental/babel-preset-env/test/utils.spec.js index d4d5162b81..9dbc454ae2 100644 --- a/experimental/babel-preset-env/test/utils.spec.js +++ b/experimental/babel-preset-env/test/utils.spec.js @@ -3,11 +3,7 @@ const utils = require("../lib/utils"); const assert = require("assert"); -const { - prettifyTargets, - prettifyVersion, - semverify, -} = utils; +const { prettifyTargets, prettifyVersion, semverify } = utils; describe("utils", () => { describe("semverify", () => { diff --git a/experimental/babel-preset-env/yarn.lock b/experimental/babel-preset-env/yarn.lock index 848f87d3c9..fcab33a71e 100644 --- a/experimental/babel-preset-env/yarn.lock +++ b/experimental/babel-preset-env/yarn.lock @@ -1906,9 +1906,9 @@ commoner@^0.10.1: q "^1.1.2" recast "^0.11.17" -compat-table@kangax/compat-table#c38f039b8ea7fadf347d3e300fec3611645e31e9: +compat-table@kangax/compat-table#861954b6e13d3eaa1ba9ef1a016906c0fc1072db: version "0.0.0" - resolved "https://codeload.github.com/kangax/compat-table/tar.gz/c38f039b8ea7fadf347d3e300fec3611645e31e9" + resolved "https://codeload.github.com/kangax/compat-table/tar.gz/861954b6e13d3eaa1ba9ef1a016906c0fc1072db" dependencies: babel-core latest babel-polyfill latest @@ -2697,7 +2697,7 @@ glob@5.0.x, glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.0.5, glob@^7.0.3: +glob@7.0.5: version "7.0.5" resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" dependencies: @@ -2708,7 +2708,7 @@ glob@7.0.5, glob@^7.0.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.1.1, glob@^7.0.0, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1: +glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: