@podlite/markdown 0.0.31 → 0.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.esm.js CHANGED
@@ -25644,7 +25644,7 @@ var require_package = __commonJS({
25644
25644
  "../podlite-schema/package.json"(exports, module) {
25645
25645
  module.exports = {
25646
25646
  name: "@podlite/schema",
25647
- version: "0.0.38",
25647
+ version: "0.0.40",
25648
25648
  description: "AST tools and schema for Podlite markup language \u2014 validate, traverse, and transform document trees",
25649
25649
  main: "./src/index.ts",
25650
25650
  homepage: "https://podlite.org",
@@ -27026,6 +27026,245 @@ var plugin_defn_fill_term_default = () => (tree) => {
27026
27026
  function flattenDeep3(arr) {
27027
27027
  return arr.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep3(val)) : acc.concat(val), []);
27028
27028
  }
27029
+ function parseCsv(text4) {
27030
+ const rows = [];
27031
+ let field = "";
27032
+ let row = [];
27033
+ let inQuote = false;
27034
+ let i = 0;
27035
+ while (i < text4.length) {
27036
+ const c = text4[i];
27037
+ if (inQuote) {
27038
+ if (c === '"' && text4[i + 1] === '"') {
27039
+ field += '"';
27040
+ i += 2;
27041
+ continue;
27042
+ }
27043
+ if (c === '"') {
27044
+ inQuote = false;
27045
+ i++;
27046
+ continue;
27047
+ }
27048
+ field += c;
27049
+ i++;
27050
+ continue;
27051
+ }
27052
+ if (c === '"' && field === "") {
27053
+ inQuote = true;
27054
+ i++;
27055
+ continue;
27056
+ }
27057
+ if (c === ",") {
27058
+ row.push(field);
27059
+ field = "";
27060
+ i++;
27061
+ continue;
27062
+ }
27063
+ if (c === "\r") {
27064
+ i++;
27065
+ continue;
27066
+ }
27067
+ if (c === "\n") {
27068
+ row.push(field);
27069
+ rows.push(row);
27070
+ row = [];
27071
+ field = "";
27072
+ i++;
27073
+ continue;
27074
+ }
27075
+ field += c;
27076
+ i++;
27077
+ }
27078
+ if (field !== "" || row.length > 0) {
27079
+ row.push(field);
27080
+ rows.push(row);
27081
+ }
27082
+ return rows.filter((r) => !(r.length === 1 && r[0].trim() === ""));
27083
+ }
27084
+ function parseTsv(text4) {
27085
+ const lines = text4.split(/\r?\n/);
27086
+ const rows = lines.map((line) => line.split(" "));
27087
+ return rows.filter((r) => !(r.length === 1 && r[0].trim() === ""));
27088
+ }
27089
+ function parseMimeType(raw) {
27090
+ if (!raw || typeof raw !== "string")
27091
+ return { type: "", params: {} };
27092
+ const parts = raw.split(";").map((s) => s.trim());
27093
+ const type = (parts.shift() || "").toLowerCase();
27094
+ const params = {};
27095
+ for (const p of parts) {
27096
+ if (!p)
27097
+ continue;
27098
+ const eq = p.indexOf("=");
27099
+ if (eq < 0)
27100
+ continue;
27101
+ const key = p.slice(0, eq).trim().toLowerCase();
27102
+ let value = p.slice(eq + 1).trim();
27103
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
27104
+ value = value.slice(1, -1);
27105
+ }
27106
+ if (key)
27107
+ params[key] = value;
27108
+ }
27109
+ return { type, params };
27110
+ }
27111
+ function findDataBlockByKey(tree, key) {
27112
+ let found = null;
27113
+ const walk = (node) => {
27114
+ if (found)
27115
+ return;
27116
+ if (Array.isArray(node)) {
27117
+ node.forEach(walk);
27118
+ return;
27119
+ }
27120
+ if (!node || typeof node !== "object")
27121
+ return;
27122
+ if (node.type === "block" && node.name === "data") {
27123
+ const attrs = config_default(node, {});
27124
+ if (attrs.getFirstValue("key") === key) {
27125
+ found = node;
27126
+ return;
27127
+ }
27128
+ }
27129
+ if (Array.isArray(node.content))
27130
+ node.content.forEach(walk);
27131
+ };
27132
+ walk(tree);
27133
+ return found;
27134
+ }
27135
+ function extractDataText(dataNode) {
27136
+ if (!dataNode || !Array.isArray(dataNode.content))
27137
+ return "";
27138
+ const verbatim = dataNode.content.find((c) => c && c.type === "verbatim");
27139
+ return verbatim && typeof verbatim.value === "string" ? verbatim.value : "";
27140
+ }
27141
+ function detectSourceReference(tableNode) {
27142
+ const texts = [];
27143
+ const walker = makeTransformer_default({
27144
+ "row:text": (r) => {
27145
+ texts.push(r.value);
27146
+ return r;
27147
+ },
27148
+ "head:text": (h) => {
27149
+ texts.push(h.value);
27150
+ return h;
27151
+ }
27152
+ });
27153
+ walker(tableNode, {});
27154
+ const joined = texts.join("\n").trim();
27155
+ const lines = joined.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
27156
+ if (lines.length !== 1)
27157
+ return null;
27158
+ const m = lines[0].match(/^(data|file):(\S+)$/);
27159
+ if (!m)
27160
+ return null;
27161
+ return { scheme: m[1], target: m[2] };
27162
+ }
27163
+ function buildCellBlock(text4) {
27164
+ return {
27165
+ name: "cell",
27166
+ type: "block",
27167
+ margin: "",
27168
+ content: [{ type: "text", value: text4 }]
27169
+ };
27170
+ }
27171
+ function buildRowBlock(cells, isHeader) {
27172
+ const block = {
27173
+ name: "row",
27174
+ type: "block",
27175
+ margin: "",
27176
+ content: cells
27177
+ };
27178
+ if (isHeader) {
27179
+ block.config = [{ name: "header", value: true, type: "boolean" }];
27180
+ }
27181
+ return block;
27182
+ }
27183
+ function csvToTableContent(csvRows, hasHeader = false) {
27184
+ return csvRows.map((row, i) => {
27185
+ const cells = row.map((v) => buildCellBlock(v.trim()));
27186
+ return buildRowBlock(cells, hasHeader && i === 0);
27187
+ });
27188
+ }
27189
+ function normalizeCellCounts(tableNode, source = "table") {
27190
+ if (!tableNode || !Array.isArray(tableNode.content))
27191
+ return tableNode;
27192
+ const rows = tableNode.content.filter((c) => c && c.type === "block" && c.name === "row");
27193
+ if (rows.length === 0)
27194
+ return tableNode;
27195
+ const cellsOf = (row) => Array.isArray(row.content) ? row.content.filter((c) => c && c.name === "cell") : [];
27196
+ const isHeaderRow = (row) => Array.isArray(row.config) && row.config.some((a) => a.name === "header" && a.value === true);
27197
+ const cellHasSpan = (cell) => Array.isArray(cell.config) && cell.config.some((a) => a.name === "colspan" || a.name === "rowspan");
27198
+ const rowHasSpan = (row) => cellsOf(row).some(cellHasSpan);
27199
+ if (rows.some(rowHasSpan))
27200
+ return tableNode;
27201
+ const headerRow = rows.find(isHeaderRow);
27202
+ const expected = headerRow ? cellsOf(headerRow).length : Math.max(...rows.map((r) => cellsOf(r).length));
27203
+ if (expected === 0)
27204
+ return tableNode;
27205
+ let mutated = false;
27206
+ const newContent = tableNode.content.map((child) => {
27207
+ if (!child || child.type !== "block" || child.name !== "row")
27208
+ return child;
27209
+ const cells = cellsOf(child);
27210
+ if (cells.length === expected)
27211
+ return child;
27212
+ if (cells.length < expected) {
27213
+ const padding = [];
27214
+ for (let i = cells.length; i < expected; i++)
27215
+ padding.push(buildCellBlock(""));
27216
+ console.warn(
27217
+ `[${source}] row has ${cells.length} cells, expected ${expected} \u2014 padded with ${padding.length} empty`
27218
+ );
27219
+ mutated = true;
27220
+ return { ...child, content: [...child.content, ...padding] };
27221
+ }
27222
+ const dropped = cells.length - expected;
27223
+ console.warn(`[${source}] row has ${cells.length} cells, expected ${expected} \u2014 truncated ${dropped}`);
27224
+ mutated = true;
27225
+ let keepCells = expected;
27226
+ const trimmed = [];
27227
+ for (const c of child.content || []) {
27228
+ if (c && c.type === "block" && c.name === "cell") {
27229
+ if (keepCells > 0) {
27230
+ trimmed.push(c);
27231
+ keepCells--;
27232
+ }
27233
+ } else {
27234
+ trimmed.push(c);
27235
+ }
27236
+ }
27237
+ return { ...child, content: trimmed };
27238
+ });
27239
+ return mutated ? { ...tableNode, content: newContent } : tableNode;
27240
+ }
27241
+ function detectMixedSeparators(lines) {
27242
+ const seen = /* @__PURE__ */ new Set();
27243
+ for (const line of lines) {
27244
+ if (!line || typeof line !== "string")
27245
+ continue;
27246
+ if (/\s\|\s/.test(line))
27247
+ seen.add("pipe");
27248
+ else if (/\s\+\s/.test(line))
27249
+ seen.add("plus");
27250
+ else if (line.trim().length > 0)
27251
+ seen.add("whitespace");
27252
+ if (seen.size > 1)
27253
+ break;
27254
+ }
27255
+ if (seen.size > 1) {
27256
+ console.warn(`[table] mixed separator types detected: ${Array.from(seen).join(", ")} \u2014 recommend a single style`);
27257
+ }
27258
+ }
27259
+ function buildCodeFromDataBlock(tableNode, dataBlock) {
27260
+ return {
27261
+ type: "block",
27262
+ name: "code",
27263
+ margin: tableNode.margin || "",
27264
+ content: Array.isArray(dataBlock.content) ? dataBlock.content : [],
27265
+ config: Array.isArray(tableNode.config) ? tableNode.config : []
27266
+ };
27267
+ }
27029
27268
  var strbin = (str1, str2, cb) => {
27030
27269
  let res = [];
27031
27270
  for (let i = 0; str1.length > i; i++) {
@@ -27036,49 +27275,91 @@ var strbin = (str1, str2, cb) => {
27036
27275
  var makeMask = (lines, separators) => {
27037
27276
  const tmplLength = Math.max(...[...lines, ...separators].map((s) => s.length));
27038
27277
  const masks = lines.map((str) => {
27039
- let tstr = str + " ".repeat(tmplLength - str.length);
27040
- let mask = [];
27278
+ const tstr = str + " ".repeat(tmplLength - str.length);
27279
+ const mask = [];
27041
27280
  const re = /\s+[+|\s]\s/g;
27042
27281
  let match;
27043
27282
  while ((match = re.exec(tstr)) != null) {
27044
27283
  const tmpMask = "1".repeat(match.index) + "0".repeat(match[0].length);
27045
27284
  mask.push(tmpMask + "1".repeat(tmplLength - tmpMask.length));
27046
27285
  }
27047
- return mask.reduce((a, b) => {
27048
- return strbin(a, b, (i1, i2) => i1 & i2);
27049
- }, "1".repeat(tmplLength));
27286
+ return mask.reduce((a, b) => strbin(a, b, (i1, i2) => i1 & i2), "1".repeat(tmplLength));
27050
27287
  });
27051
27288
  const inverted = masks.map((m) => strbin(m, "", (i1) => i1 == 0 ? 1 : 0));
27052
- const columnTemplate = inverted.reduce((a, b) => {
27053
- return strbin(a, b, (i1, i2) => i1 & i2);
27054
- }, "1".repeat(tmplLength));
27055
- return columnTemplate;
27289
+ return inverted.reduce((a, b) => strbin(a, b, (i1, i2) => i1 & i2), "1".repeat(tmplLength));
27056
27290
  };
27057
27291
  var extractColumnsByTemplate = (text4, template) => {
27058
- const lines = flattenDeep3(
27059
- text4.split(/\n/).filter((str) => str.length > 0)
27060
- );
27292
+ const lines = flattenDeep3(text4.split(/\n/).filter((s) => s.length > 0));
27061
27293
  const cols = lines.map((line) => {
27062
27294
  const re = /((1+|0+))/g;
27063
- let columns = [];
27295
+ const columns = [];
27064
27296
  let match;
27065
27297
  while ((match = re.exec(template)) != null) {
27066
- if (match[0][0] == 1)
27298
+ if (match[0][0] == "1")
27067
27299
  continue;
27068
- const s = line.substring(match.index, match.index + match[0].length);
27069
- columns.push(s);
27300
+ columns.push(line.substring(match.index, match.index + match[0].length));
27070
27301
  }
27071
27302
  return columns;
27072
27303
  });
27073
- let result = [];
27074
- result = cols.reduce((a, b) => {
27304
+ const result = [];
27305
+ cols.reduce((a, b) => {
27075
27306
  for (let i = 0; i < b.length; i++) {
27076
27307
  a[i] = (a[i] === void 0 ? "" : a[i]) + " " + b[i];
27077
27308
  }
27078
27309
  return a;
27079
- }, []);
27310
+ }, result);
27080
27311
  return result;
27081
27312
  };
27313
+ var detectLineSeparator = (line) => {
27314
+ if (/(?:^|\s)\|(?:\s|$)/.test(line))
27315
+ return "pipe";
27316
+ if (/(?:^|\s)\+(?:\s|$)/.test(line))
27317
+ return "plus";
27318
+ return "whitespace";
27319
+ };
27320
+ var trimEdgeEmpty = (cells) => {
27321
+ let start = 0;
27322
+ let end = cells.length;
27323
+ if (cells[start] === "")
27324
+ start++;
27325
+ if (end > start && cells[end - 1] === "")
27326
+ end--;
27327
+ return cells.slice(start, end);
27328
+ };
27329
+ var splitLineByPipe = (line) => trimEdgeEmpty(line.split(/\s*\|\s*/).map((c) => c.trim()));
27330
+ var splitLineByPlus = (line) => trimEdgeEmpty(line.split(/\s*\+\s*/).map((c) => c.trim()));
27331
+ var splitLineByWhitespace = (line) => line.trim().split(/\s{2,}/).filter((c) => c !== "");
27332
+ var splitLineCells = (line) => {
27333
+ const trimmed = line.trim();
27334
+ if (trimmed === "")
27335
+ return [];
27336
+ const kind = detectLineSeparator(line);
27337
+ if (kind === "pipe")
27338
+ return splitLineByPipe(line);
27339
+ if (kind === "plus")
27340
+ return splitLineByPlus(line);
27341
+ return splitLineByWhitespace(line);
27342
+ };
27343
+ var rowToCells = (rowValue) => {
27344
+ const lines = rowValue.split(/\r?\n/).filter((l) => l.trim() !== "");
27345
+ if (lines.length === 0)
27346
+ return [];
27347
+ if (lines.length === 1)
27348
+ return splitLineCells(lines[0]);
27349
+ const kind = detectLineSeparator(lines[0]);
27350
+ const splitFn = kind === "pipe" ? splitLineByPipe : kind === "plus" ? splitLineByPlus : splitLineByWhitespace;
27351
+ const lineCells = lines.map(splitFn);
27352
+ const maxCols = Math.max(...lineCells.map((c) => c.length));
27353
+ const merged = [];
27354
+ for (let i = 0; i < maxCols; i++) {
27355
+ const parts = lineCells.map((line) => {
27356
+ var _a;
27357
+ return (_a = line[i]) != null ? _a : "";
27358
+ }).filter((p) => p !== "");
27359
+ merged.push(parts.join(" "));
27360
+ }
27361
+ return merged;
27362
+ };
27082
27363
  var wrapImplicitCells = (rowNode) => {
27083
27364
  if (!Array.isArray(rowNode.content) || rowNode.content.length === 0)
27084
27365
  return rowNode;
@@ -27098,13 +27379,42 @@ var isStructured = (tableNode) => Array.isArray(tableNode.content) && tableNode.
27098
27379
  var plugin_tables_default = () => (tree) => {
27099
27380
  const transformer = makeTransformer_default({
27100
27381
  table: (node) => {
27382
+ const ref = detectSourceReference(node);
27383
+ if (ref && ref.scheme === "data") {
27384
+ const dataBlock = findDataBlockByKey(tree, ref.target);
27385
+ if (!dataBlock) {
27386
+ console.warn(`[table] no =data block found for data:${ref.target} \u2014 rendered as empty`);
27387
+ return { ...node, content: [] };
27388
+ }
27389
+ const rawMime = config_default(dataBlock, {}).getFirstValue("mime-type");
27390
+ const { type: mimeType, params: mimeParams } = parseMimeType(rawMime);
27391
+ const isCsv = mimeType === "text/csv";
27392
+ const isTsv = mimeType === "text/tab-separated-values";
27393
+ if (isCsv || isTsv) {
27394
+ const text4 = extractDataText(dataBlock);
27395
+ const rows2 = isCsv ? parseCsv(text4) : parseTsv(text4);
27396
+ if (rows2.length === 0) {
27397
+ console.warn(
27398
+ `[table] ${isCsv ? "CSV" : "TSV"} parse produced no rows for data:${ref.target} \u2014 rendered as empty`
27399
+ );
27400
+ return { ...node, content: [] };
27401
+ }
27402
+ const hasHeader = mimeParams.header === "present";
27403
+ const filledNode = { ...node, content: csvToTableContent(rows2, hasHeader) };
27404
+ return normalizeCellCounts(filledNode, `table data:${ref.target}`);
27405
+ }
27406
+ console.warn(
27407
+ `[table] =data :key<${ref.target}> has non-tabular mime-type ${rawMime || "(none)"} \u2014 rendered as =code`
27408
+ );
27409
+ return buildCodeFromDataBlock(node, dataBlock);
27410
+ }
27101
27411
  if (isStructured(node)) {
27102
27412
  const transformedContent = (node.content || []).map((c) => {
27103
27413
  if (c && c.name === "row")
27104
27414
  return wrapImplicitCells(c);
27105
27415
  return c;
27106
27416
  });
27107
- return { ...node, content: transformedContent };
27417
+ return normalizeCellCounts({ ...node, content: transformedContent }, "table");
27108
27418
  }
27109
27419
  let rows = [];
27110
27420
  const collectValues = (row) => {
@@ -27123,37 +27433,46 @@ var plugin_tables_default = () => (tree) => {
27123
27433
  const splitToLines = (row) => row.split(/\n/).filter((str) => str.length > 0);
27124
27434
  const lines = flattenDeep3(rows.map(splitToLines));
27125
27435
  const separators = flattenDeep3(seps.map(splitToLines));
27436
+ detectMixedSeparators(lines);
27126
27437
  let textRows = [];
27127
27438
  makeTransformer_default({
27128
27439
  "row:text": (row) => {
27129
27440
  textRows.push(row.value);
27130
27441
  }
27131
27442
  })(node);
27132
- const columnTemplate = makeMask(lines, separators);
27133
27443
  const makeBlock = (name, content4, extra = {}) => {
27134
27444
  return { ...extra, name, type: "block", content: Array.isArray(content4) ? content4 : [content4] };
27135
27445
  };
27136
27446
  const makeRow = (cells) => makeBlock("row", cells);
27137
27447
  const makeHeaderRow = (cells) => makeBlock("row", cells, { config: [{ name: "header", value: true, type: "boolean" }] });
27138
27448
  const makeCell = (text4) => makeBlock("cell", { type: "text", value: text4 });
27449
+ const columnTemplate = makeMask(lines, separators);
27450
+ const seenSeparatorKinds = new Set(lines.map(detectLineSeparator));
27451
+ const hasVisible = seenSeparatorKinds.has("pipe") || seenSeparatorKinds.has("plus");
27452
+ const hasWhitespace = seenSeparatorKinds.has("whitespace");
27453
+ const useMixedSplitting = hasVisible && hasWhitespace;
27454
+ const splitToCells = (rowValue) => {
27455
+ if (useMixedSplitting) {
27456
+ const rowLines = rowValue.split(/\r?\n/).filter((l) => l.trim() !== "");
27457
+ if (rowLines.length <= 1)
27458
+ return rowToCells(rowValue);
27459
+ }
27460
+ return extractColumnsByTemplate(rowValue, columnTemplate);
27461
+ };
27139
27462
  const res = makeTransformer_default({
27140
27463
  "row:text": (row) => {
27141
27464
  if (textRows.length == 1) {
27142
27465
  const textRowsLines = flattenDeep3([row.value].map(splitToLines));
27143
- return textRowsLines.map((rowValue) => {
27144
- const cols2 = extractColumnsByTemplate(rowValue, columnTemplate);
27145
- return makeRow(cols2.map(makeCell));
27146
- });
27466
+ if (useMixedSplitting) {
27467
+ return textRowsLines.map((line) => makeRow(splitLineCells(line).map(makeCell)));
27468
+ }
27469
+ return textRowsLines.map((line) => makeRow(extractColumnsByTemplate(line, columnTemplate).map(makeCell)));
27147
27470
  }
27148
- const cols = extractColumnsByTemplate(row.value, columnTemplate);
27149
- return makeRow(cols.map(makeCell));
27471
+ return makeRow(splitToCells(row.value).map(makeCell));
27150
27472
  },
27151
- "head:text": (head) => {
27152
- const cols = extractColumnsByTemplate(head.value, columnTemplate);
27153
- return makeHeaderRow(cols.map(makeCell));
27154
- }
27473
+ "head:text": (head) => makeHeaderRow(splitToCells(head.value).map(makeCell))
27155
27474
  })(node);
27156
- return res;
27475
+ return normalizeCellCounts(res, "table");
27157
27476
  }
27158
27477
  });
27159
27478
  return transformer(tree, {});