@@ -4,93 +4,117 @@ module.exports = function lint (args = {}, api, silent) {
4
4
const path = require ( 'path' )
5
5
const globby = require ( 'globby' )
6
6
const tslint = require ( 'tslint' )
7
+ const ts = require ( 'typescript' )
7
8
/* eslint-disable-next-line node/no-extraneous-require */
8
9
const vueCompiler = require ( 'vue-template-compiler' )
10
+ const isVueFile = file => / \. v u e ( \. t s ) ? $ / . test ( file )
9
11
10
12
const options = {
11
13
fix : args [ 'fix' ] !== false ,
12
14
formatter : args . format || 'codeFrame' ,
13
15
formattersDirectory : args [ 'formatters-dir' ] ,
14
16
rulesDirectory : args [ 'rules-dir' ]
15
17
}
16
- const linter = new tslint . Linter ( options )
17
18
18
- const config = tslint . Configuration . findConfiguration ( api . resolve ( 'tslint.json' ) ) . results
19
- // create a patched config that disables the blank lines rule,
20
- // so that we get correct line numbers in error reports for *.vue files.
21
- const vueConfig = Object . assign ( config )
22
- const rules = vueConfig . rules = new Map ( vueConfig . rules )
23
- const rule = rules . get ( 'no-consecutive-blank-lines' )
24
- rules . set ( 'no-consecutive-blank-lines' , Object . assign ( { } , rule , {
25
- ruleSeverity : 'off'
26
- } ) )
27
-
28
- // hack to make tslint --fix work for *.vue files
29
- // this works because (luckily) tslint lints synchronously
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.
30
24
const vueFileCache = new Map ( )
31
25
const writeFileSync = fs . writeFileSync
26
+
32
27
const patchWriteFile = ( ) => {
33
28
fs . writeFileSync = ( file , content , options ) => {
34
- if ( / \. v u e ( \. t s ) ? $ / . test ( file ) ) {
35
- file = file . replace ( / \. t s $ / , '' )
29
+ if ( isVueFile ( file ) ) {
36
30
const { before, after } = vueFileCache . get ( path . normalize ( file ) )
37
31
content = `${ before } \n${ content . trim ( ) } \n${ after } `
38
32
}
39
33
return writeFileSync ( file , content , options )
40
34
}
41
35
}
36
+
42
37
const restoreWriteFile = ( ) => {
43
38
fs . writeFileSync = writeFileSync
44
39
}
45
40
46
- const lint = file => new Promise ( ( resolve , reject ) => {
47
- const filePath = api . resolve ( file )
48
- fs . readFile ( filePath , 'utf-8' , ( err , content ) => {
49
- if ( err ) return reject ( err )
50
- const isVue = / \. v u e ( \. t s ) ? $ / . test ( file )
51
- if ( isVue ) {
52
- const { script } = vueCompiler . parseComponent ( content , { pad : 'line' } )
53
- if ( script ) {
54
- vueFileCache . set ( filePath , {
55
- before : content . slice ( 0 , script . start ) ,
56
- after : content . slice ( script . end )
57
- } )
58
- }
59
- content = script && script . content
60
- }
61
- if ( content ) {
62
- patchWriteFile ( )
63
- linter . lint (
64
- // append .ts so that tslint apply TS rules
65
- `${ filePath } ${ isVue ? `.ts` : `` } ` ,
66
- content ,
67
- // use Vue config to ignore blank lines
68
- isVue ? vueConfig : config
69
- )
70
- restoreWriteFile ( )
41
+ const parseTSFromVueFile = file => {
42
+ const content = fs . readFileSync ( file , 'utf-8' )
43
+ const { script } = vueCompiler . parseComponent ( content , { pad : 'line' } )
44
+ if ( script ) {
45
+ vueFileCache . set ( file , {
46
+ before : content . slice ( 0 , script . start ) ,
47
+ after : content . slice ( script . end )
48
+ } )
49
+ }
50
+ return script && script . content
51
+ }
52
+
53
+ const program = tslint . Linter . createProgram ( api . resolve ( 'tsconfig.json' ) )
54
+
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 script = parseTSFromVueFile ( file )
62
+ return ts . createSourceFile ( file , script , languageVersion , true )
63
+ } else {
64
+ return getSourceFile . call ( this , file , languageVersion , onError )
71
65
}
72
- resolve ( )
73
- } )
74
- } )
66
+ }
67
+ }
68
+
69
+ patchProgram ( program )
70
+
71
+ const linter = new tslint . Linter ( options , program )
72
+
73
+ // patch linter.updateProgram to ensure every program has correct getSourceFile
74
+ const updateProgram = linter . updateProgram
75
+ linter . updateProgram = function ( ...args ) {
76
+ updateProgram . call ( this , ...args )
77
+ patchProgram ( this . program )
78
+ }
79
+
80
+ const config = tslint . Configuration . findConfiguration ( api . resolve ( 'tslint.json' ) ) . results
81
+ // create a patched config that disables the blank lines rule,
82
+ // so that we get correct line numbers in error reports for *.vue files.
83
+ const vueConfig = Object . assign ( config )
84
+ const rules = vueConfig . rules = new Map ( vueConfig . rules )
85
+ const rule = rules . get ( 'no-consecutive-blank-lines' )
86
+ rules . set ( 'no-consecutive-blank-lines' , Object . assign ( { } , rule , {
87
+ ruleSeverity : 'off'
88
+ } ) )
89
+
90
+ const lint = file => {
91
+ const filePath = api . resolve ( file )
92
+ const isVue = isVueFile ( file )
93
+ patchWriteFile ( )
94
+ linter . lint (
95
+ // append .ts so that tslint apply TS rules
96
+ filePath ,
97
+ '' ,
98
+ // use Vue config to ignore blank lines
99
+ isVue ? vueConfig : config
100
+ )
101
+ restoreWriteFile ( )
102
+ }
75
103
76
104
const files = args . _ && args . _ . length
77
105
? args . _
78
106
: [ 'src/**/*.ts' , 'src/**/*.vue' , 'src/**/*.tsx' , 'tests/**/*.ts' , 'tests/**/*.tsx' ]
79
107
80
- const stripTsExtension = str => str . replace ( / \. v u e \. t s \b / g, '.vue' )
81
-
82
108
return globby ( files , { cwd } ) . then ( files => {
83
- return Promise . all ( files . map ( lint ) )
84
- } ) . then ( ( ) => {
109
+ files . forEach ( lint )
85
110
if ( silent ) return
86
-
87
111
const result = linter . getResult ( )
88
112
if ( result . output . trim ( ) ) {
89
- process . stdout . write ( stripTsExtension ( result . output ) )
113
+ process . stdout . write ( result . output )
90
114
} else if ( result . fixes . length ) {
91
115
// some formatters do not report fixes.
92
116
const f = new tslint . Formatters . ProseFormatter ( )
93
- process . stdout . write ( stripTsExtension ( f . format ( result . failures , result . fixes ) ) )
117
+ process . stdout . write ( f . format ( result . failures , result . fixes ) )
94
118
} else if ( ! result . failures . length ) {
95
119
console . log ( `No lint errors found.\n` )
96
120
}
0 commit comments