Skip to content

Commit 759d77f

Browse files
authored
refactor: use babel overrides to transpile babel runtime helpers (#4777)
* refactor: use babel overrides to transpile babel runtime helpers As recommended in babel/babel#9903. Get rid of the module-resolver plugin, may fix #3928. Seems to have fixed #4742 as well. There may be a small breaking change: as we now use `excludes` & `includes`, babel requires `filename` option to be present (introduced in https://github.com/babel/babel/pull/10181/files). So users who call `babel.transformSync` directly may encounter an error. However, as we explicitly stated that this preset is only used for Vue CLI internally, I don't expect too many such use cases there. And the error messages are clear enough. Considering the benefits that this PR brings, I think it's an acceptable tradeoff. test: update tests for babel * test: fix windows tests * test: remove unused variables * fix: fix scope package paths on Windows * test: wait some time in router tests in case dom hasn't updated in time
1 parent 0a5c79b commit 759d77f

File tree

6 files changed

+120
-24
lines changed

6 files changed

+120
-24
lines changed

packages/@vue/babel-preset-app/__tests__/babel-preset.spec.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ test('async/await', () => {
107107
// should use regenerator runtime
108108
expect(code).toMatch(`import "regenerator-runtime/runtime"`)
109109
// should use required helper instead of inline
110-
expect(code).toMatch(/import _asyncToGenerator from ".*runtime-corejs3\/helpers\/esm\/asyncToGenerator\"/)
110+
expect(code).toMatch(/import _asyncToGenerator from ".*runtime\/helpers\/esm\/asyncToGenerator\"/)
111111
})
112112

113113
test('jsx', () => {
@@ -135,7 +135,8 @@ test('jsx options', () => {
135135
jsx: {
136136
injectH: false
137137
}
138-
}]]
138+
}]],
139+
filename: 'test-entry-file.js'
139140
})
140141
expect(code).not.toMatch(`var h = arguments[0]`)
141142
expect(code).toMatch(`return h("div", ["bar"])`)
@@ -152,6 +153,6 @@ test('disable absoluteRuntime', () => {
152153
filename: 'test-entry-file.js'
153154
})
154155

155-
expect(code).toMatch('import _toConsumableArray from "@babel/runtime-corejs3/helpers/esm/toConsumableArray"')
156+
expect(code).toMatch('import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"')
156157
expect(code).not.toMatch(getAbsolutePolyfill('es.promise'))
157158
})

packages/@vue/babel-preset-app/index.js

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -183,24 +183,25 @@ module.exports = (context, options = {}) => {
183183
absoluteRuntime
184184
}])
185185

186-
// use @babel/runtime-corejs3 so that helpers that need polyfillable APIs will reference core-js instead.
187-
// if useBuiltIns is not set to 'usage', then it means users would take care of the polyfills on their own,
188-
// i.e., core-js 3 is no longer needed.
189-
// this extra plugin can be removed once one of the two issues resolves:
190-
// https://github.com/babel/babel/issues/7597
191-
// https://github.com/babel/babel/issues/9903
192-
if (useBuiltIns === 'usage' && !process.env.VUE_CLI_MODERN_BUILD) {
193-
const runtimeCoreJs3Path = path.dirname(require.resolve('@babel/runtime-corejs3/package.json'))
194-
plugins.push([require('babel-plugin-module-resolver'), {
195-
alias: {
196-
'@babel/runtime': '@babel/runtime-corejs3',
197-
[runtimePath]: runtimeCoreJs3Path
198-
}
199-
}])
200-
}
201-
202186
return {
203-
presets,
204-
plugins
187+
overrides: [{
188+
exclude: [/@babel[\/|\\\\]runtime/, /core-js/],
189+
presets,
190+
plugins
191+
}, {
192+
// there are some untranspiled code in @babel/runtime
193+
// https://github.com/babel/babel/issues/9903
194+
include: [/@babel[\/|\\\\]runtime/],
195+
presets: [
196+
[require('@babel/preset-env'), {
197+
useBuiltIns,
198+
corejs: 3
199+
}]
200+
]
201+
}]
205202
}
206203
}
204+
205+
// a special flag to tell @vue/cli-plugin-babel to include @babel/runtime for transpilation
206+
// otherwise the above `include` option won't take effect
207+
process.env.VUE_CLI_TRANSPILE_BABEL_RUNTIME = true

