Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QfHKQs7J3hQELKMWQMUJEy
111 lines
3.3 KiB
JavaScript
111 lines
3.3 KiB
JavaScript
// 302-1 수량산출 이음매 — 영역 모으기 → 인쇄 페이지 서버 몸(pdfBody · xlsxBody) 을 node 로.
|
|
// node print_quantity_seam.mjs <저장소 뿌리>
|
|
// 표준입력 = { project, responses: {"/quantity/…": 응답}, image_b64 } (302-3 시험 자료 꼴)
|
|
// 표준출력 끝 줄 = { pdf: {title, sheets}, xlsx: {title, sheets} }
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
|
|
const root = path.resolve(process.argv[2] ?? ".");
|
|
const { build } = await import(
|
|
pathToFileURL(path.join(root, "config/node_modules/vite/dist/node/index.js")).href
|
|
);
|
|
const input = JSON.parse(fs.readFileSync(0, "utf-8"));
|
|
|
|
// 화면 · 세션만 흉내 — 이 길은 DOM 을 안 그림
|
|
class Node {
|
|
constructor() {
|
|
this.children = [];
|
|
this.classList = { toggle() {}, add() {}, remove() {}, contains: () => false };
|
|
this.style = {};
|
|
this.dataset = {};
|
|
}
|
|
append(...kids) {
|
|
this.children.push(...kids);
|
|
}
|
|
appendChild(kid) {
|
|
this.children.push(kid);
|
|
return kid;
|
|
}
|
|
addEventListener() {}
|
|
setAttribute() {}
|
|
}
|
|
const session = new Map();
|
|
const storage = {
|
|
getItem: (k) => (session.has(k) ? session.get(k) : null),
|
|
setItem: (k, v) => session.set(k, String(v)),
|
|
removeItem: (k) => session.delete(k),
|
|
};
|
|
globalThis.document = {
|
|
createElement: () => new Node(),
|
|
createElementNS: () => new Node(),
|
|
createTextNode: () => new Node(),
|
|
head: new Node(),
|
|
body: new Node(),
|
|
addEventListener() {},
|
|
documentElement: new Node(),
|
|
};
|
|
globalThis.window = {
|
|
sessionStorage: storage,
|
|
localStorage: storage,
|
|
location: { origin: "http://localhost", hash: "" },
|
|
addEventListener() {},
|
|
setTimeout: (...a) => setTimeout(...a),
|
|
clearTimeout: (id) => clearTimeout(id),
|
|
};
|
|
globalThis.sessionStorage = storage;
|
|
globalThis.localStorage = storage;
|
|
|
|
const png = Buffer.from(input.image_b64, "base64");
|
|
globalThis.fetch = async (url) => {
|
|
const at = String(url)
|
|
.replace(/^.*\/projects\/[^/]+/, "")
|
|
.split("?")[0];
|
|
if (at === "/quantity/milestone/image")
|
|
return new Response(png, { status: 200, headers: { "Content-Type": "image/png" } });
|
|
const body = input.responses[at];
|
|
return body
|
|
? new Response(JSON.stringify(body), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
})
|
|
: new Response(JSON.stringify({ detail: `없음 ${at}` }), { status: 404 });
|
|
};
|
|
|
|
const outDir = fs.mkdtempSync(path.join(os.tmpdir(), "aislo-302-1-"));
|
|
try {
|
|
await build({
|
|
configFile: false,
|
|
logLevel: "silent",
|
|
root,
|
|
resolve: {
|
|
alias: {
|
|
"@ui": path.join(root, "ui_template"),
|
|
"@config": path.join(root, "config"),
|
|
"@util": path.join(root, "common_util"),
|
|
},
|
|
},
|
|
build: {
|
|
outDir,
|
|
minify: false,
|
|
target: "es2022",
|
|
lib: {
|
|
entry: path.join(root, "resources/tester/print_frame/print_quantity_entry.ts"),
|
|
formats: ["es"],
|
|
fileName: "entry",
|
|
},
|
|
},
|
|
});
|
|
const mod = await import(pathToFileURL(path.join(outDir, "entry.js")).href);
|
|
const sheets = await mod.collectQuantitySheets(input.project);
|
|
console.log(
|
|
JSON.stringify({
|
|
pdf: mod.pdfBody("수량산출", sheets),
|
|
xlsx: mod.xlsxBody("수량산출", sheets),
|
|
}),
|
|
);
|
|
} finally {
|
|
fs.rmSync(outDir, { recursive: true, force: true });
|
|
}
|