@vizejs/vite-plugin-musea 0.0.1-alpha.101 → 0.0.1-alpha.103

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.
@@ -1,4 +1,4 @@
1
- import { A11yOptions, A11yResult, ArtFileInfo, MuseaVrtRunner$1 as MuseaVrtRunner } from "./vrt-m01uFerp.js";
1
+ import { A11yOptions, A11yResult, ArtFileInfo, MuseaVrtRunner$1 as MuseaVrtRunner } from "./vrt-Vb4aqPZE.js";
2
2
  import { Page } from "playwright";
3
3
 
4
4
  //#region src/a11y.d.ts
@@ -58,4 +58,4 @@ declare class MuseaA11yRunner {
58
58
 
59
59
  //# sourceMappingURL=a11y.d.ts.map
60
60
  export { A11ySummary, MuseaA11yRunner as MuseaA11yRunner$1 };
61
- //# sourceMappingURL=a11y-CHcxz6UR.d.ts.map
61
+ //# sourceMappingURL=a11y-Bvx5TJb8.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"a11y-CHcxz6UR.d.ts","names":[],"sources":["../src/a11y.ts"],"sourcesContent":null,"mappings":";;;;;;;;AAmBA;;;UAAiB,WAAA;EA4BJ,eAAA,EAAA,MAAe;EAAA,aAAA,EAAA,MAAA;EAAA,eAGL,EAAA,MAAA;EAAgB,aAczB,EAAA,MAAA;EAAW,YAET,EAAA,MAAA;EAAc,aACjB,EAAA,MAAA;EAAU,UAAlB,EAAA,MAAA;;;;;AAkGgC,cAtHxB,eAAA,CAsHwB;EAAW,QAkBlB,OAAA;EAAU,WA6IV,CAAA,OAAA,CAAA,EAlRP,WAkRO;EAAU;;;;sBApQ1B,4CAEE,iBACX,QAAQ;;;;kBA+DW,6CAA6C,QAAQ;;;;sBAmCvD,eAAe;;;;8BAkBP;;;;8BA6IA"}
1
+ {"version":3,"file":"a11y-Bvx5TJb8.d.ts","names":[],"sources":["../src/a11y.ts"],"sourcesContent":null,"mappings":";;;;;;;;AAmBA;;;UAAiB,WAAA;EA4BJ,eAAA,EAAA,MAAe;EAAA,aAAA,EAAA,MAAA;EAAA,eAGL,EAAA,MAAA;EAAgB,aAczB,EAAA,MAAA;EAAW,YAET,EAAA,MAAA;EAAc,aACjB,EAAA,MAAA;EAAU,UAAlB,EAAA,MAAA;;;;;AAkGgC,cAtHxB,eAAA,CAsHwB;EAAW,QAkBlB,OAAA;EAAU,WA6IV,CAAA,OAAA,CAAA,EAlRP,WAkRO;EAAU;;;;sBApQ1B,4CAEE,iBACX,QAAQ;;;;kBA+DW,6CAA6C,QAAQ;;;;sBAmCvD,eAAe;;;;8BAkBP;;;;8BA6IA"}
package/dist/a11y.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import "./vrt-m01uFerp.js";
2
- import { A11ySummary, MuseaA11yRunner$1 as MuseaA11yRunner } from "./a11y-CHcxz6UR.js";
1
+ import "./vrt-Vb4aqPZE.js";
2
+ import { A11ySummary, MuseaA11yRunner$1 as MuseaA11yRunner } from "./a11y-Bvx5TJb8.js";
3
3
  export { A11ySummary, MuseaA11yRunner };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A11yOptions, A11yResult, AnalysisApiResponse, ArtFileInfo, ArtMetadata, ArtVariant, CaptureConfig, CiConfig, ComparisonConfig, CsfOutput, MuseaOptions, MuseaTheme, MuseaThemeColors, MuseaVrtRunner$1 as MuseaVrtRunner, PaletteApiResponse, ViewportConfig, VrtOptions, VrtResult, VrtSummary, generateVrtJsonReport$1 as generateVrtJsonReport, generateVrtReport$1 as generateVrtReport } from "./vrt-m01uFerp.js";
2
- import { A11ySummary, MuseaA11yRunner$1 as MuseaA11yRunner } from "./a11y-CHcxz6UR.js";
1
+ import { A11yOptions, A11yResult, AnalysisApiResponse, ArtFileInfo, ArtMetadata, ArtVariant, CaptureConfig, CiConfig, ComparisonConfig, CsfOutput, MuseaOptions, MuseaTheme, MuseaThemeColors, MuseaVrtRunner$1 as MuseaVrtRunner, PaletteApiResponse, ViewportConfig, VrtOptions, VrtResult, VrtSummary, generateVrtJsonReport$1 as generateVrtJsonReport, generateVrtReport$1 as generateVrtReport } from "./vrt-Vb4aqPZE.js";
2
+ import { A11ySummary, MuseaA11yRunner$1 as MuseaA11yRunner } from "./a11y-Bvx5TJb8.js";
3
3
  import { AutogenOptions, AutogenOutput, GeneratedVariant, PropDefinition, generateArtFile$1 as generateArtFile, writeArtFile$1 as writeArtFile } from "./autogen-D3Zjc3zI.js";
