@vizejs/vite-plugin-musea 0.250.0 → 0.263.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/dist/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  import { c as createDevSessionToken, d as resolveInside, f as resolveInsideAny, h as validateDevApiRequest, i as generateGalleryScript, l as decodeUrlComponent, m as serializeScriptValue, n as generateGalleryModule, o as HttpError, p as resolveUrlPathInside, r as generateGalleryBody, s as collectRequestBody, u as parseJsonBody } from "./gallery-DIBlUrB6.mjs";
2
2
  import { i as MuseaVrtRunner, n as generateVrtJsonReport, r as generateVrtReport } from "./vrt-Cv1PK1EF.mjs";
3
3
  import { t as MuseaA11yRunner } from "./a11y-62l8G1tr.mjs";
4
- import { n as writeArtFile, t as generateArtFile } from "./autogen-Blm3Qy_O.mjs";
4
+ import { n as writeArtFile, t as generateArtFile } from "./autogen-B7_TO0n5.mjs";
5
5
  import { createRequire } from "node:module";
6
6
  import { transformWithEsbuild } from "vite";
7
7
  import fs from "node:fs";
8
8
  import path from "node:path";
9
- import { vizeConfigStore } from "@vizejs/vite-plugin";
10
- import crypto from "node:crypto";
11
9
  import { fileURLToPath } from "node:url";
10
+ import crypto from "node:crypto";
11
+ import { VIZE_CONFIG_FILE_ENV, loadConfig, vizeConfigStore } from "@vizejs/vite-plugin";
12
12
  //#region src/native-loader.ts
