@@ -24,6 +24,19 @@ import { readdirSync, readFileSync, statSync } from 'node:fs';
2424import { dirname , join , relative , sep } from 'node:path' ;
2525import { fileURLToPath } from 'node:url' ;
2626import { describe , it , expect } from 'vitest' ;
27+ // The repo's ONE answer to "is this span a comment, or code?" — its header
28+ // carries the two private-stripper families that drifted apart and the
29+ // parser-differential sweep that measured which way each fails. The private
30+ // scanner this replaces was string-aware but REGEX-BLIND: the doubled slash
31+ // closing `/^https?:\/\//i` read as a line-comment opener and took the rest of
32+ // the line with it, which is the same defect #12398 found live in two sibling
33+ // guards. `stripComments` (not `maskComments`) is the projection this file
34+ // wants: it deletes comment characters but keeps every newline, so the
35+ // `file:line` every finding here reports still points at the real line, and
36+ // nothing in this file reports an offset. The `.mjs` specifier is deliberate;
37+ // `scripts/js-comment-mask.d.mts` beside it is a hand-written declaration, so
38+ // this import needs no `allowJs`.
39+ import { stripComments } from '../../../../scripts/js-comment-mask.mjs' ;
2740import { CONSOLE_ROUTE_LEDGER } from './console-route-ledger.js' ;
2841
2942/**
@@ -59,49 +72,6 @@ const NON_ROUTE_MEMBERS = new Set(['use', 'notFound', 'onError', 'fire', 'fetch'
5972// Scanning machinery
6073// ---------------------------------------------------------------------------
6174
62- /**
63- * Strip comments before scanning, PRESERVING newlines inside block comments so
64- * every finding's `file:line` points at the real line — `console.ts` opens with
65- * a 35-line header, and reporting a mount 35 lines short makes an accurate
66- * finding read as a wrong one.
67- */
68- export function stripComments ( source : string ) : string {
69- let out = '' ;
70- let i = 0 ;
71- while ( i < source . length ) {
72- const c = source [ i ] ;
73- const next = source [ i + 1 ] ;
74- if ( c === '/' && next === '/' ) {
75- while ( i < source . length && source [ i ] !== '\n' ) i ++ ;
76- continue ;
77- }
78- if ( c === '/' && next === '*' ) {
79- i += 2 ;
80- while ( i < source . length && ! ( source [ i ] === '*' && source [ i + 1 ] === '/' ) ) {
81- if ( source [ i ] === '\n' ) out += '\n' ;
82- i ++ ;
83- }
84- i += 2 ;
85- continue ;
86- }
87- if ( c === '\'' || c === '"' || c === '`' ) {
88- const quote = c ;
89- out += c ;
90- i ++ ;
91- while ( i < source . length ) {
92- if ( source [ i ] === '\\' ) { out += source . slice ( i , i + 2 ) ; i += 2 ; continue ; }
93- out += source [ i ] ;
94- if ( source [ i ] === quote ) { i ++ ; break ; }
95- i ++ ;
96- }
97- continue ;
98- }
99- out += c ;
100- i ++ ;
101- }
102- return out ;
103- }
104-
10575/** Module-scope `const NAME = '<literal>';` bindings, exported or not. */
10676export function constantBindings ( code : string ) : Map < string , string > {
10777 const out = new Map < string , string > ( ) ;
@@ -380,13 +350,29 @@ describe('cli console route ledger hygiene', () => {
380350} ) ;
381351
382352describe ( 'scan machinery, pinned in both directions' , ( ) => {
383- it ( 'the comment stripper drops prose paths, keeps code paths, and preserves line numbers' , ( ) => {
353+ it ( 'the shared stripper drops prose paths, keeps code paths, and preserves line numbers' , ( ) => {
354+ // Not a re-pin of `js-comment-mask.mjs` -- that module pins its own
355+ // behaviour. This pins the PROPERTY this census rests on: comment
356+ // characters go, every newline stays, so `lineOf()` below still counts
357+ // the real line.
384358 const stripped = stripComments ( "// app.get('/ghost', h)\n/* a\nb */\napp.get(`/real`, h);\n" ) ;
385359 expect ( stripped ) . not . toContain ( 'ghost' ) ;
386360 expect ( stripped ) . toContain ( '/real' ) ;
387361 expect ( censusOf ( [ 'f.ts' ] , ( ) => "/* a\nb\nc */\napp.get('/x', h);\n" ) . routes [ 0 ] . line ) . toBe ( 4 ) ;
388362 } ) ;
389363
364+ it ( 'a doubled slash inside a REGEX LITERAL does not swallow the rest of its line' , ( ) => {
365+ // The defect the private scanner this file used to carry was measured
366+ // committing on 7 of this package's 110 sources: string-aware but
367+ // regex-blind, it read the `//` that CLOSES `/^https?:\/\//i` as a
368+ // line-comment opener and deleted to end of line. A mount sharing that
369+ // line went with it, and the census reported clean over text it never
370+ // read. Live in `commands/dev.ts`, `commands/serve.ts` and
371+ // `commands/start.ts` at conversion time.
372+ const stripped = stripComments ( "const ok = /^https?:\\/\\//i.test(u); app.get('/real', h);\n" ) ;
373+ expect ( stripped ) . toContain ( '/real' ) ;
374+ } ) ;
375+
390376 it ( 'resolves the spellings this package uses, and refuses the rest' , ( ) => {
391377 const b = constantBindings ( "export const CONSOLE_PATH = '/_console';\n" ) ;
392378 expect ( b . get ( 'CONSOLE_PATH' ) ) . toBe ( '/_console' ) ;
0 commit comments