@vizejs/vite-plugin-musea 0.0.1-alpha.102 → 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
  };
@@ -2440,10 +2551,15 @@ function __museaInitAddons(container, variantName) {
2440
2551
  window.parent.postMessage({ type: 'musea:ready', payload: {} }, '*');
2441
2552
  }
2442
2553
  `;
2443
- function generatePreviewModule(art, variantComponentName, variantName) {
2554
+ function generatePreviewModule(art, variantComponentName, variantName, cssImports = [], previewSetup = null) {
2444
2555
  const artModuleId = `virtual:musea-art:${art.path}`;
2445
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);" : "";
2446
2560
  return `
2561
+ ${cssImportStatements}
2562
+ ${setupImport}
2447
2563
  import { createApp, reactive, h } from 'vue';
2448
2564
  import * as artModule from '${artModuleId}';
2449
2565
 
@@ -2479,6 +2595,7 @@ async function mount() {
2479
2595
 
2480
2596
  // Create and mount the app
2481
2597
  const app = createApp(VariantComponent);
2598
+ ${setupCall}
2482
2599
  container.innerHTML = '';
2483
2600
  container.className = 'musea-variant';
2484
2601
  app.mount(container);
@@ -2513,7 +2630,7 @@ async function mount() {
2513
2630
  }
2514
2631
  }
2515
2632
 
2516
- function remountWithProps(Component) {
2633
+ async function remountWithProps(Component) {
2517
2634
  if (currentApp) {
2518
2635
  currentApp.unmount();
2519
2636
  }
@@ -2524,12 +2641,11 @@ function remountWithProps(Component) {
2524
2641
  if (slotsOverride.default) {
2525
2642
  slotFns.default = () => h('span', { innerHTML: slotsOverride.default });
2526
2643
  }
2527
- return h('div', { class: 'musea-variant' }, [
2528
- h(Component, { ...propsOverride }, slotFns)
2529
- ]);
2644
+ return h(Component, { ...propsOverride }, slotFns);
2530
2645
  };
2531
2646
  }
2532
2647
  });
2648
+ ${setupCall}
2533
2649
  container.innerHTML = '';
2534
2650
  app.mount(container);
2535
2651
  currentApp = app;
@@ -2542,6 +2658,57 @@ function generateManifestModule(artFiles) {
2542
2658
  const arts = Array.from(artFiles.values());
2543
2659
  return `export const arts = ${JSON.stringify(arts, null, 2)};`;
2544
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
+ }
2545
2712
  function generateArtModule(art, filePath) {
2546
2713
  let componentImportPath;
2547
2714
  let componentName;
@@ -2553,12 +2720,27 @@ function generateArtModule(art, filePath) {
2553
2720
  componentImportPath = path.isAbsolute(comp) ? comp : path.resolve(path.dirname(filePath), comp);
2554
2721
  componentName = path.basename(comp, ".vue");
2555
2722
  }
2723
+ const scriptSetup = art.scriptSetupContent ? parseScriptSetupForArt(art.scriptSetupContent) : null;
2556
2724
  let code = `
2557
2725
  // Auto-generated module for: ${path.basename(filePath)}
2558
2726
  import { defineComponent, h } from 'vue';
2559
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
+ }
2560
2738
  if (componentImportPath && componentName) {
2561
- 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`;
2562
2744
  code += `export const __component__ = ${componentName};\n`;
2563
2745
  }
2564
2746
  code += `
@@ -2570,12 +2752,27 @@ export const variants = ${JSON.stringify(art.variants)};
2570
2752
  let template = variant.template;
2571
2753
  if (componentName) template = template.replace(/<Self/g, `<${componentName}`).replace(/<\/Self>/g, `</${componentName}>`);
2572
2754
  const escapedTemplate = template.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
2573
- const fullTemplate = `<div class="musea-variant" data-variant="${variant.name}">${escapedTemplate}</div>`;
2574
- if (componentName) code += `
2575
- 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({
2576
2764
  name: '${variantComponentName}',
2577
- components: { ${componentName} },
2765
+ ${components} setup() {
2766
+ ${scriptSetup.setupBody.map((l) => ` ${l}`).join("\n")}
2767
+ return { ${scriptSetup.returnNames.join(", ")} };
2768
+ },
2578
2769
  template: \`${fullTemplate}\`,
2770
+ });
2771
+ `;
2772
+ else if (componentName) code += `
2773
+ export const ${variantComponentName} = {
2774
+ name: '${variantComponentName}',
2775
+ ${components} template: \`${fullTemplate}\`,
2579
2776
  };
2580
2777
  `;
2581
2778
  else code += `
@@ -2611,11 +2808,16 @@ function toPascalCase(str) {
2611
2808
  function escapeTemplate(str) {
2612
2809
  return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n");
2613
2810
  }
2614
- function generatePreviewModuleWithProps(art, variantComponentName, variantName, propsOverride) {
2811
+ function generatePreviewModuleWithProps(art, variantComponentName, variantName, propsOverride, cssImports = [], previewSetup = null) {
2615
2812
  const artModuleId = `virtual:musea-art:${art.path}`;
2616
2813
  const escapedVariantName = escapeTemplate(variantName);
2617
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);" : "";
2618
2818
  return `
2819
+ ${cssImportStatements}
2820
+ ${setupImport}
2619
2821
  import { createApp, h } from 'vue';
2620
2822
  import * as artModule from '${artModuleId}';
2621
2823
 
@@ -2638,6 +2840,7 @@ async function mount() {
2638
2840
  };
2639
2841
 
2640
2842
  const app = createApp(WrappedComponent);
2843
+ ${setupCall}
2641
2844
  container.innerHTML = '';
2642
2845
  container.className = 'musea-variant';
2643
2846
  app.mount(container);
@@ -2652,14 +2855,16 @@ async function mount() {
2652
2855
  mount();
2653
2856
  `;
2654
2857
  }
2655
- function generatePreviewHtml(art, variant, _basePath) {
2656
- 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(/\/$/, "");
2657
2861
  return `<!DOCTYPE html>
2658
2862
  <html lang="en">
2659
2863
  <head>
2660
2864
  <meta charset="UTF-8">
2661
2865
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
2662
2866
  <title>${escapeHtml(art.metadata.title)} - ${escapeHtml(variant.name)}</title>
2867
+ <script type="module" src="${base}/@vite/client"></script>
2663
2868
  <style>
2664
2869
  * { box-sizing: border-box; margin: 0; padding: 0; }
2665
2870
  html, body {
@@ -2671,10 +2876,6 @@ function generatePreviewHtml(art, variant, _basePath) {
2671
2876
  background: #ffffff;
2672
2877
  }
2673
2878
  .musea-variant {
2674
- padding: 1.5rem;
2675
- display: flex;
2676
- align-items: center;
2677
- justify-content: center;
2678
2879
  min-height: 100vh;
2679
2880
  }
2680
2881
  .musea-error {
@@ -2748,13 +2949,7 @@ function generatePreviewHtml(art, variant, _basePath) {
2748
2949
  Loading component...
2749
2950
  </div>
2750
2951
  </div>
2751
- <script type="module">
2752
- import(${importSpecifier}).catch(function(e) {
2753
- console.error('[musea-preview] Failed to load module:', e);
2754
- var c = document.getElementById('app');
2755
- if (c) c.innerHTML = '<div class="musea-error"><div class="musea-error-title">Failed to load preview module</div><div>' + e.message + '</div></div>';
2756
- });
2757
- </script>
2952
+ <script type="module" src="${previewModuleUrl}"></script>
2758
2953
  </body>
2759
2954
  </html>`;
2760
2955
  }