@@ -4,93 +4,117 @@ module.exports = function lint (args = {}, api, silent) {
44 const path = require ( 'path' )
55 const globby = require ( 'globby' )
66 const tslint = require ( 'tslint' )
7+ const ts = require ( 'typescript' )
78 /* eslint-disable-next-line node/no-extraneous-require */
89 const vueCompiler = require ( 'vue-template-compiler' )
10+ const isVueFile = file => / \. v u e ( \. t s ) ? $ / . test ( file )
911
1012 const options = {
1113 fix : args [ 'fix' ] !== false ,
1214 formatter : args . format || 'codeFrame' ,
1315 formattersDirectory : args [ 'formatters-dir' ] ,
1416 rulesDirectory : args [ 'rules-dir' ]
1517 }
16- const linter = new tslint . Linter ( options )
1718
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.
3024 const vueFileCache = new Map ( )
3125 const writeFileSync = fs . writeFileSync
26+
3227 const patchWriteFile = ( ) => {
3328 fs . writeFileSync = ( file , content , options ) => {
34- if ( / \. v u e ( \. t s ) ? $ / . test ( file ) ) {
35- file = file . replace ( / \. t s $ / , '' )
29+ if ( isVueFile ( file ) ) {
3630 const { before, after } = vueFileCache . get ( path . normalize ( file ) )
3731 content = `${ before } \n${ content . trim ( ) } \n${ after } `
3832 }
3933 return writeFileSync ( file , content , options )
4034 }
4135 }
36+
4237 const restoreWriteFile = ( ) => {
4338 fs . writeFileSync = writeFileSync
4439 }
4540
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 )
7165 }
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+ }
75103
76104 const files = args . _ && args . _ . length
77105 ? args . _
78106 : [ 'src/**/*.ts' , 'src/**/*.vue' , 'src/**/*.tsx' , 'tests/**/*.ts' , 'tests/**/*.tsx' ]
79107
80- const stripTsExtension = str => str . replace ( / \. v u e \. t s \b / g, '.vue' )
81-
82108 return globby ( files , { cwd } ) . then ( files => {
83- return Promise . all ( files . map ( lint ) )
84- } ) . then ( ( ) => {
109+ files . forEach ( lint )
85110 if ( silent ) return
86-
87111 const result = linter . getResult ( )
88112 if ( result . output . trim ( ) ) {
89- process . stdout . write ( stripTsExtension ( result . output ) )
113+ process . stdout . write ( result . output )
90114 } else if ( result . fixes . length ) {
91115 // some formatters do not report fixes.
92116 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 ) )
94118 } else if ( ! result . failures . length ) {
95119 console . log ( `No lint errors found.\n` )
96120 }
0 commit comments