@vizejs/vite-plugin-musea 0.429.1 → 0.430.1

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.
Files changed (68) hide show
  1. package/dist/a11y/index.d.mts +1 -1
  2. package/dist/a11y/index.mjs +1 -1
  3. package/dist/{a11y-Ci9EEv6F.mjs → a11y-BbFK942j.mjs} +66 -15
  4. package/dist/a11y-BbFK942j.mjs.map +1 -0
  5. package/dist/api-gMwahZKm.d.mts +112 -0
  6. package/dist/api-gMwahZKm.d.mts.map +1 -0
  7. package/dist/cli/index.d.mts +1 -1
  8. package/dist/cli/index.d.mts.map +1 -1
  9. package/dist/cli/index.mjs +79 -33
  10. package/dist/cli/index.mjs.map +1 -1
  11. package/dist/gallery/assets/{MonacoEditor-BIjYdDrA.js → MonacoEditor-CnzIyKMy.js} +2 -2
  12. package/dist/gallery/assets/{cssMode-C5PB9d_X.js → cssMode-DZ_me5CG.js} +1 -1
  13. package/dist/gallery/assets/{editor.api-tyf2wJeZ.js → editor.api-CjD4Hpms.js} +1 -1
  14. package/dist/gallery/assets/{freemarker2-Df2SP4C4.js → freemarker2-F7z49Hfe.js} +1 -1
  15. package/dist/gallery/assets/{handlebars-CbFr-5KQ.js → handlebars-Dt4eGgf-.js} +1 -1
  16. package/dist/gallery/assets/{html-C1ubjnt5.js → html-B1zmbcur.js} +1 -1
  17. package/dist/gallery/assets/{htmlMode-9FYaGqmo.js → htmlMode-DmDwpA4v.js} +1 -1
  18. package/dist/gallery/assets/{index-D81pMC9f.css → index-C4vm-yqv.css} +1 -1
  19. package/dist/gallery/assets/{index-BjW1cVGf.js → index-vJ3ftuqF.js} +3 -3
  20. package/dist/gallery/assets/{javascript-DjzYR9ls.js → javascript-CLKbyvz8.js} +1 -1
  21. package/dist/gallery/assets/{jsonMode-BRxSdIjW.js → jsonMode-sms9-L2I.js} +1 -1
  22. package/dist/gallery/assets/{liquid-DD0LqlcF.js → liquid-CesACmVT.js} +1 -1
  23. package/dist/gallery/assets/{lspLanguageFeatures-C0f4qlAU.js → lspLanguageFeatures-C8BR-41u.js} +1 -1
  24. package/dist/gallery/assets/{mdx-DtcWR2Lx.js → mdx-Cd93KUo_.js} +1 -1
  25. package/dist/gallery/assets/{python-C1OlrT9L.js → python-8gi7tgMv.js} +1 -1
  26. package/dist/gallery/assets/{razor-BN2Zj1B9.js → razor-sOyhBXSy.js} +1 -1
  27. package/dist/gallery/assets/{register-CTEjv03I.js → register-C-_4TcWq.js} +2 -2
  28. package/dist/gallery/assets/{toggleHighContrast-BELgo5Kd.js → toggleHighContrast-BCHobRx5.js} +1 -1
  29. package/dist/gallery/assets/{tsMode-Bd8a8shj.js → tsMode-BkQQD7P8.js} +1 -1
  30. package/dist/gallery/assets/{typescript-DLlFIuFu.js → typescript-XiwOdN8G.js} +1 -1
  31. package/dist/gallery/assets/{vs-YCtNwrNl.js → vs-Coo3fK7w.js} +3 -3
  32. package/dist/gallery/assets/{workers-CBv4Qjp-.js → workers-Cypb28_F.js} +1 -1
  33. package/dist/gallery/assets/{xml-wUPbJH_s.js → xml-CN1ixSLs.js} +1 -1
  34. package/dist/gallery/assets/{yaml-B52zgDg-.js → yaml-l30MdsZA.js} +1 -1
  35. package/dist/gallery/index.html +2 -2
  36. package/dist/{gallery-qE5deHL0.mjs → gallery-CIcrB07g.mjs} +2 -2
  37. package/dist/{gallery-qE5deHL0.mjs.map → gallery-CIcrB07g.mjs.map} +1 -1
  38. package/dist/index-C5evpfmF.d.mts +81 -0
  39. package/dist/index-C5evpfmF.d.mts.map +1 -0
  40. package/dist/index.d.mts +5 -4
  41. package/dist/index.d.mts.map +1 -1
  42. package/dist/index.mjs +33 -9
  43. package/dist/index.mjs.map +1 -1
  44. package/dist/options-CZYdDtTW.mjs +581 -0
  45. package/dist/options-CZYdDtTW.mjs.map +1 -0
  46. package/dist/{plugin-CIrKPW8K.d.mts → plugin-DkFXWcCD.d.mts} +11 -2
  47. package/dist/plugin-DkFXWcCD.d.mts.map +1 -0
  48. package/dist/{template-DNhEgY8-.mjs → template-DDl7rsgo.mjs} +2 -2
  49. package/dist/{template-DNhEgY8-.mjs.map → template-DDl7rsgo.mjs.map} +1 -1
  50. package/dist/utils-DeMLVlNS.mjs +628 -0
  51. package/dist/utils-DeMLVlNS.mjs.map +1 -0
  52. package/dist/{vrt-DVub2B6r.d.mts → vrt-3T_e4bJ3.d.mts} +7 -1
  53. package/dist/vrt-3T_e4bJ3.d.mts.map +1 -0
  54. package/dist/{vrt-BPwRc95_.d.mts → vrt-6iDBfqNg.d.mts} +2 -2
  55. package/dist/{vrt-BPwRc95_.d.mts.map → vrt-6iDBfqNg.d.mts.map} +1 -1
  56. package/dist/{vrt-UxzWFygI.mjs → vrt-VuGK9wTX.mjs} +11 -4
  57. package/dist/vrt-VuGK9wTX.mjs.map +1 -0
  58. package/dist/vrt.d.mts +1 -1
  59. package/dist/vrt.mjs +1 -1
  60. package/package.json +4 -4
  61. package/dist/a11y-Ci9EEv6F.mjs.map +0 -1
  62. package/dist/index-HVtO9FK5.d.mts +0 -170
  63. package/dist/index-HVtO9FK5.d.mts.map +0 -1
  64. package/dist/options-Cd7bzLGr.mjs +0 -1142
  65. package/dist/options-Cd7bzLGr.mjs.map +0 -1
  66. package/dist/plugin-CIrKPW8K.d.mts.map +0 -1
  67. package/dist/vrt-DVub2B6r.d.mts.map +0 -1
  68. package/dist/vrt-UxzWFygI.mjs.map +0 -1
