|
1 | 1 | module.exports = function lint (args = {}, api, silent) {
|
2 | 2 | process.chdir(api.resolve('.'))
|
3 | 3 |
|
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') |
5 | 8 |
|
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 = { |
10 | 10 | 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', |
15 | 12 | 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 | + } |
30 | 94 | })
|
31 | 95 | }
|
0 commit comments