diff --git a/experimental/babel-preset-env/README.md b/experimental/babel-preset-env/README.md index c44d976420..3d23f6eda9 100644 --- a/experimental/babel-preset-env/README.md +++ b/experimental/babel-preset-env/README.md @@ -181,7 +181,7 @@ For more information on setting options for a preset, refer to the [plugin/prese Takes an object of environment versions to support. -Each target environment takes a number or a string (we recommend using a string when specifying minor versions like `node: "6.10"`). +Each target environment takes a number or a string (we recommend using a string when specifying minor versions like `node: "6.10"`). You can also specify `tp` (technology preview) version for Safari. Example environments: `chrome`, `opera`, `edge`, `firefox`, `safari`, `ie`, `ios`, `android`, `node`, `electron`. @@ -197,7 +197,7 @@ If you want to compile against the current node version, you can specify `"node" `Array | string` -A query to select browsers (ex: last 2 versions, > 5%) using [browserslist](https://github.com/ai/browserslist). +A query to select browsers (ex: last 2 versions, > 5%, safari tp) using [browserslist](https://github.com/ai/browserslist). Note, browsers' results are overridden by explicit items from `targets`. diff --git a/experimental/babel-preset-env/data/built-ins.json b/experimental/babel-preset-env/data/built-ins.json index f6d6003e96..0646703e00 100644 --- a/experimental/babel-preset-env/data/built-ins.json +++ b/experimental/babel-preset-env/data/built-ins.json @@ -948,6 +948,7 @@ }, "es7.promise.finally": { "chrome": "63", + "safari": "tp", "opera": "50" } } diff --git a/experimental/babel-preset-env/data/plugins.json b/experimental/babel-preset-env/data/plugins.json index c8e529d799..9750ec43f8 100644 --- a/experimental/babel-preset-env/data/plugins.json +++ b/experimental/babel-preset-env/data/plugins.json @@ -231,14 +231,18 @@ "proposal-async-generator-functions": { "chrome": "63", "firefox": "57", + "safari": "tp", "opera": "50" }, "proposal-object-rest-spread": { "chrome": "60", "firefox": "55", + "safari": "tp", "node": "8.3", "opera": "47" }, - "proposal-optional-catch-binding": {}, + "proposal-optional-catch-binding": { + "safari": "tp" + }, "proposal-unicode-property-regex": {} } diff --git a/experimental/babel-preset-env/data/unreleased-labels.js b/experimental/babel-preset-env/data/unreleased-labels.js new file mode 100644 index 0000000000..bc5130567d --- /dev/null +++ b/experimental/babel-preset-env/data/unreleased-labels.js @@ -0,0 +1,3 @@ +module.exports = { + safari: "tp", +}; diff --git a/experimental/babel-preset-env/scripts/build-data.js b/experimental/babel-preset-env/scripts/build-data.js index 5bbab1fa55..b477b8ac44 100644 --- a/experimental/babel-preset-env/scripts/build-data.js +++ b/experimental/babel-preset-env/scripts/build-data.js @@ -7,6 +7,7 @@ const flattenDeep = require("lodash/flattenDeep"); const isEqual = require("lodash/isEqual"); const mapValues = require("lodash/mapValues"); const pickBy = require("lodash/pickBy"); +const unreleasedLabels = require("../data/unreleased-labels"); const electronToChromiumVersions = require("electron-to-chromium").versions; const electronToChromiumKeys = Object.keys( @@ -185,6 +186,7 @@ const getLowestImplementedVersion = ({ features }, env) => { return result; }, []); + const unreleasedLabelForEnv = unreleasedLabels[env]; const envTests = tests.map(({ res: test, isBuiltIn }, i) => { // Babel itself doesn't implement the feature correctly, // don't count against it @@ -200,9 +202,15 @@ const getLowestImplementedVersion = ({ features }, env) => { .filter( test => tests[i].res[test] === true || tests[i].res[test] === "strict" ) - // normalize some keys - .map(test => test.replace("_", ".")) - .filter(test => !isNaN(parseFloat(test.replace(env, "")))) + // normalize some keys and get version from full string. + .map(test => { + return test.replace("_", ".").replace(env, ""); + }) + // version must be label from the unreleasedLabels (like tp) or number. + .filter( + version => + unreleasedLabelForEnv === version || !isNaN(parseFloat(version)) + ) .shift() ); }); @@ -220,9 +228,14 @@ const getLowestImplementedVersion = ({ features }, env) => { return null; } - return envTests.map(str => Number(str.replace(env, ""))).reduce((a, b) => { - return a < b ? b : a; - }); + return envTests + .map(str => { + const version = str.replace(env, ""); + return version === unreleasedLabelForEnv ? version : parseFloat(version); + }) + .reduce((a, b) => { + return b === unreleasedLabelForEnv || a < b ? b : a; + }); }; const generateData = (environments, features) => { diff --git a/experimental/babel-preset-env/src/index.js b/experimental/babel-preset-env/src/index.js index b9ad693828..ed572ae626 100644 --- a/experimental/babel-preset-env/src/index.js +++ b/experimental/babel-preset-env/src/index.js @@ -16,7 +16,12 @@ import useBuiltInsEntryPlugin from "./use-built-ins-entry-plugin"; import addUsedBuiltInsPlugin from "./use-built-ins-plugin"; import getTargets from "./targets-parser"; import availablePlugins from "./available-plugins"; -import { filterStageFromList, prettifyTargets, semverify } from "./utils"; +import { + filterStageFromList, + prettifyTargets, + semverify, + isUnreleasedVersion, +} from "./utils"; import type { Plugin, Targets } from "./types"; const getPlugin = (pluginName: string) => { @@ -61,6 +66,13 @@ export const isPluginRequired = ( const lowestImplementedVersion: string = plugin[environment]; const lowestTargetedVersion: string = supportedEnvironments[environment]; + // If targets has unreleased value as a lowest version, then don't require a plugin. + if (isUnreleasedVersion(lowestTargetedVersion, environment)) { + return false; + // Include plugin if it is supported in the unreleased environment, which wasn't specified in targets + } else if (isUnreleasedVersion(lowestImplementedVersion, environment)) { + return true; + } if (!semver.valid(lowestTargetedVersion)) { throw new Error( diff --git a/experimental/babel-preset-env/src/targets-parser.js b/experimental/babel-preset-env/src/targets-parser.js index 4144bab98b..3eb0a5e9ee 100644 --- a/experimental/babel-preset-env/src/targets-parser.js +++ b/experimental/babel-preset-env/src/targets-parser.js @@ -2,7 +2,7 @@ import browserslist from "browserslist"; import semver from "semver"; -import { semverify } from "./utils"; +import { semverify, isUnreleasedVersion, getLowestUnreleased } from "./utils"; import { objectToBrowserslist } from "./normalize-options"; import type { Targets } from "./types"; @@ -20,7 +20,7 @@ const browserNameMap = { const isBrowsersQueryValid = (browsers: string | Array): boolean => typeof browsers === "string" || Array.isArray(browsers); -const semverMin = (first: ?string, second: string): string => { +export const semverMin = (first: ?string, second: string): string => { return first && semver.lt(first, second) ? first : second; }; @@ -44,7 +44,16 @@ const getLowestVersions = (browsers: Array): Targets => { try { // Browser version can return as "10.0-10.2" - const splitVersion = browserVersion.split("-")[0]; + const splitVersion = browserVersion.split("-")[0].toLowerCase(); + + if (isUnreleasedVersion(splitVersion, browserName)) { + all[normalizedBrowserName] = getLowestUnreleased( + all[normalizedBrowserName], + splitVersion, + browserName, + ); + } + const parsedBrowserVersion = semverify(splitVersion); all[normalizedBrowserName] = semverMin( @@ -76,7 +85,12 @@ const outputDecimalWarning = (decimalTargets: Array): void => { }; const targetParserMap = { - __default: (target, value) => [target, semverify(value)], + __default: (target, value) => { + const version = isUnreleasedVersion(value, target) + ? value.toLowerCase() + : semverify(value); + return [target, version]; + }, // Parse `node: true` and `node: "current"` to version node: (target, value) => { diff --git a/experimental/babel-preset-env/src/utils.js b/experimental/babel-preset-env/src/utils.js index 88635af41b..a974e7116f 100644 --- a/experimental/babel-preset-env/src/utils.js +++ b/experimental/babel-preset-env/src/utils.js @@ -1,6 +1,8 @@ // @flow import semver from "semver"; +import unreleasedLabels from "../data/unreleased-labels"; +import { semverMin } from "./targets-parser"; import type { Targets } from "./types"; // Convert version to a semver value. @@ -43,7 +45,8 @@ export const prettifyTargets = (targets: Targets): Object => { return Object.keys(targets).reduce((results, target) => { let value = targets[target]; - if (typeof value === "string") { + const unreleasedLabel = unreleasedLabels[target]; + if (typeof value === "string" && unreleasedLabel !== value) { value = prettifyVersion(value); } @@ -52,6 +55,26 @@ export const prettifyTargets = (targets: Targets): Object => { }, {}); }; +export const isUnreleasedVersion = (version: string, env: string): boolean => { + const unreleasedLabel = unreleasedLabels[env]; + return ( + unreleasedLabel && unreleasedLabel === version.toString().toLowerCase() + ); +}; + +export const getLowestUnreleased = ( + a: string, + b: string, + env: string, +): string => { + const unreleasedLabel = unreleasedLabels[env]; + const hasUnreleased = [a, b].some(item => item === unreleasedLabel); + if (hasUnreleased) { + return a === hasUnreleased ? b : a || b; + } + return semverMin(a, b); +}; + export const filterStageFromList = (list: any, stageList: any) => { return Object.keys(list).reduce((result, item) => { if (!stageList[item]) { diff --git a/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp-browsers/actual.js b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp-browsers/actual.js new file mode 100644 index 0000000000..c13687d6fd --- /dev/null +++ b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp-browsers/actual.js @@ -0,0 +1,4 @@ +let n = { x, y, ...z }; +try { + throw 0; +} catch {} diff --git a/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp-browsers/expected.js b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp-browsers/expected.js new file mode 100644 index 0000000000..5f8f4633db --- /dev/null +++ b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp-browsers/expected.js @@ -0,0 +1,9 @@ +let n = { + x, + y, + ...z +}; + +try { + throw 0; +} catch {} diff --git a/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp-browsers/options.json b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp-browsers/options.json new file mode 100644 index 0000000000..99be2bf1a3 --- /dev/null +++ b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp-browsers/options.json @@ -0,0 +1,11 @@ +{ + "presets": [ + ["../../../../lib", { + "targets": { + "browsers": "safari tp" + }, + "shippedProposals": true + } + ] + ] +} diff --git a/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp/actual.js b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp/actual.js new file mode 100644 index 0000000000..da92b9ceca --- /dev/null +++ b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp/actual.js @@ -0,0 +1 @@ +let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; diff --git a/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp/expected.js b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp/expected.js new file mode 100644 index 0000000000..788499e332 --- /dev/null +++ b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp/expected.js @@ -0,0 +1,10 @@ +let { + x, + y, + ...z +} = { + x: 1, + y: 2, + a: 3, + b: 4 +}; diff --git a/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp/options.json b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp/options.json new file mode 100644 index 0000000000..03d076914c --- /dev/null +++ b/experimental/babel-preset-env/test/fixtures/preset-options/safari-tp/options.json @@ -0,0 +1,10 @@ +{ + "presets": [ + ["../../../../lib", { + "targets": { + "safari": "tp" + }, + "shippedProposals": true + }] + ] +} diff --git a/experimental/babel-preset-env/test/targets-parser.spec.js b/experimental/babel-preset-env/test/targets-parser.spec.js index 204a7d3457..77c8f38967 100644 --- a/experimental/babel-preset-env/test/targets-parser.spec.js +++ b/experimental/babel-preset-env/test/targets-parser.spec.js @@ -39,6 +39,28 @@ describe("getTargets", () => { ); }); + it("works with TP versions", () => { + assert.deepEqual( + getTargets({ + browsers: "safari tp", + }), + { + safari: "tp", + }, + ); + }); + + it("returns TP version in lower case", () => { + assert.deepEqual( + getTargets({ + safari: "TP", + }), + { + safari: "tp", + }, + ); + }); + it("ignores invalid", () => { assert.deepEqual( getTargets({