@podlite/schema 0.0.40 → 0.0.42
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 +17 -0
- package/esm/blocks-helpers.d.ts +5 -0
- package/esm/blocks-helpers.js +3 -0
- package/esm/blocks-helpers.js.map +1 -1
- package/esm/exportAny.d.ts +1 -0
- package/esm/exportAny.js +1 -1
- package/esm/exportAny.js.map +1 -1
- package/esm/exportHtml.js +47 -5
- package/esm/exportHtml.js.map +1 -1
- package/esm/exportMarkdown.js +37 -10
- package/esm/exportMarkdown.js.map +1 -1
- package/esm/grammar.js +603 -497
- package/esm/grammar.js.map +1 -1
- package/esm/grammarfc.js +418 -406
- package/esm/grammarfc.js.map +1 -1
- package/esm/helpers/handlers.d.ts +11 -0
- package/esm/helpers/handlers.js +25 -0
- package/esm/helpers/handlers.js.map +1 -1
- package/esm/helpers/makeInterator.js +2 -4
- package/esm/helpers/makeInterator.js.map +1 -1
- package/esm/helpers/parseAttributes.d.ts +2 -0
- package/esm/helpers/parseAttributes.js +12 -0
- package/esm/helpers/parseAttributes.js.map +1 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js +3 -0
- package/esm/index.js.map +1 -1
- package/esm/plugin-data-table.d.ts +2 -0
- package/esm/plugin-data-table.js +152 -0
- package/esm/plugin-data-table.js.map +1 -0
- package/esm/plugin-formatting-codes.js +10 -23
- package/esm/plugin-formatting-codes.js.map +1 -1
- package/esm/plugin-tables.d.ts +10 -0
- package/esm/plugin-tables.js +7 -7
- package/esm/plugin-tables.js.map +1 -1
- package/esm/selectors.d.ts +17 -1
- package/esm/selectors.js +294 -18
- package/esm/selectors.js.map +1 -1
- package/lib/blocks-helpers.d.ts +5 -0
- package/lib/blocks-helpers.js +5 -1
- package/lib/exportAny.d.ts +1 -0
- package/lib/exportAny.js +1 -1
- package/lib/exportHtml.js +46 -4
- package/lib/exportMarkdown.js +36 -9
- package/lib/grammar.js +603 -497
- package/lib/grammarfc.js +418 -406
- package/lib/helpers/handlers.d.ts +11 -0
- package/lib/helpers/handlers.js +28 -1
- package/lib/helpers/makeInterator.js +2 -4
- package/lib/helpers/parseAttributes.d.ts +2 -0
- package/lib/helpers/parseAttributes.js +35 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +5 -1
- package/lib/plugin-data-table.d.ts +2 -0
- package/lib/plugin-data-table.js +157 -0
- package/lib/plugin-formatting-codes.js +10 -23
- package/lib/plugin-tables.d.ts +10 -0
- package/lib/plugin-tables.js +8 -0
- package/lib/selectors.d.ts +17 -1
- package/lib/selectors.js +294 -18
- package/package.json +2 -2
package/lib/selectors.js
CHANGED
|
@@ -2,36 +2,305 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runSelector = exports.parseSelector = void 0;
|
|
4
4
|
const index_1 = require("./index");
|
|
5
|
+
// --- predicate parser ---------------------------------------------------
|
|
6
|
+
const isIdentStart = (c) => /[a-zA-Z_]/.test(c);
|
|
7
|
+
const isIdentCont = (c) => /[a-zA-Z0-9_-]/.test(c);
|
|
8
|
+
// Read an angle-bracketed value starting at `<`. Returns the inner content
|
|
9
|
+
// (without delimiters) and the index right after the closing `>`.
|
|
10
|
+
// Nested `<>` pairs are matched recursively.
|
|
11
|
+
const readAngleValue = (s, start) => {
|
|
12
|
+
if (s[start] !== '<')
|
|
13
|
+
return undefined;
|
|
14
|
+
let depth = 1;
|
|
15
|
+
let i = start + 1;
|
|
16
|
+
while (i < s.length && depth > 0) {
|
|
17
|
+
if (s[i] === '<')
|
|
18
|
+
depth++;
|
|
19
|
+
else if (s[i] === '>') {
|
|
20
|
+
depth--;
|
|
21
|
+
if (depth === 0)
|
|
22
|
+
return { value: s.slice(start + 1, i), end: i + 1 };
|
|
23
|
+
}
|
|
24
|
+
i++;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
};
|
|
28
|
+
const parseCondition = (raw) => {
|
|
29
|
+
const s = raw.trim();
|
|
30
|
+
if (!s.startsWith(':'))
|
|
31
|
+
return undefined;
|
|
32
|
+
let i = 1;
|
|
33
|
+
let modifier;
|
|
34
|
+
if (s.startsWith('!?', i)) {
|
|
35
|
+
modifier = '!?';
|
|
36
|
+
i += 2;
|
|
37
|
+
}
|
|
38
|
+
else if (s[i] === '!') {
|
|
39
|
+
modifier = '!';
|
|
40
|
+
i += 1;
|
|
41
|
+
}
|
|
42
|
+
else if (s[i] === '?') {
|
|
43
|
+
modifier = '?';
|
|
44
|
+
i += 1;
|
|
45
|
+
}
|
|
46
|
+
if (i >= s.length || !isIdentStart(s[i]))
|
|
47
|
+
return undefined;
|
|
48
|
+
const nameStart = i;
|
|
49
|
+
while (i < s.length && isIdentCont(s[i]))
|
|
50
|
+
i++;
|
|
51
|
+
const attrName = s.slice(nameStart, i);
|
|
52
|
+
if (i >= s.length)
|
|
53
|
+
return { modifier, attrName };
|
|
54
|
+
if (s[i] === '~' && s[i + 1] === '<') {
|
|
55
|
+
const read = readAngleValue(s, i + 1);
|
|
56
|
+
if (!read || read.end !== s.length)
|
|
57
|
+
return undefined;
|
|
58
|
+
return { modifier, attrName, valueSpec: { kind: 'contains', value: read.value } };
|
|
59
|
+
}
|
|
60
|
+
if (s[i] === '<') {
|
|
61
|
+
const read = readAngleValue(s, i);
|
|
62
|
+
if (!read || read.end !== s.length)
|
|
63
|
+
return undefined;
|
|
64
|
+
return { modifier, attrName, valueSpec: { kind: 'angle', value: read.value } };
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
};
|
|
68
|
+
// Split predicate body by whitespace at top level, respecting <...> nesting.
|
|
69
|
+
const splitConditions = (body) => {
|
|
70
|
+
const chunks = [];
|
|
71
|
+
let depth = 0;
|
|
72
|
+
let buf = '';
|
|
73
|
+
for (let i = 0; i < body.length; i++) {
|
|
74
|
+
const c = body[i];
|
|
75
|
+
if (c === '<')
|
|
76
|
+
depth++;
|
|
77
|
+
else if (c === '>') {
|
|
78
|
+
if (depth === 0)
|
|
79
|
+
return undefined;
|
|
80
|
+
depth--;
|
|
81
|
+
}
|
|
82
|
+
if (depth === 0 && /\s/.test(c)) {
|
|
83
|
+
if (buf) {
|
|
84
|
+
chunks.push(buf);
|
|
85
|
+
buf = '';
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
buf += c;
|
|
90
|
+
}
|
|
91
|
+
if (depth !== 0)
|
|
92
|
+
return undefined;
|
|
93
|
+
if (buf)
|
|
94
|
+
chunks.push(buf);
|
|
95
|
+
return chunks;
|
|
96
|
+
};
|
|
97
|
+
const parsePredicate = (body) => {
|
|
98
|
+
const chunks = splitConditions(body);
|
|
99
|
+
if (!chunks)
|
|
100
|
+
return undefined;
|
|
101
|
+
const conditions = [];
|
|
102
|
+
for (const chunk of chunks) {
|
|
103
|
+
const cond = parseCondition(chunk);
|
|
104
|
+
if (!cond)
|
|
105
|
+
return undefined;
|
|
106
|
+
conditions.push(cond);
|
|
107
|
+
}
|
|
108
|
+
return conditions;
|
|
109
|
+
};
|
|
110
|
+
const parsePattern = (raw) => {
|
|
111
|
+
const s = raw.trim();
|
|
112
|
+
if (!s)
|
|
113
|
+
return undefined;
|
|
114
|
+
let blockType;
|
|
115
|
+
let i = 0;
|
|
116
|
+
if (s[0] === '*') {
|
|
117
|
+
blockType = '*';
|
|
118
|
+
i = 1;
|
|
119
|
+
}
|
|
120
|
+
else if (isIdentStart(s[0])) {
|
|
121
|
+
let j = 1;
|
|
122
|
+
while (j < s.length && isIdentCont(s[j]))
|
|
123
|
+
j++;
|
|
124
|
+
blockType = s.slice(0, j);
|
|
125
|
+
i = j;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
// skip optional whitespace between block-type and predicate
|
|
131
|
+
while (i < s.length && /\s/.test(s[i]))
|
|
132
|
+
i++;
|
|
133
|
+
if (i >= s.length)
|
|
134
|
+
return { blockType };
|
|
135
|
+
if (s[i] !== '[' || s[s.length - 1] !== ']')
|
|
136
|
+
return undefined;
|
|
137
|
+
const body = s.slice(i + 1, s.length - 1).trim();
|
|
138
|
+
if (!body)
|
|
139
|
+
return undefined;
|
|
140
|
+
const predicate = parsePredicate(body);
|
|
141
|
+
if (!predicate)
|
|
142
|
+
return undefined;
|
|
143
|
+
return { blockType, predicate };
|
|
144
|
+
};
|
|
145
|
+
// Split pattern-list by comma at top level, respecting [...] and <...> nesting.
|
|
146
|
+
const splitPatterns = (s) => {
|
|
147
|
+
const chunks = [];
|
|
148
|
+
let bracketDepth = 0;
|
|
149
|
+
let angleDepth = 0;
|
|
150
|
+
let buf = '';
|
|
151
|
+
for (let i = 0; i < s.length; i++) {
|
|
152
|
+
const c = s[i];
|
|
153
|
+
if (c === '[')
|
|
154
|
+
bracketDepth++;
|
|
155
|
+
else if (c === ']') {
|
|
156
|
+
if (bracketDepth === 0)
|
|
157
|
+
return undefined;
|
|
158
|
+
bracketDepth--;
|
|
159
|
+
}
|
|
160
|
+
else if (c === '<')
|
|
161
|
+
angleDepth++;
|
|
162
|
+
else if (c === '>') {
|
|
163
|
+
if (angleDepth === 0)
|
|
164
|
+
return undefined;
|
|
165
|
+
angleDepth--;
|
|
166
|
+
}
|
|
167
|
+
if (c === ',' && bracketDepth === 0 && angleDepth === 0) {
|
|
168
|
+
chunks.push(buf);
|
|
169
|
+
buf = '';
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
buf += c;
|
|
173
|
+
}
|
|
174
|
+
if (bracketDepth !== 0 || angleDepth !== 0)
|
|
175
|
+
return undefined;
|
|
176
|
+
chunks.push(buf);
|
|
177
|
+
return chunks;
|
|
178
|
+
};
|
|
179
|
+
const parsePatternList = (filterPart) => {
|
|
180
|
+
if (!filterPart)
|
|
181
|
+
return [];
|
|
182
|
+
const chunks = splitPatterns(filterPart);
|
|
183
|
+
if (!chunks)
|
|
184
|
+
return undefined;
|
|
185
|
+
const patterns = [];
|
|
186
|
+
for (const chunk of chunks) {
|
|
187
|
+
const trimmed = chunk.trim();
|
|
188
|
+
if (!trimmed)
|
|
189
|
+
continue;
|
|
190
|
+
const p = parsePattern(trimmed);
|
|
191
|
+
if (!p)
|
|
192
|
+
return undefined;
|
|
193
|
+
patterns.push(p);
|
|
194
|
+
}
|
|
195
|
+
return patterns;
|
|
196
|
+
};
|
|
5
197
|
const parseSelector = (selector) => {
|
|
6
198
|
const trimmed = selector.trim();
|
|
7
199
|
if (!trimmed)
|
|
8
200
|
return undefined;
|
|
9
|
-
// Split on the first '|' — left is source, right is
|
|
201
|
+
// Split on the first '|' — left is source, right is pattern-list
|
|
10
202
|
const pipeIdx = trimmed.indexOf('|');
|
|
11
203
|
const sourcePart = (pipeIdx === -1 ? trimmed : trimmed.slice(0, pipeIdx)).trim();
|
|
12
204
|
const filterPart = pipeIdx === -1 ? '' : trimmed.slice(pipeIdx + 1).trim();
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// Source: scheme:path or scheme:path#anchor
|
|
20
|
-
const sourceMatch = sourcePart.match(/^([^:]+):([^#]+)(?:#(.+))?$/);
|
|
205
|
+
const patterns = parsePatternList(filterPart);
|
|
206
|
+
if (patterns === undefined)
|
|
207
|
+
return undefined;
|
|
208
|
+
// Source: scheme:path or scheme:path#anchor. Scheme is identifier-shaped
|
|
209
|
+
// so that bare predicates like `*[:attr<v>]` don't get consumed as scheme.
|
|
210
|
+
const sourceMatch = sourcePart.match(/^([a-zA-Z][a-zA-Z0-9-]*):([^#]+)(?:#(.+))?$/);
|
|
21
211
|
if (sourceMatch) {
|
|
22
212
|
return {
|
|
23
213
|
scheme: sourceMatch[1],
|
|
24
214
|
document: sourceMatch[2].trim(),
|
|
25
215
|
anchor: sourceMatch[3],
|
|
26
|
-
|
|
216
|
+
patterns,
|
|
27
217
|
};
|
|
28
218
|
}
|
|
29
|
-
if (
|
|
30
|
-
return {
|
|
219
|
+
if (patterns.length > 0) {
|
|
220
|
+
return { patterns };
|
|
221
|
+
}
|
|
222
|
+
// No pipe — treat the whole input as a pattern-list (CLI-friendly shorthand
|
|
223
|
+
// for filter-only selectors, e.g. `head1, code[:lang<python>]`).
|
|
224
|
+
if (pipeIdx === -1) {
|
|
225
|
+
const fallback = parsePatternList(sourcePart);
|
|
226
|
+
if (fallback && fallback.length > 0)
|
|
227
|
+
return { patterns: fallback };
|
|
31
228
|
}
|
|
32
229
|
return undefined;
|
|
33
230
|
};
|
|
34
231
|
exports.parseSelector = parseSelector;
|
|
232
|
+
// --- predicate matcher --------------------------------------------------
|
|
233
|
+
const matchCondition = (node, cond) => {
|
|
234
|
+
const attrs = (0, index_1.makeAttrs)(node, {});
|
|
235
|
+
const exists = attrs.exists(cond.attrName);
|
|
236
|
+
if (!cond.valueSpec) {
|
|
237
|
+
switch (cond.modifier) {
|
|
238
|
+
case undefined:
|
|
239
|
+
return exists && Boolean(attrs.getFirstValue(cond.attrName));
|
|
240
|
+
case '!':
|
|
241
|
+
return exists && attrs.getFirstValue(cond.attrName) === false;
|
|
242
|
+
case '?':
|
|
243
|
+
return exists;
|
|
244
|
+
case '!?':
|
|
245
|
+
return !exists;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (cond.valueSpec.kind === 'angle') {
|
|
249
|
+
if (!exists)
|
|
250
|
+
return false;
|
|
251
|
+
const equal = String(attrs.getFirstValue(cond.attrName)) === cond.valueSpec.value;
|
|
252
|
+
return cond.modifier === '!' ? !equal : equal;
|
|
253
|
+
}
|
|
254
|
+
if (cond.valueSpec.kind === 'contains') {
|
|
255
|
+
if (!exists)
|
|
256
|
+
return false;
|
|
257
|
+
// Author-facing semantics: `:tags<a b c>` is a list of three elements.
|
|
258
|
+
// Grammar parses unquoted identifier sequences as a single string, so
|
|
259
|
+
// split string values on whitespace/comma to recover list shape.
|
|
260
|
+
const tokens = [];
|
|
261
|
+
for (const v of attrs.getAllValues(cond.attrName)) {
|
|
262
|
+
if (typeof v === 'string') {
|
|
263
|
+
for (const t of v.split(/[\s,]+/))
|
|
264
|
+
if (t)
|
|
265
|
+
tokens.push(t);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
tokens.push(String(v));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const present = tokens.includes(cond.valueSpec.value);
|
|
272
|
+
return cond.modifier === '!' ? !present : present;
|
|
273
|
+
}
|
|
274
|
+
return false;
|
|
275
|
+
};
|
|
276
|
+
// Replicate name/level handling from getFromTree for backward compat with
|
|
277
|
+
// 'head1' / 'item' style block-types.
|
|
278
|
+
const blockTypeMatches = (node, blockType) => {
|
|
279
|
+
if (blockType === '*')
|
|
280
|
+
return true;
|
|
281
|
+
const anyNode = node;
|
|
282
|
+
if (anyNode.name === blockType)
|
|
283
|
+
return true;
|
|
284
|
+
const m = blockType.match(/^(head|item)(\d+)?$/);
|
|
285
|
+
if (m) {
|
|
286
|
+
const [, baseName, levelStr] = m;
|
|
287
|
+
if (anyNode.name !== baseName)
|
|
288
|
+
return false;
|
|
289
|
+
const expectedLevel = levelStr ? parseInt(levelStr, 10) : baseName === 'item' ? 1 : undefined;
|
|
290
|
+
if (expectedLevel === undefined)
|
|
291
|
+
return true;
|
|
292
|
+
// Heading plugin stores level as the regex capture string; coerce.
|
|
293
|
+
return Number(anyNode.level) === expectedLevel;
|
|
294
|
+
}
|
|
295
|
+
return false;
|
|
296
|
+
};
|
|
297
|
+
const matchesPattern = (node, pattern) => {
|
|
298
|
+
if (!blockTypeMatches(node, pattern.blockType))
|
|
299
|
+
return false;
|
|
300
|
+
if (!pattern.predicate)
|
|
301
|
+
return true;
|
|
302
|
+
return pattern.predicate.every(c => matchCondition(node, c));
|
|
303
|
+
};
|
|
35
304
|
// Normalize a path for loose suffix comparison:
|
|
36
305
|
// 'src/foo.podlite' ~= 'foo.podlite'
|
|
37
306
|
// './includes/x.podlite' ~= 'includes/x.podlite'
|
|
@@ -124,7 +393,7 @@ const runSelector = (selector, docs) => {
|
|
|
124
393
|
const parsed = (0, exports.parseSelector)(selector);
|
|
125
394
|
if (!parsed)
|
|
126
395
|
return [];
|
|
127
|
-
const { scheme, document, anchor,
|
|
396
|
+
const { scheme, document, anchor, patterns } = parsed;
|
|
128
397
|
let matchedDocs = docs;
|
|
129
398
|
if (scheme === 'doc' && document) {
|
|
130
399
|
matchedDocs = docs.filter(doc => getDocIDs(doc).includes(document));
|
|
@@ -146,17 +415,24 @@ const runSelector = (selector, docs) => {
|
|
|
146
415
|
}
|
|
147
416
|
return collectedBlocks;
|
|
148
417
|
}
|
|
149
|
-
//
|
|
150
|
-
if (
|
|
418
|
+
// Patterns — full-tree traversal, apply each pattern, dedupe across patterns
|
|
419
|
+
if (patterns.length > 0) {
|
|
151
420
|
const collectedBlocks = [];
|
|
421
|
+
const seen = new Set();
|
|
152
422
|
for (const d of matchedDocs) {
|
|
153
|
-
|
|
154
|
-
|
|
423
|
+
const allBlocks = (0, index_1.getFromTree)(d.node, { type: 'block' });
|
|
424
|
+
for (const block of allBlocks) {
|
|
425
|
+
if (seen.has(block))
|
|
426
|
+
continue;
|
|
427
|
+
if (patterns.some(p => matchesPattern(block, p))) {
|
|
428
|
+
collectedBlocks.push(block);
|
|
429
|
+
seen.add(block);
|
|
430
|
+
}
|
|
155
431
|
}
|
|
156
432
|
}
|
|
157
433
|
return collectedBlocks;
|
|
158
434
|
}
|
|
159
|
-
// No anchor, no
|
|
435
|
+
// No anchor, no patterns — return whole docs
|
|
160
436
|
return matchedDocs.map(d => d.node);
|
|
161
437
|
};
|
|
162
438
|
exports.runSelector = runSelector;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@podlite/schema",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.42",
|
|
4
4
|
"description": "AST tools and schema for Podlite markup language — validate, traverse, and transform document trees",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"homepage": "https://podlite.org",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"build": "run-s build:pegjs build_lib build:ast ",
|
|
41
41
|
"build_lib": "yarn g:build",
|
|
42
42
|
"build:ast": "ts-node scripts/make-ast-scheme.ts",
|
|
43
|
-
"build:pegjs": "pegmill -o src/grammar.js src/grammar.pegjs && pegmill -o src/grammarfc.js src/grammarfc.pegjs",
|
|
43
|
+
"build:pegjs": "pegmill --allowed-start-rules Document,attributesOnly -o src/grammar.js src/grammar.pegjs && pegmill -o src/grammarfc.js src/grammarfc.pegjs",
|
|
44
44
|
"watch": "npx nodemon -e js,ts --exec 'yarn' build",
|
|
45
45
|
"watch:pegjs": "npx nodemon -e pegmill --exec 'yarn' build:pegjs",
|
|
46
46
|
"test": "yarn g:jest --passWithNoTests"
|