import { spawnSync } from "node:child_process";
import * as fs from "node:fs";
import * as fsp from "node:fs/promises";
import * as path from "node:path";
function run(cmd, args, opts = {}) {
return spawnSync(cmd, args, {
stdio: ["ignore", "pipe", "pipe"],
encoding: "utf8",
shell: false,
...opts,
});
}
function runInherit(cmd, args, opts = {}) {
return spawnSync(cmd, args, {
stdio: "inherit",
encoding: "utf8",
shell: false,
...opts,
});
}
function normalizeRelPath(p) {
return String(p || "").replace(/\\/g, "/");
}
function readJsonIfExists(filePath) {
try {
if (!fs.existsSync(filePath)) return undefined;
const raw = fs.readFileSync(filePath, "utf8");
return JSON.parse(raw);
} catch {
return undefined;
}
}
function detectTypeScriptProject() {
if (fs.existsSync("tsconfig.json")) return true;
const pkg = readJsonIfExists("package.json");
const buildScript = String(pkg?.scripts?.build || "");
if (buildScript.includes("tsc")) return true;
if (pkg?.devDependencies?.typescript) return true;
return false;
}
function detectDistEntry() {
// default requested: /dist/index.js
const defaultEntry = "dist/index.js";
const pkg = readJsonIfExists("package.json");
const start = String(pkg?.scripts?.start || "").trim();
const m = /^node\s+(.+)$/.exec(start);
const inferred = m && m[1] ? String(m[1]).trim() : "";
const entry = inferred || defaultEntry;
return normalizeRelPath(entry);
}
function ensureGitRepoRoot() {
const r = run("git", ["rev-parse", "--show-toplevel"], { cwd: process.cwd() });
if (r.status !== 0) {
const err = (r.stderr || r.stdout || "").trim();
throw new Error(
"git repo is required for packaging (to respect .gitignore).\n" + err,
);
}
const top = String(r.stdout || "").trim();
if (!top) throw new Error("Failed to resolve git repo root");
process.chdir(top);
return top;
}
function gitFileList() {
const r = run("git", [
"ls-files",
"-z",
"--cached",
"--others",
"--exclude-standard",
]);
if (r.status !== 0) {
const err = (r.stderr || r.stdout || "").trim();
throw new Error(
"git is required for packaging (and to respect .gitignore).\n" + err,
);
}
const raw = r.stdout || "";
return raw.split("\u0000").filter(Boolean).map(normalizeRelPath);
}
function shouldExcludeFromPackage(relPath) {
const p = normalizeRelPath(relPath);
if (!p) return true;
// 1) scripts/package.mjs does not need to be packaged
if (p === "scripts/package.mjs") return true;
// temp files created by this script
if (p.startsWith(".glosc-tmp/")) return true;
return false;
}
function uniqStable(list) {
const seen = new Set();
const out = [];
for (const item of list) {
if (seen.has(item)) continue;
seen.add(item);
out.push(item);
}
return out;
}
async function ensureDir(p) {
await fsp.mkdir(p, { recursive: true });
}
async function prepareTmpDir() {
const tmpDir = path.join(process.cwd(), ".glosc-tmp");
await fsp.rm(tmpDir, { recursive: true, force: true });
await ensureDir(tmpDir);
return tmpDir;
}
async function writeNulSeparatedList(tmpDir, filePaths) {
// Use NUL-separated pathspec so we can pass it to git safely (no quoting issues).
const listPath = path.join(tmpDir, "pathspec.nul");
const normalized = filePaths.map(normalizeRelPath);
const buf = Buffer.from(normalized.join("