From ba4d0f285ba3dacc2bc580dc2d74e1cd0e578b1e Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 18 Jul 2018 16:44:03 -0400 Subject: [PATCH] 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. --- .../cli-plugin-typescript/generator/index.js | 12 +++++++++++ packages/@vue/cli/__tests__/invoke.spec.js | 20 ++++++++---------- packages/@vue/cli/lib/GeneratorAPI.js | 5 +++-- .../@vue/cli/lib/util/configTransforms.js | 21 ++++++++++++++----- 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/packages/@vue/cli-plugin-typescript/generator/index.js b/packages/@vue/cli-plugin-typescript/generator/index.js index d2cf7525c..b6e8062b1 100644 --- a/packages/@vue/cli-plugin-typescript/generator/index.js +++ b/packages/@vue/cli-plugin-typescript/generator/index.js @@ -73,6 +73,18 @@ module.exports = (api, { }) } + const hasESLint = api.hasPlugin('eslint') + if (hasESLint) { + api.extendPackage({ + devDependencies: { + '@vue/eslint-config-typescript': '^3.0.0-rc.5' + }, + eslintConfig: { + extends: ['@vue/typescript'] + } + }) + } + api.render('./template', { isTest: process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG, hasMocha, diff --git a/packages/@vue/cli/__tests__/invoke.spec.js b/packages/@vue/cli/__tests__/invoke.spec.js index 9430b601b..369e70499 100644 --- a/packages/@vue/cli/__tests__/invoke.spec.js +++ b/packages/@vue/cli/__tests__/invoke.spec.js @@ -76,7 +76,7 @@ test('invoke with prompts', async () => { await assertUpdates(project) }) -test('invoke with existing files', async () => { +test('invoke with ts', async () => { const project = await create(`invoke-existing`, { useConfigFiles: true, plugins: { @@ -86,7 +86,7 @@ test('invoke with existing files', async () => { }) // mock install const pkg = JSON.parse(await project.read('package.json')) - pkg.devDependencies['@vue/cli-plugin-eslint'] = '*' + pkg.devDependencies['@vue/cli-plugin-typescript'] = '*' await project.write('package.json', JSON.stringify(pkg, null, 2)) // mock existing vue.config.js @@ -97,11 +97,12 @@ test('invoke with existing files', async () => { extends: ['plugin:vue/essential', 'eslint:recommended'] })) - await project.run(`${require.resolve('../bin/vue')} invoke eslint --config airbnb --lintOn commit`) + await project.run(`${require.resolve('../bin/vue')} invoke typescript --classComponent --useTsWithBabel`) - await assertUpdates(project) - const updatedVueConfig = await project.read('vue.config.js') - expect(updatedVueConfig).toMatch(`module.exports = { lintOnSave: false }`) + const updatedESLintrc = parseJS(await project.read('.eslintrc.js')) + expect(updatedESLintrc).toEqual(Object.assign({}, baseESLintConfig, { + extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/typescript'] + })) }) test('invoke with existing files (yaml)', async () => { @@ -109,13 +110,9 @@ test('invoke with existing files (yaml)', async () => { useConfigFiles: true, plugins: { '@vue/cli-plugin-babel': {}, - '@vue/cli-plugin-eslint': { config: 'base' } + '@vue/cli-plugin-eslint': {} } }) - // mock install - const pkg = JSON.parse(await project.read('package.json')) - pkg.devDependencies['@vue/cli-plugin-eslint'] = '*' - await project.write('package.json', JSON.stringify(pkg, null, 2)) const eslintrc = parseJS(await project.read('.eslintrc.js')) expect(eslintrc).toEqual(Object.assign({}, baseESLintConfig, { @@ -137,6 +134,7 @@ extends: extends: - 'plugin:vue/essential' - 'eslint:recommended' + - '@vue/airbnb' `.trim()) }) diff --git a/packages/@vue/cli/lib/GeneratorAPI.js b/packages/@vue/cli/lib/GeneratorAPI.js index 044a5c39a..4048783dc 100644 --- a/packages/@vue/cli/lib/GeneratorAPI.js +++ b/packages/@vue/cli/lib/GeneratorAPI.js @@ -14,6 +14,7 @@ const ConfigTransform = require('./ConfigTransform') const isString = val => typeof val === 'string' const isFunction = val => typeof val === 'function' const isObject = val => val && typeof val === 'object' +const mergeArrayWithDedupe = (a, b) => Array.from(new Set([...a, ...b])) class GeneratorAPI { /** @@ -140,9 +141,9 @@ class GeneratorAPI { } else if (!(key in pkg)) { pkg[key] = value } else if (Array.isArray(value) && Array.isArray(existing)) { - pkg[key] = existing.concat(value) + pkg[key] = mergeArrayWithDedupe(existing, value) } else if (isObject(value) && isObject(existing)) { - pkg[key] = merge(existing, value) + pkg[key] = merge(existing, value, { arrayMerge: mergeArrayWithDedupe }) } else { pkg[key] = value } diff --git a/packages/@vue/cli/lib/util/configTransforms.js b/packages/@vue/cli/lib/util/configTransforms.js index 79328292f..abc397617 100644 --- a/packages/@vue/cli/lib/util/configTransforms.js +++ b/packages/@vue/cli/lib/util/configTransforms.js @@ -3,6 +3,11 @@ const stringifyJS = require('./stringifyJS') const { loadModule } = require('@vue/cli-shared-utils') const merge = require('deepmerge') +const mergeArrayWithDedupe = (a, b) => Array.from(new Set([...a, ...b])) +const mergeOptions = { + arrayMerge: mergeArrayWithDedupe +} + const isObject = val => val && typeof val === 'object' const transformJS = { @@ -20,10 +25,10 @@ const transformJS = { Object.keys(value).forEach(key => { const originalValue = existing[key] const newValue = value[key] - if (Array.isArray(newValue)) { - changedData[key] = newValue + if (Array.isArray(originalValue) && Array.isArray(newValue)) { + changedData[key] = mergeArrayWithDedupe(originalValue, newValue) } else if (isObject(originalValue) && isObject(newValue)) { - changedData[key] = merge(originalValue, newValue) + changedData[key] = merge(originalValue, newValue, mergeOptions) } else { changedData[key] = newValue } @@ -37,12 +42,18 @@ const transformJS = { const transformJSON = { read: ({ source }) => JSON.parse(source), - write: ({ value, existing }) => JSON.stringify(merge(existing, value), null, 2) + write: ({ value, existing }) => { + return JSON.stringify(merge(existing, value, mergeOptions), null, 2) + } } const transformYAML = { read: ({ source }) => require('js-yaml').safeLoad(source), - write: ({ value, existing }) => require('js-yaml').safeDump(merge(existing, value)) + write: ({ value, existing }) => { + return require('js-yaml').safeDump(merge(existing, value, mergeOptions), { + skipInvalid: true + }) + } } const transformLines = {