From 7e39a12d1287307f82a94f4211022ab6f0972fb9 Mon Sep 17 00:00:00 2001 From: Ward Peeters Date: Mon, 1 Jul 2019 16:54:15 +0200 Subject: [PATCH] fix(*): enable separate config for node_modules transpilation (#15270) --- packages/babel-preset-gatsby/package.json | 1 + .../src/__tests__/index.js | 15 ++++- .../babel-preset-gatsby/src/dependencies.js | 55 +++++++++++++++++++ packages/babel-preset-gatsby/src/index.js | 10 +++- .../__snapshots__/webpack-utils.js.snap | 23 ++++++-- .../src/utils/__tests__/webpack-utils.js | 10 ++-- packages/gatsby/src/utils/webpack-utils.js | 38 +++++++++++-- packages/gatsby/src/utils/webpack.config.js | 13 +++-- yarn.lock | 1 + 9 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 packages/babel-preset-gatsby/src/dependencies.js diff --git a/packages/babel-preset-gatsby/package.json b/packages/babel-preset-gatsby/package.json index 2cf6ccf609fa2..be742a806a392 100644 --- a/packages/babel-preset-gatsby/package.json +++ b/packages/babel-preset-gatsby/package.json @@ -8,6 +8,7 @@ "@babel/plugin-transform-runtime": "^7.0.0", "@babel/preset-env": "^7.4.1", "@babel/preset-react": "^7.0.0", + "@babel/runtime": "^7.4.5", "babel-plugin-macros": "^2.4.2" }, "peerDependencies": { diff --git a/packages/babel-preset-gatsby/src/__tests__/index.js b/packages/babel-preset-gatsby/src/__tests__/index.js index 36aaa2cbf8034..6979dcfd02a1a 100644 --- a/packages/babel-preset-gatsby/src/__tests__/index.js +++ b/packages/babel-preset-gatsby/src/__tests__/index.js @@ -8,6 +8,7 @@ it(`Specifies proper presets and plugins for test stage`, () => { [ expect.stringContaining(path.join(`@babel`, `preset-env`)), { + exclude: [`transform-typeof-symbol`], corejs: 2, loose: true, modules: `commonjs`, @@ -40,8 +41,13 @@ it(`Specifies proper presets and plugins for test stage`, () => { [ expect.stringContaining(path.join(`@babel`, `plugin-transform-runtime`)), { + absoluteRuntimePath: expect.stringContaining( + path.join(`@babel`, `runtime`) + ), + corejs: false, helpers: true, regenerator: true, + useESModules: false, }, ], ]) @@ -63,6 +69,7 @@ it(`Specifies proper presets and plugins for build-html stage`, () => { [ expect.stringContaining(path.join(`@babel`, `preset-env`)), { + exclude: [`transform-typeof-symbol`], corejs: 2, loose: true, modules: false, @@ -95,8 +102,13 @@ it(`Specifies proper presets and plugins for build-html stage`, () => { [ expect.stringContaining(path.join(`@babel`, `plugin-transform-runtime`)), { - helpers: true, + absoluteRuntimePath: expect.stringContaining( + path.join(`@babel`, `runtime`) + ), + helpers: false, regenerator: true, + corejs: false, + useESModules: true, }, ], ]) @@ -111,6 +123,7 @@ it(`Allows to configure browser targets`, () => { expect(presets[0]).toEqual([ expect.stringContaining(path.join(`@babel`, `preset-env`)), { + exclude: [`transform-typeof-symbol`], corejs: 2, loose: true, modules: false, diff --git a/packages/babel-preset-gatsby/src/dependencies.js b/packages/babel-preset-gatsby/src/dependencies.js new file mode 100644 index 0000000000000..c26fac98d1f45 --- /dev/null +++ b/packages/babel-preset-gatsby/src/dependencies.js @@ -0,0 +1,55 @@ +// This file is heavily based on create-react-app's implementation +// @see https://github.com/facebook/create-react-app/blob/master/packages/babel-preset-react-app/dependencies.js + +const path = require(`path`) +const resolve = m => require.resolve(m) + +module.exports = function(api, options = {}) { + const absoluteRuntimePath = path.dirname( + resolve(`@babel/runtime/package.json`) + ) + + return { + // Babel assumes ES Modules, which isn't safe until CommonJS + // dies. This changes the behavior to assume CommonJS unless + // an `import` or `export` is present in the file. + // https://github.com/webpack/webpack/issues/4039#issuecomment-419284940 + sourceType: `unambiguous`, + presets: [ + [ + // Latest stable ECMAScript features + `@babel/preset-env`, + { + // Allow importing core-js in entrypoint and use browserlist to select polyfills + useBuiltIns: `usage`, + corejs: 2, + modules: false, + // Exclude transforms that make all code slower (https://github.com/facebook/create-react-app/pull/5278) + exclude: [`transform-typeof-symbol`], + }, + ], + ], + plugins: [ + // Polyfills the runtime needed for async/await, generators, and friends + // https://babeljs.io/docs/en/babel-plugin-transform-runtime + [ + resolve(`@babel/plugin-transform-runtime`), + { + corejs: false, + helpers: true, + regenerator: true, + // https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules + // We should turn this on once the lowest version of Node LTS + // supports ES Modules. + useESModules: true, + // Undocumented option that lets us encapsulate our runtime, ensuring + // the correct version is used + // https://github.com/babel/babel/blob/090c364a90fe73d36a30707fc612ce037bdbbb24/packages/babel-plugin-transform-runtime/src/index.js#L35-L42 + absoluteRuntime: absoluteRuntimePath, + }, + ], + // Adds syntax support for import() + resolve(`@babel/plugin-syntax-dynamic-import`), + ], + } +} diff --git a/packages/babel-preset-gatsby/src/index.js b/packages/babel-preset-gatsby/src/index.js index 02ab096447a66..297dba0c9039d 100644 --- a/packages/babel-preset-gatsby/src/index.js +++ b/packages/babel-preset-gatsby/src/index.js @@ -29,6 +29,9 @@ module.exports = function preset(_, options = {}) { const pluginBabelConfig = loadCachedConfig() const stage = process.env.GATSBY_BUILD_STAGE || `test` + const absoluteRuntimePath = path.dirname( + require.resolve(`@babel/runtime/package.json`) + ) if (!targets) { if (stage === `build-html` || stage === `test`) { @@ -50,6 +53,8 @@ module.exports = function preset(_, options = {}) { modules: stage === `test` ? `commonjs` : false, useBuiltIns: `usage`, targets, + // Exclude transforms that make all code slower (https://github.com/facebook/create-react-app/pull/5278) + exclude: [`transform-typeof-symbol`], }, ], [ @@ -73,8 +78,11 @@ module.exports = function preset(_, options = {}) { [ resolve(`@babel/plugin-transform-runtime`), { - helpers: true, + corejs: false, + helpers: stage === `develop` || stage === `test`, regenerator: true, + useESModules: stage !== `test`, + absoluteRuntimePath, }, ], ], diff --git a/packages/gatsby/src/utils/__tests__/__snapshots__/webpack-utils.js.snap b/packages/gatsby/src/utils/__tests__/__snapshots__/webpack-utils.js.snap index b215eea6216a8..eb3793f597a77 100644 --- a/packages/gatsby/src/utils/__tests__/__snapshots__/webpack-utils.js.snap +++ b/packages/gatsby/src/utils/__tests__/__snapshots__/webpack-utils.js.snap @@ -2,7 +2,7 @@ exports[`webpack utils js returns default values without any options 1`] = ` Object { - "exclude": /core-js\\|event-source-polyfill\\|webpack-hot-middleware\\\\/client/, + "exclude": /\\(node_modules\\|bower_components\\)/, "test": /\\\\\\.\\(js\\|mjs\\|jsx\\)\\$/, "type": "javascript/auto", "use": Array [ @@ -14,15 +14,28 @@ Object { } `; -exports[`webpack utils js returns the correct exclude paths 1`] = ` +exports[`webpack utils js returns default values without any options 2`] = ` Object { - "exclude": /core-js\\|event-source-polyfill\\|webpack-hot-middleware\\\\/client\\|node_modules/, - "test": /\\\\\\.\\(js\\|mjs\\|jsx\\)\\$/, + "exclude": /@babel\\(\\?:\\\\/\\|\\\\\\\\\\{1,2\\}\\)runtime\\|core-js/, + "test": /\\\\\\.\\(js\\|mjs\\)\\$/, "type": "javascript/auto", "use": Array [ Object { "loader": "/packages/gatsby/src/utils/babel-loader.js", - "options": Object {}, + "options": Object { + "babelrc": false, + "compact": false, + "configFile": false, + "presets": Array [ + Array [ + "/packages/babel-preset-gatsby/dependencies.js", + Object { + "stage": "develop", + }, + ], + ], + "sourceMaps": false, + }, }, ], } diff --git a/packages/gatsby/src/utils/__tests__/webpack-utils.js b/packages/gatsby/src/utils/__tests__/webpack-utils.js index 58436833d9572..14951ba0f1969 100644 --- a/packages/gatsby/src/utils/__tests__/webpack-utils.js +++ b/packages/gatsby/src/utils/__tests__/webpack-utils.js @@ -22,10 +22,12 @@ describe(`webpack utils`, () => { expect(rule).toMatchSnapshot() }) - it(`returns the correct exclude paths`, () => { - const rule = config.rules.js({ - exclude: [`node_modules`], - }) + it(`adds dependency rule`, () => { + expect(config.rules.dependencies).toEqual(expect.any(Function)) + }) + + it(`returns default values without any options`, () => { + const rule = config.rules.dependencies() expect(rule).toMatchSnapshot() }) diff --git a/packages/gatsby/src/utils/webpack-utils.js b/packages/gatsby/src/utils/webpack-utils.js index fd3d482f2d2ef..d47963ecc85be 100644 --- a/packages/gatsby/src/utils/webpack-utils.js +++ b/packages/gatsby/src/utils/webpack-utils.js @@ -293,14 +293,10 @@ module.exports = async ({ * JavaScript loader via babel, excludes node_modules */ { - let js = ({ exclude = [], ...options } = {}) => { - const excludeRegex = [ - `core-js|event-source-polyfill|webpack-hot-middleware/client`, - ].concat(exclude) - + let js = (options = {}) => { return { test: /\.(js|mjs|jsx)$/, - exclude: new RegExp(excludeRegex.join(`|`)), + exclude: vendorRegex, type: `javascript/auto`, use: [loaders.js(options)], } @@ -309,6 +305,36 @@ module.exports = async ({ rules.js = js } + /** + * Node_modules JavaScript loader via babel (exclude core-js & babel-runtime to speedup babel transpilation) + */ + { + let dependencies = (options = {}) => { + const jsOptions = { + babelrc: false, + configFile: false, + compact: false, + presets: [ + [require.resolve(`babel-preset-gatsby/dependencies`), { stage }], + ], + // If an error happens in a package, it's possible to be + // because it was compiled. Thus, we don't want the browser + // debugger to show the original code. Instead, the code + // being evaluated would be much more helpful. + sourceMaps: false, + } + + return { + test: /\.(js|mjs)$/, + exclude: /@babel(?:\/|\\{1,2})runtime|core-js/, + type: `javascript/auto`, + use: [loaders.js(jsOptions)], + } + } + + rules.dependencies = dependencies + } + { let eslint = schema => { return { diff --git a/packages/gatsby/src/utils/webpack.config.js b/packages/gatsby/src/utils/webpack.config.js index 1cb4cadf35a6e..3b369c6bd084a 100644 --- a/packages/gatsby/src/utils/webpack.config.js +++ b/packages/gatsby/src/utils/webpack.config.js @@ -248,12 +248,6 @@ module.exports = async (program, directory, suppliedStage) => { function getModule() { const jsOptions = {} - // Speedup 🏎️💨 the build! We only include transpilation of node_modules on production builds - // TODO create gatsby plugin to enable this behaviour on develop (only when people are requesting this feature) - if (stage === `develop`) { - jsOptions.exclude = [`node_modules`] - } - // Common config for every env. // prettier-ignore let configRules = [ @@ -264,6 +258,13 @@ module.exports = async (program, directory, suppliedStage) => { rules.media(), rules.miscAssets(), ] + + // Speedup 🏎️💨 the build! We only include transpilation of node_modules on javascript production builds + // TODO create gatsby plugin to enable this behaviour on develop (only when people are requesting this feature) + if (stage === `build-javascript`) { + configRules.push(rules.dependencies()) + } + if (store.getState().themes.themes) { configRules = configRules.concat( store.getState().themes.themes.map(theme => { diff --git a/yarn.lock b/yarn.lock index 194c62c558eee..4d92a013d4649 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1937,6 +1937,7 @@ "@babel/runtime@^7.4.5": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" + integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ== dependencies: regenerator-runtime "^0.13.2"