Skip to content

Commit c76d2e6

Browse files
authored
style: add a "no-shadow" linter rule (#4385)
It has become a common source of mistakes. For example, during PR #4363 I've referred to the wrong `options` several times due to the variable shadowing.
1 parent 2e3fa92 commit c76d2e6

File tree

23 files changed

+146
-144
lines changed

23 files changed

+146
-144
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module.exports = {
1515
"indent": ["error", 2, {
1616
"MemberExpression": "off"
1717
}],
18+
"no-shadow": ["error"],
1819
"node/no-extraneous-require": ["error", {
1920
"allowModules": [
2021
"@vue/cli-service",

packages/@vue/cli-plugin-e2e-cypress/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ module.exports.defaultModes = {
5353
'test:e2e': 'production'
5454
}
5555

56-
function removeArg (rawArgs, arg, offset = 1) {
57-
const matchRE = new RegExp(`^--${arg}`)
58-
const equalRE = new RegExp(`^--${arg}=`)
56+
function removeArg (rawArgs, argToRemove, offset = 1) {
57+
const matchRE = new RegExp(`^--${argToRemove}`)
58+
const equalRE = new RegExp(`^--${argToRemove}=`)
5959
const i = rawArgs.findIndex(arg => matchRE.test(arg))
6060
if (i > -1) {
6161
rawArgs.splice(i, offset + (equalRE.test(rawArgs[i]) ? 0 : 1))

packages/@vue/cli-plugin-e2e-nightwatch/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ module.exports.defaultModes = {
6464
'test:e2e': 'production'
6565
}
6666

67-
function removeArg (rawArgs, arg, offset = 1) {
68-
const matchRE = new RegExp(`^--${arg}`)
69-
const equalRE = new RegExp(`^--${arg}=`)
67+
function removeArg (rawArgs, argToRemove, offset = 1) {
68+
const matchRE = new RegExp(`^--${argToRemove}`)
69+
const equalRE = new RegExp(`^--${argToRemove}=`)
7070
const i = rawArgs.findIndex(arg => matchRE.test(arg))
7171
if (i > -1) {
7272
rawArgs.splice(i, offset + (equalRE.test(rawArgs[i]) ? 0 : 1))

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

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,33 +26,16 @@ module.exports = (api, { config, lintOn = [] }, _, invoking) => {
2626
pkg.devDependencies['babel-eslint'] = '^10.0.1'
2727
}
2828

29-
const injectEditorConfig = (config) => {
30-
const filePath = api.resolve('.editorconfig')
31-
if (fs.existsSync(filePath)) {
32-
// Append to existing .editorconfig
33-
api.render(files => {
34-
const configPath = path.resolve(__dirname, `./template/${config}/_editorconfig`)
35-
const editorconfig = fs.readFileSync(configPath, 'utf-8')
36-
37-
files['.editorconfig'] += `\n${editorconfig}`
38-
})
39-
} else {
40-
api.render(`./template/${config}`)
41-
}
42-
}
43-
4429
if (config === 'airbnb') {
4530
eslintConfig.extends.push('@vue/airbnb')
4631
Object.assign(pkg.devDependencies, {
4732
'@vue/eslint-config-airbnb': '^4.0.0'
4833
})
49-
injectEditorConfig('airbnb')
5034
} else if (config === 'standard') {
5135
eslintConfig.extends.push('@vue/standard')
5236
Object.assign(pkg.devDependencies, {
5337
'@vue/eslint-config-standard': '^4.0.0'
5438
})
55-
injectEditorConfig('standard')
5639
} else if (config === 'prettier') {
5740
eslintConfig.extends.push('@vue/prettier')
5841
Object.assign(pkg.devDependencies, {
@@ -67,6 +50,19 @@ module.exports = (api, { config, lintOn = [] }, _, invoking) => {
6750
eslintConfig.extends.push('eslint:recommended')
6851
}
6952

53+
const editorConfigTemplatePath = path.resolve(__dirname, `./template/${config}/_editorconfig`)
54+
if (fs.existsSync(editorConfigTemplatePath)) {
55+
if (fs.existsSync(api.resolve('.editorconfig'))) {
56+
// Append to existing .editorconfig
57+
api.render(files => {
58+
const editorconfig = fs.readFileSync(editorConfigTemplatePath, 'utf-8')
59+
files['.editorconfig'] += `\n${editorconfig}`
60+
})
61+
} else {
62+
api.render(`./template/${config}`)
63+
}
64+
}
65+
7066
if (!lintOn.includes('save')) {
7167
pkg.vue = {
7268
lintOnSave: false // eslint-loader configured in runtime plugin

packages/@vue/cli-plugin-pwa/ui.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,33 +102,33 @@ module.exports = api => {
102102
]
103103
}
104104
},
105-
onWrite: async ({ api, prompts, cwd }) => {
105+
onWrite: async ({ onWriteApi, prompts, cwd }) => {
106106
const result = {}
107107
for (const prompt of prompts.filter(p => !p.raw.skipSave)) {
108-
result[`pwa.${prompt.id}`] = await api.getAnswer(prompt.id)
108+
result[`pwa.${prompt.id}`] = await onWriteApi.getAnswer(prompt.id)
109109
}
110-
api.setData('vue', result)
110+
onWriteApi.setData('vue', result)
111111

112112
// Update app manifest
113113

114114
const name = result['name']
115115
if (name) {
116-
api.setData('manifest', {
116+
onWriteApi.setData('manifest', {
117117
name,
118118
short_name: name
119119
})
120120
}
121121

122122
const themeColor = result['themeColor']
123123
if (themeColor) {
124-
api.setData('manifest', {
124+
onWriteApi.setData('manifest', {
125125
theme_color: themeColor
126126
})
127127
}
128128

129-
const backgroundColor = await api.getAnswer('backgroundColor')
129+
const backgroundColor = await onWriteApi.getAnswer('backgroundColor')
130130
if (backgroundColor) {
131-
api.setData('manifest', {
131+
onWriteApi.setData('manifest', {
132132
background_color: backgroundColor
133133
})
134134
}

packages/@vue/cli-plugin-typescript/__tests__/tsPluginBabel.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ test('using correct loader', () => {
1414

1515
service.init()
1616
const config = service.resolveWebpackConfig()
17+
// eslint-disable-next-line no-shadow
1718
const rule = config.module.rules.find(rule => rule.test.test('foo.ts'))
1819
expect(rule.use[0].loader).toMatch('cache-loader')
1920
expect(rule.use[1].loader).toMatch('babel-loader')

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
const path = require('path')
22

3-
module.exports = (api, options) => {
3+
module.exports = (api, projectOptions) => {
44
const fs = require('fs')
5-
const useThreads = process.env.NODE_ENV === 'production' && !!options.parallel
5+
const useThreads = process.env.NODE_ENV === 'production' && !!projectOptions.parallel
66

77
api.chainWebpack(config => {
88
config.resolveLoader.modules.prepend(path.join(__dirname, 'node_modules'))
99

10-
if (!options.pages) {
10+
if (!projectOptions.pages) {
1111
config.entry('app')
1212
.clear()
1313
.add('./src/main.ts')
@@ -40,8 +40,8 @@ module.exports = (api, options) => {
4040
addLoader({
4141
loader: 'thread-loader',
4242
options:
43-
typeof options.parallel === 'number'
44-
? { workers: options.parallel }
43+
typeof projectOptions.parallel === 'number'
44+
? { workers: projectOptions.parallel }
4545
: {}
4646
})
4747
}
@@ -75,7 +75,7 @@ module.exports = (api, options) => {
7575
.plugin('fork-ts-checker')
7676
.use(require('fork-ts-checker-webpack-plugin'), [{
7777
vue: true,
78-
tslint: options.lintOnSave !== false && fs.existsSync(api.resolve('tslint.json')),
78+
tslint: projectOptions.lintOnSave !== false && fs.existsSync(api.resolve('tslint.json')),
7979
formatter: 'codeframe',
8080
// https://github.com/TypeStrong/ts-loader#happypackmode-boolean-defaultfalse
8181
checkSyntacticErrors: useThreads

packages/@vue/cli-plugin-typescript/lib/tslint.js

Lines changed: 74 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,88 @@
1-
module.exports = function lint (args = {}, api, silent) {
2-
const cwd = api.resolve('.')
3-
const fs = require('fs')
4-
const path = require('path')
5-
const globby = require('globby')
6-
const tslint = require('tslint')
7-
const ts = require('typescript')
8-
/* eslint-disable-next-line node/no-extraneous-require */
9-
const vueCompiler = require('vue-template-compiler')
10-
const isVueFile = file => /\.vue(\.ts)?$/.test(file)
11-
12-
const options = {
13-
fix: args['fix'] !== false,
14-
formatter: args.format || 'codeFrame',
15-
formattersDirectory: args['formatters-dir'],
16-
rulesDirectory: args['rules-dir']
17-
}
18-
19-
// hack to make tslint --fix work for *.vue files:
20-
// we save the non-script parts to a cache right before
21-
// linting the file, and patch fs.writeFileSync to combine the fixed script
22-
// back with the non-script parts.
23-
// this works because (luckily) tslint lints synchronously.
24-
const vueFileCache = new Map()
25-
const writeFileSync = fs.writeFileSync
26-
27-
const patchWriteFile = () => {
28-
fs.writeFileSync = (file, content, options) => {
29-
if (isVueFile(file)) {
30-
const parts = vueFileCache.get(path.normalize(file))
31-
if (parts) {
32-
parts.content = content
33-
const { before, after } = parts
34-
content = `${before}\n${content.trim()}\n${after}`
35-
}
1+
const fs = require('fs')
2+
const path = require('path')
3+
const globby = require('globby')
4+
const tslint = require('tslint')
5+
const ts = require('typescript')
6+
/* eslint-disable-next-line node/no-extraneous-require */
7+
const vueCompiler = require('vue-template-compiler')
8+
9+
const isVueFile = file => /\.vue(\.ts)?$/.test(file)
10+
11+
// hack to make tslint --fix work for *.vue files:
12+
// we save the non-script parts to a cache right before
13+
// linting the file, and patch fs.writeFileSync to combine the fixed script
14+
// back with the non-script parts.
15+
// this works because (luckily) tslint lints synchronously.
16+
const vueFileCache = new Map()
17+
const writeFileSync = fs.writeFileSync
18+
19+
const patchWriteFile = () => {
20+
fs.writeFileSync = (file, content, options) => {
21+
if (isVueFile(file)) {
22+
const parts = vueFileCache.get(path.normalize(file))
23+
if (parts) {
24+
parts.content = content
25+
const { before, after } = parts
26+
content = `${before}\n${content.trim()}\n${after}`
3627
}
37-
return writeFileSync(file, content, options)
3828
}
29+
return writeFileSync(file, content, options)
3930
}
31+
}
4032

41-
const restoreWriteFile = () => {
42-
fs.writeFileSync = writeFileSync
43-
}
44-
45-
const parseTSFromVueFile = file => {
46-
// If the file has already been cached, don't read the file again. Use the cache instead.
47-
if (vueFileCache.has(file)) {
48-
return vueFileCache.get(file)
49-
}
33+
const restoreWriteFile = () => {
34+
fs.writeFileSync = writeFileSync
35+
}
5036

51-
const content = fs.readFileSync(file, 'utf-8')
52-
const { script } = vueCompiler.parseComponent(content, { pad: 'line' })
53-
if (script && /^tsx?$/.test(script.lang)) {
54-
vueFileCache.set(file, {
55-
before: content.slice(0, script.start),
56-
after: content.slice(script.end),
57-
content: script.content
58-
})
59-
return script
60-
}
37+
const parseTSFromVueFile = file => {
38+
// If the file has already been cached, don't read the file again. Use the cache instead.
39+
if (vueFileCache.has(file)) {
40+
return vueFileCache.get(file)
6141
}
6242

63-
const program = tslint.Linter.createProgram(api.resolve('tsconfig.json'))
43+
const content = fs.readFileSync(file, 'utf-8')
44+
const { script } = vueCompiler.parseComponent(content, { pad: 'line' })
45+
if (script && /^tsx?$/.test(script.lang)) {
46+
vueFileCache.set(file, {
47+
before: content.slice(0, script.start),
48+
after: content.slice(script.end),
49+
content: script.content
50+
})
51+
return script
52+
}
53+
}
6454

65-
// patch getSourceFile for *.vue files
66-
// so that it returns the <script> block only
67-
const patchProgram = program => {
68-
const getSourceFile = program.getSourceFile
69-
program.getSourceFile = function (file, languageVersion, onError) {
70-
if (isVueFile(file)) {
71-
const { content, lang = 'js' } = parseTSFromVueFile(file) || { content: '', lang: 'js' }
72-
const contentLang = ts.ScriptKind[lang.toUpperCase()]
73-
return ts.createSourceFile(file, content, languageVersion, true, contentLang)
74-
} else {
75-
return getSourceFile.call(this, file, languageVersion, onError)
76-
}
55+
// patch getSourceFile for *.vue files
56+
// so that it returns the <script> block only
57+
const patchProgram = program => {
58+
const getSourceFile = program.getSourceFile
59+
program.getSourceFile = function (file, languageVersion, onError) {
60+
if (isVueFile(file)) {
61+
const { content, lang = 'js' } = parseTSFromVueFile(file) || { content: '', lang: 'js' }
62+
const contentLang = ts.ScriptKind[lang.toUpperCase()]
63+
return ts.createSourceFile(file, content, languageVersion, true, contentLang)
64+
} else {
65+
return getSourceFile.call(this, file, languageVersion, onError)
7766
}
7867
}
68+
}
7969

70+
module.exports = function lint (args = {}, api, silent) {
71+
const cwd = api.resolve('.')
72+
73+
const program = tslint.Linter.createProgram(api.resolve('tsconfig.json'))
8074
patchProgram(program)
8175

82-
const linter = new tslint.Linter(options, program)
76+
const linter = new tslint.Linter({
77+
fix: args['fix'] !== false,
78+
formatter: args.format || 'codeFrame',
79+
formattersDirectory: args['formatters-dir'],
80+
rulesDirectory: args['rules-dir']
81+
}, program)
8382

8483
// patch linter.updateProgram to ensure every program has correct getSourceFile
8584
const updateProgram = linter.updateProgram
85+
// eslint-disable-next-line no-shadow
8686
linter.updateProgram = function (...args) {
8787
updateProgram.call(this, ...args)
8888
patchProgram(this.program)
@@ -102,7 +102,7 @@ module.exports = function lint (args = {}, api, silent) {
102102
ruleSeverity: 'off'
103103
}))
104104

105-
const lint = file => {
105+
const lintFile = file => {
106106
const filePath = api.resolve(file)
107107
const isVue = isVueFile(file)
108108
patchWriteFile()
@@ -116,7 +116,7 @@ module.exports = function lint (args = {}, api, silent) {
116116
restoreWriteFile()
117117
}
118118

119-
const files = args._ && args._.length
119+
const patterns = args._ && args._.length
120120
? args._
121121
: ['src/**/*.ts', 'src/**/*.vue', 'src/**/*.tsx', 'tests/**/*.ts', 'tests/**/*.tsx']
122122

@@ -125,11 +125,11 @@ module.exports = function lint (args = {}, api, silent) {
125125
// use the raw tslint.json data because config contains absolute paths
126126
const rawTslintConfig = tslint.Configuration.readConfigurationFile(tslintConfigPath)
127127
const excludedGlobs = rawTslintConfig.linterOptions.exclude
128-
excludedGlobs.forEach((g) => files.push('!' + g))
128+
excludedGlobs.forEach((g) => patterns.push('!' + g))
129129
}
130130

131-
return globby(files, { cwd }).then(files => {
132-
files.forEach(lint)
131+
return globby(patterns, { cwd }).then(files => {
132+
files.forEach(lintFile)
133133
if (silent) return
134134
const result = linter.getResult()
135135
if (result.output.trim()) {

0 commit comments

Comments
 (0)