Files
docmd-io--docmd/tools/print-okf-runtime-deps.js
T
wehub-resource-sync 6db8fca185
docmd CI verification / verify (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 12:31:55 +08:00

99 lines
3.6 KiB
JavaScript

/**
* --------------------------------------------------------------------
* docmd : the zero-config documentation engine.
*
* @package @docmd/core (and ecosystem)
* @website https://docmd.io
* @repository https://github.com/docmd-io/docmd
* @license MIT
* @copyright Copyright (c) 2025-present docmd.io
*
* [docmd-source] - Please do not remove this header.
* --------------------------------------------------------------------
*
* Resolves the external (non-@docmd) runtime dependencies of @docmd/core
* and its transitive deps, then either:
*
* node tools/print-okf-runtime-deps.js
* → prints `name@range` lines to stdout (human readable / CI log)
*
* node tools/print-okf-runtime-deps.js --emit-pkg=<path>
* → writes a package.json to <path> listing all external deps under
* `dependencies`. Used by the Docker builder stage so the
* production stage can do a single `npm install --prefix /usr/local`
* instead of maintaining a hand-rolled list in the Dockerfile.
*
* The script is the single source of truth for what external deps the
* Docker image needs. Run it after adding a new dep to any @docmd/*
* package — the next Docker build will pick up the change automatically
* because the builder stage regenerates the package.json fresh.
*/
const fs = require("fs");
const path = require("path");
const root = process.cwd();
const emitArg = process.argv.find((a) => a.startsWith("--emit-pkg="));
const emitPath = emitArg ? emitArg.slice("--emit-pkg=".length) : null;
function findPackageDir(name, dir = path.join(root, "packages")) {
for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
if (!entry.isDirectory()) continue;
const full = path.join(dir, entry.name);
const pj = path.join(full, "package.json");
if (fs.existsSync(pj)) {
const pkg = JSON.parse(fs.readFileSync(pj, "utf8"));
if (pkg.name === name) return full;
}
const r = findPackageDir(name, full);
if (r) return r;
}
return null;
}
function collectDeps(name, seen = new Set()) {
if (seen.has(name)) return seen;
seen.add(name);
const dir = findPackageDir(name);
if (!dir) return seen;
const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8"));
for (const dep of Object.keys(pkg.dependencies || {})) {
if (dep.startsWith("@docmd/")) collectDeps(dep, seen);
}
return seen;
}
const internal = collectDeps("@docmd/core");
const external = new Map();
for (const name of internal) {
const dir = findPackageDir(name);
if (!dir) continue;
const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8"));
for (const [dep, range] of Object.entries(pkg.dependencies || {})) {
if (dep.startsWith("@docmd/")) continue;
if (dep.startsWith("@mgks/")) continue;
if (!external.has(dep)) external.set(dep, range);
}
}
const lines = [...external].sort(([a], [b]) => a.localeCompare(b));
if (emitPath) {
// Write a package.json the production stage can `npm install` directly.
// `name` and `version` are required by npm but otherwise arbitrary.
const pkg = {
name: "docmd-runtime-deps",
version: "0.0.0",
private: true,
description: "Auto-generated by tools/print-okf-runtime-deps.js — do not edit.",
dependencies: Object.fromEntries(lines)
};
fs.mkdirSync(path.dirname(emitPath), { recursive: true });
fs.writeFileSync(emitPath, JSON.stringify(pkg, null, 2) + "\n");
console.log(`[generate-runtime-pkg] wrote ${lines.length} deps → ${emitPath}`);
} else {
for (const [dep, range] of lines) {
console.log(`${dep}@${range}`);
}
}