UNPKG

@notjustcoders/ioc-arise

Version:

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

268 lines 9.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigParser = void 0; const fs_1 = require("fs"); const napi_1 = require("@ast-grep/napi"); const errorFactory_1 = require("../errors/errorFactory"); /** * Parses a TypeScript config file (ioc.config.ts) and extracts the config object * from a defineConfig() call */ class ConfigParser { /** * Parse ioc.config.ts file and extract config object */ static parseConfigFile(filePath) { try { const content = (0, fs_1.readFileSync)(filePath, 'utf-8'); const ast = napi_1.ts.parse(content); const root = ast.root(); // Try to find export default defineConfig({...}) const defaultExport = this.findDefaultExport(root); if (defaultExport) { const config = this.extractConfigFromNode(defaultExport); if (config) { return config; } } // Try to find export const config = defineConfig({...}) const namedExport = this.findNamedExport(root); if (namedExport) { const config = this.extractConfigFromNode(namedExport); if (config) { return config; } } throw errorFactory_1.ErrorFactory.configParseError(filePath, 'Could not find defineConfig() call in config file'); } catch (error) { if (error instanceof Error && error.message.includes('Could not find')) { throw error; } throw errorFactory_1.ErrorFactory.configParseError(filePath, error instanceof Error ? error.message : String(error)); } } /** * Find export default defineConfig({...}) */ static findDefaultExport(root) { try { // Find export default statements const exportDefaults = root.findAll({ rule: { kind: 'export_statement' } }); for (const exportStmt of exportDefaults) { const text = exportStmt.text(); if (text.includes('export default') && text.includes('defineConfig')) { // Find call expressions within this export const callExprs = exportStmt.findAll({ rule: { kind: 'call_expression' } }); for (const callExpr of callExprs) { const callText = callExpr.text(); if (callText.includes('defineConfig')) { // Get the arguments - should be an object literal const args = callExpr.findAll({ rule: { kind: 'object' } }); if (args.length > 0) { return args[0]; } } } } } } catch (error) { // Fallback: try text-based extraction } return null; } /** * Find export const config = defineConfig({...}) */ static findNamedExport(root) { try { // Find variable declarations that are exported const varDecls = root.findAll({ rule: { kind: 'variable_declaration' } }); for (const varDecl of varDecls) { // Check if it's exported and contains defineConfig const parent = varDecl.parent(); if (parent) { const parentText = parent.text(); if (parentText.includes('export') && varDecl.text().includes('defineConfig')) { // Find call expressions const callExprs = varDecl.findAll({ rule: { kind: 'call_expression' } }); for (const callExpr of callExprs) { const callText = callExpr.text(); if (callText.includes('defineConfig')) { // Get the arguments - should be an object literal const args = callExpr.findAll({ rule: { kind: 'object' } }); if (args.length > 0) { return args[0]; } } } } } } } catch (error) { // Fallback handling } return null; } /** * Extract config object from defineConfig() call node * The node should already be the argument to defineConfig */ static extractConfigFromNode(node) { try { // The node should be an object literal const kind = node.kind(); if (kind === 'object' || node.text().trim().startsWith('{')) { return this.parseObjectLiteral(node); } return null; } catch { return null; } } /** * Parse an object literal AST node into a JavaScript object */ static parseObjectLiteral(node) { const config = {}; // Find all property assignments: key: value const properties = node.findAll({ rule: { kind: 'pair' } }); for (const prop of properties) { // Try to find key and value const keyMatches = prop.findAll({ rule: { kind: 'property_key' } }); const valueMatches = prop.findAll({ rule: { kind: 'property_value' } }); if (keyMatches.length === 0 || valueMatches.length === 0) continue; const keyNode = keyMatches[0]; const valueNode = valueMatches[0]; const key = this.extractStringValue(keyNode); if (!key) continue; const value = this.extractValue(valueNode); config[key] = value; } return config; } /** * Extract value from an AST node (handles strings, numbers, booleans, arrays, objects) */ static extractValue(node) { if (!node) return undefined; const kind = node.kind(); const text = node.text().trim(); // String literal if (kind === 'string' || text.startsWith('"') || text.startsWith("'") || text.startsWith('`')) { return this.extractStringValue(node); } // Number literal if (kind === 'number' || /^-?\d+\.?\d*$/.test(text)) { return parseFloat(text); } // Boolean literal if (text === 'true') return true; if (text === 'false') return false; // Array literal if (kind === 'array' || text.startsWith('[')) { return this.parseArrayLiteral(node); } // Object literal if (kind === 'object' || text.startsWith('{')) { return this.parseObjectLiteral(node); } // Template literal (for paths) if (kind === 'template_string' || text.startsWith('`')) { return this.extractStringValue(node); } // Undefined/null if (text === 'undefined') return undefined; if (text === 'null') return null; // Fallback: try to extract as string return this.extractStringValue(node); } /** * Extract string value from string literal node */ static extractStringValue(node) { try { const text = node.text(); // Remove quotes if ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'"))) { return text.slice(1, -1); } // Template literal if (text.startsWith('`') && text.endsWith('`')) { return text.slice(1, -1); } return text; } catch { return undefined; } } /** * Parse an array literal AST node into a JavaScript array */ static parseArrayLiteral(node) { const array = []; // Find all array elements const elements = node.findAll({ rule: { kind: 'array_element' } }); for (const element of elements) { const value = this.extractValue(element); if (value !== undefined) { array.push(value); } } return array; } } exports.ConfigParser = ConfigParser; //# sourceMappingURL=configParser.js.map