@@ -25,17 +25,8 @@ import { packageJsonStore } from './file-stores/index.js';
2525import { stripBOM } from '../../rules/helpers/files.js' ;
2626
2727const NOT_DETECTED = 'not-detected' ;
28- const PATH_COMPILER_OPTIONS = new Set ( [
29- 'baseUrl' ,
30- 'declarationDir' ,
31- 'mapRoot' ,
32- 'outDir' ,
33- 'outFile' ,
34- 'rootDir' ,
35- 'rootDirs' ,
36- 'sourceRoot' ,
37- 'tsBuildInfoFile' ,
38- ] ) ;
28+ const KNOWN_COMPILER_OPTIONS = buildKnownCompilerOptions ( ) ;
29+ const PATH_COMPILER_OPTIONS = buildPathCompilerOptions ( ) ;
3930
4031let projectAnalysisTelemetryCollector : ProjectAnalysisTelemetryCollector | undefined ;
4132
@@ -181,7 +172,7 @@ export class ProjectAnalysisTelemetryCollector {
181172 if ( optionName === 'lib' ) {
182173 return [ normalizeLibValue ( optionValue ) ] ;
183174 }
184- const sanitized = sanitizeStringOptionValue ( optionValue ) ;
175+ const sanitized = sanitizeStringOptionValue ( optionName , optionValue ) ;
185176 return sanitized === undefined ? [ ] : [ sanitized ] ;
186177 }
187178
@@ -190,7 +181,7 @@ export class ProjectAnalysisTelemetryCollector {
190181 }
191182
192183 if ( typeof optionValue === 'object' ) {
193- const sanitized = sanitizeObjectOptionValue ( optionValue ) ;
184+ const sanitized = sanitizeObjectOptionValue ( optionName , optionValue ) ;
194185 if ( sanitized === undefined ) {
195186 return [ ] ;
196187 }
@@ -248,28 +239,31 @@ function normalizeLibValue(value: string): string {
248239 return match ? match [ 1 ] : value ;
249240}
250241
251- function sanitizeStringOptionValue ( value : string ) : string | undefined {
252- if ( looksLikePath ( value ) ) {
242+ function sanitizeStringOptionValue ( optionName : string , value : string ) : string | undefined {
243+ if ( looksLikeFilesystemPath ( value ) ) {
244+ return undefined ;
245+ }
246+ if ( ! KNOWN_COMPILER_OPTIONS . has ( optionName ) && looksLikePathWithSeparator ( value ) ) {
253247 return undefined ;
254248 }
255249 return value ;
256250}
257251
258- function sanitizeObjectOptionValue ( value : unknown ) : unknown {
252+ function sanitizeObjectOptionValue ( optionName : string , value : unknown ) : unknown {
259253 if ( typeof value === 'string' ) {
260- return sanitizeStringOptionValue ( value ) ;
254+ return sanitizeStringOptionValue ( optionName , value ) ;
261255 }
262256 if ( Array . isArray ( value ) ) {
263257 const sanitizedValues = value . flatMap ( item => {
264- const sanitized = sanitizeObjectOptionValue ( item ) ;
258+ const sanitized = sanitizeObjectOptionValue ( optionName , item ) ;
265259 return sanitized === undefined ? [ ] : [ sanitized ] ;
266260 } ) ;
267261 return sanitizedValues . length > 0 ? sanitizedValues : undefined ;
268262 }
269263 if ( value && typeof value === 'object' ) {
270264 const sanitizedEntries = Object . entries ( value as Record < string , unknown > ) . flatMap (
271265 ( [ key , nested ] ) => {
272- const sanitized = sanitizeObjectOptionValue ( nested ) ;
266+ const sanitized = sanitizeObjectOptionValue ( optionName , nested ) ;
273267 return sanitized === undefined ? [ ] : [ [ key , sanitized ] as [ string , unknown ] ] ;
274268 } ,
275269 ) ;
@@ -278,23 +272,58 @@ function sanitizeObjectOptionValue(value: unknown): unknown {
278272 return value ;
279273}
280274
281- function looksLikePath ( value : string ) : boolean {
275+ function looksLikeFilesystemPath ( value : string ) : boolean {
282276 return (
283277 value . startsWith ( '/' ) ||
284278 / ^ [ a - z A - Z ] : [ \\ / ] / . test ( value ) ||
285279 value . startsWith ( '\\\\' ) ||
286280 value . startsWith ( 'file://' ) ||
287- value . includes ( '/' ) ||
288- value . includes ( '\\' )
281+ value . startsWith ( './' ) ||
282+ value . startsWith ( '../' ) ||
283+ value . startsWith ( '.\\' ) ||
284+ value . startsWith ( '..\\' ) ||
285+ value . startsWith ( '~/' ) ||
286+ value . startsWith ( '~\\' )
289287 ) ;
290288}
291289
290+ function looksLikePathWithSeparator ( value : string ) : boolean {
291+ return value . includes ( '/' ) || value . includes ( '\\' ) ;
292+ }
293+
294+ function buildKnownCompilerOptions ( ) : Set < string > {
295+ return new Set ( getTypeScriptOptionDeclarations ( ) . map ( declaration => declaration . name ) ) ;
296+ }
297+
298+ function buildPathCompilerOptions ( ) : Set < string > {
299+ const pathOptions = new Set ( [ 'paths' ] ) ;
300+ for ( const declaration of getTypeScriptOptionDeclarations ( ) ) {
301+ if (
302+ declaration . isFilePath === true ||
303+ ( declaration . type === 'list' && declaration . element ?. isFilePath === true )
304+ ) {
305+ pathOptions . add ( declaration . name ) ;
306+ }
307+ }
308+ return pathOptions ;
309+ }
310+
311+ type TypeScriptOptionDeclaration = {
312+ name : string ;
313+ type : unknown ;
314+ isFilePath ?: boolean ;
315+ element ?: {
316+ isFilePath ?: boolean ;
317+ } ;
318+ } ;
319+
320+ function getTypeScriptOptionDeclarations ( ) : TypeScriptOptionDeclaration [ ] {
321+ return ( ( ts as unknown as { optionDeclarations ?: unknown [ ] } ) . optionDeclarations ??
322+ [ ] ) as TypeScriptOptionDeclaration [ ] ;
323+ }
324+
292325function buildEnumOptionValues ( ) : Map < string , Map < number , string > > {
293- const optionDeclarations = ( ( ts as unknown as { optionDeclarations ?: unknown [ ] } )
294- . optionDeclarations ?? [ ] ) as Array < {
295- name : string ;
296- type : unknown ;
297- } > ;
326+ const optionDeclarations = getTypeScriptOptionDeclarations ( ) ;
298327 const enums = new Map < string , Map < number , string > > ( ) ;
299328 for ( const declaration of optionDeclarations ) {
300329 if ( ! ( declaration . type instanceof Map ) ) {
0 commit comments