// The kernel's function form of an ES module (src/kernel/function-form.ts), checked in // Node: positions survive the rewrite, and the export plan is exactly the module's. import { createHash } from "node:crypto"; import { readdirSync, readFileSync } from "node:fs"; import { gunzipSync } from "node:zlib"; import { test, expect } from "@playwright/test"; import { toFunctionForm } from "../src/kernel/function-form"; import { tarball } from "./tarball-cache"; const GUESTS = "tests/fixtures/guests/require-esm"; const LOCK = JSON.parse(readFileSync("tests/fixtures/require-esm-emnapi/package-lock.json", "utf8")); /** A file of an npm tarball, its bytes checked against the fixture lockfile. */ async function packageFile(pkg: string, file: string): Promise { const entry = LOCK.packages[`node_modules/${pkg}`]; const tgz = await tarball(entry.resolved); expect(`sha512-${createHash("sha512").update(tgz).digest("base64")}`).toBe(entry.integrity); const tar = gunzipSync(tgz); for (let at = 0; at + 512 <= tar.length;) { const field = (from: number, len: number): string => tar.subarray(at + from, at + from + len).toString("latin1").replace(/\0.*$/s, ""); const size = parseInt(field(124, 12), 8); const prefix = field(345, 155); if (`${prefix ? `${prefix}/` : ""}${field(0, 100)}` === `package/${file}`) return tar.subarray(at + 512, at + 512 + size).toString("utf8"); at += 512 + Math.ceil(size / 512) * 512; } throw new Error(`${pkg} has no ${file}`); } /** Every line after the first keeps its length, and each column either holds the * source's character, a blank, or part of a same-width replacement. */ function expectPositionsKept(source: string, code: string): void { const src = source.split("\n"); const out = code.split("\n"); expect(out.length).toBe(src.length + 2); for (let i = 1; i < src.length; i++) { expect(out[i]!.length, `line ${i + 1}`).toBe(src[i]!.length); const replaced = new Set(); for (const m of out[i]!.matchAll(/__nibI|__nib_meta|const __nib_d=/g)) for (let c = m.index; c < m.index + m[0].length; c++) replaced.add(c); for (let c = 0; c < src[i]!.length; c++) { if (out[i]![c] !== src[i]![c] && out[i]![c] !== " " && !replaced.has(c)) throw new Error(`line ${i + 1} column ${c + 1}: ${JSON.stringify(out[i])}`); } } } test("every line and column after the first survives, for each fixture module", () => { const modules = readdirSync(GUESTS).filter((f) => f.endsWith(".mjs")); expect(modules.length).toBeGreaterThan(30); for (const f of modules) { const text = readFileSync(`${GUESTS}/${f}`, "utf8"); expectPositionsKept(text, toFunctionForm(text, `/app/${f}`).code); } }); test("same-width rewrites keep positions on the lines they touch", () => { const text = [ "#!/usr/bin/env node", 'import a, { b as c, "d-e" as de } from "./x.mjs";', "export default (1);", "export { c as f } ;export * as g from 'y';", "const u = import.meta.url, p = import ( './z.mjs' );", "export const h = 1, k = 2; export { h as \"module.exports\" };", "export * from \"./w.mjs\";", ].join("\n"); const form = toFunctionForm(text, "/app/m.mjs"); expectPositionsKept(text, form.code); expect(form.code).toBe([ '(function (__nib_meta, __nib_def, __nib_bind, __nibI) { let a,c,de;__nib_bind((g)=>{a=g(0);c=g(1);de=g(2);});return function () {"use strict";__nib_def({"default":()=>__nib_d,"h":()=>h,"k":()=>k,"module.exports":()=>h});///usr/bin/env node', " ;", "const __nib_d= (1);", " ; ;", "const u = __nib_meta .url, p = __nibI ( './z.mjs' );", " const h = 1, k = 2; ;", " ;", "}; })", "//# sourceURL=file:///app/m.mjs", ].join("\n")); expect({ requests: form.requests, bindings: form.bindings, exports: form.exports }).toEqual({ requests: ["./x.mjs", "y", "./w.mjs"], bindings: [{ request: 0, imported: "default" }, { request: 0, imported: "b" }, { request: 0, imported: "d-e" }], exports: [ { kind: "local", name: "default", local: "__nib_d" }, { kind: "reexport", name: "f", request: 0, imported: "b" }, { kind: "reexport", name: "g", request: 1, imported: null }, { kind: "local", name: "h", local: "h" }, { kind: "local", name: "k", local: "k" }, { kind: "local", name: "module.exports", local: "h" }, { kind: "star", request: 2 }, ], }); }); test("default declarations keep their own names; anonymous ones bind the default local", () => { const plan = (text: string) => toFunctionForm(text, "/app/m.mjs").exports; expect(plan("export default function hello() {}")).toEqual([{ kind: "local", name: "default", local: "hello" }]); expect(plan("export default async function hello() {}")).toEqual([{ kind: "local", name: "default", local: "hello" }]); expect(plan("export default class K {}")).toEqual([{ kind: "local", name: "default", local: "K" }]); expect(plan("export default function () {}")).toEqual([{ kind: "local", name: "default", local: "__nib_d" }]); expect(plan("export default class {}")).toEqual([{ kind: "local", name: "default", local: "__nib_d" }]); expect(plan("const x = 1; export { x as default };")).toEqual([{ kind: "local", name: "default", local: "x" }]); }); test("@emnapi/core 2.0.0-beta.2: positions and its exact export plan", async () => { const text = await packageFile("@emnapi/core", "dist/emnapi-core.js"); const form = toFunctionForm(text, "/app/node_modules/@emnapi/core/dist/emnapi-core.js"); expectPositionsKept(text, form.code); const local = (name: string) => ({ kind: "local", name, local: name }); expect({ requests: form.requests, bindings: form.bindings, exports: form.exports }).toEqual({ requests: ["@emnapi/wasi-threads"], bindings: [ { request: 0, imported: "ThreadManager" }, { request: 0, imported: "WASIThreads" }, { request: 0, imported: "ThreadMessageHandler" }, ], exports: [ { kind: "star", request: 0 }, ...["MessageHandler", "createAddonModule", "createNapiModule", "instantiateAddon", "instantiateAddonSync", "instantiateNapiModule", "instantiateNapiModuleSync", "loadAddon", "loadAddonSync", "loadNapiModule", "loadNapiModuleSync", "version"].map(local), ], }); });