@@ -1,2 +1,2 @@
1
- import { a as generateA11yHtmlReport, i as computeA11ySummary, n as MuseaA11yRunner, o as generateA11yJsonReport, r as resolveAxeSource, t as A11ySummary } from "../index-HVtO9FK5.mjs";
1
+ import { a as generateA11yHtmlReport, i as computeA11ySummary, n as MuseaA11yRunner, o as generateA11yJsonReport, r as resolveAxeSource, t as A11ySummary } from "../index-C5evpfmF.mjs";
2
2
  export { A11ySummary, MuseaA11yRunner, computeA11ySummary, generateA11yHtmlReport, generateA11yJsonReport, resolveAxeSource };
@@ -1,2 +1,2 @@
1
- import { a as generateA11yJsonReport, i as generateA11yHtmlReport, n as resolveAxeSource, r as computeA11ySummary, t as MuseaA11yRunner } from "../a11y-Ci9EEv6F.mjs";
1
+ import { a as generateA11yJsonReport, i as generateA11yHtmlReport, n as resolveAxeSource, r as computeA11ySummary, t as MuseaA11yRunner } from "../a11y-BbFK942j.mjs";
2
2
  export { MuseaA11yRunner, computeA11ySummary, generateA11yHtmlReport, generateA11yJsonReport, resolveAxeSource };
@@ -1,4 +1,21 @@
1
+ import { t as escapeHtml$1 } from "./utils-DeMLVlNS.mjs";
1
2
  import path from "node:path";
3
+ //#region src/a11y/node-report.ts
4
+ function nodeDetails(result) {
5
+ return (result.targets ?? []).map((node) => {
6
+ const selectors = node.target.map((target) => typeof target === "string" ? target : JSON.stringify(target)).join(", ");
7
+ const checks = JSON.stringify({
8
+ any: node.any,
9
+ all: node.all,
10
+ none: node.none
11
+ }, null, 2);
12
+ return `<details class="node-details"><summary><code>${escapeHtml$1(selectors)}</code></summary>
13
+ <pre>${escapeHtml$1(node.html)}</pre>
14
+ ${node.failureSummary ? `<pre>${escapeHtml$1(node.failureSummary)}</pre>` : ""}
15
+ <pre>${escapeHtml$1(checks)}</pre></details>`;
16
+ }).join("");
17
+ }
18
+ //#endregion
2
19
  //#region src/a11y/report.ts
