@@ -60,6 +60,7 @@ let themeWatcherTimer: NodeJS.Timeout | null = null
6060let watchedThemesDir : string | null = null
6161let watcherStartToken = 0
6262let chokidarWatchLoader : Promise < ChokidarWatch > | null = null
63+ const reportedThemeIssues = new Map < string , string > ( )
6364
6465type ChokidarWatch = (
6566 path : string | readonly string [ ] ,
@@ -100,6 +101,27 @@ function ensureThemesDir(): string {
100101 return THEMES_DIR
101102}
102103
104+ function reportThemeIssue (
105+ fileName : string ,
106+ reason : string ,
107+ error ?: unknown ,
108+ ) : void {
109+ const message = `${ fileName } : ${ reason } `
110+
111+ if ( reportedThemeIssues . get ( fileName ) === message ) {
112+ return
113+ }
114+
115+ reportedThemeIssues . set ( fileName , message )
116+
117+ if ( error ) {
118+ console . warn ( `[theme] ${ message } ` , error )
119+ return
120+ }
121+
122+ console . warn ( `[theme] ${ message } ` )
123+ }
124+
103125function isThemeType ( value : unknown ) : value is ThemeType {
104126 return value === 'light' || value === 'dark'
105127}
@@ -114,7 +136,7 @@ function isStringRecord(value: unknown): value is Record<string, string> {
114136
115137function parseThemeFile ( raw : unknown , fileName : string ) : ThemeFile | null {
116138 if ( ! raw || typeof raw !== 'object' || Array . isArray ( raw ) ) {
117- log ( 'theme:invalid-file' , new Error ( ` Invalid JSON object in ${ fileName } ` ) )
139+ reportThemeIssue ( fileName , ' Invalid JSON object' )
118140 return null
119141 }
120142
@@ -127,30 +149,32 @@ function parseThemeFile(raw: unknown, fileName: string): ThemeFile | null {
127149 }
128150
129151 if ( typeof data . name !== 'string' || ! data . name . trim ( ) ) {
130- log ( 'theme:invalid-file' , new Error ( ` Invalid name in ${ fileName } ` ) )
152+ reportThemeIssue ( fileName , ' Invalid name' )
131153 return null
132154 }
133155
134156 if ( ! isThemeType ( data . type ) ) {
135- log ( 'theme:invalid-file' , new Error ( ` Invalid type in ${ fileName } ` ) )
157+ reportThemeIssue ( fileName , ' Invalid type' )
136158 return null
137159 }
138160
139161 if ( data . author !== undefined && typeof data . author !== 'string' ) {
140- log ( 'theme:invalid-file' , new Error ( ` Invalid author in ${ fileName } ` ) )
162+ reportThemeIssue ( fileName , ' Invalid author' )
141163 return null
142164 }
143165
144166 if ( data . colors !== undefined && ! isStringRecord ( data . colors ) ) {
145- log ( 'theme:invalid-file' , new Error ( ` Invalid colors in ${ fileName } ` ) )
167+ reportThemeIssue ( fileName , ' Invalid colors' )
146168 return null
147169 }
148170
149171 if ( data . editorColors !== undefined && ! isStringRecord ( data . editorColors ) ) {
150- log ( 'theme:invalid-file' , new Error ( ` Invalid editorColors in ${ fileName } ` ) )
172+ reportThemeIssue ( fileName , ' Invalid editorColors' )
151173 return null
152174 }
153175
176+ reportedThemeIssues . delete ( fileName )
177+
154178 return {
155179 name : data . name . trim ( ) ,
156180 author : data . author ,
@@ -211,7 +235,7 @@ function readThemeFromFile(
211235 return parseThemeFile ( parsed , fileName )
212236 }
213237 catch ( error ) {
214- log ( 'theme: read-file ', error )
238+ reportThemeIssue ( fileName , 'Failed to read or parse JSON ', error )
215239 return null
216240 }
217241}
0 commit comments