Files
vercel-labs--zero-native/docs/scripts/check-code-toggle.mjs
Chris Tate 584dbbbaa9 TypeScript authoring: write app cores in TypeScript (#119)
* TypeScript authoring: write app cores in TypeScript

- App cores can be authored in TypeScript and compiled ahead of time to arena-backed native code: the complete language minus the ecosystem and purity violations, checked by tsc plus a teaching checker (NS1001-NS1060), emitting readable Zig with 83ns dispatch, no JS engine, and no GC
- The full platform surface reaches TS cores: the Cmd and Sub effects vocabulary bridged to the real engine, markup views binding the committed model, record and replay byte-identical to node semantics, stock-IDE support, multi-file cores with @native-sdk/core library modules, and native init scaffolding TypeScript by default with Zig first-class by choice
- Two showcase ports prove the bar with zero hand-written Zig: soundboard-ts at pixel parity with its Zig original and system-monitor-ts sampling the real OS, each with end-to-end batteries including replayed sessions with zero host calls
- Docs lead TypeScript-first with a segmented language toggle and a markup-first components reference; the eval suite gains dual-track realistic cases measuring both authoring tiers' health and efficiency

* Ship packages/core in the npm package and run its .ts modules from any layout

- copy-framework.js stages the @native-sdk/core closure (src/, sdk/, rt/, package.json + package-lock.json; test/ and scripts/ stay out), the sync check pins each staged entry plus the dep.path coverage, and package.json "files" covers the mirrored paths
- build/ts_run.mjs runs the transpiler tier's .ts modules on every layout: node refuses builtin type stripping under node_modules, so the runner strips those modules with the transpiler's own installed TypeScript and passes repo checkouts through untouched; build/app.zig, native check, and native dev --core all invoke through it
- the missing-dependency teaching now names the real dependency root (works verbatim on the npm-installed layout, where npm ci runs in the shipped packages/core against its shipped lockfile)

* TS scaffolds ship a CI workflow

- the --full ts-core template now writes the Zig full template's workflow (logic tests + Linux automation smoke, no WebKitGTK) with the node tier added to both jobs: setup-node and one npm ci in the fetched SDK's packages/core, the same install native build's teaching names
- slim scaffolds keep shipping no workflow (zero-config parity with the slim Zig template), now pinned by the ts slim template test

* Wire @native-sdk/core into the release automation

- sync-version.js stamps packages/core (manifest + lockfile own-package fields) and the committed TS examples' pins with the CLI release version, check-version-sync.js refuses a half-bumped tree, and the npm version script stages the stamped files; packages/core rides 0.4.4 from here on and scaffold pins follow the bundled manifest automatically
- the release publish step gains the packages/core publish gated on its "private" flag: private (until the 0.5.0 cut, by design) skips with a loud flip-requirement comment; dropping the flag is the publish switch, no workflow edit needed
- the TS scaffold README notes npm install is optional (the CLI materializes and refreshes the editor package itself), closing the pre-publish gap window honestly

* Provide node to the CI jobs that build TypeScript cores

- The Native Examples job panicked on the missing transpiler dependency, and the Zig Core and tooling jobs were silently skipping every node-gated ts-core suite; all three now set up node and npm ci packages/core
2026-07-12 21:37:07 -05:00

66 lines
2.5 KiB
JavaScript

// Regression pin for the TS | Zig code toggle: assert the *prerendered*
// HTML actually contains the tab headers. The toggle once shipped broken —
// a client component introspected its RSC children, saw one opaque node,
// and silently fell back to stacked fences on every page — and nothing
// caught it because no check looked at rendered output. This one does.
//
// For every page.mdx that uses <CodeToggle>, the built HTML under
// ${NEXT_DIST_DIR:-.next}/server/app must contain exactly as many
// role="tablist" headers as the source has <CodeToggle> usages.
//
// Runs after `next build` as part of `pnpm check`.
import { readFileSync, readdirSync, existsSync, statSync } from "node:fs";
import { join, relative, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const docsDir = join(dirname(fileURLToPath(import.meta.url)), "..");
const appDir = join(docsDir, "src", "app");
const distDir = join(docsDir, process.env.NEXT_DIST_DIR || ".next");
const htmlDir = join(distDir, "server", "app");
function* mdxPages(dir) {
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) yield* mdxPages(full);
else if (entry === "page.mdx") yield full;
}
}
function count(haystack, needle) {
return haystack.split(needle).length - 1;
}
let failures = 0;
let togglePages = 0;
for (const page of mdxPages(appDir)) {
const expected = count(readFileSync(page, "utf8"), "<CodeToggle>");
if (expected === 0) continue;
togglePages += 1;
const route = relative(appDir, dirname(page)); // e.g. "typescript/packages"
const htmlPath = join(htmlDir, `${route}.html`);
if (!existsSync(htmlPath)) {
console.error(`FAIL /${route}: no prerendered HTML at ${htmlPath} — expected a static page with ${expected} code toggle(s)`);
failures += 1;
continue;
}
const actual = count(readFileSync(htmlPath, "utf8"), 'role="tablist"');
if (actual !== expected) {
console.error(`FAIL /${route}: ${expected} <CodeToggle> usage(s) in MDX but ${actual} role="tablist" in prerendered HTML — the toggle is falling back to stacked fences`);
failures += 1;
} else {
console.log(`ok /${route}: ${actual} code toggle(s) rendered with tabs`);
}
}
if (togglePages === 0) {
console.error("FAIL: no page.mdx uses <CodeToggle> — if the component was renamed, update scripts/check-code-toggle.mjs so this pin keeps checking rendered output");
failures += 1;
}
if (failures > 0) process.exit(1);
console.log(`code-toggle check passed: ${togglePages} page(s) verified`);