|
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