From 0cce2a5c6fdd94c92edd43da083eb602bdca5609 Mon Sep 17 00:00:00 2001 From: Arunim Shukla <54760103+arunimshukla@users.noreply.github.com> Date: Mon, 28 Sep 2026 23:39:55 +0530 Subject: [PATCH] fix: preserve continuation-line glossary definitions --- scripts/build-graph.mjs | 29 ++++++++++--- src/data/glossary.json | 2 +- tests/build-graph.test.mjs | 87 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 6 deletions(-) diff --git a/scripts/build-graph.mjs b/scripts/build-graph.mjs index 8c2f693..97b9daf 100644 --- a/scripts/build-graph.mjs +++ b/scripts/build-graph.mjs @@ -239,20 +239,39 @@ export function parseGlossary(repoRoot) { const content = readFileSync(glossaryPath, "utf-8"); const terms = []; let currentCategory = ""; + const lines = content.split("\n"); + const termPattern = + /^\*\*(?:\[([^\]]+)\]\(([^)]+)\)|([^*]+))\*\*:\s*(.*)/; - for (const line of content.split("\n")) { + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; const catMatch = line.match(/^### (.+)/); if (catMatch) { currentCategory = catMatch[1].trim(); continue; } - const termMatch = line.match( - /^\*\*(?:\[([^\]]+)\]\(([^)]+)\)|([^*]+))\*\*:\s*(.*)/, - ); + const termMatch = line.match(termPattern); if (termMatch) { + let definition = termMatch[4].trim(); + + // GLOSSARY.md occasionally puts a term's definition on the next line. + // Only consume the immediate continuation line, and never swallow the + // next category or glossary term if the definition is actually missing. + if (!definition) { + const continuation = lines[i + 1]?.trim(); + if ( + continuation && + !continuation.startsWith("### ") && + !termPattern.test(continuation) + ) { + definition = continuation; + i += 1; + } + } + const term = { term: (termMatch[1] ?? termMatch[3]).trim(), - definition: termMatch[4].trim(), + definition, category: currentCategory, }; if (termMatch[2]) term.href = termMatch[2].trim(); diff --git a/src/data/glossary.json b/src/data/glossary.json index b1e2dd9..444b525 100644 --- a/src/data/glossary.json +++ b/src/data/glossary.json @@ -56,7 +56,7 @@ }, { "term": "Data Availability Layer (DA Layer)", - "definition": "", + "definition": "A dedicated network or service that publishes and stores the data required for DA (for example, rollup or application data), separate from the main execution chain.", "category": "Blockchain Architecture" }, { diff --git a/tests/build-graph.test.mjs b/tests/build-graph.test.mjs index cd4393d..6805370 100644 --- a/tests/build-graph.test.mjs +++ b/tests/build-graph.test.mjs @@ -233,6 +233,93 @@ describe('classifyEdge', () => { // --------------------------------------------------------------------------- describe('parseGlossary', () => { + it('reads a definition from the immediate continuation line', () => { + const repoRoot = mkdtempSync(join(tmpdir(), 'ethsystems-glossary-')); + try { + writeFileSync( + join(repoRoot, 'GLOSSARY.md'), + `### Blockchain Architecture + +**Data Availability Layer (DA Layer)**: +A dedicated network or service that publishes and stores the data required for DA. + +**Sequencer**: Orders L2 transactions. +`, + ); + + const glossary = parseGlossary(repoRoot); + + expect(glossary).toHaveLength(2); + expect(glossary[0]).toEqual({ + term: 'Data Availability Layer (DA Layer)', + definition: + 'A dedicated network or service that publishes and stores the data required for DA.', + category: 'Blockchain Architecture', + }); + expect(glossary[1]).toEqual({ + term: 'Sequencer', + definition: 'Orders L2 transactions.', + category: 'Blockchain Architecture', + }); + } finally { + rmSync(repoRoot, { recursive: true, force: true }); + } + }); + + it('does not consume the next term when a definition is missing', () => { + const repoRoot = mkdtempSync(join(tmpdir(), 'ethsystems-glossary-')); + try { + writeFileSync( + join(repoRoot, 'GLOSSARY.md'), + `### Blockchain Architecture + +**Missing Definition**: +**Sequencer**: Orders L2 transactions. +`, + ); + + const glossary = parseGlossary(repoRoot); + + expect(glossary).toHaveLength(2); + expect(glossary[0].definition).toBe(''); + expect(glossary[1].definition).toBe('Orders L2 transactions.'); + } finally { + rmSync(repoRoot, { recursive: true, force: true }); + } + }); + + it('does not consume the next category when a definition is missing', () => { + const repoRoot = mkdtempSync(join(tmpdir(), 'ethsystems-glossary-')); + try { + writeFileSync( + join(repoRoot, 'GLOSSARY.md'), + `### Blockchain Architecture + +**Missing Definition**: +### Next Category + +**Sequencer**: Orders L2 transactions. +`, + ); + + const glossary = parseGlossary(repoRoot); + + expect(glossary).toHaveLength(2); + expect(glossary[0]).toEqual({ + term: 'Missing Definition', + definition: '', + category: 'Blockchain Architecture', + }); + expect(glossary[1]).toEqual({ + term: 'Sequencer', + definition: 'Orders L2 transactions.', + category: 'Next Category', + }); + } finally { + rmSync(repoRoot, { recursive: true, force: true }); + } + }); + it('preserves linked term hrefs and definition markdown', () => { const repoRoot = mkdtempSync(join(tmpdir(), 'ethsystems-glossary-')); try {