Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: when transpileDependencies is set to true, transpile all dependencies in node_modules #6354

Merged
merged 8 commits into from
Mar 24, 2021
8 changes: 5 additions & 3 deletions docs/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,12 @@ Deprecated since Vue CLI 3.3, please use [`publicPath`](#publicPath) instead.

### transpileDependencies

- Type: `Array<string | RegExp>`
- Default: `[]`
- Type: `boolean | Array<string | RegExp>`
- Default: `false`

By default `babel-loader` ignores all files inside `node_modules`. You can enable this option to avoid unexpected untranspiled code from third-party dependencies.

By default `babel-loader` ignores all files inside `node_modules`. If you want to explicitly transpile a dependency with Babel, you can list it in this option.
Transpiling all the dependencies could slow down the build process, though. If build performance is a concern, you can explicitly transpile only some of the dependencies by passing an array of package names or name patterns to this option.

::: warning Jest config
This option is not respected by the [cli-unit-jest plugin](#jest), because in jest, we don't have to transpile code from `/node_modules` unless it uses non-standard features - Node >8.11 supports the latest ECMAScript features already.
Expand Down
4 changes: 3 additions & 1 deletion docs/core-plugins/babel.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

Uses Babel 7 + `babel-loader` + [@vue/babel-preset-app](https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/babel-preset-app) by default, but can be configured via `babel.config.js` to use any other Babel presets or plugins.

By default, `babel-loader` excludes files inside `node_modules` dependencies. If you wish to explicitly transpile a dependency module, you will need to add it to the `transpileDependencies` option in `vue.config.js`:
By default, `babel-loader` excludes files inside `node_modules` dependencies. You can enable the `transpileDependencies` option in `vue.config.js` to transpile the dependencies. Or, if you want to only transpile some of the dependency modules, you can pass an array to the `transpileDependencies` option:

``` js
module.exports = {
// Set to `true` to transpile all dependencies.
// Or pass an array to transpile selectively.
transpileDependencies: [
// can be string or regex
'my-dep',
Expand Down
4 changes: 4 additions & 0 deletions docs/migrations/migrate-from-v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ No longer supports generating project with `node-sass`. It has been [deprecated]
* `terser-webpack-plugin` is upgraded from v2 to v5 (v4 if you choose to stay at webpack 4), using terser 5 and some there are some changes in the options format. See full details in its [changelog](https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/CHANGELOG.md).
* When creating new projects, the default `less-loader` is updated from [v5 to v8](https://github.com/webpack-contrib/less-loader/blob/master/CHANGELOG.md)(v7 for webpack 4 users); `less` from [v3 to v4](https://github.com/less/less.js/pull/3573); `sass-loader` from [v8 to v11](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md) (v10 for webpack 4 users); `stylus-loader` from [v3 to v5](https://github.com/webpack-contrib/stylus-loader/blob/master/CHANGELOG.md) (v4 for webpack 4 users).

### Babel Plugin

The [`transpileDependencies` option](../config/#transpiledependencies) now accepts a boolean value. Setting it to `true` will transpile all dependencies inside `node_modules`.

### ESLint Plugin

* `eslint-loader` is replaced by [eslint-webpack-plugin](https://github.com/webpack-contrib/eslint-webpack-plugin), dropping support for ESLint <= 6.
Expand Down
9 changes: 6 additions & 3 deletions docs/zh/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,13 @@ module.exports = {

### transpileDependencies

- Type: `Array<string | RegExp>`
- Default: `[]`
- Type: `boolean | Array<string | RegExp>`
- Default: `false`

默认情况下 `babel-loader` 会忽略所有 `node_modules` 中的文件。你可以启用本选项,以避免构建后的代码中出现未转译的第三方依赖。

不过,对所有的依赖都进行转译可能会降低构建速度。如果对构建性能有所顾虑,你可以只转译部分特定的依赖:给本选项传一个数组,列出需要转译的第三方包包名或正则表达式即可。

默认情况下 `babel-loader` 会忽略所有 `node_modules` 中的文件。如果你想要通过 Babel 显式转译一个依赖,可以在这个选项中列出来。

### productionSourceMap

Expand Down
43 changes: 22 additions & 21 deletions packages/@vue/cli-plugin-babel/__tests__/babelRuntime.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,31 @@ const { defaultPreset } = require('@vue/cli/lib/options')
const create = require('@vue/cli-test-utils/createTestProject')
const serve = require('@vue/cli-test-utils/serveWithPuppeteer')

test('should add polyfills for code in @babel/runtime', async () => {
const project = await create('babel-runtime-polyfills', defaultPreset)
// iterableToArray no longer required in babel/runtime 7.8.7+
// test('should add polyfills for code in @babel/runtime', async () => {
// const project = await create('babel-runtime-polyfills', defaultPreset)

await project.write('src/main.js', `
const x = function () {
setTimeout(
// eslint-disable-next-line
() => console.log(...arguments), 100
);
}
x(1, 2)
`)
// await project.write('src/main.js', `
// const x = function () {
// setTimeout(
// // eslint-disable-next-line
// () => console.log(...arguments), 100
// );
// }
// x(1, 2)
// `)

await project.run('vue-cli-service build --mode development')
const vendorFile = await project.read('dist/js/chunk-vendors.js')
// await project.run('vue-cli-service build --mode development')
// const vendorFile = await project.read('dist/js/chunk-vendors.js')

// iterableToArray is used to transform `console.log(...arguments)`
expect(vendorFile).toMatch('iterableToArray')
// with inline helpers, preset-env can detect the symbol polyfill is required
// (because the implementation of `iterableToArray` relies on it)
// however, with transform-runtime plugin, helpers are only references to @babel/runtime modules
// so we need to make sure polyfill detection is enabled for @babel/runtime too
expect(vendorFile).toMatch('es.symbol')
})
// // iterableToArray is used to transform `console.log(...arguments)`
// expect(vendorFile).toMatch('iterableToArray')
// // with inline helpers, preset-env can detect the symbol polyfill is required
// // (because the implementation of `iterableToArray` relies on it)
// // however, with transform-runtime plugin, helpers are only references to @babel/runtime modules
// // so we need to make sure polyfill detection is enabled for @babel/runtime too
// expect(vendorFile).toMatch('es.symbol')
// })

