import * as fs from "fs" ; import * as path from "path" ; import { ESLint } from "eslint" ; import * as lodash from "lodash" ; import * as minimist from "minimist" ; const rulesPath = path . join ( __dirname , "../lib/rules" ) ; run ( ) ; async function run ( ) { const argv = minimist ( process . argv . slice ( 2 ) , { string : [ "rule" ] , boolean : [ "update" ] , } ) ; const rules = getRules ( argv . rule ) ; if ( ! rules . length ) { console . error ( "No rules found!" ) ; process . exit ( 1 ) ; } console . log ( "Found rules:" ) ; rules . forEach ( rule => { console . log ( "  *" , rule ) ; } ) ; console . log ( "" ) ; const sourcesPath = path . join ( __dirname , "javascript-test-sources/src" ) ; if ( ! fs . existsSync ( sourcesPath ) ) { console . error ( "No sources found!" ) ; process . exit ( 1 ) ; } const eslint = new ESLint ( { overrideConfig : { parser : "babel-eslint" , parserOptions : { ecmaFeatures : { jsx : true , experimentalObjectRestSpread : true } , ecmaVersion : 2018 , sourceType : "module" , } , rules : getEslintRules ( rules ) } , rulePaths : [ rulesPath ] , useEslintrc : false , allowInlineConfig : false , ignorePath : path . join ( __dirname , ".eslintignore" ) , } ) ; const reportResults = await eslint . lintFiles ( [ sourcesPath ] ) ; const results = { } ; rules . forEach ( rule => ( results [ rule ] = { } ) ) ; reportResults . forEach ( result => { result . messages . forEach ( message => { if ( message . ruleId ) { addToResults ( results , getFileNameForSnapshot ( result . filePath ) , message . ruleId , message . line ) ; } else { throw new Error ( `Unexpected error: ${ JSON . stringify ( message ) } Filepath: ${ result . filePath }` ) ; } } ) ; } ) ; if ( argv . update ) { writeResults ( results ) ; } else { const passed = checkResults ( results ) ; if ( ! passed ) { process . exitCode = 1 ; } } } function getRules ( rule ? ) { const rules = fs . readdirSync ( rulesPath ) . filter ( file => file . endsWith ( ".js" ) ) . map ( file => file . substring ( 0 , file . indexOf ( ".js" ) ) ) ; if ( rule ) { return rules . includes ( rule ) ? [ rule ] : [ ] ; } else { return lodash . sortBy ( rules ) ; } } function getEslintRules ( rules ) { const eslintRules = { } ; rules . forEach ( rule => { eslintRules [ rule ] = "error" ; } ) ; return eslintRules ; } function addToResults ( results , filePath , ruleId , line ) { if ( ! results [ ruleId ] ) { results [ ruleId ] = { } ; } if ( ! results [ ruleId ] [ filePath ] ) { results [ ruleId ] [ filePath ] = [ ] ; } results [ ruleId ] [ filePath ] . push ( line ) ; } function getFileNameForSnapshot ( path ) { const marker = "/javascript-test-sources/" ; const pos = path . indexOf ( marker ) ; return path . substr ( pos + marker . length ) ; } interface Results { [ rule ] : { [ file ] : number [ ] ; } ; } function writeResults ( results ) { Object . keys ( results ) . forEach ( rule => { const content = [ ] ; Object . keys ( results [ rule ] ) . sort ( ) . forEach ( file => { const lines = results [ rule ] [ file ] ; content . push ( `${ file }: ${ lines . join ( ) }` ) ; } ) ; writeSnapshot ( rule , content . join ( "\n" ) + "\n" ) ; } ) ; } function checkResults ( actual ) { const actualRules = Object . keys ( actual ) ; const expected = readSnapshots ( actualRules ) ; let passed = true ; actualRules . forEach ( rule => { const expectedFiles = expected [ rule ] ; const actualFiles = actual [ rule ] || { } ; const allFiles = lodash . union ( Object . keys ( actualFiles ) , Object . keys ( expectedFiles ) ) ; allFiles . forEach ( file => { const expectedLines = expectedFiles [ file ] || [ ] ; const actualLines = actualFiles [ file ] || [ ] ; const missingLines = lodash . difference ( expectedLines , actualLines ) ; if ( missingLines . length > 0 ) { passed = false ; console . log ( "Missing issues:" ) ; console . log ( "  * Rule:" , rule ) ; console . log ( "  * File:" , file ) ; console . log ( "  * Lines:" , missingLines . join ( ", " ) ) ; console . log ( ) ; } const extraLines = lodash . difference ( actualLines , expectedLines ) ; if ( extraLines . length > 0 ) { passed = false ; console . log ( "Extra issues:" ) ; console . log ( "  * Rule:" , rule ) ; console . log ( "  * File:" , file ) ; console . log ( "  * Lines:" , extraLines . join ( ", " ) ) ; console . log ( ) ; } } ) ; } ) ; return passed ; } function writeSnapshot ( rule , content ) { const fileName = path . join ( __dirname , "snapshots" , rule ) ; fs . writeFileSync ( fileName , content ) ; } function readSnapshots ( rules ) { const snapshotsDir = path . join ( __dirname , "snapshots" ) ; const results = { } ; rules . forEach ( rule => { results [ rule ] = { } ; const content = readSnapshotFile ( snapshotsDir , rule ) ; content . forEach ( line => { const colonIndex = line . indexOf ( ":" ) ; if ( colonIndex !== - 1 ) { const file = line . substring ( 0 , colonIndex ) ; const lines = line . substr ( colonIndex + 1 ) . split ( "," ) . map ( s => parseInt ( s , 10 ) ) ; results [ rule ] [ file ] = lines ; } } ) ; } ) ; return results ; } function readSnapshotFile ( snapshotsDir , ruleName ) { const rulePath = path . join ( snapshotsDir , ruleName ) ; if ( fs . existsSync ( rulePath ) ) { return fs . readFileSync ( rulePath , "utf-8" ) . split ( "\n" ) ; } else { return [ ] ; } }