@p-buddy/parkdown 0.0.0
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/.assets/api-note.md +3 -0
- package/.assets/api.md +34 -0
- package/.assets/authoring.md +69 -0
- package/.assets/code/depopulate.ts +6 -0
- package/.assets/code/inclusions.ts +6 -0
- package/.assets/depopulated.md +25 -0
- package/.assets/invocation.md +16 -0
- package/.assets/populated/block.md +9 -0
- package/.assets/populated/inline.multi.md +5 -0
- package/.assets/populated/inline.single.md +3 -0
- package/.assets/query.md +73 -0
- package/.assets/remap-imports.md +0 -0
- package/.assets/unpopulated/block.md +5 -0
- package/.assets/unpopulated/inline.multi.md +3 -0
- package/.assets/unpopulated/inline.single.md +1 -0
- package/.devcontainer/Dockerfile +16 -0
- package/.devcontainer/devcontainer.json +35 -0
- package/LICENSE +21 -0
- package/README.md +418 -0
- package/dist/cli.js +14 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +396 -0
- package/dist/index.umd.cjs +15 -0
- package/package.json +42 -0
- package/src/api/index.test.ts +32 -0
- package/src/api/index.ts +8 -0
- package/src/api/types.ts +78 -0
- package/src/api/utils.test.ts +132 -0
- package/src/api/utils.ts +161 -0
- package/src/cli.ts +31 -0
- package/src/include.test.ts +369 -0
- package/src/include.ts +252 -0
- package/src/index.ts +35 -0
- package/src/region.test.ts +145 -0
- package/src/region.ts +138 -0
- package/src/remap.test.ts +37 -0
- package/src/remap.ts +72 -0
- package/src/utils.test.ts +238 -0
- package/src/utils.ts +184 -0
- package/src/wrap.ts +61 -0
- package/tsconfig.json +5 -0
- package/vite.cli.config.ts +23 -0
- package/vite.config.ts +20 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { writeFileSync as q, readFileSync as te } from "node:fs";
|
|
2
|
+
import { basename as ne, dirname as _, join as C, resolve as U } from "node:path";
|
|
3
|
+
import { URLSearchParams as oe } from "node:url";
|
|
4
|
+
import { unified as se } from "unified";
|
|
5
|
+
import re from "remark-parse";
|
|
6
|
+
import { visit as ie } from "unist-util-visit";
|
|
7
|
+
import ce from "stable-hash";
|
|
8
|
+
import { dedent as k } from "ts-dedent";
|
|
9
|
+
import le from "extract-comments";
|
|
10
|
+
const g = (e, n) => e.position.start.offset - n.position.start.offset;
|
|
11
|
+
g.reverse = (e, n) => g(n, e);
|
|
12
|
+
const T = (e) => e.position !== void 0 && e.position.start.offset !== void 0 && e.position.end.offset !== void 0, ae = se().use(re), I = {
|
|
13
|
+
md: (e) => ae.parse(e)
|
|
14
|
+
}, y = (e, n) => {
|
|
15
|
+
const t = [];
|
|
16
|
+
return ie(e, (s, o, r) => {
|
|
17
|
+
if (s.type !== "root") {
|
|
18
|
+
if (n && s.type !== n) return;
|
|
19
|
+
if (T(s)) {
|
|
20
|
+
const i = ce(r), c = ((r == null ? void 0 : r.children.length) ?? 0) - 1;
|
|
21
|
+
t.push({ ...s, parentID: i, siblingIndex: o, siblingCount: c });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}), t;
|
|
25
|
+
}, pe = (e) => e.children.length === 0, G = (e, n, ...t) => {
|
|
26
|
+
if (t.length === 0) throw new Error("No nodes to replace content from");
|
|
27
|
+
t.sort(g);
|
|
28
|
+
const s = t.at(0), o = t.at(-1);
|
|
29
|
+
return e.slice(0, s.position.start.offset) + n + e.slice(o.position.end.offset);
|
|
30
|
+
}, me = (e, n, t) => {
|
|
31
|
+
const s = Math.min(n.position.end.offset, t.position.end.offset), o = Math.max(n.position.start.offset, t.position.start.offset);
|
|
32
|
+
return e.slice(s, o);
|
|
33
|
+
}, L = (...e) => e.join(" "), ue = (...e) => e.join(`
|
|
34
|
+
`), he = (e) => {
|
|
35
|
+
const n = e.split("?");
|
|
36
|
+
return n.length > 1 ? [n.slice(0, -1).join("?"), n.at(-1)] : [e, ""];
|
|
37
|
+
}, ge = (e) => he(e)[0];
|
|
38
|
+
class d {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.intervals = [];
|
|
41
|
+
}
|
|
42
|
+
push(n, t) {
|
|
43
|
+
this.intervals.push([Math.min(n, t), Math.max(n, t)]);
|
|
44
|
+
}
|
|
45
|
+
combine(n) {
|
|
46
|
+
this.intervals.push(...n.intervals);
|
|
47
|
+
}
|
|
48
|
+
collapse() {
|
|
49
|
+
const { intervals: n } = this;
|
|
50
|
+
if (!n.length) return this.intervals = [];
|
|
51
|
+
n.sort((r, i) => r[0] - i[0]);
|
|
52
|
+
const t = [];
|
|
53
|
+
let [s, o] = n[0];
|
|
54
|
+
for (let r = 1; r < n.length; r++) {
|
|
55
|
+
const [i, c] = n[r];
|
|
56
|
+
i <= o ? o = Math.max(o, c) : (t.push([s, o]), s = i, o = c);
|
|
57
|
+
}
|
|
58
|
+
return t.push([s, o]), this.intervals = t;
|
|
59
|
+
}
|
|
60
|
+
subtract(n) {
|
|
61
|
+
const { intervals: t } = this, { intervals: s } = n;
|
|
62
|
+
if (!t.length || !s.length) return t;
|
|
63
|
+
let o = [...t];
|
|
64
|
+
for (const [r, i] of s) {
|
|
65
|
+
const c = [];
|
|
66
|
+
for (const [l, a] of o) {
|
|
67
|
+
if (i <= l || r >= a) {
|
|
68
|
+
c.push([l, a]);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
r > l && c.push([l, r]), i < a && c.push([i, a]);
|
|
72
|
+
}
|
|
73
|
+
o = c;
|
|
74
|
+
}
|
|
75
|
+
return this.intervals = o;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const P = /,\s*(?![^()]*\))/, fe = [
|
|
79
|
+
[/'''/g, '"'],
|
|
80
|
+
[/''/g, "'"],
|
|
81
|
+
[/parkdown:\s+/g, ""],
|
|
82
|
+
[/p▼:\s+/g, ""]
|
|
83
|
+
], de = (e, n = "-") => {
|
|
84
|
+
const t = fe.reduce((s, [o, r]) => s.replaceAll(o, r), e);
|
|
85
|
+
return n ? t.replaceAll(n, " ") : t;
|
|
86
|
+
}, we = ["string", "number", "boolean"], ve = (e) => we.includes(e), $e = /^([a-zA-Z0-9_-]+)(?:\(([^)]*)\))?$/, Ce = (e) => {
|
|
87
|
+
const n = e.match($e);
|
|
88
|
+
if (!n) throw new Error(`Invalid invocation: ${e}`);
|
|
89
|
+
const [, t, s = ""] = n;
|
|
90
|
+
if (!s.trim()) return { name: t, parameters: [] };
|
|
91
|
+
const o = Ee(s);
|
|
92
|
+
return { name: t, parameters: o };
|
|
93
|
+
}, ye = (e, n) => {
|
|
94
|
+
for (let t = n + 1; t < e.length; t++)
|
|
95
|
+
if (e[t] !== void 0) return !1;
|
|
96
|
+
return !0;
|
|
97
|
+
}, Ee = (e) => {
|
|
98
|
+
const n = [];
|
|
99
|
+
let t = "", s = !1;
|
|
100
|
+
for (let o = 0; o < e.length; o++) {
|
|
101
|
+
const r = e[o], i = r === "'";
|
|
102
|
+
if (i && e.at(o + 1) === "'") {
|
|
103
|
+
const c = e.at(o + 2) === "'";
|
|
104
|
+
s = !s, t += c ? "'''" : "''", o += c ? 2 : 1;
|
|
105
|
+
} else i ? (s = !s, t += r) : r === "," && !s ? (n.push(t.trim()), t = "") : t += r;
|
|
106
|
+
}
|
|
107
|
+
return n.push(t.trim()), n.map((o) => o === "" ? void 0 : o ? Se(o) : void 0).filter((o, r, i) => o !== void 0 || !ye(i, r));
|
|
108
|
+
}, Se = (e) => {
|
|
109
|
+
const n = e.length >= 2 && e[0] === "'" && e.at(-1) === "'", t = n && e.length >= 4 && e[1] === "'" && e.at(-2) === "'";
|
|
110
|
+
return !n || t ? e : e.slice(1, -1);
|
|
111
|
+
}, Ne = (e) => {
|
|
112
|
+
const n = /^(\w+)(?:\(([^)]*)\))?/, t = e.match(n);
|
|
113
|
+
if (!t) return { name: e };
|
|
114
|
+
const [, s, o] = t;
|
|
115
|
+
if (!o) return { name: s };
|
|
116
|
+
const r = /(\w+)(\?)?:\s*([^,]+)/g, i = [];
|
|
117
|
+
let c;
|
|
118
|
+
for (; (c = r.exec(o)) !== null; ) {
|
|
119
|
+
const [, l, a, p] = c, m = p.trim();
|
|
120
|
+
if (!ve(m)) throw new Error(`Unsupported type: ${m}`);
|
|
121
|
+
i.push({ name: l, optional: a === "?", type: m });
|
|
122
|
+
}
|
|
123
|
+
return { name: s, parameters: i };
|
|
124
|
+
}, H = (e) => {
|
|
125
|
+
const n = e.map(Ne).reduce(
|
|
126
|
+
(t, { name: s, parameters: o }) => t.set(s, o),
|
|
127
|
+
/* @__PURE__ */ new Map()
|
|
128
|
+
);
|
|
129
|
+
return (t) => {
|
|
130
|
+
const { name: s, parameters: o } = Ce(t);
|
|
131
|
+
if (!n.has(s))
|
|
132
|
+
throw new Error(`Unknown method: ${s}`);
|
|
133
|
+
const r = n.get(s);
|
|
134
|
+
if (r === void 0)
|
|
135
|
+
return { name: s };
|
|
136
|
+
if (o.length > r.length) {
|
|
137
|
+
const i = r.filter(({ optional: l }) => !l).length, c = i === r.length ? i.toString() : `${i} - ${r.length}`;
|
|
138
|
+
throw new Error(`Too many parameters: ${o.length} for method '${s}' (expected: ${c})`);
|
|
139
|
+
}
|
|
140
|
+
return r.reduce((i, { name: c, optional: l, type: a }, p) => {
|
|
141
|
+
const m = o[p];
|
|
142
|
+
if (m === void 0) {
|
|
143
|
+
if (l) return i;
|
|
144
|
+
throw new Error(`Missing required parameter: ${c} for method '${s}'`);
|
|
145
|
+
}
|
|
146
|
+
switch (a) {
|
|
147
|
+
case "string":
|
|
148
|
+
i[c] = m;
|
|
149
|
+
break;
|
|
150
|
+
case "number":
|
|
151
|
+
case "boolean":
|
|
152
|
+
i[c] = JSON.parse(m);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
if (typeof i[c] !== a)
|
|
156
|
+
throw new Error(`Invalid type: ${c} must be ${a}, got ${typeof i[c]} for method '${s}'`);
|
|
157
|
+
return i;
|
|
158
|
+
}, { name: s });
|
|
159
|
+
};
|
|
160
|
+
}, R = (e) => Object.entries(e).filter(([n]) => n !== "name" && !isNaN(Number(n))).map(([n, t]) => t), xe = [
|
|
161
|
+
/**
|
|
162
|
+
* Wraps the content in a markdown-formatted code block.
|
|
163
|
+
* @param lang The language of the code block (defaults to the file extension).
|
|
164
|
+
* @param meta Additional metadata to include in the top line of the code block (i.e. to the right of the `lang`).
|
|
165
|
+
* @example [](<url>?wrap=code)
|
|
166
|
+
* @example [](<url>?wrap=code())
|
|
167
|
+
* @example [](<url>?wrap=code(ts))
|
|
168
|
+
* @example [](<url>?wrap=code(,some-meta))
|
|
169
|
+
*/
|
|
170
|
+
"code(lang?: string, meta?: string)",
|
|
171
|
+
/**
|
|
172
|
+
* Wraps the content in a markdown-formatted blockquote
|
|
173
|
+
* (using the `>` character if the content is a single line,
|
|
174
|
+
* or the `<blockquote>` tag if the content is a multi-line block).
|
|
175
|
+
* @example [](<url>?wrap=quote)
|
|
176
|
+
* @example [](<url>?wrap=quote())
|
|
177
|
+
* @example [](<url>?wrap=quote(,))
|
|
178
|
+
*/
|
|
179
|
+
"quote()",
|
|
180
|
+
/**
|
|
181
|
+
* Wraps the content in a markdown-formatted dropdown (using the `<details>` and `<summary>` tags).
|
|
182
|
+
* @param summary The summary text of the dropdown.
|
|
183
|
+
* @param open Whether the dropdown should be open by default.
|
|
184
|
+
* @param space The space character to use between words in the summary (defaults to `-`).
|
|
185
|
+
* @example [](<url>?wrap=dropdown(hello-world))
|
|
186
|
+
* @example [](<url>?wrap=dropdown('hello,-world',true))
|
|
187
|
+
* @example [](<url>?wrap=dropdown(hello_world,,_))
|
|
188
|
+
*/
|
|
189
|
+
"dropdown(summary: string, open?: boolean, space?: string)"
|
|
190
|
+
], Me = "-", be = H(xe), W = (e, n, t) => {
|
|
191
|
+
const s = be(n), o = (t == null ? void 0 : t.inline) && !e.includes(`
|
|
192
|
+
|
|
193
|
+
`);
|
|
194
|
+
switch (s.name) {
|
|
195
|
+
case "code":
|
|
196
|
+
const r = s.lang ?? (t == null ? void 0 : t.extension) ?? "", i = s.meta ? ` ${s.meta}` : "";
|
|
197
|
+
return `\`\`\`${r}${i}
|
|
198
|
+
${e}
|
|
199
|
+
\`\`\``;
|
|
200
|
+
case "quote":
|
|
201
|
+
return o ? `> ${e}` : `<blockquote>
|
|
202
|
+
|
|
203
|
+
${e}
|
|
204
|
+
|
|
205
|
+
</blockquote>
|
|
206
|
+
`;
|
|
207
|
+
case "dropdown":
|
|
208
|
+
const c = `<details${s.open ? " open" : ""}>`, l = `<summary>${s.summary.split(s.space ?? Me).join(" ")}</summary>`;
|
|
209
|
+
return ["", c, l, "", e, "</details>", ""].join(`
|
|
210
|
+
`);
|
|
211
|
+
}
|
|
212
|
+
}, _e = [
|
|
213
|
+
"extract(id: string, 0?: string, 1?: string, 2?: string)",
|
|
214
|
+
"remove(id: string, 0?: string, 1?: string, 2?: string)",
|
|
215
|
+
"replace(id: string, with?: string, space?: string)"
|
|
216
|
+
], ke = H(_e), D = (e) => le(e), B = (e, n) => D(e).filter(({ value: t }) => t.includes(n)).sort((t, s) => t.range[0] - s.range[0]), Te = (e, ...n) => {
|
|
217
|
+
if (n.length === 0) return e;
|
|
218
|
+
const t = ([i, c]) => e.slice(i, c), s = D(e), o = new d(), r = new d();
|
|
219
|
+
for (const i of n) {
|
|
220
|
+
const c = s.filter(({ value: l }) => l.includes(i)).sort((l, a) => l.range[0] - a.range[0]);
|
|
221
|
+
for (let l = 0; l < c.length - 1; l += 2) {
|
|
222
|
+
const a = c[l], p = c[l + 1];
|
|
223
|
+
o.push(a.range[1], p.range[0]);
|
|
224
|
+
const [m, ...f] = t([a.range[1], p.range[0]]), S = f[f.length - 1];
|
|
225
|
+
r.push(a.range[0], a.range[1] + (m.trim() ? 0 : 1)), r.push(p.range[0], p.range[1] + (S.trim() ? 0 : 1));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return o.collapse(), r.collapse(), k(
|
|
229
|
+
o.subtract(r).map(t).filter(Boolean).join("")
|
|
230
|
+
).trim();
|
|
231
|
+
}, Ie = (e, ...n) => {
|
|
232
|
+
if (n.length === 0) return e;
|
|
233
|
+
const t = ([i, c]) => e.slice(i, c), s = D(e), o = new d();
|
|
234
|
+
for (const i of n) {
|
|
235
|
+
const c = s.filter(({ value: l }) => l.includes(i)).sort((l, a) => l.range[0] - a.range[0]);
|
|
236
|
+
for (let l = 0; l < c.length - 1; l += 2) {
|
|
237
|
+
const a = c[l], p = c[l + 1], m = t([p.range[1], p.range[1] + 1]).at(-1);
|
|
238
|
+
o.push(a.range[0], m === `
|
|
239
|
+
` ? p.range[1] + 1 : p.range[1]);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
o.collapse();
|
|
243
|
+
const r = new d();
|
|
244
|
+
return r.push(0, e.length), r.subtract(o), k(
|
|
245
|
+
r.collapse().map(t).filter(Boolean).join("")
|
|
246
|
+
).trim();
|
|
247
|
+
}, De = (e, n, t, s) => {
|
|
248
|
+
if (!n) return e;
|
|
249
|
+
const o = B(e, n);
|
|
250
|
+
if (o.length < 2) return e;
|
|
251
|
+
let r = "", i = 0;
|
|
252
|
+
for (let l = 0; l < o.length - 1; l += 2) {
|
|
253
|
+
const a = o[l], p = o[l + 1];
|
|
254
|
+
r += e.slice(i, a.range[1]), r += de(t ?? a.value, s), i = p.range[0];
|
|
255
|
+
}
|
|
256
|
+
r += e.slice(i);
|
|
257
|
+
const c = new d();
|
|
258
|
+
return c.push(0, r.length), c.subtract(
|
|
259
|
+
B(r, n).reduce((l, { range: a }) => (l.push(...a), l), new d())
|
|
260
|
+
), k(
|
|
261
|
+
c.collapse().map(([l, a]) => r.slice(l, a)).filter(Boolean).join("")
|
|
262
|
+
).trim();
|
|
263
|
+
}, je = (e, n) => {
|
|
264
|
+
const t = ke(n);
|
|
265
|
+
switch (t.name) {
|
|
266
|
+
case "extract":
|
|
267
|
+
return Te(e, t.id, ...R(t));
|
|
268
|
+
case "remove":
|
|
269
|
+
return Ie(e, t.id, ...R(t));
|
|
270
|
+
case "replace":
|
|
271
|
+
return De(e, t.id, t.with, t.space);
|
|
272
|
+
}
|
|
273
|
+
}, Oe = ["http", "./", "../"], Ae = ({ url: e }) => Oe.some((n) => e.startsWith(n)), Le = (e) => T(e) && pe(e) && Ae(e), b = ({ url: e }, n) => `[](${n ? C(n, e) : e})`, h = {
|
|
274
|
+
_open: "<!--",
|
|
275
|
+
_close: "-->",
|
|
276
|
+
_flag: "p▼",
|
|
277
|
+
get begin() {
|
|
278
|
+
return L(h._open, h._flag, "BEGIN", h._close);
|
|
279
|
+
},
|
|
280
|
+
get end() {
|
|
281
|
+
return L(h._open, h._flag, "END", h._close);
|
|
282
|
+
}
|
|
283
|
+
}, F = (e) => (n) => T(n) && n.value === h[e], Pe = ({ position: e, url: n, siblingCount: t }, s) => ({ position: e, url: n, headingDepth: s, inline: t >= 1 }), Re = ({ position: { start: e }, url: n, siblingCount: t }, { position: { end: s } }, o) => ({ position: { start: e, end: s }, url: n, headingDepth: o, inline: t >= 1 }), We = (e, n, t) => e.inline ? `${b(e, t)} ${h.begin} ${n} ${h.end}` : ue(b(e, t), h.begin, n, h.end), Be = (e) => {
|
|
284
|
+
const n = y(e, "heading").reduce((t, { position: s, depth: o }) => t.set(s.start.line, o), /* @__PURE__ */ new Map());
|
|
285
|
+
return (t) => {
|
|
286
|
+
for (let s = t.position.start.line; s >= 1; s--) {
|
|
287
|
+
const o = n.get(s);
|
|
288
|
+
if (o) return o;
|
|
289
|
+
}
|
|
290
|
+
return 0;
|
|
291
|
+
};
|
|
292
|
+
}, E = {
|
|
293
|
+
openingCommentDoesNotFollowLink: ({ position: { start: e } }) => new Error(`Opening comment (@${e.line}:${e.column}) does not follow link`),
|
|
294
|
+
closingCommentNotMatchedToOpening: ({ position: { start: e } }) => new Error(`Closing comment (@${e.line}:${e.column}) does not match to opening comment`),
|
|
295
|
+
openingCommentNotClosed: ({ position: { start: e } }) => new Error(`Opening comment (@${e.line}:${e.column}) is not followed by a closing comment`)
|
|
296
|
+
}, Fe = (e, n) => {
|
|
297
|
+
const t = [], s = [
|
|
298
|
+
...e.map((r) => ({ node: r, type: "open" })),
|
|
299
|
+
...n.map((r) => ({ node: r, type: "close" }))
|
|
300
|
+
].sort((r, i) => g(r.node, i.node)), o = [];
|
|
301
|
+
for (const r of s)
|
|
302
|
+
if (r.type === "open") o.push(r);
|
|
303
|
+
else {
|
|
304
|
+
const i = r.node;
|
|
305
|
+
if (o.length === 0)
|
|
306
|
+
throw E.closingCommentNotMatchedToOpening(i);
|
|
307
|
+
const c = o.pop().node;
|
|
308
|
+
if (o.length > 0) continue;
|
|
309
|
+
t.push({ open: c, close: i });
|
|
310
|
+
}
|
|
311
|
+
if (o.length > 0)
|
|
312
|
+
throw E.openingCommentNotClosed(o[0].node);
|
|
313
|
+
return t;
|
|
314
|
+
}, qe = (e, n, t) => {
|
|
315
|
+
const s = [...n].sort(g), o = [];
|
|
316
|
+
return [...t].sort((r, i) => g.reverse(r.open, i.open)).forEach((r) => {
|
|
317
|
+
for (; s.length > 0; ) {
|
|
318
|
+
const i = s.pop();
|
|
319
|
+
if (i.position.start.offset < r.open.position.start.offset) {
|
|
320
|
+
if (me(e, i, r.open).trim() !== "")
|
|
321
|
+
throw E.openingCommentDoesNotFollowLink(r.open);
|
|
322
|
+
return o.push([i, r]);
|
|
323
|
+
}
|
|
324
|
+
o.push(i);
|
|
325
|
+
}
|
|
326
|
+
throw E.openingCommentDoesNotFollowLink(r.open);
|
|
327
|
+
}), o.push(...s.reverse()), o.reverse();
|
|
328
|
+
}, Q = (e, n) => {
|
|
329
|
+
n ?? (n = I.md(e));
|
|
330
|
+
const t = Be(n), s = y(n, "link").filter(Le), o = y(n, "html").sort(g), r = o.filter(F("begin")), i = o.filter(F("end")), c = Fe(r, i);
|
|
331
|
+
return qe(e, s, c).map((a) => Array.isArray(a) ? Re(a[0], a[1].close, t(a[0])) : Pe(a, t(a)));
|
|
332
|
+
}, Ue = (e, { url: n }) => (t) => e(C(_(n), t)), z = (...e) => {
|
|
333
|
+
const n = e.reduce((t, s) => t + s, 0);
|
|
334
|
+
return Math.min(Math.max(n, 1), 6);
|
|
335
|
+
}, Ge = (e, n, t) => {
|
|
336
|
+
if (n === 0) return e;
|
|
337
|
+
t ?? (t = I.md(e));
|
|
338
|
+
const s = y(t, "heading"), o = e.split(`
|
|
339
|
+
`);
|
|
340
|
+
for (const r of s) {
|
|
341
|
+
const { depth: i, position: { start: c, end: l } } = r, a = z(i, n), p = o[c.line - 1].slice(i, l.column), m = "#".repeat(a) + p;
|
|
342
|
+
o[c.line - 1] = m, r.depth = a;
|
|
343
|
+
}
|
|
344
|
+
return o.join(`
|
|
345
|
+
`);
|
|
346
|
+
}, X = (e) => Q(e).reverse().sort(g.reverse).reduce((n, t) => G(n, b(t), t), e), J = (e, n, t, s) => {
|
|
347
|
+
e = X(e), e = Ge(e, n);
|
|
348
|
+
const o = I.md(e);
|
|
349
|
+
return Q(e, o).sort(g).reverse().map((r) => {
|
|
350
|
+
var O, A;
|
|
351
|
+
const { url: i, headingDepth: c } = r, [l, ...a] = ne(i).split("?"), p = l.split(".").pop() ?? "", m = a.join("?"), f = _(i), S = C(f, l);
|
|
352
|
+
if (i.startsWith("./") || i.startsWith("../")) {
|
|
353
|
+
let u = t(S);
|
|
354
|
+
const w = new oe(m), N = (O = w.get("region")) == null ? void 0 : O.split(P);
|
|
355
|
+
u = (N == null ? void 0 : N.reduce((v, $) => je(v, $), u)) ?? u;
|
|
356
|
+
const Z = w.has("skip"), K = w.get("heading") ?? 0, Y = w.has("inline");
|
|
357
|
+
let { inline: x } = r;
|
|
358
|
+
if (Y && (x = !0), !Z)
|
|
359
|
+
if (p === "md") {
|
|
360
|
+
const v = Ue(t, r), $ = s ? C(s, f) : f, ee = z(c, Number(K));
|
|
361
|
+
u = J(
|
|
362
|
+
u,
|
|
363
|
+
/** p▼: ... */
|
|
364
|
+
ee,
|
|
365
|
+
v,
|
|
366
|
+
$
|
|
367
|
+
/** p▼: ... */
|
|
368
|
+
);
|
|
369
|
+
} else /^(js|ts)x?|svelte$/i.test(p) && (u = W(
|
|
370
|
+
u,
|
|
371
|
+
"code",
|
|
372
|
+
/** p▼: ... */
|
|
373
|
+
{ extension: p, inline: x }
|
|
374
|
+
/** p▼: ... */
|
|
375
|
+
));
|
|
376
|
+
const M = (A = w.get("wrap")) == null ? void 0 : A.split(P);
|
|
377
|
+
return u = (M == null ? void 0 : M.reduce((v, $) => W(v, $, { extension: p, inline: x }), u)) ?? u, { target: r, content: We(r, u, s) };
|
|
378
|
+
} else throw i.startsWith("http") ? new Error("External web links are not implemented yet") : new Error(`Unsupported link type: ${i}`);
|
|
379
|
+
}).reduce((r, { target: i, content: c }) => G(r, c, i), e);
|
|
380
|
+
}, V = (e) => te(e, "utf-8"), j = (e) => {
|
|
381
|
+
const n = U(e), t = _(n);
|
|
382
|
+
return { markdown: V(n), dir: t, path: n };
|
|
383
|
+
}, et = (e, n = !0) => {
|
|
384
|
+
const { dir: t, path: s, markdown: o } = j(e), i = J(o, 0, (c) => V(U(t, ge(c))));
|
|
385
|
+
return n && q(s, i), i;
|
|
386
|
+
}, tt = (e, n = !0) => {
|
|
387
|
+
const { path: t, markdown: s } = j(e), o = X(s);
|
|
388
|
+
return n && q(t, o), o;
|
|
389
|
+
}, nt = (e, n = !0) => {
|
|
390
|
+
j(e);
|
|
391
|
+
};
|
|
392
|
+
export {
|
|
393
|
+
tt as depopulateMarkdownInclusions,
|
|
394
|
+
et as populateMarkdownInclusions,
|
|
395
|
+
nt as remapImportSpecifiers
|
|
396
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
(function(u,w){typeof exports=="object"&&typeof module<"u"?w(exports,require("node:fs"),require("node:path"),require("node:url"),require("unified"),require("remark-parse"),require("unist-util-visit"),require("stable-hash"),require("ts-dedent"),require("extract-comments")):typeof define=="function"&&define.amd?define(["exports","node:fs","node:path","node:url","unified","remark-parse","unist-util-visit","stable-hash","ts-dedent","extract-comments"],w):(u=typeof globalThis<"u"?globalThis:u||self,w(u.index={},u.node_fs,u.node_path,u.node_url,u.unified,u.remarkParse,u.unistUtilVisit,u.hash,u.tsDedent,u._extractComments))})(this,function(u,w,f,J,Z,K,Y,ee,x,te){"use strict";const g=(e,n)=>e.position.start.offset-n.position.start.offset;g.reverse=(e,n)=>g(n,e);const N=e=>e.position!==void 0&&e.position.start.offset!==void 0&&e.position.end.offset!==void 0,ne=Z.unified().use(K),k={md:e=>ne.parse(e)},E=(e,n)=>{const t=[];return Y.visit(e,(o,s,r)=>{if(o.type!=="root"){if(n&&o.type!==n)return;if(N(o)){const i=ee(r),c=((r==null?void 0:r.children.length)??0)-1;t.push({...o,parentID:i,siblingIndex:s,siblingCount:c})}}}),t},se=e=>e.children.length===0,P=(e,n,...t)=>{if(t.length===0)throw new Error("No nodes to replace content from");t.sort(g);const o=t.at(0),s=t.at(-1);return e.slice(0,o.position.start.offset)+n+e.slice(s.position.end.offset)},oe=(e,n,t)=>{const o=Math.min(n.position.end.offset,t.position.end.offset),s=Math.max(n.position.start.offset,t.position.start.offset);return e.slice(o,s)},A=(...e)=>e.join(" "),re=(...e)=>e.join(`
|
|
2
|
+
`),ie=e=>{const n=e.split("?");return n.length>1?[n.slice(0,-1).join("?"),n.at(-1)]:[e,""]},ce=e=>ie(e)[0];class v{constructor(){this.intervals=[]}push(n,t){this.intervals.push([Math.min(n,t),Math.max(n,t)])}combine(n){this.intervals.push(...n.intervals)}collapse(){const{intervals:n}=this;if(!n.length)return this.intervals=[];n.sort((r,i)=>r[0]-i[0]);const t=[];let[o,s]=n[0];for(let r=1;r<n.length;r++){const[i,c]=n[r];i<=s?s=Math.max(s,c):(t.push([o,s]),o=i,s=c)}return t.push([o,s]),this.intervals=t}subtract(n){const{intervals:t}=this,{intervals:o}=n;if(!t.length||!o.length)return t;let s=[...t];for(const[r,i]of o){const c=[];for(const[l,a]of s){if(i<=l||r>=a){c.push([l,a]);continue}r>l&&c.push([l,r]),i<a&&c.push([i,a])}s=c}return this.intervals=s}}const L=/,\s*(?![^()]*\))/,le=[[/'''/g,'"'],[/''/g,"'"],[/parkdown:\s+/g,""],[/p▼:\s+/g,""]],ae=(e,n="-")=>{const t=le.reduce((o,[s,r])=>o.replaceAll(s,r),e);return n?t.replaceAll(n," "):t},pe=["string","number","boolean"],ue=e=>pe.includes(e),me=/^([a-zA-Z0-9_-]+)(?:\(([^)]*)\))?$/,de=e=>{const n=e.match(me);if(!n)throw new Error(`Invalid invocation: ${e}`);const[,t,o=""]=n;if(!o.trim())return{name:t,parameters:[]};const s=fe(o);return{name:t,parameters:s}},he=(e,n)=>{for(let t=n+1;t<e.length;t++)if(e[t]!==void 0)return!1;return!0},fe=e=>{const n=[];let t="",o=!1;for(let s=0;s<e.length;s++){const r=e[s],i=r==="'";if(i&&e.at(s+1)==="'"){const c=e.at(s+2)==="'";o=!o,t+=c?"'''":"''",s+=c?2:1}else i?(o=!o,t+=r):r===","&&!o?(n.push(t.trim()),t=""):t+=r}return n.push(t.trim()),n.map(s=>s===""?void 0:s?ge(s):void 0).filter((s,r,i)=>s!==void 0||!he(i,r))},ge=e=>{const n=e.length>=2&&e[0]==="'"&&e.at(-1)==="'",t=n&&e.length>=4&&e[1]==="'"&&e.at(-2)==="'";return!n||t?e:e.slice(1,-1)},we=e=>{const n=/^(\w+)(?:\(([^)]*)\))?/,t=e.match(n);if(!t)return{name:e};const[,o,s]=t;if(!s)return{name:o};const r=/(\w+)(\?)?:\s*([^,]+)/g,i=[];let c;for(;(c=r.exec(s))!==null;){const[,l,a,p]=c,m=p.trim();if(!ue(m))throw new Error(`Unsupported type: ${m}`);i.push({name:l,optional:a==="?",type:m})}return{name:o,parameters:i}},R=e=>{const n=e.map(we).reduce((t,{name:o,parameters:s})=>t.set(o,s),new Map);return t=>{const{name:o,parameters:s}=de(t);if(!n.has(o))throw new Error(`Unknown method: ${o}`);const r=n.get(o);if(r===void 0)return{name:o};if(s.length>r.length){const i=r.filter(({optional:l})=>!l).length,c=i===r.length?i.toString():`${i} - ${r.length}`;throw new Error(`Too many parameters: ${s.length} for method '${o}' (expected: ${c})`)}return r.reduce((i,{name:c,optional:l,type:a},p)=>{const m=s[p];if(m===void 0){if(l)return i;throw new Error(`Missing required parameter: ${c} for method '${o}'`)}switch(a){case"string":i[c]=m;break;case"number":case"boolean":i[c]=JSON.parse(m);break}if(typeof i[c]!==a)throw new Error(`Invalid type: ${c} must be ${a}, got ${typeof i[c]} for method '${o}'`);return i},{name:o})}},_=e=>Object.entries(e).filter(([n])=>n!=="name"&&!isNaN(Number(n))).map(([n,t])=>t),ve=["code(lang?: string, meta?: string)","quote()","dropdown(summary: string, open?: boolean, space?: string)"],$e="-",Ce=R(ve),F=(e,n,t)=>{const o=Ce(n),s=(t==null?void 0:t.inline)&&!e.includes(`
|
|
3
|
+
|
|
4
|
+
`);switch(o.name){case"code":const r=o.lang??(t==null?void 0:t.extension)??"",i=o.meta?` ${o.meta}`:"";return`\`\`\`${r}${i}
|
|
5
|
+
${e}
|
|
6
|
+
\`\`\``;case"quote":return s?`> ${e}`:`<blockquote>
|
|
7
|
+
|
|
8
|
+
${e}
|
|
9
|
+
|
|
10
|
+
</blockquote>
|
|
11
|
+
`;case"dropdown":const c=`<details${o.open?" open":""}>`,l=`<summary>${o.summary.split(o.space??$e).join(" ")}</summary>`;return["",c,l,"",e,"</details>",""].join(`
|
|
12
|
+
`)}},ye=R(["extract(id: string, 0?: string, 1?: string, 2?: string)","remove(id: string, 0?: string, 1?: string, 2?: string)","replace(id: string, with?: string, space?: string)"]),T=e=>te(e),W=(e,n)=>T(e).filter(({value:t})=>t.includes(n)).sort((t,o)=>t.range[0]-o.range[0]),Se=(e,...n)=>{if(n.length===0)return e;const t=([i,c])=>e.slice(i,c),o=T(e),s=new v,r=new v;for(const i of n){const c=o.filter(({value:l})=>l.includes(i)).sort((l,a)=>l.range[0]-a.range[0]);for(let l=0;l<c.length-1;l+=2){const a=c[l],p=c[l+1];s.push(a.range[1],p.range[0]);const[m,...$]=t([a.range[1],p.range[0]]),b=$[$.length-1];r.push(a.range[0],a.range[1]+(m.trim()?0:1)),r.push(p.range[0],p.range[1]+(b.trim()?0:1))}}return s.collapse(),r.collapse(),x.dedent(s.subtract(r).map(t).filter(Boolean).join("")).trim()},Ee=(e,...n)=>{if(n.length===0)return e;const t=([i,c])=>e.slice(i,c),o=T(e),s=new v;for(const i of n){const c=o.filter(({value:l})=>l.includes(i)).sort((l,a)=>l.range[0]-a.range[0]);for(let l=0;l<c.length-1;l+=2){const a=c[l],p=c[l+1],m=t([p.range[1],p.range[1]+1]).at(-1);s.push(a.range[0],m===`
|
|
13
|
+
`?p.range[1]+1:p.range[1])}}s.collapse();const r=new v;return r.push(0,e.length),r.subtract(s),x.dedent(r.collapse().map(t).filter(Boolean).join("")).trim()},Me=(e,n,t,o)=>{if(!n)return e;const s=W(e,n);if(s.length<2)return e;let r="",i=0;for(let l=0;l<s.length-1;l+=2){const a=s[l],p=s[l+1];r+=e.slice(i,a.range[1]),r+=ae(t??a.value,o),i=p.range[0]}r+=e.slice(i);const c=new v;return c.push(0,r.length),c.subtract(W(r,n).reduce((l,{range:a})=>(l.push(...a),l),new v)),x.dedent(c.collapse().map(([l,a])=>r.slice(l,a)).filter(Boolean).join("")).trim()},xe=(e,n)=>{const t=ye(n);switch(t.name){case"extract":return Se(e,t.id,..._(t));case"remove":return Ee(e,t.id,..._(t));case"replace":return Me(e,t.id,t.with,t.space)}},Ne=["http","./","../"],ke=({url:e})=>Ne.some(n=>e.startsWith(n)),Te=e=>N(e)&&se(e)&&ke(e),I=({url:e},n)=>`[](${n?f.join(n,e):e})`,d={_open:"<!--",_close:"-->",_flag:"p▼",get begin(){return A(d._open,d._flag,"BEGIN",d._close)},get end(){return A(d._open,d._flag,"END",d._close)}},B=e=>n=>N(n)&&n.value===d[e],Ie=({position:e,url:n,siblingCount:t},o)=>({position:e,url:n,headingDepth:o,inline:t>=1}),je=({position:{start:e},url:n,siblingCount:t},{position:{end:o}},s)=>({position:{start:e,end:o},url:n,headingDepth:s,inline:t>=1}),be=(e,n,t)=>e.inline?`${I(e,t)} ${d.begin} ${n} ${d.end}`:re(I(e,t),d.begin,n,d.end),qe=e=>{const n=E(e,"heading").reduce((t,{position:o,depth:s})=>t.set(o.start.line,s),new Map);return t=>{for(let o=t.position.start.line;o>=1;o--){const s=n.get(o);if(s)return s}return 0}},M={openingCommentDoesNotFollowLink:({position:{start:e}})=>new Error(`Opening comment (@${e.line}:${e.column}) does not follow link`),closingCommentNotMatchedToOpening:({position:{start:e}})=>new Error(`Closing comment (@${e.line}:${e.column}) does not match to opening comment`),openingCommentNotClosed:({position:{start:e}})=>new Error(`Opening comment (@${e.line}:${e.column}) is not followed by a closing comment`)},De=(e,n)=>{const t=[],o=[...e.map(r=>({node:r,type:"open"})),...n.map(r=>({node:r,type:"close"}))].sort((r,i)=>g(r.node,i.node)),s=[];for(const r of o)if(r.type==="open")s.push(r);else{const i=r.node;if(s.length===0)throw M.closingCommentNotMatchedToOpening(i);const c=s.pop().node;if(s.length>0)continue;t.push({open:c,close:i})}if(s.length>0)throw M.openingCommentNotClosed(s[0].node);return t},Oe=(e,n,t)=>{const o=[...n].sort(g),s=[];return[...t].sort((r,i)=>g.reverse(r.open,i.open)).forEach(r=>{for(;o.length>0;){const i=o.pop();if(i.position.start.offset<r.open.position.start.offset){if(oe(e,i,r.open).trim()!=="")throw M.openingCommentDoesNotFollowLink(r.open);return s.push([i,r])}s.push(i)}throw M.openingCommentDoesNotFollowLink(r.open)}),s.push(...o.reverse()),s.reverse()},U=(e,n)=>{n??(n=k.md(e));const t=qe(n),o=E(n,"link").filter(Te),s=E(n,"html").sort(g),r=s.filter(B("begin")),i=s.filter(B("end")),c=De(r,i);return Oe(e,o,c).map(a=>Array.isArray(a)?je(a[0],a[1].close,t(a[0])):Ie(a,t(a)))},Pe=(e,{url:n})=>t=>e(f.join(f.dirname(n),t)),G=(...e)=>{const n=e.reduce((t,o)=>t+o,0);return Math.min(Math.max(n,1),6)},Ae=(e,n,t)=>{if(n===0)return e;t??(t=k.md(e));const o=E(t,"heading"),s=e.split(`
|
|
14
|
+
`);for(const r of o){const{depth:i,position:{start:c,end:l}}=r,a=G(i,n),p=s[c.line-1].slice(i,l.column),m="#".repeat(a)+p;s[c.line-1]=m,r.depth=a}return s.join(`
|
|
15
|
+
`)},H=e=>U(e).reverse().sort(g.reverse).reduce((n,t)=>P(n,I(t),t),e),Q=(e,n,t,o)=>{e=H(e),e=Ae(e,n);const s=k.md(e);return U(e,s).sort(g).reverse().map(r=>{var X,V;const{url:i,headingDepth:c}=r,[l,...a]=f.basename(i).split("?"),p=l.split(".").pop()??"",m=a.join("?"),$=f.dirname(i),b=f.join($,l);if(i.startsWith("./")||i.startsWith("../")){let h=t(b);const C=new J.URLSearchParams(m),q=(X=C.get("region"))==null?void 0:X.split(L);h=(q==null?void 0:q.reduce((y,S)=>xe(y,S),h))??h;const Fe=C.has("skip"),We=C.get("heading")??0,Be=C.has("inline");let{inline:D}=r;if(Be&&(D=!0),!Fe)if(p==="md"){const y=Pe(t,r),S=o?f.join(o,$):$,Ue=G(c,Number(We));h=Q(h,Ue,y,S)}else/^(js|ts)x?|svelte$/i.test(p)&&(h=F(h,"code",{extension:p,inline:D}));const O=(V=C.get("wrap"))==null?void 0:V.split(L);return h=(O==null?void 0:O.reduce((y,S)=>F(y,S,{extension:p,inline:D}),h))??h,{target:r,content:be(r,h,o)}}else throw i.startsWith("http")?new Error("External web links are not implemented yet"):new Error(`Unsupported link type: ${i}`)}).reduce((r,{target:i,content:c})=>P(r,c,i),e)},z=e=>w.readFileSync(e,"utf-8"),j=e=>{const n=f.resolve(e),t=f.dirname(n);return{markdown:z(n),dir:t,path:n}},Le=(e,n=!0)=>{const{dir:t,path:o,markdown:s}=j(e),i=Q(s,0,c=>z(f.resolve(t,ce(c))));return n&&w.writeFileSync(o,i),i},Re=(e,n=!0)=>{const{path:t,markdown:o}=j(e),s=H(o);return n&&w.writeFileSync(t,s),s},_e=(e,n=!0)=>{j(e)};u.depopulateMarkdownInclusions=Re,u.populateMarkdownInclusions=Le,u.remapImportSpecifiers=_e,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@p-buddy/parkdown",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"private": false,
|
|
5
|
+
"version": "0.0.0",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": "dist/cli.js",
|
|
8
|
+
"devDependencies": {
|
|
9
|
+
"@types/node": "^22.13.10",
|
|
10
|
+
"@types/unist": "^3.0.3",
|
|
11
|
+
"typescript": "^5.8.2",
|
|
12
|
+
"vite": "^6.2.1",
|
|
13
|
+
"vite-plugin-dts": "^4.5.3",
|
|
14
|
+
"vite-plugin-externalize-deps": "^0.9.0",
|
|
15
|
+
"vitest": "^3.0.8"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@commander-js/extra-typings": "^13.1.0",
|
|
19
|
+
"@flex-development/import-regex": "^3.0.0",
|
|
20
|
+
"extract-comments": "^1.1.0",
|
|
21
|
+
"remark-parse": "^11.0.0",
|
|
22
|
+
"stable-hash": "^0.0.4",
|
|
23
|
+
"ts-dedent": "^2.2.0",
|
|
24
|
+
"unified": "^11.0.5",
|
|
25
|
+
"unist-util-visit": "^5.0.0"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build:lib": "vite build",
|
|
29
|
+
"build:cli": "vite build --config vite.cli.config.ts",
|
|
30
|
+
"build": "pnpm run build:lib && pnpm run build:cli",
|
|
31
|
+
"test": "vitest",
|
|
32
|
+
"test:run": "vitest run"
|
|
33
|
+
},
|
|
34
|
+
"typings": "dist/index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "dist/index.d.ts",
|
|
38
|
+
"import": "dist/index.js",
|
|
39
|
+
"require": "dist/index.umd.cjs"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { MethodDefinition } from "./types";
|
|
3
|
+
import { numberedParameters, createParser } from "./index";
|
|
4
|
+
|
|
5
|
+
describe(numberedParameters.name, () => {
|
|
6
|
+
test("simple", () => {
|
|
7
|
+
const definitions = [
|
|
8
|
+
"example(0: string, 1?: string)" satisfies MethodDefinition,
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
const result = createParser(definitions)("example(z,a)");
|
|
12
|
+
expect(numberedParameters(result)).toEqual(["z", "a"]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("mixed types", () => {
|
|
16
|
+
const definitions = [
|
|
17
|
+
"example(0: string, 1?: string, 2: boolean)" satisfies MethodDefinition,
|
|
18
|
+
] as const;
|
|
19
|
+
|
|
20
|
+
const result = createParser(definitions)("example(z,a,true)");
|
|
21
|
+
expect(numberedParameters(result)).toEqual(["z", "a", true]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("mixed positions", () => {
|
|
25
|
+
const definitions = [
|
|
26
|
+
"example(0: string, hi: string, 1: boolean)" satisfies MethodDefinition,
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
const result = createParser(definitions)("example(z,hi,true)");
|
|
30
|
+
expect(numberedParameters(result)).toEqual(["z", true]);
|
|
31
|
+
});
|
|
32
|
+
});
|
package/src/api/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { MethodDefinition } from "./types";
|
|
2
|
+
import type { Parser } from "./types";
|
|
3
|
+
export { createParser } from "./utils";
|
|
4
|
+
|
|
5
|
+
export const numberedParameters = (result: ReturnType<Parser<any>>) =>
|
|
6
|
+
Object.entries(result)
|
|
7
|
+
.filter(([key]) => key !== "name" && !isNaN(Number(key)))
|
|
8
|
+
.map(([_, value]) => value);
|
package/src/api/types.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export type TypeRecord = {
|
|
2
|
+
"string": string,
|
|
3
|
+
"number": number,
|
|
4
|
+
"boolean": boolean,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
type ExpandRecursively<T> =
|
|
8
|
+
T extends (...args: infer A) => infer R
|
|
9
|
+
/**/ ? (...args: ExpandRecursively<A>) => ExpandRecursively<R>
|
|
10
|
+
/**/ : T extends object
|
|
11
|
+
/**/ ? T extends infer O
|
|
12
|
+
/**/ ? { [K in keyof O]: ExpandRecursively<O[K]> }
|
|
13
|
+
/**/ : never
|
|
14
|
+
/**/ : T;
|
|
15
|
+
|
|
16
|
+
type WithoutLeadingWhitespace<T extends string> = T extends ` ${infer Without}` ? Without : T;
|
|
17
|
+
|
|
18
|
+
type SplitOn<T extends string, Delimeter extends string> =
|
|
19
|
+
T extends `${infer Left}${Delimeter}${infer Right}` ? [Left, ...SplitOn<Right, Delimeter>] : [T];
|
|
20
|
+
|
|
21
|
+
type RepeatInTuple<Element, Count extends number, Acc extends unknown[] = []> = {
|
|
22
|
+
[k in Count]: Acc['length'] extends k
|
|
23
|
+
? Acc
|
|
24
|
+
: RepeatInTuple<Element, k, [...Acc, Element]>;
|
|
25
|
+
}[Count];
|
|
26
|
+
|
|
27
|
+
type Join<T extends string[], D extends string> =
|
|
28
|
+
T extends []
|
|
29
|
+
/**/ ? ''
|
|
30
|
+
/**/ : T extends [infer F extends string]
|
|
31
|
+
/**/ ? F
|
|
32
|
+
/**/ : T extends [infer F extends string, ...infer R extends string[]]
|
|
33
|
+
/**/ ? `${F}${D}${Join<R, D>}`
|
|
34
|
+
/**/ : string;
|
|
35
|
+
|
|
36
|
+
type Capture<T extends string, Start extends string, End extends string> = {
|
|
37
|
+
[k in T]: k extends `${infer Before}${Start}${infer Captured}${End}${infer After}`
|
|
38
|
+
/**/ ? { before: Before, captured: Captured, after: After }
|
|
39
|
+
/**/ : { before: T, captured: "", after: "" };
|
|
40
|
+
}[T]
|
|
41
|
+
|
|
42
|
+
type SplitOnComma<T extends string> = { [k in SplitOn<T, ",">[number]]: k }[SplitOn<T, ",">[number]];
|
|
43
|
+
|
|
44
|
+
type Parameter<Name extends string, Optional extends boolean, Type extends TypeRecord[keyof TypeRecord]> = { name: Name, optional: Optional, type: Type };
|
|
45
|
+
|
|
46
|
+
type ExtractParameter<T extends string> = T extends `${infer Name}?: ${infer Type extends keyof TypeRecord}`
|
|
47
|
+
/**/ ? Parameter<Name, true, Type>
|
|
48
|
+
/**/ : T extends `${infer Name}: ${infer Type extends keyof TypeRecord}`
|
|
49
|
+
/**/ ? Parameter<Name, false, Type>
|
|
50
|
+
/**/ : never;
|
|
51
|
+
|
|
52
|
+
type FunctionName<T extends string> = Capture<T, "(", ")">["before"];
|
|
53
|
+
|
|
54
|
+
type ParameterRecord<T extends string> = {
|
|
55
|
+
[k in WithoutLeadingWhitespace<SplitOnComma<Capture<T, "(", ")">["captured"]>> as ExtractParameter<k>["name"]]: ExtractParameter<k>["optional"] extends true ? TypeRecord[ExtractParameter<k>["type"]] | undefined : TypeRecord[ExtractParameter<k>["type"]]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type UndefinedToOptional<T> = {
|
|
59
|
+
[K in keyof T as undefined extends T[K] ? K : never]?: Exclude<T[K], undefined>;
|
|
60
|
+
} & {
|
|
61
|
+
[K in keyof T as undefined extends T[K] ? never : K]: T[K];
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
type ParseMethodDefinition<T extends string> = { [t in T as FunctionName<t>]: UndefinedToOptional<ParameterRecord<t>> };
|
|
65
|
+
|
|
66
|
+
type ParameterDefinition = `${string}${"?" | ""}: ${keyof TypeRecord}`;
|
|
67
|
+
/** Can add more parameters by adding more numbers to the count union, but it adds a lot of complexity to the type */
|
|
68
|
+
type ZeroOrMoreParameters = Join<RepeatInTuple<ParameterDefinition, 0 | 1 | 2>, ", ">;
|
|
69
|
+
export type MethodDefinition = `${string}(${ZeroOrMoreParameters})`;
|
|
70
|
+
|
|
71
|
+
export type MethodDefinitions = string[] | readonly string[];
|
|
72
|
+
|
|
73
|
+
export type ParsingCandidates<T extends MethodDefinitions> = ExpandRecursively<{
|
|
74
|
+
[k in keyof ParseMethodDefinition<T[number]>]: ParseMethodDefinition<T[number]>[k] & { name: k }
|
|
75
|
+
}[keyof ParseMethodDefinition<T[number]>]>;
|
|
76
|
+
|
|
77
|
+
export type Parser<T extends MethodDefinitions> = (query: string) => ParsingCandidates<T>;
|
|
78
|
+
export type CreateParser = <T extends MethodDefinitions>(definitions: T) => Parser<T>;
|