packages/@vue/babel-preset-app/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,8 @@
3131
"@babel/plugin-transform-runtime": "^7.6.2",
3232
"@babel/preset-env": "^7.6.3",
3333
"@babel/runtime": "^7.6.3",
34-
"@babel/runtime-corejs3": "^7.6.3",
3534
"@vue/babel-preset-jsx": "^1.1.1",
3635
"babel-plugin-dynamic-import-node": "^2.2.0",
37-
"babel-plugin-module-resolver": "^3.2.0",
3836
"core-js": "^3.3.2",
3937
"core-js-compat": "^3.3.2"
4038
},
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
jest.setTimeout(80000)
2+
3+
const { defaultPreset } = require('@vue/cli/lib/options')
4+
const create = require('@vue/cli-test-utils/createTestProject')
5+
const serve = require('@vue/cli-test-utils/serveWithPuppeteer')
6+
7+
test('should add polyfills for code in @babel/runtime', async () => {
8+
const project = await create('babel-runtime-polyfills', defaultPreset)
9+
10+
await project.write('src/main.js', `
11+
const x = function () {
12+
setTimeout(
13+
// eslint-disable-next-line
14+
() => console.log(...arguments), 100
15+
);
16+
}
17+
x(1, 2)
18+
`)
19+
20+
await project.run('vue-cli-service build --mode development')
21+
const vendorFile = await project.read('dist/js/chunk-vendors.js')
22+
23+
// iterableToArray is used to transform `console.log(...arguments)`
24+
expect(vendorFile).toMatch('iterableToArray')
25+
// with inline helpers, preset-env can detect the symbol polyfill is required
26+
// (because the implementation of `iterableToArray` relies on it)
27+
// however, with transform-runtime plugin, helpers are only references to @babel/runtime modules
28+
// so we need to make sure polyfill detection is enabled for @babel/runtime too
29+
expect(vendorFile).toMatch('es.symbol')
30+
})
31+
32+
test('should not transpile babel helpers multiple times', async () => {
33+
const project = await create('babel-runtime-helpers', defaultPreset)
34+
35+
const mainjs = await project.read('src/main.js')
36+
await project.write('src/main.js', `
37+
// eslint-disable-next-line
38+
console.log(typeof Symbol('a'))
39+
40+
${mainjs}
41+
`)
42+
43+
// if the typeof symbol helper is transpiled recursively,
44+
// there would be an error thrown and the page would be empty
45+
await serve(
46+
() => project.run('vue-cli-service serve'),
47+
async ({ helpers }) => {
48+
const msg = `Welcome to Your Vue.js App`
49+
expect(await helpers.getText('h1')).toMatch(msg)
50+
}
51+
)
52+
})
53+
54+
// #4742 core-js-pure imports are likely to be caused by
55+
// incorrect configuration of @babel/plugin-transform-runtime
56+
test('should not introduce polyfills from core-js-pure', async () => {
57+
const project = await create('babel-runtime-core-js-pure', defaultPreset)
58+
59+
await project.write('src/main.js', `
60+
import Vue from 'vue'
61+
import App from './App.vue'
62+
63+
Vue.config.productionTip = false
64+
65+
new Vue({
66+
render: h => h(App),
67+
methods: {
68+
myfunc: async function () {}
69+
}
70+
}).$mount('#app')
71+
`)
72+
73+
await project.run('vue-cli-service build --mode development')
74+
const vendorFile = await project.read('dist/js/chunk-vendors.js')
75+
76+
expect(vendorFile).not.toMatch('core-js-pure')
77+
})

packages/@vue/cli-plugin-babel/index.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const path = require('path')
2+
const babel = require('@babel/core')
23
const { isWindows } = require('@vue/cli-shared-utils')
34

45
function genTranspileDepRegex (transpileDependencies) {
@@ -17,9 +18,14 @@ function genTranspileDepRegex (transpileDependencies) {
1718

1819
module.exports = (api, options) => {
1920
const useThreads = process.env.NODE_ENV === 'production' && !!options.parallel
20-
const cliServicePath = require('path').dirname(require.resolve('@vue/cli-service'))
21+
const cliServicePath = path.dirname(require.resolve('@vue/cli-service'))
2122
const transpileDepRegex = genTranspileDepRegex(options.transpileDependencies)
2223

24+
// try to load the project babel config;
25+
// if the default preset is used,
26+
// there will be a VUE_CLI_TRANSPILE_BABEL_RUNTIME env var set.
27+
babel.loadPartialConfig()
28+
2329
api.chainWebpack(webpackConfig => {
2430
webpackConfig.resolveLoader.modules.prepend(path.join(__dirname, 'node_modules'))
2531

@@ -36,6 +42,15 @@ module.exports = (api, options) => {
3642
if (filepath.startsWith(cliServicePath)) {
3743
return true
3844
}
45+
46+
// only include @babel/runtime when the @vue/babel-preset-app preset is used
47+
if (
48+
process.env.VUE_CLI_TRANSPILE_BABEL_RUNTIME &&
49+
filepath.includes(path.join('@babel', 'runtime'))
50+
) {
51+
return false
52+
}
53+
3954
// check if this is something the user explicitly wants to transpile
4055
if (transpileDepRegex && transpileDepRegex.test(filepath)) {
4156
return false

packages/@vue/cli-service/__tests__/serve.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const { defaultPreset } = require('@vue/cli/lib/options')
66
const create = require('@vue/cli-test-utils/createTestProject')
77
const serve = require('@vue/cli-test-utils/serveWithPuppeteer')
88

9+
const sleep = n => new Promise(resolve => setTimeout(resolve, n))
10+
911
test('serve', async () => {
1012
const project = await create('e2e-serve', defaultPreset)
1113

@@ -53,6 +55,7 @@ test('serve with router', async () => {
5355
expect(await helpers.hasClass('a[href="#/about"]', 'router-link-exact-active')).toBe(false)
5456

5557
await page.click('a[href="#/about"]')
58+
await sleep(1000)
5659
expect(await helpers.getText('h1')).toMatch(`This is an about page`)
5760
expect(await helpers.hasElement('#nav')).toBe(true)
5861
expect(await helpers.hasClass('a[href="#/"]', 'router-link-exact-active')).toBe(false)
@@ -76,6 +79,7 @@ test('serve with legacy router option', async () => {
7679
expect(await helpers.hasClass('a[href="/about"]', 'router-link-exact-active')).toBe(false)
7780

7881
await page.click('a[href="/about"]')
82+
await sleep(1000)
7983
expect(await helpers.getText('h1')).toMatch(`This is an about page`)
8084
expect(await helpers.hasElement('#nav')).toBe(true)
8185
expect(await helpers.hasClass('a[href="/"]', 'router-link-exact-active')).toBe(false)

0 commit comments

Comments
 (0)