@riebeckite/plugin-dataview 0.0.8 → 0.0.9
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/dist/index.js +145 -141
- package/dist/src/parse-from.d.ts +2 -0
- package/dist/src/parse-shared.d.ts +4 -0
- package/dist/src/parse.d.ts +1 -3
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -7,6 +7,139 @@ import {
|
|
|
7
7
|
// src/remark.ts
|
|
8
8
|
import { visit } from "unist-util-visit";
|
|
9
9
|
|
|
10
|
+
// src/parse-shared.ts
|
|
11
|
+
var DataviewParseError = class extends Error {
|
|
12
|
+
};
|
|
13
|
+
function splitTopLevel(text, separator) {
|
|
14
|
+
const parts = [];
|
|
15
|
+
let current = "";
|
|
16
|
+
let depth = 0;
|
|
17
|
+
let quote = null;
|
|
18
|
+
for (const char of text) {
|
|
19
|
+
if (quote) {
|
|
20
|
+
current += char;
|
|
21
|
+
if (char === quote) quote = null;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (char === '"' || char === "'") {
|
|
25
|
+
quote = char;
|
|
26
|
+
current += char;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (char === "(") depth += 1;
|
|
30
|
+
if (char === ")") depth = Math.max(0, depth - 1);
|
|
31
|
+
if (char === separator && depth === 0) {
|
|
32
|
+
parts.push(current.trim());
|
|
33
|
+
current = "";
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
current += char;
|
|
37
|
+
}
|
|
38
|
+
parts.push(current.trim());
|
|
39
|
+
return parts.filter((part) => part.length > 0);
|
|
40
|
+
}
|
|
41
|
+
function unquote(value) {
|
|
42
|
+
if (value.length >= 2) {
|
|
43
|
+
const first = value[0];
|
|
44
|
+
const last = value[value.length - 1];
|
|
45
|
+
if (first === '"' && last === '"' || first === "'" && last === "'") {
|
|
46
|
+
return value.slice(1, -1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/parse-from.ts
|
|
53
|
+
var FROM_TOKEN_PATTERN = /(\[\[[^\]]*\]\])|(#[^\s()]+)|("(?:[^"\\]|\\.)*")|('(?:[^'\\]|\\.)*')|(\()|(\))|(!|-)|([^\s()]+)/g;
|
|
54
|
+
function parseFrom(text) {
|
|
55
|
+
const tokens = [];
|
|
56
|
+
FROM_TOKEN_PATTERN.lastIndex = 0;
|
|
57
|
+
for (let match = FROM_TOKEN_PATTERN.exec(text); match !== null; match = FROM_TOKEN_PATTERN.exec(text)) {
|
|
58
|
+
tokens.push(toFromToken(match));
|
|
59
|
+
}
|
|
60
|
+
if (tokens.length === 0) {
|
|
61
|
+
throw new DataviewParseError("the FROM clause is empty.");
|
|
62
|
+
}
|
|
63
|
+
const state = { tokens, index: 0 };
|
|
64
|
+
const node = parseOr(state);
|
|
65
|
+
if (state.index < tokens.length) {
|
|
66
|
+
throw new DataviewParseError("unexpected token in FROM.");
|
|
67
|
+
}
|
|
68
|
+
return node;
|
|
69
|
+
}
|
|
70
|
+
function toFromToken(match) {
|
|
71
|
+
const [raw, link, tag, doubleQuoted, singleQuoted, lparen, rparen, negate] = match;
|
|
72
|
+
if (link !== void 0) {
|
|
73
|
+
const value = link.slice(2, -2).split("|")[0].split("#")[0].trim();
|
|
74
|
+
if (value.length === 0) {
|
|
75
|
+
throw new DataviewParseError("a link source in FROM is empty.");
|
|
76
|
+
}
|
|
77
|
+
return { type: "source", source: { kind: "link", value } };
|
|
78
|
+
}
|
|
79
|
+
if (tag !== void 0) {
|
|
80
|
+
return { type: "source", source: { kind: "tag", value: tag.slice(1) } };
|
|
81
|
+
}
|
|
82
|
+
if (doubleQuoted !== void 0 || singleQuoted !== void 0) {
|
|
83
|
+
return {
|
|
84
|
+
type: "source",
|
|
85
|
+
source: { kind: "folder", value: unquote(raw.trim()) }
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (lparen !== void 0) return { type: "lparen" };
|
|
89
|
+
if (rparen !== void 0) return { type: "rparen" };
|
|
90
|
+
if (negate !== void 0) return { type: "not" };
|
|
91
|
+
const word = (raw ?? "").toLowerCase();
|
|
92
|
+
if (word === "and") return { type: "and" };
|
|
93
|
+
if (word === "or") return { type: "or" };
|
|
94
|
+
return { type: "source", source: { kind: "folder", value: raw.trim() } };
|
|
95
|
+
}
|
|
96
|
+
function parseOr(state) {
|
|
97
|
+
let node = parseAnd(state);
|
|
98
|
+
while (state.tokens[state.index]?.type === "or") {
|
|
99
|
+
state.index += 1;
|
|
100
|
+
node = { kind: "or", left: node, right: parseAnd(state) };
|
|
101
|
+
}
|
|
102
|
+
return node;
|
|
103
|
+
}
|
|
104
|
+
function parseAnd(state) {
|
|
105
|
+
let node = parseUnary(state);
|
|
106
|
+
for (; ; ) {
|
|
107
|
+
const token = state.tokens[state.index];
|
|
108
|
+
if (token?.type === "and") {
|
|
109
|
+
state.index += 1;
|
|
110
|
+
node = { kind: "and", left: node, right: parseUnary(state) };
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (token?.type === "source" || token?.type === "lparen" || token?.type === "not") {
|
|
114
|
+
node = { kind: "and", left: node, right: parseUnary(state) };
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
return node;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function parseUnary(state) {
|
|
121
|
+
const token = state.tokens[state.index];
|
|
122
|
+
if (!token) throw new DataviewParseError("unexpected end of FROM.");
|
|
123
|
+
if (token.type === "not") {
|
|
124
|
+
state.index += 1;
|
|
125
|
+
return { kind: "not", child: parseUnary(state) };
|
|
126
|
+
}
|
|
127
|
+
if (token.type === "lparen") {
|
|
128
|
+
state.index += 1;
|
|
129
|
+
const node = parseOr(state);
|
|
130
|
+
if (state.tokens[state.index]?.type !== "rparen") {
|
|
131
|
+
throw new DataviewParseError("unbalanced parentheses in FROM.");
|
|
132
|
+
}
|
|
133
|
+
state.index += 1;
|
|
134
|
+
return node;
|
|
135
|
+
}
|
|
136
|
+
if (token.type === "source") {
|
|
137
|
+
state.index += 1;
|
|
138
|
+
return { kind: "source", source: token.source };
|
|
139
|
+
}
|
|
140
|
+
throw new DataviewParseError("unexpected token in FROM.");
|
|
141
|
+
}
|
|
142
|
+
|
|
10
143
|
// src/parse.ts
|
|
11
144
|
var QUERY_TYPES = /* @__PURE__ */ new Set([
|
|
12
145
|
"list",
|
|
@@ -16,8 +149,6 @@ var QUERY_TYPES = /* @__PURE__ */ new Set([
|
|
|
16
149
|
]);
|
|
17
150
|
var CLAUSE_PATTERN = /^(from|where|sort|group\s+by|limit)\b(.*)$/i;
|
|
18
151
|
var FIELD_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*$/;
|
|
19
|
-
var DataviewParseError = class extends Error {
|
|
20
|
-
};
|
|
21
152
|
function parseDataview(source) {
|
|
22
153
|
try {
|
|
23
154
|
return { status: "ok", spec: parseSpec(source) };
|
|
@@ -160,102 +291,13 @@ function parseField(value, context) {
|
|
|
160
291
|
}
|
|
161
292
|
return field;
|
|
162
293
|
}
|
|
163
|
-
var FROM_TOKEN_PATTERN = /(\[\[[^\]]*\]\])|(#[^\s()]+)|("(?:[^"\\]|\\.)*")|('(?:[^'\\]|\\.)*')|(\()|(\))|(!|-)|([^\s()]+)/g;
|
|
164
|
-
function parseFrom(text) {
|
|
165
|
-
const tokens = [];
|
|
166
|
-
FROM_TOKEN_PATTERN.lastIndex = 0;
|
|
167
|
-
for (let match = FROM_TOKEN_PATTERN.exec(text); match !== null; match = FROM_TOKEN_PATTERN.exec(text)) {
|
|
168
|
-
tokens.push(toFromToken(match));
|
|
169
|
-
}
|
|
170
|
-
if (tokens.length === 0) {
|
|
171
|
-
throw new DataviewParseError("the FROM clause is empty.");
|
|
172
|
-
}
|
|
173
|
-
const state = { tokens, index: 0 };
|
|
174
|
-
const node = parseFromOr(state);
|
|
175
|
-
if (state.index < tokens.length) {
|
|
176
|
-
throw new DataviewParseError("unexpected token in FROM.");
|
|
177
|
-
}
|
|
178
|
-
return node;
|
|
179
|
-
}
|
|
180
|
-
function toFromToken(match) {
|
|
181
|
-
const [raw, link, tag, doubleQuoted, singleQuoted, lparen, rparen, negate] = match;
|
|
182
|
-
if (link !== void 0) {
|
|
183
|
-
const value = link.slice(2, -2).split("|")[0].split("#")[0].trim();
|
|
184
|
-
if (value.length === 0) {
|
|
185
|
-
throw new DataviewParseError("a link source in FROM is empty.");
|
|
186
|
-
}
|
|
187
|
-
return { type: "source", source: { kind: "link", value } };
|
|
188
|
-
}
|
|
189
|
-
if (tag !== void 0) {
|
|
190
|
-
return { type: "source", source: { kind: "tag", value: tag.slice(1) } };
|
|
191
|
-
}
|
|
192
|
-
if (doubleQuoted !== void 0 || singleQuoted !== void 0) {
|
|
193
|
-
return {
|
|
194
|
-
type: "source",
|
|
195
|
-
source: { kind: "folder", value: unquote(raw.trim()) }
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
if (lparen !== void 0) return { type: "lparen" };
|
|
199
|
-
if (rparen !== void 0) return { type: "rparen" };
|
|
200
|
-
if (negate !== void 0) return { type: "not" };
|
|
201
|
-
const word = (raw ?? "").toLowerCase();
|
|
202
|
-
if (word === "and") return { type: "and" };
|
|
203
|
-
if (word === "or") return { type: "or" };
|
|
204
|
-
return { type: "source", source: { kind: "folder", value: raw.trim() } };
|
|
205
|
-
}
|
|
206
|
-
function parseFromOr(state) {
|
|
207
|
-
let node = parseFromAnd(state);
|
|
208
|
-
while (state.tokens[state.index]?.type === "or") {
|
|
209
|
-
state.index += 1;
|
|
210
|
-
node = { kind: "or", left: node, right: parseFromAnd(state) };
|
|
211
|
-
}
|
|
212
|
-
return node;
|
|
213
|
-
}
|
|
214
|
-
function parseFromAnd(state) {
|
|
215
|
-
let node = parseFromUnary(state);
|
|
216
|
-
for (; ; ) {
|
|
217
|
-
const token = state.tokens[state.index];
|
|
218
|
-
if (token?.type === "and") {
|
|
219
|
-
state.index += 1;
|
|
220
|
-
node = { kind: "and", left: node, right: parseFromUnary(state) };
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
if (token?.type === "source" || token?.type === "lparen" || token?.type === "not") {
|
|
224
|
-
node = { kind: "and", left: node, right: parseFromUnary(state) };
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
return node;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
function parseFromUnary(state) {
|
|
231
|
-
const token = state.tokens[state.index];
|
|
232
|
-
if (!token) throw new DataviewParseError("unexpected end of FROM.");
|
|
233
|
-
if (token.type === "not") {
|
|
234
|
-
state.index += 1;
|
|
235
|
-
return { kind: "not", child: parseFromUnary(state) };
|
|
236
|
-
}
|
|
237
|
-
if (token.type === "lparen") {
|
|
238
|
-
state.index += 1;
|
|
239
|
-
const node = parseFromOr(state);
|
|
240
|
-
if (state.tokens[state.index]?.type !== "rparen") {
|
|
241
|
-
throw new DataviewParseError("unbalanced parentheses in FROM.");
|
|
242
|
-
}
|
|
243
|
-
state.index += 1;
|
|
244
|
-
return node;
|
|
245
|
-
}
|
|
246
|
-
if (token.type === "source") {
|
|
247
|
-
state.index += 1;
|
|
248
|
-
return { kind: "source", source: token.source };
|
|
249
|
-
}
|
|
250
|
-
throw new DataviewParseError("unexpected token in FROM.");
|
|
251
|
-
}
|
|
252
294
|
function parseExpression(text) {
|
|
253
295
|
const tokens = tokenizeExpression(text);
|
|
254
296
|
if (tokens.length === 0) {
|
|
255
297
|
throw new DataviewParseError("the WHERE clause is empty.");
|
|
256
298
|
}
|
|
257
299
|
const state = { tokens, index: 0 };
|
|
258
|
-
const node =
|
|
300
|
+
const node = parseOr2(state);
|
|
259
301
|
if (state.index < tokens.length) {
|
|
260
302
|
throw new DataviewParseError("unexpected token in the WHERE expression.");
|
|
261
303
|
}
|
|
@@ -346,26 +388,26 @@ function readQuoted(text, start) {
|
|
|
346
388
|
}
|
|
347
389
|
throw new DataviewParseError("unterminated string literal in WHERE.");
|
|
348
390
|
}
|
|
349
|
-
function
|
|
350
|
-
let node =
|
|
391
|
+
function parseOr2(state) {
|
|
392
|
+
let node = parseAnd2(state);
|
|
351
393
|
while (isWord(state.tokens[state.index], "or")) {
|
|
352
394
|
state.index += 1;
|
|
353
|
-
node = { kind: "or", left: node, right:
|
|
395
|
+
node = { kind: "or", left: node, right: parseAnd2(state) };
|
|
354
396
|
}
|
|
355
397
|
return node;
|
|
356
398
|
}
|
|
357
|
-
function
|
|
358
|
-
let node =
|
|
399
|
+
function parseAnd2(state) {
|
|
400
|
+
let node = parseUnary2(state);
|
|
359
401
|
while (isWord(state.tokens[state.index], "and")) {
|
|
360
402
|
state.index += 1;
|
|
361
|
-
node = { kind: "and", left: node, right:
|
|
403
|
+
node = { kind: "and", left: node, right: parseUnary2(state) };
|
|
362
404
|
}
|
|
363
405
|
return node;
|
|
364
406
|
}
|
|
365
|
-
function
|
|
407
|
+
function parseUnary2(state) {
|
|
366
408
|
if (state.tokens[state.index]?.type === "not") {
|
|
367
409
|
state.index += 1;
|
|
368
|
-
return { kind: "not", operand:
|
|
410
|
+
return { kind: "not", operand: parseUnary2(state) };
|
|
369
411
|
}
|
|
370
412
|
return parseComparison(state);
|
|
371
413
|
}
|
|
@@ -388,7 +430,7 @@ function parsePrimary(state) {
|
|
|
388
430
|
}
|
|
389
431
|
if (token.type === "lparen") {
|
|
390
432
|
state.index += 1;
|
|
391
|
-
const node =
|
|
433
|
+
const node = parseOr2(state);
|
|
392
434
|
if (state.tokens[state.index]?.type !== "rparen") {
|
|
393
435
|
throw new DataviewParseError("unbalanced parentheses in WHERE.");
|
|
394
436
|
}
|
|
@@ -421,10 +463,10 @@ function parseCall(state, name) {
|
|
|
421
463
|
state.index += 2;
|
|
422
464
|
const args = [];
|
|
423
465
|
if (state.tokens[state.index]?.type !== "rparen") {
|
|
424
|
-
args.push(
|
|
466
|
+
args.push(parseOr2(state));
|
|
425
467
|
while (state.tokens[state.index]?.type === "comma") {
|
|
426
468
|
state.index += 1;
|
|
427
|
-
args.push(
|
|
469
|
+
args.push(parseOr2(state));
|
|
428
470
|
}
|
|
429
471
|
}
|
|
430
472
|
if (state.tokens[state.index]?.type !== "rparen") {
|
|
@@ -436,44 +478,6 @@ function parseCall(state, name) {
|
|
|
436
478
|
function isWord(token, value) {
|
|
437
479
|
return token?.type === "word" && token.value.toLowerCase() === value;
|
|
438
480
|
}
|
|
439
|
-
function splitTopLevel(text, separator) {
|
|
440
|
-
const parts = [];
|
|
441
|
-
let current = "";
|
|
442
|
-
let depth = 0;
|
|
443
|
-
let quote = null;
|
|
444
|
-
for (const char of text) {
|
|
445
|
-
if (quote) {
|
|
446
|
-
current += char;
|
|
447
|
-
if (char === quote) quote = null;
|
|
448
|
-
continue;
|
|
449
|
-
}
|
|
450
|
-
if (char === '"' || char === "'") {
|
|
451
|
-
quote = char;
|
|
452
|
-
current += char;
|
|
453
|
-
continue;
|
|
454
|
-
}
|
|
455
|
-
if (char === "(") depth += 1;
|
|
456
|
-
if (char === ")") depth = Math.max(0, depth - 1);
|
|
457
|
-
if (char === separator && depth === 0) {
|
|
458
|
-
parts.push(current.trim());
|
|
459
|
-
current = "";
|
|
460
|
-
continue;
|
|
461
|
-
}
|
|
462
|
-
current += char;
|
|
463
|
-
}
|
|
464
|
-
parts.push(current.trim());
|
|
465
|
-
return parts.filter((part) => part.length > 0);
|
|
466
|
-
}
|
|
467
|
-
function unquote(value) {
|
|
468
|
-
if (value.length >= 2) {
|
|
469
|
-
const first = value[0];
|
|
470
|
-
const last = value[value.length - 1];
|
|
471
|
-
if (first === '"' && last === '"' || first === "'" && last === "'") {
|
|
472
|
-
return value.slice(1, -1);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
return value;
|
|
476
|
-
}
|
|
477
481
|
|
|
478
482
|
// src/placeholder.ts
|
|
479
483
|
var DATAVIEW_ATTRIBUTE = "data-rr-dataview";
|
package/dist/src/parse.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type { DataviewParseResult } from "./types.js";
|
|
2
|
-
declare class DataviewParseError extends Error {
|
|
3
|
-
}
|
|
4
2
|
/**
|
|
5
3
|
* Parse a declarative `dataview` block body into a {@link DataviewSpec}.
|
|
6
4
|
*
|
|
@@ -8,4 +6,4 @@ declare class DataviewParseError extends Error {
|
|
|
8
6
|
* failed {@link DataviewParseResult} so callers can render a fallback.
|
|
9
7
|
*/
|
|
10
8
|
export declare function parseDataview(source: string): DataviewParseResult;
|
|
11
|
-
export { DataviewParseError };
|
|
9
|
+
export { DataviewParseError } from "./parse-shared.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-dataview",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@types/mdast": "^4.0.4",
|
|
19
19
|
"unist-util-visit": "^5.1.0",
|
|
20
|
-
"@riebeckite/core": "0.0.
|
|
20
|
+
"@riebeckite/core": "0.0.9"
|
|
21
21
|
},
|
|
22
22
|
"publishConfig": {
|
|
23
23
|
"access": "public"
|