Reproduction
NODE_ENV=development bun test packages/stx/test/process/script-setup.test.tsThree failures in Script Processing Fixes > Server script stripping with </script> in source:
does not terminate early on </script> inside a double-quoted stringdoes not terminate early on </script> inside a single-quoted stringdoes not terminate early on </script> inside a block comment
The same file passes with no failures under a bare bun test, which sets NODE_ENV=test.
What happens
Given
<script server>const html = "<script>x</script>"; const visible = 1;</script><div>after</div>the output is
<div data-stx-server-script-error role="alert" …>
stx: a <script server> does not parse, so this component rendered nothing.
/test.stx — Unexpected EOF
</div>"; const visible = 1;</script><div>after</div>The boundary markup is spliced in over a span that ends at the nested </script>, so everything from there to the real close tag — "; const visible = 1;</script> — is left behind as literal page text. The server script's source leaks into the rendered document.
Why it is invisible in CI and in tests
process.ts:
const showBoundaries = !isProduction() && !isTest()and env.ts:
export function isTest(): boolean {
return process.env.NODE_ENV === 'test'
}bun test sets NODE_ENV=test, so showBoundaries is false and the boundary is never rendered during the suite. The splice is therefore untested, and the only mode where the boundary does render — development — is the one nobody exercises.
That also explains the intermittent red I saw on a full bun test packages/stx/test/: several test files legitimately mutate process.env.NODE_ENV (server-script-error-boundary.test.ts sets development per test and restores it correctly; directives/conditionals.test.ts sets production). A save/restore in one file that captures "original" while another file's beforeEach has it set to development will restore development permanently, and from then on every suite takes the boundary branch. Running the two files together passes; the full package run does not.
Suggested direction
Two separable things:
The splice span. The boundary's
index/lengthshould cover the whole<script server>…</script>element asfindScriptBodyEndresolves it — the same quote/comment/template-aware scan the stripper already uses — not the first</script>the splice site happens to find. The stripper gets this right; the boundary path does not reuse it.isTest()is not a reliable guard. It trusts an env var that any test may set, so a mode flag derived from it flips mid-suite. Detecting the runner directly would makeshowBoundariesdeterministic, and would also mean the boundary path gets covered by the suite instead of being skipped by it.
Found while counting what remained of stacksjs/stx#1993; not caused by that work. Introduced in f71f544f17.