@podlite/schema 0.0.37 → 0.0.39
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/CHANGELOG.podlite +26 -0
- package/README.md +61 -4
- package/esm/blocks-helpers.d.ts +2 -2
- package/esm/blocks-helpers.js +11 -4
- package/esm/blocks-helpers.js.map +1 -1
- package/esm/exportHtml.js +57 -6
- package/esm/exportHtml.js.map +1 -1
- package/esm/exportMarkdown.js +52 -6
- package/esm/exportMarkdown.js.map +1 -1
- package/esm/grammar.js +442 -215
- package/esm/grammar.js.map +1 -1
- package/esm/grammarfc.js +1819 -1459
- package/esm/grammarfc.js.map +1 -1
- package/esm/helpers/config.d.ts +1 -0
- package/esm/helpers/config.js +8 -0
- package/esm/helpers/config.js.map +1 -1
- package/esm/index.d.ts +2 -0
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/plugin-tables.d.ts +0 -3
- package/esm/plugin-tables.js +425 -45
- package/esm/plugin-tables.js.map +1 -1
- package/esm/selectors.d.ts +18 -0
- package/esm/selectors.js +158 -0
- package/esm/selectors.js.map +1 -0
- package/esm/types.d.ts +9 -7
- package/lib/blocks-helpers.d.ts +2 -2
- package/lib/blocks-helpers.js +13 -6
- package/lib/exportHtml.js +57 -6
- package/lib/exportMarkdown.js +52 -6
- package/lib/grammar.js +442 -215
- package/lib/grammarfc.js +1819 -1459
- package/lib/helpers/config.d.ts +1 -0
- package/lib/helpers/config.js +8 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +4 -1
- package/lib/plugin-tables.d.ts +0 -3
- package/lib/plugin-tables.js +425 -45
- package/lib/selectors.d.ts +18 -0
- package/lib/selectors.js +163 -0
- package/lib/types.d.ts +9 -7
- package/package.json +2 -2
- package/schema/AstTree.json +6 -3
- package/schema/PodliteDocument.json +6 -3
package/esm/helpers/config.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare type Context = {
|
|
|
4
4
|
export interface Attr {
|
|
5
5
|
getAllValues: (name: any) => any;
|
|
6
6
|
getFirstValue: (name: any) => any;
|
|
7
|
+
getMapValue: (name: string) => Record<string, string | number | boolean> | undefined;
|
|
7
8
|
asHash: () => {};
|
|
8
9
|
(): {};
|
|
9
10
|
exists(name: string): boolean;
|
package/esm/helpers/config.js
CHANGED
|
@@ -40,6 +40,14 @@ export const makeAttrs = (node, ctx = {}) => {
|
|
|
40
40
|
resfn.getFirstValue = name => {
|
|
41
41
|
return resfn.exists(name) ? resfn.getAllValues(name)[0] : undefined;
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* return first map-typed value (from `:attr{ k=>v, ... }` syntax) or
|
|
45
|
+
* undefined when the attribute is absent or not a map value
|
|
46
|
+
*/
|
|
47
|
+
resfn.getMapValue = name => {
|
|
48
|
+
const first = resfn.getFirstValue(name);
|
|
49
|
+
return first && typeof first === 'object' && !Array.isArray(first) ? first : undefined;
|
|
50
|
+
};
|
|
43
51
|
/**
|
|
44
52
|
* return key: val
|
|
45
53
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/helpers/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/helpers/config.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,MAAe,EAAE,EAAQ,EAAE;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;IAC9D,wBAAwB;IACxB,IAAI,UAAU,GAAG,EAAE,CAAA;IACnB,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACtD,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;KACnC;IACD,IAAI,MAAM,GAAG,EAAE,CACd;IAAA,CAAC,GAAG,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;QAClC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE;YAClC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;SACpB;QACD,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE;YACtB,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAA;SAChC;aAAM;YACL,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;SAC7B;IACH,CAAC,CAAC,CAAA;IACF,IAAI,KAAK,GAAG,cAAa,CAAS,CAAA;IAClC;;;;OAIG;IACH,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;IAElD;;;;OAIG;IACH,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,EAAE;QAC1B,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAC/C,CAAC,CAAA;IAED;;;;OAIG;IACH,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,EAAE;QAC3B,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACrE,CAAC,CAAA;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;QACvC,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;IACxF,CAAC,CAAA;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAA;IAE3B,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AACD,eAAe,SAAS,CAAA"}
|
package/esm/index.d.ts
CHANGED
|
@@ -78,5 +78,7 @@ export { parse as parse };
|
|
|
78
78
|
export { default as toHtml } from './exportHtml';
|
|
79
79
|
export { default as toMarkdown } from './exportMarkdown';
|
|
80
80
|
export { default as Writer } from './writer';
|
|
81
|
+
export { parseSelector, runSelector } from './selectors';
|
|
82
|
+
export type { SelectorDoc, ParsedSelector } from './selectors';
|
|
81
83
|
declare const VERSION: any;
|
|
82
84
|
export { VERSION as version };
|
package/esm/index.js
CHANGED
|
@@ -140,6 +140,7 @@ export { parse as parse };
|
|
|
140
140
|
export { default as toHtml } from './exportHtml';
|
|
141
141
|
export { default as toMarkdown } from './exportMarkdown';
|
|
142
142
|
export { default as Writer } from './writer';
|
|
143
|
+
export { parseSelector, runSelector } from './selectors';
|
|
143
144
|
// Cannot be `import` as it's not under TS root dir
|
|
144
145
|
// https://stackoverflow.com/questions/51070138/how-to-import-package-json-into-typescript-file-without-including-it-in-the-comp
|
|
145
146
|
const { version: VERSION } = require('../package.json');
|
package/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/esm/plugin-tables.d.ts
CHANGED
package/esm/plugin-tables.js
CHANGED
|
@@ -4,13 +4,271 @@
|
|
|
4
4
|
* and the remaining content is treated as the definition for the term.
|
|
5
5
|
*/
|
|
6
6
|
import makeTransformer from './helpers/makeTransformer';
|
|
7
|
+
import makeAttrs from './helpers/config';
|
|
7
8
|
function flattenDeep(arr) {
|
|
8
9
|
return arr.reduce((acc, val) => (Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val)), []);
|
|
9
10
|
}
|
|
11
|
+
// ─── CSV source for =table (spec §1672) ────────────────────────────────────
|
|
12
|
+
// Parse CSV content per RFC 4180: comma delimiter, `"`-quoted fields with
|
|
13
|
+
// `""` as embedded-quote escape. Supports LF or CRLF line endings. Leading
|
|
14
|
+
// indentation typical of =data block bodies is stripped from each line.
|
|
15
|
+
function parseCsv(text) {
|
|
16
|
+
const rows = [];
|
|
17
|
+
let field = '';
|
|
18
|
+
let row = [];
|
|
19
|
+
let inQuote = false;
|
|
20
|
+
let i = 0;
|
|
21
|
+
while (i < text.length) {
|
|
22
|
+
const c = text[i];
|
|
23
|
+
if (inQuote) {
|
|
24
|
+
if (c === '"' && text[i + 1] === '"') {
|
|
25
|
+
field += '"';
|
|
26
|
+
i += 2;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (c === '"') {
|
|
30
|
+
inQuote = false;
|
|
31
|
+
i++;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
field += c;
|
|
35
|
+
i++;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (c === '"' && field === '') {
|
|
39
|
+
inQuote = true;
|
|
40
|
+
i++;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (c === ',') {
|
|
44
|
+
row.push(field);
|
|
45
|
+
field = '';
|
|
46
|
+
i++;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (c === '\r') {
|
|
50
|
+
i++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (c === '\n') {
|
|
54
|
+
row.push(field);
|
|
55
|
+
rows.push(row);
|
|
56
|
+
row = [];
|
|
57
|
+
field = '';
|
|
58
|
+
i++;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
field += c;
|
|
62
|
+
i++;
|
|
63
|
+
}
|
|
64
|
+
if (field !== '' || row.length > 0) {
|
|
65
|
+
row.push(field);
|
|
66
|
+
rows.push(row);
|
|
67
|
+
}
|
|
68
|
+
// Drop rows that are entirely blank (empty content after trim).
|
|
69
|
+
return rows.filter(r => !(r.length === 1 && r[0].trim() === ''));
|
|
70
|
+
}
|
|
71
|
+
// Parse a TSV (tab-separated values) blob. Unlike CSV, TSV has no quoting
|
|
72
|
+
// mechanism — fields are split strictly on tabs and `"` is a literal
|
|
73
|
+
// character. Tabs and newlines inside fields are not representable in TSV.
|
|
74
|
+
function parseTsv(text) {
|
|
75
|
+
const lines = text.split(/\r?\n/);
|
|
76
|
+
const rows = lines.map(line => line.split('\t'));
|
|
77
|
+
return rows.filter(r => !(r.length === 1 && r[0].trim() === ''));
|
|
78
|
+
}
|
|
79
|
+
// Locate the first `=data` block with a matching `:key` attribute anywhere
|
|
80
|
+
// in the document tree.
|
|
81
|
+
function findDataBlockByKey(tree, key) {
|
|
82
|
+
let found = null;
|
|
83
|
+
const walk = node => {
|
|
84
|
+
if (found)
|
|
85
|
+
return;
|
|
86
|
+
if (Array.isArray(node)) {
|
|
87
|
+
node.forEach(walk);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (!node || typeof node !== 'object')
|
|
91
|
+
return;
|
|
92
|
+
if (node.type === 'block' && node.name === 'data') {
|
|
93
|
+
const attrs = makeAttrs(node, {});
|
|
94
|
+
if (attrs.getFirstValue('key') === key) {
|
|
95
|
+
found = node;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (Array.isArray(node.content))
|
|
100
|
+
node.content.forEach(walk);
|
|
101
|
+
};
|
|
102
|
+
walk(tree);
|
|
103
|
+
return found;
|
|
104
|
+
}
|
|
105
|
+
function extractDataText(dataNode) {
|
|
106
|
+
if (!dataNode || !Array.isArray(dataNode.content))
|
|
107
|
+
return '';
|
|
108
|
+
const verbatim = dataNode.content.find(c => c && c.type === 'verbatim');
|
|
109
|
+
return verbatim && typeof verbatim.value === 'string' ? verbatim.value : '';
|
|
110
|
+
}
|
|
111
|
+
// Detect whether the =table body is a single-line source reference like
|
|
112
|
+
// `data:key` or `file:path` (spec §1672). Returns the parsed reference or
|
|
113
|
+
// null if the body is ordinary table content.
|
|
114
|
+
function detectSourceReference(tableNode) {
|
|
115
|
+
const texts = [];
|
|
116
|
+
const walker = makeTransformer({
|
|
117
|
+
'row:text': r => {
|
|
118
|
+
texts.push(r.value);
|
|
119
|
+
return r;
|
|
120
|
+
},
|
|
121
|
+
'head:text': h => {
|
|
122
|
+
texts.push(h.value);
|
|
123
|
+
return h;
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
walker(tableNode, {});
|
|
127
|
+
const joined = texts.join('\n').trim();
|
|
128
|
+
const lines = joined
|
|
129
|
+
.split(/\r?\n/)
|
|
130
|
+
.map(l => l.trim())
|
|
131
|
+
.filter(l => l.length > 0);
|
|
132
|
+
if (lines.length !== 1)
|
|
133
|
+
return null;
|
|
134
|
+
const m = lines[0].match(/^(data|file):(\S+)$/);
|
|
135
|
+
if (!m)
|
|
136
|
+
return null;
|
|
137
|
+
return { scheme: m[1], target: m[2] };
|
|
138
|
+
}
|
|
139
|
+
function buildCellBlock(text) {
|
|
140
|
+
return {
|
|
141
|
+
name: 'cell',
|
|
142
|
+
type: 'block',
|
|
143
|
+
margin: '',
|
|
144
|
+
content: [{ type: 'text', value: text }],
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function buildRowBlock(cells, isHeader) {
|
|
148
|
+
const block = {
|
|
149
|
+
name: 'row',
|
|
150
|
+
type: 'block',
|
|
151
|
+
margin: '',
|
|
152
|
+
content: cells,
|
|
153
|
+
};
|
|
154
|
+
if (isHeader) {
|
|
155
|
+
block.config = [{ name: 'header', value: true, type: 'boolean' }];
|
|
156
|
+
}
|
|
157
|
+
return block;
|
|
158
|
+
}
|
|
159
|
+
// Convert parsed CSV/TSV rows to plain `=row`/`=cell` blocks. The spec is
|
|
160
|
+
// silent on how to mark a header row in `=table data:<key>` references, so
|
|
161
|
+
// no row receives `:header` here. Authors who need a header row can use a
|
|
162
|
+
// structured table with explicit `=begin row :header`, or a Markdown GFM
|
|
163
|
+
// table with a separator line.
|
|
164
|
+
function csvToTableContent(csvRows) {
|
|
165
|
+
return csvRows.map(row => {
|
|
166
|
+
const cells = row.map(v => buildCellBlock(v.trim()));
|
|
167
|
+
return buildRowBlock(cells, false);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// ─── Error recovery (design notes Rules 2-4) ───────────────────────────────
|
|
171
|
+
// Rule 2 — table-level cell count validation. Pad short rows with empty
|
|
172
|
+
// cells; truncate long rows. Emit a warning whenever a row is changed.
|
|
173
|
+
// Expected count is taken from the `:header` row if present, otherwise from
|
|
174
|
+
// the row with the maximum cell count.
|
|
175
|
+
//
|
|
176
|
+
// Skipped when any cell uses `:colspan` or `:rowspan`: a spanning cell
|
|
177
|
+
// occupies multiple columns, so naive cell counting would misreport row
|
|
178
|
+
// width and drop legitimate spanned cells.
|
|
179
|
+
function normalizeCellCounts(tableNode, source = 'table') {
|
|
180
|
+
if (!tableNode || !Array.isArray(tableNode.content))
|
|
181
|
+
return tableNode;
|
|
182
|
+
const rows = tableNode.content.filter(c => c && c.type === 'block' && c.name === 'row');
|
|
183
|
+
if (rows.length === 0)
|
|
184
|
+
return tableNode;
|
|
185
|
+
const cellsOf = row => (Array.isArray(row.content) ? row.content.filter(c => c && c.name === 'cell') : []);
|
|
186
|
+
const isHeaderRow = row => Array.isArray(row.config) && row.config.some(a => a.name === 'header' && a.value === true);
|
|
187
|
+
const cellHasSpan = cell => Array.isArray(cell.config) && cell.config.some(a => a.name === 'colspan' || a.name === 'rowspan');
|
|
188
|
+
const rowHasSpan = row => cellsOf(row).some(cellHasSpan);
|
|
189
|
+
if (rows.some(rowHasSpan))
|
|
190
|
+
return tableNode;
|
|
191
|
+
const headerRow = rows.find(isHeaderRow);
|
|
192
|
+
const expected = headerRow ? cellsOf(headerRow).length : Math.max(...rows.map(r => cellsOf(r).length));
|
|
193
|
+
if (expected === 0)
|
|
194
|
+
return tableNode;
|
|
195
|
+
let mutated = false;
|
|
196
|
+
const newContent = tableNode.content.map(child => {
|
|
197
|
+
if (!child || child.type !== 'block' || child.name !== 'row')
|
|
198
|
+
return child;
|
|
199
|
+
const cells = cellsOf(child);
|
|
200
|
+
if (cells.length === expected)
|
|
201
|
+
return child;
|
|
202
|
+
if (cells.length < expected) {
|
|
203
|
+
const padding = [];
|
|
204
|
+
for (let i = cells.length; i < expected; i++)
|
|
205
|
+
padding.push(buildCellBlock(''));
|
|
206
|
+
console.warn(`[${source}] row has ${cells.length} cells, expected ${expected} — padded with ${padding.length} empty`);
|
|
207
|
+
mutated = true;
|
|
208
|
+
return { ...child, content: [...child.content, ...padding] };
|
|
209
|
+
}
|
|
210
|
+
// cells.length > expected → truncate
|
|
211
|
+
const dropped = cells.length - expected;
|
|
212
|
+
console.warn(`[${source}] row has ${cells.length} cells, expected ${expected} — truncated ${dropped}`);
|
|
213
|
+
mutated = true;
|
|
214
|
+
// Keep non-cell entries (e.g. blanklines) and the first `expected` cells
|
|
215
|
+
let keepCells = expected;
|
|
216
|
+
const trimmed = [];
|
|
217
|
+
for (const c of child.content || []) {
|
|
218
|
+
if (c && c.type === 'block' && c.name === 'cell') {
|
|
219
|
+
if (keepCells > 0) {
|
|
220
|
+
trimmed.push(c);
|
|
221
|
+
keepCells--;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
trimmed.push(c);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return { ...child, content: trimmed };
|
|
229
|
+
});
|
|
230
|
+
return mutated ? { ...tableNode, content: newContent } : tableNode;
|
|
231
|
+
}
|
|
232
|
+
// Rule 3 — mixed separator detection (text-mode only). Inspects each line
|
|
233
|
+
// for visible separators (`|` / `+`) surrounded by whitespace; lines without
|
|
234
|
+
// any visible separator fall back to whitespace separation. Warns when more
|
|
235
|
+
// than one separator type is observed within a single table.
|
|
236
|
+
function detectMixedSeparators(lines) {
|
|
237
|
+
const seen = new Set();
|
|
238
|
+
for (const line of lines) {
|
|
239
|
+
if (!line || typeof line !== 'string')
|
|
240
|
+
continue;
|
|
241
|
+
if (/\s\|\s/.test(line))
|
|
242
|
+
seen.add('pipe');
|
|
243
|
+
else if (/\s\+\s/.test(line))
|
|
244
|
+
seen.add('plus');
|
|
245
|
+
else if (line.trim().length > 0)
|
|
246
|
+
seen.add('whitespace');
|
|
247
|
+
if (seen.size > 1)
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
if (seen.size > 1) {
|
|
251
|
+
console.warn(`[table] mixed separator types detected: ${Array.from(seen).join(', ')} — recommend a single style`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Rule 4 — replace =table with =code block. Used when a referenced =data
|
|
255
|
+
// source has a non-CSV mime type: the spec mandates the source be rendered
|
|
256
|
+
// as a code block so the user can still see the underlying content.
|
|
257
|
+
function buildCodeFromDataBlock(tableNode, dataBlock) {
|
|
258
|
+
return {
|
|
259
|
+
type: 'block',
|
|
260
|
+
name: 'code',
|
|
261
|
+
margin: tableNode.margin || '',
|
|
262
|
+
content: Array.isArray(dataBlock.content) ? dataBlock.content : [],
|
|
263
|
+
config: Array.isArray(tableNode.config) ? tableNode.config : [],
|
|
264
|
+
};
|
|
265
|
+
}
|
|
10
266
|
/**
|
|
11
267
|
* Helpers section
|
|
12
268
|
*/
|
|
13
|
-
//
|
|
269
|
+
// Bit-mask helper used by the positional column extractor for multi-line
|
|
270
|
+
// rows. Compares two same-length digit strings character by character via
|
|
271
|
+
// `cb`, returning the joined result.
|
|
14
272
|
const strbin = (str1, str2, cb) => {
|
|
15
273
|
let res = [];
|
|
16
274
|
for (let i = 0; str1.length > i; i++) {
|
|
@@ -18,68 +276,170 @@ const strbin = (str1, str2, cb) => {
|
|
|
18
276
|
}
|
|
19
277
|
return res.join('');
|
|
20
278
|
};
|
|
21
|
-
//
|
|
279
|
+
// Build a unified column-position mask across a set of lines. Used as a
|
|
280
|
+
// fallback when a row spans multiple lines (continuation lines that align
|
|
281
|
+
// content by character position rather than by separator). Returns a binary
|
|
282
|
+
// string where `0` runs mark column ranges and `1` runs mark gaps.
|
|
22
283
|
const makeMask = (lines, separators) => {
|
|
23
|
-
// calculate template length
|
|
24
284
|
const tmplLength = Math.max(...[...lines, ...separators].map(s => s.length));
|
|
25
|
-
// make bin mask for each string
|
|
26
285
|
const masks = lines.map(str => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
'0000000011111111111110001111111111111000001111111111111111111111111111' ]
|
|
30
|
-
then not(mask) ... then & masks
|
|
31
|
-
*/
|
|
32
|
-
// enlarge string to tmplLength
|
|
33
|
-
let tstr = str + ' '.repeat(tmplLength - str.length);
|
|
34
|
-
let mask = [];
|
|
286
|
+
const tstr = str + ' '.repeat(tmplLength - str.length);
|
|
287
|
+
const mask = [];
|
|
35
288
|
const re = /\s+[+|\s]\s/g;
|
|
36
289
|
let match;
|
|
37
290
|
while ((match = re.exec(tstr)) != null) {
|
|
38
291
|
const tmpMask = '1'.repeat(match.index) + '0'.repeat(match[0].length);
|
|
39
292
|
mask.push(tmpMask + '1'.repeat(tmplLength - tmpMask.length));
|
|
40
293
|
}
|
|
41
|
-
return mask.reduce((a, b) =>
|
|
42
|
-
return strbin(a, b, (i1, i2) => i1 & i2);
|
|
43
|
-
}, '1'.repeat(tmplLength));
|
|
294
|
+
return mask.reduce((a, b) => strbin(a, b, (i1, i2) => i1 & i2), '1'.repeat(tmplLength));
|
|
44
295
|
});
|
|
45
|
-
// make result mask
|
|
46
296
|
const inverted = masks.map(m => strbin(m, '', i1 => (i1 == 0 ? 1 : 0)));
|
|
47
|
-
|
|
48
|
-
return strbin(a, b, (i1, i2) => i1 & i2);
|
|
49
|
-
}, '1'.repeat(tmplLength));
|
|
50
|
-
return columnTemplate;
|
|
297
|
+
return inverted.reduce((a, b) => strbin(a, b, (i1, i2) => i1 & i2), '1'.repeat(tmplLength));
|
|
51
298
|
};
|
|
299
|
+
// Apply a column-position template to a multi-line text block, extracting
|
|
300
|
+
// per-column substrings and aggregating across lines (continuation lines
|
|
301
|
+
// append to the cell at the same column position).
|
|
52
302
|
const extractColumnsByTemplate = (text, template) => {
|
|
53
|
-
const lines = flattenDeep(text
|
|
54
|
-
.split(/\n/) // split each row by eol
|
|
55
|
-
.filter(str => str.length > 0));
|
|
303
|
+
const lines = flattenDeep(text.split(/\n/).filter(s => s.length > 0));
|
|
56
304
|
const cols = lines.map(line => {
|
|
57
305
|
const re = /((1+|0+))/g;
|
|
58
|
-
|
|
306
|
+
const columns = [];
|
|
59
307
|
let match;
|
|
60
308
|
while ((match = re.exec(template)) != null) {
|
|
61
|
-
if (match[0][0] == 1)
|
|
309
|
+
if (match[0][0] == '1')
|
|
62
310
|
continue;
|
|
63
|
-
|
|
64
|
-
columns.push(s);
|
|
311
|
+
columns.push(line.substring(match.index, match.index + match[0].length));
|
|
65
312
|
}
|
|
66
313
|
return columns;
|
|
67
314
|
});
|
|
68
|
-
|
|
69
|
-
|
|
315
|
+
const result = [];
|
|
316
|
+
cols.reduce((a, b) => {
|
|
70
317
|
for (let i = 0; i < b.length; i++) {
|
|
71
318
|
a[i] = (a[i] === undefined ? '' : a[i]) + ' ' + b[i];
|
|
72
319
|
}
|
|
73
320
|
return a;
|
|
74
|
-
},
|
|
321
|
+
}, result);
|
|
75
322
|
return result;
|
|
76
323
|
};
|
|
324
|
+
const detectLineSeparator = (line) => {
|
|
325
|
+
if (/(?:^|\s)\|(?:\s|$)/.test(line))
|
|
326
|
+
return 'pipe';
|
|
327
|
+
if (/(?:^|\s)\+(?:\s|$)/.test(line))
|
|
328
|
+
return 'plus';
|
|
329
|
+
return 'whitespace';
|
|
330
|
+
};
|
|
331
|
+
const trimEdgeEmpty = (cells) => {
|
|
332
|
+
// A leading `|` produces an empty cell at index 0; a trailing `|` does the
|
|
333
|
+
// same at the end. Drop those edge artifacts. Empty cells in the middle of
|
|
334
|
+
// the row are preserved.
|
|
335
|
+
let start = 0;
|
|
336
|
+
let end = cells.length;
|
|
337
|
+
if (cells[start] === '')
|
|
338
|
+
start++;
|
|
339
|
+
if (end > start && cells[end - 1] === '')
|
|
340
|
+
end--;
|
|
341
|
+
return cells.slice(start, end);
|
|
342
|
+
};
|
|
343
|
+
const splitLineByPipe = (line) => trimEdgeEmpty(line.split(/\s*\|\s*/).map(c => c.trim()));
|
|
344
|
+
const splitLineByPlus = (line) => trimEdgeEmpty(line.split(/\s*\+\s*/).map(c => c.trim()));
|
|
345
|
+
const splitLineByWhitespace = (line) => line
|
|
346
|
+
.trim()
|
|
347
|
+
.split(/\s{2,}/)
|
|
348
|
+
.filter(c => c !== '');
|
|
349
|
+
const splitLineCells = (line) => {
|
|
350
|
+
const trimmed = line.trim();
|
|
351
|
+
if (trimmed === '')
|
|
352
|
+
return [];
|
|
353
|
+
const kind = detectLineSeparator(line);
|
|
354
|
+
if (kind === 'pipe')
|
|
355
|
+
return splitLineByPipe(line);
|
|
356
|
+
if (kind === 'plus')
|
|
357
|
+
return splitLineByPlus(line);
|
|
358
|
+
return splitLineByWhitespace(line);
|
|
359
|
+
};
|
|
360
|
+
// Convert a row's raw text (which may span multiple lines) to cell values.
|
|
361
|
+
// Multi-line rows: take the separator kind from the first non-blank line and
|
|
362
|
+
// apply it to every line of the row, then aggregate column-wise so that a
|
|
363
|
+
// continuation line (e.g. wrapped cell content) appends to the cell from the
|
|
364
|
+
// previous line in the same column.
|
|
365
|
+
const rowToCells = (rowValue) => {
|
|
366
|
+
const lines = rowValue.split(/\r?\n/).filter(l => l.trim() !== '');
|
|
367
|
+
if (lines.length === 0)
|
|
368
|
+
return [];
|
|
369
|
+
if (lines.length === 1)
|
|
370
|
+
return splitLineCells(lines[0]);
|
|
371
|
+
const kind = detectLineSeparator(lines[0]);
|
|
372
|
+
const splitFn = kind === 'pipe' ? splitLineByPipe : kind === 'plus' ? splitLineByPlus : splitLineByWhitespace;
|
|
373
|
+
const lineCells = lines.map(splitFn);
|
|
374
|
+
const maxCols = Math.max(...lineCells.map(c => c.length));
|
|
375
|
+
const merged = [];
|
|
376
|
+
for (let i = 0; i < maxCols; i++) {
|
|
377
|
+
const parts = lineCells.map(line => line[i] ?? '').filter(p => p !== '');
|
|
378
|
+
merged.push(parts.join(' '));
|
|
379
|
+
}
|
|
380
|
+
return merged;
|
|
381
|
+
};
|
|
77
382
|
/**
|
|
78
383
|
* Main transforms
|
|
79
384
|
*/
|
|
385
|
+
const wrapImplicitCells = rowNode => {
|
|
386
|
+
if (!Array.isArray(rowNode.content) || rowNode.content.length === 0)
|
|
387
|
+
return rowNode;
|
|
388
|
+
const hasNonCell = rowNode.content.some(c => c && c.type === 'block' && c.name !== 'cell' && c.name !== 'blankline');
|
|
389
|
+
if (!hasNonCell)
|
|
390
|
+
return rowNode;
|
|
391
|
+
const wrapped = rowNode.content.map(c => {
|
|
392
|
+
if (!c || c.type !== 'block')
|
|
393
|
+
return c;
|
|
394
|
+
if (c.name === 'cell' || c.name === 'blankline')
|
|
395
|
+
return c;
|
|
396
|
+
return { type: 'block', name: 'cell', content: [c], margin: c.margin || '' };
|
|
397
|
+
});
|
|
398
|
+
return { ...rowNode, content: wrapped };
|
|
399
|
+
};
|
|
400
|
+
const isStructured = tableNode => Array.isArray(tableNode.content) &&
|
|
401
|
+
tableNode.content.some(c => c && c.type === 'block' && (c.name === 'row' || c.name === 'cell'));
|
|
80
402
|
export default () => tree => {
|
|
81
403
|
const transformer = makeTransformer({
|
|
82
404
|
table: node => {
|
|
405
|
+
// CSV/data source reference (spec §1672):
|
|
406
|
+
// =table data:<key> → resolve =data block with :key<key>
|
|
407
|
+
// =table file:<path> → defer to host reader (not implemented here)
|
|
408
|
+
const ref = detectSourceReference(node);
|
|
409
|
+
if (ref && ref.scheme === 'data') {
|
|
410
|
+
const dataBlock = findDataBlockByKey(tree, ref.target);
|
|
411
|
+
if (!dataBlock) {
|
|
412
|
+
// Rule 4: source not found → empty table (still a =table block)
|
|
413
|
+
console.warn(`[table] no =data block found for data:${ref.target} — rendered as empty`);
|
|
414
|
+
return { ...node, content: [] };
|
|
415
|
+
}
|
|
416
|
+
const mimeType = makeAttrs(dataBlock, {}).getFirstValue('mime-type');
|
|
417
|
+
const isCsv = mimeType === 'text/csv';
|
|
418
|
+
const isTsv = mimeType === 'text/tab-separated-values';
|
|
419
|
+
if (isCsv || isTsv) {
|
|
420
|
+
const text = extractDataText(dataBlock);
|
|
421
|
+
const rows = isCsv ? parseCsv(text) : parseTsv(text);
|
|
422
|
+
if (rows.length === 0) {
|
|
423
|
+
console.warn(`[table] ${isCsv ? 'CSV' : 'TSV'} parse produced no rows for data:${ref.target} — rendered as empty`);
|
|
424
|
+
return { ...node, content: [] };
|
|
425
|
+
}
|
|
426
|
+
const filledNode = { ...node, content: csvToTableContent(rows) };
|
|
427
|
+
return normalizeCellCounts(filledNode, `table data:${ref.target}`);
|
|
428
|
+
}
|
|
429
|
+
// Rule 4: source not tabular → render as code block so content remains visible
|
|
430
|
+
console.warn(`[table] =data :key<${ref.target}> has non-tabular mime-type ${mimeType || '(none)'} — rendered as =code`);
|
|
431
|
+
return buildCodeFromDataBlock(node, dataBlock);
|
|
432
|
+
}
|
|
433
|
+
// structured mode: transform row children (wrap implicit cells), then
|
|
434
|
+
// apply Rule 2 cell count normalization.
|
|
435
|
+
if (isStructured(node)) {
|
|
436
|
+
const transformedContent = (node.content || []).map(c => {
|
|
437
|
+
if (c && c.name === 'row')
|
|
438
|
+
return wrapImplicitCells(c);
|
|
439
|
+
return c;
|
|
440
|
+
});
|
|
441
|
+
return normalizeCellCounts({ ...node, content: transformedContent }, 'table');
|
|
442
|
+
}
|
|
83
443
|
let rows = [];
|
|
84
444
|
const collectValues = row => {
|
|
85
445
|
rows.push(row.value);
|
|
@@ -103,6 +463,8 @@ export default () => tree => {
|
|
|
103
463
|
// split each row into lines
|
|
104
464
|
const lines = flattenDeep(rows.map(splitToLines));
|
|
105
465
|
const separators = flattenDeep(seps.map(splitToLines));
|
|
466
|
+
// Rule 3: warn on mixed separator types within a single table
|
|
467
|
+
detectMixedSeparators(lines);
|
|
106
468
|
// collect text rows
|
|
107
469
|
let textRows = [];
|
|
108
470
|
makeTransformer({
|
|
@@ -110,30 +472,48 @@ export default () => tree => {
|
|
|
110
472
|
textRows.push(row.value);
|
|
111
473
|
},
|
|
112
474
|
})(node);
|
|
475
|
+
const makeBlock = (name, content, extra = {}) => {
|
|
476
|
+
return { ...extra, name, type: 'block', content: Array.isArray(content) ? content : [content] };
|
|
477
|
+
};
|
|
478
|
+
const makeRow = cells => makeBlock('row', cells);
|
|
479
|
+
const makeHeaderRow = cells => makeBlock('row', cells, { config: [{ name: 'header', value: true, type: 'boolean' }] });
|
|
480
|
+
const makeCell = text => makeBlock('cell', { type: 'text', value: text });
|
|
481
|
+
// Routing: per-line separator detection (Rule 1) is used only when a
|
|
482
|
+
// line with a visible separator (`|` or `+`) coexists with one that
|
|
483
|
+
// has only whitespace separation — the Scenario 2 case where the
|
|
484
|
+
// legacy positional mask collapses columns. Tables with a uniform
|
|
485
|
+
// separator, or with both visible kinds (pipe + plus) handled by the
|
|
486
|
+
// shared mask, fall back to the legacy positional template, which
|
|
487
|
+
// preserves continuation-line alignment in multi-line rows and keeps
|
|
488
|
+
// byte-for-byte AST/HTML output stable.
|
|
113
489
|
const columnTemplate = makeMask(lines, separators);
|
|
114
|
-
const
|
|
115
|
-
|
|
490
|
+
const seenSeparatorKinds = new Set(lines.map(detectLineSeparator));
|
|
491
|
+
const hasVisible = seenSeparatorKinds.has('pipe') || seenSeparatorKinds.has('plus');
|
|
492
|
+
const hasWhitespace = seenSeparatorKinds.has('whitespace');
|
|
493
|
+
const useMixedSplitting = hasVisible && hasWhitespace;
|
|
494
|
+
const splitToCells = (rowValue) => {
|
|
495
|
+
if (useMixedSplitting) {
|
|
496
|
+
const rowLines = rowValue.split(/\r?\n/).filter(l => l.trim() !== '');
|
|
497
|
+
if (rowLines.length <= 1)
|
|
498
|
+
return rowToCells(rowValue);
|
|
499
|
+
}
|
|
500
|
+
return extractColumnsByTemplate(rowValue, columnTemplate);
|
|
116
501
|
};
|
|
117
|
-
// make columns
|
|
118
502
|
const res = makeTransformer({
|
|
119
503
|
'row:text': row => {
|
|
120
504
|
if (textRows.length == 1) {
|
|
121
|
-
//
|
|
505
|
+
// No separator blocks: each line of the only text row becomes its own row
|
|
122
506
|
const textRowsLines = flattenDeep([row.value].map(splitToLines));
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
507
|
+
if (useMixedSplitting) {
|
|
508
|
+
return textRowsLines.map(line => makeRow(splitLineCells(line).map(makeCell)));
|
|
509
|
+
}
|
|
510
|
+
return textRowsLines.map(line => makeRow(extractColumnsByTemplate(line, columnTemplate).map(makeCell)));
|
|
127
511
|
}
|
|
128
|
-
|
|
129
|
-
return makeBlock('table_row', res.map(col => makeBlock('table_cell', { type: 'text', value: col })));
|
|
130
|
-
},
|
|
131
|
-
'head:text': head => {
|
|
132
|
-
const res = extractColumnsByTemplate(head.value, columnTemplate);
|
|
133
|
-
return makeBlock('table_head', res.map(col => makeBlock('table_cell', { type: 'text', value: col })));
|
|
512
|
+
return makeRow(splitToCells(row.value).map(makeCell));
|
|
134
513
|
},
|
|
514
|
+
'head:text': head => makeHeaderRow(splitToCells(head.value).map(makeCell)),
|
|
135
515
|
})(node);
|
|
136
|
-
return res;
|
|
516
|
+
return normalizeCellCounts(res, 'table');
|
|
137
517
|
},
|
|
138
518
|
});
|
|
139
519
|
return transformer(tree, {});
|