// TypeScript type-stripping acceptance proofs (TS slice brief §Verification). // Ground truth for every asserted behavior was captured from real node v24.16 // (type stripping is on by default there) and the nodejs.org TypeScript docs: // - `import type` is erased before resolution: a type-only import of a module // that does not exist loads fine (node main.ts with such an import exits 0) // - extensionless require() of a .ts-only tree is MODULE_NOT_FOUND — Node's // Module._extensions stays [.js, .json, .node]; extensions are mandatory for // TS in require() (nodejs.org/docs: "file extensions are also mandatory in // require() calls"). The TS-SLICE-BRIEF expected probing to add .ts/.cts — // deviation recorded in the slice report. // - .tsx is not a recognized extension in strip-only mode → ERR_UNKNOWN_FILE_EXTENSION // - enums/value namespaces/parameter properties → ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX // - TS under node_modules → ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING import { test, expect } from "@playwright/test"; test("strip-only ESM: annotations/interfaces/generics erased, import type never resolved, CJS .ts via facade, import.meta", async ({ page }) => { await page.goto("/demo/ts.html"); // The whole happy-path line: greet(leftpad(name.txt)) proves the stripped graph // runs; `tsts` proves a generic with default arg + `as` cast; `strip-only` proves // interface/type-alias annotations; `named-through-lexer` proves the CJS facade // was lexed from the STRIPPED source; `[reg]` proves the inline `type` modifier // was erased; file:/// proves import.meta survived stripping AND was rewritten. await expect(page.locator("#out")).toContainText( /Hello, .+! \| tsts \| strip-only \| cjs-via-facade \| named-through-lexer \| \[reg\] \| file:\/\/\/app\/main\.mts/, { timeout: 30_000 }, ); // CJS process on the same page: extensions mandatory (no probing, no rewriting), // then the explicit-require path runs stripped TS through the CJS wrapper. await expect(page.locator("#out")).toContainText("extensionless: MODULE_NOT_FOUND", { timeout: 30_000 }); await expect(page.locator("#out")).toContainText("no-rewrite: MODULE_NOT_FOUND", { timeout: 30_000 }); await expect(page.locator("#out")).toContainText("legacy: cjs-via-facade req! named-through-lexer", { timeout: 30_000 }); // .ts under a type:"module" package is ESM (nearest-type rule, like .js); TS // under node_modules is refused, like real Node. await expect(page.locator("#out")).toContainText("type-module ts: esm-via-type-field", { timeout: 30_000 }); await expect(page.locator("#out")).toContainText("nm-ts: ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING", { timeout: 30_000 }); // .tsx (require + ESM graph) and the enum are refused by name; the happy paths // exited 0 and the refusals exited 1. await expect(page.locator("#exit")).toHaveText("esm=0 cjs=0 typed=0 nm-ts=0 tsx=1 enum=1 tsx-esm=1", { timeout: 30_000 }); await expect(page.locator("#err")).toContainText("ERR_UNKNOWN_FILE_EXTENSION", { timeout: 30_000 }); await expect(page.locator("#err")).toContainText("ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX", { timeout: 30_000 }); }); test("erasure precedes resolution: the missing type-only import target is the proof", async ({ page }) => { // /app/main.mts contains `import type { Nope } from "./does-not-exist.mts"`. // If that import survived stripping, resolution would miss (or the SW fetch // would 404) and the esm process could not print its line or exit 0. await page.goto("/demo/ts.html"); await expect(page.locator("#exit")).toHaveText("esm=0 cjs=0 typed=0 nm-ts=0 tsx=1 enum=1 tsx-esm=1", { timeout: 30_000 }); }); test("TS engine failure is contained: kernel boots, plain JS runs, .ts fails with ERR_NIB_TS_ENGINE", async ({ page }) => { // The kernel is booted with ?nib-selftest=ts-engine-off (the genuine CSP route // cannot be staged: this Chromium binds 'wasm-unsafe-eval' to neither the page // policy nor the worker response for dedicated module workers). await page.goto("/demo/ts-noengine.html"); await expect(page.locator("#out")).toContainText("plain js still runs", { timeout: 30_000 }); await expect(page.locator("#err")).toContainText("ERR_NIB_TS_ENGINE", { timeout: 30_000 }); await expect(page.locator("#exit")).toHaveText("js=0 ts=1", { timeout: 30_000 }); });