Skip to content

Commit 01d4bea

Browse files
authored
fix: should throw errors if there is bad require() in vue.config.js (#5500)
reverts #5305 this makes the tests a little more tedious, need to find a better way to test these functionalities fixes #5442
1 parent 02a365d commit 01d4bea

File tree

5 files changed

+70
-57
lines changed

5 files changed

+70
-57
lines changed

Diff for: packages/@vue/cli-service/__tests__/Service.spec.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ beforeEach(() => {
2929
delete process.env.BAZ
3030
})
3131

32+
afterEach(() => {
33+
if (fs.existsSync('/vue.config.js')) {
34+
fs.unlinkSync('/vue.config.js')
35+
}
36+
})
37+
3238
test('env loading', () => {
3339
process.env.FOO = 0
3440
fs.writeFileSync('/.env.local', `FOO=1\nBAR=2`)
@@ -124,6 +130,7 @@ test('keep publicPath when empty', () => {
124130
})
125131

126132
test('load project options from vue.config.js', () => {
133+
fs.writeFileSync(path.resolve('/', 'vue.config.js'), '') // only to ensure fs.existsSync returns true
127134
jest.mock(path.resolve('/', 'vue.config.js'), () => ({ lintOnSave: false }), { virtual: true })
128135
mockPkg({
129136
vue: {
@@ -136,7 +143,8 @@ test('load project options from vue.config.js', () => {
136143
})
137144

138145
test('load project options from vue.config.js as a function', () => {
139-
jest.mock('/vue.config.js', () => function () { return { lintOnSave: false } }, { virtual: true })
146+
fs.writeFileSync(path.resolve('/', 'vue.config.js'), '')
147+
jest.mock(path.resolve('/', 'vue.config.js'), () => function () { return { lintOnSave: false } }, { virtual: true })
140148
mockPkg({
141149
vue: {
142150
lintOnSave: 'default'

Diff for: packages/@vue/cli-service/__tests__/ServiceESM.spec.js

+23-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
1-
const { join } = require('path')
21
const Service = require('../lib/Service')
32

4-
const mockDir = join(__dirname, 'mockESM')
5-
const configPath = join(mockDir, 'vue.config.cjs')
3+
const path = require('path')
4+
const configPath = path.resolve('/', 'vue.config.cjs')
5+
6+
jest.mock('fs')
7+
const fs = require('fs')
8+
9+
beforeEach(() => {
10+
fs.writeFileSync(path.resolve('/', 'package.json'), JSON.stringify({
11+
type: 'module',
12+
vue: {
13+
lintOnSave: 'default'
14+
}
15+
}, null, 2))
16+
})
17+
18+
afterEach(() => {
19+
if (fs.existsSync(configPath)) {
20+
fs.unlinkSync(configPath)
21+
}
22+
})
623

724
const createService = () => {
8-
const service = new Service(mockDir, {
25+
const service = new Service('/', {
926
plugins: [],
1027
useBuiltIn: false
1128
})
@@ -21,12 +38,14 @@ test('load project options from package.json', async () => {
2138
})
2239

2340
test('load project options from vue.config.cjs', async () => {
41+
fs.writeFileSync(configPath, '')
2442
jest.mock(configPath, () => ({ lintOnSave: true }), { virtual: true })
2543
const service = createService()
2644
expect(service.projectOptions.lintOnSave).toBe(true)
2745
})
2846

2947
test('load project options from vue.config.cjs as a function', async () => {
48+
fs.writeFileSync(configPath, '')
3049
jest.mock(configPath, () => function () { return { lintOnSave: true } }, { virtual: true })
3150
const service = createService()
3251
expect(service.projectOptions.lintOnSave).toBe(true)

Diff for: packages/@vue/cli-service/__tests__/mockESM/package.json

-6
This file was deleted.

Diff for: packages/@vue/cli-service/lib/Service.js

+34-44
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const fs = require('fs')
12
const path = require('path')
23
const debug = require('debug')
34
const merge = require('webpack-merge')
@@ -10,22 +11,6 @@ const { chalk, warn, error, isPlugin, resolvePluginId, loadModule, resolvePkg }
1011

1112
const { defaults, validate } = require('./options')
1213

13-
const loadConfig = configPath => {
14-
let fileConfig = require(configPath)
15-
16-
if (typeof fileConfig === 'function') {
17-
fileConfig = fileConfig()
18-
}
19-
20-
if (!fileConfig || typeof fileConfig !== 'object') {
21-
error(
22-
`Error loading ${chalk.bold('vue.config.js')}: should export an object or a function that returns object.`
23-
)
24-
fileConfig = null
25-
}
26-
return fileConfig
27-
}
28-
2914
module.exports = class Service {
3015
constructor (context, { plugins, pkg, inlineOptions, useBuiltIn } = {}) {
3116
process.VUE_CLI_SERVICE = this
@@ -314,41 +299,46 @@ module.exports = class Service {
314299
}
315300

316301
loadUserOptions () {
317-
// vue.config.js
318-
// vue.config.cjs
302+
// vue.config.c?js
319303
let fileConfig, pkgConfig, resolved, resolvedFrom
320304
const esm = this.pkg.type && this.pkg.type === 'module'
321-
const jsConfigPath = path.resolve(this.context, 'vue.config.js')
322-
const cjsConfigPath = path.resolve(this.context, 'vue.config.cjs')
323-
const configPath = (
324-
process.env.VUE_CLI_SERVICE_CONFIG_PATH ||
325-
jsConfigPath
326-
)
327-
328-
try {
329-
fileConfig = loadConfig(configPath)
330-
} catch (e) {
331-
if (e.code !== 'MODULE_NOT_FOUND') {
332-
if (e.code === 'ERR_REQUIRE_ESM') {
333-
warn(`Rename ${chalk.bold('vue.config.js')} to ${chalk.bold('vue.config.cjs')} when ECMAScript modules is enabled`)
334-
}
335-
error(`Error loading ${chalk.bold('vue.config.js')}:`)
336-
throw e
305+
306+
const possibleConfigPaths = [
307+
process.env.VUE_CLI_SERVICE_CONFIG_PATH,
308+
'./vue.config.js',
309+
'./vue.config.cjs'
310+
]
311+
312+
let fileConfigPath
313+
for (const p of possibleConfigPaths) {
314+
const resolvedPath = p && path.resolve(this.context, p)
315+
if (resolvedPath && fs.existsSync(resolvedPath)) {
316+
fileConfigPath = resolvedPath
337317
}
338318
}
339319

340-
// vue.config.js not found, esm enabled, no env set
341-
if (!fileConfig && esm && !process.env.VUE_CLI_SERVICE_CONFIG_PATH) {
320+
if (fileConfigPath) {
321+
if (esm && fileConfigPath === './vue.config.js') {
322+
throw new Error(`Please rename ${chalk.bold('vue.config.js')} to ${chalk.bold('vue.config.cjs')} when ECMAScript modules is enabled`)
323+
}
324+
342325
try {
343-
fileConfig = loadConfig(cjsConfigPath)
344-
} catch (e) {
345-
if (e.code !== 'MODULE_NOT_FOUND') {
346-
error(`Error loading ${chalk.bold('vue.config.cjs')}:`)
347-
throw e
326+
fileConfig = loadModule(fileConfigPath, this.context)
327+
328+
if (typeof fileConfig === 'function') {
329+
fileConfig = fileConfig()
348330
}
349-
}
350-
if (fileConfig) {
351-
warn(`ECMAScript modules is detected, config loaded from ${chalk.bold('vue.config.cjs')}`)
331+
332+
if (!fileConfig || typeof fileConfig !== 'object') {
333+
// TODO: show throw an Error here, to be fixed in v5
334+
error(
335+
`Error loading ${chalk.bold(fileConfigPath)}: should export an object or a function that returns object.`
336+
)
337+
fileConfig = null
338+
}
339+
} catch (e) {
340+
error(`Error loading ${chalk.bold(fileConfigPath)}:`)
341+
throw e
352342
}
353343
}
354344

Diff for: packages/@vue/cli-shared-utils/lib/module.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@ exports.resolveModule = function (request, context) {
6161
}
6262

6363
exports.loadModule = function (request, context, force = false) {
64-
// createRequire doesn't work with jest mock modules (which we used in migrator, for inquirer)
65-
if (process.env.VUE_CLI_TEST && request.endsWith('migrator')) {
64+
// createRequire doesn't work with jest mock modules
65+
// (which we used in migrator for inquirer, and in tests for cli-service)
66+
// TODO: it's supported in Jest 25
67+
if (process.env.VUE_CLI_TEST && (request.endsWith('migrator') || context === '/')) {
6668
return require(request)
6769
}
6870

0 commit comments

Comments
 (0)