### `@babel/runtime` - Added `@babel/runtime-corejs3` package and `corejs: 3` options to `@babel/plugin-transform-runtime`. - Added support of instance methods, fixes #8928. - Added flag `proposals` (in `corejs: { version: 3, proposals: true }` format) for support all proposals polyfills from `core-js`. - Used separate directories in runtime for `core-js` entry points with proposals and without. - Used `get-iterator-method` helper for getting iterators, fixes #2500. - As a cheap bonus, added support of IE8- (except some cases of `regenerator`). ### `@babel/polyfill` - Should be deprecated in favor of separate usage required features from `core-js` and `regenerator-runtime` with an informative message. ### `@babel/preset-env` - Uses for built-ins data from [`core-js-compat`](https://github.com/zloirock/core-js/tree/master/packages/core-js-compat) instead of `compat-table` since information from `compat-table` [is not enough](https://github.com/zloirock/core-js/tree/master/packages/core-js-compat). - `useBuilIns` now requires direct setting of `corejs` version option, without it will be used `2` by default and shown deprecation warning. - Added support of minor `core-js` versions for simplify updating in the future. - For preventing some order-related problems, polyfills in the both `core-js@3` plugins added on `post` stage in the order of `core-js-compat` data. - Divided plugins and polyfills parts of `preset-env`, instead of 2 internal plugins for adding polyfills, we have 6: usage and entry versions of plugins for `core-js@2`, ### Current state: `core-js@3`, `regenerator-runtime`. - Added support `samsung` target (for Samsung Internet) since `core-js-compat` and `compat-table` now contains mapping for this, fixes #6602. #### `useBuilIns: entry` with `corejs: 3` - No longer transforms `@babel/polyfill`. - Transforms **all possible** `core-js` entry points to import of related modules (based on data from [`core-js-compat`](https://unpkg.com/core-js-compat@3.0.0-beta.15/entries.json)). - Since of this, we no longer need `shippedProposals` / `proposals` flags with `useBuilIns: entry`. - Removes `regenerator-runtime/runtime` import where it's not required. #### `useBuilIns: usage` with `corejs: 3` - In addition to `shippedProposals`, added flag `proposals` (in `corejs: { version: 3, proposals: true }` format) for polyfill all proposals from `core-js`. - Fixed list of dependencies in built-in definitions. - Improved the way of determination method / built-in name and source of this method. - Adds import of required polyfills on `MemberExpression`, `ObjectPattern`, `in` operator. - Adds import of required polyfills on access to global object properties. - Adds import of all required common iterators on all syntax features which use iterators protocol (`for-of`, destructuring, spread, `yield` delegation, etc.). - Adds import of promises on syntax features which use promises (async functions/generators, dynamic import, etc.), fixes #9250, #7402, etc. ### `core-js@2` stuff I didn't want to tough `core-js@2`-related stuff, however - Fixed some serious errors in definitions which breaks `Object.getOwnPropertySymbols`, `Symbol.toStringTag` logic, `Promise#finally`, `Array#forEach`, etc. - `Array#flatMap` and trim methods moved to stable features as a part of ES2019 and loaded by deprecated `@babel/polyfill` and `@babel/preset-env` with `corejs: 2` option.
316 lines
8.7 KiB
JavaScript
316 lines
8.7 KiB
JavaScript
"use strict";
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const semver = require("semver");
|
|
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(
|
|
electronToChromiumVersions
|
|
).reverse();
|
|
|
|
const chromiumToElectronMap = electronToChromiumKeys.reduce((all, electron) => {
|
|
all[electronToChromiumVersions[electron]] = +electron;
|
|
return all;
|
|
}, {});
|
|
const chromiumToElectronVersions = Object.keys(chromiumToElectronMap);
|
|
|
|
const findClosestElectronVersion = targetVersion => {
|
|
const chromiumVersionsLength = chromiumToElectronVersions.length;
|
|
const maxChromium = +chromiumToElectronVersions[chromiumVersionsLength - 1];
|
|
if (targetVersion > maxChromium) return null;
|
|
|
|
const closestChrome = chromiumToElectronVersions.find(
|
|
version => targetVersion <= version
|
|
);
|
|
return chromiumToElectronMap[closestChrome];
|
|
};
|
|
|
|
const chromiumToElectron = chromium =>
|
|
chromiumToElectronMap[chromium] || findClosestElectronVersion(chromium);
|
|
|
|
const renameTests = (tests, getName, category) =>
|
|
tests.map(test =>
|
|
Object.assign({}, test, { name: getName(test.name), category })
|
|
);
|
|
|
|
// The following is adapted from compat-table:
|
|
// https://github.com/kangax/compat-table/blob/gh-pages/build.js
|
|
//
|
|
// It parses and interpolates data so environments that "equal" other
|
|
// environments (node4 and chrome45), as well as familial relationships (edge
|
|
// and ie11) can be handled properly.
|
|
|
|
const envs = require("compat-table/environments");
|
|
|
|
const byTestSuite = suite => browser => {
|
|
return Array.isArray(browser.test_suites)
|
|
? browser.test_suites.indexOf(suite) > -1
|
|
: true;
|
|
};
|
|
|
|
const compatSources = ["es5", "es6", "es2016plus", "esnext"].reduce(
|
|
(result, source) => {
|
|
const data = require(`compat-table/data-${source}`);
|
|
data.browsers = pickBy(envs, byTestSuite(source));
|
|
result.push(data);
|
|
return result;
|
|
},
|
|
[]
|
|
);
|
|
|
|
const interpolateAllResults = (rawBrowsers, tests) => {
|
|
const interpolateResults = res => {
|
|
let browser;
|
|
let prevBrowser;
|
|
let result;
|
|
let prevResult;
|
|
let prevBid;
|
|
|
|
for (const bid in rawBrowsers) {
|
|
// For browsers that are essentially equal to other browsers,
|
|
// copy over the results.
|
|
browser = rawBrowsers[bid];
|
|
if (browser.equals && res[bid] === undefined) {
|
|
result = res[browser.equals];
|
|
res[bid] =
|
|
browser.ignore_flagged && result === "flagged" ? false : result;
|
|
// For each browser, check if the previous browser has the same
|
|
// browser full name (e.g. Firefox) or family name (e.g. Chakra) as this one.
|
|
} else if (
|
|
prevBrowser &&
|
|
(prevBrowser.full.replace(/,.+$/, "") ===
|
|
browser.full.replace(/,.+$/, "") ||
|
|
(browser.family !== undefined &&
|
|
prevBrowser.family === browser.family))
|
|
) {
|
|
// For each test, check if the previous browser has a result
|
|
// that this browser lacks.
|
|
result = res[bid];
|
|
prevResult = res[prevBid];
|
|
if (prevResult !== undefined && result === undefined) {
|
|
res[bid] = prevResult;
|
|
}
|
|
}
|
|
prevBrowser = browser;
|
|
prevBid = bid;
|
|
}
|
|
};
|
|
|
|
// Now print the results.
|
|
tests.forEach(function(t) {
|
|
// Calculate the result totals for tests which consist solely of subtests.
|
|
if ("subtests" in t) {
|
|
t.subtests.forEach(function(e) {
|
|
interpolateResults(e.res);
|
|
});
|
|
} else {
|
|
interpolateResults(t.res);
|
|
}
|
|
});
|
|
};
|
|
|
|
compatSources.forEach(({ browsers, tests }) =>
|
|
interpolateAllResults(browsers, tests)
|
|
);
|
|
|
|
// End of compat-table code adaptation
|
|
|
|
const environments = [
|
|
"chrome",
|
|
"opera",
|
|
"edge",
|
|
"firefox",
|
|
"safari",
|
|
"node",
|
|
"ie",
|
|
"android",
|
|
"ios",
|
|
"phantom",
|
|
"samsung",
|
|
];
|
|
|
|
const compatibilityTests = flattenDeep(
|
|
compatSources.map(data =>
|
|
data.tests.map(test => {
|
|
return test.subtests
|
|
? [
|
|
test,
|
|
renameTests(
|
|
test.subtests,
|
|
name => test.name + " / " + name,
|
|
test.category
|
|
),
|
|
]
|
|
: test;
|
|
})
|
|
)
|
|
);
|
|
|
|
const getLowestImplementedVersion = ({ features }, env) => {
|
|
const tests = compatibilityTests
|
|
.filter(test => {
|
|
return (
|
|
features.indexOf(test.name) >= 0 ||
|
|
// for features === ["DataView"]
|
|
// it covers "DataView (Int8)" and "DataView (UInt8)"
|
|
(features.length === 1 && test.name.indexOf(features[0]) === 0)
|
|
);
|
|
})
|
|
.reduce((result, test) => {
|
|
if (!test.subtests) {
|
|
result.push({
|
|
name: test.name,
|
|
res: test.res,
|
|
});
|
|
} else {
|
|
test.subtests.forEach(subtest =>
|
|
result.push({
|
|
name: `${test.name}/${subtest.name}`,
|
|
res: subtest.res,
|
|
})
|
|
);
|
|
}
|
|
|
|
return result;
|
|
}, []);
|
|
|
|
const unreleasedLabelForEnv = unreleasedLabels[env];
|
|
const envTests = tests.map(({ res: test }, i) => {
|
|
const reportedVersions = Object.keys(test)
|
|
.filter(t => t.startsWith(env))
|
|
.map(t => {
|
|
const version = t.replace(/_/g, ".").replace(env, "");
|
|
return {
|
|
version,
|
|
semver: semver.coerce(version) || version,
|
|
// Babel assumes strict mode
|
|
implements: tests[i].res[t] === true || tests[i].res[t] === "strict",
|
|
};
|
|
})
|
|
// version must be label from the unreleasedLabels (like tp) or number.
|
|
.filter(
|
|
version =>
|
|
unreleasedLabelForEnv === version.version ||
|
|
!isNaN(parseFloat(version.version))
|
|
)
|
|
// Sort in desc order, with unreleasedLabelForEnv coming last.
|
|
.sort(({ semver: av }, { semver: bv }) => {
|
|
if (av === unreleasedLabelForEnv) return -1;
|
|
if (bv === unreleasedLabelForEnv) return 1;
|
|
if (semver.gt(av, bv)) return -1;
|
|
if (semver.gt(bv, av)) return 1;
|
|
return 0;
|
|
});
|
|
|
|
// Find the lowest version such that all higher versions implement it.
|
|
// Eg, given { chrome70: true, chrome60: false, chrome50: true }, the
|
|
// lowest version is chrome70, not chrome50.
|
|
let lowest = null;
|
|
for (const version of reportedVersions) {
|
|
if (!version.implements) {
|
|
break;
|
|
}
|
|
lowest = version;
|
|
}
|
|
return lowest;
|
|
});
|
|
|
|
const envFiltered = envTests.filter(t => t);
|
|
if (envTests.length > envFiltered.length || envTests.length === 0) {
|
|
// envTests.forEach((test, i) => {
|
|
// if (!test) {
|
|
// // print unsupported features
|
|
// if (env === 'node') {
|
|
// console.log(`ENV(${env}): ${tests[i].name}`);
|
|
// }
|
|
// }
|
|
// });
|
|
return null;
|
|
}
|
|
|
|
return envFiltered.reduce((a, b) => {
|
|
if (
|
|
a.semver === unreleasedLabelForEnv ||
|
|
b.semver === unreleasedLabelForEnv
|
|
) {
|
|
return unreleasedLabelForEnv;
|
|
}
|
|
|
|
return semver.lt(a.semver, b.semver) ? b : a;
|
|
});
|
|
};
|
|
|
|
const generateData = (environments, features) => {
|
|
return mapValues(features, options => {
|
|
if (!options.features) {
|
|
options = {
|
|
features: [options],
|
|
};
|
|
}
|
|
|
|
const plugin = {};
|
|
|
|
environments.forEach(env => {
|
|
const version = getLowestImplementedVersion(options, env);
|
|
|
|
if (version !== null) {
|
|
const versionString = version.version;
|
|
|
|
// NOTE(bng): A number of environments in compat-table changed to
|
|
// include a trailing zero (node10 -> node10_0), so for now stripping
|
|
// it to be consistent
|
|
plugin[env] = versionString.endsWith(".0")
|
|
? versionString.slice(0, -2)
|
|
: versionString;
|
|
}
|
|
});
|
|
|
|
if (plugin.chrome) {
|
|
// add opera
|
|
if (plugin.chrome >= 28) {
|
|
plugin.opera = (plugin.chrome - 13).toString();
|
|
} else if (plugin.chrome === 5) {
|
|
plugin.opera = "12";
|
|
}
|
|
|
|
// add electron
|
|
const electronVersion = chromiumToElectron(plugin.chrome);
|
|
if (electronVersion) {
|
|
plugin.electron = electronVersion.toString();
|
|
}
|
|
}
|
|
|
|
return plugin;
|
|
});
|
|
};
|
|
|
|
["plugin", "corejs2-built-in"].forEach(target => {
|
|
const newData = generateData(
|
|
environments,
|
|
require(`../data/${target}-features`)
|
|
);
|
|
const dataPath = path.join(__dirname, `../data/${target}s.json`);
|
|
|
|
if (process.argv[2] === "--check") {
|
|
const currentData = require(dataPath);
|
|
|
|
if (!isEqual(currentData, newData)) {
|
|
console.error(
|
|
"The newly generated plugin/built-in data does not match the current " +
|
|
"files. Re-run `npm run build-data`."
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
process.exit(0);
|
|
}
|
|
|
|
fs.writeFileSync(dataPath, `${JSON.stringify(newData, null, 2)}\n`);
|
|
});
|