Installing 0.1.0 failed twice, both my mistakes.
- Node refuses to strip types under node_modules, so shipping .ts for the
CLI could never work from an installed package. tsconfig.cli.json now
compiles those modules to dist/ as ES modules, and the helper scripts
are plain .mjs.
- serverExternalPackages made Turbopack emit require("onvif-<hash>"),
a name that resolves nowhere, because onvif comes from a git URL.
Bundling it fixes that; checked from an installed tarball, where
/api/discover answered 200 and a real camera's info and snapshot came
back through the packaged server.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
50 lines
2.1 KiB
JavaScript
50 lines
2.1 KiB
JavaScript
// After next build, the standalone server needs the static files beside it; Next leaves
|
|
// that copy to us (Next docs, config/output.md). Also drops in the CLI's own modules, so
|
|
// the published package can run from anywhere (vrek iss-ej4ahga).
|
|
import { access, cp, mkdir, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
import path from "node:path";
|
|
|
|
const root = process.cwd();
|
|
const standalone = path.join(root, ".next", "standalone");
|
|
|
|
const exists = (file) => access(file).then(() => true, () => false);
|
|
|
|
/** Bytes under a path, so the build can say what it dropped. */
|
|
async function size(target) {
|
|
const info = await stat(target);
|
|
if (!info.isDirectory()) return info.size;
|
|
let total = 0;
|
|
for (const entry of await readdir(target)) total += await size(path.join(target, entry));
|
|
return total;
|
|
}
|
|
|
|
if (!(await exists(standalone))) {
|
|
console.error("No .next/standalone: is output: 'standalone' still set in next.config.ts?");
|
|
process.exit(1);
|
|
}
|
|
|
|
await mkdir(path.join(standalone, ".next"), { recursive: true });
|
|
await cp(path.join(root, ".next", "static"), path.join(standalone, ".next", "static"), { recursive: true });
|
|
if (await exists(path.join(root, "public"))) {
|
|
await cp(path.join(root, "public"), path.join(standalone, "public"), { recursive: true });
|
|
}
|
|
// Next's tracing sweeps in things the app never runs: the MediaMTX binary that happens to
|
|
// sit in bin/, and sharp's platform binaries, which are only for next/image (unused here).
|
|
// outputFileTracingExcludes didn't keep them out, so they go now, before publishing.
|
|
const prune = ["bin", "node_modules/@img", "node_modules/sharp"];
|
|
let freed = 0;
|
|
for (const name of prune) {
|
|
const target = path.join(standalone, name);
|
|
if (!(await exists(target))) continue;
|
|
freed += await size(target);
|
|
await rm(target, { recursive: true, force: true });
|
|
}
|
|
|
|
// The compiled CLI modules are ES modules; without this Node would read them as CommonJS.
|
|
await writeFile(path.join(root, "dist", "package.json"), '{\n "type": "module"\n}\n');
|
|
|
|
console.log(
|
|
`Bundled the standalone server with its static files` +
|
|
(freed ? `, and dropped ${(freed / 1024 / 1024).toFixed(0)} MB it doesn't need.` : "."),
|
|
);
|