44 *--------------------------------------------------------------------------------------------*/
55
66import yargs , { CommandModule } from "yargs" ;
7- import { TextDocument } from "vscode-languageserver-textdocument" ;
87import { SepticCnfg } from "../cnfg" ;
98import {
109 getDiagnostics ,
1110 SepticDiagnostic ,
1211 SepticDiagnosticLevel ,
1312} from "../diagnostics" ;
1413import { SepticMetaInfoProvider } from "../metaInfoProvider" ;
15- import { validateFileExists , createDocumentFromFile } from "./utils" ;
14+ import {
15+ createDocumentFromFile ,
16+ SepticConfigProviderFs ,
17+ } from "../configProvider" ;
18+ import * as fs from "fs" ;
19+ import * as path from "path" ;
20+ import { scgConfigFromYAML , ScgContext } from "../scg" ;
1621
1722interface LintOptions {
1823 file : string ;
1924 ignore ?: string [ ] ;
2025 septicversion ?: string ;
2126}
2227
23- async function lintSepticConfig (
24- document : TextDocument ,
25- ) : Promise < SepticDiagnostic [ ] > {
28+ async function lintSepticConfig ( filePath : string ) : Promise < SepticDiagnostic [ ] > {
29+ const document = await createDocumentFromFile ( filePath ) ;
2630 const cnfg = new SepticCnfg ( document ) ;
2731 cnfg . parse ( undefined ) ;
2832 await cnfg . updateObjectParents ( ) ;
@@ -31,50 +35,97 @@ async function lintSepticConfig(
3135 ) ;
3236}
3337
38+ async function lintScg ( scgConfigPath : string ) : Promise < SepticDiagnostic [ ] > {
39+ let scgConfig ;
40+ try {
41+ const content = await fs . promises . readFile ( scgConfigPath , {
42+ encoding : "utf-8" ,
43+ } ) ;
44+ scgConfig = scgConfigFromYAML ( content ) ;
45+ } catch ( error ) {
46+ console . error ( `Failed to read or parse SCG config: ${ error } ` ) ;
47+ process . exit ( 1 ) ;
48+ }
49+ const configProvider = new SepticConfigProviderFs ( ) ;
50+ const scgContext = new ScgContext (
51+ "SCG Context" ,
52+ scgConfigPath ,
53+ scgConfig ,
54+ configProvider ,
55+ ) ;
56+ await scgContext . load ( ) ;
57+ await scgContext . updateObjectParents ( ) ;
58+ const diagnostics : SepticDiagnostic [ ] = [ ] ;
59+ for ( const file of scgContext . files ) {
60+ const cnfg = await configProvider . get ( file ) ;
61+ if ( cnfg === undefined ) {
62+ continue ;
63+ }
64+ const templateDiagnostics = getDiagnostics ( cnfg , scgContext ) . filter (
65+ ( diagnostic ) => diagnostic . level !== SepticDiagnosticLevel . hint ,
66+ ) ;
67+ diagnostics . push ( ...templateDiagnostics ) ;
68+ }
69+ return diagnostics ;
70+ }
71+
3472function filterDiagnostics (
3573 diagnostics : SepticDiagnostic [ ] ,
3674 ignoreCodes ?: string [ ] ,
3775) : SepticDiagnostic [ ] {
3876 if ( ! ignoreCodes || ignoreCodes . length === 0 ) {
3977 return diagnostics ;
4078 }
41-
4279 const ignoreSet = new Set ( ignoreCodes . map ( ( code ) => code . toUpperCase ( ) ) ) ;
4380 return diagnostics . filter (
4481 ( diagnostic ) => ! ignoreSet . has ( diagnostic . code . toUpperCase ( ) ) ,
4582 ) ;
4683}
4784
48- function formatDiagnostic ( diagnostic : SepticDiagnostic , uri : string ) : string {
85+ function formatDiagnostic ( diagnostic : SepticDiagnostic ) : string {
4986 const line = diagnostic . range . start . line + 1 ;
5087 const character = diagnostic . range . start . character + 1 ;
5188 const level = diagnostic . level . toUpperCase ( ) ;
5289 const code = diagnostic . code ;
53- return `${ level } [${ code } ]: ${ diagnostic . message } ${ uri } #${ line } :${ character } ` ;
90+ return `${ level } [${ code } ]: ${ diagnostic . message } ${ diagnostic . uri } #${ line } :${ character } ` ;
5491}
5592
56- function printDiagnostics ( diagnostics : SepticDiagnostic [ ] , uri : string ) : void {
93+ function printDiagnostics ( diagnostics : SepticDiagnostic [ ] ) : void {
5794 if ( diagnostics . length === 0 ) {
58- console . log ( "No issues found. " ) ;
95+ console . log ( "No issues found! " ) ;
5996 return ;
6097 }
61-
62- console . log ( `${ diagnostics . length } issue(s) found:` ) ;
63- for ( const diagnostic of diagnostics ) {
64- console . log ( formatDiagnostic ( diagnostic , uri ) ) ;
98+ const files = diagnostics . map ( ( diag ) => diag . uri ) ;
99+ const uniqueFiles = Array . from ( new Set ( files ) ) ;
100+ for ( const file of uniqueFiles ) {
101+ const diagsForFile = diagnostics . filter ( ( diag ) => diag . uri === file ) ;
102+ console . log ( `\nLinting results for file: ${ file } ` ) ;
103+ console . log ( `${ diagsForFile . length } issue(s) found:` ) ;
104+ for ( const diagnostic of diagsForFile ) {
105+ console . log ( formatDiagnostic ( diagnostic ) ) ;
106+ }
65107 }
66108}
67109
68110async function handler ( options : LintOptions ) : Promise < void > {
69- validateFileExists ( options . file ) ;
70-
71- // Set the version to use for linting
72111 if ( options . septicversion ) {
73112 SepticMetaInfoProvider . setVersion ( options . septicversion ) ;
74113 }
114+ const allDiagnostics : SepticDiagnostic [ ] = [ ] ;
115+ if ( options . file . endsWith ( ".cnfg" ) ) {
116+ const fileDiagnostics = await lintSepticConfig ( options . file ) ;
117+ allDiagnostics . push ( ...fileDiagnostics ) ;
118+ } else if ( options . file . endsWith ( ".yaml" ) ) {
119+ console . log ( `Linting SCG project ${ path . resolve ( options . file ) } ` ) ;
120+ const scgDiagnostics = await lintScg ( options . file ) ;
121+ allDiagnostics . push ( ...scgDiagnostics ) ;
122+ } else {
123+ console . error (
124+ "Unsupported file type. Please provide a .cnfg or .yaml file." ,
125+ ) ;
126+ process . exit ( 1 ) ;
127+ }
75128
76- const document = createDocumentFromFile ( options . file ) ;
77- const allDiagnostics = await lintSepticConfig ( document ) ;
78129 const diagnostics = filterDiagnostics ( allDiagnostics , options . ignore ) ;
79130
80131 if ( options . ignore && options . ignore . length > 0 ) {
@@ -86,7 +137,7 @@ async function handler(options: LintOptions): Promise<void> {
86137 }
87138 }
88139
89- printDiagnostics ( diagnostics , document . uri ) ;
140+ printDiagnostics ( diagnostics ) ;
90141
91142 const exitCode = diagnostics . length === 0 ? 0 : 1 ;
92143 process . exit ( exitCode ) ;
@@ -102,7 +153,8 @@ export const lintCommand: CommandModule<object, LintOptions> = {
102153 return yargs
103154 . positional ( "file" , {
104155 type : "string" ,
105- description : "Path to Septic config file to lint" ,
156+ description :
157+ "Path to Septic config file or SCG config file to lint" ,
106158 demandOption : true ,
107159 } )
108160 . option ( "ignore" , {
@@ -118,6 +170,7 @@ export const lintCommand: CommandModule<object, LintOptions> = {
118170 default : "latest" ,
119171 } )
120172 . example ( "$0 lint config.cnfg" , "Lint a config file" )
173+ . example ( "$0 lint scg.yaml" , "Lint all templates in an SCG project" )
121174 . example (
122175 "$0 lint config.cnfg --ignore W101 W203" ,
123176 "Lint and ignore specific diagnostic codes" ,
0 commit comments