@@ -4,93 +4,102 @@ 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 )
17-
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- } ) )
2718
2819 // hack to make tslint --fix work for *.vue files
2920 // this works because (luckily) tslint lints synchronously
3021 const vueFileCache = new Map ( )
3122 const writeFileSync = fs . writeFileSync
23+
3224 const patchWriteFile = ( ) => {
3325 fs . writeFileSync = ( file , content , options ) => {
34- if ( / \. v u e ( \. t s ) ? $ / . test ( file ) ) {
35- file = file . replace ( / \. t s $ / , '' )
26+ if ( isVueFile ( file ) ) {
3627 const { before, after } = vueFileCache . get ( path . normalize ( file ) )
3728 content = `${ before } \n${ content . trim ( ) } \n${ after } `
3829 }
3930 return writeFileSync ( file , content , options )
4031 }
4132 }
33+
4234 const restoreWriteFile = ( ) => {
4335 fs . writeFileSync = writeFileSync
4436 }
4537
46- const lint = file => new Promise ( ( resolve , reject ) => {
38+ const parseTSFromVueFile = file => {
39+ const content = fs . readFileSync ( file , 'utf-8' )
40+ const { script } = vueCompiler . parseComponent ( content , { pad : 'line' } )
41+ if ( script ) {
42+ vueFileCache . set ( file , {
43+ before : content . slice ( 0 , script . start ) ,
44+ after : content . slice ( script . end )
45+ } )
46+ }
47+ return script && script . content
48+ }
49+
50+ const program = tslint . Linter . createProgram ( api . resolve ( 'tsconfig.json' ) )
51+
52+ // patch getSourceFile for *.vue files
53+ const getSourceFile = program . getSourceFile
54+ program . getSourceFile = function ( file , languageVersion , onError ) {
55+ if ( isVueFile ( file ) ) {
56+ const script = parseTSFromVueFile ( file )
57+ return ts . createSourceFile ( file , script , languageVersion , true )
58+ } else {
59+ return getSourceFile . call ( this , file , languageVersion , onError )
60+ }
61+ }
62+
63+ const linter = new tslint . Linter ( options , program )
64+
65+ const config = tslint . Configuration . findConfiguration ( api . resolve ( 'tslint.json' ) ) . results
66+ // create a patched config that disables the blank lines rule,
67+ // so that we get correct line numbers in error reports for *.vue files.
68+ const vueConfig = Object . assign ( config )
69+ const rules = vueConfig . rules = new Map ( vueConfig . rules )
70+ const rule = rules . get ( 'no-consecutive-blank-lines' )
71+ rules . set ( 'no-consecutive-blank-lines' , Object . assign ( { } , rule , {
72+ ruleSeverity : 'off'
73+ } ) )
74+
75+ const lint = file => {
4776 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 ( )
71- }
72- resolve ( )
73- } )
74- } )
77+ const isVue = isVueFile ( file )
78+ patchWriteFile ( )
79+ linter . lint (
80+ // append .ts so that tslint apply TS rules
81+ filePath ,
82+ '' ,
83+ // use Vue config to ignore blank lines
84+ isVue ? vueConfig : config
85+ )
86+ restoreWriteFile ( )
87+ }
7588
7689 const files = args . _ && args . _ . length
7790 ? args . _
7891 : [ 'src/**/*.ts' , 'src/**/*.vue' , 'src/**/*.tsx' , 'tests/**/*.ts' , 'tests/**/*.tsx' ]
7992
80- const stripTsExtension = str => str . replace ( / \. v u e \. t s \b / g, '.vue' )
81-
8293 return globby ( files , { cwd } ) . then ( files => {
83- return Promise . all ( files . map ( lint ) )
84- } ) . then ( ( ) => {
94+ files . forEach ( lint )
8595 if ( silent ) return
86-
8796 const result = linter . getResult ( )
8897 if ( result . output . trim ( ) ) {
89- process . stdout . write ( stripTsExtension ( result . output ) )
98+ process . stdout . write ( result . output )
9099 } else if ( result . fixes . length ) {
91100 // some formatters do not report fixes.
92101 const f = new tslint . Formatters . ProseFormatter ( )
93- process . stdout . write ( stripTsExtension ( f . format ( result . failures , result . fixes ) ) )
102+ process . stdout . write ( f . format ( result . failures , result . fixes ) )
94103 } else if ( ! result . failures . length ) {
95104 console . log ( `No lint errors found.\n` )
96105 }
0 commit comments