Skip to content

build: stop embedding source text in published source maps - #16492

Merged
baozhoutao merged 4 commits into
mainfrom
claude/issue-16469-sourcemaps-no-sources-content
Sep 7, 2026
Merged

build: stop embedding source text in published source maps#16492
baozhoutao merged 4 commits into
mainfrom
claude/issue-16469-sourcemaps-no-sources-content

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #16469

What

sourcesContent is now false by construction for every tsup/esbuild build in this repo, and a new gate pins it. packages/cli (built with tsc, not tsup) needed no code change — see the "cli decision" section below.

Where the setting lives

There is no monorepo-wide tsup config every package extends — tsup.config.ts files are independent per-package files, most of them thin wrappers tsup reads via --config. Measured on origin/main:

  • 47 packages build directly through the repo-root tsup.config.ts (tsup --config ../../tsup.config.ts or ../../../tsup.config.ts in their build script — no config file of their own).
  • 20 packages carry a bespoke tsup.config.ts (multiple entries, external, shims, etc.) and are not eligible for the shared file.
  • 1 of those 20 (create-objectstack) never sets sourcemap: true at all — it ships no .map files, so it is untouched (verified: no .map in its dist/ before or after).

So the "shared configuration level" the card asks for is real but split: scripts/tsup-drop-sources-content.mjs is the one place the setting is decided (options.sourcesContent = false, wired through tsup's esbuildOptions hook — the only seam tsup exposes into esbuild's real BuildOptions, since tsup's own Options type has no sourcesContent key). The root config picks it up once and every package that builds through it inherits it with zero config changes of their own. The 19 packages with bespoke configs (i.e. 20 minus create-objectstack) each get a one-line esbuildOptions: dropSourcesContent (or, where an esbuildOptions-shaped shared/base object already exists, the call is folded into that one object). That is the smallest correct route — not a shared preset nobody asked for, and not 55 independently-reasoned edits.

mappings are untouched everywhere; verified a rebuilt @objectstack/spec map still resolves a real generated position to the correct original file/line/column (node:module's SourceMap.findEntry).

The cli decision (the card's open question)

The card's options were: ship src/** in files[] (probably not), or "emit maps whose sources resolve" — picking whichever is consistent with the rest of the fleet.

Measured: cli's situation is not actually unique — it is what the whole fleet's maps now look like. Every tsup-built package's map sources are ../src/**-relative paths (../src/index.ts, etc.), and files: ["dist", "README.md", "CHANGELOG.md"] (the same three entries on cli and on every plain package) does not ship src/. Before this PR that was invisible because sourcesContent gave a debugger the file's text without ever needing to resolve sources on disk. cli never had that mask (tsc's sourceMap does not set inlineSources here), which is why its dead references were the ones the #15905 measurement actually saw — not because its maps are worse, but because they were the only ones already showing the fleet's real shape.

So: left cli's maps exactly as tsc emits them. Shipping src/** was rejected for the same reason it would be rejected everywhere else — it puts most of the bytes this card removes straight back (measured on @objectstack/spec: src/**/*.zod.ts is 63% of that package's unique map sources, and it already ships in files[] for a different, pre-existing reason — those particular entries already resolve; the fleet is not "all dead," it's mixed, and cli ships no source directory at all so 100% of its sources are non-resolving). There is no lighter-weight way to make a sources entry "resolve" for a package that intentionally ships only dist/ — the two real options are "ship the source" (rejected) or "don't" (what tsc and every tsup config now do alike). cli's maps are the fleet's shape now, not an exception to it.

Measured, before → after

Fresh builds, same commit, real bytes (@objectstack/spec and @objectstack/types — small):

package dist total .map bytes .map % of dist
@objectstack/spec — before 163.08 MB 97.49 MB 59.8%
@objectstack/spec — after 80.88 MB 15.29 MB 18.9%
@objectstack/types — before 928.3 KB 577.0 KB 62.2%
@objectstack/types — after 406.5 KB 55.3 KB 13.6%

These are materially larger reductions than the card's own recollection ("spec: about half of 49 MB") — that number came from a different session's measurement and does not match a fresh build read on this commit; recorded here as the real numbers rather than reconciled against the card's.

The gate

pnpm check:sourcemap-no-sources-content (scripts/check-sourcemap-no-sources-content.mjs) sweeps every built, non-private workspace package's dist/**/*.map (recursively — dist/browser/** included) and fails if any of them carries a non-empty sourcesContent array. mappings are not its business. Self-test is fixture-driven against real files on real disk (temp dir), including the positive control the acceptance list asks for: a planted map that genuinely embeds sourcesContent and is asserted to go red, named by package and file. Wired into ci.yml's Build Core job immediately after check:dual-build-cjs-loads, for the same reason that gate lives there — it reads a real dist/ and refuses (exit 3, PREREQUISITE NOT MET) rather than passing silently when there is none. Declares the ROOT_DIR_WATCH_HINTS/ROOT_FILE_WATCH_HINTS dispatch-gates idiom so a tsup.config.ts edit (anywhere) correctly derives this gate.

Confirmed red-first: ran the gate against a fresh, unmodified build of origin/main before making any config change — 55 built packages flagged, matching the card's "55 of 57" figure closely (the small delta is which packages happened to be in the built closure for that run, not a disagreement).

A transient, unrelated build race observed and ruled out

One full pnpm build run in this branch's verification failed at @objectstack/client-react's DTS pass with TS2307: Cannot find module '@objectstack/client', even though @objectstack/client's own build had already completed and its dist/index.d.ts was on disk and correctly symlinked. Neither @objectstack/client nor @objectstack/client-react (nor their configs) are touched by this diff, and client-react has no tsup.config.ts of its own — it builds through the shared root config, whose only change here is the esbuildOptions hook (esbuild-only; it cannot affect the separate rollup/tsc-based DTS pass that failed). Rebuilding @objectstack/client-react alone, immediately after, succeeded cleanly. Read as a pre-existing closure-build race (the class check:dts-closure's header already documents, #15042), not a regression from this change — recorded here rather than silently re-run away.

Scope

Out of scope per the card and left untouched: the E3 text change itself (#15905, skills seat), and any classification of which comments are "internal."

Test plan

  • node scripts/check-sourcemap-no-sources-content.mjs --self-test — fixture batteries including the positive control
  • Fresh, pre-fix build of origin/main: gate reports 55 built packages with sourcesContent (red, as expected)
  • Post-fix rebuild of @objectstack/spec, @objectstack/cli's full dependency closure, and a full pnpm build: gate green
  • node:module's SourceMap.findEntry against a rebuilt map resolves a real generated position to the correct original file/line/column — mappings unaffected
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack --ran <file> reconciled

🤖 Generated with Claude Code

https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8


Generated by Claude Code

…es-content gate

Set sourcesContent:false once (scripts/tsup-drop-sources-content.mjs, wired
into every tsup.config.ts via tsup's esbuildOptions hook) so published
.js.map files no longer embed the complete original source text. mappings
are untouched -- stack-trace positions still resolve correctly.

Add pnpm check:sourcemap-no-sources-content, a closure-wide gate over
dist/**/*.map with a positive-control fixture, wired into ci.yml's Build
Core job beside check:dual-build-cjs-loads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
…e root tsc program

The root tsup.config.ts imports the shared helper, and the root TypeScript
program (scripts/** plus top-level configs) type-checks it -- an untyped
.mjs import there is TS7016, which pushed the @objectstack/spec-monorepo
DEBT ledger from 26 to 27. Follows the invoked-as.d.mts / workspace-
enumerator.d.mts convention: a hand-written declaration, discovered by
check-declaration-mirrors.mjs. Re-measured: 26, no drift.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
@github-actions github-actions Bot added size/l ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tooling labels Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 19 changed package(s); no hand-written page names any of them. ⚠️ 16 changed file(s) yielded no anchor (packages/apps/account/tsup.config.ts, packages/apps/setup/tsup.config.ts, packages/apps/studio/tsup.config.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 16 changed file(s) yielded no anchor (packages/apps/account/tsup.config.ts, packages/apps/setup/tsup.config.ts, packages/apps/studio/tsup.config.ts, …) — pages documenting those are invisible to this run
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 144 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 4500bc5d7917324330f062424efebdadc62c6fb7packageMentionDocs.

Which tree this was computed on

This run read content/docs from b9bd03ac7169a80aec6119e47febddf970b8b7b3 — the merge of head 205163545494e7a6fb252a5f15f89898b0b86b6b into base 4500bc5d7917324330f062424efebdadc62c6fb7, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b9bd03ac7169a80aec6119e47febddf970b8b7b3 && git checkout b9bd03ac7169a80aec6119e47febddf970b8b7b3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4500bc5d7917324330f062424efebdadc62c6fb7 205163545494e7a6fb252a5f15f89898b0b86b6b && git checkout -B drift-repro 4500bc5d7917324330f062424efebdadc62c6fb7 && git merge --no-ff 205163545494e7a6fb252a5f15f89898b0b86b6b

node scripts/docs-audit/affected-docs.mjs --json 4500bc5d7917324330f062424efebdadc62c6fb7

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Queue reading 2026-09-07T05:48Z (PM seat devx, session_01Vbw3RPgdtqesx4azk9SbW8) — still queued, regroup count 1 (was 0 at the 05:25Z probe), no removed_from_merge_queue.

  • Timeline unchanged: added_to_merge_queue 05:00:32Z at head 205163545; no removal, no merge.
  • Group run 1 (34086185555, created 05:16:40Z): all six shards green, Test Core aggregate completed/success 05:45:34Z. It finished clean — it simply was not the live group by then.
  • Group run 2 (34087826215, created 05:42:25Z): all six shards restarted 05:42:43Z. The rebuild happened ~3 min before run 1's aggregate landed, which is why a fully green run did not merge this PR.

Worth stating plainly, since it looks like a failure and is not: a green group run that gets superseded is the queue re-forming around a change in the batch ahead, not a verdict on this PR. Nothing here is red, no shard has hit the wall (#16173), and the seat's one re-queue stays unspent.

Next probe 06:05Z.


Generated by Claude Code

Merged via the queue into main with commit c5d6803 Sep 7, 2026
37 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-16469-sourcemaps-no-sources-content branch September 7, 2026 06:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/l tooling

Projects

None yet

2 participants