#!/usr/bin/env node /** * scripts/generate-voice-aliases.mjs * * 读取 intelligent_cabin/config/voice_aliases.yml * 生成 src/lib/nlu/voice-aliases.gen.ts(构建时嵌入,运行时无需 HTTP 请求) * * 用法: * node scripts/generate-voice-aliases.mjs * 或在 package.json 的 prebuild / predev 中调用 */ import { readFileSync, writeFileSync } from "node:fs"; import { resolve, dirname } from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = dirname(fileURLToPath(import.meta.url)); const ROOT = resolve(__dirname, ".."); // ── 极简 YAML 解析(只处理本文件的结构,无需外部依赖)──────────────────── function parseSimpleYaml(text) { const lines = text.split("\n"); const result = {}; let currentKey = null; let currentListKey = null; let currentObject = result; for (const raw of lines) { const line = raw; const stripped = raw.trimStart(); if (!stripped || stripped.startsWith("#")) continue; const indent = raw.length - stripped.length; // 顶级 key(indent = 0) if (indent === 0 && stripped.endsWith(":")) { currentKey = stripped.slice(0, -1).trim(); result[currentKey] = {}; currentObject = result[currentKey]; currentListKey = null; continue; } // intent_aliases 下的子 key(indent = 2) if (indent === 2 && stripped.endsWith(":")) { currentListKey = stripped.slice(0, -1).trim(); if (!Array.isArray(currentObject[currentListKey])) { currentObject[currentListKey] = []; } continue; } // list items(indent = 2 for top-level lists, indent = 4 for nested) if (stripped.startsWith("- ")) { const value = stripped.slice(2).trim().replace(/^["']|["']$/g, ""); if (indent === 2 && !currentListKey) { // top-level list under result key if (!Array.isArray(result[currentKey])) { result[currentKey] = []; } result[currentKey].push(value); } else if (indent === 4 && currentListKey) { currentObject[currentListKey].push(value); } continue; } } return result; } // ── 读取 YAML ──────────────────────────────────────────────────────────────── const yamlPath = resolve(ROOT, "intelligent_cabin/config/voice_aliases.yml"); const raw = readFileSync(yamlPath, "utf8"); const parsed = parseSimpleYaml(raw); const affirmWords = parsed["affirm_words"] ?? []; const denyWords = parsed["deny_words"] ?? []; const cancelWords = parsed["cancel_words"] ?? []; const intentAliases = parsed["intent_aliases"] ?? {}; // ── 生成 TypeScript ─────────────────────────────────────────────────────────── const ts = `// AUTO-GENERATED — do not edit manually // Source: intelligent_cabin/config/voice_aliases.yml // Regenerate: node scripts/generate-voice-aliases.mjs export const AFFIRM_WORDS: readonly string[] = ${JSON.stringify(affirmWords, null, 2)} as const; export const DENY_WORDS: readonly string[] = ${JSON.stringify(denyWords, null, 2)} as const; export const CANCEL_WORDS: readonly string[] = ${JSON.stringify(cancelWords, null, 2)} as const; export const INTENT_ALIASES: Readonly> = ${JSON.stringify(intentAliases, null, 2)} as const; `; const outPath = resolve(ROOT, "src/lib/nlu/voice-aliases.gen.ts"); writeFileSync(outPath, ts, "utf8"); console.log(`✅ Generated: ${outPath}`);