/* 진짜 TS 를 CommonJS 로 트랜스파일해 부르는 공용 불러오기 + 가짜 모듈 끼워 넣기(507-4 · 새 시험부터 이걸 씀). * 옛 헬퍼 198 벌이 저마다 베낀 `loadTs`(+ `STUBS`) 한 벌 — 진짜 모듈이 새 export · import 를 늘리면 헬퍼마다 낡아 * 깨졌음(482). 여기만 고치면 되게 모음. * * const { makeLoader, memoryStorage } = require("./helper_ts_stub_loader.cjs"); * const STUBS = {}; // 끝이 이 이름으로 끝나는 import 를 가짜로(헬퍼가 나중에 채움) * const { load, loadTs } = makeLoader({ root: ROOT, stubs: STUBS, ui: () => anyModule() }); * const store = load("B06_Section", "B06_Section_Section_Store.ts"); * * 풀이 순서(기본): 카탈로그 · STUBS · @config(가짜 주소) · @ui(`ui` 쪽 값 · 기본 showToast) · .css · .json · @util · 상대 경로 · node. */ const fs = require("fs"); const path = require("path"); const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript")); const CATALOG = require("./helper_322_catalog_module.cjs"); function memoryStorage() { 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; }, }; } /* 옵션 — 옮겨 온 옛 헬퍼가 저마다 달리 쓰던 것(헬퍼 동작 그대로). * requireFor(full, file) 풀이 함수를 헬퍼가 직접 지음(기본 풀이 대신) · 인자 없으면 진짜 require * interop esModuleInterop 로 트랜스파일 * cache:false 같은 파일을 부를 때마다 새로 읽음(기본은 한 번만) * fileStub(full) 파일 경로로 통째 대신할 값(없으면 undefined) * target 트랜스파일 대상(기본 ES2020) * rewrite(js) 트랜스파일한 글 손봄(`import.meta` 처럼 CommonJS 가 못 읽는 것) */ function makeLoader({ root, stubs = {}, ui = () => ({ showToast: () => undefined }), requireFor, interop = false, cache: useCache = true, fileStub, target = ts.ScriptTarget.ES2020, rewrite = (js) => js, }) { const cache = new Map(); function defaultRequire(full) { return (request) => { if (CATALOG.isCatalogRequest(request)) return CATALOG; const key = Object.keys(stubs).find((name) => request.endsWith(name)); if (key) return stubs[key]; if (request.startsWith("@config/")) return { API_BASE_URL: "http://fake" }; if (request.startsWith("@ui/")) return ui(); if (request.endsWith(".css")) return {}; if (request.endsWith(".json")) return require(path.join(path.dirname(full), request)); if (request.startsWith("@util/")) 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); }; } function loadTs(file) { const full = path.resolve(file); if (useCache && cache.has(full)) return cache.get(full); const stub = fileStub && fileStub(full); if (stub) return stub; if (full.endsWith(".json")) { const data = JSON.parse(fs.readFileSync(full, "utf8")); if (useCache) cache.set(full, data); return data; } const compilerOptions = { module: ts.ModuleKind.CommonJS, target }; if (interop) compilerOptions.esModuleInterop = true; 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); new Function("exports", "module", "require", js)(box.exports, box, localRequire); if (useCache) cache.set(full, box.exports); return box.exports; } return { loadTs, load: (...parts) => loadTs(path.join(root, ...parts)) }; } module.exports = { makeLoader, memoryStorage };