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__/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 434c6b5e81..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 @@ -299,31 +315,43 @@ 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 || - 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) } 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') { diff --git a/yarn.lock b/yarn.lock index 92f2ee79d7..2fbe678840 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== @@ -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"