ReviewOS

stacks/stx

stx typecheck: a glob reports 0 errors for a file that reports 46 on its own — the multi-file path finds no type errors at all

#1924
Closed glennmichael123 opened this 14 days ago · 1 comment
14 days ago

stx typecheck reports errors for a single file and zero for a glob that contains that same file. A project checking itself the documented way — stx typecheck with a pattern — gets a clean bill of health while real type errors sit in its client blocks.

Repro

Plant one obvious error in a <script client> block:

<script client>
const probe: number = "definitely not a number"

Then check the same file two ways, clearing .stx/cache between runs:

$ stx typecheck resources/views/dashboard.stx
  Checked 2 script block(s) and 54 template expression(s) in 1 file(s) — 46 error(s), 0 warning(s)

$ stx typecheck 'resources/**/*.stx'
  Checked 51 script block(s) and 357 template expression(s) in 38 file(s) — 0 error(s), 0 warning(s)

The glob run walks 38 files including that one, reports 51 script blocks — so it opened the block — and finds nothing. --no-client is not set. Same on resources/views/**/*.stx (49 blocks, 0 errors).

Reproduced on stx 0.2.175, bun 1.3.14, macOS. Not a caching artifact: rm -rf .stx/cache before each run, and the counts differ every time in the same direction.

Why it matters

The glob is the form a project actually uses — it is what the docs show, and it is what goes in CI. In this repo it reports 0 error(s) across 38 files, which I quoted as evidence in three commit messages before testing whether it could fail at all. It cannot: I could not construct a type error that a glob run would report.

The other half

Single-file mode is not a usable fallback, because it over-reports. On a component whose types come from elsewhere in the project it produces errors that are artifacts of checking in isolation:

AutofixPanel.stx:28:25  error TS2304  Cannot find name 'AutofixRun'.
AutofixPanel.stx:32:28  error TS2304  Cannot find name 'AutofixState'.

Those names resolve fine at runtime and in the glob run. It also reported Checked 1 script block(s) for a file with two, so it appears to skip the server block and check the client block without whatever the server block establishes.

So the two modes fail in opposite directions: the glob sees the blocks and reports nothing, single-file reports things that are not wrong. Between them there is currently no configuration that answers "does this project typecheck".

Note on warnings

Warnings behave correctly in glob mode, which is what makes the split visible. Removing one defineClientPayload takes a glob run from 0 warning(s) to 1 warning(s) and restoring it goes back — so the glob path is walking the blocks and producing diagnostics, just not error-level ones.

14 days ago

Both halves fixed, and the headline one was already fixed before you filed — in f85e31ac65, which shipped in v0.2.176. You were on 0.2.175.

Why the glob found nothing

Your clue was the one that solved it: "warnings behave correctly in glob mode." stx's own warnings are raised at code 0; tsc's diagnostics are not. So the pipeline was running and only tsc's output was vanishing.

tsc collects semantic diagnostics only when the program has no syntactic ones — program-wide, not per file (emitFilesAndReportErrors). Every generated buffer shares one program, so a single unparseable block anywhere in the run switches type checking off for all 38 files. Single-file mode had no unparseable sibling in scope, so it worked.

Reproduced against v0.2.175 with a two-file fixture — one file with a planted TS2322, one sibling whose client block cannot parse:

v0.2.175  single file  -> 1 error   (TS2322 reported)
v0.2.175  glob         -> 1 error   (TS1109 only — the TS2322 is gone)
main      glob         -> 2 errors  (both)

In your tree it read as 0 error(s) rather than 1 because the syntactic diagnostics landed in buffers whose output is filtered (a template buffer only surfaces expression-level diagnostics). Same mechanism, quieter symptom.

The fix drops the unparseable buffers and re-checks the rest, so you get both halves from one run instead of discovering the second only after fixing the first. Your "I could not construct a type error that a glob run would report" is now false, which is the property that matters.

The other half — single-file over-reporting

That was real and is fixed in 968a541, and it is not an isolation artifact.

AutofixPanel.stx:28:25  error TS2304  Cannot find name 'AutofixRun'

The payload projection wraps the server body in a scope function so each published value's type can be inferred from real code. That works for values and traps types: an interface AutofixRun declared beside the value it describes lives inside that function, so a client block annotating with it could not name it — and there was nothing you could add to fix it, because a type needs no payload entry. Top-level type declarations are now carried to the buffer's top level.

Depth-zero only, so a type declared inside a helper is not lifted out of the scope it belongs to, and skipped when the client block declares the same name so a local definition shadows rather than collides. My own test caught the depth bug — the first version lifted a type out of a function body.

One correction: your glob run did not resolve those names. It reported nothing at all, so Cannot find name was suppressed along with everything else. The two observations were the same bug seen from both ends.

On "Checked 1 script block(s) for a file with two"

I could not reproduce this — a file with a server and a client block reports 2 on both 0.2.175 and main. If you still see it, the file itself would help; a src= or a non-JS type= on the block would explain it, since both are deliberately skipped.

Verification: 7 tests for the type-carrying half, sabotage-checked (2 fail without it), plus the existing 4 pinning the retry.

Sign in to comment on this issue.