|
| 1 | +# ts-packages build step for xtask build-all / verify (bd-6rczoll3) |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +`cargo xtask build-all` documents itself as the fresh-clone source of truth, |
| 6 | +but no step in it (nor in `cargo xtask verify`) ever builds `ts-packages/*`. |
| 7 | +This goes unnoticed because hub-client consumes ts-packages **from source**: |
| 8 | +every ts-package's exports map points `types`/`source` at `./src/index.ts`, |
| 9 | +hub-client's `tsc -b` has no project references into ts-packages, and Vite |
| 10 | +bundles from source. The one runtime consumer of ts-package `dist/` output is |
| 11 | +the **quarto-hub-mcp server**, which Node executes directly and which resolves |
| 12 | +workspace imports through the `"import": "./dist/index.js"` export condition. |
| 13 | +A missing/stale dependency dist (observed: `@quarto/quarto-sync-client` had no |
| 14 | +`dist/` at all) kills the MCP at startup with `ERR_MODULE_NOT_FOUND`, which |
| 15 | +the MCP harness surfaces as a `-32000` error. |
| 16 | + |
| 17 | +Observed tree state at diagnosis time (2026-06-12): 7 of 9 ts-packages had no |
| 18 | +`dist/` (`annotated-qmd`, `pandoc-types`, `preview-renderer`, |
| 19 | +`preview-runtime`, `quarto-sync-client`, `sync-test-harness`, |
| 20 | +`wasm-js-bridge`); the two that did had been built by hand. |
| 21 | + |
| 22 | +## Design |
| 23 | + |
| 24 | +1. **New shared helper** `crates/xtask/src/ts_packages.rs`: |
| 25 | + - `package_dirs(project_root) -> Vec<PathBuf>`: sorted list of |
| 26 | + `ts-packages/*` subdirectories containing a `package.json`; empty when |
| 27 | + `ts-packages/` is absent (old branches). Unit-tested with tempdir |
| 28 | + fixtures. |
| 29 | + - The build invocation is one root-level npm run: |
| 30 | + `npm run build --if-present -w ts-packages/<a> -w ts-packages/<b> ...` |
| 31 | + `--if-present` covers packages without a `build` script |
| 32 | + (`sync-test-harness`, `wasm-js-bridge`). Build **order doesn't matter**: |
| 33 | + since types resolve via `src/`, each package's `tsc` compiles without its |
| 34 | + dependencies' `dist/` present. |
| 35 | +2. **build-all**: new step between `npm install` and the hub-client build, |
| 36 | + with a `--skip-ts-packages-build` flag. |
| 37 | +3. **verify**: new step 6 (before the hub-client build; `TOTAL_STEPS` 12→13, |
| 38 | + later steps renumbered), same `--skip-ts-packages-build` flag. The step |
| 39 | + builds ts-packages, then **smoke-checks the MCP server**: |
| 40 | + `node ts-packages/quarto-hub-mcp/dist/index.js --help` must exit 0. The |
| 41 | + `--help` path runs after the whole ESM graph links, prints usage, and |
| 42 | + exits 0 — no hang, no network, no auth env needed. A broken module graph |
| 43 | + exits 1 with `ERR_MODULE_NOT_FOUND` before any code runs. |
| 44 | +4. **Cross-platform fix**: quarto-hub-mcp's build script is |
| 45 | + `tsc && chmod +x dist/index.js`, which fails on Windows (no `chmod`). |
| 46 | + Replace with a portable node one-liner |
| 47 | + (`node -e "fs.chmodSync(...)"`-style, no-op semantics on Windows) so the |
| 48 | + new verify/build-all step doesn't break Windows. |
| 49 | +5. **Docs**: update step lists in `build_all.rs` / `verify.rs` module docs, |
| 50 | + the `Verify` / `BuildAll` doc comments in `main.rs`, and the verify |
| 51 | + description in `CLAUDE.md`. |
| 52 | + |
| 53 | +Out of scope (deliberately): TypeScript project references between |
| 54 | +ts-packages and hub-client; building hub-client/trace-viewer/q2-preview-spa |
| 55 | +from the root `npm run build --workspaces` script (they keep their dedicated |
| 56 | +steps, which include WASM and embedding concerns the root script doesn't |
| 57 | +know about). |
| 58 | + |
| 59 | +## Work Items |
| 60 | + |
| 61 | +### Phase 1: Red (failing checks first) |
| 62 | + |
| 63 | +- [x] Reproduce the failure mechanically: `node |
| 64 | + ts-packages/quarto-hub-mcp/dist/index.js --help` exits 1 with |
| 65 | + `ERR_MODULE_NOT_FOUND` for `@quarto/quarto-sync-client/dist/index.js` |
| 66 | + (recorded below) |
| 67 | +- [x] Write unit tests for `ts_packages::workspace_paths` (tempdir fixtures: |
| 68 | + sorted result, skips dirs without package.json, missing ts-packages/ |
| 69 | + → empty, ignores plain files) |
| 70 | + |
| 71 | +### Phase 2: Implementation |
| 72 | + |
| 73 | +- [x] Add `crates/xtask/src/ts_packages.rs` helper + tests |
| 74 | +- [x] Make quarto-hub-mcp's `build` script cross-platform (drop bare `chmod`; |
| 75 | + now `node -e "import('node:fs').then(fs => fs.chmodSync(...))"`) |
| 76 | +- [x] build-all: add ts-packages step + `--skip-ts-packages-build` flag |
| 77 | +- [x] verify: add step 6 (build + MCP smoke check), renumber steps 6–12 → |
| 78 | + 7–13, add `--skip-ts-packages-build` flag |
| 79 | +- [x] Update doc comments in `main.rs`, module docs, `CLAUDE.md` |
| 80 | + |
| 81 | +### Phase 3: Verification |
| 82 | + |
| 83 | +- [x] `cargo nextest run -p xtask` — 4 new unit tests pass |
| 84 | +- [x] End-to-end: verify with all other steps skipped → ts-packages step |
| 85 | + builds 7 packages (2 have no build script, covered by `--if-present`) |
| 86 | + and the MCP smoke check passes; same for the build-all step |
| 87 | +- [x] End-to-end: `node ts-packages/quarto-hub-mcp/dist/index.js --help` |
| 88 | + exits 0 and prints usage (the original failing command from the bug |
| 89 | + report) |
| 90 | +- [x] `cargo build --workspace` (clean) + `cargo nextest run --workspace` |
| 91 | + (9967 passed, 196 skipped) |
| 92 | +- [x] `cargo xtask verify --skip-hub-build` (also exercises the new step |
| 93 | + for real) — all steps passed, exit 0 |
| 94 | +- [ ] Close bd-6rczoll3 |
| 95 | + |
| 96 | +## Evidence |
| 97 | + |
| 98 | +### Red state (before fix, 2026-06-12) |
| 99 | + |
| 100 | +``` |
| 101 | +$ node ts-packages/quarto-hub-mcp/dist/index.js --help |
| 102 | +Error [ERR_MODULE_NOT_FOUND]: Cannot find module |
| 103 | +'/Users/cscheid/repos/github/quarto-dev/q2/node_modules/@quarto/quarto-sync-client/dist/index.js' |
| 104 | +imported from .../ts-packages/quarto-hub-mcp/dist/connection-manager.js |
| 105 | +exit code: 1 |
| 106 | +``` |
| 107 | + |
| 108 | +(Tree state: 7 of 9 ts-packages had no `dist/`. The `--help` flag still |
| 109 | +fails because ESM module resolution happens before any code runs — which is |
| 110 | +exactly why it works as a smoke check.) |
| 111 | + |
| 112 | +### Green state (after fix) |
| 113 | + |
| 114 | +``` |
| 115 | +$ cargo xtask verify --skip-rust-build --skip-rust-tests --skip-hub-build \ |
| 116 | + --skip-hub-tests --skip-trace-viewer-build --skip-trace-viewer-tests \ |
| 117 | + --skip-treesitter-tests --skip-shared-package-tests --skip-q2-preview-spa-build |
| 118 | +━━━ Step 6/13: Building ts-packages workspaces ━━━ |
| 119 | +> @quarto/annotated-qmd@0.1.1 build ... (7 packages built via |
| 120 | + npm run build --if-present -w ts-packages/<pkg> ...) |
| 121 | + ↳ Smoke-checking quarto-hub-mcp module graph (node dist/index.js --help)... |
| 122 | +Usage: quarto-hub-mcp --server <url> [--read-only] [--redirect-port <N>] |
| 123 | + ✓ quarto-hub-mcp module graph loads |
| 124 | +✓ ts-packages build complete |
| 125 | +... |
| 126 | +✓ All verification steps passed! |
| 127 | +
|
| 128 | +$ node ts-packages/quarto-hub-mcp/dist/index.js --help |
| 129 | +Usage: quarto-hub-mcp --server <url> [--read-only] [--redirect-port <N>] |
| 130 | +MCP --help exit code: 0 |
| 131 | +``` |
| 132 | + |
| 133 | +Output inspected: usage text printed, exit 0, and |
| 134 | +`ts-packages/quarto-sync-client/dist/index.js` (the originally missing |
| 135 | +module) now exists. |
0 commit comments