Skip to content

Commit 52b587e

Browse files
committed
feat: make tslint work for vue files
1 parent 91b20b7 commit 52b587e

File tree

5 files changed

+101
-34
lines changed

5 files changed

+101
-34
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ module.exports = (api, { lintOnSave }) => {
2222
descriptions: 'lint source files',
2323
usage: 'vue-cli-service lint [options] [...files]',
2424
options: {
25-
'--format': 'specify formatter (default: codeframe)',
25+
'--format [formatter]': 'specify formatter (default: codeframe)',
2626
'--no-fix': 'do not fix errors'
2727
},
2828
details: 'For more options, see https://eslint.org/docs/user-guide/command-line-interface#options'

packages/@vue/cli-plugin-typescript/generator/template/tslint.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"indent": [true, "spaces", 2],
1010
"interface-name": false,
1111
"ordered-imports": false,
12-
"object-literal-sort-keys": false
12+
"object-literal-sort-keys": false,
13+
"no-consecutive-blank-lines": false
1314
}
1415
}
1516
<%_ } _%>

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,11 @@ module.exports = (api, options) => {
5959
descriptions: 'lint source files with TSLint',
6060
usage: 'vue-cli-service lint [options] [...files]',
6161
options: {
62-
'--format': 'specify formatter (default: codeframe)',
63-
'--no-fix': 'do not fix errors'
64-
},
65-
details: 'For more options, see https://palantir.github.io/tslint/usage/cli/'
62+
'--format [formatter]': 'specify formatter (default: codeframe)',
63+
'--no-fix': 'do not fix errors',
64+
'--formatters-dir [dir]': 'formatter directory',
65+
'--rules-dir [dir]': 'rules directory'
66+
}
6667
}, args => {
6768
return require('./lib/tslint')(args, api)
6869
})
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,95 @@
11
module.exports = function lint (args = {}, api, silent) {
22
process.chdir(api.resolve('.'))
33

4-
const { run } = require('tslint/lib/runner')
4+
const fs = require('fs')
5+
const globby = require('globby')
6+
const tslint = require('tslint')
7+
const vueCompiler = require('vue-template-compiler')
58

6-
// TODO make this support *.vue files
7-
return run({
8-
files: args._ && args._.length ? args._ : ['src/**/*.ts', 'test/**/*.ts'],
9-
exclude: args.exclude || [],
9+
const options = {
1010
fix: !args['no-fix'],
11-
project: args.project,
12-
config: args.config || api.resolve('tslint.json'),
13-
force: args.force,
14-
format: args.format,
11+
formatter: args.format || 'codeframe',
1512
formattersDirectory: args['formatters-dir'],
16-
init: args.init,
17-
out: args.out,
18-
outputAbsolutePaths: args['output-absolute-paths'],
19-
rulesDirectory: args['rules-dir'],
20-
test: args.test,
21-
typeCheck: args['type-check']
22-
}, {
23-
log (m) { if (!silent) process.stdout.write(m) },
24-
error (m) { process.stdout.write(m) }
25-
}).then(code => {
26-
process.exitCode = code
27-
}).catch(err => {
28-
console.error(err)
29-
process.exitCode = 1
13+
rulesDirectory: args['rules-dir']
14+
}
15+
const linter = new tslint.Linter(options)
16+
17+
const config = tslint.Configuration.findConfiguration(api.resolve('tslint.json')).results
18+
// create a patched config that disables the blank lines rule,
19+
// so that we get correct line numbers in error reports.
20+
const vueConfig = Object.assign(config)
21+
const rules = vueConfig.rules = new Map(vueConfig.rules)
22+
const rule = rules.get('no-consecutive-blank-lines')
23+
rules.set('no-consecutive-blank-lines', Object.assign({}, rule, {
24+
ruleSeverity: 'off'
25+
}))
26+
27+
// hack to make tslint --fix work for *.vue files
28+
// this works because (luckily) tslint lints synchronously
29+
const vueFileCache = new Map()
30+
const writeFileSync = fs.writeFileSync
31+
const patchWriteFile = () => {
32+
fs.writeFileSync = (file, content, options) => {
33+
if (/\.vue(\.ts)?$/.test(file)) {
34+
file = file.replace(/\.ts$/, '')
35+
const { before, after } = vueFileCache.get(file)
36+
content = `${before}\n${content.trim()}\n${after}`
37+
}
38+
return writeFileSync(file, content, options)
39+
}
40+
}
41+
const restoreWriteFile = () => {
42+
fs.writeFileSync = writeFileSync
43+
}
44+
45+
const lint = file => new Promise((resolve, reject) => {
46+
const filePath = api.resolve(file)
47+
fs.readFile(filePath, 'utf-8', (err, content) => {
48+
if (err) return reject(err)
49+
const isVue = /\.vue(\.ts)?$/.test(file)
50+
if (isVue) {
51+
const { script } = vueCompiler.parseComponent(content, { pad: 'line' })
52+
if (script) {
53+
vueFileCache.set(filePath, {
54+
before: content.slice(0, script.start),
55+
after: content.slice(script.end)
56+
})
57+
}
58+
content = script && script.content
59+
}
60+
if (content) {
61+
patchWriteFile()
62+
linter.lint(
63+
// append .ts so that tslint apply TS rules
64+
`${filePath}${isVue ? `.ts` : ``}`,
65+
content,
66+
// use Vue config to ignore blank lines
67+
isVue ? vueConfig : config
68+
)
69+
restoreWriteFile()
70+
}
71+
resolve()
72+
})
73+
})
74+
75+
const files = args._ && args._.length ? args._ : ['src/**/*.ts', 'src/**/*.vue', 'test/**/*.ts']
76+
77+
return globby(files).then(files => {
78+
return Promise.all(files.map(lint))
79+
}).then(() => {
80+
const result = linter.getResult()
81+
if (result.output.trim()) {
82+
process.stdout.write(result.output.replace(/\.vue\.ts\b/g, '.vue'))
83+
} else if (result.fixes.length) {
84+
// some formatters do not report fixes.
85+
const f = new tslint.Formatters.ProseFormatter()
86+
process.stdout.write(f.format(result.failures, result.fixes))
87+
} else if (!result.failures.length) {
88+
console.log(`No lint errors found.\n`)
89+
}
90+
91+
if (result.failures.length && !args.force) {
92+
process.exitCode = 1
93+
}
3094
})
3195
}

packages/@vue/cli-plugin-typescript/package.json

+6-5
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,19 @@
2323
},
2424
"dependencies": {
2525
"fork-ts-checker-webpack-plugin": "^0.3.0",
26+
"globby": "^7.1.1",
2627
"ts-loader": "^3.2.0",
2728
"tslint": "^5.9.1",
2829
"typescript": "^2.6.2"
2930
},
3031
"devDependencies": {
31-
"@types/mocha": "^2.2.46",
32+
"@babel/plugin-proposal-class-properties": "7 || ^7.0.0-beta || ^7.0.0-rc",
33+
"@babel/plugin-proposal-decorators": "7 || ^7.0.0-beta || ^7.0.0-rc",
34+
"@babel/preset-typescript": "7 || ^7.0.0-beta || ^7.0.0-rc",
3235
"@types/chai": "^4.1.0",
3336
"@types/jest": "^22.0.1",
37+
"@types/mocha": "^2.2.46",
3438
"vue-class-component": "^6.0.0",
35-
"vue-property-decorator": "^6.0.0",
36-
"@babel/preset-typescript": "7 || ^7.0.0-beta || ^7.0.0-rc",
37-
"@babel/plugin-proposal-decorators": "7 || ^7.0.0-beta || ^7.0.0-rc",
38-
"@babel/plugin-proposal-class-properties": "7 || ^7.0.0-beta || ^7.0.0-rc"
39+
"vue-property-decorator": "^6.0.0"
3940
}
4041
}

0 commit comments

Comments
 (0)