UNPKG

yarn-spinner-runner-ts

Version:

TypeScript parser, compiler, and runtime for Yarn Spinner 3.x with React adapter [NPM package](https://www.npmjs.com/package/yarn-spinner-runner-ts)

174 lines 8.27 kB
export function compile(doc, opts = {}) { const program = { enums: {}, nodes: {} }; // Store enum definitions for (const enumDef of doc.enums) { program.enums[enumDef.name] = enumDef.cases; } const genOnce = opts.generateOnceIds ?? ((x) => `${x.node}#once#${x.index}`); let globalLineCounter = 0; function ensureLineId(tags) { const t = tags ? [...tags] : []; if (!t.some((x) => x.startsWith("line:"))) { t.push(`line:${(globalLineCounter++).toString(16)}`); } return t; } // Group nodes by title to handle node groups const nodesByTitle = new Map(); for (const node of doc.nodes) { if (!nodesByTitle.has(node.title)) { nodesByTitle.set(node.title, []); } nodesByTitle.get(node.title).push(node); } for (const [title, nodesWithSameTitle] of nodesByTitle) { // If only one node with this title, treat as regular node if (nodesWithSameTitle.length === 1) { const node = nodesWithSameTitle[0]; const instructions = []; let onceCounter = 0; function emitBlock(stmts) { const block = []; for (const s of stmts) { switch (s.type) { case "Line": { const line = s; block.push({ op: "line", speaker: line.speaker, text: line.text, tags: ensureLineId(line.tags), markup: line.markup }); } break; case "Command": block.push({ op: "command", content: s.content }); break; case "Jump": block.push({ op: "jump", target: s.target }); break; case "Detour": block.push({ op: "detour", target: s.target }); break; case "OptionGroup": { // Add #lastline tag to the most recent line, if present for (let i = block.length - 1; i >= 0; i--) { const ins = block[i]; if (ins.op === "line") { const tags = new Set(ins.tags ?? []); if (![...tags].some((x) => x === "lastline" || x === "#lastline")) { tags.add("lastline"); } ins.tags = Array.from(tags); break; } if (ins.op !== "command") break; // stop if non-line non-command before options } block.push({ op: "options", options: s.options.map((o) => ({ text: o.text, tags: ensureLineId(o.tags), css: o.css, markup: o.markup, condition: o.condition, block: emitBlock(o.body) })), }); break; } case "If": block.push({ op: "if", branches: s.branches.map((b) => ({ condition: b.condition, block: emitBlock(b.body) })), }); break; case "Once": block.push({ op: "once", id: genOnce({ node: node.title, index: onceCounter++ }), block: emitBlock(s.body) }); break; case "Enum": // Enums are metadata, skip during compilation (already stored in program.enums) break; } } return block; } instructions.push(...emitBlock(node.body)); const irNode = { title: node.title, instructions, when: node.when, css: node.css, scene: node.headers.scene?.trim() || undefined }; program.nodes[node.title] = irNode; } else { // Multiple nodes with same title - create node group const groupNodes = []; for (const node of nodesWithSameTitle) { const instructions = []; let onceCounter = 0; function emitBlock(stmts) { const block = []; for (const s of stmts) { switch (s.type) { case "Line": { const line = s; block.push({ op: "line", speaker: line.speaker, text: line.text, tags: ensureLineId(line.tags), markup: line.markup }); } break; case "Command": block.push({ op: "command", content: s.content }); break; case "Jump": block.push({ op: "jump", target: s.target }); break; case "Detour": block.push({ op: "detour", target: s.target }); break; case "OptionGroup": { for (let i = block.length - 1; i >= 0; i--) { const ins = block[i]; if (ins.op === "line") { const tags = new Set(ins.tags ?? []); if (![...tags].some((x) => x === "lastline" || x === "#lastline")) { tags.add("lastline"); } ins.tags = Array.from(tags); break; } if (ins.op !== "command") break; } block.push({ op: "options", options: s.options.map((o) => ({ text: o.text, tags: ensureLineId(o.tags), css: o.css, markup: o.markup, condition: o.condition, block: emitBlock(o.body) })), }); break; } case "If": block.push({ op: "if", branches: s.branches.map((b) => ({ condition: b.condition, block: emitBlock(b.body) })), }); break; case "Once": block.push({ op: "once", id: genOnce({ node: node.title, index: onceCounter++ }), block: emitBlock(s.body) }); break; case "Enum": break; } } return block; } instructions.push(...emitBlock(node.body)); groupNodes.push({ title: node.title, instructions, when: node.when, css: node.css, scene: node.headers.scene?.trim() || undefined }); } const group = { title, nodes: groupNodes }; program.nodes[title] = group; } } return program; } //# sourceMappingURL=compiler.js.map