// The lockfile writer (src/kernel/npm/lockfile-write.ts), checked in Node: resolve a // package.json with the registry npm saw, write package-lock.json, and compare it byte // for byte with the one real npm 11.7.0 wrote from the same inputs. import { readFileSync } from "node:fs"; import { test, expect } from "@playwright/test"; import { fetchLockMeta, jsonFormat, packageLock } from "../src/kernel/npm/lockfile-write"; import { resolve } from "../src/kernel/npm/resolve"; import { loadTree, type Source } from "../src/kernel/npm/tree"; import { fakeRegistry, recordingRegistry, type FakePackages, type TestRegistry } from "./npm-registry"; // @ts-expect-error: an untyped .mjs script import { packageText } from "../scripts/npm-oracle.mjs"; const fixture = (name: string, file: string): string => readFileSync(new URL(`./fixtures/registry/${name}/${file}`, import.meta.url), "utf8"); /** What `runtime.install()` writes as /app/package-lock.json for these inputs. */ async function write(pkg: Source, lock: Source | undefined, registry: TestRegistry): Promise { const tree = loadTree(pkg, lock); await resolve(tree, registry); return packageLock(tree, "app", await fetchLockMeta(tree, registry), jsonFormat(pkg.text)).text; } for (const name of ["hono", "express", "conflict"]) { test(`${name}: package.json alone writes the package-lock.json npm wrote`, async () => { const pkg = { text: fixture(name, "package.json"), path: "package.json" }; expect(await write(pkg, undefined, recordingRegistry(name))).toBe(fixture(name, "package-lock.json")); }); test(`${name}: the lockfile npm wrote is written back as it is`, async () => { const pkg = { text: fixture(name, "package.json"), path: "package.json" }; const lock = { text: fixture(name, "package-lock.json"), path: "package-lock.json" }; const registry = recordingRegistry(name); expect(await write(pkg, lock, registry)).toBe(lock.text); expect([registry.calls, registry.lockMetaCalls]).toEqual([[], []]); }); } interface FakeCase { description: string; indent?: string | number; newline?: string; package: Record; registry: FakePackages; npm: { status: number; seedLock?: unknown; lockfile?: string }; } const fakeCase = (name: string): FakeCase => JSON.parse(readFileSync(new URL(`./fixtures/fake/${name}.json`, import.meta.url), "utf8")); // Every fake case npm installed; eresolve is the one it refused. for (const name of ["replace", "override-ref", "bump", "peer-warn", "inert", "fields", "indent-tab", "crlf", "flat"]) { test(`${name}: writes npm's package-lock.json byte for byte (${fakeCase(name).description})`, async () => { const c = fakeCase(name); expect(c.npm.status).toBe(0); // The text npm-oracle.mjs gave npm as package.json, and the lockfile npm wrote first. const pkg = { text: packageText(c) as string, path: "package.json" }; const seed = c.npm.seedLock === undefined ? undefined : { text: `${JSON.stringify(c.npm.seedLock, null, 2)}\n`, path: "package-lock.json" }; expect(await write(pkg, seed, fakeRegistry(c.registry))).toBe(c.npm.lockfile); }); } // tests/fixtures/registry/hono-stale: hono's lockfile as the seed, under a package.json // whose hono range it no longer satisfies. Real npm read only hono's packument. test("hono-stale: a lockfile package.json outgrew rewrites only that package, as npm did", async () => { const pkg = { text: fixture("hono-stale", "package.json"), path: "package.json" }; const seed = { text: fixture("hono-stale", "seed-lock.json"), path: "package-lock.json" }; const registry = recordingRegistry("hono-stale"); expect(await write(pkg, seed, registry)).toBe(fixture("hono-stale", "package-lock.json")); expect([registry.calls, registry.lockMetaCalls]).toEqual([["hono@~4.12.0"], ["hono@4.12.34"]]); });