test('should not transpile babel helpers multiple times', async () => {
const project = await create('babel-runtime-helpers', defaultPreset)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ afterAll(async () => {
await project.rm('package.json')
})

test('dep from node_modules should not been transpiled', async () => {
test('dep from node_modules should not been transpiled by default', async () => {
await project.run('vue-cli-service build')
expect(await readVendorFile()).toMatch('() => "__TEST__"')
})

test('dep from node_modules should been transpiled', async () => {
test('dep from node_modules should been transpiled when matched by transpileDependencies', async () => {
await project.write(
'vue.config.js',
`module.exports = { transpileDependencies: ['external-dep', '@scope/external-dep'] }`
Expand All @@ -84,6 +84,17 @@ test('dep from node_modules should been transpiled', async () => {
expect(await readVendorFile()).toMatch('return "__SCOPE_TEST__"')
})

test('dep from node_modules should been transpiled when transpileDependencies is true', async () => {
await project.write(
'vue.config.js',
`module.exports = { transpileDependencies: true }`
)
await project.run('vue-cli-service build')
expect(await readVendorFile()).toMatch('return "__TEST__"')

expect(await readVendorFile()).toMatch('return "__SCOPE_TEST__"')
})

// https://github.com/vuejs/vue-cli/issues/3057
test('only transpile package with same name specified in transpileDependencies', async () => {
await project.write(
Expand Down
61 changes: 43 additions & 18 deletions packages/@vue/cli-plugin-babel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const path = require('path')
const babel = require('@babel/core')
const { isWindows } = require('@vue/cli-shared-utils')

function genTranspileDepRegex (transpileDependencies) {
const deps = transpileDependencies.map(dep => {
function getDepPathRegex (dependencies) {
const deps = dependencies.map(dep => {
if (typeof dep === 'string') {
const depPath = path.join('node_modules', dep, '/')
return isWindows
Expand All @@ -22,7 +22,6 @@ function genTranspileDepRegex (transpileDependencies) {
module.exports = (api, options) => {
const useThreads = process.env.NODE_ENV === 'production' && !!options.parallel
const cliServicePath = path.dirname(require.resolve('@vue/cli-service'))
const transpileDepRegex = genTranspileDepRegex(options.transpileDependencies)

// try to load the project babel config;
// if the default preset is used,
Expand All @@ -39,34 +38,60 @@ module.exports = (api, options) => {
.test(/\.m?jsx?$/)
.exclude
.add(filepath => {
const SHOULD_SKIP = true
const SHOULD_TRANSPILE = false

// With data URI support in webpack 5, filepath could be undefined
if (!filepath) {
return true
return SHOULD_SKIP
}

// always transpile js in vue files
// Always transpile js in vue files
if (/\.vue\.jsx?$/.test(filepath)) {
return false
return SHOULD_TRANSPILE
}
// exclude dynamic entries from cli-service
// Exclude dynamic entries from cli-service
if (filepath.startsWith(cliServicePath)) {
return true
return SHOULD_SKIP
}

// To transpile `@babel/runtime`, the config needs to be
// carefully adjusted to avoid infinite loops.
// So we only do the tranpilation when the special flag is on.
if (getDepPathRegex(['@babel/runtime']).test(filepath)) {
return process.env.VUE_CLI_TRANSPILE_BABEL_RUNTIME
? SHOULD_TRANSPILE
: SHOULD_SKIP
}

// only include @babel/runtime when the @vue/babel-preset-app preset is used
if (
process.env.VUE_CLI_TRANSPILE_BABEL_RUNTIME &&
filepath.includes(path.join('@babel', 'runtime'))
) {
return false
// if `transpileDependencies` is set to true, transpile all deps
if (options.transpileDependencies === true) {
// Some of the deps cannot be transpiled, though
// https://stackoverflow.com/a/58517865/2302258
const NON_TRANSPILABLE_DEPS = [
'core-js',
'webpack',
'webpack-4',
'css-loader',
'mini-css-extract-plugin',
'promise-polyfill',
'html-webpack-plugin',
'whatwg-fetch'
]
const nonTranspilableDepsRegex = getDepPathRegex(NON_TRANSPILABLE_DEPS)
return nonTranspilableDepsRegex.test(filepath) ? SHOULD_SKIP : SHOULD_TRANSPILE
}

// check if this is something the user explicitly wants to transpile
if (transpileDepRegex && transpileDepRegex.test(filepath)) {
return false
// Otherwise, check if this is something the user explicitly wants to transpile
if (Array.isArray(options.transpileDependencies)) {
const transpileDepRegex = getDepPathRegex(options.transpileDependencies)
if (transpileDepRegex && transpileDepRegex.test(filepath)) {
return SHOULD_TRANSPILE
}
}

// Don't transpile node_modules
return /node_modules/.test(filepath)
return /node_modules/.test(filepath) ? SHOULD_SKIP : SHOULD_TRANSPILE
})
.end()

Expand Down
11 changes: 6 additions & 5 deletions packages/@vue/cli-service/lib/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ const schema = createSchema(joi => joi.object({
indexPath: joi.string(),
filenameHashing: joi.boolean(),
runtimeCompiler: joi.boolean(),
transpileDependencies: joi.array(),
transpileDependencies: joi.alternatives().try(
joi.boolean(),
joi.array()
),
productionSourceMap: joi.boolean(),
parallel: joi.alternatives().try(
joi.boolean(),
Expand Down Expand Up @@ -94,10 +97,8 @@ exports.defaults = () => ({
// boolean, use full build?
runtimeCompiler: false,

// deps to transpile
transpileDependencies: [
/* string or regex */
],
// whether to transpile all dependencies
transpileDependencies: false,

// sourceMap for production build?
productionSourceMap: !process.env.VUE_CLI_TEST,
Expand Down
8 changes: 5 additions & 3 deletions packages/@vue/cli-service/types/ProjectOptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,13 @@ interface ProjectOptions {
*/
runtimeCompiler?: boolean;
/**
* Default: `[]`
* Default: `false`
*
* If you want to explicitly transpile a dependency with Babel, you can list it in this option
* If set to `true`, all dependencies in `node_modules` will be transpiled by Babel;
* Or, if you only want to selectively transpile some of the dependencies, you can list them
* in this option.
*/
transpileDependencies?: Array<string | RegExp>;
transpileDependencies?: boolean | Array<string | RegExp>;
/**
* Default: `true`
*
Expand Down