ai-chatbot/scripts/pascal-editor-dev.mjs

203 lines
4.8 KiB
JavaScript

#!/usr/bin/env node
import { spawn, spawnSync } from "node:child_process";
import {
copyFileSync,
existsSync,
mkdirSync,
readFileSync,
writeFileSync,
} from "node:fs";
import path from "node:path";
const cwd = process.cwd();
const pascalDir = path.resolve(
cwd,
process.env.PASCAL_EDITOR_DIR || ".pascal-editor"
);
const repoUrl =
process.env.PASCAL_EDITOR_REPO || "https://github.com/pascalorg/editor.git";
const repoRef = process.env.PASCAL_EDITOR_REF || "main";
const port = process.env.PASCAL_EDITOR_PORT || "3002";
function run(command, args, options = {}) {
const result = spawnSync(command, args, {
cwd,
stdio: "inherit",
...options,
});
if (result.status !== 0) {
process.exit(result.status ?? 1);
}
}
function commandExists(command) {
const result = spawnSync(command, ["--version"], { stdio: "ignore" });
return result.status === 0;
}
function patchNestedNextRoot() {
const configPath = path.join(pascalDir, "apps/editor/next.config.ts");
if (!existsSync(configPath)) {
return;
}
let config = readFileSync(configPath, "utf8");
if (!config.includes("import path from 'node:path'")) {
config = config.replace(
"import type { NextConfig } from 'next'\n",
"import path from 'node:path'\nimport type { NextConfig } from 'next'\n"
);
}
if (!config.includes("root: path.resolve(process.cwd(), '../..')")) {
config = config.replace(
" turbopack: {\n",
" turbopack: {\n root: path.resolve(process.cwd(), '../..'),\n"
);
}
writeFileSync(configPath, config, "utf8");
}
function ensureSparseCheckout() {
run(
"git",
[
"-C",
pascalDir,
"sparse-checkout",
"set",
"apps/editor",
"packages",
"tooling",
"styles",
],
{ cwd }
);
}
function patchCssImports() {
const source = path.join(pascalDir, "styles/elevation.css");
const targetDir = path.join(pascalDir, "apps/editor/styles");
const target = path.join(targetDir, "elevation.css");
const globalsPath = path.join(pascalDir, "apps/editor/app/globals.css");
if (existsSync(source)) {
mkdirSync(targetDir, { recursive: true });
copyFileSync(source, target);
}
if (!existsSync(globalsPath)) {
return;
}
const globals = readFileSync(globalsPath, "utf8");
const patched = globals.replace(
'../../../styles/elevation.css',
'../styles/elevation.css'
);
if (patched !== globals) {
writeFileSync(globalsPath, patched, "utf8");
}
}
function runPackageScript(packageManager, packageDir, scriptName) {
if (packageManager === "bun") {
run("bun", ["--cwd", packageDir, "run", scriptName], { cwd: pascalDir });
return;
}
run("npm", ["--prefix", packageDir, "run", scriptName], { cwd: pascalDir });
}
function ensureBuiltWorkspacePackages(packageManager) {
const requiredBuilds = [
["packages/core", "dist/index.js"],
["packages/viewer", "dist/index.js"],
["packages/nodes", "dist/index.js"],
["packages/mcp", "dist/index.js"],
];
for (const [packageDir, distFile] of requiredBuilds) {
if (existsSync(path.join(pascalDir, packageDir, distFile))) {
continue;
}
runPackageScript(packageManager, packageDir, "build");
}
}
if (!existsSync(pascalDir)) {
mkdirSync(path.dirname(pascalDir), { recursive: true });
run("git", [
"clone",
"--depth",
"1",
"--filter=blob:none",
"--sparse",
"--branch",
repoRef,
repoUrl,
pascalDir,
]);
} else if (!existsSync(path.join(pascalDir, ".git"))) {
console.error(
`${pascalDir} already exists but is not a git checkout. Move it away or set PASCAL_EDITOR_DIR.`
);
process.exit(1);
}
ensureSparseCheckout();
patchCssImports();
const envLocalPath = path.join(pascalDir, ".env.local");
if (!existsSync(envLocalPath)) {
writeFileSync(envLocalPath, `PORT=${port}\n`, "utf8");
}
patchNestedNextRoot();
const hasBun = commandExists("bun");
const hasNpm = commandExists("npm");
const hasNodeModules = existsSync(path.join(pascalDir, "node_modules"));
if (!hasBun && !hasNpm) {
console.error("Pascal Editor needs Bun or npm to install dependencies.");
process.exit(1);
}
if (!hasNodeModules) {
if (hasBun) {
run("bun", ["install"], { cwd: pascalDir });
} else {
run("npm", ["install"], { cwd: pascalDir });
}
}
const packageManager = hasBun ? "bun" : "npm";
ensureBuiltWorkspacePackages(packageManager);
const command = hasBun ? "bun" : "npm";
const args = hasBun
? ["--cwd", "apps/editor", "run", "dev"]
: ["--prefix", "apps/editor", "run", "dev"];
console.log(`Starting self-hosted Pascal Editor on http://localhost:${port}`);
const child = spawn(command, args, {
cwd: pascalDir,
stdio: "inherit",
env: {
...process.env,
PORT: port,
},
});
child.on("exit", (code, signal) => {
if (signal) {
process.kill(process.pid, signal);
return;
}
process.exit(code ?? 0);
});