UNPKG

@notjustcoders/ioc-arise

Version:

Arise type-safe IoC containers from your code. Zero overhead, zero coupling.

140 lines 4.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TsConfigPathsResolver = void 0; const fs_1 = require("fs"); const path_1 = require("path"); class TsConfigPathsResolver { constructor(startDir) { this.baseUrl = null; this.paths = {}; const tsConfigPath = this.findTsConfig((0, path_1.resolve)(startDir)); if (tsConfigPath) { this.loadTsConfig(tsConfigPath); } } findTsConfig(startDir) { let dir = startDir; while (true) { const candidate = (0, path_1.join)(dir, 'tsconfig.json'); if ((0, fs_1.existsSync)(candidate)) return candidate; const parent = (0, path_1.dirname)(dir); if (parent === dir) return null; // reached filesystem root dir = parent; } } /** * Strips JSON comments while respecting string literals. * A simple regex is not safe here because tsconfig path patterns like * "@/*" contain "/*" which would be mis-detected as a block comment. */ stripJsonComments(raw) { let result = ''; let i = 0; while (i < raw.length) { const ch = raw[i]; if (ch === '"') { // Consume the entire string literal verbatim result += ch; i++; while (i < raw.length) { const c = raw[i]; if (c === '\\') { result += c + (raw[i + 1] ?? ''); i += 2; } else { result += c; i++; if (c === '"') break; } } } else if (ch === '/' && raw[i + 1] === '/') { // Line comment — skip to end of line while (i < raw.length && raw[i] !== '\n') i++; } else if (ch === '/' && raw[i + 1] === '*') { // Block comment — skip to */ i += 2; while (i < raw.length) { if (raw[i] === '*' && raw[i + 1] === '/') { i += 2; break; } i++; } } else { result += ch; i++; } } return result; } loadTsConfig(tsConfigPath) { try { const raw = (0, fs_1.readFileSync)(tsConfigPath, 'utf-8'); const stripped = this.stripJsonComments(raw) // Strip trailing commas before } or ] .replace(/,(\s*[}\]])/g, '$1'); const config = JSON.parse(stripped); const co = config.compilerOptions ?? {}; if (!co.paths) return; this.baseUrl = co.baseUrl ? (0, path_1.resolve)((0, path_1.dirname)(tsConfigPath), co.baseUrl) : (0, path_1.dirname)(tsConfigPath); this.paths = co.paths; } catch { // Silently ignore unparseable tsconfig } } /** * Resolves a path-alias import to an absolute filesystem path. * Returns null if the import is a regular relative import or no alias matches. */ resolveImportToAbsolute(importPath) { if (!this.baseUrl || !importPath || importPath.startsWith('.')) return null; for (const [pattern, targets] of Object.entries(this.paths)) { for (const target of targets) { const resolved = this.tryMatch(importPath, pattern, target); if (resolved) return resolved; } } return null; } tryMatch(importPath, pattern, target) { if (pattern.endsWith('/*')) { const prefix = pattern.slice(0, -2); if (!importPath.startsWith(prefix + '/')) return null; const rest = importPath.slice(prefix.length + 1); const targetBase = target.endsWith('/*') ? target.slice(0, -2) : target; return this.findFile((0, path_1.join)(this.baseUrl, targetBase, rest)); } // Exact pattern match if (importPath !== pattern) return null; return this.findFile((0, path_1.join)(this.baseUrl, target)); } findFile(basePath) { const candidates = [basePath, basePath + '.ts', (0, path_1.join)(basePath, 'index.ts')]; for (const c of candidates) { if ((0, fs_1.existsSync)(c)) return c; } return null; } hasPathAliases() { return this.baseUrl !== null && Object.keys(this.paths).length > 0; } } exports.TsConfigPathsResolver = TsConfigPathsResolver; //# sourceMappingURL=tsConfigPathsResolver.js.map