diff --git a/packages/@vue/cli-service/__tests__/Service.spec.js b/packages/@vue/cli-service/__tests__/Service.spec.js index 4f1b242302..cf01610159 100644 --- a/packages/@vue/cli-service/__tests__/Service.spec.js +++ b/packages/@vue/cli-service/__tests__/Service.spec.js @@ -60,7 +60,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' } @@ -183,7 +183,7 @@ test('api: --skip-plugins', () => { id: 'test-command', apply: api => { api.registerCommand('foo', _args => { - return + }) } }, 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/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/__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 434c6b5e81..e4d07bcb10 100644 --- a/packages/@vue/cli-service/lib/Service.js +++ b/packages/@vue/cli-service/lib/Service.js @@ -10,6 +10,25 @@ const { chalk, warn, error, isPlugin, resolvePluginId, loadModule, resolvePkg } const { defaults, validate } = require('./options') +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() + } + + 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 @@ -300,25 +319,17 @@ module.exports = class Service { loadUserOptions () { // vue.config.js let fileConfig, pkgConfig, resolved, resolvedFrom + const esm = this.pkg.type && this.pkg.type === 'module' + const jsConfigPath = path.resolve(this.context, 'vue.config.js') const configPath = ( process.env.VUE_CLI_SERVICE_CONFIG_PATH || - path.resolve(this.context, 'vue.config.js') + 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, esm) } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') { + if (e.code !== 'MODULE_NOT_FOUND' && e.code !== 'ERR_MODULE_NOT_FOUND') { error(`Error loading ${chalk.bold('vue.config.js')}:`) throw e } @@ -414,3 +425,31 @@ 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 && result.default + done = true + }) + .catch(reason => { + data = reason + done = true + reject = true + }) + deasync.loopWhile(() => { + return !done + }) + if (reject) { + if (data instanceof Error) { + throw data + } else { + throw data + } + } + return data +} diff --git a/packages/@vue/cli-service/package.json b/packages/@vue/cli-service/package.json index 6706e174bb..cf906981f9 100644 --- a/packages/@vue/cli-service/package.json +++ b/packages/@vue/cli-service/package.json @@ -46,6 +46,7 @@ "copy-webpack-plugin": "^5.1.1", "css-loader": "^3.4.2", "cssnano": "^4.1.10", + "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 92f2ee79d7..532806a235 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5112,7 +5112,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== @@ -5127,7 +5127,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==
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: