diff --git a/resources/tester/helper_261_3_detail_pick.cjs b/resources/tester/helper_261_3_detail_pick.cjs index 8ef57da1e..4d5b1cab1 100644 --- a/resources/tester/helper_261_3_detail_pick.cjs +++ b/resources/tester/helper_261_3_detail_pick.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, "..", ".."); const B06 = path.join(ROOT, "B06_Section"); @@ -19,21 +19,17 @@ const STUBS = { "./B06_Section_UI_Section_Common": { CROSS_PAD }, }; const resolve = (base) => (fs.existsSync(`${base}.ts`) ? `${base}.ts` : base); -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: ROOT, + cache: false, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.endsWith(".css")) return {}; if (request.startsWith("@util/")) return loadTs(resolve(path.join(ROOT, "common_util", request.slice(6)))); return loadTs(resolve(path.join(path.dirname(full), request))); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const G = loadTs(path.join(B06, "logic/B06_Section_UI_Cross_Detail_Geom.ts")); const S = loadTs(path.join(B06, "B06_Section_UI_Cross_Detail_Select.ts")); diff --git a/resources/tester/helper_266_detail_home.cjs b/resources/tester/helper_266_detail_home.cjs index 2492ddfdb..7335fff9c 100644 --- a/resources/tester/helper_266_detail_home.cjs +++ b/resources/tester/helper_266_detail_home.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, "..", ".."); const B06 = path.join(ROOT, "B06_Section"); @@ -19,15 +19,11 @@ const STUBS = { validElevation: (s) => s.valid !== false && Number.isFinite(s.elevation_m), }, }; -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) => STUBS[request] ?? {}; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} +const { loadTs } = makeLoader({ + root: ROOT, + cache: false, + requireFor: (full) => (request) => STUBS[request] ?? {}, +}); // 휠 방향과 무관한 시험(처음으로 · 범위) — 337 기본 밂 뒤에도 당김 수치 그대로(pull 박음) globalThis.localStorage = { getItem: (k) => (k === "aislo:pref:input-prefs" ? '{"wheelZoom":"pull"}' : null), diff --git a/resources/tester/helper_270_1_cross_map.cjs b/resources/tester/helper_270_1_cross_map.cjs index f8814f58d..d835968b4 100644 --- a/resources/tester/helper_270_1_cross_map.cjs +++ b/resources/tester/helper_270_1_cross_map.cjs @@ -6,19 +6,17 @@ const fs = require("fs"); const path = require("path"); const ROOT = path.join(__dirname, "..", ".."); const NM = path.join(ROOT, "config", "node_modules"); -const ts = require(path.join(NM, "typescript")); +const { makeLoader } = require("./helper_ts_stub_loader.cjs"); function loadTs(file, stubs = {}) { - 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) => { - if (stubs[request]) return stubs[request]; - throw new Error(`stub 없음: ${request}`); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; + return makeLoader({ + root: ROOT, + cache: false, + requireFor: (full) => (request) => { + if (stubs[request]) return stubs[request]; + throw new Error(`stub 없음: ${request}`); + }, + }).loadTs(path.join(ROOT, file)); } globalThis.window = { addEventListener() {}, removeEventListener() {} }; diff --git a/resources/tester/helper_271_detail_notify.cjs b/resources/tester/helper_271_detail_notify.cjs index 53a1174b2..d84bc67eb 100644 --- a/resources/tester/helper_271_detail_notify.cjs +++ b/resources/tester/helper_271_detail_notify.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 CATALOG = require(path.join(__dirname, "helper_322_catalog_module.cjs")); const P = "P463"; @@ -140,35 +140,23 @@ const STUBS = { "design_save.ts": { savingDesign: () => false }, }; -const cache = new Map(); const resolve = (base) => [base, `${base}.ts`, path.join(base, "index.ts")].find( (p) => fs.existsSync(p) && fs.statSync(p).isFile(), ) ?? `${base}.ts`; -function loadTs(full) { - const stub = STUBS[path.basename(full)]; - if (stub) return stub; - 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, - esModuleInterop: true, - }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const near = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + interop: true, + fileStub: (full) => STUBS[path.basename(full)], + requireFor: (full) => (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; 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, near); - return box.exports; -} + }, +}); const store = loadTs(path.join(ROOT, "B06_Section", "B06_Section_Section_Store.ts")); const missing = loadTs(path.join(ROOT, "B06_Section", "B06_Section_UI_Missing_Stations.ts")); diff --git a/resources/tester/helper_271_facility_lane.cjs b/resources/tester/helper_271_facility_lane.cjs index d50a92687..6e33e51a8 100644 --- a/resources/tester/helper_271_facility_lane.cjs +++ b/resources/tester/helper_271_facility_lane.cjs @@ -5,7 +5,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 CATALOG = require(path.join(__dirname, "helper_322_catalog_module.cjs")); function memoryStorage() { @@ -39,25 +39,15 @@ const STUBS = { "B06_Section_UI_Page_Persist.ts": anyModule({ bermSpansFromStructures: () => [] }), }; -const cache = new Map(); const resolve = (base) => [base, `${base}.ts`, path.join(base, "index.ts")].find( (p) => fs.existsSync(p) && fs.statSync(p).isFile(), ) ?? `${base}.ts`; -function loadTs(full) { - const stub = STUBS[path.basename(full)]; - if (stub) return stub; - 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, - esModuleInterop: true, - }, - }).outputText; - const box = { exports: {} }; - cache.set(full, box.exports); - const near = (request) => { +const { loadTs } = makeLoader({ + root: ROOT, + interop: true, + fileStub: (full) => STUBS[path.basename(full)], + 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")); @@ -66,10 +56,8 @@ function loadTs(full) { 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, near); - return box.exports; -} + }, +}); const design = path.join(ROOT, "A00_Common", "design"); const { createFacilityLane } = loadTs(path.join(design, "design_facility_lane.ts")); diff --git a/resources/tester/helper_271_straighten_pick.cjs b/resources/tester/helper_271_straighten_pick.cjs index f2e0572a5..8b610ac8b 100644 --- a/resources/tester/helper_271_straighten_pick.cjs +++ b/resources/tester/helper_271_straighten_pick.cjs @@ -4,28 +4,19 @@ */ 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, "..", ".."); // 화면 틀 파일(@ui 별칭)은 Node 가 못 읽음 — 도구줄 그림만 쓰므로 빈 그림으로 갈음 const STUBS = { "./B05_Profile_UI_RouteEdit_Chrome": { HISTORY_ICON: { undo: "", redo: "" } } }; -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 near = (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, near); - return box.exports; -} + }, +}); const LOGIC = path.join(ROOT, "B05_Profile", "logic"); const align = loadTs(path.join(LOGIC, "B05_Profile_UI_Profile_Alignment.ts")); diff --git a/resources/tester/helper_272_1_station_list.cjs b/resources/tester/helper_272_1_station_list.cjs index 11aa038b2..df883a640 100644 --- a/resources/tester/helper_272_1_station_list.cjs +++ b/resources/tester/helper_272_1_station_list.cjs @@ -3,22 +3,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, "..", ".."); 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) => { - if (CATALOG.isCatalogRequest(request)) return CATALOG; - throw new Error(`stub 없음: ${request} (${file})`); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; + return makeLoader({ + root: ROOT, + cache: false, + requireFor: (full) => (request) => { + if (CATALOG.isCatalogRequest(request)) return CATALOG; + throw new Error(`stub 없음: ${request} (${file})`); + }, + }).loadTs(path.join(ROOT, file)); } const input = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); diff --git a/resources/tester/helper_273_2_preview_stations.cjs b/resources/tester/helper_273_2_preview_stations.cjs index 00589f01a..8dbe4da29 100644 --- a/resources/tester/helper_273_2_preview_stations.cjs +++ b/resources/tester/helper_273_2_preview_stations.cjs @@ -3,22 +3,23 @@ */ 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, "..", ".."); function loadTs(file, stubs = {}) { - const js = ts.transpileModule(fs.readFileSync(path.join(ROOT, file), "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - new Function("exports", "module", "require", js)(box.exports, box, (name) => { - if (CATALOG.isCatalogRequest(name)) return CATALOG; - if (!stubs[name]) throw new Error(`stub 없음: ${name} (${file})`); - return stubs[name]; - }); - return box.exports; + return makeLoader({ + root: ROOT, + cache: false, + requireFor: (full) => { + return (name) => { + if (CATALOG.isCatalogRequest(name)) return CATALOG; + if (!stubs[name]) throw new Error(`stub 없음: ${name} (${file})`); + return stubs[name]; + }; + }, + }).loadTs(path.join(ROOT, file)); } const station = loadTs("common_util/common_util_structure_station.ts"); diff --git a/resources/tester/helper_280_1_combo_pick.cjs b/resources/tester/helper_280_1_combo_pick.cjs index af16257b4..9b4ffd3f9 100644 --- a/resources/tester/helper_280_1_combo_pick.cjs +++ b/resources/tester/helper_280_1_combo_pick.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, "..", ".."); const UI = path.join(ROOT, "ui_template"); @@ -143,24 +143,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 (request in STUBS) return STUBS[request]; 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 item = (key, 열, 경로) => ({ key, 열, 경로 }); const GROUPS = [ diff --git a/resources/tester/helper_280_3_route_undo_facilities.cjs b/resources/tester/helper_280_3_route_undo_facilities.cjs index a92db1a09..74fc8bb1c 100644 --- a/resources/tester/helper_280_3_route_undo_facilities.cjs +++ b/resources/tester/helper_280_3_route_undo_facilities.cjs @@ -5,7 +5,7 @@ * 결과 = 표준출력 JSON. 짝 = `test_280_3_route_undo_facilities.py`. */ 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, "..", ".."); @@ -52,14 +52,9 @@ globalThis.fetch = (url, init) => 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 { load, loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; const key = Object.keys(STUBS).find((name) => request.endsWith(name)); if (key) return STUBS[key]; @@ -71,11 +66,8 @@ function loadTs(full) { return loadTs(path.join(ROOT, "common_util", `${request.slice(6)}.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 load = (...parts) => loadTs(path.join(ROOT, ...parts)); + }, +}); const sampler = load("common_util", "common_util_surface_sampler.ts"); const bytes = fs.readFileSync(process.argv[3]); diff --git a/resources/tester/helper_280_4_fit.cjs b/resources/tester/helper_280_4_fit.cjs index c5acd1a52..d0df2862e 100644 --- a/resources/tester/helper_280_4_fit.cjs +++ b/resources/tester/helper_280_4_fit.cjs @@ -3,27 +3,17 @@ */ 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) => +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 LOGIC = path.join(ROOT, "B05_Profile", "logic"); const fit = loadTs(path.join(LOGIC, "B05_Profile_RouteEdit_StationFit.ts")); diff --git a/resources/tester/helper_280_4_stations.cjs b/resources/tester/helper_280_4_stations.cjs index fa2d44782..b2c9991ca 100644 --- a/resources/tester/helper_280_4_stations.cjs +++ b/resources/tester/helper_280_4_stations.cjs @@ -3,26 +3,13 @@ * 짝 = `test_280_4_stations.py`. */ 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(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) => { - if (CATALOG.isCatalogRequest(request)) return CATALOG; - 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: ROOT, +}); const logic = loadTs(path.join(ROOT, "B05_Profile", "logic", "B05_Profile_Sections_Stations.ts")); const station = loadTs(path.join(ROOT, "common_util", "common_util_structure_station.ts")); diff --git a/resources/tester/helper_280_5_b07_settings.cjs b/resources/tester/helper_280_5_b07_settings.cjs index be4ebe47d..dc3a440fa 100644 --- a/resources/tester/helper_280_5_b07_settings.cjs +++ b/resources/tester/helper_280_5_b07_settings.cjs @@ -5,13 +5,12 @@ */ 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 B07 = path.join(ROOT, "B07_DesignDetail"); const UI = path.join(ROOT, "ui_template"); -const cache = new Map(); class FakeNode { constructor(tag) { @@ -190,15 +189,9 @@ 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 === "@ui/ui_template_collapsible") @@ -208,11 +201,8 @@ function loadTs(file) { return fs.existsSync(target) ? loadTs(target) : {}; } return {}; - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const { buildDrawingSidePanel } = loadTs(path.join(B07, "B07_DesignDetail_UI_Panels.ts")); const { attachCollapsible } = loadTs(path.join(UI, "ui_template_collapsible.ts")); diff --git a/resources/tester/helper_281_1_drainage_zoom_range.cjs b/resources/tester/helper_281_1_drainage_zoom_range.cjs index 8d066aab7..3f1007d5f 100644 --- a/resources/tester/helper_281_1_drainage_zoom_range.cjs +++ b/resources/tester/helper_281_1_drainage_zoom_range.cjs @@ -4,29 +4,19 @@ */ 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 로 옮겨 실행한다. `@ui/` 는 색 함수만 쓰므로 빈 값으로 갈음한다. */ -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 (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); if (request.startsWith("@ui/")) return { themeColor: () => "" }; return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const zoom = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Drainage_ZoomRange.ts")); const render = loadTs(path.join(ROOT, "B04_PreProcess", "B04_PreProcess_UI_MapRender.ts")); diff --git a/resources/tester/helper_283_route_reset_confirm.cjs b/resources/tester/helper_283_route_reset_confirm.cjs index 44629461d..a71e3ff41 100644 --- a/resources/tester/helper_283_route_reset_confirm.cjs +++ b/resources/tester/helper_283_route_reset_confirm.cjs @@ -4,21 +4,19 @@ */ 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 loadTs(file, stubs = {}) { - 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) => { - if (stubs[request]) return stubs[request]; - throw new Error(`stub 없음: ${request} (${file})`); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; + return makeLoader({ + root: ROOT, + cache: false, + requireFor: (full) => (request) => { + if (stubs[request]) return stubs[request]; + throw new Error(`stub 없음: ${request} (${file})`); + }, + }).loadTs(path.join(ROOT, file)); } const reset = loadTs("B05_Profile/logic/B05_Profile_Route_Reset.ts"); diff --git a/resources/tester/helper_284_1_pill_pick.cjs b/resources/tester/helper_284_1_pill_pick.cjs index 0bf09a76f..f2bcbb35a 100644 --- a/resources/tester/helper_284_1_pill_pick.cjs +++ b/resources/tester/helper_284_1_pill_pick.cjs @@ -4,23 +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"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); function loadTs(file, stubs = {}) { - const js = ts.transpileModule(fs.readFileSync(file, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - const localRequire = (request) => { - if (CATALOG.isCatalogRequest(request)) return CATALOG; - if (stubs[request]) return stubs[request]; - throw new Error(`stub 없음: ${request}`); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; + return makeLoader({ + root: ROOT, + cache: false, + requireFor: (full) => (request) => { + if (CATALOG.isCatalogRequest(request)) return CATALOG; + if (stubs[request]) return stubs[request]; + throw new Error(`stub 없음: ${request}`); + }, + }).loadTs(file); } const selection = loadTs(path.join(ROOT, "A00_Common", "design", "design_selection.ts"), { diff --git a/resources/tester/helper_286_1_ford_pavement.cjs b/resources/tester/helper_286_1_ford_pavement.cjs index 117cd3199..af326b295 100644 --- a/resources/tester/helper_286_1_ford_pavement.cjs +++ b/resources/tester/helper_286_1_ford_pavement.cjs @@ -3,23 +3,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"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); function loadTs(file, stubs = {}) { - const js = ts.transpileModule(fs.readFileSync(file, "utf8"), { - compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, - }).outputText; - const box = { exports: {} }; - const localRequire = (request) => { - if (CATALOG.isCatalogRequest(request)) return CATALOG; - if (stubs[request]) return stubs[request]; - throw new Error(`stub 없음: ${request}`); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; + return makeLoader({ + root: ROOT, + cache: false, + requireFor: (full) => (request) => { + if (CATALOG.isCatalogRequest(request)) return CATALOG; + if (stubs[request]) return stubs[request]; + throw new Error(`stub 없음: ${request}`); + }, + }).loadTs(file); } const input = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); diff --git a/resources/tester/helper_294_1_chute_remove_pick.cjs b/resources/tester/helper_294_1_chute_remove_pick.cjs index e18dc83b0..c295909a0 100644 --- a/resources/tester/helper_294_1_chute_remove_pick.cjs +++ b/resources/tester/helper_294_1_chute_remove_pick.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, "..", ".."); @@ -165,14 +165,11 @@ globalThis.document = { querySelector: (s) => body.querySelector(s), }; -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, require); - return box.exports; -} +const { loadTs } = makeLoader({ + root: ROOT, + cache: false, + requireFor: () => require, +}); const dock = loadTs(path.join(ROOT, "B06_Section", "B06_Section_UI_Adjust_Dock.ts")); // ── 폼 흉내 — 명세 상자 = fieldset[data-spec-container] > legend(글 + [삭제]) ────────── diff --git a/resources/tester/helper_294_7_ford_slab_pick.cjs b/resources/tester/helper_294_7_ford_slab_pick.cjs index b1e235a3e..82057408d 100644 --- a/resources/tester/helper_294_7_ford_slab_pick.cjs +++ b/resources/tester/helper_294_7_ford_slab_pick.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, "..", ".."); const B06 = path.join(ROOT, "B06_Section"); @@ -30,19 +30,15 @@ const STUBS = { "./B06_Section_UI_Page_Persist": { isBermType: (typeId) => typeId === "berm" }, }; const resolve = (base) => (fs.existsSync(`${base}.ts`) ? `${base}.ts` : base); -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: ROOT, + cache: false, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.endsWith(".css")) return {}; return loadTs(resolve(path.join(path.dirname(full), request))); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const SP = loadTs(path.join(B06, "B06_Section_UI_Cross_Shape_Pick.ts")); const PO = loadTs(path.join(B06, "B06_Section_UI_Cross_Pick_Order.ts")); const SEL = loadTs(path.join(B06, "B06_Section_UI_Page_Structures_Selection.ts")); diff --git a/resources/tester/helper_295_pick_order_detail.cjs b/resources/tester/helper_295_pick_order_detail.cjs index 64f1f61f6..e702ec903 100644 --- a/resources/tester/helper_295_pick_order_detail.cjs +++ b/resources/tester/helper_295_pick_order_detail.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, "..", ".."); const B06 = path.join(ROOT, "B06_Section"); @@ -18,21 +18,17 @@ const STUBS = { "./B06_Section_UI_Section_Common": { CROSS_PAD }, }; const resolve = (base) => (fs.existsSync(`${base}.ts`) ? `${base}.ts` : base); -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: ROOT, + cache: false, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; if (request.endsWith(".css")) return {}; if (request.startsWith("@util/")) return loadTs(resolve(path.join(ROOT, "common_util", request.slice(6)))); return loadTs(resolve(path.join(path.dirname(full), request))); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const P = loadTs(path.join(B06, "B06_Section_UI_Cross_Pick_Order.ts")); const G = loadTs(path.join(B06, "logic/B06_Section_UI_Cross_Detail_Geom.ts")); const S = loadTs(path.join(B06, "B06_Section_UI_Cross_Detail_Select.ts")); diff --git a/resources/tester/helper_298_3_save_shape.cjs b/resources/tester/helper_298_3_save_shape.cjs index 3eeb98d67..43c5aa17e 100644 --- a/resources/tester/helper_298_3_save_shape.cjs +++ b/resources/tester/helper_298_3_save_shape.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, "..", ".."); const calls = []; @@ -60,18 +60,14 @@ const STUBS = { }, }; -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: ROOT, + cache: false, + requireFor: (full) => (request) => { if (STUBS[request]) return STUBS[request]; throw new Error(`stub 없음: ${request}`); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; -} + }, +}); const save = loadTs(path.join(ROOT, "A00_Common", "design", "design_save.ts")); const ctx = (extra = {}) => ({ diff --git a/resources/tester/helper_298_add_reanalyze.cjs b/resources/tester/helper_298_add_reanalyze.cjs index 8edfbfa25..9e15f843e 100644 --- a/resources/tester/helper_298_add_reanalyze.cjs +++ b/resources/tester/helper_298_add_reanalyze.cjs @@ -4,35 +4,25 @@ */ 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 toasts = []; const STUBS = { "@ui/ui_template_elements": { showToast: (message, kind) => toasts.push([message, kind]) }, }; -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 === "@config/config_frontend") return loadTs(path.join(ROOT, "config", "config_frontend.ts")); 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 B05 = path.join(ROOT, "B05_Profile"); const fill = loadTs(path.join(B05, "B05_Profile_UI_Drainage_Ford_Fill.ts")); diff --git a/resources/tester/helper_311_panel_folded.cjs b/resources/tester/helper_311_panel_folded.cjs index 545a7a2f3..1f07b89b8 100644 --- a/resources/tester/helper_311_panel_folded.cjs +++ b/resources/tester/helper_311_panel_folded.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(); // ── 화면 그릇 흉내 — 요소 · 클래스 · 속성 · 글자 · 자식 ────────────────────────── class FakeNode { @@ -134,26 +133,17 @@ const STUBS = { "./B06_Section_UI_Page_Common": { L: (key) => key }, }; -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 === "@ui/ui_template_collapsible") return loadTs(path.join(ROOT, "ui_template", "ui_template_collapsible.ts")); 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 out = {}; const titleOf = (n) => n.children.find((c) => c.tagName === "h3")?.textContent ?? ""; diff --git a/resources/tester/helper_322_1_catalog.cjs b/resources/tester/helper_322_1_catalog.cjs index f1724f663..3db295cfd 100644 --- a/resources/tester/helper_322_1_catalog.cjs +++ b/resources/tester/helper_322_1_catalog.cjs @@ -6,22 +6,14 @@ 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"); -function loadTs(full) { - if (full.endsWith(".json")) return JSON.parse(fs.readFileSync(full, "utf8")); - const js = ts.transpileModule(fs.readFileSync(full, "utf8"), { - compilerOptions: { - module: ts.ModuleKind.CommonJS, - target: ts.ScriptTarget.ES2020, - esModuleInterop: true, - }, - }).outputText; - const box = { exports: {} }; - const need = (spec) => loadTs(path.join(path.dirname(full), spec)); - new Function("exports", "module", "require", js)(box.exports, box, need); - return box.exports; -} +const { loadTs } = makeLoader({ + root: ROOT, + interop: true, + cache: false, + requireFor: (full) => (spec) => loadTs(path.join(path.dirname(full), spec)), +}); const input = JSON.parse(fs.readFileSync(process.argv[2], "utf8")); const catalog = loadTs(path.join(ROOT, "common_util", "common_util_structure_catalog.ts")); diff --git a/resources/tester/helper_323_9_definition_stamp.cjs b/resources/tester/helper_323_9_definition_stamp.cjs index d0b9627e8..4af4fb1fa 100644 --- a/resources/tester/helper_323_9_definition_stamp.cjs +++ b/resources/tester/helper_323_9_definition_stamp.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 CATALOG = require("./helper_322_catalog_module.cjs"); // 구조물 표(322-1) const ROOT = path.join(__dirname, "..", ".."); @@ -54,16 +54,9 @@ globalThis.fetch = async (url, init = {}) => { throw new Error(`모르는 요청 ${at}`); }; -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; if (request === "@config/config_frontend") return { API_BASE_URL: "", API_TIMEOUT_MS: 1000 }; if (request.endsWith(".json")) return require(path.join(path.dirname(full), request)); @@ -71,11 +64,8 @@ function loadTs(file) { if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); if (request.startsWith("@")) return new Proxy({}, { get: () => () => undefined }); return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); const api = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_Api_Structures.ts")); // 472-6 — B06 [저장] = 한 본문 구조물 조각(옛 기본 창구 `flushPendingStructures` 걷음) · 서버 창구 = 가짜 PUT diff --git a/resources/tester/helper_323_9_twin_cards_3d.cjs b/resources/tester/helper_323_9_twin_cards_3d.cjs index 472538640..bd5b2d495 100644 --- a/resources/tester/helper_323_9_twin_cards_3d.cjs +++ b/resources/tester/helper_323_9_twin_cards_3d.cjs @@ -5,9 +5,8 @@ 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(); const STUBS = { "@ui/ui_template_toast": { showToast: () => undefined }, three: require(path.join(ROOT, "config", "node_modules", "three")), @@ -16,34 +15,18 @@ const resolve = (base) => [base, `${base}.ts`, path.join(base, "index.ts")].find( (p) => fs.existsSync(p) && fs.statSync(p).isFile(), ) ?? `${base}.ts`; -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); - return box.exports; -} + }, +}); const { buildCorridorStructures } = loadTs( path.join(ROOT, "B05_Profile", "B05_Profile_UI_Corridor_Structures.ts"), diff --git a/resources/tester/helper_342_3_batched.cjs b/resources/tester/helper_342_3_batched.cjs index 1190cdc09..ad33d6788 100644 --- a/resources/tester/helper_342_3_batched.cjs +++ b/resources/tester/helper_342_3_batched.cjs @@ -3,29 +3,19 @@ */ 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) => { +const { loadTs } = makeLoader({ + root: ROOT, + requireFor: (full) => (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("@ui/")) return { themeColor: () => "" }; return require(request); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - cache.set(full, box.exports); - return box.exports; -} + }, +}); (async () => { const { batched } = loadTs(path.join(ROOT, "B05_Profile", "B05_Profile_UI_Drainage_Pipes.ts")); diff --git a/resources/tester/helper_344_2_route_pairs.cjs b/resources/tester/helper_344_2_route_pairs.cjs index ce4aa2f28..546769a9f 100644 --- a/resources/tester/helper_344_2_route_pairs.cjs +++ b/resources/tester/helper_344_2_route_pairs.cjs @@ -3,21 +3,19 @@ */ 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 loadTs(file, stubs = {}) { - 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) => { - if (stubs[request]) return stubs[request]; - throw new Error(`stub 없음: ${request} (${file})`); - }; - new Function("exports", "module", "require", js)(box.exports, box, localRequire); - return box.exports; + return makeLoader({ + root: ROOT, + cache: false, + requireFor: (full) => (request) => { + if (stubs[request]) return stubs[request]; + throw new Error(`stub 없음: ${request} (${file})`); + }, + }).loadTs(path.join(ROOT, file)); } const straightJudge = loadTs("B05_Profile/logic/B05_Profile_Profile_Straight.ts");