Skip to content

Commit

Permalink
refactor: use babel overrides to transpile babel runtime helpers (vue…
Browse files Browse the repository at this point in the history
…js#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 vuejs#3928.
Seems to have fixed vuejs#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
  • Loading branch information
haoqunjiang authored Nov 4, 2019
1 parent 0a5c79b commit 759d77f
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 24 deletions.
7 changes: 4 additions & 3 deletions packages/@vue/babel-preset-app/__tests__/babel-preset.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ test('async/await', () => {
// should use regenerator runtime
expect(code).toMatch(`import "regenerator-runtime/runtime"`)
// should use required helper instead of inline
expect(code).toMatch(/import _asyncToGenerator from ".*runtime-corejs3\/helpers\/esm\/asyncToGenerator\"/)
expect(code).toMatch(/import _asyncToGenerator from ".*runtime\/helpers\/esm\/asyncToGenerator\"/)
})

test('jsx', () => {
Expand Down Expand Up @@ -135,7 +135,8 @@ test('jsx options', () => {
jsx: {
injectH: false
}
}]]
}]],
filename: 'test-entry-file.js'
})
expect(code).not.toMatch(`var h = arguments[0]`)
expect(code).toMatch(`return h("div", ["bar"])`)
Expand All @@ -152,6 +153,6 @@ test('disable absoluteRuntime', () => {
filename: 'test-entry-file.js'
})

expect(code).toMatch('import _toConsumableArray from "@babel/runtime-corejs3/helpers/esm/toConsumableArray"')
expect(code).toMatch('import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"')
expect(code).not.toMatch(getAbsolutePolyfill('es.promise'))
})
37 changes: 19 additions & 18 deletions packages/@vue/babel-preset-app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,24 +183,25 @@ module.exports = (context, options = {}) => {
absoluteRuntime
}])

// use @babel/runtime-corejs3 so that helpers that need polyfillable APIs will reference core-js instead.
// if useBuiltIns is not set to 'usage', then it means users would take care of the polyfills on their own,
// i.e., core-js 3 is no longer needed.
// this extra plugin can be removed once one of the two issues resolves:
// https://github.com/babel/babel/issues/7597
// https://github.com/babel/babel/issues/9903
if (useBuiltIns === 'usage' && !process.env.VUE_CLI_MODERN_BUILD) {
const runtimeCoreJs3Path = path.dirname(require.resolve('@babel/runtime-corejs3/package.json'))
plugins.push([require('babel-plugin-module-resolver'), {
alias: {
'@babel/runtime': '@babel/runtime-corejs3',
[runtimePath]: runtimeCoreJs3Path
}
}])
}

return {
presets,
plugins
overrides: [{
exclude: [/@babel[\/|\\\\]runtime/, /core-js/],
presets,
plugins
}, {
// there are some untranspiled code in @babel/runtime
// https://github.com/babel/babel/issues/9903
include: [/@babel[\/|\\\\]runtime/],
presets: [
[require('@babel/preset-env'), {
useBuiltIns,
corejs: 3
}]
]
}]
}
}

// a special flag to tell @vue/cli-plugin-babel to include @babel/runtime for transpilation
// otherwise the above `include` option won't take effect
process.env.VUE_CLI_TRANSPILE_BABEL_RUNTIME = true
2 changes: 0 additions & 2 deletions packages/@vue/babel-preset-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/preset-env": "^7.6.3",
"@babel/runtime": "^7.6.3",
"@babel/runtime-corejs3": "^7.6.3",
"@vue/babel-preset-jsx": "^1.1.1",
"babel-plugin-dynamic-import-node": "^2.2.0",
"babel-plugin-module-resolver": "^3.2.0",
"core-js": "^3.3.2",
"core-js-compat": "^3.3.2"
},
Expand Down
77 changes: 77 additions & 0 deletions packages/@vue/cli-plugin-babel/__tests__/babel-runtime.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
jest.setTimeout(80000)

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)

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')

// 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)

const mainjs = await project.read('src/main.js')
await project.write('src/main.js', `
// eslint-disable-next-line
console.log(typeof Symbol('a'))
${mainjs}
`)

// if the typeof symbol helper is transpiled recursively,
// there would be an error thrown and the page would be empty
await serve(
() => project.run('vue-cli-service serve'),
async ({ helpers }) => {
const msg = `Welcome to Your Vue.js App`
expect(await helpers.getText('h1')).toMatch(msg)
}
)
})

// #4742 core-js-pure imports are likely to be caused by
// incorrect configuration of @babel/plugin-transform-runtime
test('should not introduce polyfills from core-js-pure', async () => {
const project = await create('babel-runtime-core-js-pure', defaultPreset)

await project.write('src/main.js', `
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
methods: {
myfunc: async function () {}
}
}).$mount('#app')
`)

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

expect(vendorFile).not.toMatch('core-js-pure')
})
17 changes: 16 additions & 1 deletion packages/@vue/cli-plugin-babel/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const path = require('path')
const babel = require('@babel/core')
const { isWindows } = require('@vue/cli-shared-utils')

function genTranspileDepRegex (transpileDependencies) {
Expand All @@ -17,9 +18,14 @@ function genTranspileDepRegex (transpileDependencies) {

module.exports = (api, options) => {
const useThreads = process.env.NODE_ENV === 'production' && !!options.parallel
const cliServicePath = require('path').dirname(require.resolve('@vue/cli-service'))
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,
// there will be a VUE_CLI_TRANSPILE_BABEL_RUNTIME env var set.
babel.loadPartialConfig()

api.chainWebpack(webpackConfig => {
webpackConfig.resolveLoader.modules.prepend(path.join(__dirname, 'node_modules'))

Expand All @@ -36,6 +42,15 @@ module.exports = (api, options) => {
if (filepath.startsWith(cliServicePath)) {
return true
}

// 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
}

// check if this is something the user explicitly wants to transpile
if (transpileDepRegex && transpileDepRegex.test(filepath)) {
return false
Expand Down
4 changes: 4 additions & 0 deletions packages/@vue/cli-service/__tests__/serve.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const { defaultPreset } = require('@vue/cli/lib/options')
const create = require('@vue/cli-test-utils/createTestProject')
const serve = require('@vue/cli-test-utils/serveWithPuppeteer')

const sleep = n => new Promise(resolve => setTimeout(resolve, n))

test('serve', async () => {
const project = await create('e2e-serve', defaultPreset)

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

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

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

0 comments on commit 759d77f

Please sign in to comment.