Skip to content

style: add a "no-shadow" linter rule #4385

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = {
"indent": ["error", 2, {
"MemberExpression": "off"
}],
"no-shadow": ["error"],
"node/no-extraneous-require": ["error", {
"allowModules": [
"@vue/cli-service",
Expand Down
6 changes: 3 additions & 3 deletions packages/@vue/cli-plugin-e2e-cypress/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
6 changes: 3 additions & 3 deletions packages/@vue/cli-plugin-e2e-nightwatch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
30 changes: 13 additions & 17 deletions packages/@vue/cli-plugin-eslint/generator/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand All @@ -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
Expand Down
14 changes: 7 additions & 7 deletions packages/@vue/cli-plugin-pwa/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,33 +102,33 @@ 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
})
}

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
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
12 changes: 6 additions & 6 deletions packages/@vue/cli-plugin-typescript/index.js
Original file line number Diff line number Diff line change
@@ -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')
Expand Down Expand Up @@ -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 }
: {}
})
}
Expand Down Expand Up @@ -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
Expand Down
148 changes: 74 additions & 74 deletions packages/@vue/cli-plugin-typescript/lib/tslint.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,88 @@
module.exports = function lint (args = {}, api, silent) {
const cwd = api.resolve('.')
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)

const options = {
fix: args['fix'] !== false,
formatter: args.format || 'codeFrame',
formattersDirectory: args['formatters-dir'],
rulesDirectory: args['rules-dir']
}

// 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}`
}
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)
}
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 restoreWriteFile = () => {
fs.writeFileSync = writeFileSync
}

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
}
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 program = tslint.Linter.createProgram(api.resolve('tsconfig.json'))
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 <script> block only
const patchProgram = program => {
const getSourceFile = program.getSourceFile
program.getSourceFile = function (file, languageVersion, onError) {
if (isVueFile(file)) {
const { content, lang = 'js' } = parseTSFromVueFile(file) || { content: '', lang: 'js' }
const contentLang = ts.ScriptKind[lang.toUpperCase()]
return ts.createSourceFile(file, content, languageVersion, true, contentLang)
} else {
return getSourceFile.call(this, file, languageVersion, onError)
}
// patch getSourceFile for *.vue files
// so that it returns the <script> block only
const patchProgram = program => {
const getSourceFile = program.getSourceFile
program.getSourceFile = function (file, languageVersion, onError) {
if (isVueFile(file)) {
const { content, lang = 'js' } = parseTSFromVueFile(file) || { content: '', lang: 'js' }
const contentLang = ts.ScriptKind[lang.toUpperCase()]
return ts.createSourceFile(file, content, languageVersion, true, contentLang)
} else {
return getSourceFile.call(this, file, languageVersion, onError)
}
}
}

module.exports = function lint (args = {}, api, silent) {
const cwd = api.resolve('.')

const program = tslint.Linter.createProgram(api.resolve('tsconfig.json'))
patchProgram(program)

const linter = new tslint.Linter(options, program)
const linter = new tslint.Linter({
fix: args['fix'] !== false,
formatter: args.format || 'codeFrame',
formattersDirectory: args['formatters-dir'],
rulesDirectory: args['rules-dir']
}, program)

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

const lint = file => {
const lintFile = file => {
const filePath = api.resolve(file)
const isVue = isVueFile(file)
patchWriteFile()
Expand All @@ -116,7 +116,7 @@ module.exports = function lint (args = {}, api, silent) {
restoreWriteFile()
}

const files = args._ && args._.length
const patterns = args._ && args._.length
? args._
: ['src/**/*.ts', 'src/**/*.vue', 'src/**/*.tsx', 'tests/**/*.ts', 'tests/**/*.tsx']

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

return globby(files, { cwd }).then(files => {
files.forEach(lint)
return globby(patterns, { cwd }).then(files => {
files.forEach(lintFile)
if (silent) return
const result = linter.getResult()
if (result.output.trim()) {
Expand Down
Loading