Fix the TypeScript build: make build fails with TS6059 on main
#3
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
make buildis broken onmainright now:tsconfig.json:8sets"rootDir": "./src"whiletsconfig.json:20sets"include": ["src/**/*", "bin/**/*"]. The two contradict each other.Nothing catches this:
make checkruns test, lint and fmt-check only (script/check), andthe Dockerfile runs
make checkbut nevermake build. So a repo whosepackage.jsondeclares
"main": "./dist/index.js"and"bin": {"quak": "./dist/bin/quak.js"}cannotproduce either artifact, and CI is blind to it.
The README's Getting Started block also tells users to run
yarn quak login,yarn quak collections, etc. There is noquakscript inpackage.json, so every one of thosecommands fails.
Definition of done
make buildsucceeds from a clean checkout and emits JavaScript plus declaration filesunder
dist/.package.jsonmain,typesandbinpoint at paths that the build actually produces,verified by inspecting
dist/after a build.#!/usr/bin/env nodeshebang frombin/quak.ts:1and is executable.
yarn quak <command>works afteryarn build, matching what the README's Getting Startedsection already promises — add the missing
quakscript topackage.json.make buildin addition tomake check, so this class of error cannever reach
mainagain. (Do not change whatmake checkitself runs — the repo policyfixes its composition as test + lint + fmt-check.)
make cleanstill removes everything the build produces.make checkgreen.TODO.mdupdated in the same commit.Recommended approach
Set
"rootDir": "."and updatepackage.jsonmain/types/binto the resultingdist/src/…anddist/bin/…paths. This is the smallest change and keeps the source layoutthe README documents.
The alternative — moving the CLI body into
src/cli.tsand leaving a thin shim inbin/—keeps
dist/index.jsas the package main but churns more files and contradicts the layoutdiagram in the README's Design section. Pick the first unless you find a concrete reason not
to, and record the reasoning in the PR body.
Out of scope
engines,exports, andprepublishOnly— tracked separately.Implementation requirements
Written against
mainat348f23b, after #1 and #2 landed.make checkis green there (18 testfiles, 210 tests, ~10s);
make buildis not.The contradiction
tsconfig.jsonsets"rootDir": "./src"and"include": ["src/**/*", "bin/**/*"]. Those cannotboth hold —
bin/quak.tsis outsiderootDir, hence TS6059. Reproduce it first viamake buildso you have the exact message in front of you before changing anything.
Nothing catches this today:
script/checkruns test, lint and fmt-check only, and the Dockerfileruns
make checkbut nevermake build. That is why a repo declaring"main": "./dist/index.js"and
"bin": {"quak": "./dist/bin/quak.js"}has been unable to produce either artifact.Take the recommended approach unless you find a real obstacle
Set
"rootDir": ".". Emitted layout becomesdist/src/…anddist/bin/…, so updatepackage.json:main→./dist/src/index.jstypes→./dist/src/index.d.tsbin.quak→./dist/bin/quak.jsThe alternative — moving the CLI body into
src/with a thin shim inbin/— keepsdist/index.jsas the package main but churns more files and contradicts the layout diagram inthe README's Design section. If you take it instead, justify it in the PR body with the specific
obstacle you hit.
Either way,
resolveJsonModuleis already on, and #5 (single-sourcing the version frompackage.json) will need to import it from the built output — so whatever layout you pick, makesure a relative import of
package.jsonwould resolve from bothsrc/under vitest and fromdist/after a build. You do not have to implement #5 here, but do not pick a layout that makesit impossible.
Verify the artifacts exist rather than assuming tsc emitted them
After
make build, confirm on disk:main,typesandbin.quakpoints at actually exists;#!/usr/bin/env nodeshebang frombin/quak.ts:1;.d.tsand.js.mapfiles are present for the library entrypoint.State the observed paths in the PR body. This repo has failed four reviews on claims that were
not checked; "the build succeeds" is not the same statement as "the declared artifacts exist".
The
quakscriptThe README's Getting Started block tells users to run
yarn quak login,yarn quak collectionsand so on. There is no
quakscript inpackage.json, so every one of those commands failstoday. Add it so the documented commands work after
yarn build. If the invocation you adddiffers from what the README shows, update the README to match — the two must agree at the end.
Dockerfile
Add
make buildto the Dockerfile so this class of error cannot reachmainagain. Do notchange what
make checkruns —REPO_POLICIES.mdfixes its composition as test + lint +fmt-check, and #4 is separately reworking the Dockerfile into a multi-stage build. Keep your change
minimal and additive so it does not collide with #4.
Note that #4 also documents a
script/cibuildcache defect: a baredocker build .can serveRUN make checkfrom cache and exit 0 without running anything. That is #4's to fix, not yours —but it means you must not treat a fast, green
docker buildas evidence your build step ran. Ifyou verify the Docker path at all, do it with
docker build --no-cache .scoped to this image.Never run
docker builder pruneordocker system prune— the BuildKit cache is shared acrossevery repo on this host and an agent elsewhere already destroyed ~41 GB of it doing exactly that.
make cleanIt must still remove everything the build now produces. If the output layout changes, update the
target.
Process
main. TDD applies as far as it sensibly can here: this is a build-configurationfix, so if you cannot write a meaningful failing test first, say so explicitly in the PR body
rather than staging a token one. A check that the declared
package.jsonentrypoints existafter a build is the closest thing to a real regression test and is worth adding.
maketargets /script/entrypoints. Never invoketsc,vitest,eslint,prettieroryarnscripts directly.make checkandmake buildmust both be green at the end. Report the measuredmake testtime — baseline ~10s at 210 tests, 20s budget, 30s hard cap.
.claude/worktrees/in thetree you are measuring. Per #25, vitest globs them and silently runs the suite N+1 times; a
polluted run reported 1050 tests where the real number is 210.
make fmt.TODO.mdand any README change go in the same commit as the implementation.(closes #3).Implementation plan
Reproduced first, on
348f23b, before touching anything.make bootstrapthenmake build:Baseline
make testin this tree (no worktrees nested under it, checked withgit worktree list): 18 files, 210 tests, 7.9s wall.Plan
Red commit first.
test/build/entrypoints.test.ts, asserting the contract that is broken today, without needing a build:includepattern intsconfig.jsonroots undercompilerOptions.rootDir— this is exactly the TS6059 precondition, and it fails at348f23b;main,typesandbin.quakinpackage.jsonequal the pathstscwill emit forsrc/index.tsandbin/quak.tsgivenoutDir/rootDir;quakscript invokes the same path asbin.quak;bin/quak.tsstill starts with the#!/usr/bin/env nodeshebang.Take the recommended approach:
"rootDir": ".". Emitted layout becomesdist/src/…+dist/bin/…;package.jsongetsmain→./dist/src/index.js,types→./dist/src/index.d.ts,bin.quak→./dist/bin/quak.js. This also keeps #5 possible: withrootDirat the repo root,package.jsonsits inside the root dir, so a relative import of it resolves under both vitest anddist/. I will only deviate to the shim layout if I hit a concrete obstacle, and will say what it was.script/build(POSIX sh, Makefilebuildbecomes a thin shim per the Scripts to Rule Them All section ofREPO_POLICIES.md): runtsc, then verify on disk that each declared entrypoint exists and that the emitted CLI kept its shebang, thenchmod +xit. Putting the artifact check in the build rather than inmake checkkeepsmake checkcomposition untouched (test + lint + fmt-check) and keeps it independent of a prior build, while still failing loudly in CI.package.json'sbuildscript points atscript/buildso the README'syarn buildgets the same guarantees.quakscript inpackage.jsonso the README Getting Started commands (yarn quak login, ...) work afteryarn build. README stays as-is if the invocation matches; otherwise I update the README.Dockerfile: add
RUN make buildafter the existingRUN make check— additive only, no change to whatmake checkruns, minimal so it does not collide with #4. I will not rundocker builder pruneordocker system prune.make cleanalready doesrm -rf dist, which still covers the new layout; will confirm rather than assume.make fmt, thenmake checkandmake buildboth green.TODO.mdand any README change in the same commit as the implementation; commit title ends with(closes #3).Verification I will report
The actual
ls-observed emitted paths formain,types,bin.quak, the presence of.d.ts/.js.mapfor the library entrypoint, the observed shebang line and mode bits of the emitted CLI, and themake testcount/timing. Nothing goes in the PR body that I did not run in this session.Out of scope, untouched:
engines,exports,prepublishOnly, and anything belonging to #4, #5, #6, #13, #24, #25.Implemented in PR #26: #26 (branch
fix-ts-build-rootdir, offmainat348f23b).Took the recommended approach —
rootDir: "."— with no obstacle found, somainandtypesmoved to./dist/src/index.jsand./dist/src/index.d.tsandbin.quakstayed at./dist/bin/quak.js. All three exist on disk aftermake build, anddist/bin/quak.jsis mode-rwxr-xr-xwith#!/usr/bin/env nodeon line 1;./dist/bin/quak.js --helpruns.make checkis 19 files / 216 tests / 8.79s andmake buildexits 0;docker build --no-cache .ran both steps green.Two things worth flagging against the definition of done:
src/crypto/stream.tsthat no build had ever reached. Both are fixed in the PR;make buildcould not go green without them.quakscript exists and the built CLI runs, but the task I am working under forbids invoking yarn scripts, so I did not runyarn quak loginitself. The PR body says so explicitly.Follow-up found while working, filed separately rather than fixed here:
@types/libsodium-wrappers-sumois a deprecated stub package that ships no type definitions.