3
20
  /**
4
21
  * Compute a11y summary statistics from results.
@@ -47,13 +64,19 @@ function generateA11yHtmlReport(results, summary) {
47
64
  </div>
48
65
  <p style="padding:0.75rem 1rem;color:#f87171"><code>${escapeHtml(r.error ?? "")}</code></p>
49
66
  </div>`).join("");
50
- const resultItems = results.filter((r) => r.violations.length > 0).map((r) => {
67
+ const resultItems = results.filter((r) => r.violations.length > 0 || r.incomplete > 0).map((r) => {
51
68
  const artName = path.basename(r.artPath, ".art.vue");
52
- const violationRows = r.violations.map((v) => `
69
+ const violationRows = [...r.violations.map((v) => ({
70
+ v,
71
+ review: false
72
+ })), ...(r.incompleteResults ?? []).map((v) => ({
73
+ v,
74
+ review: true
75
+ }))].map(({ v, review }) => `
53
76
  <tr>
54
- <td><span style="color:${impactColor(v.impact)};font-weight:600;text-transform:uppercase;font-size:0.6875rem">${escapeHtml(v.impact)}</span></td>
77
+ <td><span style="color:${impactColor(v.impact ?? "unknown")};font-weight:600;text-transform:uppercase;font-size:0.6875rem">${escapeHtml(review ? "needs review" : v.impact ?? "unknown")}</span></td>
55
78
  <td><code>${escapeHtml(v.id)}</code></td>
56
- <td>${escapeHtml(v.description)}</td>
79
+ <td>${escapeHtml(v.description)}${nodeDetails(v)}</td>
57
80
  <td>${v.nodes}</td>
58
81
  <td>${v.helpUrl ? `<a href="${escapeHtml(v.helpUrl)}" target="_blank" style="color:#60a5fa">docs</a>` : ""}</td>
59
82
  </tr>`).join("");
@@ -62,7 +85,7 @@ function generateA11yHtmlReport(results, summary) {
62
85
  <div class="result-header">
63
86
  <div class="result-info">
64
87
  <span class="result-name">${escapeHtml(artName)} / ${escapeHtml(r.variantName)}</span>
65
- <span class="result-count">${r.violations.length} violation(s)</span>
88
+ <span class="result-count">${r.violations.length} violation(s), ${r.incomplete} need review</span>
66
89
  </div>
67
90
  </div>
68
91
  <table class="violations-table">
@@ -124,6 +147,8 @@ function generateA11yHtmlReport(results, summary) {
124
147
  .violations-table th { padding: 0.75rem 1rem; text-align: left; color: var(--musea-text-muted); font-weight: 500; font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.08em; border-bottom: 1px solid var(--musea-border); }
125
148
  .violations-table td { padding: 0.75rem 1rem; border-bottom: 1px solid var(--musea-border); }
126
149
  .violations-table code { background: var(--musea-bg-tertiary); padding: 0.125rem 0.375rem; border-radius: 3px; font-size: 0.75rem; }
150
+ .node-details { margin-top: 0.5rem; }
151
+ .node-details pre { white-space: pre-wrap; overflow-wrap: anywhere; margin-top: 0.5rem; }
127
152
  .all-clear { background: rgba(74, 222, 128, 0.1); border: 1px solid rgba(74, 222, 128, 0.2); border-radius: 8px; padding: 2rem; text-align: center; }
128
153
  .all-clear-text { color: #4ade80; font-weight: 600; }
129
154
  </style>
@@ -142,7 +167,7 @@ function generateA11yHtmlReport(results, summary) {
142
167
  <div class="stat minor"><div class="stat-value">${summary.minorCount}</div><div class="stat-label">Minor</div></div>
143
168
  <div class="stat critical"><div class="stat-value">${summary.erroredVariants}</div><div class="stat-label">Not audited</div></div>
144
169
  </div>
145
- ${summary.totalViolations === 0 && summary.erroredVariants === 0 ? `<div class="all-clear"><div class="all-clear-text">No accessibility violations found across ${summary.totalVariants} variant(s)</div></div>` : `<div class="results">${errorItems}${resultItems}</div>`}
170
+ ${summary.totalViolations === 0 && summary.erroredVariants === 0 && !results.some((r) => r.incomplete > 0) ? `<div class="all-clear"><div class="all-clear-text">No accessibility violations found across ${summary.totalVariants} variant(s)</div></div>` : `<div class="results">${errorItems}${resultItems}</div>`}
146
171
  </main>
147
172
  </body>
148
173
  </html>`;
@@ -161,6 +186,7 @@ function generateA11yJsonReport(results) {
161
186
  violations: r.violations,
162
187
  passes: r.passes,
163
188
  incomplete: r.incomplete,
189
+ ...r.incompleteResults === void 0 ? {} : { incompleteResults: r.incompleteResults },
164
190
  ...r.error === void 0 ? {} : { error: r.error }
165
191
  }))
166
192
  }, null, 2);
@@ -267,18 +293,43 @@ var MuseaA11yRunner = class {
267
293
  const axeResult = await page.evaluate((opts) => {
268
294
  return window.axe.run(document, opts);
269
295
  }, runOptions);
296
+ const mapResult = (result) => ({
297
+ id: result.id,
298
+ impact: result.impact ?? null,
299
+ description: result.description,
300
+ helpUrl: result.helpUrl,
301
+ nodes: result.nodes.length,
302
+ targets: result.nodes.map((node) => ({
303
+ target: node.target,
304
+ html: node.html,
305
+ failureSummary: node.failureSummary,
306
+ any: node.any.map(({ id, impact, message, data }) => ({
307
+ id,
308
+ impact,
309
+ message,
310
+ data
311
+ })),
312
+ all: node.all.map(({ id, impact, message, data }) => ({
313
+ id,
314
+ impact,
315
+ message,
316
+ data
317
+ })),
318
+ none: node.none.map(({ id, impact, message, data }) => ({
319
+ id,
320
+ impact,
321
+ message,
322
+ data
323
+ }))
324
+ }))
325
+ });
270
326
  return {
271
327
  artPath,
272
328
  variantName,
273
- violations: axeResult.violations.map((v) => ({
274
- id: v.id,
275
- impact: v.impact,
276
- description: v.description,
277
- helpUrl: v.helpUrl,
278
- nodes: v.nodes.length
279
- })),
329
+ violations: axeResult.violations.map(mapResult),
280
330
  passes: axeResult.passes.length,
281
- incomplete: axeResult.incomplete.length
331
+ incomplete: axeResult.incomplete.length,
332
+ incompleteResults: axeResult.incomplete.map(mapResult)
282
333
  };
283
334
  }
284
335
  /**
@@ -335,4 +386,4 @@ var MuseaA11yRunner = class {
335
386
  //#endregion
336
387
  export { generateA11yJsonReport as a, generateA11yHtmlReport as i, resolveAxeSource as n, computeA11ySummary as r, MuseaA11yRunner as t };
337
388
 
338
- //# sourceMappingURL=a11y-Ci9EEv6F.mjs.map
389
+ //# sourceMappingURL=a11y-BbFK942j.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y-BbFK942j.mjs","names":["escapeHtml"],"sources":["../src/a11y/node-report.ts","../src/a11y/report.ts","../src/a11y/index.ts"],"sourcesContent":["import type { A11yViolation } from \"../types/index.js\";\nimport { escapeHtml } from \"../utils.js\";\n\nexport function nodeDetails(result: A11yViolation): string {\n return (result.targets ?? [])\n .map((node) => {\n const selectors = node.target\n .map((target) => (typeof target === \"string\" ? target : JSON.stringify(target)))\n .join(\", \");\n const checks = JSON.stringify({ any: node.any, all: node.all, none: node.none }, null, 2);\n return `<details class=\"node-details\"><summary><code>${escapeHtml(selectors)}</code></summary>\n <pre>${escapeHtml(node.html)}</pre>\n ${node.failureSummary ? `<pre>${escapeHtml(node.failureSummary)}</pre>` : \"\"}\n <pre>${escapeHtml(checks)}</pre></details>`;\n })\n .join(\"\");\n}\n","/**\n * A11y report generation for Musea.\n *\n * Generates HTML and JSON reports from accessibility audit results,\n * plus summary statistics computation.\n */\n\nimport type { A11yResult } from \"../types/index.js\";\nimport type { A11ySummary } from \"./index.js\";\nimport path from \"node:path\";\nimport { nodeDetails } from \"./node-report.js\";\n\n/**\n * Compute a11y summary statistics from results.\n */\nexport function computeA11ySummary(results: A11yResult[]): A11ySummary {\n const components = new Set(results.map((r) => r.artPath));\n const allViolations = results.flatMap((r) => r.violations);\n\n return {\n totalComponents: components.size,\n totalVariants: results.length,\n erroredVariants: results.filter((r) => r.error !== undefined).length,\n totalViolations: allViolations.length,\n criticalCount: allViolations.filter((v) => v.impact === \"critical\").length,\n seriousCount: allViolations.filter((v) => v.impact === \"serious\").length,\n moderateCount: allViolations.filter((v) => v.impact === \"moderate\").length,\n minorCount: allViolations.filter((v) => v.impact === \"minor\").length,\n };\n}\n\n/**\n * Generate HTML report from a11y results.\n */\nexport function generateA11yHtmlReport(results: A11yResult[], summary: A11ySummary): string {\n const timestamp = new Date().toLocaleString(\"ja-JP\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n\n const impactColor = (impact: string): string => {\n switch (impact) {\n case \"critical\":\n return \"#f87171\";\n case \"serious\":\n return \"#fb923c\";\n case \"moderate\":\n return \"#fbbf24\";\n case \"minor\":\n return \"#60a5fa\";\n default:\n return \"#7b8494\";\n }\n };\n\n const errorItems = results\n .filter((r) => r.error !== undefined)\n .map(\n (r) => `\n <div class=\"result\">\n <div class=\"result-header\">\n <div class=\"result-info\">\n <span class=\"result-name\">${escapeHtml(path.basename(r.artPath, \".art.vue\"))} / ${escapeHtml(r.variantName)}</span>\n <span class=\"result-count\">audit did not run</span>\n </div>\n </div>\n <p style=\"padding:0.75rem 1rem;color:#f87171\"><code>${escapeHtml(r.error ?? \"\")}</code></p>\n </div>`,\n )\n .join(\"\");\n\n const resultItems = results\n .filter((r) => r.violations.length > 0 || r.incomplete > 0)\n .map((r) => {\n const artName = path.basename(r.artPath, \".art.vue\");\n const findings = [\n ...r.violations.map((v) => ({ v, review: false })),\n ...(r.incompleteResults ?? []).map((v) => ({ v, review: true })),\n ];\n const violationRows = findings\n .map(\n ({ v, review }) => `\n <tr>\n <td><span style=\"color:${impactColor(v.impact ?? \"unknown\")};font-weight:600;text-transform:uppercase;font-size:0.6875rem\">${escapeHtml(review ? \"needs review\" : (v.impact ?? \"unknown\"))}</span></td>\n <td><code>${escapeHtml(v.id)}</code></td>\n <td>${escapeHtml(v.description)}${nodeDetails(v)}</td>\n <td>${v.nodes}</td>\n <td>${v.helpUrl ? `<a href=\"${escapeHtml(v.helpUrl)}\" target=\"_blank\" style=\"color:#60a5fa\">docs</a>` : \"\"}</td>\n </tr>`,\n )\n .join(\"\");\n\n return `\n <div class=\"result\">\n <div class=\"result-header\">\n <div class=\"result-info\">\n <span class=\"result-name\">${escapeHtml(artName)} / ${escapeHtml(r.variantName)}</span>\n <span class=\"result-count\">${r.violations.length} violation(s), ${r.incomplete} need review</span>\n </div>\n </div>\n <table class=\"violations-table\">\n <thead><tr><th>Impact</th><th>Rule</th><th>Description</th><th>Nodes</th><th>Help</th></tr></thead>\n <tbody>${violationRows}</tbody>\n </table>\n </div>`;\n })\n .join(\"\");\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>A11y Report - Musea</title>\n <style>\n :root {\n --musea-bg-primary: #0d0d0d;\n --musea-bg-secondary: #1a1815;\n --musea-bg-tertiary: #252220;\n --musea-accent: #a34828;\n --musea-text: #e6e9f0;\n --musea-text-muted: #7b8494;\n --musea-border: #3a3530;\n }\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n background: var(--musea-bg-primary);\n color: var(--musea-text);\n min-height: 100vh;\n line-height: 1.5;\n }\n .header {\n background: var(--musea-bg-secondary);\n border-bottom: 1px solid var(--musea-border);\n padding: 1rem 2rem;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n .logo { font-size: 1.25rem; font-weight: 700; color: var(--musea-accent); }\n .header-meta { color: var(--musea-text-muted); font-size: 0.8125rem; }\n .main { max-width: 1200px; margin: 0 auto; padding: 2rem; }\n .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 1rem; margin-bottom: 2rem; }\n .stat { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 8px; padding: 1rem; text-align: center; }\n .stat-value { font-size: 1.75rem; font-weight: 700; font-variant-numeric: tabular-nums; }\n .stat-label { color: var(--musea-text-muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.08em; }\n .stat.critical .stat-value { color: #f87171; }\n .stat.serious .stat-value { color: #fb923c; }\n .stat.moderate .stat-value { color: #fbbf24; }\n .stat.minor .stat-value { color: #60a5fa; }\n .stat.total .stat-value { color: var(--musea-text); }\n .results { display: flex; flex-direction: column; gap: 1rem; }\n .result { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 8px; overflow: hidden; }\n .result-header { padding: 1rem; background: var(--musea-bg-tertiary); display: flex; justify-content: space-between; align-items: center; }\n .result-name { font-weight: 600; }\n .result-count { color: var(--musea-text-muted); font-size: 0.8125rem; }\n .violations-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }\n .violations-table th { padding: 0.75rem 1rem; text-align: left; color: var(--musea-text-muted); font-weight: 500; font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.08em; border-bottom: 1px solid var(--musea-border); }\n .violations-table td { padding: 0.75rem 1rem; border-bottom: 1px solid var(--musea-border); }\n .violations-table code { background: var(--musea-bg-tertiary); padding: 0.125rem 0.375rem; border-radius: 3px; font-size: 0.75rem; }\n .node-details { margin-top: 0.5rem; }\n .node-details pre { white-space: pre-wrap; overflow-wrap: anywhere; margin-top: 0.5rem; }\n .all-clear { background: rgba(74, 222, 128, 0.1); border: 1px solid rgba(74, 222, 128, 0.2); border-radius: 8px; padding: 2rem; text-align: center; }\n .all-clear-text { color: #4ade80; font-weight: 600; }\n </style>\n</head>\n<body>\n <header class=\"header\">\n <div><span class=\"logo\">Musea</span> <span style=\"color:var(--musea-text-muted);font-size:0.875rem;margin-left:1rem\">Accessibility Report</span></div>\n <div class=\"header-meta\">${timestamp}</div>\n </header>\n <main class=\"main\">\n <div class=\"summary\">\n <div class=\"stat total\"><div class=\"stat-value\">${summary.totalViolations}</div><div class=\"stat-label\">Violations</div></div>\n <div class=\"stat critical\"><div class=\"stat-value\">${summary.criticalCount}</div><div class=\"stat-label\">Critical</div></div>\n <div class=\"stat serious\"><div class=\"stat-value\">${summary.seriousCount}</div><div class=\"stat-label\">Serious</div></div>\n <div class=\"stat moderate\"><div class=\"stat-value\">${summary.moderateCount}</div><div class=\"stat-label\">Moderate</div></div>\n <div class=\"stat minor\"><div class=\"stat-value\">${summary.minorCount}</div><div class=\"stat-label\">Minor</div></div>\n <div class=\"stat critical\"><div class=\"stat-value\">${summary.erroredVariants}</div><div class=\"stat-label\">Not audited</div></div>\n </div>\n ${\n summary.totalViolations === 0 &&\n summary.erroredVariants === 0 &&\n !results.some((r) => r.incomplete > 0)\n ? `<div class=\"all-clear\"><div class=\"all-clear-text\">No accessibility violations found across ${summary.totalVariants} variant(s)</div></div>`\n : `<div class=\"results\">${errorItems}${resultItems}</div>`\n }\n </main>\n</body>\n</html>`;\n}\n\n/**\n * Generate JSON report for CI integration.\n */\nexport function generateA11yJsonReport(results: A11yResult[]): string {\n const summary = computeA11ySummary(results);\n return JSON.stringify(\n {\n timestamp: new Date().toISOString(),\n summary,\n results: results.map((r) => ({\n art: path.basename(r.artPath, \".art.vue\"),\n variant: r.variantName,\n violations: r.violations,\n passes: r.passes,\n incomplete: r.incomplete,\n ...(r.incompleteResults === undefined ? {} : { incompleteResults: r.incompleteResults }),\n ...(r.error === undefined ? {} : { error: r.error }),\n })),\n },\n null,\n 2,\n );\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#x27;\");\n}\n","/**\n * Accessibility (a11y) testing module for Musea.\n * Uses axe-core for automated accessibility auditing via Playwright.\n */\n\nimport type { Page } from \"playwright\";\nimport type { AxeResults, Result } from \"axe-core\";\nimport type {\n ArtFileInfo,\n A11yResult,\n A11yViolation,\n A11yOptions,\n ViewportConfig,\n} from \"../types/index.js\";\nimport type { MuseaVrtRunner } from \"../vrt.js\";\n\nimport { computeA11ySummary, generateA11yHtmlReport, generateA11yJsonReport } from \"./report.js\";\n\n// Re-export report functions so consumers importing from a11y.js still work\nexport { computeA11ySummary, generateA11yHtmlReport, generateA11yJsonReport } from \"./report.js\";\n\n/**\n * A11y audit summary.\n */\nexport interface A11ySummary {\n totalComponents: number;\n totalVariants: number;\n /** Variants whose audit could not run at all; see `A11yResult.error`. */\n erroredVariants: number;\n totalViolations: number;\n criticalCount: number;\n seriousCount: number;\n moderateCount: number;\n minorCount: number;\n}\n\n/**\n * Read axe-core's injectable browser bundle.\n *\n * axe-core ships CommonJS (`\"main\": \"axe.js\"`, no `exports` map), so under\n * ESM its exports arrive on the namespace's `default` and `source` is not\n * interop-detected as a named export: `(await import(\"axe-core\")).source`\n * is `undefined`. Injecting that injects nothing, leaves `window.axe`\n * undefined, and the audit dies later as `Cannot read properties of\n * undefined (reading 'run')` — a failure that points at the wrong line. So\n * read both shapes and fail here, where the cause is still legible.\n */\nexport async function resolveAxeSource(): Promise<string> {\n let namespace: { source?: unknown; default?: { source?: unknown } };\n try {\n namespace = await import(\"axe-core\");\n } catch (cause) {\n throw new Error(\n \"axe-core is not installed. Install it as a peer dependency: npm install axe-core\",\n { cause },\n );\n }\n\n const source = namespace.source ?? namespace.default?.source;\n if (typeof source !== \"string\" || source.length === 0) {\n throw new Error(\n \"axe-core is installed but exposes no `source` bundle to inject. \" +\n \"Expected a non-empty string on either the module namespace or its \" +\n \"default export; check that the installed axe-core version is >= 4.\",\n );\n }\n return source;\n}\n\n/**\n * A11y runner using axe-core via Playwright.\n */\nexport class MuseaA11yRunner {\n private options: Required<A11yOptions>;\n\n constructor(options: A11yOptions = {}) {\n this.options = {\n enabled: options.enabled ?? true,\n includeRules: options.includeRules ?? [],\n excludeRules: options.excludeRules ?? [],\n level: options.level ?? \"AA\",\n };\n }\n\n /**\n * Run a11y audits on all art file variants.\n * Reuses VRT runner's browser if available.\n */\n async runAudits(\n artFiles: ArtFileInfo[],\n baseUrl: string,\n vrtRunner?: MuseaVrtRunner,\n ): Promise<A11yResult[]> {\n const results: A11yResult[] = [];\n const defaultViewport: ViewportConfig = { width: 1280, height: 720, name: \"desktop\" };\n\n for (const art of artFiles) {\n for (const variant of art.variants) {\n if (variant.skipVrt) continue;\n\n let page: Page | null = null;\n let context: { page: Page; context: { close(): Promise<void> } } | null = null;\n\n try {\n if (vrtRunner) {\n context = await vrtRunner.createPage(defaultViewport);\n page = context.page;\n } else {\n // Standalone mode: launch own browser\n const { chromium } = await import(\"playwright\");\n const browser = await chromium.launch({ headless: true });\n const ctx = await browser.newContext({\n viewport: { width: defaultViewport.width, height: defaultViewport.height },\n });\n page = await ctx.newPage();\n context = { page, context: ctx };\n }\n\n const variantUrl = this.buildVariantUrl(baseUrl, art.path, variant.name);\n await page.goto(variantUrl, { waitUntil: \"networkidle\" });\n await page.waitForSelector(\".musea-variant\", { timeout: 10000 });\n await page.waitForTimeout(200);\n\n const result = await this.auditPage(page, art.path, variant.name);\n results.push(result);\n } catch (error) {\n results.push({\n artPath: art.path,\n variantName: variant.name,\n violations: [],\n passes: 0,\n incomplete: 0,\n error: error instanceof Error ? error.message : String(error),\n });\n } finally {\n if (context) {\n await context.context.close();\n }\n }\n }\n }\n\n return results;\n }\n\n /**\n * Audit a single page using axe-core.\n */\n async auditPage(page: Page, artPath: string, variantName: string): Promise<A11yResult> {\n // Inject axe-core into the page\n const axeSource = await resolveAxeSource();\n await page.evaluate(axeSource);\n\n // Build axe-core run options\n const runOptions = this.buildAxeOptions();\n\n // Run axe-core\n const axeResult = (await page.evaluate((opts) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (window as any).axe.run(document, opts);\n }, runOptions)) as AxeResults;\n\n // Map to our result format\n const mapResult = (result: Result): A11yViolation => ({\n id: result.id,\n impact: result.impact ?? null,\n description: result.description,\n helpUrl: result.helpUrl,\n nodes: result.nodes.length,\n targets: result.nodes.map((node) => ({\n target: node.target,\n html: node.html,\n failureSummary: node.failureSummary,\n any: node.any.map(({ id, impact, message, data }) => ({ id, impact, message, data })),\n all: node.all.map(({ id, impact, message, data }) => ({ id, impact, message, data })),\n none: node.none.map(({ id, impact, message, data }) => ({ id, impact, message, data })),\n })),\n });\n const violations = axeResult.violations.map(mapResult);\n\n return {\n artPath,\n variantName,\n violations,\n passes: axeResult.passes.length,\n incomplete: axeResult.incomplete.length,\n incompleteResults: axeResult.incomplete.map(mapResult),\n };\n }\n\n /**\n * Get summary statistics from results.\n */\n getSummary(results: A11yResult[]): A11ySummary {\n return computeA11ySummary(results);\n }\n\n /**\n * Generate HTML report.\n */\n generateHtmlReport(results: A11yResult[]): string {\n const summary = this.getSummary(results);\n return generateA11yHtmlReport(results, summary);\n }\n\n /**\n * Generate JSON report for CI integration.\n */\n generateJsonReport(results: A11yResult[]): string {\n return generateA11yJsonReport(results);\n }\n\n /**\n * Build axe-core run options from configuration.\n */\n private buildAxeOptions(): Record<string, unknown> {\n const runOnly: Record<string, unknown> = {};\n\n // Set WCAG level\n const tags: string[] = [];\n switch (this.options.level) {\n case \"A\":\n tags.push(\"wcag2a\", \"wcag21a\");\n break;\n case \"AA\":\n tags.push(\"wcag2a\", \"wcag2aa\", \"wcag21a\", \"wcag21aa\", \"wcag22aa\");\n break;\n case \"AAA\":\n tags.push(\"wcag2a\", \"wcag2aa\", \"wcag2aaa\", \"wcag21a\", \"wcag21aa\", \"wcag22aa\");\n break;\n }\n\n if (tags.length > 0) {\n runOnly.type = \"tag\";\n runOnly.values = tags;\n }\n\n const rules: Record<string, { enabled: boolean }> = {};\n\n for (const ruleId of this.options.includeRules) {\n rules[ruleId] = { enabled: true };\n }\n for (const ruleId of this.options.excludeRules) {\n rules[ruleId] = { enabled: false };\n }\n\n return {\n ...(Object.keys(runOnly).length > 0 ? { runOnly } : {}),\n ...(Object.keys(rules).length > 0 ? { rules } : {}),\n };\n }\n\n private buildVariantUrl(baseUrl: string, artPath: string, variantName: string): string {\n const encodedPath = encodeURIComponent(artPath);\n const encodedVariant = encodeURIComponent(variantName);\n return `${baseUrl}/__musea__/preview?art=${encodedPath}&variant=${encodedVariant}`;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ //#region src/types/api.d.ts
2
+ /**
3
+ * Palette API response.
4
+ */
5
+ interface PaletteApiResponse {
6
+ title: string;
7
+ controls: PaletteControl[];
8
+ groups: string[];
9
+ json: string;
10
+ typescript: string;
11
+ }
12
+ /**
13
+ * Single prop control definition.
14
+ */
15
+ interface PaletteControl {
16
+ name: string;
17
+ control: ControlKind;
18
+ default_value?: unknown;
19
+ description?: string;
20
+ required: boolean;
21
+ options: Array<{
22
+ label: string;
23
+ value: unknown;
24
+ }>;
25
+ range?: {
26
+ min: number;
27
+ max: number;
28
+ step?: number;
29
+ };
30
+ group?: string;
31
+ }
32
+ /**
33
+ * Supported control kinds for the props panel.
34
+ */
35
+ type ControlKind = "text" | "number" | "boolean" | "range" | "select" | "radio" | "color" | "date" | "object" | "array" | "file" | "raw";
36
+ /**
37
+ * Analysis API response (Props/Emits info).
38
+ */
39
+ interface AnalysisApiResponse {
40
+ props: Array<{
41
+ name: string;
42
+ type: string;
43
+ required: boolean;
44
+ default_value?: unknown;
45
+ }>;
46
+ emits: string[];
47
+ }
48
+ /**
49
+ * Accessibility testing options.
50
+ */
51
+ interface A11yOptions {
52
+ /** Enable a11y auditing during VRT */
53
+ enabled?: boolean;
54
+ /** axe-core rules to include */
55
+ includeRules?: string[];
56
+ /** axe-core rules to exclude */
57
+ excludeRules?: string[];
58
+ /** WCAG level (A, AA, AAA) */
59
+ level?: "A" | "AA" | "AAA";
60
+ }
61
+ /**
62
+ * Accessibility audit result.
63
+ */
64
+ interface A11yResult {
65
+ artPath: string;
66
+ variantName: string;
67
+ violations: A11yViolation[];
68
+ passes: number;
69
+ incomplete: number;
70
+ /** Rules requiring manual review, with the same per-node details as violations. */
71
+ incompleteResults?: A11yViolation[];
72
+ /**
73
+ * Why the audit could not run, when it could not run.
74
+ *
75
+ * A variant that failed to audit measured *nothing* about accessibility,
76
+ * so it reports no violations. Recording the failure here instead of as a
77
+ * synthetic `critical` violation keeps the counts honest: a broken run
78
+ * reads as broken rather than as a component with critical defects.
79
+ */
80
+ error?: string;
81
+ }
82
+ /**
83
+ * Single accessibility violation.
84
+ */
85
+ interface A11yViolation {
86
+ id: string;
87
+ impact: "minor" | "moderate" | "serious" | "critical" | null;
88
+ description: string;
89
+ helpUrl: string;
90
+ nodes: number;
91
+ /** Per-node results from axe; optional for older stored reports. */
92
+ targets?: A11yNode[];
93
+ }
94
+ interface A11yNode {
95
+ /** Selectors retain axe's nested arrays for iframe/shadow DOM targets. */
96
+ target: (string | string[])[];
97
+ html: string;
98
+ failureSummary?: string;
99
+ any: A11yCheck[];
100
+ all: A11yCheck[];
101
+ none: A11yCheck[];
102
+ }
103
+ interface A11yCheck {
104
+ id: string;
105
+ impact: string;
106
+ message: string;
107
+ /** Measured values such as foreground/background colors and contrast ratios. */
108
+ data?: unknown;
109
+ }
110
+ //#endregion
111
+ export { PaletteApiResponse as i, A11yResult as n, AnalysisApiResponse as r, A11yOptions as t };
112
+ //# sourceMappingURL=api-gMwahZKm.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-gMwahZKm.d.mts","names":[],"sources":["../src/types/api.ts"],"mappings":";;AAGA;;UAAiB,kBAAA;EACf,KAAA;EACA,QAAA,EAAU,cAAc;EACxB,MAAA;EACA,IAAA;EACA,UAAA;AAAA;;;AAAU;UAMK,cAAA;EACf,IAAA;EACA,OAAA,EAAS,WAAA;EACT,aAAA;EACA,WAAA;EACA,QAAA;EACA,OAAA,EAAS,KAAK;IAAG,KAAA;IAAe,KAAA;EAAA;EAChC,KAAA;IAAU,GAAA;IAAa,GAAA;IAAa,IAAA;EAAA;EACpC,KAAA;AAAA;;;;KAMU,WAAA;AAAZ;;;AAAA,UAiBiB,mBAAA;EACf,KAAA,EAAO,KAAK;IACV,IAAA;IACA,IAAA;IACA,QAAA;IACA,aAAA;EAAA;EAEF,KAAA;AAAA;;;;UAUe,WAAA;EAVV;EAYL,OAAA;EAFe;EAIf,YAAA;;EAEA,YAAA;EAJA;EAMA,KAAA;AAAA;;;AAAK;UAMU,UAAA;EACf,OAAA;EACA,WAAA;EACA,UAAA,EAAY,aAAA;EACZ,MAAA;EACA,UAAA;EAFA;EAIA,iBAAA,GAAoB,aAAa;EAHjC;;;;;;AAYK;AAMP;EANE,KAAA;AAAA;;;;UAMe,aAAA;EACf,EAAA;EACA,MAAA;EACA,WAAA;EACA,OAAA;EACA,KAAA;EAEkB;EAAlB,OAAA,GAAU,QAAQ;AAAA;AAAA,UAGH,QAAA;EAKV;EAHL,MAAA;EACA,IAAA;EACA,cAAA;EACA,GAAA,EAAK,SAAA;EACL,GAAA,EAAK,SAAA;EACL,IAAA,EAAM,SAAA;AAAA;AAAA,UAGS,SAAA;EACf,EAAA;EACA,MAAA;EACA,OAAA;EANA;EAQA,IAAA;AAAA"}
@@ -1,4 +1,4 @@
1
- import { i as MuseaVrtOptions } from "../plugin-CIrKPW8K.mjs";
1
+ import { i as MuseaVrtOptions } from "../plugin-DkFXWcCD.mjs";
2
2
 
3
3
  //#region src/cli/index.d.ts
4
4
  type Command = "run" | "approve" | "clean" | "generate";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/cli/index.ts"],"mappings":";;;KAiCK,OAAA;AAAA,UAEY,UAAA;EACf,OAAA,EAAS,OAAA;EACT,MAAA;EACA,MAAA;EACA,MAAA;EACA,SAAA;EACA,iBAAA;EACA,OAAA;EACA,eAAA;EACA,IAAA;EACA,EAAA;EACA,IAAA;EACA,IAAA;EACA,OAAA;EACA,OAAA;EACA,aAAA;EACA,GAAA,GAAM,eAAe;AAAA;AAAA,iBAGP,SAAA,CAAU,IAAA,aAAiB,UAAU"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/cli/index.ts"],"mappings":";;;KAkCK,OAAA;AAAA,UAEY,UAAA;EACf,OAAA,EAAS,OAAA;EACT,MAAA;EACA,MAAA;EACA,MAAA;EACA,SAAA;EACA,iBAAA;EACA,OAAA;EACA,eAAA;EACA,IAAA;EACA,EAAA;EACA,IAAA;EACA,IAAA;EACA,OAAA;EACA,OAAA;EACA,aAAA;EACA,GAAA,GAAM,eAAe;AAAA;AAAA,iBAGP,SAAA,CAAU,IAAA,aAAiB,UAAU"}
@@ -1,37 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { n as readMuseaOptions, r as processMuseaArtFile } from "../options-Cd7bzLGr.mjs";
3
- import { a as normalizeVrtWorkerCount, i as MuseaVrtRunner, n as generateVrtJsonReport, r as generateVrtReport } from "../vrt-UxzWFygI.mjs";
2
+ import { a as scanArtFiles } from "../utils-DeMLVlNS.mjs";
3
+ import { n as readMuseaOptions, r as processMuseaArtFile } from "../options-CZYdDtTW.mjs";
4
+ import { a as normalizeVrtWorkerCount, i as MuseaVrtRunner, n as generateVrtJsonReport, r as generateVrtReport } from "../vrt-VuGK9wTX.mjs";
4
5
  import fs, { realpathSync } from "node:fs";
5
6
  import path from "node:path";
6
7
  import { fileURLToPath } from "node:url";
7
8
  import { loadConfigFromFile } from "vite";
8
9
  //#region src/cli/utils.ts
9
- /**
10
- * CLI utility functions for art file scanning and parsing.
11
- *
12
- * Extracted from cli.ts to keep file sizes manageable.
13
- */
14
- /** Recursively scan a directory for .art.vue files. */
15
- async function scanArtFiles(root) {
16
- const files = [];
17
- async function scan(dir) {
18
- let entries;
19
- try {
20
- entries = await fs.promises.readdir(dir, { withFileTypes: true });
21
- } catch (error) {
22
- if (error.code === "EACCES") return;
23
- throw error;
24
- }
25
- for (const entry of entries) {
26
- const fullPath = path.join(dir, entry.name);
27
- if (entry.name === "node_modules" || entry.name === "dist") continue;
28
- if (entry.isDirectory()) await scan(fullPath);
29
- else if (entry.isFile() && entry.name.endsWith(".art.vue")) files.push(fullPath);
30
- }
31
- }
32
- await scan(root);
33
- return files;
34
- }
35
10
  /** Parse a single .art.vue file into an ArtFileInfo structure. */
36
11
  async function parseArtFile(filePath) {
37
12
  return processMuseaArtFile(filePath, {
@@ -54,16 +29,34 @@ function isArtFileInput(filePath) {
54
29
  return filePath.endsWith(".art.vue");
55
30
  }
56
31
  function createVrtOptions(options) {
57
- const configured = options.vrt ?? {};
32
+ const configured = withoutA11y(options.vrt);
58
33
  const vrtOptions = {
59
34
  ...configured,
60
- snapshotDir: path.join(options.output, "snapshots"),
35
+ snapshotDir: resolveVrtSnapshotDir(options, configured.snapshotDir),
61
36
  threshold: options.thresholdProvided ? options.threshold : configured.threshold ?? options.threshold
62
37
  };
63
38
  const workers = options.workersProvided ? options.workers : configured.workers;
64
39
  if (workers !== void 0) vrtOptions.workers = normalizeVrtWorkerCount(workers);
65
40
  return vrtOptions;
66
41
  }
42
+ /**
43
+ * `vrt.snapshotDir` is resolved against the config file's directory.
44
+ * When it is unset, baselines stay under `<--output>/snapshots`.
45
+ */
46
+ function resolveVrtSnapshotDir(options, snapshotDir) {
47
+ if (!snapshotDir) return path.join(options.output, "snapshots");
48
+ return path.resolve(path.dirname(path.resolve(options.config)), snapshotDir);
49
+ }
50
+ function withoutA11y(vrt) {
51
+ if (!vrt) return {};
52
+ const configured = { ...vrt };
53
+ delete configured.a11y;
54
+ return configured;
55
+ }
56
+ /** Options object `musea-vrt --a11y` passes to `MuseaA11yRunner`. */
57
+ function createA11yRunner(options, Runner) {
58
+ return new Runner(options.vrt?.a11y);
59
+ }
67
60
  async function runVrt(options, artFiles) {
68
61
  const totalVariants = artFiles.reduce((sum, art) => sum + art.variants.filter((v) => !v.skipVrt).length, 0);
69
62
  console.log(` Testing ${totalVariants} variant(s) across ${artFiles.length} art file(s)\n`);
@@ -93,7 +86,7 @@ async function runVrt(options, artFiles) {
93
86
  console.log(" Running accessibility audits...\n");
94
87
  try {
95
88
  const { MuseaA11yRunner } = await import("../a11y/index.mjs");
96
- const a11yRunner = new MuseaA11yRunner();
89
+ const a11yRunner = createA11yRunner(options, MuseaA11yRunner);
97
90
  const a11yResults = await a11yRunner.runAudits(artFiles, options.baseUrl, runner);
98
91
  const a11ySummary = a11yRunner.getSummary(a11yResults);
99
92
  console.log(" A11y Results:");
@@ -215,7 +208,33 @@ async function runGenerate(options) {
215
208
  }
216
209
  //#endregion
217
210
  //#region src/cli/config.ts
211
+ const DEFAULT_INCLUDE = ["**/*.art.vue"];
212
+ const DEFAULT_EXCLUDE = ["node_modules/**", "dist/**"];
213
+ const VIZE_CONFIG_NAMES = [
214
+ "vize.config.ts",
215
+ "vize.config.mts",
216
+ "vize.config.js",
217
+ "vize.config.mjs",
218
+ "vize.config.cjs"
219
+ ];
218
220
  async function loadMuseaVrtOptions(configPath, cwd = process.cwd()) {
221
+ const loaded = await loadViteConfig(configPath, cwd);
222
+ if (!loaded) return void 0;
223
+ return readPluginMuseaOptions(loaded.plugins)?.vrt;
224
+ }
225
+ async function loadMuseaFileSet(configPath, cwd = process.cwd()) {
226
+ const loaded = await loadViteConfig(configPath, cwd);
227
+ const pluginOptions = loaded ? readPluginMuseaOptions(loaded.plugins) : void 0;
228
+ const vizeOptions = pluginOptions?.include ? void 0 : await readVizeMuseaOptions(loaded?.configDir ?? cwd);
229
+ const include = pluginOptions?.include ?? vizeOptions?.include ?? DEFAULT_INCLUDE;
230
+ const exclude = pluginOptions?.exclude ?? vizeOptions?.exclude ?? DEFAULT_EXCLUDE;
231
+ return {
232
+ root: loaded?.root ?? cwd,
233
+ include,
234
+ exclude
235
+ };
236
+ }
237
+ async function loadViteConfig(configPath, cwd) {
219
238
  const resolvedConfigPath = path.isAbsolute(configPath) ? configPath : path.resolve(cwd, configPath);
220
239
  if (!await fileExists(resolvedConfigPath)) return void 0;
221
240
  const loaded = await loadConfigFromFile({
@@ -227,9 +246,35 @@ async function loadMuseaVrtOptions(configPath, cwd = process.cwd()) {
227
246
  if (!loaded) return void 0;
228
247
  const plugins = [];
229
248
  await collectPlugins(loaded.config.plugins, plugins);
249
+ const configDir = path.dirname(resolvedConfigPath);
250
+ const configuredRoot = loaded.config.root;
251
+ return {
252
+ root: configuredRoot ? path.resolve(configDir, configuredRoot) : configDir,
253
+ configDir,
254
+ plugins
255
+ };
256
+ }
257
+ function readPluginMuseaOptions(plugins) {
230
258
  for (const plugin of plugins) {
231
259
  const options = readMuseaOptions(plugin);
232
- if (options?.vrt) return options.vrt;
260
+ if (options) return options;
261
+ }
262
+ }
263
+ async function readVizeMuseaOptions(dir) {
264
+ for (const name of VIZE_CONFIG_NAMES) {
265
+ const file = path.join(dir, name);
266
+ if (!await fileExists(file)) continue;
267
+ try {
268
+ const musea = ((await loadConfigFromFile({
269
+ command: "serve",
270
+ mode: "development",
271
+ isPreview: false,
272
+ isSsrBuild: false
273
+ }, file))?.config)?.musea;
274
+ if (musea?.include || musea?.exclude) return musea;
275
+ } catch {
276
+ continue;
277
+ }
233
278
  }
234
279
  }
235
280
  async function collectPlugins(input, plugins) {
@@ -442,8 +487,9 @@ async function main() {
442
487
  return;
443
488
  }
444
489
  options.vrt = await loadMuseaVrtOptions(options.config, cwd);
490
+ const fileSet = await loadMuseaFileSet(options.config, cwd);
445
491
  console.log(" Scanning for art files...");
446
- const artFilePaths = await scanArtFiles(cwd);
492
+ const artFilePaths = await scanArtFiles(fileSet.root, fileSet.include, fileSet.exclude);
447
493
  if (artFilePaths.length === 0) {
448
494
  console.log(" No art files found.\n");
449
495
  process.exit(0);