Problem
Test files are outside every app's tsconfig.json include, so webjs typecheck never reads them and a type error in a test is invisible until a human notices it.
website/tsconfig.json:16 includes app/**/*, components/**/*, lib/**/*, modules/**/* and nothing else, while 25 .ts files live under website/test/. npm run typecheck in website/ therefore reports success on a tree where a test file could be arbitrarily broken.
This is not hypothetical. During review of #1259 (PR #1295) a helper was written const quoted = (key) => ... in website/test/ssr/docs-links.test.ts, an implicitly-any parameter in a .ts file. Root AGENTS.md says plainly that "any has no carve-out at all", strict: true is set in that same tsconfig, and every sibling arrow in the file is annotated. Nothing caught it. It survived until a reviewer read the line by eye, and it would have merged otherwise.
The gap is systemic, not specific to website/. All four in-repo apps and the scaffold share the shape:
| Config |
include |
website/tsconfig.json:16 |
app, components, lib, modules |
examples/blog/tsconfig.json |
app, components, modules, lib, middleware.* |
docs/tsconfig.json |
app, middleware.ts |
packages/ui/packages/website/tsconfig.json |
app, middleware.ts |
packages/cli/lib/create.js:537-543 |
app, components, modules, lib, middleware.* |
The last row is the one that matters most: every app webjs create generates ships this gap, so an app author who follows the scaffold's own testing guidance gets no type checking on the tests they write.
Design / approach
Add the test directory to the include of each app tsconfig, and to the array the scaffold generator emits.
The obvious objection is that test files import node:test and node:assert, and may use shapes the app config is strict about. So this is likely to surface a batch of pre-existing errors on first run. That is the point of filing it rather than doing it as a drive-by: the fix is one line per config, and the work is whatever tsc then reports.
Two decisions the implementer should make deliberately rather than by default:
- One config or two. Extending each app's
include is the simplest thing and keeps one webjs typecheck command honest. A separate tsconfig.test.json extending the base is the alternative, and is worth it only if the tests genuinely need looser settings than the app. Prefer the single config until something forces the split, since a second config that nobody runs reproduces this bug in a new place.
- Whether
checkJs matters. The app configs set allowJs: true, checkJs: false, so .js tests would be parsed but not checked even after inclusion. Browser tests under website/test/components/browser/ are .js. Decide whether those are in scope or explicitly left out, and say which in the config comment.
Implementation notes (for the implementing agent)
Where to edit:
website/tsconfig.json L16, the include array.
examples/blog/tsconfig.json, docs/tsconfig.json, packages/ui/packages/website/tsconfig.json, same field.
packages/cli/lib/create.js L537-543, the include array the scaffold writes. This is a generator emitting a literal, so the change is to the array, and it must be verified by generating an app rather than by reading the diff.
Landmines:
- Expect a wall of errors on the first run, and read them before "fixing" them. These files have never been type-checked, so some errors will be real bugs (like the implicit
any above) and some will be the config being wrong for test code. Do not blanket-add any or @ts-expect-error to make it green; that converts an invisible gap into a visible lie.
website/package.json runs node scripts/copy-registry.mjs before typecheck, because modules/ui/components/ is a gitignored mirror. Run npm run typecheck, not a bare tsc, or you will get phantom missing-module errors.
erasableSyntaxOnly: true is set in these configs (AGENTS.md invariant 10). Any fix must stay erasable: no enum, no value namespace, no constructor parameter properties.
- The scaffold's
include is also read by editor tooling and @webjsdev/intellisense, so widening it changes what the editor checks in a generated app. That is the desired outcome, but it means the scaffold change needs the generate-and-boot verification, not just a unit assertion.
website/test/fixtures/ may hold deliberately-malformed files. Check before including it wholesale; it may need an exclude entry.
Invariants to respect:
- Root
AGENTS.md: "any has no carve-out at all", and the derive-the-type rule. The whole point of this issue is to make that enforceable rather than aspirational.
- AGENTS.md invariant 10 (erasable TypeScript only).
Tests + docs surfaces:
test/scaffolds/** covers generated-app shape; assert the emitted include contains the test dir.
- Verify by generating an app and running
webjs typecheck in it, per the scaffold rule in AGENTS.md (generators emit strings, so an escaping or shape bug only shows in a freshly generated app).
website/AGENTS.md and the per-app AGENTS.md files if they describe what typecheck covers.
references/testing.md and references/typescript.md in the skill, if either states what is type-checked.
- The docs site page for testing or TypeScript, if it makes the same claim.
Acceptance criteria
Problem
Test files are outside every app's
tsconfig.jsoninclude, sowebjs typechecknever reads them and a type error in a test is invisible until a human notices it.website/tsconfig.json:16includesapp/**/*,components/**/*,lib/**/*,modules/**/*and nothing else, while 25.tsfiles live underwebsite/test/.npm run typecheckinwebsite/therefore reports success on a tree where a test file could be arbitrarily broken.This is not hypothetical. During review of #1259 (PR #1295) a helper was written
const quoted = (key) => ...inwebsite/test/ssr/docs-links.test.ts, an implicitly-anyparameter in a.tsfile. RootAGENTS.mdsays plainly that "anyhas no carve-out at all",strict: trueis set in that same tsconfig, and every sibling arrow in the file is annotated. Nothing caught it. It survived until a reviewer read the line by eye, and it would have merged otherwise.The gap is systemic, not specific to
website/. All four in-repo apps and the scaffold share the shape:includewebsite/tsconfig.json:16app,components,lib,modulesexamples/blog/tsconfig.jsonapp,components,modules,lib,middleware.*docs/tsconfig.jsonapp,middleware.tspackages/ui/packages/website/tsconfig.jsonapp,middleware.tspackages/cli/lib/create.js:537-543app,components,modules,lib,middleware.*The last row is the one that matters most: every app
webjs creategenerates ships this gap, so an app author who follows the scaffold's own testing guidance gets no type checking on the tests they write.Design / approach
Add the test directory to the
includeof each app tsconfig, and to the array the scaffold generator emits.The obvious objection is that test files import
node:testandnode:assert, and may use shapes the app config is strict about. So this is likely to surface a batch of pre-existing errors on first run. That is the point of filing it rather than doing it as a drive-by: the fix is one line per config, and the work is whatevertscthen reports.Two decisions the implementer should make deliberately rather than by default:
includeis the simplest thing and keeps onewebjs typecheckcommand honest. A separatetsconfig.test.jsonextending the base is the alternative, and is worth it only if the tests genuinely need looser settings than the app. Prefer the single config until something forces the split, since a second config that nobody runs reproduces this bug in a new place.checkJsmatters. The app configs setallowJs: true, checkJs: false, so.jstests would be parsed but not checked even after inclusion. Browser tests underwebsite/test/components/browser/are.js. Decide whether those are in scope or explicitly left out, and say which in the config comment.Implementation notes (for the implementing agent)
Where to edit:
website/tsconfig.jsonL16, theincludearray.examples/blog/tsconfig.json,docs/tsconfig.json,packages/ui/packages/website/tsconfig.json, same field.packages/cli/lib/create.jsL537-543, theincludearray the scaffold writes. This is a generator emitting a literal, so the change is to the array, and it must be verified by generating an app rather than by reading the diff.Landmines:
anyabove) and some will be the config being wrong for test code. Do not blanket-addanyor@ts-expect-errorto make it green; that converts an invisible gap into a visible lie.website/package.jsonrunsnode scripts/copy-registry.mjsbeforetypecheck, becausemodules/ui/components/is a gitignored mirror. Runnpm run typecheck, not a baretsc, or you will get phantom missing-module errors.erasableSyntaxOnly: trueis set in these configs (AGENTS.md invariant 10). Any fix must stay erasable: noenum, no valuenamespace, no constructor parameter properties.includeis also read by editor tooling and@webjsdev/intellisense, so widening it changes what the editor checks in a generated app. That is the desired outcome, but it means the scaffold change needs the generate-and-boot verification, not just a unit assertion.website/test/fixtures/may hold deliberately-malformed files. Check before including it wholesale; it may need anexcludeentry.Invariants to respect:
AGENTS.md: "anyhas no carve-out at all", and the derive-the-type rule. The whole point of this issue is to make that enforceable rather than aspirational.Tests + docs surfaces:
test/scaffolds/**covers generated-app shape; assert the emittedincludecontains the test dir.webjs typecheckin it, per the scaffold rule inAGENTS.md(generators emit strings, so an escaping or shape bug only shows in a freshly generated app).website/AGENTS.mdand the per-app AGENTS.md files if they describe whattypecheckcovers.references/testing.mdandreferences/typescript.mdin the skill, if either states what is type-checked.Acceptance criteria
website/tsconfig.jsonincludes the test directory, andnpm run typecheckinwebsite/type-checks the files underwebsite/test/examples/blog,docs, andpackages/ui/packages/websitepackages/cli/lib/create.jsemits anincludecovering the test directory, verified by generating an app and runningwebjs typecheckin itanyor@ts-expect-erroranyparameter added to a test file makeswebjs typecheckfail, and it passes once annotatedtest/scaffolds/**assertion covers the generatedinclude