Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/lint.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -172,6 +172,32 @@ jobs:
- name: scripts/ entry guards go through one predicate
run: pnpm check:entry-guard

# Every `scripts/**` TypeScript parse goes through ONE module (#10133 /
# #10573), and this is the half that keeps it that way (#10574).
# NONE of the three parser entry points throws on a source it cannot
# read: `ts.createSourceFile` returns a tree built by error recovery with
# the errors parked on `parseDiagnostics`; `ts.createProgram` parks them
# behind a second call, `getSyntacticDiagnostics()`; `ts.transpileModule`
# reports nothing at all without `reportDiagnostics: true` and still
# hands back an `outputText`. A gate then walks the wreckage, finds none
# of the shapes it is looking for, and scores the file CLEAN — so the
# SYMPTOM OF A MISSING REFUSAL IS A GREEN LINE, and an unguarded gate is
# indistinguishable from a guarded one by reading CI. That is not a
# theory: one gate here forced `ScriptKind.TSX` on 2504 test files, read
# 32 of them as wreckage, and printed `OK` while six pinned engine
# doubles went uncounted.
# The #10573 sweep converted 32 call sites across 15 gates; it could not
# stop the sixteenth being typed, and within the hour a new gate landed
# with two raw calls in it — caught by this step, which is the whole
# argument for having it. Same shape as `check:entry-guard` above.
# Also prints the parses OUTSIDE `scripts/**` (#10575) that it does not
# govern, so its green line is read as a claim about `scripts/` and not
# about the repository.
# Scans ~121 scripts/ files plus a read-only census of the rest, no
# spawns; ~0.6s.
- name: scripts/ TypeScript parses go through one module
run: pnpm check:parse-guard

# Stack-collection enumerations vs the schema (#6242). `stack.zod.ts`
# decides which collections a stack may declare; eight other enumerations
# of that same set are hand-maintained (the map-format list, the
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -35,6 +35,7 @@
"check:app-nav-i18n": "pnpm --filter @objectstack/cli run check:app-nav-i18n",
"check:nul-bytes": "node scripts/check-nul-bytes.mjs --self-test && node scripts/check-nul-bytes.mjs",
"check:entry-guard": "node scripts/check-entry-guard.mjs --self-test && node scripts/check-entry-guard.mjs",
"check:parse-guard": "node scripts/check-parse-guard.mjs --self-test && node scripts/check-parse-guard.mjs",
"check:stack-collection-maps": "node scripts/check-stack-collection-maps.mjs --self-test && node scripts/check-stack-collection-maps.mjs",
"check:doc-authoring": "node scripts/check-doc-authoring.mjs --self-test && node scripts/check-doc-authoring.mjs",
"check:doc-anchors": "node scripts/check-doc-anchors.mjs --self-test && node scripts/check-doc-anchors.mjs",
Expand Down
13 changes: 11 additions & 2 deletions scripts/check-optional-error-sink-contract.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,6 +164,8 @@ import { dirname, join, relative, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
import ts from 'typescript';

import { parseSourceFile } from './ts-parse.mjs';

const HERE = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(HERE, '..');
const BASELINE_PATH = join(HERE, 'optional-error-sink-contract.baseline.json');
Expand DownExpand Up@@ -489,7 +491,14 @@ function run({ list = false } = {}) {
// this regex is exactly how the first draft of this population read a
// clean tree while missing both audit sinks.
if (!/\berror\s*\??\s*[:(]/.test(text)) continue;
const sf = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
// `parseSourceFile` rather than the raw call: ts.createSourceFile never
// throws, so a `.ts` file with a syntax error would be walked as a
// recovered partial tree, contribute nothing to the census, and be
// scored as a file with no sinks to report. scriptKind is OMITTED —
// `collectSourceFiles` yields `.ts` only, so the file name infers
// exactly what the forced `ScriptKind.TS` used to say, and forcing
// one is its own blind spot (see scripts/ts-parse.mjs).
const sf = parseSourceFile(file, text);
analyzeSourceFile(sf, relative(ROOT, file).split(sep).join('/'), census);
}
}
Expand DownExpand Up@@ -656,7 +665,7 @@ function selfTest() {

let failures = 0;
for (const c of cases) {
const sf = ts.createSourceFile('t.ts', c.code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
const sf = parseSourceFile('t.ts', c.code);
const census = emptyCensus();
analyzeSourceFile(sf, 't.ts', census);
const verdicts = census.sinks.map((s) => s.verdict);
Expand Down
Loading
Loading