diff --git a/packages/babel-preset-gatsby-package/__tests__/__snapshots__/index.js.snap b/packages/babel-preset-gatsby-package/__tests__/__snapshots__/index.js.snap index 8caa0e21d0dab..966617470aca3 100644 --- a/packages/babel-preset-gatsby-package/__tests__/__snapshots__/index.js.snap +++ b/packages/babel-preset-gatsby-package/__tests__/__snapshots__/index.js.snap @@ -67,7 +67,7 @@ Array [ Array [ "@babel/preset-env", Object { - "corejs": 2, + "corejs": 3, "debug": false, "loose": true, "modules": "commonjs", @@ -90,7 +90,7 @@ Array [ Array [ "@babel/preset-env", Object { - "corejs": 2, + "corejs": 3, "debug": true, "loose": true, "modules": "commonjs", diff --git a/packages/babel-preset-gatsby-package/index.js b/packages/babel-preset-gatsby-package/index.js index 9d034c7d5fa39..864e20e5ea3e0 100644 --- a/packages/babel-preset-gatsby-package/index.js +++ b/packages/babel-preset-gatsby-package/index.js @@ -14,7 +14,7 @@ function preset(context, options = {}) { } const nodeConfig = { - corejs: 2, + corejs: 3, useBuiltIns: `entry`, targets: { node: nodeVersion, diff --git a/packages/babel-preset-gatsby-package/package.json b/packages/babel-preset-gatsby-package/package.json index a052a29106d7e..13bbc97354ce9 100644 --- a/packages/babel-preset-gatsby-package/package.json +++ b/packages/babel-preset-gatsby-package/package.json @@ -18,7 +18,7 @@ "@babel/preset-flow": "^7.10.1", "@babel/preset-react": "^7.10.1", "babel-plugin-dynamic-import-node": "^2.3.3", - "core-js": "^2.6.11" + "core-js": "^3.6.5" }, "peerDependencies": { "@babel/core": "^7.0.0" diff --git a/packages/babel-preset-gatsby/package.json b/packages/babel-preset-gatsby/package.json index a9e3264b14ba4..396f3b7bf3b5c 100644 --- a/packages/babel-preset-gatsby/package.json +++ b/packages/babel-preset-gatsby/package.json @@ -24,7 +24,8 @@ "gatsby-core-utils": "^1.3.8" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0", + "core-js": "^3.0.0" }, "license": "MIT", "main": "index.js", diff --git a/packages/babel-preset-gatsby/src/__tests__/__snapshots__/dependencies.ts.snap b/packages/babel-preset-gatsby/src/__tests__/__snapshots__/dependencies.ts.snap index 64ec918af09b1..ffcf375b9e0b5 100644 --- a/packages/babel-preset-gatsby/src/__tests__/__snapshots__/dependencies.ts.snap +++ b/packages/babel-preset-gatsby/src/__tests__/__snapshots__/dependencies.ts.snap @@ -19,7 +19,7 @@ Object { Array [ "/node_modules/@babel/preset-env/lib/index.js", Object { - "corejs": 2, + "corejs": 3, "exclude": Array [ "transform-typeof-symbol", ], diff --git a/packages/babel-preset-gatsby/src/__tests__/__snapshots__/index.js.snap b/packages/babel-preset-gatsby/src/__tests__/__snapshots__/index.js.snap index 13accfb93ac9e..fd425311be8ab 100644 --- a/packages/babel-preset-gatsby/src/__tests__/__snapshots__/index.js.snap +++ b/packages/babel-preset-gatsby/src/__tests__/__snapshots__/index.js.snap @@ -45,7 +45,7 @@ Object { Array [ "/node_modules/@babel/preset-env/lib/index.js", Object { - "corejs": 2, + "corejs": 3, "exclude": Array [ "transform-typeof-symbol", ], @@ -120,7 +120,7 @@ Object { Array [ "/node_modules/@babel/preset-env/lib/index.js", Object { - "corejs": 2, + "corejs": 3, "exclude": Array [ "transform-typeof-symbol", ], @@ -187,7 +187,7 @@ Object { Array [ "/node_modules/@babel/preset-env/lib/index.js", Object { - "corejs": 2, + "corejs": 3, "exclude": Array [ "transform-typeof-symbol", ], @@ -254,7 +254,7 @@ Object { Array [ "/node_modules/@babel/preset-env/lib/index.js", Object { - "corejs": 2, + "corejs": 3, "exclude": Array [ "transform-typeof-symbol", ], diff --git a/packages/babel-preset-gatsby/src/__tests__/index.js b/packages/babel-preset-gatsby/src/__tests__/index.js index d59f0b85294bc..6ba025f9539c7 100644 --- a/packages/babel-preset-gatsby/src/__tests__/index.js +++ b/packages/babel-preset-gatsby/src/__tests__/index.js @@ -24,7 +24,7 @@ describe(`babel-preset-gatsby`, () => { expect.stringContaining(path.join(`@babel`, `preset-env`)), { exclude: [`transform-typeof-symbol`], - corejs: 2, + corejs: 3, loose: true, modules: false, useBuiltIns: `usage`, diff --git a/packages/babel-preset-gatsby/src/dependencies.ts b/packages/babel-preset-gatsby/src/dependencies.ts index 048cf85c7f200..8494fcadda7ae 100644 --- a/packages/babel-preset-gatsby/src/dependencies.ts +++ b/packages/babel-preset-gatsby/src/dependencies.ts @@ -32,7 +32,7 @@ export default (_?: unknown, options: IPresetOptions = {}) => { { // Allow importing core-js in entrypoint and use browserlist to select polyfills useBuiltIns: `usage`, - corejs: 2, + corejs: 3, modules: false, // Exclude transforms that make all code slower (https://github.com/facebook/create-react-app/pull/5278) exclude: [`transform-typeof-symbol`], diff --git a/packages/babel-preset-gatsby/src/index.js b/packages/babel-preset-gatsby/src/index.js index fa92e82efa7e7..6fbb8adf299bc 100644 --- a/packages/babel-preset-gatsby/src/index.js +++ b/packages/babel-preset-gatsby/src/index.js @@ -51,7 +51,7 @@ module.exports = function preset(_, options = {}) { [ resolve(`@babel/preset-env`), { - corejs: 2, + corejs: 3, loose: true, modules: stage === `test` ? `commonjs` : false, useBuiltIns: `usage`, diff --git a/packages/gatsby-cli/package.json b/packages/gatsby-cli/package.json index ed01ce897a552..0473d4c72240e 100644 --- a/packages/gatsby-cli/package.json +++ b/packages/gatsby-cli/package.json @@ -11,7 +11,6 @@ }, "dependencies": { "@babel/code-frame": "^7.10.3", - "@babel/runtime": "^7.10.3", "@hapi/joi": "^15.1.1", "@types/common-tags": "^1.8.0", "better-opn": "^1.0.0", diff --git a/packages/gatsby-cli/src/index.ts b/packages/gatsby-cli/src/index.ts index 7c131ae25ab6a..18285c9abd0b1 100755 --- a/packages/gatsby-cli/src/index.ts +++ b/packages/gatsby-cli/src/index.ts @@ -1,6 +1,5 @@ #!/usr/bin/env node -import "@babel/polyfill" import os from "os" import semver from "semver" import util from "util" @@ -46,7 +45,7 @@ if (semver.prerelease(version)) { report.warn( report.stripIndent(` You are currently using a prerelease version of Node (${version}), which is not supported. - You can use this for testing, but we do not recommend it in production. + You can use this for testing, but we do not recommend it in production. Before reporting any bugs, please test with a supported version of Node (>=${MIN_NODE_VERSION}).`) ) } diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 95acf9c5046b5..cdf8dc8e37671 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -13,8 +13,6 @@ "@babel/code-frame": "^7.10.3", "@babel/core": "^7.10.3", "@babel/parser": "^7.10.3", - "@babel/polyfill": "^7.8.7", - "@babel/runtime": "^7.10.3", "@babel/traverse": "^7.10.3", "@hapi/joi": "^15.1.1", "@mikaelkristiansson/domready": "^1.0.10", @@ -46,7 +44,7 @@ "compression": "^1.7.4", "convert-hrtime": "^3.0.0", "copyfiles": "^2.3.0", - "core-js": "^2.6.11", + "core-js": "^3.6.5", "cors": "^2.8.5", "css-loader": "^1.0.1", "date-fns": "^2.14.0", @@ -167,6 +165,7 @@ "babel-preset-gatsby-package": "^0.4.6", "cross-env": "^5.2.1", "documentation": "^12.3.0", + "enhanced-resolve": "^4.2.0", "eslint-plugin-jsx-a11y": "^6.3.1", "react": "^16.12.0", "react-dom": "^16.12.0", diff --git a/packages/gatsby/src/utils/webpack.config.js b/packages/gatsby/src/utils/webpack.config.js index 95262fbe5c498..edfcedf4db64f 100644 --- a/packages/gatsby/src/utils/webpack.config.js +++ b/packages/gatsby/src/utils/webpack.config.js @@ -5,6 +5,7 @@ const fs = require(`fs-extra`) const path = require(`path`) const dotenv = require(`dotenv`) const PnpWebpackPlugin = require(`pnp-webpack-plugin`) +const { CoreJSResolver } = require(`./webpack/corejs-resolver`) const { store } = require(`../redux`) const { actions } = require(`../redux/actions`) const { getPublicPath } = require(`./get-public-path`) @@ -388,7 +389,6 @@ module.exports = async ( "@babel/runtime": path.dirname( require.resolve(`@babel/runtime/package.json`) ), - "core-js": path.dirname(require.resolve(`core-js/package.json`)), // TODO: Remove entire block when we make fast-refresh the default ...(process.env.GATSBY_HOT_LOADER !== `fast-refresh` ? { @@ -415,6 +415,7 @@ module.exports = async ( PnpWebpackPlugin.bind(directoryPath(`public`), module), // Transparently resolve packages via PnP when needed; noop otherwise PnpWebpackPlugin, + new CoreJSResolver(), ], } diff --git a/packages/gatsby/src/utils/webpack/__tests__/corejs-resolver.ts b/packages/gatsby/src/utils/webpack/__tests__/corejs-resolver.ts new file mode 100644 index 0000000000000..a4304c95daac9 --- /dev/null +++ b/packages/gatsby/src/utils/webpack/__tests__/corejs-resolver.ts @@ -0,0 +1,75 @@ +import { slash } from "gatsby-core-utils" +import { CoreJSResolver } from "../corejs-resolver" + +function executeResolve( + resolver: CoreJSResolver, + request: { request: string }, + doResolveMock: unknown +): Promise { + return new Promise((resolve, reject) => { + const webpackResolver = { + doResolve: doResolveMock, + ensureHook: (hook: string): string => hook, + getHook: (): Record => { + return { + tapAsync: (_name: string, fn: Function): void => { + fn(request, null, (err, result) => + err ? reject(err) : resolve(result) + ) + }, + } + }, + } + + resolver.apply(webpackResolver) + }) +} + +describe(`CoreJSResolver`, () => { + it(`should convert core-js@2 file to core-js@3`, async () => { + const resolver = new CoreJSResolver() + + const doResolve = jest.fn((_, request, __, ___, callback) => + callback(null, slash(request.request)) + ) + + expect( + await executeResolve( + resolver, + { request: `core-js/modules/es6.array.split.js` }, + doResolve + ) + ).toEqual(expect.stringContaining(`core-js/modules/es.array.split.js`)) + }) + + it(`should convert es6.regexp.replace to its corejs@3 equivalent`, async () => { + const resolver = new CoreJSResolver() + + const doResolve = jest.fn((_, request, __, ___, callback) => + callback(null, slash(request.request)) + ) + + expect( + await executeResolve( + resolver, + { request: `core-js/modules/es6.regexp.replace.js` }, + doResolve + ) + ).toEqual(expect.stringContaining(`core-js/modules/es.string.replace.js`)) + }) + + it(`should ignore non corejs requests`, async () => { + const resolver = new CoreJSResolver() + + const doResolve = jest.fn() + + expect( + await executeResolve( + resolver, + { request: `gatsby/not/core-js.js` }, + doResolve + ) + ).toBeUndefined() + expect(doResolve).not.toHaveBeenCalled() + }) +}) diff --git a/packages/gatsby/src/utils/webpack/corejs-resolver.ts b/packages/gatsby/src/utils/webpack/corejs-resolver.ts new file mode 100644 index 0000000000000..bafbc4da24e39 --- /dev/null +++ b/packages/gatsby/src/utils/webpack/corejs-resolver.ts @@ -0,0 +1,103 @@ +import Resolver from "enhanced-resolve/lib/Resolver" +import * as path from "path" + +// Core-js uses es6, es7 & web prefixes, which we'll convert to core-js 3 +const coreJs2FileRegex = /\/modules\/(es6|es7|web)\.|\/es6\/|\/es7\// + +// Try to replace core-js2 files to core-js@3 to reduce file size +const replaceMap = [ + [`/es6.`, `/es.`], + [`/es7.`, `/es.`], + [`/es6/`, `/es/`], + [`/es7/`, `/es/`], + [`/es7/`, `/es/`], + [`web.dom.iterable`, `web.dom-collections.iterator.js`], + [`typed.data-view`, `data-view`], + [`regexp.match`, `string.match`], + [`regexp.replace`, `string.replace`], + [`regexp.search`, `string.search`], + [`regexp.split`, `string.split`], +] + +interface IRequest { + request?: string + path: string +} + +/** + * Babel-preset is set to corejs@3 which will add automatic polyfills. If a project has core-js@2 installed in their root or a package got compiled with core-js@2 + * we need to convert it to corejs@3 because core-js@2 isn't available or we might add multiple polyfills for the same problem. + * + * The resolver converts core-js@2 imports to core-js@3 imports to make our bundle as small as possible. + */ +export class CoreJSResolver { + _coreJSNodeModulesPath: string + _resolver?: Resolver + _target?: string + + constructor() { + // Get the nodemodules directory where core-js of gatsby lives + // it might be inside gatsby/node_modules when multiple core-js versions are loaded + this._coreJSNodeModulesPath = path.dirname( + path.dirname(require.resolve(`core-js`)) + ) + } + + resolve( + request: IRequest, + resolveContext: unknown, + callback: (err?: Error | null, result?: unknown) => void + ): void { + const innerRequest = request.request || request.path + + // we only care about core-js + if (!innerRequest || !innerRequest.startsWith(`core-js/`)) { + return void callback() + } + + let coreJsRequest = innerRequest + let resolveMessage = `alias core-js@3 to gatsby's core-js package` + + // preset-env adds packages from modules/ so we rewrite them to our gatsby package + if (coreJs2FileRegex.test(coreJsRequest)) { + replaceMap.forEach(([search, replace]) => { + coreJsRequest = coreJsRequest.replace(search, replace) + }) + + resolveMessage = `map core-js@2(${innerRequest}) to corejs@3(${coreJsRequest})` + } + + return void this._resolver.doResolve( + this._target, + { + ...request, + request: path.resolve(this._coreJSNodeModulesPath, coreJsRequest), + }, + resolveMessage, + resolveContext, + (err, result) => { + if (err) { + return callback(err) + } + + // if a rename fails we try to load the original file + // this could error when our mapping isn't complete. I've tested this on a couple of sites + // and couldn't find anything but you never know. + if (result === undefined) { + return callback() + } + + return callback(null, result) + } + ) + } + + apply(resolver: Resolver): void { + this._target = resolver.ensureHook(`resolve`) + this._resolver = resolver + + resolver + .getHook(`described-resolve`) + .tapAsync(`CoreJSResolver`, this.resolve.bind(this)) + } +} diff --git a/yarn.lock b/yarn.lock index e5d0c15d5c10d..405ddb30a8882 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1033,13 +1033,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.1" "@babel/helper-plugin-utils" "^7.10.1" -"@babel/polyfill@^7.2.5", "@babel/polyfill@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.8.7.tgz#151ec24c7135481336168c3bd8b8bf0cf91c032f" - integrity sha512-LeSfP9bNZH2UOZgcGcZ0PIHUt1ZuHub1L3CVmEyqLxCeDLm4C5Gi8jRH8ZX2PNpDhQCo0z6y/+DIs2JlliXW8w== +"@babel/polyfill@^7.2.5": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.6.0.tgz#6d89203f8b6cd323e8d946e47774ea35dc0619cc" + integrity sha512-q5BZJI0n/B10VaQQvln1IlDK3BTBJFbADx7tv+oXDPIDZuTo37H5Adb9jhlXm/fEN4Y7/64qD9mnrJJG7rmaTw== dependencies: core-js "^2.6.5" - regenerator-runtime "^0.13.4" + regenerator-runtime "^0.13.2" "@babel/preset-env@^7.1.6", "@babel/preset-env@^7.10.2", "@babel/preset-env@^7.10.3", "@babel/preset-env@^7.9.0": version "7.10.3" @@ -7065,10 +7065,10 @@ core-js@2, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0, core-js@^2.6.11, core resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== -core-js@^3.2.1, core-js@^3.4.1: - version "3.6.4" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" - integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== +core-js@^3.2.1, core-js@^3.4.1, core-js@^3.6.5: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" + integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -8613,7 +8613,7 @@ engine.io@~3.4.0: engine.io-parser "~2.2.0" ws "^7.1.2" -enhanced-resolve@^4.1.0, enhanced-resolve@^4.1.1: +enhanced-resolve@^4.1.0, enhanced-resolve@^4.1.1, enhanced-resolve@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz#5d43bda4a0fd447cb0ebbe71bef8deff8805ad0d" integrity sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ==