@ingcreators/annot-product-docs 0.2.0 → 0.3.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/CHANGELOG.md +190 -0
- package/README.md +37 -2
- package/dist/config.d.ts +14 -14
- package/dist/fixture.d.ts +45 -15
- package/dist/index.d.ts +4 -2
- package/dist/index.js +536 -449
- package/dist/mdx-annotations.d.ts +91 -0
- package/dist/playwright-screenshot-hook.d.ts +17 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,15 +1,432 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
1
|
+
import { annotSourceResolvers as e, test as t } from "@ingcreators/annot-playwright";
|
|
2
|
+
import { mkdir as n, readFile as r, readdir as i, writeFile as a } from "node:fs/promises";
|
|
3
|
+
import o from "js-yaml";
|
|
4
|
+
import s from "remark-frontmatter";
|
|
5
|
+
import c from "remark-mdx";
|
|
6
|
+
import l from "remark-parse";
|
|
7
|
+
import { unified as u } from "unified";
|
|
8
|
+
import { visit as d } from "unist-util-visit";
|
|
9
|
+
import { z as f } from "zod";
|
|
10
|
+
import { dirname as p, isAbsolute as ee, join as te, relative as m, resolve as h } from "node:path";
|
|
11
|
+
import { bboxAnnotationsToSvg as g } from "@ingcreators/annot-annotator";
|
|
12
|
+
//#region src/config.ts
|
|
13
|
+
var ne = f.enum([
|
|
14
|
+
"cover",
|
|
15
|
+
"history",
|
|
16
|
+
"list",
|
|
17
|
+
"screen",
|
|
18
|
+
"reference"
|
|
19
|
+
]), re = f.object({
|
|
20
|
+
book: f.string().optional(),
|
|
21
|
+
sheet: f.string().optional(),
|
|
22
|
+
sheets: f.record(f.string(), f.string()).optional(),
|
|
23
|
+
role: ne.optional(),
|
|
24
|
+
order: f.number().optional()
|
|
25
|
+
}).strict().refine((e) => !(e.sheet && e.sheets), "Specify either `xlsx.sheet` (single sheet) or `xlsx.sheets` (multi-sheet), not both."), ie = f.record(f.string(), f.unknown()), _ = f.object({
|
|
26
|
+
id: f.string().min(1, "`annot.id` is required."),
|
|
27
|
+
title: f.string().optional(),
|
|
28
|
+
purpose: f.string().optional(),
|
|
29
|
+
meta: ie.optional(),
|
|
30
|
+
xlsx: re.optional()
|
|
31
|
+
}).strict(), ae = f.object({
|
|
32
|
+
template: f.string().optional(),
|
|
33
|
+
templateSheets: f.object({
|
|
34
|
+
cover: f.string().optional(),
|
|
35
|
+
history: f.string().optional(),
|
|
36
|
+
list: f.string().optional(),
|
|
37
|
+
screen: f.string().optional(),
|
|
38
|
+
reference: f.string().optional()
|
|
39
|
+
}).strict().optional()
|
|
40
|
+
}).strict(), v = f.object({
|
|
41
|
+
meta: f.record(f.string(), f.unknown()).optional(),
|
|
42
|
+
xlsx: f.object({
|
|
43
|
+
defaultBook: f.string().optional(),
|
|
44
|
+
books: f.record(f.string(), ae).optional()
|
|
45
|
+
}).strict().optional()
|
|
46
|
+
}).strict();
|
|
47
|
+
function oe(e) {
|
|
48
|
+
let t = v.safeParse(e);
|
|
49
|
+
if (!t.success) {
|
|
50
|
+
let e = t.error.issues.map((e) => ` - ${e.path.length ? e.path.join(".") + ": " : ""}${e.message}`).join("\n");
|
|
51
|
+
throw Error(`Invalid \`annot-docs.config.ts\`:\n${e}`);
|
|
52
|
+
}
|
|
53
|
+
return t.data;
|
|
54
|
+
}
|
|
55
|
+
function se(e) {
|
|
56
|
+
return (e.xlsx?.role ?? "screen") === "screen";
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/mdx.ts
|
|
60
|
+
var y = "annot:snapshot", ce = "/annot:snapshot", b = "annot:attributes", le = "/annot:attributes", ue = /^\s*\/\*\s*([^\s*]+)\s*([\s\S]*?)\*\/\s*$/;
|
|
61
|
+
async function x(e) {
|
|
62
|
+
return S(await r(e, "utf8"), { filePath: e });
|
|
63
|
+
}
|
|
64
|
+
function S(e, t = {}) {
|
|
65
|
+
let n = u().use(l).use(s, ["yaml"]).use(c).parse(e), r = de(n, t.filePath);
|
|
66
|
+
if (!r) return null;
|
|
67
|
+
let i = [], a = [], o = [], f = [], p = {};
|
|
68
|
+
return d(n, (t) => {
|
|
69
|
+
switch (t.type) {
|
|
70
|
+
case "mdxJsxFlowElement":
|
|
71
|
+
case "mdxJsxTextElement":
|
|
72
|
+
pe(t, e, {
|
|
73
|
+
screens: i,
|
|
74
|
+
transitions: a,
|
|
75
|
+
history: o,
|
|
76
|
+
screenLists: f
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
case "mdxFlowExpression":
|
|
80
|
+
case "mdxTextExpression":
|
|
81
|
+
ye(t, p);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}), {
|
|
85
|
+
frontmatter: r,
|
|
86
|
+
screens: i,
|
|
87
|
+
transitions: a,
|
|
88
|
+
history: o,
|
|
89
|
+
screenLists: f,
|
|
90
|
+
commentBlocks: p,
|
|
91
|
+
source: e
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function de(e, t) {
|
|
95
|
+
let n = e.children.find((e) => e.type === "yaml");
|
|
96
|
+
if (!n || !n.value) return null;
|
|
97
|
+
let r = o.load(n.value);
|
|
98
|
+
if (!r || typeof r != "object") return null;
|
|
99
|
+
let i = r.annot;
|
|
100
|
+
if (!i || typeof i != "object") return null;
|
|
101
|
+
let a = _.safeParse(i);
|
|
102
|
+
if (!a.success) {
|
|
103
|
+
let e = t ? ` in ${t}` : "";
|
|
104
|
+
throw Error(`Invalid \`annot:\` frontmatter${e}:\n${fe(a.error)}`);
|
|
105
|
+
}
|
|
106
|
+
return a.data;
|
|
107
|
+
}
|
|
108
|
+
function fe(e) {
|
|
109
|
+
return e.issues.map((e) => ` - ${e.path.length ? e.path.join(".") + ": " : ""}${e.message}`).join("\n");
|
|
110
|
+
}
|
|
111
|
+
function pe(e, t, n) {
|
|
112
|
+
let r = e;
|
|
113
|
+
switch (r.name) {
|
|
114
|
+
case "Screen":
|
|
115
|
+
n.screens.push(me(r, t));
|
|
116
|
+
return;
|
|
117
|
+
case "Transition":
|
|
118
|
+
n.transitions.push(ge(r, t));
|
|
119
|
+
return;
|
|
120
|
+
case "HistoryEntry":
|
|
121
|
+
n.history.push(_e(r, t));
|
|
122
|
+
return;
|
|
123
|
+
case "ScreenList":
|
|
124
|
+
n.screenLists.push(ve(r));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function me(e, t) {
|
|
129
|
+
let n = C(e), r = [], i = (n) => {
|
|
130
|
+
let a = n;
|
|
131
|
+
if (a.name === "Overlay") {
|
|
132
|
+
r.push(he(a, t));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (!(a.name === "Screen" && a !== e)) for (let e of a.children ?? []) i(e);
|
|
136
|
+
};
|
|
137
|
+
for (let t of e.children ?? []) i(t);
|
|
138
|
+
let a = w(n.id);
|
|
139
|
+
if (!a) throw Error("<Screen> requires an `id` prop.");
|
|
140
|
+
return {
|
|
141
|
+
id: a,
|
|
142
|
+
src: w(n.src),
|
|
143
|
+
overlays: r
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function he(e, t) {
|
|
147
|
+
let n = C(e), r = n.match;
|
|
148
|
+
if (!T(r)) throw Error("<Overlay> requires a `match` prop with at least `{ role, name }`.");
|
|
149
|
+
return {
|
|
150
|
+
match: r,
|
|
151
|
+
intent: w(n.intent),
|
|
152
|
+
number: xe(n.number),
|
|
153
|
+
body: E(e, t)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function ge(e, t) {
|
|
157
|
+
let n = C(e), r = n.trigger;
|
|
158
|
+
if (!T(r)) throw Error("<Transition> requires a `trigger` prop with at least `{ role, name }`.");
|
|
159
|
+
return {
|
|
160
|
+
trigger: r,
|
|
161
|
+
on: w(n.on),
|
|
162
|
+
to: w(n.to),
|
|
163
|
+
body: E(e, t)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function _e(e, t) {
|
|
167
|
+
let n = C(e), r = w(n.version) ?? "", i = w(n.date) ?? "", a = w(n.author) ?? "";
|
|
168
|
+
if (!r || !i) throw Error("<HistoryEntry> requires `version` and `date` props.");
|
|
169
|
+
return {
|
|
170
|
+
version: r,
|
|
171
|
+
date: i,
|
|
172
|
+
author: a,
|
|
173
|
+
body: E(e, t)
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function ve(e) {
|
|
177
|
+
let t = C(e);
|
|
178
|
+
return {
|
|
179
|
+
book: w(t.book),
|
|
180
|
+
sort: w(t.sort)
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function ye(e, t) {
|
|
184
|
+
let n = e.value;
|
|
185
|
+
if (typeof n != "string") return;
|
|
186
|
+
let r = n.match(ue);
|
|
187
|
+
if (!r) return;
|
|
188
|
+
let i = (r[1] ?? "").trim(), a = (r[2] ?? "").trim();
|
|
189
|
+
switch (i) {
|
|
190
|
+
case y:
|
|
191
|
+
t.snapshot = a;
|
|
192
|
+
return;
|
|
193
|
+
case b:
|
|
194
|
+
t.attributes = a;
|
|
195
|
+
return;
|
|
196
|
+
case ce:
|
|
197
|
+
case le: return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function C(e) {
|
|
201
|
+
let t = {};
|
|
202
|
+
for (let n of e.attributes ?? []) {
|
|
203
|
+
let e = n;
|
|
204
|
+
if (e.type !== "mdxJsxAttribute" || !e.name) continue;
|
|
205
|
+
if (e.value === null || e.value === void 0) {
|
|
206
|
+
t[e.name] = !0;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (typeof e.value == "string") {
|
|
210
|
+
t[e.name] = e.value;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
let r = e.value;
|
|
214
|
+
r.type === "mdxJsxAttributeValueExpression" && typeof r.value == "string" && (t[e.name] = be(r.value));
|
|
215
|
+
}
|
|
216
|
+
return t;
|
|
217
|
+
}
|
|
218
|
+
function be(e) {
|
|
219
|
+
let t = e.trim();
|
|
220
|
+
if (t) {
|
|
221
|
+
if (t === "true") return !0;
|
|
222
|
+
if (t === "false") return !1;
|
|
223
|
+
if (t === "null") return null;
|
|
224
|
+
if (/^-?\d+(\.\d+)?$/.test(t)) return Number.parseFloat(t);
|
|
225
|
+
if (t.startsWith("\"") && t.endsWith("\"")) try {
|
|
226
|
+
return JSON.parse(t);
|
|
227
|
+
} catch {
|
|
228
|
+
return t.slice(1, -1);
|
|
229
|
+
}
|
|
230
|
+
if (t.startsWith("'") && t.endsWith("'")) {
|
|
231
|
+
let e = t.slice(1, -1), n = e.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
232
|
+
try {
|
|
233
|
+
return JSON.parse(`"${n}"`);
|
|
234
|
+
} catch {
|
|
235
|
+
return e;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (t.startsWith("{") && t.endsWith("}") || t.startsWith("[") && t.endsWith("]")) {
|
|
239
|
+
let e = t.replace(/([{,]\s*)([A-Za-z_$][A-Za-z0-9_$]*)(\s*:)/g, "$1\"$2\"$3").replace(/'([^'\\]*)'/g, "\"$1\"").replace(/,(\s*[}\]])/g, "$1");
|
|
240
|
+
try {
|
|
241
|
+
return JSON.parse(e);
|
|
242
|
+
} catch {
|
|
243
|
+
return t;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return t;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function w(e) {
|
|
250
|
+
return typeof e == "string" ? e : void 0;
|
|
251
|
+
}
|
|
252
|
+
function xe(e) {
|
|
253
|
+
return typeof e == "number" ? e : void 0;
|
|
254
|
+
}
|
|
255
|
+
function T(e) {
|
|
256
|
+
if (!e || typeof e != "object") return !1;
|
|
257
|
+
let t = e;
|
|
258
|
+
return !(typeof t.role != "string" || typeof t.name != "string" || t.under !== void 0 && !T(t.under));
|
|
259
|
+
}
|
|
260
|
+
function E(e, t) {
|
|
261
|
+
let n = e.position;
|
|
262
|
+
if (!n) return "";
|
|
263
|
+
let r = t.slice(n.start.offset, n.end.offset);
|
|
264
|
+
if (/\/>\s*$/.test(r)) return "";
|
|
265
|
+
let i = r.indexOf(">"), a = r.lastIndexOf("</");
|
|
266
|
+
return i < 0 || a < 0 || a <= i ? "" : r.slice(i + 1, a).trim();
|
|
267
|
+
}
|
|
268
|
+
function D(e, t) {
|
|
269
|
+
let n = e;
|
|
270
|
+
return t.snapshot !== void 0 && (n = O(n, y, t.snapshot)), t.attributes !== void 0 && (n = O(n, b, t.attributes)), n;
|
|
271
|
+
}
|
|
272
|
+
function O(e, t, n) {
|
|
273
|
+
let r = new RegExp(String.raw`\{\s*/\*\s*` + Ce(t) + String.raw`\s*[\s\S]*?\*/\s*\}`, "m"), i = Se(t, n);
|
|
274
|
+
return r.test(e) ? e.replace(r, i) : `${e.trimEnd()}\n\n${i}\n`;
|
|
275
|
+
}
|
|
276
|
+
function Se(e, t) {
|
|
277
|
+
let n = t.trim();
|
|
278
|
+
return n ? `{/* ${e}\n${n}\n*/}` : `{/* ${e} */}`;
|
|
279
|
+
}
|
|
280
|
+
function Ce(e) {
|
|
281
|
+
return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/fixture.ts
|
|
285
|
+
var k = [
|
|
286
|
+
"type",
|
|
287
|
+
"required",
|
|
288
|
+
"placeholder",
|
|
289
|
+
"maxlength",
|
|
290
|
+
"minlength",
|
|
291
|
+
"pattern",
|
|
292
|
+
"min",
|
|
293
|
+
"max",
|
|
294
|
+
"step",
|
|
295
|
+
"disabled",
|
|
296
|
+
"readonly",
|
|
297
|
+
"checked",
|
|
298
|
+
"aria-required",
|
|
299
|
+
"aria-disabled",
|
|
300
|
+
"aria-readonly"
|
|
301
|
+
], we = t.extend({
|
|
302
|
+
productDocs: async ({ page: e }, t) => {
|
|
303
|
+
await t({ sync: (t) => A(e, t) });
|
|
304
|
+
},
|
|
305
|
+
screen: async ({ page: e }, t) => {
|
|
306
|
+
let n = (t) => A(e, t);
|
|
307
|
+
await t({
|
|
308
|
+
sync: n,
|
|
309
|
+
capture: n
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
async function A(e, t) {
|
|
314
|
+
let n = await x(t.mdxPath);
|
|
315
|
+
if (!n) throw Error(`syncProductDocs: ${t.mdxPath} has no \`annot:\` frontmatter — refusing to write.`);
|
|
316
|
+
let r = n.screens.find((e) => e.id === t.id);
|
|
317
|
+
if (!r) throw Error(`syncProductDocs: ${t.mdxPath} has no <Screen id="${t.id}"> block.`);
|
|
318
|
+
let i = await (t.rootLocator ?? e.locator("body")).ariaSnapshot({
|
|
319
|
+
mode: "ai",
|
|
320
|
+
boxes: !0
|
|
321
|
+
}), o = await j(e, r.overlays, t.attributeWhitelist ?? k), s = D(n.source, {
|
|
322
|
+
snapshot: i.trim(),
|
|
323
|
+
attributes: o
|
|
324
|
+
});
|
|
325
|
+
await a(t.mdxPath, s, "utf8");
|
|
326
|
+
}
|
|
327
|
+
var Te = A;
|
|
328
|
+
async function j(e, t, n) {
|
|
329
|
+
let r = [];
|
|
330
|
+
for (let i of t) {
|
|
331
|
+
let t = e.getByRole(i.match.role, {
|
|
332
|
+
name: i.match.name,
|
|
333
|
+
exact: !0
|
|
334
|
+
});
|
|
335
|
+
if (await t.count() !== 1) continue;
|
|
336
|
+
let a = await t.evaluate((e, t) => {
|
|
337
|
+
let n = {};
|
|
338
|
+
for (let r of t) {
|
|
339
|
+
let t = e.getAttribute(r);
|
|
340
|
+
t !== null && (n[r] = t);
|
|
341
|
+
}
|
|
342
|
+
return n;
|
|
343
|
+
}, n), o = Object.keys(a);
|
|
344
|
+
if (o.length !== 0) {
|
|
345
|
+
r.push(`${i.match.role} "${i.match.name}":`);
|
|
346
|
+
for (let e of o) r.push(` ${e}: ${a[e]}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return r.join("\n");
|
|
350
|
+
}
|
|
351
|
+
//#endregion
|
|
352
|
+
//#region src/mdx-annotations.ts
|
|
353
|
+
function M(e) {
|
|
354
|
+
let t = [];
|
|
355
|
+
for (let n of e.split(/\r?\n/)) {
|
|
356
|
+
if (!n.trim()) continue;
|
|
357
|
+
let e = n.match(/^\s*-\s+([a-z]+)(?:\s+"([^"]*?)")?/);
|
|
358
|
+
if (!e) continue;
|
|
359
|
+
let r = e[1] ?? "", i = e[2] ?? "", a = n.match(/\[ref=(e\d+)\]/)?.[1], o = n.match(/\[box=(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)\]/);
|
|
360
|
+
!a || !o || t.push({
|
|
361
|
+
role: r,
|
|
362
|
+
name: i,
|
|
363
|
+
ref: a,
|
|
364
|
+
box: {
|
|
365
|
+
x: Number.parseFloat(o[1] ?? "0"),
|
|
366
|
+
y: Number.parseFloat(o[2] ?? "0"),
|
|
367
|
+
width: Number.parseFloat(o[3] ?? "0"),
|
|
368
|
+
height: Number.parseFloat(o[4] ?? "0")
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
return t;
|
|
373
|
+
}
|
|
374
|
+
function N(e, t, n) {
|
|
375
|
+
let r = [], i = 1;
|
|
376
|
+
for (let a of e) {
|
|
377
|
+
let e = t.find((e) => e.role === a.match.role && e.name === a.match.name);
|
|
378
|
+
if (!e) continue;
|
|
379
|
+
let o = a.number ?? i++;
|
|
380
|
+
r.push({
|
|
381
|
+
type: "numberedBadge",
|
|
382
|
+
bbox: e.box,
|
|
383
|
+
number: o,
|
|
384
|
+
placement: "auto",
|
|
385
|
+
imageWidth: n.width,
|
|
386
|
+
imageHeight: n.height,
|
|
387
|
+
intent: a.intent === "action" ? "warning" : a.intent === "required" ? "error" : "info"
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
return r;
|
|
391
|
+
}
|
|
392
|
+
async function P(e) {
|
|
393
|
+
let t = e.cwd ?? process.cwd(), n = await x(ee(e.mdxPath) ? e.mdxPath : h(t, e.mdxPath));
|
|
394
|
+
if (!n) throw Error(`resolveMdxAnnotations: ${e.mdxPath} has no \`annot:\` frontmatter — cannot resolve.`);
|
|
395
|
+
let r = n.screens.find((t) => t.id === e.screenId);
|
|
396
|
+
if (!r) throw Error(`resolveMdxAnnotations: ${e.mdxPath} has no <Screen id="${e.screenId}"> block.`);
|
|
397
|
+
let i = M(n.commentBlocks.snapshot ?? "");
|
|
398
|
+
return N(r.overlays, i, e.dims);
|
|
399
|
+
}
|
|
400
|
+
function Ee(e) {
|
|
401
|
+
return `<svg xmlns="http://www.w3.org/2000/svg">${g(e)}</svg>`;
|
|
402
|
+
}
|
|
403
|
+
function De(e) {
|
|
404
|
+
return e.length === 0 ? F() : `<svg xmlns="http://www.w3.org/2000/svg">${g(e)}</svg>`;
|
|
405
|
+
}
|
|
406
|
+
function F() {
|
|
407
|
+
return "<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>";
|
|
408
|
+
}
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/playwright-screenshot-hook.ts
|
|
411
|
+
var I = Symbol.for("@ingcreators/annot-product-docs:mdx-resolver"), L = async ({ annot: e, page: t }) => {
|
|
412
|
+
if (!e.mdx) return null;
|
|
413
|
+
let { id: n, path: r } = e.mdx;
|
|
414
|
+
return {
|
|
415
|
+
prepare: () => A(t, {
|
|
416
|
+
id: n,
|
|
417
|
+
mdxPath: r
|
|
418
|
+
}),
|
|
419
|
+
resolveAnnotations: (e) => P({
|
|
420
|
+
mdxPath: r,
|
|
421
|
+
screenId: n,
|
|
422
|
+
dims: e
|
|
423
|
+
})
|
|
424
|
+
};
|
|
425
|
+
}, R = L;
|
|
426
|
+
R[I] || (R[I] = !0, e.push(L));
|
|
427
|
+
//#endregion
|
|
11
428
|
//#region src/resolver.ts
|
|
12
|
-
function
|
|
429
|
+
function z(e) {
|
|
13
430
|
let t = [], n = e.split(/\r?\n/), r = [];
|
|
14
431
|
for (let e of n) {
|
|
15
432
|
if (!e.trim()) continue;
|
|
@@ -35,16 +452,16 @@ function g(e) {
|
|
|
35
452
|
}
|
|
36
453
|
return t;
|
|
37
454
|
}
|
|
38
|
-
async function
|
|
39
|
-
let r =
|
|
40
|
-
if (r.length === 0) return
|
|
455
|
+
async function B(e, t, n) {
|
|
456
|
+
let r = Ae(t.filter((t) => t.role === e.role && t.name === e.name), e.under);
|
|
457
|
+
if (r.length === 0) return je(e, t);
|
|
41
458
|
if (r.length > 1) return {
|
|
42
459
|
ok: !1,
|
|
43
460
|
kind: "ambiguous",
|
|
44
461
|
reason: `Multiple elements match role="${e.role}" name="${e.name}". Use \`under\` to disambiguate.`,
|
|
45
462
|
candidates: r.map((e) => `[ref=${e.ref}]`)
|
|
46
463
|
};
|
|
47
|
-
let i = r[0], a =
|
|
464
|
+
let i = r[0], a = ke(n, e), o = await a.count();
|
|
48
465
|
return o === 0 ? {
|
|
49
466
|
ok: !1,
|
|
50
467
|
kind: "live-mismatch",
|
|
@@ -59,15 +476,15 @@ async function _(e, t, n) {
|
|
|
59
476
|
entry: i
|
|
60
477
|
};
|
|
61
478
|
}
|
|
62
|
-
async function
|
|
479
|
+
async function Oe(e, t, n) {
|
|
63
480
|
let r = [];
|
|
64
481
|
for (let i of e) r.push({
|
|
65
482
|
overlay: i,
|
|
66
|
-
result: await
|
|
483
|
+
result: await B(i.match, t, n)
|
|
67
484
|
});
|
|
68
485
|
return r;
|
|
69
486
|
}
|
|
70
|
-
function
|
|
487
|
+
function ke(e, t) {
|
|
71
488
|
let n = e.getByRole(t.role, {
|
|
72
489
|
name: t.name,
|
|
73
490
|
exact: !0
|
|
@@ -80,10 +497,10 @@ function v(e, t) {
|
|
|
80
497
|
exact: !0
|
|
81
498
|
})), n;
|
|
82
499
|
}
|
|
83
|
-
function
|
|
500
|
+
function Ae(e, t) {
|
|
84
501
|
return t ? e.filter((e) => e.ancestors.some((e) => e.role === t.role && e.name === t.name)) : e;
|
|
85
502
|
}
|
|
86
|
-
function
|
|
503
|
+
function je(e, t) {
|
|
87
504
|
let n = t.filter((t) => t.name === e.name);
|
|
88
505
|
if (n.length === 1 && n[0].role !== e.role) return {
|
|
89
506
|
ok: !1,
|
|
@@ -93,7 +510,7 @@ function ne(e, t) {
|
|
|
93
510
|
};
|
|
94
511
|
let r = t.filter((t) => t.role === e.role);
|
|
95
512
|
if (r.length > 0) {
|
|
96
|
-
let t =
|
|
513
|
+
let t = Me(e.name, r.map((e) => e.name));
|
|
97
514
|
if (t) return {
|
|
98
515
|
ok: !1,
|
|
99
516
|
kind: "renamed",
|
|
@@ -107,16 +524,16 @@ function ne(e, t) {
|
|
|
107
524
|
reason: `No element with role="${e.role}" name="${e.name}".`
|
|
108
525
|
};
|
|
109
526
|
}
|
|
110
|
-
function
|
|
527
|
+
function Me(e, t) {
|
|
111
528
|
if (t.length === 0) return null;
|
|
112
|
-
let n = t[0], r =
|
|
529
|
+
let n = t[0], r = V(e, n);
|
|
113
530
|
for (let i of t.slice(1)) {
|
|
114
|
-
let t =
|
|
531
|
+
let t = V(e, i);
|
|
115
532
|
t > r && (n = i, r = t);
|
|
116
533
|
}
|
|
117
534
|
return r > .3 ? n : null;
|
|
118
535
|
}
|
|
119
|
-
function
|
|
536
|
+
function V(e, t) {
|
|
120
537
|
if (!e || !t) return 0;
|
|
121
538
|
let n = (e) => {
|
|
122
539
|
let t = /* @__PURE__ */ new Set();
|
|
@@ -130,7 +547,7 @@ function y(e, t) {
|
|
|
130
547
|
}
|
|
131
548
|
//#endregion
|
|
132
549
|
//#region src/drift.ts
|
|
133
|
-
function
|
|
550
|
+
function H(e) {
|
|
134
551
|
let { screen: t, liveSnapshot: n, storedAttributesYaml: r, freshAttributesYaml: i } = e, a = [], o = /* @__PURE__ */ new Set();
|
|
135
552
|
for (let e of t.overlays) {
|
|
136
553
|
let r = n.filter((t) => t.role === e.match.role && t.name === e.match.name);
|
|
@@ -160,7 +577,7 @@ function b(e) {
|
|
|
160
577
|
}
|
|
161
578
|
let s = n.filter((t) => t.role === e.match.role);
|
|
162
579
|
if (s.length > 0) {
|
|
163
|
-
let n =
|
|
580
|
+
let n = Ie(e.match.name, s.map((e) => e.name));
|
|
164
581
|
if (n) {
|
|
165
582
|
o.add(`${e.match.role}|${n}`), a.push({
|
|
166
583
|
severity: "warning",
|
|
@@ -183,7 +600,7 @@ function b(e) {
|
|
|
183
600
|
}
|
|
184
601
|
let s = new Set(t.overlays.map((e) => `${e.match.role}|${e.match.name}`));
|
|
185
602
|
for (let e of n) {
|
|
186
|
-
if (!
|
|
603
|
+
if (!Fe(e.role)) continue;
|
|
187
604
|
let n = `${e.role}|${e.name}`;
|
|
188
605
|
s.has(n) || o.has(n) || a.push({
|
|
189
606
|
severity: "warning",
|
|
@@ -196,22 +613,22 @@ function b(e) {
|
|
|
196
613
|
}
|
|
197
614
|
});
|
|
198
615
|
}
|
|
199
|
-
return r !== void 0 && i !== void 0 &&
|
|
616
|
+
return r !== void 0 && i !== void 0 && G(r) !== G(i) && a.push({
|
|
200
617
|
severity: "info",
|
|
201
618
|
kind: "attribute-drift",
|
|
202
619
|
screenId: t.id,
|
|
203
620
|
message: "`annot:attributes` block no longer matches live element attributes. Run `annot docs sync` to update."
|
|
204
621
|
}), a;
|
|
205
622
|
}
|
|
206
|
-
function
|
|
207
|
-
return
|
|
623
|
+
function Ne(e) {
|
|
624
|
+
return H({
|
|
208
625
|
screen: e.screen,
|
|
209
|
-
liveSnapshot:
|
|
626
|
+
liveSnapshot: z(e.liveSnapshotYaml),
|
|
210
627
|
storedAttributesYaml: e.storedAttributesYaml,
|
|
211
628
|
freshAttributesYaml: e.freshAttributesYaml
|
|
212
629
|
});
|
|
213
630
|
}
|
|
214
|
-
function
|
|
631
|
+
function U(e) {
|
|
215
632
|
let t = 0, n = 0, r = 0;
|
|
216
633
|
for (let i of e) i.severity === "error" ? t++ : i.severity === "warning" ? n++ : r++;
|
|
217
634
|
return {
|
|
@@ -220,7 +637,7 @@ function x(e) {
|
|
|
220
637
|
infos: r
|
|
221
638
|
};
|
|
222
639
|
}
|
|
223
|
-
var
|
|
640
|
+
var Pe = new Set([
|
|
224
641
|
"button",
|
|
225
642
|
"checkbox",
|
|
226
643
|
"combobox",
|
|
@@ -238,19 +655,19 @@ var ae = new Set([
|
|
|
238
655
|
"textbox",
|
|
239
656
|
"treeitem"
|
|
240
657
|
]);
|
|
241
|
-
function
|
|
242
|
-
return
|
|
658
|
+
function Fe(e) {
|
|
659
|
+
return Pe.has(e);
|
|
243
660
|
}
|
|
244
|
-
function
|
|
661
|
+
function Ie(e, t) {
|
|
245
662
|
if (t.length === 0) return null;
|
|
246
|
-
let n = t[0], r =
|
|
663
|
+
let n = t[0], r = W(e, n);
|
|
247
664
|
for (let i of t.slice(1)) {
|
|
248
|
-
let t =
|
|
665
|
+
let t = W(e, i);
|
|
249
666
|
t > r && (n = i, r = t);
|
|
250
667
|
}
|
|
251
668
|
return r > .3 ? n : null;
|
|
252
669
|
}
|
|
253
|
-
function
|
|
670
|
+
function W(e, t) {
|
|
254
671
|
if (!e || !t) return 0;
|
|
255
672
|
let n = (e) => {
|
|
256
673
|
let t = /* @__PURE__ */ new Set();
|
|
@@ -262,364 +679,34 @@ function S(e, t) {
|
|
|
262
679
|
for (let e of r) i.has(e) && a++;
|
|
263
680
|
return a / Math.max(r.size, i.size);
|
|
264
681
|
}
|
|
265
|
-
function
|
|
682
|
+
function G(e) {
|
|
266
683
|
return e.split(/\r?\n/).map((e) => e.trimEnd()).filter((e) => e.length > 0).join("\n");
|
|
267
684
|
}
|
|
268
|
-
function
|
|
685
|
+
function K(e) {
|
|
269
686
|
return e.overlays.length > 0;
|
|
270
687
|
}
|
|
271
|
-
function
|
|
272
|
-
return e.filter(
|
|
273
|
-
}
|
|
274
|
-
//#endregion
|
|
275
|
-
//#region src/config.ts
|
|
276
|
-
var E = h.enum([
|
|
277
|
-
"cover",
|
|
278
|
-
"history",
|
|
279
|
-
"list",
|
|
280
|
-
"screen",
|
|
281
|
-
"reference"
|
|
282
|
-
]), ce = h.object({
|
|
283
|
-
book: h.string().optional(),
|
|
284
|
-
sheet: h.string().optional(),
|
|
285
|
-
sheets: h.record(h.string(), h.string()).optional(),
|
|
286
|
-
role: E.optional(),
|
|
287
|
-
order: h.number().optional()
|
|
288
|
-
}).strict().refine((e) => !(e.sheet && e.sheets), "Specify either `xlsx.sheet` (single sheet) or `xlsx.sheets` (multi-sheet), not both."), le = h.record(h.string(), h.unknown()), D = h.object({
|
|
289
|
-
id: h.string().min(1, "`annot.id` is required."),
|
|
290
|
-
title: h.string().optional(),
|
|
291
|
-
purpose: h.string().optional(),
|
|
292
|
-
meta: le.optional(),
|
|
293
|
-
xlsx: ce.optional()
|
|
294
|
-
}).strict(), ue = h.object({
|
|
295
|
-
template: h.string().optional(),
|
|
296
|
-
templateSheets: h.object({
|
|
297
|
-
cover: h.string().optional(),
|
|
298
|
-
history: h.string().optional(),
|
|
299
|
-
list: h.string().optional(),
|
|
300
|
-
screen: h.string().optional(),
|
|
301
|
-
reference: h.string().optional()
|
|
302
|
-
}).strict().optional()
|
|
303
|
-
}).strict(), O = h.object({
|
|
304
|
-
meta: h.record(h.string(), h.unknown()).optional(),
|
|
305
|
-
xlsx: h.object({
|
|
306
|
-
defaultBook: h.string().optional(),
|
|
307
|
-
books: h.record(h.string(), ue).optional()
|
|
308
|
-
}).strict().optional()
|
|
309
|
-
}).strict();
|
|
310
|
-
function de(e) {
|
|
311
|
-
let t = O.safeParse(e);
|
|
312
|
-
if (!t.success) {
|
|
313
|
-
let e = t.error.issues.map((e) => ` - ${e.path.length ? e.path.join(".") + ": " : ""}${e.message}`).join("\n");
|
|
314
|
-
throw Error(`Invalid \`annot-docs.config.ts\`:\n${e}`);
|
|
315
|
-
}
|
|
316
|
-
return t.data;
|
|
317
|
-
}
|
|
318
|
-
function fe(e) {
|
|
319
|
-
return (e.xlsx?.role ?? "screen") === "screen";
|
|
320
|
-
}
|
|
321
|
-
//#endregion
|
|
322
|
-
//#region src/mdx.ts
|
|
323
|
-
var k = "annot:snapshot", pe = "/annot:snapshot", A = "annot:attributes", me = "/annot:attributes", he = /^\s*\/\*\s*([^\s*]+)\s*([\s\S]*?)\*\/\s*$/;
|
|
324
|
-
async function j(e) {
|
|
325
|
-
return M(await t(e, "utf8"), { filePath: e });
|
|
326
|
-
}
|
|
327
|
-
function M(e, t = {}) {
|
|
328
|
-
let n = p().use(f).use(u, ["yaml"]).use(d).parse(e), r = N(n, t.filePath);
|
|
329
|
-
if (!r) return null;
|
|
330
|
-
let i = [], a = [], o = [], s = [], c = {};
|
|
331
|
-
return m(n, (t) => {
|
|
332
|
-
switch (t.type) {
|
|
333
|
-
case "mdxJsxFlowElement":
|
|
334
|
-
case "mdxJsxTextElement":
|
|
335
|
-
F(t, e, {
|
|
336
|
-
screens: i,
|
|
337
|
-
transitions: a,
|
|
338
|
-
history: o,
|
|
339
|
-
screenLists: s
|
|
340
|
-
});
|
|
341
|
-
return;
|
|
342
|
-
case "mdxFlowExpression":
|
|
343
|
-
case "mdxTextExpression":
|
|
344
|
-
_e(t, c);
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
}), {
|
|
348
|
-
frontmatter: r,
|
|
349
|
-
screens: i,
|
|
350
|
-
transitions: a,
|
|
351
|
-
history: o,
|
|
352
|
-
screenLists: s,
|
|
353
|
-
commentBlocks: c,
|
|
354
|
-
source: e
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
function N(e, t) {
|
|
358
|
-
let n = e.children.find((e) => e.type === "yaml");
|
|
359
|
-
if (!n || !n.value) return null;
|
|
360
|
-
let r = l.load(n.value);
|
|
361
|
-
if (!r || typeof r != "object") return null;
|
|
362
|
-
let i = r.annot;
|
|
363
|
-
if (!i || typeof i != "object") return null;
|
|
364
|
-
let a = D.safeParse(i);
|
|
365
|
-
if (!a.success) {
|
|
366
|
-
let e = t ? ` in ${t}` : "";
|
|
367
|
-
throw Error(`Invalid \`annot:\` frontmatter${e}:\n${P(a.error)}`);
|
|
368
|
-
}
|
|
369
|
-
return a.data;
|
|
370
|
-
}
|
|
371
|
-
function P(e) {
|
|
372
|
-
return e.issues.map((e) => ` - ${e.path.length ? e.path.join(".") + ": " : ""}${e.message}`).join("\n");
|
|
373
|
-
}
|
|
374
|
-
function F(e, t, n) {
|
|
375
|
-
let r = e;
|
|
376
|
-
switch (r.name) {
|
|
377
|
-
case "Screen":
|
|
378
|
-
n.screens.push(I(r, t));
|
|
379
|
-
return;
|
|
380
|
-
case "Transition":
|
|
381
|
-
n.transitions.push(R(r, t));
|
|
382
|
-
return;
|
|
383
|
-
case "HistoryEntry":
|
|
384
|
-
n.history.push(ge(r, t));
|
|
385
|
-
return;
|
|
386
|
-
case "ScreenList":
|
|
387
|
-
n.screenLists.push(z(r));
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
function I(e, t) {
|
|
392
|
-
let n = B(e), r = [], i = (n) => {
|
|
393
|
-
let a = n;
|
|
394
|
-
if (a.name === "Overlay") {
|
|
395
|
-
r.push(L(a, t));
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
if (!(a.name === "Screen" && a !== e)) for (let e of a.children ?? []) i(e);
|
|
399
|
-
};
|
|
400
|
-
for (let t of e.children ?? []) i(t);
|
|
401
|
-
let a = V(n.id);
|
|
402
|
-
if (!a) throw Error("<Screen> requires an `id` prop.");
|
|
403
|
-
return {
|
|
404
|
-
id: a,
|
|
405
|
-
src: V(n.src),
|
|
406
|
-
overlays: r
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
function L(e, t) {
|
|
410
|
-
let n = B(e), r = n.match;
|
|
411
|
-
if (!H(r)) throw Error("<Overlay> requires a `match` prop with at least `{ role, name }`.");
|
|
412
|
-
return {
|
|
413
|
-
match: r,
|
|
414
|
-
intent: V(n.intent),
|
|
415
|
-
number: ye(n.number),
|
|
416
|
-
body: U(e, t)
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
function R(e, t) {
|
|
420
|
-
let n = B(e), r = n.trigger;
|
|
421
|
-
if (!H(r)) throw Error("<Transition> requires a `trigger` prop with at least `{ role, name }`.");
|
|
422
|
-
return {
|
|
423
|
-
trigger: r,
|
|
424
|
-
on: V(n.on),
|
|
425
|
-
to: V(n.to),
|
|
426
|
-
body: U(e, t)
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
function ge(e, t) {
|
|
430
|
-
let n = B(e), r = V(n.version) ?? "", i = V(n.date) ?? "", a = V(n.author) ?? "";
|
|
431
|
-
if (!r || !i) throw Error("<HistoryEntry> requires `version` and `date` props.");
|
|
432
|
-
return {
|
|
433
|
-
version: r,
|
|
434
|
-
date: i,
|
|
435
|
-
author: a,
|
|
436
|
-
body: U(e, t)
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
function z(e) {
|
|
440
|
-
let t = B(e);
|
|
441
|
-
return {
|
|
442
|
-
book: V(t.book),
|
|
443
|
-
sort: V(t.sort)
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
function _e(e, t) {
|
|
447
|
-
let n = e.value;
|
|
448
|
-
if (typeof n != "string") return;
|
|
449
|
-
let r = n.match(he);
|
|
450
|
-
if (!r) return;
|
|
451
|
-
let i = (r[1] ?? "").trim(), a = (r[2] ?? "").trim();
|
|
452
|
-
switch (i) {
|
|
453
|
-
case k:
|
|
454
|
-
t.snapshot = a;
|
|
455
|
-
return;
|
|
456
|
-
case A:
|
|
457
|
-
t.attributes = a;
|
|
458
|
-
return;
|
|
459
|
-
case pe:
|
|
460
|
-
case me: return;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
function B(e) {
|
|
464
|
-
let t = {};
|
|
465
|
-
for (let n of e.attributes ?? []) {
|
|
466
|
-
let e = n;
|
|
467
|
-
if (e.type !== "mdxJsxAttribute" || !e.name) continue;
|
|
468
|
-
if (e.value === null || e.value === void 0) {
|
|
469
|
-
t[e.name] = !0;
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
if (typeof e.value == "string") {
|
|
473
|
-
t[e.name] = e.value;
|
|
474
|
-
continue;
|
|
475
|
-
}
|
|
476
|
-
let r = e.value;
|
|
477
|
-
r.type === "mdxJsxAttributeValueExpression" && typeof r.value == "string" && (t[e.name] = ve(r.value));
|
|
478
|
-
}
|
|
479
|
-
return t;
|
|
480
|
-
}
|
|
481
|
-
function ve(e) {
|
|
482
|
-
let t = e.trim();
|
|
483
|
-
if (t) {
|
|
484
|
-
if (t === "true") return !0;
|
|
485
|
-
if (t === "false") return !1;
|
|
486
|
-
if (t === "null") return null;
|
|
487
|
-
if (/^-?\d+(\.\d+)?$/.test(t)) return Number.parseFloat(t);
|
|
488
|
-
if (t.startsWith("\"") && t.endsWith("\"")) try {
|
|
489
|
-
return JSON.parse(t);
|
|
490
|
-
} catch {
|
|
491
|
-
return t.slice(1, -1);
|
|
492
|
-
}
|
|
493
|
-
if (t.startsWith("'") && t.endsWith("'")) {
|
|
494
|
-
let e = t.slice(1, -1), n = e.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
495
|
-
try {
|
|
496
|
-
return JSON.parse(`"${n}"`);
|
|
497
|
-
} catch {
|
|
498
|
-
return e;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
if (t.startsWith("{") && t.endsWith("}") || t.startsWith("[") && t.endsWith("]")) {
|
|
502
|
-
let e = t.replace(/([{,]\s*)([A-Za-z_$][A-Za-z0-9_$]*)(\s*:)/g, "$1\"$2\"$3").replace(/'([^'\\]*)'/g, "\"$1\"").replace(/,(\s*[}\]])/g, "$1");
|
|
503
|
-
try {
|
|
504
|
-
return JSON.parse(e);
|
|
505
|
-
} catch {
|
|
506
|
-
return t;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
return t;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
function V(e) {
|
|
513
|
-
return typeof e == "string" ? e : void 0;
|
|
514
|
-
}
|
|
515
|
-
function ye(e) {
|
|
516
|
-
return typeof e == "number" ? e : void 0;
|
|
517
|
-
}
|
|
518
|
-
function H(e) {
|
|
519
|
-
if (!e || typeof e != "object") return !1;
|
|
520
|
-
let t = e;
|
|
521
|
-
return !(typeof t.role != "string" || typeof t.name != "string" || t.under !== void 0 && !H(t.under));
|
|
522
|
-
}
|
|
523
|
-
function U(e, t) {
|
|
524
|
-
let n = e.position;
|
|
525
|
-
if (!n) return "";
|
|
526
|
-
let r = t.slice(n.start.offset, n.end.offset);
|
|
527
|
-
if (/\/>\s*$/.test(r)) return "";
|
|
528
|
-
let i = r.indexOf(">"), a = r.lastIndexOf("</");
|
|
529
|
-
return i < 0 || a < 0 || a <= i ? "" : r.slice(i + 1, a).trim();
|
|
530
|
-
}
|
|
531
|
-
function W(e, t) {
|
|
532
|
-
let n = e;
|
|
533
|
-
return t.snapshot !== void 0 && (n = G(n, k, t.snapshot)), t.attributes !== void 0 && (n = G(n, A, t.attributes)), n;
|
|
534
|
-
}
|
|
535
|
-
function G(e, t, n) {
|
|
536
|
-
let r = new RegExp(String.raw`\{\s*/\*\s*` + xe(t) + String.raw`\s*[\s\S]*?\*/\s*\}`, "m"), i = be(t, n);
|
|
537
|
-
return r.test(e) ? e.replace(r, i) : `${e.trimEnd()}\n\n${i}\n`;
|
|
538
|
-
}
|
|
539
|
-
function be(e, t) {
|
|
540
|
-
let n = t.trim();
|
|
541
|
-
return n ? `{/* ${e}\n${n}\n*/}` : `{/* ${e} */}`;
|
|
542
|
-
}
|
|
543
|
-
function xe(e) {
|
|
544
|
-
return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
545
|
-
}
|
|
546
|
-
//#endregion
|
|
547
|
-
//#region src/fixture.ts
|
|
548
|
-
var K = [
|
|
549
|
-
"type",
|
|
550
|
-
"required",
|
|
551
|
-
"placeholder",
|
|
552
|
-
"maxlength",
|
|
553
|
-
"minlength",
|
|
554
|
-
"pattern",
|
|
555
|
-
"min",
|
|
556
|
-
"max",
|
|
557
|
-
"step",
|
|
558
|
-
"disabled",
|
|
559
|
-
"readonly",
|
|
560
|
-
"checked",
|
|
561
|
-
"aria-required",
|
|
562
|
-
"aria-disabled",
|
|
563
|
-
"aria-readonly"
|
|
564
|
-
], Se = c.extend({ screen: async ({ page: e }, t) => {
|
|
565
|
-
await t({ capture: (t) => q(e, t) });
|
|
566
|
-
} });
|
|
567
|
-
async function q(e, t) {
|
|
568
|
-
let n = await j(t.mdxPath);
|
|
569
|
-
if (!n) throw Error(`captureScreen: ${t.mdxPath} has no \`annot:\` frontmatter — refusing to write.`);
|
|
570
|
-
let i = n.screens.find((e) => e.id === t.id);
|
|
571
|
-
if (!i) throw Error(`captureScreen: ${t.mdxPath} has no <Screen id="${t.id}"> block.`);
|
|
572
|
-
let a = await (t.rootLocator ?? e.locator("body")).ariaSnapshot({
|
|
573
|
-
mode: "ai",
|
|
574
|
-
boxes: !0
|
|
575
|
-
}), o = await J(e, i.overlays, t.attributeWhitelist ?? K), s = W(n.source, {
|
|
576
|
-
snapshot: a.trim(),
|
|
577
|
-
attributes: o
|
|
578
|
-
});
|
|
579
|
-
await r(t.mdxPath, s, "utf8");
|
|
580
|
-
}
|
|
581
|
-
async function J(e, t, n) {
|
|
582
|
-
let r = [];
|
|
583
|
-
for (let i of t) {
|
|
584
|
-
let t = e.getByRole(i.match.role, {
|
|
585
|
-
name: i.match.name,
|
|
586
|
-
exact: !0
|
|
587
|
-
});
|
|
588
|
-
if (await t.count() !== 1) continue;
|
|
589
|
-
let a = await t.evaluate((e, t) => {
|
|
590
|
-
let n = {};
|
|
591
|
-
for (let r of t) {
|
|
592
|
-
let t = e.getAttribute(r);
|
|
593
|
-
t !== null && (n[r] = t);
|
|
594
|
-
}
|
|
595
|
-
return n;
|
|
596
|
-
}, n), o = Object.keys(a);
|
|
597
|
-
if (o.length !== 0) {
|
|
598
|
-
r.push(`${i.match.role} "${i.match.name}":`);
|
|
599
|
-
for (let e of o) r.push(` ${e}: ${a[e]}`);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
return r.join("\n");
|
|
688
|
+
function q(e) {
|
|
689
|
+
return e.filter(K);
|
|
603
690
|
}
|
|
604
691
|
//#endregion
|
|
605
692
|
//#region src/cli.ts
|
|
606
|
-
async function
|
|
693
|
+
async function Le(e, t = {}) {
|
|
607
694
|
let n = t.stdout ?? ((e) => process.stdout.write(`${e}\n`)), r = t.stderr ?? ((e) => process.stderr.write(`${e}\n`)), i = t.cwd ?? process.cwd(), [, , a, ...o] = e;
|
|
608
|
-
if (a !== "init" && a !== "sync" && a !== "lint") return r(
|
|
695
|
+
if (a !== "init" && a !== "sync" && a !== "lint") return r(Re), +!!a;
|
|
609
696
|
try {
|
|
610
697
|
switch (a) {
|
|
611
|
-
case "init": return await
|
|
698
|
+
case "init": return await ze(o, {
|
|
612
699
|
cwd: i,
|
|
613
700
|
stdout: n,
|
|
614
701
|
stderr: r
|
|
615
702
|
});
|
|
616
|
-
case "sync": return await
|
|
703
|
+
case "sync": return await He(o, {
|
|
617
704
|
...t,
|
|
618
705
|
cwd: i,
|
|
619
706
|
stdout: n,
|
|
620
707
|
stderr: r
|
|
621
708
|
});
|
|
622
|
-
case "lint": return await
|
|
709
|
+
case "lint": return await Ue(o, {
|
|
623
710
|
...t,
|
|
624
711
|
cwd: i,
|
|
625
712
|
stdout: n,
|
|
@@ -630,7 +717,7 @@ async function Ce(e, t = {}) {
|
|
|
630
717
|
return r(`annot docs ${a}: ${e.message}`), 1;
|
|
631
718
|
}
|
|
632
719
|
}
|
|
633
|
-
var
|
|
720
|
+
var Re = [
|
|
634
721
|
"annot docs <command> [options]",
|
|
635
722
|
"",
|
|
636
723
|
"Commands:",
|
|
@@ -643,71 +730,71 @@ var we = [
|
|
|
643
730
|
" --root <dir> Override MDX search root (default: docs/)",
|
|
644
731
|
" --help Show this help"
|
|
645
732
|
].join("\n");
|
|
646
|
-
async function
|
|
647
|
-
let { cwd:
|
|
733
|
+
async function ze(e, t) {
|
|
734
|
+
let { cwd: r, stdout: i } = t, o = [
|
|
648
735
|
{
|
|
649
736
|
path: "annot-docs.config.ts",
|
|
650
|
-
content:
|
|
737
|
+
content: Be
|
|
651
738
|
},
|
|
652
739
|
{
|
|
653
740
|
path: "tests/docs/example.spec.ts",
|
|
654
|
-
content:
|
|
741
|
+
content: J
|
|
655
742
|
},
|
|
656
743
|
{
|
|
657
744
|
path: "docs/books/example/SC-001-login.mdx",
|
|
658
|
-
content:
|
|
745
|
+
content: Ve
|
|
659
746
|
}
|
|
660
|
-
],
|
|
661
|
-
for (let
|
|
662
|
-
let
|
|
747
|
+
], s = 0;
|
|
748
|
+
for (let e of o) {
|
|
749
|
+
let t = h(r, e.path);
|
|
663
750
|
try {
|
|
664
|
-
await
|
|
665
|
-
} catch (
|
|
666
|
-
if (
|
|
667
|
-
else throw
|
|
751
|
+
await n(p(t), { recursive: !0 }), await a(t, e.content, { flag: "wx" }), i(` created ${e.path}`), s++;
|
|
752
|
+
} catch (t) {
|
|
753
|
+
if (t.code === "EEXIST") i(` exists ${e.path} (skipped)`);
|
|
754
|
+
else throw t;
|
|
668
755
|
}
|
|
669
756
|
}
|
|
670
|
-
return
|
|
757
|
+
return i(s === 0 ? "annot docs init: nothing to do (all targets exist)" : `annot docs init: wrote ${s} file(s)`), 0;
|
|
671
758
|
}
|
|
672
|
-
var
|
|
673
|
-
async function
|
|
759
|
+
var Be = "// Living product docs config for annot.\n// See https://github.com/ingcreators/annot/blob/main/docs/plans/living-product-docs.md\nimport { defineConfig } from \"@ingcreators/annot-product-docs\";\n\nexport default defineConfig({\n meta: {\n projectName: \"Example\",\n },\n xlsx: {\n defaultBook: \"Screen spec\",\n books: {\n \"Screen spec\": {\n // Drop your customer-supplied template here (Phase 3) and\n // the Excel adapter will fill it. Until then the OSS\n // default layout applies.\n // template: \"./templates/customer-screen-spec.xlsx\",\n },\n },\n },\n});\n", J = "// Tour file — runs through every screen and refreshes the\n// matching MDX's annot:snapshot / annot:attributes blocks.\n//\n// Run with: pnpm playwright test tests/docs/\n\nimport { test } from \"@ingcreators/annot-product-docs\";\n\ntest.describe.configure({ mode: \"serial\" });\n\ntest(\"login flow\", async ({ page, productDocs }) => {\n await page.goto(\"/login\");\n await productDocs.sync({\n id: \"login\",\n mdxPath: \"docs/books/example/SC-001-login.mdx\",\n });\n});\n", Ve = "---\nannot:\n id: SC-001\n title: Login screen\n meta:\n author: TODO\n xlsx:\n book: Screen spec\n sheet: SC-001 Login\n role: screen\n order: 100\n---\n\nimport { Screen, Overlay } from \"@ingcreators/annot-product-docs-astro\";\n\n# Login screen\n\nEnter your credentials to access the system.\n\n<Screen id=\"login\" src=\"./shots/login.png\">\n\n<Overlay match={{ role: \"textbox\", name: \"Email\" }} intent=\"required\" number={1}>\n**Email** — Enter your registered email address.\n</Overlay>\n\n<Overlay match={{ role: \"button\", name: \"Sign in\" }} intent=\"action\" number={2}>\nClick to sign in.\n</Overlay>\n\n</Screen>\n";
|
|
760
|
+
async function He(e, t) {
|
|
674
761
|
let { cwd: n, stdout: r, stderr: i } = t, a = Z(e);
|
|
675
762
|
if (!a.url) return i("annot docs sync: --url <baseUrl> is required."), 1;
|
|
676
|
-
let
|
|
677
|
-
if (
|
|
678
|
-
r(`annot docs sync: ${
|
|
679
|
-
let { newPage:
|
|
763
|
+
let o = await $(await Q(h(n, a.root ?? "docs")));
|
|
764
|
+
if (o.length === 0) return r(`annot docs sync: no MDX files with \`annot:\` frontmatter under ${a.root ?? "docs"}.`), 0;
|
|
765
|
+
r(`annot docs sync: ${o.length} MDX file(s) → ${a.url}`);
|
|
766
|
+
let { newPage: s, close: c } = await Y(a.url, t);
|
|
680
767
|
try {
|
|
681
|
-
for (let e of
|
|
682
|
-
let t = await
|
|
768
|
+
for (let e of o) {
|
|
769
|
+
let t = await x(e);
|
|
683
770
|
if (!t) continue;
|
|
684
|
-
let i =
|
|
771
|
+
let i = q(t.screens);
|
|
685
772
|
if (i.length === 0) {
|
|
686
|
-
r(` skip ${
|
|
773
|
+
r(` skip ${m(n, e)} (no <Screen> blocks)`);
|
|
687
774
|
continue;
|
|
688
775
|
}
|
|
689
776
|
for (let t of i) {
|
|
690
|
-
let { page: i, dispose: a } = await
|
|
777
|
+
let { page: i, dispose: a } = await s(t.src ?? "/");
|
|
691
778
|
try {
|
|
692
|
-
await
|
|
779
|
+
await A(i, {
|
|
693
780
|
id: t.id,
|
|
694
781
|
mdxPath: e
|
|
695
|
-
}), r(` synced ${
|
|
782
|
+
}), r(` synced ${m(n, e)} (screen=${t.id})`);
|
|
696
783
|
} finally {
|
|
697
784
|
await a();
|
|
698
785
|
}
|
|
699
786
|
}
|
|
700
787
|
}
|
|
701
788
|
} finally {
|
|
702
|
-
await
|
|
789
|
+
await c();
|
|
703
790
|
}
|
|
704
791
|
return 0;
|
|
705
792
|
}
|
|
706
|
-
async function
|
|
793
|
+
async function Ue(e, t) {
|
|
707
794
|
let { cwd: n, stdout: r, stderr: i } = t, a = Z(e);
|
|
708
795
|
if (!a.url) return i("annot docs lint: --url <baseUrl> is required."), 1;
|
|
709
|
-
let
|
|
710
|
-
if (
|
|
796
|
+
let o = await $(await Q(h(n, a.root ?? "docs")));
|
|
797
|
+
if (o.length === 0) return a.json ? r(JSON.stringify({
|
|
711
798
|
findings: [],
|
|
712
799
|
summary: {
|
|
713
800
|
errors: 0,
|
|
@@ -715,34 +802,34 @@ async function Ae(e, t) {
|
|
|
715
802
|
infos: 0
|
|
716
803
|
}
|
|
717
804
|
})) : r(`annot docs lint: no MDX files with \`annot:\` frontmatter under ${a.root ?? "docs"}.`), 0;
|
|
718
|
-
a.json || r(`annot docs lint: ${
|
|
719
|
-
let
|
|
805
|
+
a.json || r(`annot docs lint: ${o.length} MDX file(s) → ${a.url}`);
|
|
806
|
+
let s = [], c = /* @__PURE__ */ new Set(), { newPage: l, close: u } = await Y(a.url, t);
|
|
720
807
|
try {
|
|
721
|
-
for (let e of
|
|
722
|
-
let t = await
|
|
723
|
-
if (t) for (let r of
|
|
724
|
-
let { page: t, dispose: i } = await
|
|
808
|
+
for (let e of o) {
|
|
809
|
+
let t = await x(e);
|
|
810
|
+
if (t) for (let r of q(t.screens)) {
|
|
811
|
+
let { page: t, dispose: i } = await l(r.src ?? "/");
|
|
725
812
|
try {
|
|
726
|
-
let i =
|
|
813
|
+
let i = H({
|
|
727
814
|
screen: r,
|
|
728
|
-
liveSnapshot:
|
|
815
|
+
liveSnapshot: z(await t.locator("body").ariaSnapshot({ mode: "ai" }))
|
|
729
816
|
});
|
|
730
|
-
for (let t of i)
|
|
731
|
-
file:
|
|
817
|
+
for (let t of i) s.push({
|
|
818
|
+
file: m(n, e),
|
|
732
819
|
finding: t
|
|
733
|
-
}),
|
|
820
|
+
}), c.add(e);
|
|
734
821
|
} finally {
|
|
735
822
|
await i();
|
|
736
823
|
}
|
|
737
824
|
}
|
|
738
825
|
}
|
|
739
826
|
} finally {
|
|
740
|
-
await
|
|
827
|
+
await u();
|
|
741
828
|
}
|
|
742
829
|
if (a.json) {
|
|
743
|
-
let e =
|
|
830
|
+
let e = U(s.map((e) => e.finding));
|
|
744
831
|
r(JSON.stringify({
|
|
745
|
-
findings:
|
|
832
|
+
findings: s.map(({ file: e, finding: t }) => ({
|
|
746
833
|
file: e,
|
|
747
834
|
severity: t.severity,
|
|
748
835
|
kind: t.kind,
|
|
@@ -754,23 +841,23 @@ async function Ae(e, t) {
|
|
|
754
841
|
summary: e
|
|
755
842
|
}));
|
|
756
843
|
} else {
|
|
757
|
-
for (let { file: e, finding: t } of
|
|
758
|
-
let e =
|
|
844
|
+
for (let { file: e, finding: t } of s) r(`${We(t.severity)} ${e} [${t.screenId}] ${t.kind}: ${t.message}`);
|
|
845
|
+
let e = U(s.map((e) => e.finding));
|
|
759
846
|
r(`annot docs lint: ${e.errors} error(s), ${e.warnings} warning(s), ${e.infos} info(s).`);
|
|
760
847
|
}
|
|
761
|
-
let
|
|
762
|
-
if (a.fix &&
|
|
848
|
+
let d = 0;
|
|
849
|
+
if (a.fix && c.size > 0) {
|
|
763
850
|
let { newPage: e, close: n } = await Y(a.url, t);
|
|
764
851
|
try {
|
|
765
|
-
for (let t of
|
|
766
|
-
let n = await
|
|
767
|
-
if (n) for (let r of
|
|
852
|
+
for (let t of c) {
|
|
853
|
+
let n = await x(t);
|
|
854
|
+
if (n) for (let r of q(n.screens)) {
|
|
768
855
|
let { page: n, dispose: i } = await e(r.src ?? "/");
|
|
769
856
|
try {
|
|
770
|
-
await
|
|
857
|
+
await A(n, {
|
|
771
858
|
id: r.id,
|
|
772
859
|
mdxPath: t
|
|
773
|
-
}),
|
|
860
|
+
}), d++;
|
|
774
861
|
} finally {
|
|
775
862
|
await i();
|
|
776
863
|
}
|
|
@@ -779,10 +866,10 @@ async function Ae(e, t) {
|
|
|
779
866
|
} finally {
|
|
780
867
|
await n();
|
|
781
868
|
}
|
|
782
|
-
a.json || r(`annot docs lint --fix: refreshed snapshot/attributes for ${
|
|
869
|
+
a.json || r(`annot docs lint --fix: refreshed snapshot/attributes for ${d} screen(s).`);
|
|
783
870
|
}
|
|
784
|
-
let
|
|
785
|
-
return a.ci ? +(
|
|
871
|
+
let f = U(s.map((e) => e.finding));
|
|
872
|
+
return a.ci ? +(f.errors + f.warnings > 0) : +(f.errors > 0);
|
|
786
873
|
}
|
|
787
874
|
async function Y(e, t) {
|
|
788
875
|
if (t.newPage) return {
|
|
@@ -823,21 +910,21 @@ function Z(e) {
|
|
|
823
910
|
return t;
|
|
824
911
|
}
|
|
825
912
|
async function Q(e) {
|
|
826
|
-
let t = [],
|
|
827
|
-
for (;
|
|
828
|
-
let e =
|
|
913
|
+
let t = [], n = [e];
|
|
914
|
+
for (; n.length > 0;) {
|
|
915
|
+
let e = n.pop(), r;
|
|
829
916
|
try {
|
|
830
|
-
|
|
917
|
+
r = await i(e, { withFileTypes: !0 });
|
|
831
918
|
} catch (e) {
|
|
832
919
|
if (e.code === "ENOENT") continue;
|
|
833
920
|
throw e;
|
|
834
921
|
}
|
|
835
|
-
for (let
|
|
836
|
-
let
|
|
837
|
-
if (
|
|
838
|
-
if (
|
|
839
|
-
|
|
840
|
-
} else
|
|
922
|
+
for (let i of r) {
|
|
923
|
+
let r = te(e, i.name);
|
|
924
|
+
if (i.isDirectory()) {
|
|
925
|
+
if (i.name === "node_modules" || i.name.startsWith(".")) continue;
|
|
926
|
+
n.push(r);
|
|
927
|
+
} else i.isFile() && i.name.endsWith(".mdx") && t.push(r);
|
|
841
928
|
}
|
|
842
929
|
}
|
|
843
930
|
return t.sort();
|
|
@@ -845,11 +932,11 @@ async function Q(e) {
|
|
|
845
932
|
async function $(e) {
|
|
846
933
|
let t = [];
|
|
847
934
|
for (let n of e) try {
|
|
848
|
-
await
|
|
935
|
+
await x(n) && t.push(n);
|
|
849
936
|
} catch {}
|
|
850
937
|
return t;
|
|
851
938
|
}
|
|
852
|
-
function
|
|
939
|
+
function We(e) {
|
|
853
940
|
switch (e) {
|
|
854
941
|
case "error": return "ERROR ";
|
|
855
942
|
case "warning": return "WARN ";
|
|
@@ -857,4 +944,4 @@ function je(e) {
|
|
|
857
944
|
}
|
|
858
945
|
}
|
|
859
946
|
//#endregion
|
|
860
|
-
export {
|
|
947
|
+
export { k as DEFAULT_ATTR_WHITELIST, v as annotDocsConfigSchema, _ as annotFrontmatterSchema, N as buildBadgeAnnotations, Te as captureScreen, j as collectAttributesYaml, oe as defineConfig, H as detectDrift, Ne as detectDriftFromYaml, F as emptyAnnotationsSvg, $ as filterAnnotMdxFiles, K as isLintableScreen, se as isScreenRole, q as lintableScreens, Le as main, S as parseMdx, x as parseMdxFile, z as parseSnapshot, M as parseSnapshotBoxes, B as resolveMatch, P as resolveMdxAnnotations, Oe as resolveOverlays, U as summariseDrift, Ee as svgFromBadges, De as svgFromBboxAnnotations, A as syncProductDocs, we as test, D as updateCommentBlocks, Q as walkMdx };
|