13
13
  /**
14
14
  * Native binding loader for @vizejs/native.
@@ -92,15 +92,6 @@ function analyzeSfcFallback(source, _options) {
92
92
  }
93
93
  }
94
94
  //#endregion
95
- //#region src/component-source.ts
96
- function allowedSourceRoots(root, scanRoots = []) {
97
- return [...new Set([root, ...scanRoots].map((sourceRoot) => path.resolve(sourceRoot)))];
98
- }
99
- function resolveComponentSourcePath(art, artPath, sourceRoots) {
100
- const componentPath = art.isInline && art.componentPath ? art.componentPath : art.metadata.component ? path.isAbsolute(art.metadata.component) ? art.metadata.component : path.resolve(path.dirname(artPath), art.metadata.component) : null;
101
- return componentPath ? resolveInsideAny(sourceRoots, componentPath, "component path") : null;
102
- }
103
- //#endregion
104
95
  //#region src/utils.ts
105
96
  /**
106
97
  * Shared utility functions for the Musea Vite plugin.
@@ -182,7 +173,7 @@ async function generateStorybookFiles(artFiles, root, outDir) {
182
173
  const source = await fs.promises.readFile(filePath, "utf-8");
183
174
  const csf = binding.artToCsf(source, { filename: filePath });
184
175
  const outputPath = path.join(outputDir, csf.filename);
185
- const code = rewriteStorybookComponentImport(csf.code, art, filePath, outputPath);
176
+ const code = rewriteStorybookScriptImports(rewriteStorybookComponentImport(csf.code, art, filePath, outputPath), filePath, outputPath);
186
177
  await fs.promises.writeFile(outputPath, code, "utf-8");
187
178
  console.log(`[musea] Generated: ${path.relative(root, outputPath)}`);
188
179
  } catch (e) {
@@ -200,369 +191,50 @@ function rewriteStorybookComponentImport(code, art, artPath, outputPath) {
200
191
  function isBareImport(source) {
201
192
  return !source.startsWith(".") && !path.isAbsolute(source);
202
193
  }
203
- function toPascalCase(str) {
204
- const pascal = str.normalize("NFKD").replace(/[^\p{L}\p{N}]+/gu, " ").trim().split(/\s+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
205
- if (!pascal) return "Variant";
206
- return /^[\p{L}_$]/u.test(pascal) ? pascal : `Variant${pascal}`;
207
- }
208
- function escapeHtml(str) {
209
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
210
- }
211
194
  /**
212
- * Build the theme config object from plugin options for runtime injection.
213
- */
214
- function buildThemeConfig(theme) {
215
- if (!theme) return void 0;
216
- if (typeof theme === "string") return { default: theme };
217
- const themes = Array.isArray(theme) ? theme : [theme];
218
- const custom = {};
219
- for (const t of themes) custom[t.name] = {
220
- base: t.base,
221
- colors: t.colors
222
- };
223
- return {
224
- default: themes[0].name,
225
- custom
226
- };
227
- }
228
- //#endregion
229
- //#region src/art-module.ts
230
- /**
231
- * Art module generation for Musea.
195
+ * Rewrite relative import specifiers in the generated Storybook CSF file so that
196
+ * imports lifted verbatim from the art file's `<script setup>` resolve against
197
+ * the generated file's location instead of the original art file's directory.
232
198
  *
233
- * Generates the virtual ES modules that represent parsed `.art.vue` files,
234
- * including variant component definitions and script setup handling.
235
- */
236
- /**
237
- * Extract the content of the first <script setup> block from a Vue SFC source.
238
- */
239
- function extractScriptSetupContent(source) {
240
- return source.match(/<script\s+[^>]*setup[^>]*>([\s\S]*?)<\/script>/)?.[1]?.trim();
241
- }
242
- function extractScriptSetupIsolated(source) {
243
- const match = source.match(/<script\s+([^>]*)\bsetup\b([^>]*)>/);
244
- if (!match) return true;
245
- const isolate = `${match[1] ?? ""} ${match[2] ?? ""}`.match(/\bisolate\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/);
246
- return (isolate?.[1] ?? isolate?.[2] ?? isolate?.[3]) !== "false";
247
- }
248
- function resolveRelativeSpecifier(specifier, artDir) {
249
- if (!specifier.startsWith(".")) return specifier;
250
- return path.resolve(artDir, specifier);
251
- }
252
- function rewriteRelativeImportStatement(statement, artDir) {
253
- return statement.replace(/\bfrom\s+(['"])([^'"]+)\1/g, (_match, quote, specifier) => `from ${quote}${resolveRelativeSpecifier(specifier, artDir)}${quote}`).replace(/^(\s*import\s+)(['"])([^'"]+)\2(\s*;?\s*)$/s, (_match, prefix, quote, specifier, suffix) => `${prefix}${quote}${resolveRelativeSpecifier(specifier, artDir)}${quote}${suffix}`);
254
- }
255
- function escapeTemplateLiteral(str) {
256
- return str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
257
- }
258
- function countCharBalance(source, openChar, closeChar) {
259
- let balance = 0;
260
- for (const char of source) if (char === openChar) balance++;
261
- else if (char === closeChar) balance--;
262
- return balance;
263
- }
264
- function isCompleteImportStatement(statement) {
265
- const trimmed = statement.trim();
266
- if (!trimmed.startsWith("import ")) return false;
267
- if (countCharBalance(statement, "{", "}") > 0) return false;
268
- return /^import\s+[\s\S]+?\s+from\s+['"][^'"]+['"]\s*;?$/s.test(trimmed) || /^import\s+['"][^'"]+['"]\s*;?$/s.test(trimmed);
269
- }
270
- function splitTopLevelCommaList(source) {
271
- const parts = [];
272
- let current = "";
273
- let braceDepth = 0;
274
- let bracketDepth = 0;
275
- let parenDepth = 0;
276
- for (const char of source) {
277
- if (char === "," && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
278
- const trimmed = current.trim();
279
- if (trimmed) parts.push(trimmed);
280
- current = "";
281
- continue;
282
- }
283
- current += char;
284
- if (char === "{") braceDepth++;
285
- else if (char === "}") braceDepth--;
286
- else if (char === "[") bracketDepth++;
287
- else if (char === "]") bracketDepth--;
288
- else if (char === "(") parenDepth++;
289
- else if (char === ")") parenDepth--;
290
- }
291
- const trimmed = current.trim();
292
- if (trimmed) parts.push(trimmed);
293
- return parts;
294
- }
295
- function collectImportedNames(statement, returnNames) {
296
- const fromMatch = statement.replace(/\s+/g, " ").trim().replace(/;$/, "").match(/^import\s+(type\s+)?(.+?)\s+from\s+['"][^'"]+['"]$/);
297
- if (!fromMatch) return;
298
- if (fromMatch[1]) return;
299
- const specifierParts = splitTopLevelCommaList(fromMatch[2].trim());
300
- const defaultOrNamespace = specifierParts[0]?.trim() ?? "";
301
- const trailing = specifierParts.slice(1).join(", ").trim();
302
- if (defaultOrNamespace && !defaultOrNamespace.startsWith("{")) {
303
- const namespaceMatch = defaultOrNamespace.match(/^\*\s+as\s+([A-Za-z_$][\w$]*)$/);
304
- if (namespaceMatch) returnNames.add(namespaceMatch[1]);
305
- else if (!defaultOrNamespace.startsWith("type ")) returnNames.add(defaultOrNamespace);
306
- }
307
- const namedBlock = defaultOrNamespace.startsWith("{") ? defaultOrNamespace : trailing.startsWith("{") ? trailing : "";
308
- if (!namedBlock) return;
309
- const namedContent = namedBlock.slice(1, -1);
310
- for (const part of splitTopLevelCommaList(namedContent)) {
311
- const trimmed = part.trim();
312
- if (!trimmed || trimmed.startsWith("type ")) continue;
313
- const alias = trimmed.split(/\s+as\s+/).pop()?.trim();
314
- if (alias) returnNames.add(alias);
315
- }
316
- }
317
- function importDeclaresName(statement, name) {
318
- const names = /* @__PURE__ */ new Set();
319
- collectImportedNames(statement, names);
320
- return names.has(name);
321
- }
322
- function collectObjectDestructuredNames(statement, returnNames) {
323
- const match = statement.match(/^(?:export\s+)?(?:const|let|var)\s+\{([\s\S]*?)\}\s*=/);
324
- if (!match) return;
325
- for (const part of splitTopLevelCommaList(match[1])) {
326
- let name = part.trim();
327
- if (!name) continue;
328
- if (name.startsWith("...")) name = name.slice(3).trim();
329
- else if (name.includes(":")) name = name.split(":").pop().trim();
330
- else if (name.includes("=")) name = name.split("=")[0].trim();
331
- if (name.includes("=")) name = name.split("=")[0].trim();
332
- if (/^[A-Za-z_$][\w$]*$/.test(name)) returnNames.add(name);
333
- }
334
- }
335
- function collectArrayDestructuredNames(statement, returnNames) {
336
- const match = statement.match(/^(?:export\s+)?(?:const|let|var)\s+\[([\s\S]*?)\]\s*=/);
337
- if (!match) return;
338
- for (const part of splitTopLevelCommaList(match[1])) {
339
- let name = part.trim();
340
- if (!name) continue;
341
- if (name.startsWith("...")) name = name.slice(3).trim();
342
- if (name.includes("=")) name = name.split("=")[0].trim();
343
- if (/^[A-Za-z_$][\w$]*$/.test(name)) returnNames.add(name);
344
- }
345
- }
346
- function collectTopLevelReturnNames(setupBody, returnNames) {
347
- let braceDepth = 0;
348
- for (let i = 0; i < setupBody.length; i++) {
349
- const line = setupBody[i];
350
- const trimmed = line.trim();
351
- if (braceDepth === 0) {
352
- if (/^(?:export\s+)?(?:const|let|var)\s+\{/.test(trimmed)) {
353
- const statementLines = [line];
354
- let balance = countCharBalance(line, "{", "}") + countCharBalance(line, "[", "]") + countCharBalance(line, "(", ")");
355
- while (balance > 0 && i + 1 < setupBody.length) {
356
- i++;
357
- statementLines.push(setupBody[i]);
358
- balance += countCharBalance(setupBody[i], "{", "}") + countCharBalance(setupBody[i], "[", "]") + countCharBalance(setupBody[i], "(", ")");
359
- }
360
- collectObjectDestructuredNames(statementLines.join("\n"), returnNames);
361
- continue;
362
- }
363
- if (/^(?:export\s+)?(?:const|let|var)\s+\[/.test(trimmed)) {
364
- const statementLines = [line];
365
- let balance = countCharBalance(line, "{", "}") + countCharBalance(line, "[", "]") + countCharBalance(line, "(", ")");
366
- while (balance > 0 && i + 1 < setupBody.length) {
367
- i++;
368
- statementLines.push(setupBody[i]);
369
- balance += countCharBalance(setupBody[i], "{", "}") + countCharBalance(setupBody[i], "[", "]") + countCharBalance(setupBody[i], "(", ")");
370
- }
371
- collectArrayDestructuredNames(statementLines.join("\n"), returnNames);
372
- continue;
373
- }
374
- const constMatch = trimmed.match(/^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)/);
375
- if (constMatch) returnNames.add(constMatch[1]);
376
- const functionMatch = trimmed.match(/^(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(/);
377
- if (functionMatch) returnNames.add(functionMatch[1]);
378
- const classMatch = trimmed.match(/^(?:export\s+)?class\s+([A-Za-z_$][\w$]*)\b/);
379
- if (classMatch) returnNames.add(classMatch[1]);
380
- }
381
- braceDepth += countCharBalance(line, "{", "}");
382
- }
383
- }
384
- /**
385
- * Parse script setup content into imports and setup body.
386
- * Returns the import lines, setup body lines, and all identifiers to expose.
199
+ * The `__museaComponent` import is already rebased by
200
+ * `rewriteStorybookComponentImport` and is skipped here.
387
201
  */
388
- function parseScriptSetupForArt(content) {
389
- const lines = content.split("\n");
390
- const imports = [];
391
- const setupBody = [];
392
- const returnNames = /* @__PURE__ */ new Set();
393
- let currentImport = null;
394
- const defineArtComponent = extractDefineArtComponent(content);
395
- let defineArtBalance = 0;
396
- for (const line of lines) {
397
- const trimmed = line.trim();
398
- if (defineArtBalance > 0) {
399
- defineArtBalance += countCharBalance(line, "(", ")");
400
- continue;
401
- }
402
- if (currentImport) {
403
- currentImport.push(line);
404
- const statement = currentImport.join("\n");
405
- if (isCompleteImportStatement(statement)) {
406
- imports.push(statement);
407
- collectImportedNames(statement, returnNames);
408
- currentImport = null;
409
- }
410
- continue;
411
- }
412
- if (trimmed.startsWith("import ")) {
413
- currentImport = [line];
414
- const statement = currentImport.join("\n");
415
- if (isCompleteImportStatement(statement)) {
416
- imports.push(statement);
417
- collectImportedNames(statement, returnNames);
418
- currentImport = null;
419
- }
420
- continue;
421
- }
422
- if (isDefineArtLine(trimmed)) {
423
- defineArtBalance = Math.max(0, countCharBalance(line, "(", ")"));
424
- continue;
425
- }
426
- setupBody.push(line);
427
- }
428
- if (currentImport) {
429
- const statement = currentImport.join("\n");
430
- imports.push(statement);
431
- collectImportedNames(statement, returnNames);
432
- }
433
- collectTopLevelReturnNames(setupBody, returnNames);
434
- returnNames.delete("type");
435
- if (defineArtComponent.name) returnNames.delete(defineArtComponent.name);
436
- return {
437
- imports,
438
- setupBody,
439
- returnNames: [...returnNames],
440
- defineArtComponentName: defineArtComponent.name,
441
- defineArtComponentSource: defineArtComponent.source
442
- };
443
- }
444
- function extractDefineArtComponent(content) {
445
- const sourceMatch = content.match(/\bdefineArt\s*\(\s*(['"])([^'"]+)\1/);
446
- if (sourceMatch) return {
447
- name: componentNameFromSource(sourceMatch[2]),
448
- source: sourceMatch[2]
449
- };
450
- return { name: content.match(/\bdefineArt\s*\(\s*([A-Za-z_$][\w$]*)/)?.[1] };
451
- }
452
- function componentNameFromSource(source) {
453
- const withoutQuery = source.split(/[?#]/, 1)[0] || source;
454
- const filename = path.basename(withoutQuery);
455
- const extension = path.extname(filename);
456
- const name = toPascalCase(extension ? filename.slice(0, -extension.length) : filename);
457
- return name === "Variant" ? "MuseaComponent" : name;
202
+ function rewriteStorybookScriptImports(code, artPath, outputPath) {
203
+ const artDir = path.dirname(artPath);
204
+ const outDir = path.dirname(outputPath);
205
+ return code.replace(/(import\s+(?:[\s\S]*?)\s+from\s+)(['"])([^'"]+)\2(\s*;?)/g, (match, head, quote, specifier, tail) => {
206
+ if (!specifier.startsWith(".")) return match;
207
+ if (/\b__museaComponent\b/.test(head)) return match;
208
+ const resolved = path.resolve(artDir, specifier);
209
+ let rebased = normalizeGlobPath(path.relative(outDir, resolved));
210
+ if (!rebased.startsWith(".")) rebased = `./${rebased}`;
211
+ return `${head}${quote}${rebased}${quote}${tail}`;
212
+ });
458
213
  }
459
- function isDefineArtLine(trimmed) {
460
- return /\bdefineArt\s*\(/.test(trimmed);
214
+ function toPascalCase(str) {
215
+ const pascal = str.normalize("NFKD").replace(/[^\p{L}\p{N}]+/gu, " ").trim().split(/\s+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
216
+ if (!pascal) return "Variant";
217
+ return /^[\p{L}_$]/u.test(pascal) ? pascal : `Variant${pascal}`;
461
218
  }
462
- function generateArtModule(art, filePath, options = {}) {
463
- let componentImportPath;
464
- let componentTagName;
465
- let componentBindingName = "__MuseaComponent";
466
- const scriptSetup = art.scriptSetupContent ? parseScriptSetupForArt(art.scriptSetupContent) : null;
467
- const defineArtComponentName = scriptSetup?.defineArtComponentName;
468
- const defineArtComponentSource = scriptSetup?.defineArtComponentSource;
469
- if (art.isInline && art.componentPath) {
470
- componentImportPath = options.root ? resolveComponentSourcePath(art, filePath, allowedSourceRoots(options.root, options.scanRoots ?? [])) ?? void 0 : art.componentPath;
471
- componentTagName = "MuseaComponent";
472
- } else if (defineArtComponentSource || art.metadata.component) {
473
- const componentSource = defineArtComponentSource ?? art.metadata.component;
474
- if (componentSource) {
475
- const sourceArt = componentSource === art.metadata.component ? art : {
476
- ...art,
477
- metadata: {
478
- ...art.metadata,
479
- component: componentSource
480
- }
481
- };
482
- componentImportPath = options.root ? resolveComponentSourcePath(sourceArt, filePath, allowedSourceRoots(options.root, options.scanRoots ?? [])) ?? void 0 : path.isAbsolute(componentSource) ? componentSource : path.resolve(path.dirname(filePath), componentSource);
483
- }
484
- componentTagName = defineArtComponentName ?? (art.metadata.component ? componentNameFromSource(art.metadata.component) : "MuseaComponent");
485
- componentBindingName = componentTagName;
486
- }
487
- let code = `
488
- // Auto-generated module for: ${path.basename(filePath)}
489
- import { defineComponent, h } from 'vue';
490
- `;
491
- if (scriptSetup) {
492
- const artDir = path.dirname(filePath);
493
- for (const imp of scriptSetup.imports) {
494
- const resolved = rewriteRelativeImportStatement(imp, artDir);
495
- code += `${resolved}\n`;
496
- }
497
- }
498
- if (componentImportPath && componentTagName) {
499
- if (!scriptSetup?.imports.some((imp) => importDeclaresName(imp, componentBindingName))) code += `import ${componentBindingName} from ${JSON.stringify(componentImportPath)};\n`;
500
- code += `export const __component__ = ${componentBindingName};\n`;
501
- }
502
- code += `
503
- export const metadata = ${JSON.stringify(art.metadata)};
504
- export const variants = ${JSON.stringify(art.variants)};
505
- export const __styles__ = ${JSON.stringify(art.styleBlocks ?? [])};
506
- `;
507
- const hasSetupBody = scriptSetup?.setupBody.some((line) => line.trim().length > 0) ?? false;
508
- const hasSetup = !!scriptSetup && (hasSetupBody || scriptSetup.returnNames.length > 0);
509
- const setupReturn = `{ ${scriptSetup?.returnNames.join(", ") ?? ""} }`;
510
- const isolatedSetup = art.scriptSetupIsolated !== false;
511
- if (scriptSetup && hasSetup && !isolatedSetup) code += `
512
- const __museaSharedSetup = (() => {
513
- ${scriptSetup.setupBody.map((l) => ` ${l}`).join("\n")}
514
- return ${setupReturn};
515
- })();
516
- `;
517
- for (const variant of art.variants) {
518
- const variantComponentName = toPascalCase(variant.name);
519
- let template = variant.template;
520
- if (componentTagName) template = template.replace(/<Self/g, `<${componentTagName}`).replace(/<\/Self>/g, `</${componentTagName}>`);
521
- const escapedTemplate = escapeTemplateLiteral(template);
522
- const fullTemplate = `<div data-variant="${escapeTemplateLiteral(escapeHtml(variant.name))}">${escapedTemplate}</div>`;
523
- const componentNames = /* @__PURE__ */ new Map();
524
- if (componentTagName) componentNames.set(componentTagName, componentBindingName);
525
- if (scriptSetup) {
526
- for (const name of scriptSetup.returnNames) if (/^[A-Z]/.test(name)) componentNames.set(name, name);
527
- }
528
- const components = componentNames.size > 0 ? ` components: { ${[...componentNames].map(([name, value]) => `${JSON.stringify(name)}: ${value}`).join(", ")} },\n` : "";
529
- if (scriptSetup && hasSetup && isolatedSetup) code += `
530
- export const ${variantComponentName} = defineComponent({
531
- name: '${variantComponentName}',
532
- ${components} setup() {
533
- ${scriptSetup.setupBody.map((l) => ` ${l}`).join("\n")}
534
- return ${setupReturn};
535
- },
536
- template: \`${fullTemplate}\`,
537
- });
538
- `;
539
- else if (scriptSetup && hasSetup) code += `
540
- export const ${variantComponentName} = defineComponent({
541
- name: '${variantComponentName}',
542
- ${components} setup() {
543
- return __museaSharedSetup;
544
- },
545
- template: \`${fullTemplate}\`,
546
- });
547
- `;
548
- else if (componentTagName) code += `
549
- export const ${variantComponentName} = {
550
- name: '${variantComponentName}',
551
- ${components} template: \`${fullTemplate}\`,
552
- };
553
- `;
554
- else code += `
555
- export const ${variantComponentName} = {
556
- name: '${variantComponentName}',
557
- template: \`${fullTemplate}\`,
558
- };
559
- `;
560
- }
561
- const defaultVariant = art.variants.find((v) => v.isDefault) || art.variants[0];
562
- if (defaultVariant) code += `
563
- export default ${toPascalCase(defaultVariant.name)};
564
- `;
565
- return code;
219
+ function escapeHtml(str) {
220
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
221
+ }
222
+ /**
223
+ * Build the theme config object from plugin options for runtime injection.
224
+ */
225
+ function buildThemeConfig(theme) {
226
+ if (!theme) return void 0;
227
+ if (typeof theme === "string") return { default: theme };
228
+ const themes = Array.isArray(theme) ? theme : [theme];
229
+ const custom = {};
230
+ for (const t of themes) custom[t.name] = {
231
+ base: t.base,
232
+ colors: t.colors
233
+ };
234
+ return {
235
+ default: themes[0].name,
236
+ custom
237
+ };
566
238
  }
567
239
  //#endregion
568
240
  //#region src/preview/addons.ts
@@ -1181,53 +853,401 @@ function ensureArtStyles(styles) {
1181
853
  document.head.appendChild(tag);
1182
854
  }
1183
855
  }
1184
-
1185
- function renderError(title, error) {
1186
- container.textContent = '';
1187
- const root = document.createElement('div');
1188
- root.className = 'musea-error';
1189
-
1190
- const titleEl = document.createElement('div');
1191
- titleEl.className = 'musea-error-title';
1192
- titleEl.textContent = title;
1193
- root.appendChild(titleEl);
1194
-
1195
- const messageEl = document.createElement('div');
1196
- messageEl.textContent = error instanceof Error ? error.message : String(error);
1197
- root.appendChild(messageEl);
1198
-
1199
- container.appendChild(root);
856
+
857
+ function renderError(title, error) {
858
+ container.textContent = '';
859
+ const root = document.createElement('div');
860
+ root.className = 'musea-error';
861
+
862
+ const titleEl = document.createElement('div');
863
+ titleEl.className = 'musea-error-title';
864
+ titleEl.textContent = title;
865
+ root.appendChild(titleEl);
866
+
867
+ const messageEl = document.createElement('div');
868
+ messageEl.textContent = error instanceof Error ? error.message : String(error);
869
+ root.appendChild(messageEl);
870
+
871
+ container.appendChild(root);
872
+ }
873
+
874
+ async function mount() {
875
+ try {
876
+ const VariantComponent = artModule[${variantComponentNameLiteral}];
877
+ if (!VariantComponent) {
878
+ throw new Error('Variant component ' + ${variantComponentNameLiteral} + ' not found');
879
+ }
880
+
881
+ const WrappedComponent = {
882
+ render() {
883
+ return h(VariantComponent, propsOverride);
884
+ }
885
+ };
886
+
887
+ const app = createApp(WrappedComponent);
888
+ ensureArtStyles(artModule.__styles__);
889
+ ${setupCall}
890
+ container.innerHTML = '';
891
+ container.className = 'musea-variant';
892
+ app.mount(container);
893
+ console.log('[musea-preview] Mounted variant with props override:', ${variantNameLiteral});
894
+ __museaInitAddons(container, ${variantNameLiteral}, ${actionEvents});
895
+ } catch (error) {
896
+ console.error('[musea-preview] Failed to mount:', error);
897
+ renderError('Failed to render', error);
898
+ }
899
+ }
900
+
901
+ mount();
902
+ `;
903
+ }
904
+ //#endregion
905
+ //#region src/component-source.ts
906
+ function allowedSourceRoots(root, scanRoots = []) {
907
+ return [...new Set([root, ...scanRoots].map((sourceRoot) => path.resolve(sourceRoot)))];
908
+ }
909
+ function resolveComponentSourcePath(art, artPath, sourceRoots) {
910
+ const componentPath = art.isInline && art.componentPath ? art.componentPath : art.metadata.component ? path.isAbsolute(art.metadata.component) ? art.metadata.component : path.resolve(path.dirname(artPath), art.metadata.component) : null;
911
+ return componentPath ? resolveInsideAny(sourceRoots, componentPath, "component path") : null;
912
+ }
913
+ //#endregion
914
+ //#region src/art-module.ts
915
+ /**
916
+ * Art module generation for Musea.
917
+ *
918
+ * Generates the virtual ES modules that represent parsed `.art.vue` files,
919
+ * including variant component definitions and script setup handling.
920
+ */
921
+ /**
922
+ * Extract the content of the first <script setup> block from a Vue SFC source.
923
+ */
924
+ function extractScriptSetupContent(source) {
925
+ return source.match(/<script\s+[^>]*setup[^>]*>([\s\S]*?)<\/script>/)?.[1]?.trim();
926
+ }
927
+ function extractScriptSetupIsolated(source) {
928
+ const match = source.match(/<script\s+([^>]*)\bsetup\b([^>]*)>/);
929
+ if (!match) return true;
930
+ const isolate = `${match[1] ?? ""} ${match[2] ?? ""}`.match(/\bisolate\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/);
931
+ return (isolate?.[1] ?? isolate?.[2] ?? isolate?.[3]) !== "false";
932
+ }
933
+ function resolveRelativeSpecifier(specifier, artDir) {
934
+ if (!specifier.startsWith(".")) return specifier;
935
+ return path.resolve(artDir, specifier);
936
+ }
937
+ function rewriteRelativeImportStatement(statement, artDir) {
938
+ return statement.replace(/\bfrom\s+(['"])([^'"]+)\1/g, (_match, quote, specifier) => `from ${quote}${resolveRelativeSpecifier(specifier, artDir)}${quote}`).replace(/^(\s*import\s+)(['"])([^'"]+)\2(\s*;?\s*)$/s, (_match, prefix, quote, specifier, suffix) => `${prefix}${quote}${resolveRelativeSpecifier(specifier, artDir)}${quote}${suffix}`);
939
+ }
940
+ function escapeTemplateLiteral(str) {
941
+ return str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
942
+ }
943
+ function countCharBalance(source, openChar, closeChar) {
944
+ let balance = 0;
945
+ for (const char of source) if (char === openChar) balance++;
946
+ else if (char === closeChar) balance--;
947
+ return balance;
948
+ }
949
+ function isCompleteImportStatement(statement) {
950
+ const trimmed = statement.trim();
951
+ if (!trimmed.startsWith("import ")) return false;
952
+ if (countCharBalance(statement, "{", "}") > 0) return false;
953
+ return /^import\s+[\s\S]+?\s+from\s+['"][^'"]+['"]\s*;?$/s.test(trimmed) || /^import\s+['"][^'"]+['"]\s*;?$/s.test(trimmed);
954
+ }
955
+ function splitTopLevelCommaList(source) {
956
+ const parts = [];
957
+ let current = "";
958
+ let braceDepth = 0;
959
+ let bracketDepth = 0;
960
+ let parenDepth = 0;
961
+ for (const char of source) {
962
+ if (char === "," && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
963
+ const trimmed = current.trim();
964
+ if (trimmed) parts.push(trimmed);
965
+ current = "";
966
+ continue;
967
+ }
968
+ current += char;
969
+ if (char === "{") braceDepth++;
970
+ else if (char === "}") braceDepth--;
971
+ else if (char === "[") bracketDepth++;
972
+ else if (char === "]") bracketDepth--;
973
+ else if (char === "(") parenDepth++;
974
+ else if (char === ")") parenDepth--;
975
+ }
976
+ const trimmed = current.trim();
977
+ if (trimmed) parts.push(trimmed);
978
+ return parts;
979
+ }
980
+ function collectImportedNames(statement, returnNames) {
981
+ const fromMatch = statement.replace(/\s+/g, " ").trim().replace(/;$/, "").match(/^import\s+(type\s+)?(.+?)\s+from\s+['"][^'"]+['"]$/);
982
+ if (!fromMatch) return;
983
+ if (fromMatch[1]) return;
984
+ const specifierParts = splitTopLevelCommaList(fromMatch[2].trim());
985
+ const defaultOrNamespace = specifierParts[0]?.trim() ?? "";
986
+ const trailing = specifierParts.slice(1).join(", ").trim();
987
+ if (defaultOrNamespace && !defaultOrNamespace.startsWith("{")) {
988
+ const namespaceMatch = defaultOrNamespace.match(/^\*\s+as\s+([A-Za-z_$][\w$]*)$/);
989
+ if (namespaceMatch) returnNames.add(namespaceMatch[1]);
990
+ else if (!defaultOrNamespace.startsWith("type ")) returnNames.add(defaultOrNamespace);
991
+ }
992
+ const namedBlock = defaultOrNamespace.startsWith("{") ? defaultOrNamespace : trailing.startsWith("{") ? trailing : "";
993
+ if (!namedBlock) return;
994
+ const namedContent = namedBlock.slice(1, -1);
995
+ for (const part of splitTopLevelCommaList(namedContent)) {
996
+ const trimmed = part.trim();
997
+ if (!trimmed || trimmed.startsWith("type ")) continue;
998
+ const alias = trimmed.split(/\s+as\s+/).pop()?.trim();
999
+ if (alias) returnNames.add(alias);
1000
+ }
1001
+ }
1002
+ function importDeclaresName(statement, name) {
1003
+ const names = /* @__PURE__ */ new Set();
1004
+ collectImportedNames(statement, names);
1005
+ return names.has(name);
1006
+ }
1007
+ function collectObjectDestructuredNames(statement, returnNames) {
1008
+ const match = statement.match(/^(?:export\s+)?(?:const|let|var)\s+\{([\s\S]*?)\}\s*=/);
1009
+ if (!match) return;
1010
+ for (const part of splitTopLevelCommaList(match[1])) {
1011
+ let name = part.trim();
1012
+ if (!name) continue;
1013
+ if (name.startsWith("...")) name = name.slice(3).trim();
1014
+ else if (name.includes(":")) name = name.split(":").pop().trim();
1015
+ else if (name.includes("=")) name = name.split("=")[0].trim();
1016
+ if (name.includes("=")) name = name.split("=")[0].trim();
1017
+ if (/^[A-Za-z_$][\w$]*$/.test(name)) returnNames.add(name);
1018
+ }
1019
+ }
1020
+ function collectArrayDestructuredNames(statement, returnNames) {
1021
+ const match = statement.match(/^(?:export\s+)?(?:const|let|var)\s+\[([\s\S]*?)\]\s*=/);
1022
+ if (!match) return;
1023
+ for (const part of splitTopLevelCommaList(match[1])) {
1024
+ let name = part.trim();
1025
+ if (!name) continue;
1026
+ if (name.startsWith("...")) name = name.slice(3).trim();
1027
+ if (name.includes("=")) name = name.split("=")[0].trim();
1028
+ if (/^[A-Za-z_$][\w$]*$/.test(name)) returnNames.add(name);
1029
+ }
1030
+ }
1031
+ function collectTopLevelReturnNames(setupBody, returnNames) {
1032
+ let braceDepth = 0;
1033
+ for (let i = 0; i < setupBody.length; i++) {
1034
+ const line = setupBody[i];
1035
+ const trimmed = line.trim();
1036
+ if (braceDepth === 0) {
1037
+ if (/^(?:export\s+)?(?:const|let|var)\s+\{/.test(trimmed)) {
1038
+ const statementLines = [line];
1039
+ let balance = countCharBalance(line, "{", "}") + countCharBalance(line, "[", "]") + countCharBalance(line, "(", ")");
1040
+ while (balance > 0 && i + 1 < setupBody.length) {
1041
+ i++;
1042
+ statementLines.push(setupBody[i]);
1043
+ balance += countCharBalance(setupBody[i], "{", "}") + countCharBalance(setupBody[i], "[", "]") + countCharBalance(setupBody[i], "(", ")");
1044
+ }
1045
+ collectObjectDestructuredNames(statementLines.join("\n"), returnNames);
1046
+ continue;
1047
+ }
1048
+ if (/^(?:export\s+)?(?:const|let|var)\s+\[/.test(trimmed)) {
1049
+ const statementLines = [line];
1050
+ let balance = countCharBalance(line, "{", "}") + countCharBalance(line, "[", "]") + countCharBalance(line, "(", ")");
1051
+ while (balance > 0 && i + 1 < setupBody.length) {
1052
+ i++;
1053
+ statementLines.push(setupBody[i]);
1054
+ balance += countCharBalance(setupBody[i], "{", "}") + countCharBalance(setupBody[i], "[", "]") + countCharBalance(setupBody[i], "(", ")");
1055
+ }
1056
+ collectArrayDestructuredNames(statementLines.join("\n"), returnNames);
1057
+ continue;
1058
+ }
1059
+ const constMatch = trimmed.match(/^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)/);
1060
+ if (constMatch) returnNames.add(constMatch[1]);
1061
+ const functionMatch = trimmed.match(/^(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(/);
1062
+ if (functionMatch) returnNames.add(functionMatch[1]);
1063
+ const classMatch = trimmed.match(/^(?:export\s+)?class\s+([A-Za-z_$][\w$]*)\b/);
1064
+ if (classMatch) returnNames.add(classMatch[1]);
1065
+ }
1066
+ braceDepth += countCharBalance(line, "{", "}");
1067
+ }
1068
+ }
1069
+ /**
1070
+ * Parse script setup content into imports and setup body.
1071
+ * Returns the import lines, setup body lines, and all identifiers to expose.
1072
+ */
1073
+ function parseScriptSetupForArt(content) {
1074
+ const lines = content.split("\n");
1075
+ const imports = [];
1076
+ const setupBody = [];
1077
+ const returnNames = /* @__PURE__ */ new Set();
1078
+ let currentImport = null;
1079
+ const defineArtComponent = extractDefineArtComponent(content);
1080
+ let defineArtBalance = 0;
1081
+ for (const line of lines) {
1082
+ const trimmed = line.trim();
1083
+ if (defineArtBalance > 0) {
1084
+ defineArtBalance += countCharBalance(line, "(", ")");
1085
+ continue;
1086
+ }
1087
+ if (currentImport) {
1088
+ currentImport.push(line);
1089
+ const statement = currentImport.join("\n");
1090
+ if (isCompleteImportStatement(statement)) {
1091
+ imports.push(statement);
1092
+ collectImportedNames(statement, returnNames);
1093
+ currentImport = null;
1094
+ }
1095
+ continue;
1096
+ }
1097
+ if (trimmed.startsWith("import ")) {
1098
+ currentImport = [line];
1099
+ const statement = currentImport.join("\n");
1100
+ if (isCompleteImportStatement(statement)) {
1101
+ imports.push(statement);
1102
+ collectImportedNames(statement, returnNames);
1103
+ currentImport = null;
1104
+ }
1105
+ continue;
1106
+ }
1107
+ if (isDefineArtLine(trimmed)) {
1108
+ defineArtBalance = Math.max(0, countCharBalance(line, "(", ")"));
1109
+ continue;
1110
+ }
1111
+ setupBody.push(line);
1112
+ }
1113
+ if (currentImport) {
1114
+ const statement = currentImport.join("\n");
1115
+ imports.push(statement);
1116
+ collectImportedNames(statement, returnNames);
1117
+ }
1118
+ collectTopLevelReturnNames(setupBody, returnNames);
1119
+ returnNames.delete("type");
1120
+ if (defineArtComponent.name) returnNames.delete(defineArtComponent.name);
1121
+ return {
1122
+ imports,
1123
+ setupBody,
1124
+ returnNames: [...returnNames],
1125
+ defineArtComponentName: defineArtComponent.name,
1126
+ defineArtComponentSource: defineArtComponent.source
1127
+ };
1128
+ }
1129
+ function extractDefineArtComponent(content) {
1130
+ const sourceMatch = content.match(/\bdefineArt\s*\(\s*(['"])([^'"]+)\1/);
1131
+ if (sourceMatch) return {
1132
+ name: componentNameFromSource(sourceMatch[2]),
1133
+ source: sourceMatch[2]
1134
+ };
1135
+ return { name: content.match(/\bdefineArt\s*\(\s*([A-Za-z_$][\w$]*)/)?.[1] };
1200
1136
  }
1201
-
1202
- async function mount() {
1203
- try {
1204
- const VariantComponent = artModule[${variantComponentNameLiteral}];
1205
- if (!VariantComponent) {
1206
- throw new Error('Variant component ' + ${variantComponentNameLiteral} + ' not found');
1207
- }
1208
-
1209
- const WrappedComponent = {
1210
- render() {
1211
- return h(VariantComponent, propsOverride);
1212
- }
1213
- };
1214
-
1215
- const app = createApp(WrappedComponent);
1216
- ensureArtStyles(artModule.__styles__);
1217
- ${setupCall}
1218
- container.innerHTML = '';
1219
- container.className = 'musea-variant';
1220
- app.mount(container);
1221
- console.log('[musea-preview] Mounted variant with props override:', ${variantNameLiteral});
1222
- __museaInitAddons(container, ${variantNameLiteral}, ${actionEvents});
1223
- } catch (error) {
1224
- console.error('[musea-preview] Failed to mount:', error);
1225
- renderError('Failed to render', error);
1226
- }
1137
+ function componentNameFromSource(source) {
1138
+ const withoutQuery = source.split(/[?#]/, 1)[0] || source;
1139
+ const filename = path.basename(withoutQuery);
1140
+ const extension = path.extname(filename);
1141
+ const name = toPascalCase(extension ? filename.slice(0, -extension.length) : filename);
1142
+ return name === "Variant" ? "MuseaComponent" : name;
1227
1143
  }
1228
-
1229
- mount();
1144
+ function isDefineArtLine(trimmed) {
1145
+ return /\bdefineArt\s*\(/.test(trimmed);
1146
+ }
1147
+ function generateArtModule(art, filePath, options = {}) {
1148
+ let componentImportPath;
1149
+ let componentTagName;
1150
+ let componentBindingName = "__MuseaComponent";
1151
+ const scriptSetup = art.scriptSetupContent ? parseScriptSetupForArt(art.scriptSetupContent) : null;
1152
+ const defineArtComponentName = scriptSetup?.defineArtComponentName;
1153
+ const defineArtComponentSource = scriptSetup?.defineArtComponentSource;
1154
+ if (art.isInline && art.componentPath) {
1155
+ componentImportPath = options.root ? resolveComponentSourcePath(art, filePath, allowedSourceRoots(options.root, options.scanRoots ?? [])) ?? void 0 : art.componentPath;
1156
+ componentTagName = "MuseaComponent";
1157
+ } else if (defineArtComponentSource || art.metadata.component) {
1158
+ const componentSource = defineArtComponentSource ?? art.metadata.component;
1159
+ if (componentSource) {
1160
+ const sourceArt = componentSource === art.metadata.component ? art : {
1161
+ ...art,
1162
+ metadata: {
1163
+ ...art.metadata,
1164
+ component: componentSource
1165
+ }
1166
+ };
1167
+ componentImportPath = options.root ? resolveComponentSourcePath(sourceArt, filePath, allowedSourceRoots(options.root, options.scanRoots ?? [])) ?? void 0 : path.isAbsolute(componentSource) ? componentSource : path.resolve(path.dirname(filePath), componentSource);
1168
+ }
1169
+ componentTagName = defineArtComponentName ?? (art.metadata.component ? componentNameFromSource(art.metadata.component) : "MuseaComponent");
1170
+ componentBindingName = componentTagName;
1171
+ }
1172
+ let code = `
1173
+ // Auto-generated module for: ${path.basename(filePath)}
1174
+ import { defineComponent, h } from 'vue';
1175
+ `;
1176
+ if (scriptSetup) {
1177
+ const artDir = path.dirname(filePath);
1178
+ for (const imp of scriptSetup.imports) {
1179
+ const resolved = rewriteRelativeImportStatement(imp, artDir);
1180
+ code += `${resolved}\n`;
1181
+ }
1182
+ }
1183
+ if (componentImportPath && componentTagName) {
1184
+ if (!scriptSetup?.imports.some((imp) => importDeclaresName(imp, componentBindingName))) code += `import ${componentBindingName} from ${JSON.stringify(componentImportPath)};\n`;
1185
+ code += `export const __component__ = ${componentBindingName};\n`;
1186
+ }
1187
+ code += `
1188
+ export const metadata = ${JSON.stringify(art.metadata)};
1189
+ export const variants = ${JSON.stringify(art.variants)};
1190
+ export const __styles__ = ${JSON.stringify(art.styleBlocks ?? [])};
1191
+ `;
1192
+ const hasSetupBody = scriptSetup?.setupBody.some((line) => line.trim().length > 0) ?? false;
1193
+ const hasSetup = !!scriptSetup && (hasSetupBody || scriptSetup.returnNames.length > 0);
1194
+ const setupReturn = `{ ${scriptSetup?.returnNames.join(", ") ?? ""} }`;
1195
+ const isolatedSetup = art.scriptSetupIsolated !== false;
1196
+ if (scriptSetup && hasSetup && !isolatedSetup) code += `
1197
+ const __museaSharedSetup = (() => {
1198
+ ${scriptSetup.setupBody.map((l) => ` ${l}`).join("\n")}
1199
+ return ${setupReturn};
1200
+ })();
1201
+ `;
1202
+ for (const variant of art.variants) {
1203
+ const variantComponentName = toPascalCase(variant.name);
1204
+ let template = variant.template;
1205
+ if (componentTagName) template = template.replace(/<Self/g, `<${componentTagName}`).replace(/<\/Self>/g, `</${componentTagName}>`);
1206
+ const escapedTemplate = escapeTemplateLiteral(template);
1207
+ const fullTemplate = `<div data-variant="${escapeTemplateLiteral(escapeHtml(variant.name))}">${escapedTemplate}</div>`;
1208
+ const componentNames = /* @__PURE__ */ new Map();
1209
+ if (componentTagName) componentNames.set(componentTagName, componentBindingName);
1210
+ if (scriptSetup) {
1211
+ for (const name of scriptSetup.returnNames) if (/^[A-Z]/.test(name)) componentNames.set(name, name);
1212
+ }
1213
+ const components = componentNames.size > 0 ? ` components: { ${[...componentNames].map(([name, value]) => `${JSON.stringify(name)}: ${value}`).join(", ")} },\n` : "";
1214
+ if (scriptSetup && hasSetup && isolatedSetup) code += `
1215
+ export const ${variantComponentName} = defineComponent({
1216
+ name: '${variantComponentName}',
1217
+ ${components} setup() {
1218
+ ${scriptSetup.setupBody.map((l) => ` ${l}`).join("\n")}
1219
+ return ${setupReturn};
1220
+ },
1221
+ template: \`${fullTemplate}\`,
1222
+ });
1223
+ `;
1224
+ else if (scriptSetup && hasSetup) code += `
1225
+ export const ${variantComponentName} = defineComponent({
1226
+ name: '${variantComponentName}',
1227
+ ${components} setup() {
1228
+ return __museaSharedSetup;
1229
+ },
1230
+ template: \`${fullTemplate}\`,
1231
+ });
1232
+ `;
1233
+ else if (componentTagName) code += `
1234
+ export const ${variantComponentName} = {
1235
+ name: '${variantComponentName}',
1236
+ ${components} template: \`${fullTemplate}\`,
1237
+ };
1238
+ `;
1239
+ else code += `
1240
+ export const ${variantComponentName} = {
1241
+ name: '${variantComponentName}',
1242
+ template: \`${fullTemplate}\`,
1243
+ };
1244
+ `;
1245
+ }
1246
+ const defaultVariant = art.variants.find((v) => v.isDefault) || art.variants[0];
1247
+ if (defaultVariant) code += `
1248
+ export default ${toPascalCase(defaultVariant.name)};
1230
1249
  `;
1250
+ return code;
1231
1251
  }
1232
1252
  //#endregion
1233
1253
  //#region src/server-middleware.ts
@@ -2121,7 +2141,9 @@ async function processStyleDictionary(config) {
2121
2141
  //#endregion
2122
2142
  //#region src/api-tokens.ts
2123
2143
  function resolveTokensPath(ctx) {
2124
- return resolveInside(ctx.config.root, ctx.tokensPath, "tokensPath");
2144
+ const allowedRoots = [ctx.config.root, ...ctx.scanRoots];
2145
+ if (ctx.projectRoot) allowedRoots.push(ctx.projectRoot);
2146
+ return resolveInsideAny(allowedRoots, ctx.tokensPath, "tokensPath");
2125
2147
  }
2126
2148
  function sendTokenMutationError(e, sendError) {
2127
2149
  if (e instanceof HttpError) {
@@ -2922,6 +2944,55 @@ function resolveVueRuntimeCompiler() {
2922
2944
  }
2923
2945
  }
2924
2946
  //#endregion
2947
+ //#region src/plugin/storybook-inputs.ts
2948
+ function isStorybookTsxInput(filePath) {
2949
+ return /\.(?:stories|story)\.tsx$/i.test(filePath);
2950
+ }
2951
+ async function scanStorybookTsxInputs(root, include, exclude) {
2952
+ const files = /* @__PURE__ */ new Set();
2953
+ const visitedDirs = /* @__PURE__ */ new Set();
2954
+ async function scan(dir) {
2955
+ const resolvedDir = path.resolve(dir);
2956
+ if (visitedDirs.has(resolvedDir)) return;
2957
+ visitedDirs.add(resolvedDir);
2958
+ let entries;
2959
+ try {
2960
+ entries = await fs.promises.readdir(resolvedDir, { withFileTypes: true });
2961
+ } catch {
2962
+ return;
2963
+ }
2964
+ for (const entry of entries) {
2965
+ const fullPath = path.join(resolvedDir, entry.name);
2966
+ if (isExcluded(fullPath, entry.name, exclude, root)) continue;
2967
+ if (entry.isDirectory()) await scan(fullPath);
2968
+ else if (entry.isFile() && isStorybookTsxInput(entry.name) && shouldProcess(fullPath, include, exclude, root)) files.add(fullPath);
2969
+ }
2970
+ }
2971
+ for (const scanRoot of resolveScanRoots(root, include)) await scan(scanRoot);
2972
+ return [...files].sort();
2973
+ }
2974
+ async function assertNoUnsupportedStorybookTsxInputs(root, include, exclude) {
2975
+ const files = await scanStorybookTsxInputs(root, include, exclude);
2976
+ if (files.length > 0) throw new Error(formatUnsupportedStorybookTsxInputError(root, files));
2977
+ }
2978
+ function formatUnsupportedStorybookTsxInputError(root, files) {
2979
+ return [
2980
+ "[musea] Storybook TSX files matched by include are not supported as Musea art inputs yet.",
2981
+ "Remove *.stories.tsx from musea.include or migrate those stories to .art.vue files.",
2982
+ "Matched files:",
2983
+ `${files.slice(0, 5).map((file) => ` - ${relativePath(root, file)}`).join("\n")}${files.length > 5 ? `\n ... and ${files.length - 5} more` : ""}`
2984
+ ].join("\n");
2985
+ }
2986
+ function isExcluded(filePath, name, exclude, root) {
2987
+ return exclude.some((pattern) => matchesPathPattern(filePath, pattern, root) || matchGlob(name, pattern));
2988
+ }
2989
+ function matchesPathPattern(filePath, pattern, root) {
2990
+ return matchGlob(path.isAbsolute(pattern) ? path.resolve(filePath) : path.relative(root, filePath), pattern);
2991
+ }
2992
+ function relativePath(root, filePath) {
2993
+ return path.relative(root, filePath).split(path.sep).join("/");
2994
+ }
2995
+ //#endregion
2925
2996
  //#region src/static-data.ts
2926
2997
  function staticPreviewId(artPath, variantName) {
2927
2998
  return crypto.createHash("sha256").update(artPath).update("\0").update(variantName).digest("hex").slice(0, 20);
@@ -2980,6 +3051,7 @@ function createApiContext(ctx) {
2980
3051
  artFiles: ctx.artFiles,
2981
3052
  scanRoots: ctx.scanRoots,
2982
3053
  tokensPath: ctx.tokensPath,
3054
+ projectRoot: ctx.projectRoot,
2983
3055
  basePath: ctx.basePath,
2984
3056
  resolvedPreviewCss: ctx.resolvedPreviewCss,
2985
3057
  resolvedPreviewSetup: ctx.resolvedPreviewSetup,
@@ -3267,7 +3339,27 @@ function relativeUrl(fromDir, toFile) {
3267
3339
  return relative.startsWith(".") ? relative : `./${relative}`;
3268
3340
  }
3269
3341
  //#endregion
3270
- //#region src/plugin/index.ts
3342
+ //#region src/plugin/config.ts
3343
+ async function resolveMuseaSharedConfig(resolvedConfig) {
3344
+ const sharedConfig = vizeConfigStore.get(resolvedConfig.root);
3345
+ if (sharedConfig) return sharedConfig;
3346
+ const configFile = process.env[VIZE_CONFIG_FILE_ENV];
3347
+ if (!configFile) return null;
3348
+ try {
3349
+ return await loadConfig(resolvedConfig.root, {
3350
+ configFile,
3351
+ env: {
3352
+ mode: resolvedConfig.mode,
3353
+ command: resolvedConfig.command === "build" ? "build" : "serve",
3354
+ isSsrBuild: !!resolvedConfig.build?.ssr
3355
+ }
3356
+ });
3357
+ } catch (error) {
3358
+ throw new Error(`[musea] Failed to load Vize config from ${configFile}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
3359
+ }
3360
+ }
3361
+ //#endregion
3362
+ //#region src/plugin/art-processing.ts
3271
3363
  function extractArtTagAttributes(source) {
3272
3364
  const artTagMatch = source.match(/<art\b([\s\S]*?)>/i);
3273
3365
  if (!artTagMatch) return {};
@@ -3303,6 +3395,62 @@ function extractStyleBlocks(source) {
3303
3395
  }
3304
3396
  return styles;
3305
3397
  }
3398
+ function formatArtProcessingError(root, filePath, error) {
3399
+ const detail = error instanceof Error ? error.message || error.name : error == null ? "unknown error" : stringifyUnknown(error);
3400
+ return `[musea] Failed to process ${path.relative(root, filePath) || filePath}: ${detail}`;
3401
+ }
3402
+ function stringifyUnknown(value) {
3403
+ if (typeof value === "string") return value;
3404
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return value.toString();
3405
+ try {
3406
+ return JSON.stringify(value) ?? "unknown error";
3407
+ } catch {
3408
+ return "unknown error";
3409
+ }
3410
+ }
3411
+ async function processMuseaArtFile(filePath, ctx) {
3412
+ try {
3413
+ const source = await fs.promises.readFile(filePath, "utf-8");
3414
+ const parsed = loadNative().parseArt(source, { filename: filePath });
3415
+ const customMetadata = extractCustomArtMetadata(source);
3416
+ if (!parsed.variants || parsed.variants.length === 0) return null;
3417
+ const isInline = !filePath.endsWith(".art.vue");
3418
+ return {
3419
+ path: filePath,
3420
+ metadata: {
3421
+ title: parsed.metadata.title || (isInline ? path.basename(filePath, ".vue") : ""),
3422
+ description: parsed.metadata.description,
3423
+ component: isInline ? void 0 : parsed.metadata.component,
3424
+ category: parsed.metadata.category,
3425
+ tags: parsed.metadata.tags,
3426
+ status: parsed.metadata.status,
3427
+ order: parsed.metadata.order,
3428
+ actionEvents: customMetadata.actionEvents ?? parsed.metadata.actionEvents
3429
+ },
3430
+ variants: parsed.variants.map((v) => ({
3431
+ name: v.name,
3432
+ template: v.template,
3433
+ isDefault: v.isDefault,
3434
+ skipVrt: v.skipVrt
3435
+ })),
3436
+ hasScriptSetup: isInline ? false : parsed.hasScriptSetup,
3437
+ scriptSetupContent: !isInline && parsed.hasScriptSetup ? extractScriptSetupContent(source) : void 0,
3438
+ scriptSetupIsolated: !isInline && parsed.hasScriptSetup ? extractScriptSetupIsolated(source) : true,
3439
+ hasScript: parsed.hasScript,
3440
+ styleCount: parsed.styleCount,
3441
+ styleBlocks: isInline ? [] : extractStyleBlocks(source),
3442
+ isInline,
3443
+ componentPath: isInline ? filePath : void 0
3444
+ };
3445
+ } catch (error) {
3446
+ const message = formatArtProcessingError(ctx.root, filePath, error);
3447
+ if (ctx.command === "build") throw new Error(message);
3448
+ (ctx.onError ?? console.error)(message);
3449
+ return null;
3450
+ }
3451
+ }
3452
+ //#endregion
3453
+ //#region src/plugin/index.ts
3306
3454
  function musea(options = {}) {
3307
3455
  let include = options.include ?? ["**/*.art.vue"];
3308
3456
  let exclude = options.exclude ?? ["node_modules/**", "dist/**"];
@@ -3311,6 +3459,7 @@ function musea(options = {}) {
3311
3459
  const storybookOutDir = options.storybookOutDir ?? ".storybook/stories";
3312
3460
  let inlineArt = options.inlineArt ?? false;
3313
3461
  const tokensPath = options.tokensPath;
3462
+ const projectRootOption = options.projectRoot;
3314
3463
  const themeConfig = buildThemeConfig(options.theme);
3315
3464
  const previewCss = options.previewCss ?? [];
3316
3465
  const previewSetup = options.previewSetup;
@@ -3352,9 +3501,9 @@ function musea(options = {}) {
3352
3501
  };
3353
3502
  },
3354
3503
  options: applyMuseaStaticBuildInput,
3355
- configResolved(resolvedConfig) {
3504
+ async configResolved(resolvedConfig) {
3356
3505
  config = resolvedConfig;
3357
- const vizeConfig = vizeConfigStore.get(resolvedConfig.root);
3506
+ const vizeConfig = await resolveMuseaSharedConfig(resolvedConfig);
3358
3507
  if (vizeConfig?.musea) {
3359
3508
  const mc = vizeConfig.musea;
3360
3509
  if (!options.include && mc.include) include = mc.include;
@@ -3386,6 +3535,7 @@ function musea(options = {}) {
3386
3535
  artFiles,
3387
3536
  scanRoots,
3388
3537
  tokensPath,
3538
+ projectRoot: resolveProjectRoot(projectRootOption, config.root),
3389
3539
  basePath,
3390
3540
  resolvedPreviewCss,
3391
3541
  resolvedPreviewSetup,
@@ -3433,7 +3583,12 @@ function musea(options = {}) {
3433
3583
  if (address && typeof address === "object") {
3434
3584
  const protocol = devServer.config.server.https ? "https" : "http";
3435
3585
  const rawHost = address.address;
3436
- const url = `${protocol}://${rawHost === "::" || rawHost === "::1" || rawHost === "0.0.0.0" || rawHost === "127.0.0.1" ? "localhost" : rawHost}:${address.port}${basePath}`;
3586
+ const url = `${protocol}://${[
3587
+ "::",
3588
+ "::1",
3589
+ "0.0.0.0",
3590
+ "127.0.0.1"
3591
+ ].includes(rawHost) ? "localhost" : rawHost}:${address.port}${basePath}`;
3437
3592
  console.log();
3438
3593
  console.log(` \x1b[36m➜\x1b[0m \x1b[1mMusea Gallery:\x1b[0m \x1b[36m${url}\x1b[0m`);
3439
3594
  }
@@ -3442,6 +3597,7 @@ function musea(options = {}) {
3442
3597
  },
3443
3598
  async buildStart() {
3444
3599
  console.log(`[musea] config.root: ${config.root}, include: ${JSON.stringify(include)}`);
3600
+ if (storybookCompat && staticBuildEnabled) await assertNoUnsupportedStorybookTsxInputs(config.root, include, exclude);
3445
3601
  const files = await scanArtFiles(config.root, include, exclude, inlineArt);
3446
3602
  console.log(`[musea] Found ${files.length} art files`);
3447
3603
  if (server) watchMuseaArtFiles(server.watcher, files);
@@ -3461,6 +3617,7 @@ function musea(options = {}) {
3461
3617
  artFiles,
3462
3618
  scanRoots,
3463
3619
  tokensPath,
3620
+ projectRoot: resolveProjectRoot(projectRootOption, config.root),
3464
3621
  basePath,
3465
3622
  resolvedPreviewCss,
3466
3623
  resolvedPreviewSetup,
@@ -3482,46 +3639,19 @@ function musea(options = {}) {
3482
3639
  handleHotUpdate: createHandleHotUpdate(virtualState)
3483
3640
  };
3484
3641
  async function processArtFile(filePath) {
3485
- try {
3486
- const source = await fs.promises.readFile(filePath, "utf-8");
3487
- const parsed = loadNative().parseArt(source, { filename: filePath });
3488
- const customMetadata = extractCustomArtMetadata(source);
3489
- if (!parsed.variants || parsed.variants.length === 0) return;
3490
- const isInline = !filePath.endsWith(".art.vue");
3491
- const info = {
3492
- path: filePath,
3493
- metadata: {
3494
- title: parsed.metadata.title || (isInline ? path.basename(filePath, ".vue") : ""),
3495
- description: parsed.metadata.description,
3496
- component: isInline ? void 0 : parsed.metadata.component,
3497
- category: parsed.metadata.category,
3498
- tags: parsed.metadata.tags,
3499
- status: parsed.metadata.status,
3500
- order: parsed.metadata.order,
3501
- actionEvents: customMetadata.actionEvents ?? parsed.metadata.actionEvents
3502
- },
3503
- variants: parsed.variants.map((v) => ({
3504
- name: v.name,
3505
- template: v.template,
3506
- isDefault: v.isDefault,
3507
- skipVrt: v.skipVrt
3508
- })),
3509
- hasScriptSetup: isInline ? false : parsed.hasScriptSetup,
3510
- scriptSetupContent: !isInline && parsed.hasScriptSetup ? extractScriptSetupContent(source) : void 0,
3511
- scriptSetupIsolated: !isInline && parsed.hasScriptSetup ? extractScriptSetupIsolated(source) : true,
3512
- hasScript: parsed.hasScript,
3513
- styleCount: parsed.styleCount,
3514
- styleBlocks: isInline ? [] : extractStyleBlocks(source),
3515
- isInline,
3516
- componentPath: isInline ? filePath : void 0
3517
- };
3518
- artFiles.set(filePath, info);
3519
- } catch (e) {
3520
- console.error(`[musea] Failed to process ${filePath}:`, e);
3521
- }
3642
+ const info = await processMuseaArtFile(filePath, {
3643
+ root: config.root,
3644
+ command: config.command
3645
+ });
3646
+ if (info) artFiles.set(filePath, info);
3522
3647
  }
3523
3648
  return [mainPlugin];
3524
3649
  }
3650
+ function resolveProjectRoot(projectRoot, viteRoot) {
3651
+ if (!projectRoot) return void 0;
3652
+ const absolute = path.isAbsolute(projectRoot) ? projectRoot : path.resolve(viteRoot, projectRoot);
3653
+ return absolute === path.resolve(viteRoot) ? void 0 : absolute;
3654
+ }
3525
3655
  //#endregion
3526
3656
  //#region src/index.ts
3527
3657
  var src_default = musea;