UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

213 lines (212 loc) 6.35 kB
/** * @module YamlConfigParser * @description YAML 配置解析器 — 从 XcodeGen project.yml 中提取项目结构信息 * * 支持解析: * - targets(构建目标)及其依赖关系 * - settings(项目级/目标级构建设置) * - schemes(构建方案) * - sources(源文件路径) * * 使用 js-yaml 进行安全解析(禁用危险的 YAML 特性)。 */ import yaml from 'js-yaml'; // ── 目标类型 → 层级推断 ──────────────────────────────── const TARGET_TYPE_LAYER = { application: 'App', 'app-extension': 'Extension', framework: 'Framework', 'static-library': 'Library', 'dynamic-library': 'Library', bundle: 'Resource', 'unit-test': 'Test', 'ui-test': 'Test', tool: 'Tool', }; // ── 公开 API ──────────────────────────────────────── /** * 解析 XcodeGen project.yml 内容 * 返回与 RubyDslParser 兼容的 ParsedProjectConfig */ export function parseXcodeGenProject(content) { let doc; try { doc = yaml.load(content, { schema: yaml.CORE_SCHEMA }); } catch { return { layers: [], globalDependencies: [] }; } if (!doc || typeof doc !== 'object') { return { layers: [], globalDependencies: [] }; } const result = { layers: [], globalDependencies: [], }; // 提取宿主应用 if (doc.name) { result.hostApp = { name: doc.name, version: '0.0.0' }; } if (!doc.targets) { return result; } // 将 targets 按类型分层 const layerMap = new Map(); for (const [targetName, target] of Object.entries(doc.targets)) { const targetType = target.type ?? 'framework'; const layerName = TARGET_TYPE_LAYER[targetType] ?? 'Other'; const isLocal = !isExternalTarget(targetName, doc); const mod = { name: targetName, version: '0.0.0', isLocal, localPath: extractSourcePath(target), group: layerName, }; if (!layerMap.has(layerName)) { layerMap.set(layerName, []); } layerMap.get(layerName)?.push(mod); } // 转为 ParsedLayer[] let order = 0; for (const [name, modules] of layerMap.entries()) { result.layers.push({ name, order: order++, accessibleLayers: [], modules, }); } // 提取 SPM packages → globalDependencies if (doc.packages) { for (const [pkgName, pkg] of Object.entries(doc.packages)) { result.globalDependencies.push({ name: pkgName, version: pkg.from ?? pkg.version ?? pkg.branch ?? '0.0.0', isLocal: !!pkg.path, localPath: pkg.path, }); } } return result; } /** * 解析单个 target 为 ParsedModuleSpec 格式 */ export function parseXcodeGenTarget(targetName, content) { let doc; try { doc = yaml.load(content, { schema: yaml.CORE_SCHEMA }); } catch { return null; } if (!doc?.targets?.[targetName]) { return null; } const target = doc.targets[targetName]; return { name: targetName, version: '0.0.0', sources: extractSourcePath(target) ?? targetName, dependencies: extractTargetDependencies(target), publicHeaders: [], deploymentTarget: extractDeploymentTarget(target), }; } /** * 提取所有 target 的依赖图 * 返回 [from, to][] 形式的有向边列表 */ export function extractXcodeGenDependencyEdges(content) { let doc; try { doc = yaml.load(content, { schema: yaml.CORE_SCHEMA }); } catch { return []; } if (!doc?.targets) { return []; } const edges = []; for (const [targetName, target] of Object.entries(doc.targets)) { if (!target.dependencies) { continue; } for (const dep of target.dependencies) { const depName = dep.target ?? dep.framework ?? dep.package ?? dep.carthage; if (depName) { edges.push([targetName, depName]); } } } return edges; } // ── 内部帮助函数 ──────────────────────────────────── function extractSourcePath(target) { if (!target.sources || target.sources.length === 0) { return undefined; } const first = target.sources[0]; if (typeof first === 'string') { return first; } return first.path; } function extractTargetDependencies(target) { if (!target.dependencies) { return []; } return target.dependencies .map((dep) => dep.target ?? dep.framework ?? dep.package ?? dep.carthage) .filter((name) => !!name); } function extractDeploymentTarget(target) { if (!target.deploymentTarget) { return undefined; } if (typeof target.deploymentTarget === 'string') { return target.deploymentTarget; } // Object form: { iOS: "15.0", macOS: "12.0" } const values = Object.values(target.deploymentTarget); return values[0]; } function isExternalTarget(targetName, doc) { // SPM packages referenced as targets are external if (doc.packages?.[targetName]) { return true; } // Targets defined in the project are local return false; } /** * 解析 melos.yaml 内容 * 提取项目名、包路径 glob 模式、scripts 列表 */ export function parseMelosProject(content) { const result = { name: '', packageGlobs: [], scripts: [], }; try { const doc = yaml.load(content, { schema: yaml.CORE_SCHEMA }); if (!doc) { return result; } result.name = doc.name ?? ''; if (Array.isArray(doc.packages)) { result.packageGlobs = doc.packages.filter((p) => typeof p === 'string'); } if (doc.scripts && typeof doc.scripts === 'object') { result.scripts = Object.keys(doc.scripts); } } catch { // YAML 解析失败时返回空结果 } return result; }