18
18
/* eslint-disable @typescript-eslint/no-explicit-any */
19
19
/* eslint-disable no-console */
20
20
21
- import * as fs from "fs" ;
22
- import { getRandomValues } from 'node:crypto' ;
23
- import * as path from " path" ;
21
+ import * as fs from 'fs' ;
22
+ import { getRandomValues } from 'node:crypto' ;
23
+ import * as path from ' path' ;
24
24
25
- import * as ts from " typescript" ;
26
- import yargs from " yargs" ;
27
- import { hideBin } from " yargs/helpers" ;
25
+ import * as ts from ' typescript' ;
26
+ import yargs from ' yargs' ;
27
+ import { hideBin } from ' yargs/helpers' ;
28
28
29
29
let isVerbose : boolean = false ;
30
30
@@ -35,7 +35,7 @@ function log(message: any): void {
35
35
}
36
36
37
37
// Define the names of the functions we are looking for
38
- const targetFunctionNames : Set < string > = new Set ( [ " fail" , " hardAssert" ] ) ;
38
+ const targetFunctionNames : Set < string > = new Set ( [ ' fail' , ' hardAssert' ] ) ;
39
39
40
40
// Interface to store information about found call sites
41
41
interface CallSiteInfo {
@@ -68,9 +68,9 @@ function getTsFilesRecursive(dirPath: string): string[] {
68
68
}
69
69
// Recursively scan subdirectories
70
70
tsFiles = tsFiles . concat ( getTsFilesRecursive ( fullPath ) ) ;
71
- } else if ( entry . isFile ( ) && ( entry . name . endsWith ( " .ts" ) ) ) {
71
+ } else if ( entry . isFile ( ) && entry . name . endsWith ( ' .ts' ) ) {
72
72
// Exclude declaration files (.d.ts) as they usually don't contain implementation
73
- if ( ! entry . name . endsWith ( " .d.ts" ) ) {
73
+ if ( ! entry . name . endsWith ( ' .d.ts' ) ) {
74
74
tsFiles . push ( fullPath ) ;
75
75
}
76
76
}
@@ -168,23 +168,36 @@ function findFunctionCalls(filePaths: string[]): CallSiteInfo[] {
168
168
169
169
function handleList ( occurrences : CallSiteInfo [ ] ) : void {
170
170
if ( occurrences . length === 0 ) {
171
- log ( " No assertion ids found." ) ;
171
+ log ( ' No assertion ids found.' ) ;
172
172
return ;
173
173
}
174
174
175
- occurrences . sort ( ( a , b ) => a . assertionId . localeCompare ( b . assertionId ) ) . forEach ( ( call ) => {
176
- console . log (
177
- `ID: ${ call . assertionId } ; MESSAGE: ${ call . errorMessage } ; SOURCE: '${ call . functionName } ' call at ${ path . relative ( process . cwd ( ) , call . fileName ) } :${ call . line } :${ call . character } `
178
- ) ;
179
- } ) ;
175
+ occurrences
176
+ . sort ( ( a , b ) => a . assertionId . localeCompare ( b . assertionId ) )
177
+ . forEach ( call => {
178
+ console . log (
179
+ `ID: ${ call . assertionId } ; MESSAGE: ${ call . errorMessage } ; SOURCE: '${
180
+ call . functionName
181
+ } ' call at ${ path . relative ( process . cwd ( ) , call . fileName ) } :${
182
+ call . line
183
+ } :${ call . character } `
184
+ ) ;
185
+ } ) ;
180
186
}
181
187
182
- function find ( occurrences : CallSiteInfo [ ] , targetId : string | number ) : CallSiteInfo [ ] {
183
- const target = typeof targetId === 'number' ? targetId . toString ( 16 ) : targetId ;
188
+ function find (
189
+ occurrences : CallSiteInfo [ ] ,
190
+ targetId : string | number
191
+ ) : CallSiteInfo [ ] {
192
+ const target =
193
+ typeof targetId === 'number' ? targetId . toString ( 16 ) : targetId ;
184
194
return occurrences . filter ( o => String ( o . assertionId ) === String ( target ) ) ;
185
195
}
186
196
187
- function handleFind ( occurrences : CallSiteInfo [ ] , targetId : string | number ) : void {
197
+ function handleFind (
198
+ occurrences : CallSiteInfo [ ] ,
199
+ targetId : string | number
200
+ ) : void {
188
201
const foundLocations = find ( occurrences , targetId ) ;
189
202
190
203
if ( foundLocations . length === 0 ) {
@@ -197,7 +210,7 @@ function handleFind(occurrences: CallSiteInfo[], targetId: string | number): voi
197
210
198
211
function handleCheck ( occurrences : CallSiteInfo [ ] ) : void {
199
212
if ( occurrences . length === 0 ) {
200
- log ( " No assertion ids found to check for duplicates." ) ;
213
+ log ( ' No assertion ids found to check for duplicates.' ) ;
201
214
return ;
202
215
}
203
216
const idCounts : { [ id : string ] : CallSiteInfo [ ] } = { } ;
@@ -212,19 +225,23 @@ function handleCheck(occurrences: CallSiteInfo[]): void {
212
225
213
226
// validate formats
214
227
if ( ! / ^ 0 x [ 0 - 9 a - f ] { 4 } $ / . test ( occ . assertionId ) ) {
215
- console . error ( `Invalid assertion ID '${ occ . assertionId } '. Must match /^0x[0-9a-f]{4}$/` ) ;
228
+ console . error (
229
+ `Invalid assertion ID '${ occ . assertionId } '. Must match /^0x[0-9a-f]{4}$/`
230
+ ) ;
216
231
217
232
const relativePath = path . relative ( process . cwd ( ) , occ . fileName ) ;
218
233
console . error ( `- at '${ relativePath } :${ occ . line } :${ occ . character } ` ) ;
219
234
}
220
235
} ) ;
221
236
222
237
let duplicatesFound = false ;
223
- log ( " Checking for duplicate assertion id usage:" ) ;
238
+ log ( ' Checking for duplicate assertion id usage:' ) ;
224
239
Object . entries ( idCounts ) . forEach ( ( [ code , locations ] ) => {
225
240
if ( locations . length > 1 ) {
226
241
duplicatesFound = true ;
227
- console . error ( `\nDuplicate assertion id "${ code } " found at ${ locations . length } locations:` ) ;
242
+ console . error (
243
+ `\nDuplicate assertion id "${ code } " found at ${ locations . length } locations:`
244
+ ) ;
228
245
locations . forEach ( loc => {
229
246
const relativePath = path . relative ( process . cwd ( ) , loc . fileName ) ;
230
247
console . error ( `- ${ relativePath } :${ loc . line } :${ loc . character } ` ) ;
@@ -233,9 +250,8 @@ function handleCheck(occurrences: CallSiteInfo[]): void {
233
250
} ) ;
234
251
235
252
if ( ! duplicatesFound ) {
236
- log ( "No duplicate assertion ids found." ) ;
237
- }
238
- else {
253
+ log ( 'No duplicate assertion ids found.' ) ;
254
+ } else {
239
255
process . exit ( 1 ) ;
240
256
}
241
257
}
@@ -244,9 +260,12 @@ function randomId(): string {
244
260
const randomBytes = new Uint8Array ( 2 ) ;
245
261
getRandomValues ( randomBytes ) ;
246
262
247
- return '0x' + Array . from ( randomBytes )
248
- . map ( byte => byte . toString ( 16 ) . padStart ( 2 , '0' ) )
249
- . join ( '' ) ;
263
+ return (
264
+ '0x' +
265
+ Array . from ( randomBytes )
266
+ . map ( byte => byte . toString ( 16 ) . padStart ( 2 , '0' ) )
267
+ . join ( '' )
268
+ ) ;
250
269
}
251
270
252
271
function handleNew ( occurrences : CallSiteInfo [ ] ) : void {
@@ -263,44 +282,44 @@ function handleNew(occurrences: CallSiteInfo[]): void {
263
282
// --- Main Execution ---
264
283
async function main ( ) : Promise < void > {
265
284
const argv = yargs ( hideBin ( process . argv ) )
266
- . usage ( " Usage: $0 [options]" )
267
- . option ( " dir" , {
285
+ . usage ( ' Usage: $0 [options]' )
286
+ . option ( ' dir' , {
268
287
alias : 'D' ,
269
- describe : " Directory to scan recursively for TS files" ,
270
- type : " string" ,
271
- demandOption : true ,
288
+ describe : ' Directory to scan recursively for TS files' ,
289
+ type : ' string' ,
290
+ demandOption : true
272
291
} )
273
- . option ( " verbose" , {
274
- alias : "V" ,
275
- describe : " verbose" ,
276
- type : " boolean" ,
292
+ . option ( ' verbose' , {
293
+ alias : 'V' ,
294
+ describe : ' verbose' ,
295
+ type : ' boolean'
277
296
} )
278
- . option ( " find" , {
279
- alias : "F" ,
280
- describe : " Find locations of a specific {assertionId}" ,
281
- type : " string" ,
282
- nargs : 1 ,
297
+ . option ( ' find' , {
298
+ alias : 'F' ,
299
+ describe : ' Find locations of a specific {assertionId}' ,
300
+ type : ' string' ,
301
+ nargs : 1
283
302
} )
284
- . option ( " list" , {
285
- alias : "L" ,
286
- describe : " List all unique assertion ids found (default action)" ,
287
- type : " boolean" ,
303
+ . option ( ' list' , {
304
+ alias : 'L' ,
305
+ describe : ' List all unique assertion ids found (default action)' ,
306
+ type : ' boolean'
288
307
} )
289
- . option ( " new" , {
290
- alias : "N" ,
291
- describe : " Suggest a new assertion id based on existing ones" ,
292
- type : " boolean" ,
308
+ . option ( ' new' , {
309
+ alias : 'N' ,
310
+ describe : ' Suggest a new assertion id based on existing ones' ,
311
+ type : ' boolean'
293
312
} )
294
- . option ( " check" , {
295
- alias : "C" ,
296
- describe : " Check for duplicate usage of assertion ids" ,
297
- type : " boolean" ,
313
+ . option ( ' check' , {
314
+ alias : 'C' ,
315
+ describe : ' Check for duplicate usage of assertion ids' ,
316
+ type : ' boolean'
298
317
} )
299
- . check ( ( argv ) => {
318
+ . check ( argv => {
300
319
// Enforce mutual exclusivity among options *within* the scan command
301
320
const options = [ argv . F , argv . L , argv . N , argv . C ] . filter ( Boolean ) . length ;
302
321
if ( options > 1 ) {
303
- throw new Error ( " Options -F, -L, -N, -C are mutually exclusive." ) ;
322
+ throw new Error ( ' Options -F, -L, -N, -C are mutually exclusive.' ) ;
304
323
}
305
324
return true ;
306
325
} )
@@ -319,25 +338,31 @@ async function main(): Promise<void> {
319
338
try {
320
339
const stats = fs . statSync ( targetDirectory ) ;
321
340
if ( ! stats . isDirectory ( ) ) {
322
- console . error ( `Error: Provided path is not a directory: ${ targetDirectory } ` ) ;
341
+ console . error (
342
+ `Error: Provided path is not a directory: ${ targetDirectory } `
343
+ ) ;
323
344
process . exit ( 1 ) ;
324
345
}
325
346
} catch ( error : any ) {
326
- console . error ( `Error accessing directory ${ targetDirectory } : ${ error . message } ` ) ;
347
+ console . error (
348
+ `Error accessing directory ${ targetDirectory } : ${ error . message } `
349
+ ) ;
327
350
process . exit ( 1 ) ;
328
351
}
329
352
330
353
log ( `Scanning directory: ${ targetDirectory } ` ) ;
331
354
const filesToScan = getTsFilesRecursive ( targetDirectory ) ;
332
355
333
356
if ( filesToScan . length === 0 ) {
334
- log ( " No relevant .ts or .tsx files found." ) ;
357
+ log ( ' No relevant .ts or .tsx files found.' ) ;
335
358
process . exit ( 0 ) ;
336
359
}
337
360
log ( `Found ${ filesToScan . length } files. Analyzing for assertion ids...` ) ;
338
361
339
362
const allOccurrences = findFunctionCalls ( filesToScan ) ;
340
- log ( `Scan complete. Found ${ allOccurrences . length } potential assertion id occurrences.` ) ;
363
+ log (
364
+ `Scan complete. Found ${ allOccurrences . length } potential assertion id occurrences.`
365
+ ) ;
341
366
342
367
// Determine action based on flags
343
368
if ( argv [ 'find' ] ) {
0 commit comments