diff --git a/examples/app-showcase/test/inert-wirings.test.ts b/examples/app-showcase/test/inert-wirings.test.ts index 01752d75e0..8c1b8f1c22 100644 --- a/examples/app-showcase/test/inert-wirings.test.ts +++ b/examples/app-showcase/test/inert-wirings.test.ts @@ -2,6 +2,15 @@ import { readdirSync, readFileSync } from 'node:fs'; import { describe, it, expect } from 'vitest'; +// The repo's ONE answer to "is this span a comment, or code?". The naive block +// regex this replaces had no idea what a string literal is: it opened a phantom +// comment at a block-comment opener sitting INSIDE a string and ran to the next +// terminator far below, deleting live code on 5 of this app's 91 sources. +// `stripComments` (not `maskComments`) is the projection this file wants -- the +// one guard below reports bare file paths, never a line or an offset. The +// `.mjs` specifier is deliberate; `scripts/js-comment-mask.d.mts` beside it is a +// hand-written declaration, so this import needs no `allowJs`. +import { stripComments } from '../../../scripts/js-comment-mask.mjs'; import stack from '../objectstack.config.js'; import { PLATFORM_CAPABILITY_NAMES } from '@objectstack/spec/security'; import { FILE_REFERENCE_TYPES, valueSchemaFor } from '@objectstack/spec/data'; @@ -44,15 +53,10 @@ function sourceFiles(dir: string = SRC_ROOT): string[] { * Source text with comments removed, so a source-scan guard judges CODE. * Documentation must stay free to name a retired key (this file's own comments * do, and so do the ones explaining the rename) without tripping the guard that - * bans authoring it. Block comments go first; then whole-line `//` comments — - * never a trailing `//`, which would eat the `//` in a URL inside a string. + * bans authoring it. */ function codeOf(file: string): string { - return readFileSync(file, 'utf8') - .replace(/\/\*[\s\S]*?\*\//g, '') - .split('\n') - .filter((line: string) => !line.trimStart().startsWith('//')) - .join('\n'); + return stripComments(readFileSync(file, 'utf8')); } /** Every `functions` entry, whichever spelling it was authored in. */ diff --git a/packages/cli/src/utils/console-route-ledger.conformance.test.ts b/packages/cli/src/utils/console-route-ledger.conformance.test.ts index 3eb3b24848..8aba4c8c0b 100644 --- a/packages/cli/src/utils/console-route-ledger.conformance.test.ts +++ b/packages/cli/src/utils/console-route-ledger.conformance.test.ts @@ -24,6 +24,19 @@ import { readdirSync, readFileSync, statSync } from 'node:fs'; import { dirname, join, relative, sep } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, it, expect } from 'vitest'; +// The repo's ONE answer to "is this span a comment, or code?" — its header +// carries the two private-stripper families that drifted apart and the +// parser-differential sweep that measured which way each fails. The private +// scanner this replaces was string-aware but REGEX-BLIND: the doubled slash +// closing `/^https?:\/\//i` read as a line-comment opener and took the rest of +// the line with it, which is the same defect #12398 found live in two sibling +// guards. `stripComments` (not `maskComments`) is the projection this file +// wants: it deletes comment characters but keeps every newline, so the +// `file:line` every finding here reports still points at the real line, and +// nothing in this file reports an offset. The `.mjs` specifier is deliberate; +// `scripts/js-comment-mask.d.mts` beside it is a hand-written declaration, so +// this import needs no `allowJs`. +import { stripComments } from '../../../../scripts/js-comment-mask.mjs'; import { CONSOLE_ROUTE_LEDGER } from './console-route-ledger.js'; /** @@ -59,49 +72,6 @@ const NON_ROUTE_MEMBERS = new Set(['use', 'notFound', 'onError', 'fire', 'fetch' // Scanning machinery // --------------------------------------------------------------------------- -/** - * Strip comments before scanning, PRESERVING newlines inside block comments so - * every finding's `file:line` points at the real line — `console.ts` opens with - * a 35-line header, and reporting a mount 35 lines short makes an accurate - * finding read as a wrong one. - */ -export function stripComments(source: string): string { - let out = ''; - let i = 0; - while (i < source.length) { - const c = source[i]; - const next = source[i + 1]; - if (c === '/' && next === '/') { - while (i < source.length && source[i] !== '\n') i++; - continue; - } - if (c === '/' && next === '*') { - i += 2; - while (i < source.length && !(source[i] === '*' && source[i + 1] === '/')) { - if (source[i] === '\n') out += '\n'; - i++; - } - i += 2; - continue; - } - if (c === '\'' || c === '"' || c === '`') { - const quote = c; - out += c; - i++; - while (i < source.length) { - if (source[i] === '\\') { out += source.slice(i, i + 2); i += 2; continue; } - out += source[i]; - if (source[i] === quote) { i++; break; } - i++; - } - continue; - } - out += c; - i++; - } - return out; -} - /** Module-scope `const NAME = '';` bindings, exported or not. */ export function constantBindings(code: string): Map { const out = new Map(); @@ -380,13 +350,29 @@ describe('cli console route ledger hygiene', () => { }); describe('scan machinery, pinned in both directions', () => { - it('the comment stripper drops prose paths, keeps code paths, and preserves line numbers', () => { + it('the shared stripper drops prose paths, keeps code paths, and preserves line numbers', () => { + // Not a re-pin of `js-comment-mask.mjs` -- that module pins its own + // behaviour. This pins the PROPERTY this census rests on: comment + // characters go, every newline stays, so `lineOf()` below still counts + // the real line. const stripped = stripComments("// app.get('/ghost', h)\n/* a\nb */\napp.get(`/real`, h);\n"); expect(stripped).not.toContain('ghost'); expect(stripped).toContain('/real'); expect(censusOf(['f.ts'], () => "/* a\nb\nc */\napp.get('/x', h);\n").routes[0].line).toBe(4); }); + it('a doubled slash inside a REGEX LITERAL does not swallow the rest of its line', () => { + // The defect the private scanner this file used to carry was measured + // committing on 7 of this package's 110 sources: string-aware but + // regex-blind, it read the `//` that CLOSES `/^https?:\/\//i` as a + // line-comment opener and deleted to end of line. A mount sharing that + // line went with it, and the census reported clean over text it never + // read. Live in `commands/dev.ts`, `commands/serve.ts` and + // `commands/start.ts` at conversion time. + const stripped = stripComments("const ok = /^https?:\\/\\//i.test(u); app.get('/real', h);\n"); + expect(stripped).toContain('/real'); + }); + it('resolves the spellings this package uses, and refuses the rest', () => { const b = constantBindings("export const CONSOLE_PATH = '/_console';\n"); expect(b.get('CONSOLE_PATH')).toBe('/_console'); diff --git a/packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts b/packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts index bfd44646b2..bb5491a4d9 100644 --- a/packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts +++ b/packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts @@ -43,6 +43,14 @@ import { readFileSync, readdirSync } from 'node:fs'; import { SqlDriver } from '../src/index.js'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; +// The repo's ONE answer to "is this span a comment, or code?". The naive block +// regex this replaces had no idea what a string literal is: in +// `logger-receiver-detach.test.ts` a fixture STRING quotes a docblock and the +// sweep ate the string. `stripComments` (not `maskComments`) is the projection +// this file wants -- the guard below reports bare file names, never a line or +// an offset. The `.mjs` specifier is deliberate; `scripts/js-comment-mask.d.mts` +// beside it is a hand-written declaration, so this import needs no `allowJs`. +import { stripComments } from '../../../../scripts/js-comment-mask.mjs'; import { LIVE_SCHEMA_PREFIX, MYSQL_CELL, @@ -127,11 +135,9 @@ describe('live-dialect matrix — per-file schema isolation (#9350)', () => { }); describe('live-dialect matrix — the cell is the only route to a live server (#9350)', () => { - /** Source with line and block comments removed, so prose about the env var is not a hit. */ + /** Source with comments removed, so prose about the env var is not a hit. */ const codeOf = (file: string): string => - readFileSync(join(SRC_DIR, file), 'utf8') - .replace(/\/\*[\s\S]*?\*\//g, '') - .replace(/^[ \t]*\/\/.*$/gm, ''); + stripComments(readFileSync(join(SRC_DIR, file), 'utf8')); /** * The needle is ASSEMBLED rather than written as a literal. diff --git a/packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts b/packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts index 12bf8bfcdc..8d0ef4db7c 100644 --- a/packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts +++ b/packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts @@ -42,6 +42,16 @@ import { describe, it, expect } from 'vitest'; import { readFileSync, existsSync, readdirSync } from 'node:fs'; import { dirname, join, relative, resolve } from 'node:path'; +// The repo's ONE answer to "is this span a comment, or code?". The private +// scanner this replaces tracked the three string forms but was REGEX-BLIND, so +// the doubled slash closing a literal like `/^https?:\/\//i` read as a +// line-comment opener and took the rest of the line -- `auth-manager.ts` in this +// very package was losing that line. `stripComments` (not `maskComments`) is the +// projection this file wants: every finding reports a package-relative FILE PATH +// and a specifier, never a line or an offset into the original. The `.mjs` +// specifier is deliberate; `scripts/js-comment-mask.d.mts` beside it is a +// hand-written declaration, so this import needs no `allowJs`. +import { stripComments } from '../../../../scripts/js-comment-mask.mjs'; /** * Seeded from `__dirname`, not from a `findUp` walk of `process.cwd()`, and not @@ -109,51 +119,6 @@ const SRC = HERE; const RUNTIME_SRC = resolve(REPO, 'packages/runtime/src'); const SERVICE_SMS_SRC = resolve(REPO, 'packages/services/service-sms/src'); -/** - * Strip comments before scanning. The distinction this file turns on — a - * `import type` versus a value `import` of the same specifier — is invisible to - * a raw-text regex the moment a doc comment quotes an import line, and this - * module's own header quotes several. Handles `//`, block comments and the - * three string forms so a `'http://…'` literal is not mistaken for a comment. - */ -function stripComments(src: string): string { - let out = ''; - let i = 0; - while (i < src.length) { - const c = src[i]!; - const next = src[i + 1]; - if (c === '/' && next === '/') { - while (i < src.length && src[i] !== '\n') i++; - continue; - } - if (c === '/' && next === '*') { - i += 2; - while (i < src.length && !(src[i] === '*' && src[i + 1] === '/')) i++; - i += 2; - continue; - } - if (c === "'" || c === '"' || c === '`') { - out += c; - i++; - while (i < src.length && src[i] !== c) { - if (src[i] === '\\') { - out += src[i]! + (src[i + 1] ?? ''); - i += 2; - continue; - } - out += src[i]; - i++; - } - out += c; - i++; - continue; - } - out += c; - i++; - } - return out; -} - interface Ref { spec: string; /** `import type … from` / `export type … from` — erased at build, costs nothing at runtime. */ diff --git a/packages/runtime/src/error-envelope.conformance.test.ts b/packages/runtime/src/error-envelope.conformance.test.ts index f63a0460fd..a4338be8f7 100644 --- a/packages/runtime/src/error-envelope.conformance.test.ts +++ b/packages/runtime/src/error-envelope.conformance.test.ts @@ -27,6 +27,16 @@ import { describe, it, expect, vi } from 'vitest'; import { readFileSync } from 'node:fs'; +// The repo's ONE answer to "is this span a comment, or code?". The naive pair +// this replaces had an UNANCHORED trailing arm, so a doubled slash anywhere on a +// line opened a phantom comment and the rest of the line went -- measured +// eating route paths inside template literals in `dispatcher-plugin.ts` and an +// https URL that `domains/mcp.ts` builds. `stripComments` (not `maskComments`) +// is the projection these guards want: they report match counts and matched +// text, never a line or an offset. The `.mjs` specifier is deliberate; +// `scripts/js-comment-mask.d.mts` beside it is a hand-written declaration, so +// this import needs no `allowJs`. +import { stripComments } from '../../../scripts/js-comment-mask.mjs'; import { ApiErrorSchema, BaseResponseSchema, @@ -455,9 +465,7 @@ describe('#3842 — no dispatcher module may reintroduce the drift', () => { // Comments stripped first: these modules' own prose quotes the old shape, // and a doc comment is not a code path. const read = (file: string) => - readFileSync(new URL(file, import.meta.url), 'utf8') - .replace(/\/\*[\s\S]*?\*\//g, '') - .replace(/\/\/[^\n]*/g, ''); + stripComments(readFileSync(new URL(file, import.meta.url), 'utf8')); /** Every module that can put a body on this wire surface. */ const MODULES = [ diff --git a/scripts/check-comment-mask-adoption.mjs b/scripts/check-comment-mask-adoption.mjs index 745618045f..52d1b3abf4 100644 --- a/scripts/check-comment-mask-adoption.mjs +++ b/scripts/check-comment-mask-adoption.mjs @@ -80,9 +80,10 @@ * `packages/cloud-connection/src/marketplace-proxy-plugin.ts` (38 bytes, inside * a declared MOUNT SOURCE). That is the naive-`//` family this gate exists for, * found live rather than argued from the shape. `packages/cli/src/utils/ - * console-route-ledger.conformance.test.ts` is the fourth guard in that family - * and stays recorded: its population limb already stripped, so it was out of - * that card's scope and nobody has re-read its scanner. + * console-route-ledger.conformance.test.ts` was the fourth guard in that family + * and was left recorded here because its population limb already stripped; the + * THIRD SHRINK below converts it, and its scanner was committing the same + * defect on 7 of that package's 110 sources. * * ## SECOND ROUND (#12475): every remaining row now carries a MEASUREMENT * @@ -277,8 +278,6 @@ const VERDICTS = new Set(['unconverted', 'specimen']); * one private stripper for another has not been re-read by anyone. */ const LEDGER = new Map([ - ['examples/app-showcase/test/inert-wirings.test.ts', - { shapes: ['regex-block'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (codeOf() across 91 .ts files under examples/app-showcase/src): DELETES LIVE CODE on 9 of them, 5,987 chars, because the block regex opens a phantom comment at a block-comment opener sitting inside a string literal -- the accept-glob strings in ui/actions/index.ts and the prose strings in coverage.ts -- and runs to the next terminator far below; it also KEEPS 1,813 chars of trailing line-comment prose, which its line filter deliberately never removes. Its own retired-retryDelayMs offender set is empty under both strippers today' }], ['packages/cli/src/commands/artifact-child-env.pin.test.ts', { shapes: ['regex-block'], verdict: 'unconverted', why: 'MEASURED #12475: NOT a stripper feeding a scan. This file\'s guard is ts.createSourceFile; the one block-regex call is a 7-line inline SPECIMEN asserting the text scan it replaced reports that specimen clean, and it does -- 112 chars of live code deleted at the block-comment opener inside the route-wildcard string. Recorded as unconverted only because a shape scan cannot tell a negative control from a caller; converting it would delete the evidence, exactly as for the specimen row below' }], ['packages/cli/src/commands/migrate/multi-value-columns.no-auto-run.test.ts', @@ -289,12 +288,8 @@ const LEDGER = new Map([ { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (the two brace-matched interface BODIES interfaceFields() strips -- ResolvedMultiNodeVerdict in packages/services/service-cluster/src/multi-node-gate.ts and MultiNodeGateVerdict in commands/serve.ts, 1,462 chars): agrees with the shared mask. Narrow by construction rather than by luck -- a TSDoc-only interface slice carries no regex literal and no string holding a comment opener. The rest of the file already imports maskComments' }], ['packages/cli/src/commands/serve-verify-security-parity.contract.test.ts', { shapes: ['regex-block', 'regex-line'], verdict: 'specimen', why: 'the private two-regex strip this file carried until its conversion, kept as a NEGATIVE CONTROL that the shared mask beats it; converting it would delete the evidence' }], - ['packages/cli/src/utils/console-route-ledger.conformance.test.ts', - { shapes: ['scanner-decl'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (packageSourceFiles(), 110 non-test .ts under packages/cli/src, 1.6 MB): DELETES LIVE CODE on 9 of them, 10,263 chars. Same defect and same spelling as the two the FIRST SHRINK found -- string-aware but REGEX-BLIND, so the doubled slash inside /^https?://i reads as a line-comment opener and the rest of the line goes: dev.ts:169, serve.ts:3021, start.ts:460 and start.ts:475 are live sites today. It also KEEPS 35,382 chars of block-comment prose swallowed by phantom STRINGS opened at a quote character inside a regex class. This is the fourth guard in that family, the one #12398 left recorded; its MOUNT_SHAPED identity is still exactly [utils/console.ts] under both strippers, so the blindness has not yet cost it a finding' }], ['packages/create-objectstack/src/template-registry.test.ts', { shapes: ['regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (packages/create-objectstack/src/index.ts): deletes no live code -- it has no block arm at all -- and therefore KEEPS 2,744 chars of block-comment prose, the FABRICATION direction. Its line arm is anchored and reaches only whole-line comments' }], - ['packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts', - { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (codeOf() across all 152 *.test.ts in packages/drivers/driver-sql/src, 2.0 MB): 54 files disagree. Mostly the safe direction -- 4,582 chars of trailing line-comment prose KEPT by the anchored line arm -- but the block arm also DELETES 228 chars of live code in logger-receiver-detach.test.ts, where a fixture STRING quotes a docblock and the naive block regex eats the string. Its own direct-OS_TEST_URL offender set is empty under both strippers' }], ['packages/lint/src/validate-expressions.test.ts', { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (packages/lint/src/validate-expressions.ts, 75,403 chars): agrees with the shared mask. The unanchored trailing line arm is the dangerous spelling and it holds here only because that one rule source happens to carry no doubled slash inside a regex literal or string -- a property of today\'s file, not of this stripper' }], ['packages/lint/src/validate-org-axis-red-lines.test.ts', @@ -307,12 +302,8 @@ const LEDGER = new Map([ { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (codeOf() across all 11 *.test.ts in packages/metadata-protocol/src/migrations): deletes no live code; KEEPS 130 chars of trailing line-comment prose on 4 files, the FABRICATION direction. LIVE_TEST_FILES resolves to the same three files under both strippers, so the derived population this file guards is unaffected today' }], ['packages/plugins/plugin-approvals/src/admin-exemption-retired.test.ts', { shapes: ['scanner-decl'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (collectSources(), 25 non-test .ts under packages/plugins/plugin-approvals/src, 463 KB): agrees with the shared mask. It is REGEX-BLIND like its plugin-auth twin below and survives only because this package\'s sources carry no regex literal holding a quote character and no doubled slash inside one -- a fact about the population, not about the scanner' }], - ['packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts', - { shapes: ['scanner-decl'], verdict: 'unconverted', why: 'MEASURED #12475 over 423 files / 7.3 MB -- every .ts under plugin-auth/src as a declared SUPERSET of the import-graph limb (whose exact set is resolver-dependent), plus the two cross-package roots it reads whole, packages/runtime/src and packages/services/service-sms/src. 15 files disagree: DELETES 6,375 chars of live code, the regex-blind doubled-slash defect again (auth-manager.ts loses the line holding /^https?://i), and KEEPS 22,329 chars of comment prose behind phantom strings. Its own extracted import refs are identical under both strippers on every one of the 423 files, so the reachability verdict has not moved' }], ['packages/qa/downstream-contract/test/source-resolution.pin.test.ts', { shapes: ['regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (packages/qa/downstream-contract/tsconfig.json, 5,145 chars of JSONC): agrees with the shared mask. An anchored line arm over a file with no regex literals and no block comments -- the narrowest population in this ledger' }], - ['packages/runtime/src/error-envelope.conformance.test.ts', - { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (the 10 modules in its own MODULES list, 390 KB): DELETES 166 chars of live code on 2 of them and keeps nothing -- purely the blind direction. The unanchored trailing line arm eats route paths inside template literals: dispatcher-plugin.ts loses the rest of the line at a doubled slash in a mount path, domains/mcp.ts at the one in an https URL it builds. All four of its own per-module counts are identical under both strippers today, so the numeric-code pin has not yet been cheated' }], ['packages/spec/scripts/lazify-schemas.ts', { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (listZodFiles(), 208 *.zod.ts under packages/spec/src, 4.4 MB): the two COMMENT arms of the import-block matcher claim no character the shared scanner calls code, on any file. Sound by construction rather than by luck: both arms are anchored to a line start after indent only, and the matcher runs at the file top where no template literal can be open. (First measured as 208/208 disagreeing -- that was the instrument counting the newline each arm consumes; see the SECOND ROUND note.)' }], ]); diff --git a/scripts/cross-package-test-inputs.mjs b/scripts/cross-package-test-inputs.mjs index bf36177d13..b005cc1b07 100644 --- a/scripts/cross-package-test-inputs.mjs +++ b/scripts/cross-package-test-inputs.mjs @@ -532,6 +532,16 @@ export const CROSS_PACKAGE_TEST_INPUTS = { 'scripts/check-published-files.mjs', 'scripts/check-cross-package-test-inputs.mjs', 'packages/types/src/node-isolation.test.ts', + // That same test imports `stripComments` from `js-comment-mask.mjs` to + // separate code from prose in the 423 sources it walks -- the conversion + // #12398 began. Unlike the three mentions above this is a real coupling + // rather than a scanner artefact: the import refs the scan extracts, and + // therefore its reachability verdict, are a function of the module's + // scanning behaviour. The `.d.mts` sibling is declared alongside it + // because it is what gives `stripComments` its type, so this package's + // typecheck verdict is a function of it too. + 'scripts/js-comment-mask.mjs', + 'scripts/js-comment-mask.d.mts', ], heldBy: { // The pair #10566 was measured on. That test's walk of `PACKAGES_DIR` @@ -679,6 +689,50 @@ export const CROSS_PACKAGE_TEST_INPUTS = { 'packages/spec/src/**': ['packages/qa/downstream-contract/test/source-resolution.pin.test.ts'], }, }, + '@objectstack/runtime': { + // src/error-envelope.conformance.test.ts imports `stripComments` from + // `js-comment-mask.mjs` to decide which text in the ten dispatcher modules + // it scans is a comment and which emits an error body -- the conversion + // #12398 began. The coupling is real: the four per-module counts that guard + // reports are a function of the module's scanning behaviour, so a change to + // it has to re-run this package's suite. The `.d.mts` sibling is declared + // alongside it because it is what gives `stripComments` its type, so this + // package's typecheck verdict is a function of it too. + globs: [ + 'scripts/js-comment-mask.mjs', + 'scripts/js-comment-mask.d.mts', + ], + }, + '@objectstack/driver-sql': { + // src/live-dialect-matrix.isolation.test.ts imports `stripComments` from + // `js-comment-mask.mjs` to decide which text in this package's 152 test + // sources is a comment and which is a direct `OS_TEST_*_URL` read -- the + // conversion #12398 began. The coupling is real: that guard's offender set + // is a function of the module's scanning behaviour, so a change to it has to + // re-run this package's suite. The `.d.mts` sibling is declared alongside it + // because it is what gives `stripComments` its type, so this package's + // typecheck verdict is a function of it too. + globs: [ + 'scripts/js-comment-mask.mjs', + 'scripts/js-comment-mask.d.mts', + ], + }, + '@objectstack/example-showcase': { + // test/inert-wirings.test.ts imports `stripComments` from + // `js-comment-mask.mjs` to decide which text under this app's `src/` is a + // comment and which is an authored wiring -- the same conversion #12398 + // made for the route-ledger guards. The coupling is real: that guard's + // offender set is a function of the module's scanning behaviour, so a + // change to it has to re-run this package's suite. The `.d.mts` sibling is + // declared alongside it because it is what gives `stripComments` its type, + // so this package's typecheck verdict is a function of it too -- the reason + // the `@objectstack/cli` entry above declares the pair rather than the + // module alone. + globs: [ + 'scripts/js-comment-mask.mjs', + 'scripts/js-comment-mask.d.mts', + ], + }, 'create-objectstack': { // src/template-consistency.test.ts reads doc frontmatter by repo-relative // path to decide which templates are internal. diff --git a/turbo.json b/turbo.json index 2ac1225814..a995a3adae 100644 --- a/turbo.json +++ b/turbo.json @@ -176,6 +176,42 @@ "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" ] }, + "@objectstack/runtime#test": { + "dependsOn": ["^build"], + "outputs": [], + "inputs": [ + "$TURBO_DEFAULT$", + "!dist/**", + "!coverage/**", + "!.turbo/**", + "$TURBO_ROOT$/scripts/js-comment-mask.mjs", + "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" + ] + }, + "@objectstack/driver-sql#test": { + "dependsOn": ["^build"], + "outputs": [], + "inputs": [ + "$TURBO_DEFAULT$", + "!dist/**", + "!coverage/**", + "!.turbo/**", + "$TURBO_ROOT$/scripts/js-comment-mask.mjs", + "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" + ] + }, + "@objectstack/example-showcase#test": { + "dependsOn": ["^build"], + "outputs": [], + "inputs": [ + "$TURBO_DEFAULT$", + "!dist/**", + "!coverage/**", + "!.turbo/**", + "$TURBO_ROOT$/scripts/js-comment-mask.mjs", + "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" + ] + }, "@objectstack/plugin-auth#test": { "dependsOn": ["^build"], "outputs": [], @@ -190,7 +226,9 @@ "$TURBO_ROOT$/packages/services/service-sms/src/**", "$TURBO_ROOT$/scripts/check-published-files.mjs", "$TURBO_ROOT$/scripts/check-cross-package-test-inputs.mjs", - "$TURBO_ROOT$/packages/types/src/node-isolation.test.ts" + "$TURBO_ROOT$/packages/types/src/node-isolation.test.ts", + "$TURBO_ROOT$/scripts/js-comment-mask.mjs", + "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" ] }, "@objectstack/plugin-auth#typecheck": {