diff --git a/A00_Common/design/design_pipeline_cross.ts b/A00_Common/design/design_pipeline_cross.ts index 4382d9be0..cd22d4114 100644 --- a/A00_Common/design/design_pipeline_cross.ts +++ b/A00_Common/design/design_pipeline_cross.ts @@ -22,13 +22,20 @@ import { effectiveStandardCross, readRockBoundaryDefault, } from "../../B06_Section/B06_Section_UI_Standard_Panel"; -import type { - CrossInput, - SeatedFields, - SectionSpecs, +import { + crossAtSeat, + type CrossInput, + type SeatedFields, + type SectionSpecs, } from "../../B05_Profile/logic/B05_Profile_Pipeline_Detail"; import { SEAT_HINT } from "../../B06_Section/logic/B06_Section_Route_Carry"; -import type { PipelineStation } from "./logic/design_pipeline_types"; +import { + SOURCE_INPUT, + type ChangeSource, + type PipelineInputs, + type PipelineStation, +} from "./logic/design_pipeline_types"; +import type { DesignPipeline } from "./logic/design_pipeline"; import type { StationSeat } from "../../common_util/common_util_station_key"; export interface PipelineCrossSource { @@ -252,3 +259,20 @@ export function pipelineCrossSource( specs, }; } + +/** 맞춤이 다시 먹일 입력 — 지금 입력이 묶음 입력 · 넘긴 입력(follow 전) 둘 다와 다른 것. cross 는 지금 측점 자리로 + * 자리 칸을 가른 뒤 견줌(`crossAtSeat` — 노선 초안 넘김이 새 자리 자리 칸만 걷은 것 = 같은 값 · 508-1c). */ +export function staleInputs( + now: PipelineInputs, + given: Partial, + pipe: DesignPipeline, +): Array<[ChangeSource, unknown]> { + const stations = pipe.output("ground")?.stations ?? []; + const seen = (key: keyof PipelineInputs, value: unknown): string => + JSON.stringify(key === "cross" ? crossAtSeat(value as CrossInput | null, stations) : value); + return (Object.entries(SOURCE_INPUT) as Array<[ChangeSource, keyof PipelineInputs]>) + .filter(([, key]) => + [pipe.input(key), given[key]].every((was) => seen(key, was) !== seen(key, now[key])), + ) + .map(([source, key]) => [source, now[key]]); +} diff --git a/A00_Common/design/design_pipeline_page.ts b/A00_Common/design/design_pipeline_page.ts index 5aacf0107..8be170cbc 100644 --- a/A00_Common/design/design_pipeline_page.ts +++ b/A00_Common/design/design_pipeline_page.ts @@ -70,6 +70,7 @@ import { fetchStructureTypes } from "../../B05_Profile/B05_Profile_Api_Structure import { forgetPageData, pagePipes, pageStructures, trackPageBusy } from "./design_page_data"; import type { PagePipelineOptions, ProfileLink } from "./design_pipeline_page_types"; import { createPageSaved } from "./design_pipeline_page_saved"; +import { staleInputs } from "./design_pipeline_cross"; import { draftDetailOf, heldSectionDetail, @@ -563,10 +564,7 @@ function createPagePipeline( await pipe.settled(); if (disposed || pageSaved.canonRouteId() !== savedSource().routeId) return round; // 정본 바뀜 = restart 몫 if (facilities.saving()) return (facilities.afterSave(), round); // 저장 도중 = 옛 저장분 · 끝나면 다시 - const now = inputsNow(); - const stale = (Object.entries(SOURCE_INPUT) as Array<[ChangeSource, keyof PipelineInputs]>) - .filter(([, key]) => !same(now[key], pipe.input(key)) && !same(now[key], given[key])) - .map(([source, key]): [ChangeSource, unknown] => [source, now[key]]); + const stale = staleInputs(inputsNow(), given, pipe); // cross = 지금 측점 자리로 견줌(508-1c) if (!stale.length) return round; if (round >= RECONCILE_ROUNDS) { options.onUnstable?.(); diff --git a/resources/tester/helper_280_2_step_nav.cjs b/resources/tester/helper_280_2_step_nav.cjs index 003add5f3..3e62d8c86 100644 --- a/resources/tester/helper_280_2_step_nav.cjs +++ b/resources/tester/helper_280_2_step_nav.cjs @@ -2,9 +2,8 @@ * 트랜스파일해 가짜 DOM 위에서 ① 주소마다 줄 차례 ② [<] [>] = 이웃 단계 단추 누름(꺼진 단계 = 알림만)을 * 돌려 JSON 한 벌로 냄. 확장자 `.cjs` 까닭은 `helper_design_view_parity.cjs` 머리와 같음. */ -const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); @@ -148,20 +147,16 @@ const STUBS = { }; globalThis.__DEV = false; -function loadTs(file) { - const src = fs.readFileSync(file, "utf8").replace(/import\.meta\.env\.DEV/g, "globalThis.__DEV"); - const js = ts.transpileModule(src, { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + cache: false, + rewrite: (js) => js.replace(/import\.meta\.env\.DEV/g, "globalThis.__DEV"), + requireFor: () => (request) => { if (request.endsWith(".css")) return {}; if (STUBS[request]) return STUBS[request]; throw new Error(`대역 없음: ${request}`); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); help = loadTs(path.join(ROOT, "ui_template", "ui_template_help.ts")); const layout = loadTs(path.join(ROOT, "ui_template", "ui_template_workflow_layout.ts")); diff --git a/resources/tester/helper_298_2_b07_buttons.cjs b/resources/tester/helper_298_2_b07_buttons.cjs index f145823dd..549f1d48c 100644 --- a/resources/tester/helper_298_2_b07_buttons.cjs +++ b/resources/tester/helper_298_2_b07_buttons.cjs @@ -4,7 +4,7 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const B07 = path.join(__dirname, "..", "..", "B07_DesignDetail"); const calls = []; @@ -36,14 +36,13 @@ global.localStorage = { global.location = { reload: () => calls.push(["reload"]) }; global.document = { querySelectorAll: () => [] }; -function loadTs(file) { - const js = ts.transpileModule(fs.readFileSync(file, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - new Function("exports", "module", "require", js)(box.exports, box, (r) => STUBS[r] ?? {}); - return box.exports; -} +const { loadTs } = makeLoader({ + root: path.join(__dirname, "..", ".."), + cache: false, + requireFor: (full) => { + return (r) => STUBS[r] ?? {}; + }, +}); const Save = loadTs(path.join(B07, "B07_DesignDetail_UI_Save.ts")); diff --git a/resources/tester/helper_298_3_page_dispose.cjs b/resources/tester/helper_298_3_page_dispose.cjs index 453f7c36f..77f5449cd 100644 --- a/resources/tester/helper_298_3_page_dispose.cjs +++ b/resources/tester/helper_298_3_page_dispose.cjs @@ -2,22 +2,13 @@ * `design_leave` 를 그 자리에서 트랜스파일 · 페이지를 다섯 번 다시 세운 뒤 창 듣개 수 · 옛 노드 풀림. * `node --expose-gc` 로 부름 · 결과 JSON 을 stdout 으로. */ -const fs = require("fs"); const path = require("path"); const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader, memoryStorage } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const session = new Map(); -const storage = { - getItem: (key) => (session.has(key) ? session.get(key) : null), - setItem: (key, value) => session.set(key, String(value)), - removeItem: (key) => session.delete(key), - key: (index) => [...session.keys()][index] ?? null, - get length() { - return session.size; - }, -}; +const storage = memoryStorage(); const win = new EventTarget(); const doc = new EventTarget(); const counts = { window: 0, document: 0 }; @@ -54,24 +45,17 @@ const STUBS = { "../router": { currentRoute: () => "/b05-profile", setLeaveGuard: (fn) => (guard = fn) }, }; -const loaded = new Map(); -function loadTs(file) { - if (loaded.has(file)) return loaded.get(file); - const js = ts.transpileModule(fs.readFileSync(file, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 }, - }).outputText; - const box = { exports: {} }; - loaded.set(file, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + target: ts.ScriptTarget.ES2022, + requireFor: (full) => (request) => { if (request in STUBS) return STUBS[request]; if (request.startsWith("@ui/")) return loadTs(path.join(ROOT, "ui_template", `${request.slice(4)}.ts`)); - if (request.startsWith(".")) return loadTs(path.join(path.dirname(file), `${request}.ts`)); + if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const drag = loadTs(path.join(ROOT, "ui_template", "ui_template_overlay_drag.ts")); const leave = loadTs(path.join(ROOT, "A00_Common", "design", "design_leave.ts")); diff --git a/resources/tester/helper_322_0_golden.cjs b/resources/tester/helper_322_0_golden.cjs index 680623b4c..c8a78b601 100644 --- a/resources/tester/helper_322_0_golden.cjs +++ b/resources/tester/helper_322_0_golden.cjs @@ -7,7 +7,7 @@ const fs = require("fs"); const path = require("path"); const ROOT = path.join(__dirname, "..", ".."); -const ts = require(path.join(ROOT, "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const input = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); const clone = (v) => JSON.parse(JSON.stringify(v)); @@ -137,27 +137,11 @@ const resolveFile = (base) => [base, `${base}.ts`, base.replace(/\.js$/, ".ts"), path.join(base, "index.ts")].find( (p) => fs.existsSync(p) && fs.statSync(p).isFile(), ) ?? `${base}.ts`; -const cache = new Map(); -function loadTs(target) { - const full = path.resolve(target); - if (STUBS.has(full)) return STUBS.get(full); - if (cache.has(full)) return cache.get(full); - if (full.endsWith(".css")) return {}; // 화면 꼴(vite 몫) - if (full.endsWith(".json")) { - const data = JSON.parse(fs.readFileSync(full, "utf8")); - cache.set(full, data); - return data; - } - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { - module: ts.ModuleKind.CommonJS, - target: ts.ScriptTarget.ES2020, - esModuleInterop: true, - }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const need = (spec) => { +const { loadTs } = makeLoader({ + root: ROOT, + interop: true, + fileStub: (full) => STUBS.get(full) ?? (full.endsWith(".css") ? {} : undefined), // .css = 화면 꼴(vite 몫) + requireFor: (full) => (spec) => { if (spec === "three") return require(path.join(ROOT, "config", "node_modules", "three")); const alias = { "@util/": "common_util", "@ui/": "ui_template", "@config/": "config" }; for (const [prefix, dir] of Object.entries(alias)) @@ -165,10 +149,8 @@ function loadTs(target) { return loadTs(resolveFile(path.join(ROOT, dir, spec.slice(prefix.length)))); if (spec.startsWith(".")) return loadTs(resolveFile(path.join(path.dirname(full), spec))); return require(spec); - }; - new Function("exports", "module", "require", js)(box.exports, box, need); - return box.exports; -} + }, +}); /* ── 견본 횡단 ───────────────────────────────────────────────────────────── */ const plainAt = (at) => ({ ...clone(input.plain_section), chainage_m: at, kind: "regular" }); diff --git a/resources/tester/helper_323_9_save_redraw.cjs b/resources/tester/helper_323_9_save_redraw.cjs index c8bb2a808..0a6d2d843 100644 --- a/resources/tester/helper_323_9_save_redraw.cjs +++ b/resources/tester/helper_323_9_save_redraw.cjs @@ -2,25 +2,12 @@ * `B06_Section_UI_Page_Persist.ts`(횡단 뒤처리) · `B06_Section_Section_Store.ts`(상세 캐시)를 Node 로 돌림. * 서버 창구만 가짜 — 상세는 저장 전(옛 자리) · 저장 뒤(새 자리) 두 벌. */ -const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader, memoryStorage } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); -const memory = () => { - const map = new Map(); - return { - getItem: (key) => (map.has(key) ? map.get(key) : null), - setItem: (key, value) => map.set(key, String(value)), - removeItem: (key) => map.delete(key), - key: (index) => [...map.keys()][index] ?? null, - get length() { - return map.size; - }, - }; -}; -globalThis.window = { sessionStorage: memory(), localStorage: memory() }; +globalThis.window = { sessionStorage: memoryStorage(), localStorage: memoryStorage() }; globalThis.sessionStorage = globalThis.window.sessionStorage; const calls = []; @@ -66,29 +53,27 @@ STUBS[path.join(ROOT, "A00_Common", "design", "design_save_api.ts")] = { }, }; -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (STUBS[full]) return STUBS[full]; - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +// 라우터(302-1 draft 길 `import.meta.glob`) — 이 로더로 못 읽음 · 저장 차례는 지금 길만 봄 +STUBS[path.join(ROOT, "A00_Common", "router.ts")] = { + currentRoute: () => "", + remountRoute: () => undefined, + navigateTo: () => undefined, + setLeaveGuard: () => undefined, +}; +const anyModule = () => new Proxy({}, { get: () => () => undefined }); +const { loadTs } = makeLoader({ + root: ROOT, + fileStub: (full) => STUBS[full], + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (CATALOG.isCatalogRequest(request)) return CATALOG; if (request.endsWith(".json")) return require(path.join(path.dirname(full), request)); if (request.endsWith(".css")) return {}; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); - if (request.startsWith("@")) return new Proxy({}, { get: () => () => undefined }); + if (request.startsWith("@")) return anyModule(); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); // 측점 열쇠 한 벌(344-3 · 346-2)은 진짜로 — 빈 대역은 열쇠 목록을 못 냄 STUBS["@util/common_util_station_key"] = loadTs( diff --git a/resources/tester/helper_345_4_corridor_worker.cjs b/resources/tester/helper_345_4_corridor_worker.cjs index fa6edd906..056180c4a 100644 --- a/resources/tester/helper_345_4_corridor_worker.cjs +++ b/resources/tester/helper_345_4_corridor_worker.cjs @@ -5,20 +5,12 @@ const fs = require("fs"); const path = require("path"); const ROOT = path.join(__dirname, "..", ".."); -const ts = require(path.join(ROOT, "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts - .transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }) - .outputText.replace(/import\.meta\.url/g, '"file:///x.js"'); - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + rewrite: (js) => js.replace(/import\.meta\.url/g, '"file:///x.js"'), + requireFor: (full) => (request) => { if (request.startsWith("@ui/") || request.startsWith("@config/")) return new Proxy({}, { get: () => () => undefined }); const target = request.startsWith("@util/") @@ -26,10 +18,8 @@ function loadTs(file) { : path.join(path.dirname(full), request); if (request.endsWith(".json")) return JSON.parse(fs.readFileSync(target, "utf8")); return loadTs(fs.existsSync(`${target}.ts`) ? `${target}.ts` : path.join(target, "index.ts")); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const B05 = (name) => path.join(ROOT, "B05_Profile", `${name}.ts`); const { buildCorridor } = loadTs(B05("B05_Profile_UI_Corridor_Build")); diff --git a/resources/tester/helper_347_pipe_id_reconcile.cjs b/resources/tester/helper_347_pipe_id_reconcile.cjs index 98b4032a3..ed131c80b 100644 --- a/resources/tester/helper_347_pipe_id_reconcile.cjs +++ b/resources/tester/helper_347_pipe_id_reconcile.cjs @@ -1,30 +1,25 @@ /* 347 덧 관 고정 id — `reconcilePipes`(되돌리기 · 구조물 폼으로 관 목록이 바뀔 때)가 id 를 따라 옮기는지. * `B05_Profile_UI_Drainage_Parts.ts` 를 그 자리에서 트랜스파일 · 그 밖 import 는 빈 껍데기(틀 = helper_281_1). */ -const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); const stub = new Proxy(function () {}, { get: () => stub, apply: () => stub }); -function loadTs(file, real) { - const js = ts.transpileModule(fs.readFileSync(file, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - const localRequire = (request) => - real.some((name) => request.endsWith(name)) - ? loadTs(path.join(path.dirname(file), `${request}.ts`), ["common_util_pipe_match"]) // 472-6 - : stub; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} +const TOP = path.join(ROOT, "B05_Profile", "B05_Profile_UI_Drainage_Parts.ts"); +// 진짜로 부를 import — 맨 위 = 고름 · 그 밑 = 관 짝 맞춤(472-6) · 그 밖 = 빈 껍데기 +const realOf = (full) => (full === TOP ? ["design_selection"] : ["common_util_pipe_match"]); +const { loadTs } = makeLoader({ + root: ROOT, + cache: false, + requireFor: (full) => (request) => + realOf(full).some((name) => request.endsWith(name)) + ? loadTs(path.join(path.dirname(full), `${request}.ts`)) + : stub, +}); -const { adoptDraftPipes, reconcilePipes } = loadTs( - path.join(ROOT, "B05_Profile", "B05_Profile_UI_Drainage_Parts.ts"), - ["design_selection"], -); +const { adoptDraftPipes, reconcilePipes } = loadTs(TOP); const before = [ { chainage_m: 100, reason: "stream", id: "a1" }, { chainage_m: 200, reason: "user", id: "b2" }, diff --git a/resources/tester/helper_391_routeedit_plan_draw.cjs b/resources/tester/helper_391_routeedit_plan_draw.cjs index 91ba4ea0c..6650277c7 100644 --- a/resources/tester/helper_391_routeedit_plan_draw.cjs +++ b/resources/tester/helper_391_routeedit_plan_draw.cjs @@ -3,27 +3,18 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const B05 = path.join(__dirname, "..", "..", "B05_Profile"); const STUBS = { "./B05_Profile_UI_RouteEdit_Input": { pickHover: () => null } }; -const cache = new Map(); -function loadTs(full) { - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: path.join(__dirname, "..", ".."), + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); /** 그린 것만 적는 가짜 캔버스 — 선 = moveTo/lineTo 점 · 점 = arc 가운데 · 획 색. */ function fakeContext() { diff --git a/resources/tester/helper_398_2_curve_input.cjs b/resources/tester/helper_398_2_curve_input.cjs index 659ac5430..fa6d9c201 100644 --- a/resources/tester/helper_398_2_curve_input.cjs +++ b/resources/tester/helper_398_2_curve_input.cjs @@ -3,13 +3,11 @@ * 같은 길(스냅 켬 = `fitCurveStations` → 길이 고정 R · 하한 → `buildEditedPolyline`)로 흉내. * node helper_398_2_curve_input.cjs */ -const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); const B05 = path.join(ROOT, "B05_Profile"); -const cache = new Map(); let handlers = null; const shows = []; const STUBS = { @@ -23,23 +21,13 @@ const STUBS = { "@ui/ui_template_elements": { showToast: () => {} }, // 곡선 삭제 경고(430) }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => - STUBS[request] ?? - (request.startsWith(".") - ? loadTs(path.join(path.dirname(full), `${request}.ts`)) - : require(request)); - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} +// 곡선 판 가둠(505-3) — 판 크기 감시 · 가짜 그릇엔 판이 없어 빈 감시 +globalThis.ResizeObserver = class { + observe() {} + disconnect() {} +}; + +const { loadTs } = makeLoader({ root: ROOT, stubs: STUBS }); const curve = loadTs(path.join(B05, "logic", "B05_Profile_UI_RouteEdit_Curve.ts")); const edits = loadTs(path.join(B05, "logic", "B05_Profile_UI_RouteEdit_Edits.ts")); @@ -101,7 +89,12 @@ function editor(snap) { status.textContent = `예상노선 — ${message} 꼬리`; }; made = bar.createCurveBar({ - canvas: { getBoundingClientRect: () => ({ left: 0, top: 0, right: 800, bottom: 600 }) }, + canvas: { + getBoundingClientRect: () => ({ left: 0, top: 0, right: 800, bottom: 600 }), + closest: () => null, + parentElement: null, + isConnected: true, + }, container: { querySelector: (sel) => (sel === ".b05-routeedit__status" ? status : null) }, state: () => state, toScreen: (v) => [v[0], v[1]], diff --git a/resources/tester/helper_399_plan_structure_marks.cjs b/resources/tester/helper_399_plan_structure_marks.cjs index 540d11f70..a5a9b13a3 100644 --- a/resources/tester/helper_399_plan_structure_marks.cjs +++ b/resources/tester/helper_399_plan_structure_marks.cjs @@ -5,7 +5,7 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const B05 = path.join(__dirname, "..", "..", "B05_Profile"); const out = {}; @@ -94,25 +94,16 @@ const STUBS = { ], }, }; -const cache = new Map(); -function loadTs(full) { - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: path.join(__dirname, "..", ".."), + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith("@ui/")) return loadTs(path.join(B05, "..", "ui_template", `${request.slice(4)}.ts`)); if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); /** 그린 것만 적는 가짜 캔버스 — 획 = 색 · 굵기 · 그때 옮긴 자리(translate) · 선 점. */ function fakeContext(canvas) { diff --git a/resources/tester/helper_403_triangle_angle.cjs b/resources/tester/helper_403_triangle_angle.cjs index 72e54682e..876966f48 100644 --- a/resources/tester/helper_403_triangle_angle.cjs +++ b/resources/tester/helper_403_triangle_angle.cjs @@ -1,26 +1,22 @@ /* 403 곡선 창 하단 글 헬퍼 — `_Label.curveInfoText` 를 트랜스파일해 경우별 글을 JSON 으로 낸다. */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const B05 = path.join(__dirname, "..", "..", "B05_Profile"); const STUBS = { "@ui/ui_template_elements": { createButton: () => null }, "./B05_Profile_UI_Panel_Fields": { numberField: () => null, toggleButton: () => null }, }; -function loadTs(full) { - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: path.join(__dirname, "..", ".."), + cache: false, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const { curveInfoText } = loadTs(path.join(B05, "B05_Profile_UI_RouteEdit_Label.ts")); process.stdout.write( diff --git a/resources/tester/helper_408_radius_downstream.cjs b/resources/tester/helper_408_radius_downstream.cjs index 6f53ddc1b..1852090c1 100644 --- a/resources/tester/helper_408_radius_downstream.cjs +++ b/resources/tester/helper_408_radius_downstream.cjs @@ -3,13 +3,11 @@ * 같은 길(스냅 켬 = `fitStart` → `fitCurveStations` → 길이 고정 R · 하한 → `buildEditedPolyline`)로 흉내. * node helper_408_radius_downstream.cjs */ -const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); const B05 = path.join(ROOT, "B05_Profile"); -const cache = new Map(); let handlers = null; const STUBS = { "./B05_Profile_UI_RouteEdit_Label": { @@ -22,23 +20,13 @@ const STUBS = { "@ui/ui_template_elements": { showToast: () => {} }, // 곡선 삭제 경고(430) }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => - STUBS[request] ?? - (request.startsWith(".") - ? loadTs(path.join(path.dirname(full), `${request}.ts`)) - : require(request)); - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} +// 곡선 판 가둠(505-3) — 판 크기 감시 · 가짜 그릇엔 판이 없어 빈 감시 +globalThis.ResizeObserver = class { + observe() {} + disconnect() {} +}; + +const { loadTs } = makeLoader({ root: ROOT, stubs: STUBS }); const curve = loadTs(path.join(B05, "logic", "B05_Profile_UI_RouteEdit_Curve.ts")); const edits = loadTs(path.join(B05, "logic", "B05_Profile_UI_RouteEdit_Edits.ts")); @@ -108,7 +96,12 @@ function editor(snap, apexLock = []) { committed = copy(state); }; bar.createCurveBar({ - canvas: { getBoundingClientRect: () => ({ left: 0, top: 0, right: 800, bottom: 600 }) }, + canvas: { + getBoundingClientRect: () => ({ left: 0, top: 0, right: 800, bottom: 600 }), + closest: () => null, + parentElement: null, + isConnected: true, + }, container: { querySelector: (sel) => (sel === ".b05-routeedit__status" ? status : null) }, state: () => state, toScreen: (v) => [v[0], v[1]], diff --git a/resources/tester/helper_411_a_cross_structure.cjs b/resources/tester/helper_411_a_cross_structure.cjs index b31414518..f575cfbfd 100644 --- a/resources/tester/helper_411_a_cross_structure.cjs +++ b/resources/tester/helper_411_a_cross_structure.cjs @@ -5,7 +5,7 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const B05 = path.join(__dirname, "..", "..", "B05_Profile"); const out = {}; @@ -84,23 +84,14 @@ const STUBS = { routeCrossSource: () => null, }, }; -const cache = new Map(); -function loadTs(full) { - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: path.join(__dirname, "..", ".."), + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const cross = loadTs(path.join(B05, "B05_Profile_UI_RouteEdit_Cross.ts")); const station = (key, chainageM, kind, id = null) => ({ key, chainageM, kind, id }); diff --git a/resources/tester/helper_427_1_follow_seat.cjs b/resources/tester/helper_427_1_follow_seat.cjs index 9cedb64f9..05e36c435 100644 --- a/resources/tester/helper_427_1_follow_seat.cjs +++ b/resources/tester/helper_427_1_follow_seat.cjs @@ -4,24 +4,21 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); const ROOT = path.join(__dirname, "..", ".."); -function loadTs(file) { - const js = ts.transpileModule(fs.readFileSync(path.join(ROOT, file), "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - const localRequire = (request) => { +const { loadTs: loadFull, load } = makeLoader({ + root: ROOT, + cache: false, + requireFor: (full) => (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; - if (request.startsWith(".")) return loadTs(path.join(path.dirname(file), `${request}.ts`)); - throw new Error(`stub 없음: ${request} (${file})`); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + if (request.startsWith(".")) return loadFull(path.join(path.dirname(full), `${request}.ts`)); + throw new Error(`stub 없음: ${request} (${path.relative(ROOT, full)})`); + }, +}); +const loadTs = (file) => load(file); // 뿌리 기준 상대 경로 const cases = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); const station = loadTs("common_util/common_util_structure_station.ts"); diff --git a/resources/tester/helper_438_1_straighten.cjs b/resources/tester/helper_438_1_straighten.cjs index 88d6355f9..ebbcb8984 100644 --- a/resources/tester/helper_438_1_straighten.cjs +++ b/resources/tester/helper_438_1_straighten.cjs @@ -4,24 +4,11 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { - if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); - return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} +const { loadTs } = makeLoader({ + root: path.join(__dirname, "..", ".."), +}); const ground = loadTs( path.join(__dirname, "..", "..", "B05_Profile", "logic", "B05_Profile_Grade_Ground.ts"), diff --git a/resources/tester/helper_459_route_seat.cjs b/resources/tester/helper_459_route_seat.cjs index c140ca4f3..a10b27f30 100644 --- a/resources/tester/helper_459_route_seat.cjs +++ b/resources/tester/helper_459_route_seat.cjs @@ -4,30 +4,22 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); -function loadTs(file) { - const full = path.join(ROOT, file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs: loadFull, load } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; if (request.endsWith(".json")) return JSON.parse(fs.readFileSync(path.join(path.dirname(full), request), "utf8")); - if (request.startsWith(".")) return loadTs(path.join(path.dirname(file), `${request}.ts`)); - throw new Error(`stub 없음: ${request} (${file})`); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + if (request.startsWith(".")) return loadFull(path.join(path.dirname(full), `${request}.ts`)); + throw new Error(`stub 없음: ${request} (${path.relative(ROOT, full)})`); + }, +}); +const loadTs = (file) => load(file); // 뿌리 기준 상대 경로 const grade = loadTs("B05_Profile/logic/B05_Profile_Pipeline_Grade.ts"); const carry = loadTs("B05_Profile/logic/B05_Profile_Grade_Carry.ts"); diff --git a/resources/tester/helper_472_save_warnings.cjs b/resources/tester/helper_472_save_warnings.cjs index e1c90c6d0..3b1dbd0f1 100644 --- a/resources/tester/helper_472_save_warnings.cjs +++ b/resources/tester/helper_472_save_warnings.cjs @@ -3,24 +3,15 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => +const { loadTs } = makeLoader({ + root: path.join(__dirname, "..", ".."), + requireFor: (full) => (request) => request.startsWith(".") ? loadTs(path.join(path.dirname(full), `${request}.ts`)) - : require(request); - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + : require(request), +}); const align = loadTs( path.join(__dirname, "..", "..", "B05_Profile", "logic", "B05_Profile_UI_Profile_Alignment.ts"), diff --git a/resources/tester/helper_506_3_b07_save.cjs b/resources/tester/helper_506_3_b07_save.cjs index cd9db841b..dd4cc0d74 100644 --- a/resources/tester/helper_506_3_b07_save.cjs +++ b/resources/tester/helper_506_3_b07_save.cjs @@ -3,7 +3,7 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const B07 = path.join(__dirname, "..", "..", "B07_DesignDetail"); const calls = []; @@ -39,14 +39,13 @@ global.localStorage = { removeItem: (key) => calls.push(["drop", key]) }; global.location = { reload: () => calls.push(["reload"]) }; global.document = { querySelectorAll: () => [] }; -function loadTs(file) { - const js = ts.transpileModule(fs.readFileSync(file, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - new Function("exports", "module", "require", js)(box.exports, box, (r) => STUBS[r] ?? {}); - return box.exports; -} +const { loadTs } = makeLoader({ + root: path.join(__dirname, "..", ".."), + cache: false, + requireFor: (full) => { + return (r) => STUBS[r] ?? {}; + }, +}); Save = loadTs(path.join(B07, "B07_DesignDetail_UI_Save.ts")); diff --git a/resources/tester/helper_54_14_balloon_lines.cjs b/resources/tester/helper_54_14_balloon_lines.cjs index ac8c8d76f..2d90c4da4 100644 --- a/resources/tester/helper_54_14_balloon_lines.cjs +++ b/resources/tester/helper_54_14_balloon_lines.cjs @@ -6,28 +6,18 @@ const fs = require("fs"); const path = require("path"); const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; // 구조물 표(본보기 · 323-7) if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const { balloonLines } = loadTs( path.join(ROOT, "B05_Profile", "logic", "B05_Profile_UI_Structures_Balloon.ts"), diff --git a/resources/tester/helper_54_27_soil_tier_field.cjs b/resources/tester/helper_54_27_soil_tier_field.cjs index 3e60b790d..27b9a9e5c 100644 --- a/resources/tester/helper_54_27_soil_tier_field.cjs +++ b/resources/tester/helper_54_27_soil_tier_field.cjs @@ -4,11 +4,10 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); // ── 화면 그릇 흉내 — 태그 · 클래스 · data · 이벤트만 ───────────────────────── class FakeNode { @@ -144,25 +143,16 @@ const STUBS = { }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; if (STUBS[request]) return STUBS[request]; if (request.startsWith("@util/")) return loadTs(path.join(ROOT, "common_util", `${request.slice(6)}.ts`)); return loadTs(path.join(path.dirname(full), `${request}.ts`)); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const { createFacilityOptionsForm } = loadTs( path.join(ROOT, "B05_Profile", "B05_Profile_UI_Drainage_Facility.ts"), diff --git a/resources/tester/helper_54_9_etc_pill.cjs b/resources/tester/helper_54_9_etc_pill.cjs index 807b5d93f..605c613fa 100644 --- a/resources/tester/helper_54_9_etc_pill.cjs +++ b/resources/tester/helper_54_9_etc_pill.cjs @@ -3,19 +3,12 @@ * * ⚠ 확장자가 `.cjs` 인 까닭은 `helper_b05_structures_form_summary.cjs` 머리와 같다. */ -const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader, memoryStorage } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); -const session = new Map(); -const fakeStorage = { - getItem: (k) => session.get(k) ?? null, - setItem: (k, v) => session.set(k, String(v)), - removeItem: (k) => session.delete(k), -}; +const fakeStorage = memoryStorage(); globalThis.window = { sessionStorage: fakeStorage, localStorage: fakeStorage }; globalThis.sessionStorage = fakeStorage; globalThis.localStorage = fakeStorage; @@ -24,25 +17,7 @@ const STUBS = { "@config/config_frontend": { API_BASE_URL: "/api", API_TIMEOUT_MS: 1000 }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { - if (STUBS[request]) return STUBS[request]; - if (request.endsWith(".json")) return require(path.join(path.dirname(full), request)); - if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); - if (request.startsWith("@")) return new Proxy({}, { get: () => () => {} }); - return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} +const { loadTs } = makeLoader({ root: ROOT, stubs: STUBS }); const marks = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Structures_Marks.ts")); diff --git a/resources/tester/helper_73_4_body_guard.cjs b/resources/tester/helper_73_4_body_guard.cjs index 71de877e6..ba0734a27 100644 --- a/resources/tester/helper_73_4_body_guard.cjs +++ b/resources/tester/helper_73_4_body_guard.cjs @@ -4,44 +4,26 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); // 화면 전용 모듈(토스트 · 글) — 기하 계산엔 안 쓰임 const STUBS = { "@ui/ui_template_toast": { showToast: () => undefined }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - if (full.endsWith(".json")) { - const data = JSON.parse(fs.readFileSync(full, "utf8")); - cache.set(full, data); - return data; - } - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { - module: ts.ModuleKind.CommonJS, - target: ts.ScriptTarget.ES2020, - esModuleInterop: true, - }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + interop: true, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; const alias = { "@util/": "common_util", "@ui/": "ui_template", "@config/": "config" }; for (const [prefix, dir] of Object.entries(alias)) if (request.startsWith(prefix)) return loadTs(resolve(path.join(ROOT, dir, request.slice(prefix.length)))); return loadTs(resolve(path.join(path.dirname(full), request))); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const resolve = (base) => [base, `${base}.ts`, path.join(base, "index.ts")].find( (p) => fs.existsSync(p) && fs.statSync(p).isFile(), diff --git a/resources/tester/helper_79_guard_shape.cjs b/resources/tester/helper_79_guard_shape.cjs index 4fa0852ee..b0b53c880 100644 --- a/resources/tester/helper_79_guard_shape.cjs +++ b/resources/tester/helper_79_guard_shape.cjs @@ -3,44 +3,26 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); // 화면 전용 모듈(토스트 · 글) — 기하 계산엔 안 쓰임 const STUBS = { "@ui/ui_template_toast": { showToast: () => undefined }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - if (full.endsWith(".json")) { - const data = JSON.parse(fs.readFileSync(full, "utf8")); - cache.set(full, data); - return data; - } - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { - module: ts.ModuleKind.CommonJS, - target: ts.ScriptTarget.ES2020, - esModuleInterop: true, - }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + interop: true, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; const alias = { "@util/": "common_util", "@ui/": "ui_template", "@config/": "config" }; for (const [prefix, dir] of Object.entries(alias)) if (request.startsWith(prefix)) return loadTs(resolve(path.join(ROOT, dir, request.slice(prefix.length)))); return loadTs(resolve(path.join(path.dirname(full), request))); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const resolve = (base) => [base, `${base}.ts`, path.join(base, "index.ts")].find( (p) => fs.existsSync(p) && fs.statSync(p).isFile(), diff --git a/resources/tester/helper_80_ford_pipe.cjs b/resources/tester/helper_80_ford_pipe.cjs index 5e8833494..34ca92bb7 100644 --- a/resources/tester/helper_80_ford_pipe.cjs +++ b/resources/tester/helper_80_ford_pipe.cjs @@ -3,44 +3,26 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); // 화면 전용 모듈(토스트 · 글) — 기하 계산엔 안 쓰임 const STUBS = { "@ui/ui_template_toast": { showToast: () => undefined }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - if (full.endsWith(".json")) { - const data = JSON.parse(fs.readFileSync(full, "utf8")); - cache.set(full, data); - return data; - } - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { - module: ts.ModuleKind.CommonJS, - target: ts.ScriptTarget.ES2020, - esModuleInterop: true, - }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + interop: true, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; const alias = { "@util/": "common_util", "@ui/": "ui_template", "@config/": "config" }; for (const [prefix, dir] of Object.entries(alias)) if (request.startsWith(prefix)) return loadTs(resolve(path.join(ROOT, dir, request.slice(prefix.length)))); return loadTs(resolve(path.join(path.dirname(full), request))); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const resolve = (base) => [base, `${base}.ts`, path.join(base, "index.ts")].find( (p) => fs.existsSync(p) && fs.statSync(p).isFile(), diff --git a/resources/tester/helper_90_chute_band.cjs b/resources/tester/helper_90_chute_band.cjs index d61db73a6..461c58ba2 100644 --- a/resources/tester/helper_90_chute_band.cjs +++ b/resources/tester/helper_90_chute_band.cjs @@ -3,22 +3,13 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => loadTs(path.join(path.dirname(full), `${request}.ts`)); - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => loadTs(path.join(path.dirname(full), `${request}.ts`)), +}); class Node { constructor(tag) { diff --git a/resources/tester/helper_91_ground_free.cjs b/resources/tester/helper_91_ground_free.cjs index b2e5208f2..ae12a5ccf 100644 --- a/resources/tester/helper_91_ground_free.cjs +++ b/resources/tester/helper_91_ground_free.cjs @@ -3,44 +3,26 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); // 화면 전용 모듈(토스트 · 글) — 기하 계산엔 안 쓰임 const STUBS = { "@ui/ui_template_toast": { showToast: () => undefined }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - if (full.endsWith(".json")) { - const data = JSON.parse(fs.readFileSync(full, "utf8")); - cache.set(full, data); - return data; - } - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { - module: ts.ModuleKind.CommonJS, - target: ts.ScriptTarget.ES2020, - esModuleInterop: true, - }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + interop: true, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; const alias = { "@util/": "common_util", "@ui/": "ui_template", "@config/": "config" }; for (const [prefix, dir] of Object.entries(alias)) if (request.startsWith(prefix)) return loadTs(resolve(path.join(ROOT, dir, request.slice(prefix.length)))); return loadTs(resolve(path.join(path.dirname(full), request))); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const resolve = (base) => [base, `${base}.ts`, path.join(base, "index.ts")].find( (p) => fs.existsSync(p) && fs.statSync(p).isFile(), diff --git a/resources/tester/helper_b05_b06_selection.cjs b/resources/tester/helper_b05_b06_selection.cjs index e38ec452e..a1b845999 100644 --- a/resources/tester/helper_b05_b06_selection.cjs +++ b/resources/tester/helper_b05_b06_selection.cjs @@ -5,10 +5,9 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); const session = new Map(); const fakeStorage = { @@ -21,22 +20,13 @@ globalThis.sessionStorage = fakeStorage; globalThis.localStorage = fakeStorage; globalThis.requestAnimationFrame = (fn) => fn(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => request.startsWith(".") ? loadTs(path.join(path.dirname(full), `${request}.ts`)) - : require(request); - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + : require(request), +}); const sel = loadTs(path.join(ROOT, "A00_Common", "design", "design_selection.ts")); const pickPage = loadTs(path.join(ROOT, "B06_Section", "B06_Section_UI_Page_Structure_Pick.ts")); diff --git a/resources/tester/helper_b05_curve_radius_floor.cjs b/resources/tester/helper_b05_curve_radius_floor.cjs index c97d8812a..e115c907e 100644 --- a/resources/tester/helper_b05_curve_radius_floor.cjs +++ b/resources/tester/helper_b05_curve_radius_floor.cjs @@ -6,28 +6,18 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); /** TS 한 장을 CommonJS 로 옮겨 실행한다. 상대 import 는 같은 길로 다시 들어온다. */ -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => request.startsWith(".") ? loadTs(path.join(path.dirname(full), `${request}.ts`)) - : require(request); - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + : require(request), +}); const edits = loadTs(path.join(ROOT, "B05_Profile", "logic", "B05_Profile_UI_RouteEdit_Edits.ts")); const { applyCurveLimits, curveShortfalls, radiusFloorM } = edits; diff --git a/resources/tester/helper_b05_routeedit_cross_width.cjs b/resources/tester/helper_b05_routeedit_cross_width.cjs index 1a655ee86..aa9e8abba 100644 --- a/resources/tester/helper_b05_routeedit_cross_width.cjs +++ b/resources/tester/helper_b05_routeedit_cross_width.cjs @@ -5,7 +5,7 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); function memoryStorage() { @@ -115,24 +115,14 @@ const STUBS = { }, }; -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const { createCrossPreview } = loadTs( path.join(ROOT, "B05_Profile", "B05_Profile_UI_RouteEdit_Cross.ts"), diff --git a/resources/tester/helper_b05_routeedit_initial.cjs b/resources/tester/helper_b05_routeedit_initial.cjs index a829a8ddc..da6e4ea33 100644 --- a/resources/tester/helper_b05_routeedit_initial.cjs +++ b/resources/tester/helper_b05_routeedit_initial.cjs @@ -6,10 +6,9 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); function memoryStorage() { const map = new Map(); @@ -30,26 +29,17 @@ globalThis.fetch = () => Promise.resolve({ ok: false }); const STUBS = { "@ui/ui_template_palette": { themeColor: (_name, fallback) => fallback } }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.endsWith(".css")) return {}; if (request.startsWith("@ui/")) return loadTs(path.join(ROOT, "ui_template", `${request.slice(4)}.ts`)); if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const model = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_RouteEdit_Initial_Model.ts")); const { DEFAULT_WEIGHTS } = model; diff --git a/resources/tester/helper_b05_routeedit_initial_apply.cjs b/resources/tester/helper_b05_routeedit_initial_apply.cjs index 83245679a..8754ecf17 100644 --- a/resources/tester/helper_b05_routeedit_initial_apply.cjs +++ b/resources/tester/helper_b05_routeedit_initial_apply.cjs @@ -11,10 +11,9 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); function memoryStorage() { const map = new Map(); @@ -33,23 +32,14 @@ globalThis.fetch = () => Promise.resolve({ ok: false }); const STUBS = { "@ui/ui_template_palette": { themeColor: (_name, fallback) => fallback } }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const model = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_RouteEdit_Initial_Model.ts")); const edits = loadTs(path.join(ROOT, "B05_Profile", "logic", "B05_Profile_UI_RouteEdit_Edits.ts")); diff --git a/resources/tester/helper_b05_routeedit_lock.cjs b/resources/tester/helper_b05_routeedit_lock.cjs index 6df7fd975..ae942e25e 100644 --- a/resources/tester/helper_b05_routeedit_lock.cjs +++ b/resources/tester/helper_b05_routeedit_lock.cjs @@ -6,10 +6,9 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); // 브라우저 저장소 흉내 — `b_page_state` 가 읽고 쓰는 자리. function memoryStorage() { @@ -31,23 +30,14 @@ globalThis.fetch = () => Promise.resolve({ ok: false }); const STUBS = { "@ui/ui_template_palette": { themeColor: (_name, fallback) => fallback } }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const lock = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_RouteEdit_Lock.ts")); @@ -86,7 +76,7 @@ const arcAll = [1, 2].map((index) => lock.arcLocked([true, true, true, true], in // ⑤ 노선을 못 읽은(노드 1개 이하) 채 닫으면 적어 둔 고정을 안 지운다 · 깨진 값은 기본값. lock.writeRouteLocks("p-1", [], []); const keptAfterEmpty = lock.readRouteLocks("p-1"); -local.setItem("aislo:pref:routeedit-locks:p-3", "{\"start\":false,\"apex\":[[1,\"x\"],[2,3]]}"); +local.setItem("aislo:pref:routeedit-locks:p-3", '{"start":false,"apex":[[1,"x"],[2,3]]}'); const broken = lock.readRouteLocks("p-3"); process.stdout.write( diff --git a/resources/tester/helper_b05_routeedit_pick.cjs b/resources/tester/helper_b05_routeedit_pick.cjs index 1859f0238..28c99443f 100644 --- a/resources/tester/helper_b05_routeedit_pick.cjs +++ b/resources/tester/helper_b05_routeedit_pick.cjs @@ -4,10 +4,9 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); const STUBS = { "@ui/ui_template_palette": { themeColor: (_name, fallback) => fallback }, "@util/common_util_svg": { @@ -15,23 +14,14 @@ const STUBS = { }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const input = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_RouteEdit_Input.ts")); const curve = loadTs(path.join(ROOT, "B05_Profile", "logic", "B05_Profile_UI_RouteEdit_Curve.ts")); diff --git a/resources/tester/helper_b05_routeedit_view.cjs b/resources/tester/helper_b05_routeedit_view.cjs index 5380bd918..2bf2b8154 100644 --- a/resources/tester/helper_b05_routeedit_view.cjs +++ b/resources/tester/helper_b05_routeedit_view.cjs @@ -6,10 +6,9 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); // 브라우저 저장소 흉내 — `b_page_state` 가 읽고 쓰는 자리. function memoryStorage() { @@ -31,23 +30,14 @@ globalThis.fetch = () => Promise.resolve({ ok: false }); const STUBS = { "@ui/ui_template_palette": { themeColor: (_name, fallback) => fallback } }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const viewMod = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_RouteEdit_View.ts")); const render = loadTs(path.join(ROOT, "B04_PreProcess", "B04_PreProcess_UI_MapRender.ts")); diff --git a/resources/tester/helper_b05_station_roundtrip.cjs b/resources/tester/helper_b05_station_roundtrip.cjs index ab81f8aef..717fdd8f8 100644 --- a/resources/tester/helper_b05_station_roundtrip.cjs +++ b/resources/tester/helper_b05_station_roundtrip.cjs @@ -4,30 +4,20 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => CATALOG.isCatalogRequest(request) ? CATALOG : request.startsWith(".") ? loadTs(path.join(path.dirname(full), `${request}.ts`)) - : require(request); - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + : require(request), +}); // 측점 칸이 쓰는 만큼만 흉내 낸 DOM function fakeElement() { diff --git a/resources/tester/helper_b05_structures_form_summary.cjs b/resources/tester/helper_b05_structures_form_summary.cjs index 1cd5f9cda..8338d6767 100644 --- a/resources/tester/helper_b05_structures_form_summary.cjs +++ b/resources/tester/helper_b05_structures_form_summary.cjs @@ -5,11 +5,10 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); // ── 화면 그릇 흉내 — 요소 · 클래스 · 이벤트 · 글자만 ───────────────────────── class FakeNode { @@ -115,15 +114,9 @@ const STUBS = { "@config/config_frontend": { API_BASE_URL: "/api", API_TIMEOUT_MS: 1000 }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; if (STUBS[request]) return STUBS[request]; // 화면 공용 부품(@ui/…) — 이 시험이 안 쓰는 것이라 뭘 불러도 아무 일 안 하는 함수로 막아 둠 @@ -138,11 +131,8 @@ function loadTs(file) { return request.startsWith(".") ? loadTs(path.join(path.dirname(full), `${request}.ts`)) : require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const B05 = (name) => loadTs(path.join(ROOT, "B05_Profile", `${name}.ts`)); const item = loadTs(path.join(ROOT, "common_util", "common_util_summary_item.ts")); diff --git a/resources/tester/helper_b06_adjust_single_form.cjs b/resources/tester/helper_b06_adjust_single_form.cjs index ec94f4aaf..7fe94db76 100644 --- a/resources/tester/helper_b06_adjust_single_form.cjs +++ b/resources/tester/helper_b06_adjust_single_form.cjs @@ -5,7 +5,7 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); @@ -93,25 +93,16 @@ const STUBS = { }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; if (STUBS[request]) return STUBS[request]; if (request.startsWith("@util/")) return loadTs(path.join(ROOT, "common_util", `${request.slice(6)}.ts`)); return loadTs(path.join(path.dirname(full), `${request}.ts`)); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const B06 = (name) => loadTs(path.join(ROOT, "B06_Section", `${name}.ts`)); const { buildStructurePanel } = B06("B06_Section_UI_Cross_Structure_Panel"); diff --git a/resources/tester/helper_b06_draft_pipe_replay.cjs b/resources/tester/helper_b06_draft_pipe_replay.cjs index df2316c72..2d6d86544 100644 --- a/resources/tester/helper_b06_draft_pipe_replay.cjs +++ b/resources/tester/helper_b06_draft_pipe_replay.cjs @@ -7,11 +7,10 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); const session = new Map(); const fakeStorage = { @@ -23,15 +22,9 @@ globalThis.window = { sessionStorage: fakeStorage, localStorage: fakeStorage }; globalThis.sessionStorage = fakeStorage; globalThis.localStorage = fakeStorage; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; // 화면 공용 부품(@ui/…)은 이 시험이 안 쓰는 것이라 아무 일 안 하는 함수로 막아 둠 if (request.startsWith("@ui/")) return new Proxy({}, { get: () => () => {} }); @@ -42,11 +35,8 @@ function loadTs(file) { return request.startsWith(".") ? loadTs(path.join(path.dirname(full), `${request}.ts`)) : require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const DEF = JSON.parse( fs.readFileSync(path.join(ROOT, "resources", "master_template", "구조물정의.json"), "utf8"), diff --git a/resources/tester/helper_design_profile_single.cjs b/resources/tester/helper_design_profile_single.cjs index 06f20767b..b14a5244f 100644 --- a/resources/tester/helper_design_profile_single.cjs +++ b/resources/tester/helper_design_profile_single.cjs @@ -5,7 +5,7 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); @@ -27,23 +27,9 @@ const local = memoryStorage(); globalThis.window = { sessionStorage: session, localStorage: local }; globalThis.sessionStorage = session; -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { - if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); - return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} +const { loadTs } = makeLoader({ + root: ROOT, +}); const { useDesignStore } = loadTs(path.join(ROOT, "A00_Common", "design", "design_store.ts")); const history = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Profile_History.ts")); diff --git a/resources/tester/helper_design_store_save.cjs b/resources/tester/helper_design_store_save.cjs index 4495215e8..d77b34df1 100644 --- a/resources/tester/helper_design_store_save.cjs +++ b/resources/tester/helper_design_store_save.cjs @@ -4,7 +4,7 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); @@ -121,24 +121,14 @@ const STUBS = { "./design_leave": { designSignature: () => "", markDesignClean: () => undefined }, }; -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const { useDesignStore } = loadTs(path.join(ROOT, "A00_Common", "design", "design_store.ts")); const edit = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Profile_Edit.ts")); diff --git a/resources/tester/helper_design_view_parity.cjs b/resources/tester/helper_design_view_parity.cjs index e8a83b677..cb2fd68a1 100644 --- a/resources/tester/helper_design_view_parity.cjs +++ b/resources/tester/helper_design_view_parity.cjs @@ -6,10 +6,9 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); // ── 화면 그릇 흉내 — 요소 · 클래스 · 속성 · 글자 · 자식 ────────────────────────── class FakeNode { @@ -147,24 +146,15 @@ const STUBS = { }, }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.endsWith(".css")) return {}; if (request.startsWith(".")) return loadTs(`${path.resolve(path.dirname(full), request)}.ts`); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const panel = loadTs(path.join(ROOT, "A00_Common", "design", "design_panel.ts")); loadTs(path.join(ROOT, "A00_Common", "design", "design_sections.ts")); diff --git a/resources/tester/helper_item_picker_project.cjs b/resources/tester/helper_item_picker_project.cjs index 18928fc96..350a58372 100644 --- a/resources/tester/helper_item_picker_project.cjs +++ b/resources/tester/helper_item_picker_project.cjs @@ -9,31 +9,21 @@ */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); -const cache = new Map(); const STUBS = { "@config/config_frontend": { API_BASE_URL: "/api" } }; -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.endsWith(".css")) return {}; if (request.startsWith(".")) return loadTs(`${path.resolve(path.dirname(full), request)}.ts`); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const reply = (status, body) => ({ status, diff --git a/resources/tester/helper_m02_hopyo_check.cjs b/resources/tester/helper_m02_hopyo_check.cjs index bc42f6022..b83a8f709 100644 --- a/resources/tester/helper_m02_hopyo_check.cjs +++ b/resources/tester/helper_m02_hopyo_check.cjs @@ -3,7 +3,7 @@ * stdin = {lines, rows, ledger} · stdout = {check, choose, ledger, sheet, calls, pick} JSON. */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); const STUBS = { "@config/config_frontend": { API_BASE_URL: "/api" } }; @@ -28,25 +28,16 @@ const stub = new Proxy( : () => {}, }, ); -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (STUB_FILES.some((name) => request.endsWith(name))) return stub; if (request.startsWith(".") && !request.endsWith(".css")) return loadTs(`${path.resolve(path.dirname(full), request)}.ts`); return stub; - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const store = new Map(); global.sessionStorage = { diff --git a/resources/tester/helper_m02_hopyo_pick.cjs b/resources/tester/helper_m02_hopyo_pick.cjs index 3434f4991..7ae44299e 100644 --- a/resources/tester/helper_m02_hopyo_pick.cjs +++ b/resources/tester/helper_m02_hopyo_pick.cjs @@ -4,7 +4,7 @@ * stdin = {rows, built} · stdout = {calls, kinds, rows, picked, project, view, sheet} JSON. */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); const STUBS = { "@config/config_frontend": { API_BASE_URL: "/api" } }; @@ -17,25 +17,16 @@ const STUB_FILES = [ "M01_MasterData_UI_LogicLab_Source", ]; const stub = new Proxy({}, { get: (_t, k) => (k === "currentLanguageIndex" ? () => 0 : () => {}) }); -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (STUB_FILES.some((name) => request.endsWith(name))) return stub; if (request.startsWith(".") && !request.endsWith(".css")) return loadTs(`${path.resolve(path.dirname(full), request)}.ts`); return stub; - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); (async () => { const input = JSON.parse(fs.readFileSync(0, "utf8")); diff --git a/resources/tester/helper_m02_hopyo_seam.cjs b/resources/tester/helper_m02_hopyo_seam.cjs index 705d02497..675e1a957 100644 --- a/resources/tester/helper_m02_hopyo_seam.cjs +++ b/resources/tester/helper_m02_hopyo_seam.cjs @@ -5,7 +5,7 @@ * count: {top}} JSON — count = 「후보」 줄 1순위 근거 맞음. */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); const STUBS = { "@config/config_frontend": { API_BASE_URL: "/api" } }; @@ -17,25 +17,16 @@ const STUB_FILES = [ "M01_MasterData_UI_LogicLab_Source", ]; const stub = new Proxy({}, { get: (_t, k) => (k === "currentLanguageIndex" ? () => 0 : () => {}) }); -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (STUB_FILES.some((name) => request.endsWith(name))) return stub; if (request.startsWith(".") && !request.endsWith(".css")) return loadTs(`${path.resolve(path.dirname(full), request)}.ts`); return stub; - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const input = JSON.parse(fs.readFileSync(0, "utf8")); const M02 = path.join(ROOT, "M02_MasterTemplete"); diff --git a/resources/tester/helper_m02_labor_national.cjs b/resources/tester/helper_m02_labor_national.cjs index 6cea2ca9c..73b686eda 100644 --- a/resources/tester/helper_m02_labor_national.cjs +++ b/resources/tester/helper_m02_labor_national.cjs @@ -2,7 +2,7 @@ * `el` 대역 = 나무 꼴 {tag, className, text, attrs, children} · stdin = {origin} · stdout = 나무 JSON. */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); const el = (tag, o = {}) => ({ tag, ...o, children: o.children ?? [] }); @@ -11,24 +11,15 @@ const STUBS = { "@ui/ui_template_locale": { currentLanguageIndex: 0 }, "@config/config_frontend": { API_BASE_URL: "/api" }, }; -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".") && !request.endsWith(".css")) return loadTs(`${path.resolve(path.dirname(full), request)}.ts`); return new Proxy({}, { get: () => () => ({}) }); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const input = JSON.parse(fs.readFileSync(0, "utf8")); const national = loadTs( diff --git a/resources/tester/helper_m02_precombo_scope.cjs b/resources/tester/helper_m02_precombo_scope.cjs index 71261dffc..c6a04c4c4 100644 --- a/resources/tester/helper_m02_precombo_scope.cjs +++ b/resources/tester/helper_m02_precombo_scope.cjs @@ -4,29 +4,20 @@ * stdin = {rows, def?, view?} · stdout = {urls, tree, classes} JSON. */ const fs = require("fs"); const path = require("path"); -const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); const ROOT = path.join(__dirname, "..", ".."); const STUBS = { "@config/config_frontend": { API_BASE_URL: "/api" } }; const stub = new Proxy({}, { get: (_t, k) => (k === "currentLanguageIndex" ? () => 0 : () => {}) }); -const cache = new Map(); -function loadTs(file) { - const full = path.resolve(file); - if (cache.has(full)) return cache.get(full); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const localRequire = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.startsWith(".") && !request.endsWith(".css")) return loadTs(`${path.resolve(path.dirname(full), request)}.ts`); return stub; - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const urls = []; global.fetch = async (url) => { diff --git a/resources/tester/helper_ts_stub_loader.cjs b/resources/tester/helper_ts_stub_loader.cjs index 30eb9b369..7115b4d5c 100644 --- a/resources/tester/helper_ts_stub_loader.cjs +++ b/resources/tester/helper_ts_stub_loader.cjs @@ -32,7 +32,9 @@ function memoryStorage() { * requireFor(full, file) 풀이 함수를 헬퍼가 직접 지음(기본 풀이 대신) · 인자 없으면 진짜 require * interop esModuleInterop 로 트랜스파일 * cache:false 같은 파일을 부를 때마다 새로 읽음(기본은 한 번만) - * fileStub(full) 파일 경로로 통째 대신할 값(없으면 undefined) */ + * fileStub(full) 파일 경로로 통째 대신할 값(없으면 undefined) + * target 트랜스파일 대상(기본 ES2020) + * rewrite(js) 트랜스파일한 글 손봄(`import.meta` 처럼 CommonJS 가 못 읽는 것) */ function makeLoader({ root, stubs = {}, @@ -41,6 +43,8 @@ function makeLoader({ interop = false, cache: useCache = true, fileStub, + target = ts.ScriptTarget.ES2020, + rewrite = (js) => js, }) { const cache = new Map(); function defaultRequire(full) { @@ -68,9 +72,11 @@ function makeLoader({ if (useCache) cache.set(full, data); return data; } - const compilerOptions = { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }; + const compilerOptions = { module: ts.ModuleKind.CommonJS, target }; if (interop) compilerOptions.esModuleInterop = true; - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { compilerOptions }).outputText; + const js = rewrite( + ts.transpileModule(fs.readFileSync(full, "utf8"), { compilerOptions }).outputText, + ); const box = { exports: {} }; if (useCache) cache.set(full, box.exports); const localRequire = requireFor ? requireFor(full, file) : defaultRequire(full); diff --git a/resources/tester/helper_verify_project.py b/resources/tester/helper_verify_project.py index e2998d0ee..7de794f27 100644 --- a/resources/tester/helper_verify_project.py +++ b/resources/tester/helper_verify_project.py @@ -9,8 +9,6 @@ from __future__ import annotations import os import re - -import pytest from pathlib import Path ROOT = Path(__file__).resolve().parents[2] @@ -57,8 +55,13 @@ def find_project(*rels: str) -> Path: return min(found, key=lambda project: _touched(project, rels), default=NONE) -#: 제품 흠으로 보고한 시험만(508-1a) — 고치면 그 시험에서 걷음 -DRIFT = pytest.mark.xfail( - strict=False, - reason="508-1a 제품 흠: 노선 초안 자리 칸 넘김이 cross 입력을 바꿔 cross 군더더기 차례(a02367e61)", -) +def own_ports() -> tuple[str, str]: + """OWNERS.md 표에서 이 폴더 줄의 (API, vite) 주소 — 줄이 없으면 빈 글 둘(시험이 안 돎 · 508-1a).""" + owners = ROOT / "OWNERS.md" + for line in owners.read_text(encoding="utf-8-sig").splitlines() if owners.is_file() else []: + cells = [cell.strip().strip("`") for cell in line.strip().strip("|").split("|")] + if len(cells) >= 5 and Path(cells[2]).resolve() == ROOT.resolve(): + ports = re.findall(r"\d{4,5}", cells[4]) + if len(ports) == 2: + return f"http://localhost:{ports[0]}", f"http://localhost:{ports[1]}" + return "", "" diff --git a/resources/tester/test_225_drawer_shortcuts.py b/resources/tester/test_225_drawer_shortcuts.py index 30d355060..7265294b1 100644 --- a/resources/tester/test_225_drawer_shortcuts.py +++ b/resources/tester/test_225_drawer_shortcuts.py @@ -19,7 +19,9 @@ from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] -BASE = os.environ.get("AISLO_UI_BASE", "http://localhost:5177") # sub2 화면 서버(OWNERS 자기 줄) +from helper_verify_project import own_ports # noqa: E402 + +BASE = os.environ.get("AISLO_UI_BASE", own_ports()[1]) # 화면 서버 = OWNERS.md 이 폴더 줄 LAYOUT = ROOT / "ui_template" / "ui_template_workflow_layout.ts" NAV = ROOT / "A00_Common" / "m_settings_nav.ts" STEPS = ["파일입력", "전처리", "종단설계"] diff --git a/resources/tester/test_309_2_calc_sites_browser.py b/resources/tester/test_309_2_calc_sites_browser.py index 11f1eb950..804eee455 100644 --- a/resources/tester/test_309_2_calc_sites_browser.py +++ b/resources/tester/test_309_2_calc_sites_browser.py @@ -13,7 +13,6 @@ from __future__ import annotations import asyncio import json -import re import subprocess import urllib.request from pathlib import Path @@ -22,20 +21,9 @@ import pytest ROOT = Path(__file__).resolve().parents[2] +from helper_verify_project import own_ports # noqa: E402 -def _own_ports() -> list[str]: - """OWNERS.md 표에서 이 폴더 줄의 (API, vite) 포트(508-1a).""" - for line in (ROOT / "OWNERS.md").read_text(encoding="utf-8-sig").splitlines(): - cells = [cell.strip().strip("`") for cell in line.strip().strip("|").split("|")] - if len(cells) >= 5 and Path(cells[2]).resolve() == ROOT.resolve(): - return re.findall(r"\d{4,5}", cells[4]) - return [] - - -_PORTS = _own_ports() -API, BASE = ( - [f"http://localhost:{port}" for port in _PORTS] if len(_PORTS) == 2 else ["", ""] -) # 이 폴더 백엔드 · 그 백엔드를 프록시하는 vite +API, BASE = own_ports() # 이 폴더 백엔드 · 그 백엔드를 프록시하는 vite(OWNERS.md 이 폴더 줄) BOARD = ROOT / "resources" / "master_template" / "calc_sites.json" PROJECT = "cb15892a-5788-4a26-9316-20a15421f68b" # 검증_sub2(OWNERS.md) SHOTS = ROOT / "tmp" / "shots_504_2" diff --git a/resources/tester/test_323_9_save_redraw.py b/resources/tester/test_323_9_save_redraw.py index a9d92f71d..ebf2504a9 100644 --- a/resources/tester/test_323_9_save_redraw.py +++ b/resources/tester/test_323_9_save_redraw.py @@ -42,13 +42,11 @@ def test_after_save_cards_show_server_new_detail(out: dict) -> None: def test_no_redraw_from_stale_cache_while_saving(out: dict) -> None: assert out["savingSeen"] == [True, False] and out["savingIdle"] is False - # 저장 도중 신호는 건너뜀 · 뒤처리가 새로 한 번 받음(오버레이 안) + # 저장 도중 신호는 건너뜀 · 뒤처리가 새로 한 번 받음 · 노선 그대로 [저장] = 가림막 없음(498-3 D1) assert out["calls"] == [ - "overlay:on", "refresh:skip", "cross:save", "fetch:새 자리 4+8 · 47+0", - "overlay:off", ] diff --git a/resources/tester/test_349_0j_draft_vs_saved.py b/resources/tester/test_349_0j_draft_vs_saved.py index 5eea91ca9..d25d7fd66 100644 --- a/resources/tester/test_349_0j_draft_vs_saved.py +++ b/resources/tester/test_349_0j_draft_vs_saved.py @@ -87,6 +87,7 @@ def _server(ts: dict, sampler) -> list[dict]: "cut": design["cut_area_m2"], "fill": design["fill_area_m2"], "lineEnds": [line[0]["offset_m"], line[-1]["offset_m"]], + "unclosed": wide is not None, } ) return out @@ -172,9 +173,8 @@ def test_격자_없으면_넓힘_없음(r): def test_초안_상세_표본은_넓힌_표본(r): - widened = [ - s for s in r["wide"]["stations"] if s["lineEnds"] and max(map(abs, s["lineEnds"])) > 20.01 - ] + # 넓힘 표본 = 서버 `extend_unclosed` 가 표본을 낸 미교차 측점(지표면 끝에 막혀 못 넓혀도 · 508-1c) + widened = [s for s in r["server"] if s["unclosed"]] assert r["wide"]["shownWide"] == len(widened) # 349-0n — 서버도 넓힌 표본을 상세에 남김 assert r["plain"]["shownWide"] == 0 diff --git a/resources/tester/test_349_0o_redo_same.py b/resources/tester/test_349_0o_redo_same.py index 6b3afb5c3..d3c40c79a 100644 --- a/resources/tester/test_349_0o_redo_same.py +++ b/resources/tester/test_349_0o_redo_same.py @@ -25,7 +25,7 @@ ROOT = HERE.parents[1] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) -from helper_verify_project import DRIFT, ROUTE_SET, SKIP_REASON, find_project # noqa: E402 +from helper_verify_project import ROUTE_SET, SKIP_REASON, find_project # noqa: E402 from B05_Profile.B05_Profile_Structures_Schema import structure_type_map # noqa: E402 from common_util.common_util_surface_sampler import dtm_grid_bytes # noqa: E402 @@ -149,7 +149,6 @@ def test_열기(r): assert o["final"] is not None -@DRIFT def test_끌기_새_노선(r): d = r["first"] assert [x["sources"] for x in d["runs"]] == [["route"]], d["runs"] @@ -188,7 +187,6 @@ def test_다시_하기_첫_끌기_값(r): assert d["stations"] == f["stations"] -@DRIFT def test_횡단_보기_다녀와도_열기_값(r): o = r["open"] for step in (r["cross"], r["back"]): diff --git a/resources/tester/test_505_3_cross_card_screen.py b/resources/tester/test_505_3_cross_card_screen.py index ffb9530e4..34cd6abe4 100644 --- a/resources/tester/test_505_3_cross_card_screen.py +++ b/resources/tester/test_505_3_cross_card_screen.py @@ -19,7 +19,9 @@ import pytest ROOT = Path(__file__).resolve().parents[2] # 기본 = sub4 환경 · 다른 창은 AISLO_505_3_BASE · AISLO_505_3_PROJECT 로 바꿔 test_0 만 돌림(test_1 · 2 는 sub4 자료) -BASE = os.environ.get("AISLO_505_3_BASE", "http://localhost:5179") +from helper_verify_project import own_ports # noqa: E402 + +BASE = os.environ.get("AISLO_505_3_BASE", own_ports()[1]) # 화면 서버 = OWNERS.md 이 폴더 줄 PROJECT = os.environ.get("AISLO_505_3_PROJECT", "c725d747-50ae-46b0-87ce-4d4e56a53cf3") # 검증_sub4 SHOTS = ROOT / "tmp" / "shots_505_3" PIPE_M = 388 # 검증_sub4 배관 하나(관경 빈칸 · 19+8) diff --git a/resources/tester/test_505_4_refresh_blank_pipe.py b/resources/tester/test_505_4_refresh_blank_pipe.py index 7921a19da..7f44abdd0 100644 --- a/resources/tester/test_505_4_refresh_blank_pipe.py +++ b/resources/tester/test_505_4_refresh_blank_pipe.py @@ -14,7 +14,9 @@ from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] -BASE = "http://localhost:5179" +from helper_verify_project import own_ports # noqa: E402 + +BASE = own_ports()[1] # 화면 서버 = OWNERS.md 이 폴더 줄 PROJECT = "c725d747-50ae-46b0-87ce-4d4e56a53cf3" # 검증_sub4(OWNERS.md) LIST_URL = f"{BASE}/api/projects/{PROJECT}/route/structures" diff --git a/resources/tester/test_506_3_b07_browser.py b/resources/tester/test_506_3_b07_browser.py index 667c53788..c0c275adb 100644 --- a/resources/tester/test_506_3_b07_browser.py +++ b/resources/tester/test_506_3_b07_browser.py @@ -26,7 +26,9 @@ import pytest ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) -BASE = "http://localhost:5177" +from helper_verify_project import own_ports # noqa: E402 + +BASE = own_ports()[1] # 화면 서버 = OWNERS.md 이 폴더 줄 PROJECT = "cb15892a-5788-4a26-9316-20a15421f68b" # 검증_sub2(OWNERS.md) SHOTS = ROOT / "tmp" / "shots_506_3" COLS = ( diff --git a/resources/tester/test_app_settings.py b/resources/tester/test_app_settings.py index 1bc7f5011..071a1f6b1 100644 --- a/resources/tester/test_app_settings.py +++ b/resources/tester/test_app_settings.py @@ -18,7 +18,9 @@ import pytest HERE = os.path.dirname(__file__) ROOT = os.path.join(HERE, "..", "..") # 화면 서버(OWNERS 자기 줄) — 돌리는 창 포트로 바꿈 -BASE = os.environ.get("AISLO_UI_BASE", "http://localhost:5177") +from helper_verify_project import own_ports # noqa: E402 + +BASE = os.environ.get("AISLO_UI_BASE", own_ports()[1]) KEY = "aislo:pref:input-prefs" # 앱 스크립트보다 먼저 — 설정 키에 쓰는 횟수를 셈 @@ -51,7 +53,7 @@ def node() -> dict: def test_별칭_검사(node): - assert node["commands"] == 153 + assert node["commands"] == 154 # 488-3 IMPORT 더함 assert node["parse"] == [["LL", "CC", "X1"], [], []] assert node["ok"] == {} # 남의 기본 별칭(C = CIRCLE)을 내 것으로 덮는 건 됨(acad.pgp 식) assert node["chars"] == {"LINE": "chars", "CIRCLE": "chars"} @@ -138,7 +140,7 @@ def test_설정_창_탭_저장_물음(): page.click("[data-settings-tab=cad]") assert _visible_panel_has(page, "input[data-cad-command]") assert not _visible_panel_has(page, "input[type=radio]") - assert page.locator(".app-settings input[data-cad-command]").count() == 153 + assert page.locator(".app-settings input[data-cad-command]").count() == 154 page.click("[data-settings-tab=keys]") assert ( page.evaluate( diff --git a/resources/tester/test_cad_touch.py b/resources/tester/test_cad_touch.py index 386dd5ac1..65fcd318a 100644 --- a/resources/tester/test_cad_touch.py +++ b/resources/tester/test_cad_touch.py @@ -10,7 +10,9 @@ import urllib.request import pytest -URL = os.environ.get("CAD_TOUCH_URL", "http://localhost:5174/b07-cad/") +from helper_verify_project import own_ports # noqa: E402 + +URL = os.environ.get("CAD_TOUCH_URL", f"{own_ports()[1]}/b07-cad/") # OWNERS.md 이 폴더 줄 def _up() -> bool: diff --git a/resources/tester/test_device_audit.py b/resources/tester/test_device_audit.py index aaf6d0404..c5f3a2023 100644 --- a/resources/tester/test_device_audit.py +++ b/resources/tester/test_device_audit.py @@ -20,7 +20,9 @@ import pytest ROOT = Path(__file__).resolve().parents[2] # 다른 창 서버로 돌릴 때 — AISLO_UI_BASE · AISLO_UI_PROJECT(OWNERS 자기 줄) -BASE = os.environ.get("AISLO_UI_BASE", "http://localhost:5174") +from helper_verify_project import own_ports # noqa: E402 + +BASE = os.environ.get("AISLO_UI_BASE", own_ports()[1]) PROJECT_NAME = os.environ.get("AISLO_UI_PROJECT", "검증_sub1") PAGES = [ "a01-home", diff --git a/resources/tester/test_spreadsheet_touch.py b/resources/tester/test_spreadsheet_touch.py index f989f40bf..7c51e5620 100644 --- a/resources/tester/test_spreadsheet_touch.py +++ b/resources/tester/test_spreadsheet_touch.py @@ -10,7 +10,9 @@ import urllib.request import pytest -BASE = "http://localhost:5174" +from helper_verify_project import own_ports # noqa: E402 + +BASE = own_ports()[1] # 화면 서버 = OWNERS.md 이 폴더 줄 MOUNT = """ async () => { diff --git a/resources/tester/test_touch_bridge.py b/resources/tester/test_touch_bridge.py index d6c6b4f88..98a5bd50d 100644 --- a/resources/tester/test_touch_bridge.py +++ b/resources/tester/test_touch_bridge.py @@ -13,7 +13,9 @@ import urllib.request import pytest -BASE = os.environ.get("AISLO_UI_BASE", "http://localhost:5174") # 다른 창 서버 = 환경값 +from helper_verify_project import own_ports # noqa: E402 + +BASE = os.environ.get("AISLO_UI_BASE", own_ports()[1]) # 다른 창 서버 = 환경값 def _up() -> bool: