refactor: improve spark table lookup and error reporting

Replace the heuristic-based slug parsing with a more robust
combined-slug lookup to correctly handle page names containing
hyphens. Added `scanSparkTables` to provide better error messages
when a table is not found.
This commit is contained in:
2026-07-07 19:13:11 +08:00
parent 9a312f76ad
commit e801ac9b5f
2 changed files with 62 additions and 28 deletions
+42 -12
View File
@@ -148,18 +148,44 @@ export function findSparkTable(
return null;
}
/**
* Find a spark table by its combined slug (`pageName-columnSlug`).
* Iterates every spark table in the content, constructs the combined slug,
* and returns the first match. Avoids the need to guess where the page
* name ends and the column slug begins (page names may contain `-`).
*/
export function findSparkTableByCombinedSlug(
markdown: string,
combinedSlug: string,
pageName: string,
): SparkTableMeta | null {
const tables = parseMarkdownTables(markdown);
for (const table of tables) {
if (!isSparkTable(table)) continue;
const colSlug = sparkTableSlug(table);
const candidate = `${pageName}-${colSlug}`;
if (candidate === combinedSlug) {
return {
notation: table.headers[0],
slug: colSlug,
dataHeaders: table.headers.slice(1),
rows: table.rows,
};
}
}
return null;
}
/** Scan all spark tables in a markdown file and return their metadata */
export function scanSparkTables(
markdown: string,
): Omit<SparkTableMeta, "rows">[] {
const tables = parseMarkdownTables(markdown);
return tables
.filter(isSparkTable)
.map((table) => ({
notation: table.headers[0],
slug: sparkTableSlug(table),
dataHeaders: table.headers.slice(1),
}));
return tables.filter(isSparkTable).map((table) => ({
notation: table.headers[0],
slug: sparkTableSlug(table),
dataHeaders: table.headers.slice(1),
}));
}
// ---------------------------------------------------------------------------
@@ -170,9 +196,7 @@ export function scanSparkTables(
* Roll a spark table: for each data column, roll the dice formula and look
* up the corresponding row value.
*/
export function rollSparkTable(
meta: SparkTableMeta,
): SparkTableResult {
export function rollSparkTable(meta: SparkTableMeta): SparkTableResult {
const slugger = new Slugger();
const columns: SparkTableColumn[] = [];
@@ -183,11 +207,17 @@ export function rollSparkTable(
const roll = rollFormula(meta.notation);
const rolledValue = roll.result.total;
// Find the row matching the rolled value (1-based dice result)
// Find the row matching the rolled value — compare as integers,
// falling back to string comparison.
let value = `(no row for ${rolledValue})`;
for (const row of meta.rows) {
const diceCell = row[0] ?? "";
if (String(rolledValue) === diceCell.trim()) {
const cellNum = parseInt(diceCell.trim(), 10);
const match =
!isNaN(cellNum) && rolledValue === cellNum
? true
: String(rolledValue) === diceCell.trim();
if (match) {
value = row[colIdx + 1] ?? "";
break;
}