Documents Node islands and corrects the pages that said every package compiles to native code.
Why
A build runs npm packages it can't compile natively in Node.js, loaded into the program on first use, and prints an SC8001 note for each. No page under docs/content/docs/ mentioned islands or the libnode pack, dependencies.mdx said every package compiles to native code, and the first islanded dependency of a fresh install stops with SC8005, whose hint names a path inside this repository.
What changed
- New page
docs/content/docs/node-islands.mdx("Node Islands", in the Guides group after npm Dependencies): what an island is, a worked example (ejs), how to get a libnode pack today, what runs in an island and the reason classes, how to see it (SC8001,scriptc coverage,--explain-island,--fail-on), how to move code into or out of an island (--node-islands,--node-island,--native,package.json), how values cross and one refusal with its fix, worker threads, measured cost, shipping,@scriptc/node, theSC80xxcodes, and current limits. - Corrections:
dependencies.mdx(intro, "Which packages compile", "Unsupported code inside a package", "Upgrading from scriptc 0.2"),index.mdx,quickstart.mdx,how-it-works.mdx,coverage.mdx(a "Node islands" section, the report-field table,--fail-on),cli.mdx(--node-islands,--node-island,--native,--explain-island,--libnode, the island values of--fail-on,SCRIPTC_LIBNODE), the landing page's three cards, and the README's first paragraph. - Limitations: a "Node islands" section built from ten partials under
docs/content/limitations/node-islands/;runtime-code-evaluation.mdxno longer says a compiled program can never evaluate source;native-addons.mdxandffi.mdxsay that a package with a native addon runs in an island. - Registration:
meta.json,PAGE_TITLES, and the route-test fixture (the new page with twelve heading anchors, and the new headings on the coverage and limitations pages).
No compiler, runtime or test code changes.
What the page says where main is still moving
- Getting a pack. The
@scriptc/libnode-*packages are not on npm andscriptcdepends on none, so the page says that plainly, shows the realSC8005, and documents the one route that works today:pnpm --filter @scriptc/libnode-<platform> build:nativein a checkout, which I ran (38 minutes on 8 vCPUs, a 127 MB pack). When the packs are published this section shrinks to an install line. - Crossing rules. The page states the principle (a value crosses by reference unless a copy can't be observed), the four cases that hold on main, and one refusal, a
WeakMap, which stays refused under the open changes to the rule. It says the refused set is shrinking and does not list it. - Stale tool text, quoted as it is on main. #3116 changes the CLI's island wording, so two strings quoted on the page will change when it lands and the page needs a follow-up then: the coverage header
Node islands (would-be, report-only: the build is unchanged; …)with(would-be, report-only)in--explain-island, and theSC8001hint--node-islands=off makes this an error. The page has one sentence explaining each; those sentences go when the strings do. #3116 also makesscriptc runpass arguments to the program; this PR does not touch the sentences that say it doesn't. SCRIPTC_NODE_ISLANDS_TRACE=bootappears in the cost section because it is the only way to see when Node.js starts. It is a runtime debug variable; strike that fence if it should stay undocumented.
Proof
- Docs gate, in a Linux x64 Sandbox on this branch:
cd docs && NEXT_DIST_DIR=.next-check pnpm check— passes: compatibility drift check, typecheck, unit tests, production build, and 26 of 26 route tests, including the new page's title, twelve heading anchors, Markdown twin and internal link fragments. pnpm lint(includes the one-sentence-per-line check of the limitations partials) andpnpm format:check— 0 warnings, all files formatted.- Every
consolefence on the new page comes from a sandbox transcript of the checkout built at 714482d49 with scriptc 0.4.9, Node.js 24.22.1, Ubuntu 26.04, Intel Xeon @ 2.50GHz, 8 vCPU. A script checked the page's 39 commands and 13 source fences against the transcripts and the example files: every command ran, and every output line shown is in that command's output (lines are trimmed, none added).
Measured cost (Linux x64, one machine, hyperfine, 50 runs each)
| Program | Start to exit | Peak RSS | Executable |
|---|---|---|---|
ms compiled natively | 0.69 ms | 2.3 MB | 603,896 bytes |
ms in an island | 50.3 ms | 85.1 MB | 691,368 bytes |
ejs island never reached | 0.66 ms | 2.4 MB | |
ejs island used once | 57.8 ms | 88.4 MB | |
node duration.ts | 83.9 ms | 64.5 MB |
100,000 calls of ms("2h"): 43.1 ms native, 160.7 ms in an island, 96.4 ms under Node, so about one microsecond per crossing after the start-up. The pack is 127 MB.
Found while measuring
These are behaviours of main that the page documents as limits rather than hides. None is changed here.
--native=<pkg>and--node-islands=offdo not fail a build: the package compiles natively and its Node-only operations throw at run time.SC8002/SC8003appear only in coverage.- A crossing refusal (
SC8010) on an automatically chosen island becomes anSC8023warning and a native build that fails at run time. - An island used from a worker thread aborts with
internal error: dyn handle ops not installed(#3007 and #3047 are open for this). --node-island=<file>.tsis accepted and ignored.- A package in an island does not see the program's
process.argv(--node-island=commander). JSON.stringifyof an island object throws at run time (lodashgroupBy).- Coverage's island list can name a package the build compiles natively. One such package,
mustache@4.2.0, compiles natively to wrong output with no diagnostic (Mustache.render("plain", {})returns""); that is a compiler bug, not documented here as a limit, and needs its own issue. - A package with a native addon (
@node-rs/crc32) runs in an island and works, so the "addons can't load" statements were out of date.
🤖 Generated with Claude Code
Activity
8On hold: the "When a value can't cross" section shows the SC8010 error and the SC8023 warning for labels.ts, which main no longer prints since #3005 merged (the build now succeeds and the executable fails at run time with TypeError: scriptc: this native object cannot cross into a Node island yet). The page waits for the build-time refusal of a WeakMap crossing into an island to be restored; its console blocks will then be re-run against a tree that has the fix.
This pull request is a draft
Merging is unavailable until it is marked ready for review.
The latest updates on your projects. Learn more about Vercel for GitHub.