From 2eb37c8b9ade943904313a5a206c93ccadff2b43 Mon Sep 17 00:00:00 2001 From: simon3000 Date: Thu, 19 Mar 2020 19:32:26 +0100 Subject: [PATCH 1/6] chore: Update yarn.lock --- yarn.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/yarn.lock b/yarn.lock index 86cbbe74ef..d343e4f6be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5107,7 +5107,7 @@ cheerio@^1.0.0-rc.2: lodash "^4.15.0" parse5 "^3.0.1" -"chokidar@>=2.0.0 <4.0.0": +"chokidar@>=2.0.0 <4.0.0", chokidar@^3.3.0: version "3.3.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== @@ -5122,7 +5122,7 @@ cheerio@^1.0.0-rc.2: optionalDependencies: fsevents "~2.1.2" -chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.3, chokidar@^2.0.4, chokidar@^2.1.8: +chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.3, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -8352,14 +8352,14 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -fork-ts-checker-webpack-plugin@^1.5.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.6.0.tgz#a81fd1c6bf5258fa5318cf3e9a7e9bac006f7917" - integrity sha512-vqOY5gakcoon2s12V7MMe01OPwfgqulUWFzm+geQaPPOBKjW1I7aqqoBVlU0ECn97liMB0ECs16pRdIGe9qdRw== +fork-ts-checker-webpack-plugin@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19" + integrity sha512-DuVkPNrM12jR41KM2e+N+styka0EgLkTnXmNcXdgOM37vtGeY+oCBK/Jx0hzSeEU6memFCtWb4htrHPMDfwwUQ== dependencies: babel-code-frame "^6.22.0" chalk "^2.4.1" - chokidar "^2.0.4" + chokidar "^3.3.0" micromatch "^3.1.10" minimatch "^3.0.4" semver "^5.6.0" From ffff91a27074d9df91e4748eacb63bbbb8cc45d5 Mon Sep 17 00:00:00 2001 From: simon3000 Date: Thu, 19 Mar 2020 21:57:28 +0100 Subject: [PATCH 2/6] feat: support vue.config.cjs --- packages/@vue/cli-service/__tests__/Service.spec.js | 12 ++++++++++-- packages/@vue/cli-service/lib/Service.js | 5 ++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/@vue/cli-service/__tests__/Service.spec.js b/packages/@vue/cli-service/__tests__/Service.spec.js index 7212c0d014..166c6b0a02 100644 --- a/packages/@vue/cli-service/__tests__/Service.spec.js +++ b/packages/@vue/cli-service/__tests__/Service.spec.js @@ -1,5 +1,6 @@ jest.mock('fs') jest.mock('/vue.config.js', () => ({ lintOnSave: false }), { virtual: true }) +jest.mock('/vue.config.cjs', () => ({ lintOnSave: true }), { virtual: true }) jest.mock('vue-cli-plugin-foo', () => () => {}, { virtual: true }) const fs = require('fs') @@ -61,7 +62,7 @@ test('env loading for custom mode', () => { test('loading plugins from package.json', () => { mockPkg({ devDependencies: { - 'bar': '^1.0.0', + bar: '^1.0.0', '@vue/cli-plugin-babel': '^4.2.0', 'vue-cli-plugin-foo': '^1.0.0' } @@ -139,6 +140,13 @@ test('load project options from vue.config.js', () => { expect(service.projectOptions.lintOnSave).toBe(false) }) +test('load project options from vue.config.cjs', () => { + fs.writeFileSync('/vue.config.cjs', '') + const service = createMockService() + fs.unlinkSync('/vue.config.cjs') + expect(service.projectOptions.lintOnSave).toBe(true) +}) + test('load project options from vue.config.js', () => { process.env.VUE_CLI_SERVICE_CONFIG_PATH = `/vue.config.js` fs.writeFileSync('/vue.config.js', '') // only to ensure fs.existsSync returns true @@ -191,7 +199,7 @@ test('api: --skip-plugins', () => { id: 'test-command', apply: api => { api.registerCommand('foo', _args => { - return + }) } }, diff --git a/packages/@vue/cli-service/lib/Service.js b/packages/@vue/cli-service/lib/Service.js index 43e01096a7..32337f99ca 100644 --- a/packages/@vue/cli-service/lib/Service.js +++ b/packages/@vue/cli-service/lib/Service.js @@ -300,10 +300,13 @@ module.exports = class Service { loadUserOptions () { // vue.config.js + // vue.config.cjs let fileConfig, pkgConfig, resolved, resolvedFrom + const jsConfigPath = path.resolve(this.context, 'vue.config.js') + const cjsConfigPath = path.resolve(this.context, 'vue.config.cjs') const configPath = ( process.env.VUE_CLI_SERVICE_CONFIG_PATH || - path.resolve(this.context, 'vue.config.js') + (fs.existsSync(cjsConfigPath) ? cjsConfigPath : jsConfigPath) ) if (fs.existsSync(configPath)) { try { From 0e5fc4a0bb6a6250fb9f12faa264b96c2a3d4fa1 Mon Sep 17 00:00:00 2001 From: simon3000 Date: Mon, 23 Mar 2020 03:47:42 +0100 Subject: [PATCH 3/6] feat(cli-service): import(vue.config.js) with deasync --- packages/@vue/cli-service/lib/Service.js | 26 +++++++++++++++++++- packages/@vue/cli-service/package.json | 1 + yarn.lock | 31 +++++++++++++++++------- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/packages/@vue/cli-service/lib/Service.js b/packages/@vue/cli-service/lib/Service.js index 43e01096a7..a2c4bb039e 100644 --- a/packages/@vue/cli-service/lib/Service.js +++ b/packages/@vue/cli-service/lib/Service.js @@ -7,6 +7,7 @@ const PluginAPI = require('./PluginAPI') const dotenv = require('dotenv') const dotenvExpand = require('dotenv-expand') const defaultsDeep = require('lodash.defaultsdeep') +const deasync = require('deasync') const { chalk, warn, error, isPlugin, resolvePluginId, loadModule, resolvePkg } = require('@vue/cli-shared-utils') const { defaults, validate } = require('./options') @@ -307,7 +308,7 @@ module.exports = class Service { ) if (fs.existsSync(configPath)) { try { - fileConfig = require(configPath) + fileConfig = importConfig(configPath) if (typeof fileConfig === 'function') { fileConfig = fileConfig() @@ -415,3 +416,26 @@ function cloneRuleNames (to, from) { } }) } + +function importConfig (configPath) { + let done = false + let data + let reject = false + import(configPath) + .then(result => { + data = result + done = true + }) + .catch(reason => { + data = reason + done = true + reject = true + }) + deasync.loopWhile(() => { + return !done + }) + if (reject) { + throw new Error(data) + } + return data.default +} diff --git a/packages/@vue/cli-service/package.json b/packages/@vue/cli-service/package.json index 4c656313ec..50aedfd35f 100644 --- a/packages/@vue/cli-service/package.json +++ b/packages/@vue/cli-service/package.json @@ -46,6 +46,7 @@ "css-loader": "^3.4.2", "cssnano": "^4.1.10", "current-script-polyfill": "^1.0.0", + "deasync": "^0.1.19", "debug": "^4.1.1", "default-gateway": "^5.0.5", "dotenv": "^8.2.0", diff --git a/yarn.lock b/yarn.lock index 86cbbe74ef..66c6f4f7ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5107,7 +5107,7 @@ cheerio@^1.0.0-rc.2: lodash "^4.15.0" parse5 "^3.0.1" -"chokidar@>=2.0.0 <4.0.0": +"chokidar@>=2.0.0 <4.0.0", chokidar@^3.3.0: version "3.3.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== @@ -5122,7 +5122,7 @@ cheerio@^1.0.0-rc.2: optionalDependencies: fsevents "~2.1.2" -chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.3, chokidar@^2.0.4, chokidar@^2.1.8: +chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.3, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -6416,6 +6416,14 @@ de-indent@^1.0.2: resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0= +deasync@^0.1.19: + version "0.1.19" + resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.19.tgz#e7ea89fcc9ad483367e8a48fe78f508ca86286e8" + integrity sha512-oh3MRktfnPlLysCPpBpKZZzb4cUC/p0aA3SyRGp15lN30juJBTo/CiD0d4fR+f1kBtUQoJj1NE9RPNWQ7BQ9Mg== + dependencies: + bindings "^1.5.0" + node-addon-api "^1.7.1" + debug@*, debug@4, debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -8352,14 +8360,14 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -fork-ts-checker-webpack-plugin@^1.5.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.6.0.tgz#a81fd1c6bf5258fa5318cf3e9a7e9bac006f7917" - integrity sha512-vqOY5gakcoon2s12V7MMe01OPwfgqulUWFzm+geQaPPOBKjW1I7aqqoBVlU0ECn97liMB0ECs16pRdIGe9qdRw== +fork-ts-checker-webpack-plugin@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19" + integrity sha512-DuVkPNrM12jR41KM2e+N+styka0EgLkTnXmNcXdgOM37vtGeY+oCBK/Jx0hzSeEU6memFCtWb4htrHPMDfwwUQ== dependencies: babel-code-frame "^6.22.0" chalk "^2.4.1" - chokidar "^2.0.4" + chokidar "^3.3.0" micromatch "^3.1.10" minimatch "^3.0.4" semver "^5.6.0" @@ -12714,6 +12722,11 @@ no-case@^2.2.0, no-case@^2.3.2: dependencies: lower-case "^1.1.1" +node-addon-api@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.1.tgz#cf813cd69bb8d9100f6bdca6755fc268f54ac492" + integrity sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ== + node-cache@^4.1.1: version "4.2.1" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-4.2.1.tgz#efd8474dee4edec4138cdded580f5516500f7334" @@ -14613,7 +14626,7 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@1.11.0, puppeteer@^1.11.0: +puppeteer@^1.11.0: version "1.11.0" resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.11.0.tgz#63cdbe12b07275cd6e0b94bce41f3fcb20305770" integrity sha512-iG4iMOHixc2EpzqRV+pv7o3GgmU2dNYEMkvKwSaQO/vMZURakwSOn/EYJ6OIRFYOque1qorzIBvrytPIQB3YzQ== @@ -17851,7 +17864,7 @@ vue-router@^3.1.3, vue-router@^3.1.5: resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.5.tgz#ff29b8a1e1306c526b52d4dc0532109f16c41231" integrity sha512-BszkPvhl7I9h334GjckCh7sVFyjTPMMJFJ4Bsrem/Ik+B/9gt5tgrk8k4gGLO4ZpdvciVdg7O41gW4DisQWurg== -vue-server-renderer@^2.6.10, vue-server-renderer@^2.6.11: +vue-server-renderer@^2.6.10: version "2.6.11" resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.6.11.tgz#be8c9abc6aacc309828a755c021a05fc474b4bc3" integrity sha512-V3faFJHr2KYfdSIalL+JjinZSHYUhlrvJ9pzCIjjwSh77+pkrsXpK4PucdPcng57+N77pd1LrKqwbqjQdktU1A== From a7fe5833bd4edb01d022e56c32c292e04c8daf8a Mon Sep 17 00:00:00 2001 From: simon3000 Date: Mon, 23 Mar 2020 03:51:01 +0100 Subject: [PATCH 4/6] fix: vue.config.js prior than cjs --- packages/@vue/cli-service/lib/Service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@vue/cli-service/lib/Service.js b/packages/@vue/cli-service/lib/Service.js index 32337f99ca..01df35d779 100644 --- a/packages/@vue/cli-service/lib/Service.js +++ b/packages/@vue/cli-service/lib/Service.js @@ -306,7 +306,7 @@ module.exports = class Service { const cjsConfigPath = path.resolve(this.context, 'vue.config.cjs') const configPath = ( process.env.VUE_CLI_SERVICE_CONFIG_PATH || - (fs.existsSync(cjsConfigPath) ? cjsConfigPath : jsConfigPath) + (fs.existsSync(jsConfigPath) ? jsConfigPath : cjsConfigPath) ) if (fs.existsSync(configPath)) { try { From fabc2e0b057a98b8611317d96e15f7b0964a4f4c Mon Sep 17 00:00:00 2001 From: simon3000 Date: Tue, 24 Mar 2020 12:08:54 +0100 Subject: [PATCH 5/6] chore: merge upstream --- .../cli-service/__tests__/Service.spec.js | 9 ---- .../cli-service/__tests__/ServiceESM.spec.js | 33 ++++++++++++ .../__tests__/mockESM/package.json | 6 +++ packages/@vue/cli-service/lib/Service.js | 51 ++++++++++++++----- 4 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 packages/@vue/cli-service/__tests__/ServiceESM.spec.js create mode 100644 packages/@vue/cli-service/__tests__/mockESM/package.json diff --git a/packages/@vue/cli-service/__tests__/Service.spec.js b/packages/@vue/cli-service/__tests__/Service.spec.js index 3f15b7fc9a..cf01610159 100644 --- a/packages/@vue/cli-service/__tests__/Service.spec.js +++ b/packages/@vue/cli-service/__tests__/Service.spec.js @@ -135,15 +135,6 @@ test('load project options from vue.config.js', () => { expect(service.projectOptions.lintOnSave).toBe(false) }) - -test('load project options from vue.config.cjs', () => { - fs.writeFileSync('/vue.config.cjs', '') - const service = createMockService() - fs.unlinkSync('/vue.config.cjs') - expect(service.projectOptions.lintOnSave).toBe(true) -}) - - test('load project options from vue.config.js as a function', () => { jest.mock('/vue.config.js', () => function () { return { lintOnSave: false } }, { virtual: true }) mockPkg({ diff --git a/packages/@vue/cli-service/__tests__/ServiceESM.spec.js b/packages/@vue/cli-service/__tests__/ServiceESM.spec.js new file mode 100644 index 0000000000..88957c63c4 --- /dev/null +++ b/packages/@vue/cli-service/__tests__/ServiceESM.spec.js @@ -0,0 +1,33 @@ +const { join } = require('path') +const Service = require('../lib/Service') + +const mockDir = join(__dirname, 'mockESM') +const configPath = join(mockDir, 'vue.config.cjs') + +const createService = () => { + const service = new Service(mockDir, { + plugins: [], + useBuiltIn: false + }) + service.init() + return service +} + +// vue.config.cjs has higher priority + +test('load project options from package.json', async () => { + const service = createService() + expect(service.projectOptions.lintOnSave).toBe('default') +}) + +test('load project options from vue.config.cjs', async () => { + jest.mock(configPath, () => ({ lintOnSave: true }), { virtual: true }) + const service = createService() + expect(service.projectOptions.lintOnSave).toBe(true) +}) + +test('load project options from vue.config.cjs as a function', async () => { + jest.mock(configPath, () => function () { return { lintOnSave: true } }, { virtual: true }) + const service = createService() + expect(service.projectOptions.lintOnSave).toBe(true) +}) diff --git a/packages/@vue/cli-service/__tests__/mockESM/package.json b/packages/@vue/cli-service/__tests__/mockESM/package.json new file mode 100644 index 0000000000..bdd0885ceb --- /dev/null +++ b/packages/@vue/cli-service/__tests__/mockESM/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "vue": { + "lintOnSave": "default" + } +} diff --git a/packages/@vue/cli-service/lib/Service.js b/packages/@vue/cli-service/lib/Service.js index a2db329b7a..21db7ffaee 100644 --- a/packages/@vue/cli-service/lib/Service.js +++ b/packages/@vue/cli-service/lib/Service.js @@ -10,6 +10,22 @@ const { chalk, warn, error, isPlugin, resolvePluginId, loadModule, resolvePkg } const { defaults, validate } = require('./options') +const loadConfig = configPath => { + let fileConfig = require(configPath) + + if (typeof fileConfig === 'function') { + fileConfig = fileConfig() + } + + if (!fileConfig || typeof fileConfig !== 'object') { + error( + `Error loading ${chalk.bold('vue.config.js')}: should export an object or a function that returns object.` + ) + fileConfig = null + } + return fileConfig +} + module.exports = class Service { constructor (context, { plugins, pkg, inlineOptions, useBuiltIn } = {}) { process.VUE_CLI_SERVICE = this @@ -301,32 +317,41 @@ module.exports = class Service { // vue.config.js // vue.config.cjs let fileConfig, pkgConfig, resolved, resolvedFrom + const esm = this.pkg.type && this.pkg.type === 'module' const jsConfigPath = path.resolve(this.context, 'vue.config.js') const cjsConfigPath = path.resolve(this.context, 'vue.config.cjs') const configPath = ( process.env.VUE_CLI_SERVICE_CONFIG_PATH || - (fs.existsSync(jsConfigPath) ? jsConfigPath : cjsConfigPath) + jsConfigPath ) - try { - fileConfig = require(configPath) - - if (typeof fileConfig === 'function') { - fileConfig = fileConfig() - } - if (!fileConfig || typeof fileConfig !== 'object') { - error( - `Error loading ${chalk.bold('vue.config.js')}: should export an object or a function that returns object.` - ) - fileConfig = null - } + try { + fileConfig = loadConfig(configPath) } catch (e) { if (e.code !== 'MODULE_NOT_FOUND') { + if (e.code === 'ERR_REQUIRE_ESM') { + warn(`Rename ${chalk.bold('vue.config.js')} to ${chalk.bold('vue.config.cjs')} when ECMAScript modules is enabled`) + } error(`Error loading ${chalk.bold('vue.config.js')}:`) throw e } } + // vue.config.js not found, esm enabled, no env set + if (!fileConfig && esm && !process.env.VUE_CLI_SERVICE_CONFIG_PATH) { + try { + fileConfig = loadConfig(cjsConfigPath) + } catch (e) { + if (e.code !== 'MODULE_NOT_FOUND') { + error(`Error loading ${chalk.bold('vue.config.cjs')}:`) + throw e + } + } + if (fileConfig) { + warn(`ECMAScript modules is detected, config loaded from ${chalk.bold('vue.config.cjs')}`) + } + } + // package.vue pkgConfig = this.pkg.vue if (pkgConfig && typeof pkgConfig !== 'object') { From bedebfe0b270c86a338772923e57f13d60eb5055 Mon Sep 17 00:00:00 2001 From: simon3000 Date: Tue, 24 Mar 2020 13:10:26 +0100 Subject: [PATCH 6/6] feat: use import(vue.config.js) --- .../cli-service/__tests__/ServiceESM.spec.js | 33 -------------- .../__tests__/e2eServiceESM.spec.js | 43 +++++++++++++++++++ .../__tests__/mockESM/readConfig.cjs | 9 ++++ packages/@vue/cli-service/lib/Service.js | 43 +++++++------------ 4 files changed, 67 insertions(+), 61 deletions(-) delete mode 100644 packages/@vue/cli-service/__tests__/ServiceESM.spec.js create mode 100644 packages/@vue/cli-service/__tests__/e2eServiceESM.spec.js create mode 100644 packages/@vue/cli-service/__tests__/mockESM/readConfig.cjs diff --git a/packages/@vue/cli-service/__tests__/ServiceESM.spec.js b/packages/@vue/cli-service/__tests__/ServiceESM.spec.js deleted file mode 100644 index 88957c63c4..0000000000 --- a/packages/@vue/cli-service/__tests__/ServiceESM.spec.js +++ /dev/null @@ -1,33 +0,0 @@ -const { join } = require('path') -const Service = require('../lib/Service') - -const mockDir = join(__dirname, 'mockESM') -const configPath = join(mockDir, 'vue.config.cjs') - -const createService = () => { - const service = new Service(mockDir, { - plugins: [], - useBuiltIn: false - }) - service.init() - return service -} - -// vue.config.cjs has higher priority - -test('load project options from package.json', async () => { - const service = createService() - expect(service.projectOptions.lintOnSave).toBe('default') -}) - -test('load project options from vue.config.cjs', async () => { - jest.mock(configPath, () => ({ lintOnSave: true }), { virtual: true }) - const service = createService() - expect(service.projectOptions.lintOnSave).toBe(true) -}) - -test('load project options from vue.config.cjs as a function', async () => { - jest.mock(configPath, () => function () { return { lintOnSave: true } }, { virtual: true }) - const service = createService() - expect(service.projectOptions.lintOnSave).toBe(true) -}) diff --git a/packages/@vue/cli-service/__tests__/e2eServiceESM.spec.js b/packages/@vue/cli-service/__tests__/e2eServiceESM.spec.js new file mode 100644 index 0000000000..75013fb161 --- /dev/null +++ b/packages/@vue/cli-service/__tests__/e2eServiceESM.spec.js @@ -0,0 +1,43 @@ +// Native support for ES Modules is not complete with jest +// https://github.com/facebook/jest/issues/9430 + +const { writeFile, unlink } = require('fs').promises +const { join } = require('path') +const { fork } = require('child_process') + +const mockDir = join(__dirname, 'mockESM') +const configPath = join(mockDir, 'vue.config.js') +const e2ePath = join(mockDir, 'readConfig.cjs') + +const configData = 'export default { lintOnSave: true }' +const functionConfigData = 'export default () => ({ lintOnSave: true })' + +const e2e = () => new Promise(resolve => { + const subprocess = fork(e2ePath, { stdio: 'pipe' }) + let output = '' + subprocess.stdout.on('data', data => { + output += data + }) + subprocess.stdout.on('end', () => resolve(JSON.parse(output))) +}) + +// vue.config.cjs has higher priority + +test('load project options from package.json', async () => { + const output = await e2e() + expect(output).toBe('default') +}) + +test('load project options with import(vue.config.js)', async () => { + await writeFile(configPath, configData) + const output = await e2e() + await unlink(configPath) + expect(output).toBe(true) +}) + +test('load project options with import(vue.config.js) as a function', async () => { + await writeFile(configPath, functionConfigData) + const output = await e2e() + await unlink(configPath) + expect(output).toBe(true) +}) diff --git a/packages/@vue/cli-service/__tests__/mockESM/readConfig.cjs b/packages/@vue/cli-service/__tests__/mockESM/readConfig.cjs new file mode 100644 index 0000000000..9ebb648f09 --- /dev/null +++ b/packages/@vue/cli-service/__tests__/mockESM/readConfig.cjs @@ -0,0 +1,9 @@ +const Service = require('../../lib/Service') + +const service = new Service(__dirname, { + plugins: [], + useBuiltIn: false +}) +service.init() + +process.stdout.write(JSON.stringify(service.projectOptions.lintOnSave)) diff --git a/packages/@vue/cli-service/lib/Service.js b/packages/@vue/cli-service/lib/Service.js index c18a3ee410..e4d07bcb10 100644 --- a/packages/@vue/cli-service/lib/Service.js +++ b/packages/@vue/cli-service/lib/Service.js @@ -6,13 +6,15 @@ const PluginAPI = require('./PluginAPI') const dotenv = require('dotenv') const dotenvExpand = require('dotenv-expand') const defaultsDeep = require('lodash.defaultsdeep') -const deasync = require('deasync') const { chalk, warn, error, isPlugin, resolvePluginId, loadModule, resolvePkg } = require('@vue/cli-shared-utils') const { defaults, validate } = require('./options') -const loadConfig = configPath => { - let fileConfig = require(configPath) +const loadConfig = (configPath, esm) => { + if (esm) { + warn(`ECMAScript modules is enabled, config will be loaded with ${chalk.bold('import()')}`) + } + let fileConfig = esm ? importConfig(configPath) : require(configPath) if (typeof fileConfig === 'function') { fileConfig = fileConfig() @@ -316,43 +318,23 @@ module.exports = class Service { loadUserOptions () { // vue.config.js - // vue.config.cjs let fileConfig, pkgConfig, resolved, resolvedFrom const esm = this.pkg.type && this.pkg.type === 'module' const jsConfigPath = path.resolve(this.context, 'vue.config.js') - const cjsConfigPath = path.resolve(this.context, 'vue.config.cjs') const configPath = ( process.env.VUE_CLI_SERVICE_CONFIG_PATH || jsConfigPath ) try { - fileConfig = loadConfig(configPath) + fileConfig = loadConfig(configPath, esm) } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') { - if (e.code === 'ERR_REQUIRE_ESM') { - warn(`Rename ${chalk.bold('vue.config.js')} to ${chalk.bold('vue.config.cjs')} when ECMAScript modules is enabled`) - } + if (e.code !== 'MODULE_NOT_FOUND' && e.code !== 'ERR_MODULE_NOT_FOUND') { error(`Error loading ${chalk.bold('vue.config.js')}:`) throw e } } - // vue.config.js not found, esm enabled, no env set - if (!fileConfig && esm && !process.env.VUE_CLI_SERVICE_CONFIG_PATH) { - try { - fileConfig = loadConfig(cjsConfigPath) - } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') { - error(`Error loading ${chalk.bold('vue.config.cjs')}:`) - throw e - } - } - if (fileConfig) { - warn(`ECMAScript modules is detected, config loaded from ${chalk.bold('vue.config.cjs')}`) - } - } - // package.vue pkgConfig = this.pkg.vue if (pkgConfig && typeof pkgConfig !== 'object') { @@ -445,12 +427,13 @@ function cloneRuleNames (to, from) { } function importConfig (configPath) { + const deasync = require('deasync') let done = false let data let reject = false import(configPath) .then(result => { - data = result + data = result && result.default done = true }) .catch(reason => { @@ -462,7 +445,11 @@ function importConfig (configPath) { return !done }) if (reject) { - throw new Error(data) + if (data instanceof Error) { + throw data + } else { + throw data + } } - return data.default + return data }