4
4
  import { Plugin } from "vite";
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/style-dictionary.ts","../src/index.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;;;;;;;;;;;UAWiB,WAAA;;;;eAIF;;EAJE,UAAA,CAAA,EAAA,MAAW;;;;AAa5B;;AAEyB,UAFR,aAAA,CAEQ;EAAW,IAA1B,EAAA,MAAA;EAAM,MACE,EADR,MACQ,CAAA,MAAA,EADO,WACP,CAAA;EAAa,aAAA,CAAA,EAAb,aAAa,EAAA;;;;AAM/B;UAAiB,qBAAA;cACH;;IAWG,IAAA,EAAA,MAAA;;;;AA2BjB;;;;AAAgF,UA3B/D,qBAAA,CA2B+D;;;;EAK1D,UAAA,EAAA,MAAW;EAAA;;;AAA6B;;;;AAuJ9D;;EAA6B,SACf,CAAA,EAAA,MAAA;EAAa;;AAElB;eArKM;;;AAkMf;;AACc,KA7LF,cAAA,GA6LE,CAAA,KAAA,EA7LuB,WA6LvB,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,GA7LuD,WA6LvD;;;AACI;iBAzLI,WAAA,sBAAiC,QAAQ;;;AAsY/D;iBA/OgB,aAAA,aACF,qCAEX,eAAe;;;AA8WlB;iBAjVgB,iBAAA,aACF,2BACF,eAAe;;;AAqX3B;;;;;iBAxKgB,kBAAA,aAA+B;ACjO/C;;;AAAmD,iBDmWnC,sBAAA,CCnWmC,UAAA,EDmWA,aCnWA,EAAA,CAAA,EAAA,MAAA;AAAM;;;iBDyYnC,sBAAA,SACZ,wBACP,QAAQ;UA6CM,eAAA;;;;;UAMA,eAAA;;;;WAIN;;KAGC,aAAA,GAAgB,eAAe;;;;;;;;;;;iBAgC3B,cAAA,WACJ;;;;;;cACA,eAAe,eACxB;;;;AA/rB4B;;;iBCuNf,KAAA,WAAe,eAAoB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/style-dictionary.ts","../src/index.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;;;;;;;;;;;UAWiB,WAAA;;;;eAIF;;EAJE,UAAA,CAAA,EAAA,MAAW;;;;AAa5B;;AAEyB,UAFR,aAAA,CAEQ;EAAW,IAA1B,EAAA,MAAA;EAAM,MACE,EADR,MACQ,CAAA,MAAA,EADO,WACP,CAAA;EAAa,aAAA,CAAA,EAAb,aAAa,EAAA;;;;AAM/B;UAAiB,qBAAA;cACH;;IAWG,IAAA,EAAA,MAAA;;;;AA2BjB;;;;AAAgF,UA3B/D,qBAAA,CA2B+D;;;;EAK1D,UAAA,EAAA,MAAW;EAAA;;;AAA6B;;;;AA2J9D;;EAA6B,SACf,CAAA,EAAA,MAAA;EAAa;;AAElB;eAzKM;;;AAoMf;;AACc,KA/LF,cAAA,GA+LE,CAAA,KAAA,EA/LuB,WA+LvB,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,GA/LuD,WA+LvD;;;AACI;iBA3LI,WAAA,sBAAiC,QAAQ;;;AAgY/D;iBArOgB,aAAA,aACF,qCAEX,eAAe;;;AAoWlB;iBAzUgB,iBAAA,aACF,2BACF,eAAe;;;AA6W3B;;;;;iBAxKgB,kBAAA,aAA+B;ACtH/C;;;AAAmD,iBDwPnC,sBAAA,CCxPmC,UAAA,EDwPA,aCxPA,EAAA,CAAA,EAAA,MAAA;AAAM;;;iBD8RnC,sBAAA,SACZ,wBACP,QAAQ;UA6CM,eAAA;;;;;UAMA,eAAA;;;;WAIN;;KAGC,aAAA,GAAgB,eAAe;;;;;;;;;;;iBA8B3B,cAAA,WACJ;;;;;;cACA,eAAe,eACxB;;;;AAvrB4B;;;iBC4Tf,KAAA,WAAe,eAAoB"}
package/dist/index.js CHANGED
@@ -86,15 +86,15 @@ function extractSubcategories(obj) {
86
86
  function isTokenValue(value) {
87
87
  if (typeof value !== "object" || value === null) return false;
88
88
  const obj = value;
89
- return "value" in obj && (typeof obj.value === "string" || typeof obj.value === "number");
89
+ return "value" in obj && (typeof obj.value === "string" || typeof obj.value === "number") || "$value" in obj && (typeof obj.$value === "string" || typeof obj.$value === "number");
90
90
  }
91
91
  /**
92
92
  * Normalize token to DesignToken interface.
93
93
  */
94
94
  function normalizeToken(raw) {
95
95
  const token = {
96
- value: raw.value,
97
- type: raw.type,
96
+ value: raw.value ?? raw.$value,
97
+ type: raw.type ?? raw.$type,
98
98
  description: raw.description,
99
99
  attributes: raw.attributes
100
100
  };
@@ -549,6 +549,71 @@ function loadNative() {
549
549
  }
550
550
  }
551
551
  /**
552
+ * JS-based fallback for SFC analysis when native `analyzeSfc` is not available.
553
+ * Uses regex parsing to extract props and emits from Vue SFC source.
554
+ */
555
+ function analyzeSfcFallback(source, _options) {
556
+ try {
557
+ const props = [];
558
+ const emits = [];
559
+ const scriptSetupMatch = source.match(/<script\s+[^>]*setup[^>]*>([\s\S]*?)<\/script>/);
560
+ if (!scriptSetupMatch) {
561
+ const scriptMatch = source.match(/<script[^>]*>([\s\S]*?)<\/script>/);
562
+ if (!scriptMatch) return {
563
+ props: [],
564
+ emits: []
565
+ };
566
+ }
567
+ const scriptContent = scriptSetupMatch?.[1] || "";
568
+ const propsMatch = scriptContent.match(/defineProps\s*<\s*\{([\s\S]*?)\}>\s*\(/);
569
+ const propsMatch2 = scriptContent.match(/defineProps\s*<\s*\{([\s\S]*?)\}>/);
570
+ const propsBody = propsMatch?.[1] || propsMatch2?.[1];
571
+ if (propsBody) {
572
+ const lines = propsBody.split("\n");
573
+ let i = 0;
574
+ while (i < lines.length) {
575
+ const line = lines[i].trim();
576
+ if (line.startsWith("/**") || line.startsWith("*") || line.startsWith("*/")) {
577
+ i++;
578
+ continue;
579
+ }
580
+ const propMatch = line.match(/^(\w+)(\?)?:\s*(.+?)(?:;?\s*)$/);
581
+ if (propMatch) {
582
+ const name = propMatch[1];
583
+ const optional = !!propMatch[2];
584
+ let type = propMatch[3].replace(/;$/, "").trim();
585
+ const defaultPattern = new RegExp(`\\b${name}\\s*=\\s*([^,}\\n]+)`);
586
+ const defaultMatch = scriptContent.match(defaultPattern);
587
+ const defaultValue = defaultMatch ? defaultMatch[1].trim() : void 0;
588
+ props.push({
589
+ name,
590
+ type,
591
+ required: !optional && defaultValue === void 0,
592
+ ...defaultValue !== void 0 ? { default_value: defaultValue } : {}
593
+ });
594
+ }
595
+ i++;
596
+ }
597
+ }
598
+ const emitsMatch = scriptContent.match(/defineEmits\s*<\s*\{([\s\S]*?)\}>/);
599
+ if (emitsMatch) {
600
+ const emitsBody = emitsMatch[1];
601
+ const emitRegex = /(\w+)\s*:/g;
602
+ let match;
603
+ while ((match = emitRegex.exec(emitsBody)) !== null) emits.push(match[1]);
604
+ }
605
+ return {
606
+ props,
607
+ emits
608
+ };
609
+ } catch {
610
+ return {
611
+ props: [],
612
+ emits: []
613
+ };
614
+ }
615
+ }
616
+ /**
552
617
  * Build the theme config object from plugin options for runtime injection.
553
618
  */
554
619
  function buildThemeConfig(theme) {
@@ -577,9 +642,13 @@ function musea(options = {}) {
577
642
  let inlineArt = options.inlineArt ?? false;
578
643
  const tokensPath = options.tokensPath;
579
644
  const themeConfig = buildThemeConfig(options.theme);
645
+ const previewCss = options.previewCss ?? [];
646
+ const previewSetup = options.previewSetup;
580
647
  let config;
581
648
  let server = null;
582
649
  const artFiles = new Map();
650
+ let resolvedPreviewCss = [];
651
+ let resolvedPreviewSetup = null;
583
652
  const mainPlugin = {
584
653
  name: "vite-plugin-musea",
585
654
  enforce: "pre",
@@ -597,6 +666,8 @@ function musea(options = {}) {
597
666
  if (options.storybookCompat === void 0 && mc.storybookCompat !== void 0) storybookCompat = mc.storybookCompat;
598
667
  if (options.inlineArt === void 0 && mc.inlineArt !== void 0) inlineArt = mc.inlineArt;
599
668
  }
669
+ resolvedPreviewCss = previewCss.map((cssPath) => path.isAbsolute(cssPath) ? cssPath : path.resolve(resolvedConfig.root, cssPath));
670
+ if (previewSetup) resolvedPreviewSetup = path.isAbsolute(previewSetup) ? previewSetup : path.resolve(resolvedConfig.root, previewSetup);
600
671
  },
601
672
  configureServer(devServer) {
602
673
  server = devServer;
@@ -610,7 +681,6 @@ function musea(options = {}) {
610
681
  let html = await fs.promises.readFile(indexHtmlPath, "utf-8");
611
682
  const themeScript = themeConfig ? `window.__MUSEA_THEME_CONFIG__=${JSON.stringify(themeConfig)};` : "";
612
683
  html = html.replace("</head>", `<script>window.__MUSEA_BASE_PATH__='${basePath}';${themeScript}</script></head>`);
613
- html = await devServer.transformIndexHtml(basePath + url, html);
614
684
  res.setHeader("Content-Type", "text/html");
615
685
  res.end(html);
616
686
  return;
@@ -669,7 +739,7 @@ function musea(options = {}) {
669
739
  return;
670
740
  }
671
741
  const variantComponentName = toPascalCase(variant.name);
672
- const moduleCode = generatePreviewModule(art, variantComponentName, variant.name);
742
+ const moduleCode = generatePreviewModule(art, variantComponentName, variant.name, resolvedPreviewCss, resolvedPreviewSetup);
673
743
  try {
674
744
  const result = await devServer.transformRequest(`virtual:musea-preview:${artPath}:${variantName}`);
675
745
  if (result) {
@@ -704,8 +774,7 @@ function musea(options = {}) {
704
774
  res.end("Variant not found");
705
775
  return;
706
776
  }
707
- const rawHtml = generatePreviewHtml(art, variant, basePath);
708
- const html = await devServer.transformIndexHtml(`${basePath}/preview?art=${encodeURIComponent(artPath)}&variant=${encodeURIComponent(variantName)}`, rawHtml);
777
+ const html = generatePreviewHtml(art, variant, basePath, config.base);
709
778
  res.setHeader("Content-Type", "text/html");
710
779
  res.end(html);
711
780
  });
@@ -1021,16 +1090,57 @@ function musea(options = {}) {
1021
1090
  try {
1022
1091
  const source = await fs.promises.readFile(artPath$1, "utf-8");
1023
1092
  const binding = loadNative();
1024
- if (binding.generateArtPalette) {
1025
- const palette = binding.generateArtPalette(source, { filename: artPath$1 });
1026
- sendJson(palette);
1027
- } else sendJson({
1093
+ let palette;
1094
+ if (binding.generateArtPalette) palette = binding.generateArtPalette(source, { filename: artPath$1 });
1095
+ else palette = {
1028
1096
  title: art$1.metadata.title,
1029
1097
  controls: [],
1030
1098
  groups: [],
1031
1099
  json: "{}",
1032
1100
  typescript: ""
1033
- });
1101
+ };
1102
+ if (palette.controls.length === 0 && art$1.metadata.component) {
1103
+ const resolvedComponentPath = path.isAbsolute(art$1.metadata.component) ? art$1.metadata.component : path.resolve(path.dirname(artPath$1), art$1.metadata.component);
1104
+ try {
1105
+ const componentSource = await fs.promises.readFile(resolvedComponentPath, "utf-8");
1106
+ const analysis = binding.analyzeSfc ? binding.analyzeSfc(componentSource, { filename: resolvedComponentPath }) : analyzeSfcFallback(componentSource, { filename: resolvedComponentPath });
1107
+ if (analysis.props.length > 0) {
1108
+ palette.controls = analysis.props.map((prop) => {
1109
+ let control = "text";
1110
+ if (prop.type === "boolean") control = "boolean";
1111
+ else if (prop.type === "number") control = "number";
1112
+ else if (prop.type.includes("|") && !prop.type.includes("=>")) control = "select";
1113
+ const options$1 = [];
1114
+ if (control === "select") {
1115
+ const optionMatches = prop.type.match(/"([^"]+)"/g);
1116
+ if (optionMatches) for (const opt of optionMatches) {
1117
+ const val = opt.replace(/"/g, "");
1118
+ options$1.push({
1119
+ label: val,
1120
+ value: val
1121
+ });
1122
+ }
1123
+ }
1124
+ return {
1125
+ name: prop.name,
1126
+ control,
1127
+ default_value: prop.default_value !== void 0 ? prop.default_value === "true" ? true : prop.default_value === "false" ? false : typeof prop.default_value === "string" && prop.default_value.startsWith("\"") ? prop.default_value.replace(/^"|"$/g, "") : prop.default_value : void 0,
1128
+ description: void 0,
1129
+ required: prop.required,
1130
+ options: options$1,
1131
+ range: void 0,
1132
+ group: void 0
1133
+ };
1134
+ });
1135
+ palette.json = JSON.stringify({
1136
+ title: palette.title,
1137
+ controls: palette.controls
1138
+ }, null, 2);
1139
+ palette.typescript = `export interface ${palette.title}Props {\n${palette.controls.map((c) => ` ${c.name}${c.required ? "" : "?"}: ${c.control === "boolean" ? "boolean" : c.control === "number" ? "number" : c.control === "select" ? c.options.map((o) => `"${String(o.value)}"`).join(" | ") : "string"};`).join("\n")}\n}\n`;
1140
+ }
1141
+ } catch {}
1142
+ }
1143
+ sendJson(palette);
1034
1144
  } catch (e) {
1035
1145
  sendError(e instanceof Error ? e.message : String(e));
1036
1146
  }
@@ -1051,10 +1161,10 @@ function musea(options = {}) {
1051
1161
  if (binding.analyzeSfc) {
1052
1162
  const analysis = binding.analyzeSfc(source, { filename: resolvedComponentPath });
1053
1163
  sendJson(analysis);
1054
- } else sendJson({
1055
- props: [],
1056
- emits: []
1057
- });
1164
+ } else {
1165
+ const analysis = analyzeSfcFallback(source, { filename: resolvedComponentPath });
1166
+ sendJson(analysis);
1167
+ }
1058
1168
  } else sendJson({
1059
1169
  props: [],
1060
1170
  emits: []
@@ -1144,7 +1254,7 @@ function musea(options = {}) {
1144
1254
  return;
1145
1255
  }
1146
1256
  const variantComponentName = toPascalCase(variant.name);
1147
- const moduleCode = generatePreviewModuleWithProps(art, variantComponentName, variant.name, propsOverride);
1257
+ const moduleCode = generatePreviewModuleWithProps(art, variantComponentName, variant.name, propsOverride, resolvedPreviewCss, resolvedPreviewSetup);
1148
1258
  res.setHeader("Content-Type", "application/javascript");
1149
1259
  res.end(moduleCode);
1150
1260
  } catch (e) {
@@ -1278,15 +1388,15 @@ function musea(options = {}) {
1278
1388
  if (id.startsWith("virtual:musea-preview:")) return "\0musea-preview:" + id.slice(22);
1279
1389
  if (id.startsWith("virtual:musea-art:")) {
1280
1390
  const artPath = id.slice(18);
1281
- if (artFiles.has(artPath)) return "\0musea-art:" + artPath;
1391
+ if (artFiles.has(artPath)) return "\0musea-art:" + artPath + "?musea-virtual";
1282
1392
  }
1283
1393
  if (id.endsWith(".art.vue")) {
1284
1394
  const resolved = path.resolve(config.root, id);
1285
- if (artFiles.has(resolved)) return VIRTUAL_MUSEA_PREFIX + resolved;
1395
+ if (artFiles.has(resolved)) return VIRTUAL_MUSEA_PREFIX + resolved + "?musea-virtual";
1286
1396
  }
1287
1397
  if (inlineArt && id.endsWith(".vue") && !id.endsWith(".art.vue")) {
1288
1398
  const resolved = path.resolve(config.root, id);
1289
- if (artFiles.has(resolved)) return VIRTUAL_MUSEA_PREFIX + resolved;
1399
+ if (artFiles.has(resolved)) return VIRTUAL_MUSEA_PREFIX + resolved + "?musea-virtual";
1290
1400
  }
1291
1401
  return null;
1292
1402
  },
@@ -1302,17 +1412,17 @@ function musea(options = {}) {
1302
1412
  const art = artFiles.get(artPath);
1303
1413
  if (art) {
1304
1414
  const variantComponentName = toPascalCase(variantName);
1305
- return generatePreviewModule(art, variantComponentName, variantName);
1415
+ return generatePreviewModule(art, variantComponentName, variantName, resolvedPreviewCss, resolvedPreviewSetup);
1306
1416
  }
1307
1417
  }
1308
1418
  }
1309
1419
  if (id.startsWith("\0musea-art:")) {
1310
- const artPath = id.slice(11);
1420
+ const artPath = id.slice(11).replace(/\?musea-virtual$/, "");
1311
1421
  const art = artFiles.get(artPath);
1312
1422
  if (art) return generateArtModule(art, artPath);
1313
1423
  }
1314
1424
  if (id.startsWith(VIRTUAL_MUSEA_PREFIX)) {
1315
- const realPath = id.slice(VIRTUAL_MUSEA_PREFIX.length);
1425
+ const realPath = id.slice(VIRTUAL_MUSEA_PREFIX.length).replace(/\?musea-virtual$/, "");
1316
1426
  const art = artFiles.get(realPath);
1317
1427
  if (art) return generateArtModule(art, realPath);
1318
1428
  }
@@ -1322,7 +1432,7 @@ function musea(options = {}) {
1322
1432
  const { file } = ctx;
1323
1433
  if (file.endsWith(".art.vue") && artFiles.has(file)) {
1324
1434
  await processArtFile(file);
1325
- const virtualId = VIRTUAL_MUSEA_PREFIX + file;
1435
+ const virtualId = VIRTUAL_MUSEA_PREFIX + file + "?musea-virtual";
1326
1436
  const modules = server?.moduleGraph.getModulesByFile(virtualId);
1327
1437
  if (modules) return [...modules];
1328
1438
  }
@@ -1356,12 +1466,13 @@ function musea(options = {}) {
1356
1466
  variants: parsed.variants.map((v) => ({
1357
1467
  name: v.name,
1358
1468
  template: v.template,
1359
- isDefault: v.is_default,
1360
- skipVrt: v.skip_vrt
1469
+ isDefault: v.isDefault,
1470
+ skipVrt: v.skipVrt
1361
1471
  })),
1362
- hasScriptSetup: parsed.has_script_setup,
1363
- hasScript: parsed.has_script,
1364
- styleCount: parsed.style_count,
1472
+ hasScriptSetup: parsed.hasScriptSetup,
1473
+ scriptSetupContent: parsed.hasScriptSetup ? extractScriptSetupContent(source) : void 0,
1474
+ hasScript: parsed.hasScript,
1475
+ styleCount: parsed.styleCount,
1365
1476
  isInline,
1366
1477
  componentPath: isInline ? filePath : void 0
1367
1478
  };
@@ -1379,7 +1490,8 @@ function shouldProcess(file, include, exclude, root) {
1379
1490
  return false;
1380
1491
  }
1381
1492
  function matchGlob(filepath, pattern) {
1382
- const regex = pattern.replace(/\*\*/g, "\0GLOBSTAR\0").replace(/\./g, "\\.").replace(/\*/g, "[^/]*").replace(/\0GLOBSTAR\0/g, ".*");
1493
+ const PLACEHOLDER = "<<GLOBSTAR>>";
1494
+ const regex = pattern.replaceAll("**", PLACEHOLDER).replace(/\./g, "\\.").replace(/\*/g, "[^/]*").replaceAll(PLACEHOLDER, ".*");
1383
1495
  return new RegExp(`^${regex}$`).test(filepath);
1384
1496
  }
1385
1497
  async function scanArtFiles(root, include, exclude, scanInlineArt = false) {
@@ -2439,10 +2551,15 @@ function __museaInitAddons(container, variantName) {
2439
2551
  window.parent.postMessage({ type: 'musea:ready', payload: {} }, '*');
2440
2552
  }
2441
2553
  `;
2442
- function generatePreviewModule(art, variantComponentName, variantName) {
2554
+ function generatePreviewModule(art, variantComponentName, variantName, cssImports = [], previewSetup = null) {
2443
2555
  const artModuleId = `virtual:musea-art:${art.path}`;
2444
2556
  const escapedVariantName = escapeTemplate(variantName);
2557
+ const cssImportStatements = cssImports.map((cssPath) => `import '${cssPath}';`).join("\n");
2558
+ const setupImport = previewSetup ? `import __museaPreviewSetup from '${previewSetup}';` : "";
2559
+ const setupCall = previewSetup ? "await __museaPreviewSetup(app);" : "";
2445
2560
  return `
2561
+ ${cssImportStatements}
2562
+ ${setupImport}
2446
2563
  import { createApp, reactive, h } from 'vue';
2447
2564
  import * as artModule from '${artModuleId}';
2448
2565
 
@@ -2478,6 +2595,7 @@ async function mount() {
2478
2595
 
2479
2596
  // Create and mount the app
2480
2597
  const app = createApp(VariantComponent);
2598
+ ${setupCall}
2481
2599
  container.innerHTML = '';
2482
2600
  container.className = 'musea-variant';
2483
2601
  app.mount(container);
@@ -2512,7 +2630,7 @@ async function mount() {
2512
2630
  }
2513
2631
  }
2514
2632
 
2515
- function remountWithProps(Component) {
2633
+ async function remountWithProps(Component) {
2516
2634
  if (currentApp) {
2517
2635
  currentApp.unmount();
2518
2636
  }
@@ -2523,12 +2641,11 @@ function remountWithProps(Component) {
2523
2641
  if (slotsOverride.default) {
2524
2642
  slotFns.default = () => h('span', { innerHTML: slotsOverride.default });
2525
2643
  }
2526
- return h('div', { class: 'musea-variant' }, [
2527
- h(Component, { ...propsOverride }, slotFns)
2528
- ]);
2644
+ return h(Component, { ...propsOverride }, slotFns);
2529
2645
  };
2530
2646
  }
2531
2647
  });
2648
+ ${setupCall}
2532
2649
  container.innerHTML = '';
2533
2650
  app.mount(container);
2534
2651
  currentApp = app;
@@ -2541,6 +2658,57 @@ function generateManifestModule(artFiles) {
2541
2658
  const arts = Array.from(artFiles.values());
2542
2659
  return `export const arts = ${JSON.stringify(arts, null, 2)};`;
2543
2660
  }
2661
+ /**
2662
+ * Extract the content of the first <script setup> block from a Vue SFC source.
2663
+ */
2664
+ function extractScriptSetupContent(source) {
2665
+ const match = source.match(/<script\s+[^>]*setup[^>]*>([\s\S]*?)<\/script>/);
2666
+ return match?.[1]?.trim();
2667
+ }
2668
+ /**
2669
+ * Parse script setup content into imports and setup body.
2670
+ * Returns the import lines, setup body lines, and all identifiers to expose.
2671
+ */
2672
+ function parseScriptSetupForArt(content) {
2673
+ const lines = content.split("\n");
2674
+ const imports = [];
2675
+ const setupBody = [];
2676
+ const returnNames = new Set();
2677
+ for (const line of lines) {
2678
+ const trimmed = line.trim();
2679
+ if (!trimmed || trimmed.startsWith("//")) continue;
2680
+ if (trimmed.startsWith("import ")) {
2681
+ imports.push(line);
2682
+ const defaultMatch = trimmed.match(/^import\s+(\w+)/);
2683
+ if (defaultMatch && defaultMatch[1] !== "type") returnNames.add(defaultMatch[1]);
2684
+ const namedMatch = trimmed.match(/\{([^}]+)\}/);
2685
+ if (namedMatch) for (const part of namedMatch[1].split(",")) {
2686
+ const name = part.trim().split(/\s+as\s+/).pop()?.trim();
2687
+ if (name && !name.startsWith("type ")) returnNames.add(name);
2688
+ }
2689
+ } else {
2690
+ setupBody.push(line);
2691
+ const constMatch = trimmed.match(/^(?:const|let|var)\s+(\w+)/);
2692
+ if (constMatch) returnNames.add(constMatch[1]);
2693
+ const destructMatch = trimmed.match(/^(?:const|let|var)\s+\{([^}]+)\}/);
2694
+ if (destructMatch) for (const part of destructMatch[1].split(",")) {
2695
+ const name = part.trim().split(/\s*:\s*/).shift()?.trim();
2696
+ if (name) returnNames.add(name);
2697
+ }
2698
+ const arrayMatch = trimmed.match(/^(?:const|let|var)\s+\[([^\]]+)\]/);
2699
+ if (arrayMatch) for (const part of arrayMatch[1].split(",")) {
2700
+ const name = part.trim();
2701
+ if (name && name !== "...") returnNames.add(name);
2702
+ }
2703
+ }
2704
+ }
2705
+ returnNames.delete("type");
2706
+ return {
2707
+ imports,
2708
+ setupBody,
2709
+ returnNames: [...returnNames]
2710
+ };
2711
+ }
2544
2712
  function generateArtModule(art, filePath) {
2545
2713
  let componentImportPath;
2546
2714
  let componentName;
@@ -2552,12 +2720,27 @@ function generateArtModule(art, filePath) {
2552
2720
  componentImportPath = path.isAbsolute(comp) ? comp : path.resolve(path.dirname(filePath), comp);
2553
2721
  componentName = path.basename(comp, ".vue");
2554
2722
  }
2723
+ const scriptSetup = art.scriptSetupContent ? parseScriptSetupForArt(art.scriptSetupContent) : null;
2555
2724
  let code = `
2556
2725
  // Auto-generated module for: ${path.basename(filePath)}
2557
2726
  import { defineComponent, h } from 'vue';
2558
2727
  `;
2728
+ if (scriptSetup) {
2729
+ const artDir = path.dirname(filePath);
2730
+ for (const imp of scriptSetup.imports) {
2731
+ const resolved = imp.replace(/from\s+(['"])(\.[^'"]+)\1/, (_match, quote, relPath) => {
2732
+ const absPath = path.resolve(artDir, relPath);
2733
+ return `from ${quote}${absPath}${quote}`;
2734
+ });
2735
+ code += `${resolved}\n`;
2736
+ }
2737
+ }
2559
2738
  if (componentImportPath && componentName) {
2560
- code += `import ${componentName} from '${componentImportPath}';\n`;
2739
+ const alreadyImported = scriptSetup?.imports.some((imp) => {
2740
+ if (imp.includes(`from '${componentImportPath}'`) || imp.includes(`from "${componentImportPath}"`)) return true;
2741
+ return new RegExp(`^import\\s+${componentName}[\\s,]`).test(imp.trim());
2742
+ });
2743
+ if (!alreadyImported) code += `import ${componentName} from '${componentImportPath}';\n`;
2561
2744
  code += `export const __component__ = ${componentName};\n`;
2562
2745
  }
2563
2746
  code += `
@@ -2569,12 +2752,27 @@ export const variants = ${JSON.stringify(art.variants)};
2569
2752
  let template = variant.template;
2570
2753
  if (componentName) template = template.replace(/<Self/g, `<${componentName}`).replace(/<\/Self>/g, `</${componentName}>`);
2571
2754
  const escapedTemplate = template.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
2572
- const fullTemplate = `<div class="musea-variant" data-variant="${variant.name}">${escapedTemplate}</div>`;
2573
- if (componentName) code += `
2574
- export const ${variantComponentName} = {
2755
+ const fullTemplate = `<div data-variant="${variant.name}">${escapedTemplate}</div>`;
2756
+ const componentNames = new Set();
2757
+ if (componentName) componentNames.add(componentName);
2758
+ if (scriptSetup) {
2759
+ for (const name of scriptSetup.returnNames) if (/^[A-Z]/.test(name)) componentNames.add(name);
2760
+ }
2761
+ const components = componentNames.size > 0 ? ` components: { ${[...componentNames].join(", ")} },\n` : "";
2762
+ if (scriptSetup && scriptSetup.setupBody.length > 0) code += `
2763
+ export const ${variantComponentName} = defineComponent({
2575
2764
  name: '${variantComponentName}',
2576
- components: { ${componentName} },
2765
+ ${components} setup() {
2766
+ ${scriptSetup.setupBody.map((l) => ` ${l}`).join("\n")}
2767
+ return { ${scriptSetup.returnNames.join(", ")} };
2768
+ },
2577
2769
  template: \`${fullTemplate}\`,
2770
+ });
2771
+ `;
2772
+ else if (componentName) code += `
2773
+ export const ${variantComponentName} = {
2774
+ name: '${variantComponentName}',
2775
+ ${components} template: \`${fullTemplate}\`,
2578
2776
  };
2579
2777
  `;
2580
2778
  else code += `
@@ -2610,11 +2808,16 @@ function toPascalCase(str) {
2610
2808
  function escapeTemplate(str) {
2611
2809
  return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n");
2612
2810
  }
2613
- function generatePreviewModuleWithProps(art, variantComponentName, variantName, propsOverride) {
2811
+ function generatePreviewModuleWithProps(art, variantComponentName, variantName, propsOverride, cssImports = [], previewSetup = null) {
2614
2812
  const artModuleId = `virtual:musea-art:${art.path}`;
2615
2813
  const escapedVariantName = escapeTemplate(variantName);
2616
2814
  const propsJson = JSON.stringify(propsOverride);
2815
+ const cssImportStatements = cssImports.map((cssPath) => `import '${cssPath}';`).join("\n");
2816
+ const setupImport = previewSetup ? `import __museaPreviewSetup from '${previewSetup}';` : "";
2817
+ const setupCall = previewSetup ? "await __museaPreviewSetup(app);" : "";
2617
2818
  return `
2819
+ ${cssImportStatements}
2820
+ ${setupImport}
2618
2821
  import { createApp, h } from 'vue';
2619
2822
  import * as artModule from '${artModuleId}';
2620
2823
 
@@ -2637,6 +2840,7 @@ async function mount() {
2637
2840
  };
2638
2841
 
2639
2842
  const app = createApp(WrappedComponent);
2843
+ ${setupCall}
2640
2844
  container.innerHTML = '';
2641
2845
  container.className = 'musea-variant';
2642
2846
  app.mount(container);
@@ -2651,14 +2855,16 @@ async function mount() {
2651
2855
  mount();
2652
2856
  `;
2653
2857
  }
2654
- function generatePreviewHtml(art, variant, _basePath) {
2655
- const importSpecifier = JSON.stringify(`virtual:musea-preview:${art.path}:${variant.name}`);
2858
+ function generatePreviewHtml(art, variant, _basePath, viteBase) {
2859
+ const previewModuleUrl = `${_basePath}/preview-module?art=${encodeURIComponent(art.path)}&variant=${encodeURIComponent(variant.name)}`;
2860
+ const base = (viteBase || "/").replace(/\/$/, "");
2656
2861
  return `<!DOCTYPE html>
2657
2862
  <html lang="en">
2658
2863
  <head>
2659
2864
  <meta charset="UTF-8">
2660
2865
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
2661
2866
  <title>${escapeHtml(art.metadata.title)} - ${escapeHtml(variant.name)}</title>
2867
+ <script type="module" src="${base}/@vite/client"></script>
2662
2868
  <style>
2663
2869
  * { box-sizing: border-box; margin: 0; padding: 0; }
2664
2870
  html, body {
@@ -2670,10 +2876,6 @@ function generatePreviewHtml(art, variant, _basePath) {
2670
2876
  background: #ffffff;
2671
2877
  }
2672
2878
  .musea-variant {
2673
- padding: 1.5rem;
2674
- display: flex;
2675
- align-items: center;
2676
- justify-content: center;
2677
2879
  min-height: 100vh;
2678
2880
  }
2679
2881
  .musea-error {
@@ -2747,13 +2949,7 @@ function generatePreviewHtml(art, variant, _basePath) {
2747
2949
  Loading component...
2748
2950
  </div>
2749
2951
  </div>
2750
- <script type="module">
2751
- import(${importSpecifier}).catch(function(e) {
2752
- console.error('[musea-preview] Failed to load module:', e);
2753
- var c = document.getElementById('app');
2754
- if (c) c.innerHTML = '<div class="musea-error"><div class="musea-error-title">Failed to load preview module</div><div>' + e.message + '</div></div>';
2755
- });
2756
- </script>
2952
+ <script type="module" src="${previewModuleUrl}"></script>
2757
2953
  </body>
2758
2954
  </html>`;
2759
2955
  }