Skip to content

Commit ba4d0f2

Browse files
committed
refactor(generator): ensure proper deep merge of array values in api.extendPackage
Also fixes #970 by injecting eslint config when invoking ts plugin in an existing project with eslint plugin installed.
1 parent d6d2af5 commit ba4d0f2

File tree

4 files changed

+40
-18
lines changed

4 files changed

+40
-18
lines changed

packages/@vue/cli-plugin-typescript/generator/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ module.exports = (api, {
7373
})
7474
}
7575

76+
const hasESLint = api.hasPlugin('eslint')
77+
if (hasESLint) {
78+
api.extendPackage({
79+
devDependencies: {
80+
'@vue/eslint-config-typescript': '^3.0.0-rc.5'
81+
},
82+
eslintConfig: {
83+
extends: ['@vue/typescript']
84+
}
85+
})
86+
}
87+
7688
api.render('./template', {
7789
isTest: process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG,
7890
hasMocha,

packages/@vue/cli/__tests__/invoke.spec.js

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ test('invoke with prompts', async () => {
7676
await assertUpdates(project)
7777
})
7878

79-
test('invoke with existing files', async () => {
79+
test('invoke with ts', async () => {
8080
const project = await create(`invoke-existing`, {
8181
useConfigFiles: true,
8282
plugins: {
@@ -86,7 +86,7 @@ test('invoke with existing files', async () => {
8686
})
8787
// mock install
8888
const pkg = JSON.parse(await project.read('package.json'))
89-
pkg.devDependencies['@vue/cli-plugin-eslint'] = '*'
89+
pkg.devDependencies['@vue/cli-plugin-typescript'] = '*'
9090
await project.write('package.json', JSON.stringify(pkg, null, 2))
9191

9292
// mock existing vue.config.js
@@ -97,25 +97,22 @@ test('invoke with existing files', async () => {
9797
extends: ['plugin:vue/essential', 'eslint:recommended']
9898
}))
9999

100-
await project.run(`${require.resolve('../bin/vue')} invoke eslint --config airbnb --lintOn commit`)
100+
await project.run(`${require.resolve('../bin/vue')} invoke typescript --classComponent --useTsWithBabel`)
101101

102-
await assertUpdates(project)
103-
const updatedVueConfig = await project.read('vue.config.js')
104-
expect(updatedVueConfig).toMatch(`module.exports = { lintOnSave: false }`)
102+
const updatedESLintrc = parseJS(await project.read('.eslintrc.js'))
103+
expect(updatedESLintrc).toEqual(Object.assign({}, baseESLintConfig, {
104+
extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/typescript']
105+
}))
105106
})
106107

107108
test('invoke with existing files (yaml)', async () => {
108109
const project = await create(`invoke-existing-yaml`, {
109110
useConfigFiles: true,
110111
plugins: {
111112
'@vue/cli-plugin-babel': {},
112-
'@vue/cli-plugin-eslint': { config: 'base' }
113+
'@vue/cli-plugin-eslint': {}
113114
}
114115
})
115-
// mock install
116-
const pkg = JSON.parse(await project.read('package.json'))
117-
pkg.devDependencies['@vue/cli-plugin-eslint'] = '*'
118-
await project.write('package.json', JSON.stringify(pkg, null, 2))
119116

120117
const eslintrc = parseJS(await project.read('.eslintrc.js'))
121118
expect(eslintrc).toEqual(Object.assign({}, baseESLintConfig, {
@@ -137,6 +134,7 @@ extends:
137134
extends:
138135
- 'plugin:vue/essential'
139136
- 'eslint:recommended'
137+
- '@vue/airbnb'
140138
`.trim())
141139
})
142140

packages/@vue/cli/lib/GeneratorAPI.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const ConfigTransform = require('./ConfigTransform')
1414
const isString = val => typeof val === 'string'
1515
const isFunction = val => typeof val === 'function'
1616
const isObject = val => val && typeof val === 'object'
17+
const mergeArrayWithDedupe = (a, b) => Array.from(new Set([...a, ...b]))
1718

1819
class GeneratorAPI {
1920
/**
@@ -140,9 +141,9 @@ class GeneratorAPI {
140141
} else if (!(key in pkg)) {
141142
pkg[key] = value
142143
} else if (Array.isArray(value) && Array.isArray(existing)) {
143-
pkg[key] = existing.concat(value)
144+
pkg[key] = mergeArrayWithDedupe(existing, value)
144145
} else if (isObject(value) && isObject(existing)) {
145-
pkg[key] = merge(existing, value)
146+
pkg[key] = merge(existing, value, { arrayMerge: mergeArrayWithDedupe })
146147
} else {
147148
pkg[key] = value
148149
}

packages/@vue/cli/lib/util/configTransforms.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ const stringifyJS = require('./stringifyJS')
33
const { loadModule } = require('@vue/cli-shared-utils')
44
const merge = require('deepmerge')
55

6+
const mergeArrayWithDedupe = (a, b) => Array.from(new Set([...a, ...b]))
7+
const mergeOptions = {
8+
arrayMerge: mergeArrayWithDedupe
9+
}
10+
611
const isObject = val => val && typeof val === 'object'
712

813
const transformJS = {
@@ -20,10 +25,10 @@ const transformJS = {
2025
Object.keys(value).forEach(key => {
2126
const originalValue = existing[key]
2227
const newValue = value[key]
23-
if (Array.isArray(newValue)) {
24-
changedData[key] = newValue
28+
if (Array.isArray(originalValue) && Array.isArray(newValue)) {
29+
changedData[key] = mergeArrayWithDedupe(originalValue, newValue)
2530
} else if (isObject(originalValue) && isObject(newValue)) {
26-
changedData[key] = merge(originalValue, newValue)
31+
changedData[key] = merge(originalValue, newValue, mergeOptions)
2732
} else {
2833
changedData[key] = newValue
2934
}
@@ -37,12 +42,18 @@ const transformJS = {
3742

3843
const transformJSON = {
3944
read: ({ source }) => JSON.parse(source),
40-
write: ({ value, existing }) => JSON.stringify(merge(existing, value), null, 2)
45+
write: ({ value, existing }) => {
46+
return JSON.stringify(merge(existing, value, mergeOptions), null, 2)
47+
}
4148
}
4249

4350
const transformYAML = {
4451
read: ({ source }) => require('js-yaml').safeLoad(source),
45-
write: ({ value, existing }) => require('js-yaml').safeDump(merge(existing, value))
52+
write: ({ value, existing }) => {
53+
return require('js-yaml').safeDump(merge(existing, value, mergeOptions), {
54+
skipInvalid: true
55+
})
56+
}
4657
}
4758

4859
const transformLines = {

0 commit comments

Comments
 (0)