// 스프레드시트 크기 재기 — gzip 크기(바이트). `test_spreadsheet_size.py` 가 부름. // node spreadsheet_size.mjs <저장소 뿌리> → {"engine": {js, css}, "total": {js, css}, "static": [...]} // engine = 엔진 파일만 줄여 묶은 크기 · total = 실제 앱 빌드에서 스프레드시트를 열 때 받는 조각 // (스프레드시트 조각 + 끌어오는 공용 조각 + css · 첫 화면 `index.html` 조각은 뺌). // static = 스프레드시트 조각을 정적으로 끌어오는 조각(비어야 함 — 늦게만 받음). import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { gzipSync } from "node:zlib"; const root = path.resolve(process.argv[2] ?? "."); const dir = path.join(root, "A00_Common", "spreadsheet"); const { build } = await import( pathToFileURL(path.join(root, "config/node_modules/vite/dist/node/index.js")).href ); const ENGINE = [ "spreadsheet_address", "spreadsheet_parser", "spreadsheet_eval", "spreadsheet_graph", "spreadsheet_refshift", "spreadsheet_commands", "spreadsheet_history", "spreadsheet_recalc", "spreadsheet_functions", "spreadsheet_numfmt", "spreadsheet_fill", ]; async function engineSize() { const out = await build({ configFile: false, logLevel: "silent", root, resolve: { alias: { "@ui": path.join(root, "ui_template") } }, build: { write: false, minify: true, target: "es2022", lib: { entry: ENGINE.map((name) => path.join(dir, `${name}.ts`)), formats: ["es"] }, }, }); let js = 0; for (const o of (Array.isArray(out) ? out : [out]).flatMap((o) => o.output)) { if (o.type === "chunk") js += gzipSync(o.code).length; } return { js, css: 0 }; } async function appSize() { const outDir = fs.mkdtempSync(path.join(os.tmpdir(), "aislo-sheet-size-")); try { await build({ configLoader: "runner", configFile: path.join(root, "vite.config.ts"), logLevel: "silent", build: { outDir, emptyOutDir: true, manifest: true }, }); const manifest = JSON.parse(fs.readFileSync(path.join(outDir, ".vite/manifest.json"), "utf8")); const key = Object.keys(manifest).find((k) => k.endsWith("spreadsheet/spreadsheet.ts")); const gz = (file) => gzipSync(fs.readFileSync(path.join(outDir, file))).length; // M02 가 이미 받은 조각은 새 몫이 아님 — 첫 화면 · M02 페이지 · M02 표(ui_template_sheet 등) // 의 정적 import 사슬. 산출근거 카드는 집계표를 연 뒤에만 열림. const first = new Set(); const mark = (k) => { if (first.has(k)) return; first.add(k); for (const i of manifest[k].imports ?? []) mark(i); }; const page = Object.keys(manifest).find((k) => k.endsWith("M02_MasterTemplete_UI_Page.ts")); mark("index.html"); mark(page); for (const d of manifest[page].dynamicImports ?? []) if (d !== key) mark(d); const size = { js: 0, css: 0 }; const seen = new Set(); const walk = (k) => { if (seen.has(k) || first.has(k)) return; seen.add(k); const e = manifest[k]; size.js += gz(e.file); for (const c of e.css ?? []) size.css += gz(c); for (const i of e.imports ?? []) walk(i); // 그 밖 공용 조각은 셈(넉넉히) }; walk(key); const statics = Object.entries(manifest) .filter(([, v]) => (v.imports ?? []).includes(key)) .map(([k]) => k); return { total: size, static: statics }; } finally { fs.rmSync(outDir, { recursive: true, force: true }); } } const engine = await engineSize(); const { total, static: statics } = await appSize(); console.log(JSON.stringify({ engine, total, static: statics }));