diff --git a/.eslintrc.js b/.eslintrc.js index ee496200f..cf64baee8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,6 +15,7 @@ module.exports = { "indent": ["error", 2, { "MemberExpression": "off" }], + "no-shadow": ["error"], "node/no-extraneous-require": ["error", { "allowModules": [ "@vue/cli-service", diff --git a/packages/@vue/cli-plugin-e2e-cypress/index.js b/packages/@vue/cli-plugin-e2e-cypress/index.js index b05a38631..5553ef655 100644 --- a/packages/@vue/cli-plugin-e2e-cypress/index.js +++ b/packages/@vue/cli-plugin-e2e-cypress/index.js @@ -53,9 +53,9 @@ module.exports.defaultModes = { 'test:e2e': 'production' } -function removeArg (rawArgs, arg, offset = 1) { - const matchRE = new RegExp(`^--${arg}`) - const equalRE = new RegExp(`^--${arg}=`) +function removeArg (rawArgs, argToRemove, offset = 1) { + const matchRE = new RegExp(`^--${argToRemove}`) + const equalRE = new RegExp(`^--${argToRemove}=`) const i = rawArgs.findIndex(arg => matchRE.test(arg)) if (i > -1) { rawArgs.splice(i, offset + (equalRE.test(rawArgs[i]) ? 0 : 1)) diff --git a/packages/@vue/cli-plugin-e2e-nightwatch/index.js b/packages/@vue/cli-plugin-e2e-nightwatch/index.js index 72b6ed753..0f8c2da1e 100644 --- a/packages/@vue/cli-plugin-e2e-nightwatch/index.js +++ b/packages/@vue/cli-plugin-e2e-nightwatch/index.js @@ -64,9 +64,9 @@ module.exports.defaultModes = { 'test:e2e': 'production' } -function removeArg (rawArgs, arg, offset = 1) { - const matchRE = new RegExp(`^--${arg}`) - const equalRE = new RegExp(`^--${arg}=`) +function removeArg (rawArgs, argToRemove, offset = 1) { + const matchRE = new RegExp(`^--${argToRemove}`) + const equalRE = new RegExp(`^--${argToRemove}=`) const i = rawArgs.findIndex(arg => matchRE.test(arg)) if (i > -1) { rawArgs.splice(i, offset + (equalRE.test(rawArgs[i]) ? 0 : 1)) diff --git a/packages/@vue/cli-plugin-eslint/generator/index.js b/packages/@vue/cli-plugin-eslint/generator/index.js index 26a325b00..89331a35e 100644 --- a/packages/@vue/cli-plugin-eslint/generator/index.js +++ b/packages/@vue/cli-plugin-eslint/generator/index.js @@ -26,33 +26,16 @@ module.exports = (api, { config, lintOn = [] }, _, invoking) => { pkg.devDependencies['babel-eslint'] = '^10.0.1' } - const injectEditorConfig = (config) => { - const filePath = api.resolve('.editorconfig') - if (fs.existsSync(filePath)) { - // Append to existing .editorconfig - api.render(files => { - const configPath = path.resolve(__dirname, `./template/${config}/_editorconfig`) - const editorconfig = fs.readFileSync(configPath, 'utf-8') - - files['.editorconfig'] += `\n${editorconfig}` - }) - } else { - api.render(`./template/${config}`) - } - } - if (config === 'airbnb') { eslintConfig.extends.push('@vue/airbnb') Object.assign(pkg.devDependencies, { '@vue/eslint-config-airbnb': '^4.0.0' }) - injectEditorConfig('airbnb') } else if (config === 'standard') { eslintConfig.extends.push('@vue/standard') Object.assign(pkg.devDependencies, { '@vue/eslint-config-standard': '^4.0.0' }) - injectEditorConfig('standard') } else if (config === 'prettier') { eslintConfig.extends.push('@vue/prettier') Object.assign(pkg.devDependencies, { @@ -67,6 +50,19 @@ module.exports = (api, { config, lintOn = [] }, _, invoking) => { eslintConfig.extends.push('eslint:recommended') } + const editorConfigTemplatePath = path.resolve(__dirname, `./template/${config}/_editorconfig`) + if (fs.existsSync(editorConfigTemplatePath)) { + if (fs.existsSync(api.resolve('.editorconfig'))) { + // Append to existing .editorconfig + api.render(files => { + const editorconfig = fs.readFileSync(editorConfigTemplatePath, 'utf-8') + files['.editorconfig'] += `\n${editorconfig}` + }) + } else { + api.render(`./template/${config}`) + } + } + if (!lintOn.includes('save')) { pkg.vue = { lintOnSave: false // eslint-loader configured in runtime plugin diff --git a/packages/@vue/cli-plugin-pwa/ui.js b/packages/@vue/cli-plugin-pwa/ui.js index 570c6c9cb..ec4b45cf7 100644 --- a/packages/@vue/cli-plugin-pwa/ui.js +++ b/packages/@vue/cli-plugin-pwa/ui.js @@ -102,18 +102,18 @@ module.exports = api => { ] } }, - onWrite: async ({ api, prompts, cwd }) => { + onWrite: async ({ onWriteApi, prompts, cwd }) => { const result = {} for (const prompt of prompts.filter(p => !p.raw.skipSave)) { - result[`pwa.${prompt.id}`] = await api.getAnswer(prompt.id) + result[`pwa.${prompt.id}`] = await onWriteApi.getAnswer(prompt.id) } - api.setData('vue', result) + onWriteApi.setData('vue', result) // Update app manifest const name = result['name'] if (name) { - api.setData('manifest', { + onWriteApi.setData('manifest', { name, short_name: name }) @@ -121,14 +121,14 @@ module.exports = api => { const themeColor = result['themeColor'] if (themeColor) { - api.setData('manifest', { + onWriteApi.setData('manifest', { theme_color: themeColor }) } - const backgroundColor = await api.getAnswer('backgroundColor') + const backgroundColor = await onWriteApi.getAnswer('backgroundColor') if (backgroundColor) { - api.setData('manifest', { + onWriteApi.setData('manifest', { background_color: backgroundColor }) } diff --git a/packages/@vue/cli-plugin-typescript/__tests__/tsPluginBabel.spec.js b/packages/@vue/cli-plugin-typescript/__tests__/tsPluginBabel.spec.js index d590f839f..55c98790c 100644 --- a/packages/@vue/cli-plugin-typescript/__tests__/tsPluginBabel.spec.js +++ b/packages/@vue/cli-plugin-typescript/__tests__/tsPluginBabel.spec.js @@ -14,6 +14,7 @@ test('using correct loader', () => { service.init() const config = service.resolveWebpackConfig() + // eslint-disable-next-line no-shadow const rule = config.module.rules.find(rule => rule.test.test('foo.ts')) expect(rule.use[0].loader).toMatch('cache-loader') expect(rule.use[1].loader).toMatch('babel-loader') diff --git a/packages/@vue/cli-plugin-typescript/index.js b/packages/@vue/cli-plugin-typescript/index.js index 8d65a125d..e01d681cd 100644 --- a/packages/@vue/cli-plugin-typescript/index.js +++ b/packages/@vue/cli-plugin-typescript/index.js @@ -1,13 +1,13 @@ const path = require('path') -module.exports = (api, options) => { +module.exports = (api, projectOptions) => { const fs = require('fs') - const useThreads = process.env.NODE_ENV === 'production' && !!options.parallel + const useThreads = process.env.NODE_ENV === 'production' && !!projectOptions.parallel api.chainWebpack(config => { config.resolveLoader.modules.prepend(path.join(__dirname, 'node_modules')) - if (!options.pages) { + if (!projectOptions.pages) { config.entry('app') .clear() .add('./src/main.ts') @@ -40,8 +40,8 @@ module.exports = (api, options) => { addLoader({ loader: 'thread-loader', options: - typeof options.parallel === 'number' - ? { workers: options.parallel } + typeof projectOptions.parallel === 'number' + ? { workers: projectOptions.parallel } : {} }) } @@ -75,7 +75,7 @@ module.exports = (api, options) => { .plugin('fork-ts-checker') .use(require('fork-ts-checker-webpack-plugin'), [{ vue: true, - tslint: options.lintOnSave !== false && fs.existsSync(api.resolve('tslint.json')), + tslint: projectOptions.lintOnSave !== false && fs.existsSync(api.resolve('tslint.json')), formatter: 'codeframe', // https://github.com/TypeStrong/ts-loader#happypackmode-boolean-defaultfalse checkSyntacticErrors: useThreads diff --git a/packages/@vue/cli-plugin-typescript/lib/tslint.js b/packages/@vue/cli-plugin-typescript/lib/tslint.js index 348bf95db..5a514b63f 100644 --- a/packages/@vue/cli-plugin-typescript/lib/tslint.js +++ b/packages/@vue/cli-plugin-typescript/lib/tslint.js @@ -1,88 +1,88 @@ +const fs = require('fs') +const path = require('path') +const globby = require('globby') +const tslint = require('tslint') +const ts = require('typescript') +/* eslint-disable-next-line node/no-extraneous-require */ +const vueCompiler = require('vue-template-compiler') + +const isVueFile = file => /\.vue(\.ts)?$/.test(file) + +// hack to make tslint --fix work for *.vue files: +// we save the non-script parts to a cache right before +// linting the file, and patch fs.writeFileSync to combine the fixed script +// back with the non-script parts. +// this works because (luckily) tslint lints synchronously. +const vueFileCache = new Map() +const writeFileSync = fs.writeFileSync + +const patchWriteFile = () => { + fs.writeFileSync = (file, content, options) => { + if (isVueFile(file)) { + const parts = vueFileCache.get(path.normalize(file)) + if (parts) { + parts.content = content + const { before, after } = parts + content = `${before}\n${content.trim()}\n${after}` + } + } + return writeFileSync(file, content, options) + } +} + +const restoreWriteFile = () => { + fs.writeFileSync = writeFileSync +} + +const parseTSFromVueFile = file => { + // If the file has already been cached, don't read the file again. Use the cache instead. + if (vueFileCache.has(file)) { + return vueFileCache.get(file) + } + + const content = fs.readFileSync(file, 'utf-8') + const { script } = vueCompiler.parseComponent(content, { pad: 'line' }) + if (script && /^tsx?$/.test(script.lang)) { + vueFileCache.set(file, { + before: content.slice(0, script.start), + after: content.slice(script.end), + content: script.content + }) + return script + } +} + +// patch getSourceFile for *.vue files +// so that it returns the