@qualcomm-ui/mdx-vite 2.2.1 → 2.4.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.js CHANGED
@@ -5,7 +5,7 @@ import chalk4 from "chalk";
5
5
  import { watch } from "chokidar";
6
6
  import { glob as glob2 } from "glob";
7
7
  import { existsSync } from "node:fs";
8
- import { readFile, stat } from "node:fs/promises";
8
+ import { readFile as readFile2, stat } from "node:fs/promises";
9
9
  import { basename, dirname, join as join2, relative, resolve as resolve2 } from "node:path";
10
10
  import {
11
11
  createHighlighter
@@ -14,6 +14,7 @@ import * as ts from "typescript";
14
14
  import {
15
15
  quiCustomDarkTheme as quiCustomDarkTheme2
16
16
  } from "@qualcomm-ui/mdx-common";
17
+ import { dedent as dedent2 } from "@qualcomm-ui/utils/dedent";
17
18
 
18
19
  // src/docs-plugin/docs-plugin.ts
19
20
  import chalk3 from "chalk";
@@ -2240,6 +2241,55 @@ function removeCodeAnnotations(code) {
2240
2241
  return true;
2241
2242
  }).map(({ processed }) => processed).join("\n");
2242
2243
  }
2244
+ function extractPreviewFromHighlightedHtml(highlightedHtml) {
2245
+ const preMatch = highlightedHtml.match(/<pre((?:\s+[\w-]+="[^"]*")*)>/);
2246
+ const codeMatch = highlightedHtml.match(/<code([^>]*)>(.*?)<\/code>/s);
2247
+ if (!preMatch || !codeMatch) {
2248
+ return null;
2249
+ }
2250
+ const codeContent = codeMatch[2];
2251
+ const parts = codeContent.split(/<span class="line/);
2252
+ const previewLineParts = parts.slice(1).filter((part) => part.includes('data-preview-line="true"'));
2253
+ const indents = previewLineParts.map((part) => {
2254
+ const indentMatches = part.match(/<span class="indent">(.+?)<\/span>/g) || [];
2255
+ let total = 0;
2256
+ for (const match of indentMatches) {
2257
+ const content = match.match(/<span class="indent">(.+?)<\/span>/);
2258
+ if (content) {
2259
+ total += content[1].length;
2260
+ } else {
2261
+ break;
2262
+ }
2263
+ }
2264
+ return total;
2265
+ });
2266
+ const minIndent = Math.min(...indents.filter((n) => n > 0));
2267
+ const previewLines = previewLineParts.map((part) => {
2268
+ let processed = `<span class="line${part}`;
2269
+ let remaining = minIndent;
2270
+ while (remaining > 0 && processed.includes('<span class="indent">')) {
2271
+ const before = processed;
2272
+ processed = processed.replace(
2273
+ /<span class="indent">(.+?)<\/span>/,
2274
+ (match, spaces) => {
2275
+ if (spaces.length <= remaining) {
2276
+ remaining -= spaces.length;
2277
+ return "";
2278
+ } else {
2279
+ const kept = spaces.substring(remaining);
2280
+ remaining = 0;
2281
+ return `<span class="indent">${kept}</span>`;
2282
+ }
2283
+ }
2284
+ );
2285
+ if (before === processed) {
2286
+ break;
2287
+ }
2288
+ }
2289
+ return processed;
2290
+ });
2291
+ return `<pre${preMatch[1]}><code${codeMatch[1]}>${previewLines.join("")}</code></pre>`;
2292
+ }
2243
2293
 
2244
2294
  // src/docs-plugin/shiki/shiki-transformer-code-attribute.ts
2245
2295
  function transformerCodeAttribute(opts = { attributeName: "data-code" }) {
@@ -2315,10 +2365,11 @@ function transformerPreviewBlock(options = {
2315
2365
  },
2316
2366
  name: "transformer-preview-block",
2317
2367
  pre(node) {
2318
- if (previewContent && options.attributeName != null) {
2319
- node.properties[options.attributeName] = previewContent;
2368
+ const content = previewContent ? removeCodeAnnotations(previewContent) : null;
2369
+ if (content && options.attributeName != null) {
2370
+ node.properties[options.attributeName] = content;
2320
2371
  }
2321
- options.onComplete?.(previewContent || null);
2372
+ options.onComplete?.(content || null);
2322
2373
  },
2323
2374
  preprocess(code) {
2324
2375
  previewContent = null;
@@ -2412,6 +2463,406 @@ function getRemarkPlugins() {
2412
2463
  ];
2413
2464
  }
2414
2465
 
2466
+ // src/docs-plugin/shiki/internal/shiki-transformer-tailwind.ts
2467
+ import { fromHtml as fromHtml2 } from "hast-util-from-html";
2468
+ import { toHtml } from "hast-util-to-html";
2469
+ import { readFile } from "node:fs/promises";
2470
+ import postcss from "postcss";
2471
+ import selectorParser from "postcss-selector-parser";
2472
+ import { compile } from "tailwindcss";
2473
+ import { visit as visit7 } from "unist-util-visit";
2474
+ import { camelCase } from "@qualcomm-ui/utils/change-case";
2475
+ async function loadStylesheetContent(id) {
2476
+ const resolveId = id === "tailwindcss" ? "tailwindcss/index.css" : id;
2477
+ let resolvedPath;
2478
+ if (typeof createRequire !== "undefined") {
2479
+ resolvedPath = createRequire(import.meta.url).resolve(resolveId);
2480
+ } else {
2481
+ const createRequire2 = await import("node:module").then(
2482
+ (module) => module.createRequire
2483
+ );
2484
+ resolvedPath = createRequire2(import.meta.url).resolve(resolveId);
2485
+ }
2486
+ return readFile(resolvedPath, "utf-8");
2487
+ }
2488
+ async function createCompiler(styles) {
2489
+ return compile(styles, {
2490
+ loadStylesheet: async (id, base) => {
2491
+ const content = await loadStylesheetContent(id);
2492
+ return {
2493
+ base,
2494
+ content,
2495
+ path: `virtual:${id}`
2496
+ };
2497
+ }
2498
+ });
2499
+ }
2500
+ function extractCssVariables(css) {
2501
+ const variables = /* @__PURE__ */ new Map();
2502
+ const root = postcss.parse(css);
2503
+ root.walkAtRules("layer", (atRule) => {
2504
+ if (atRule.params !== "theme") {
2505
+ return;
2506
+ }
2507
+ atRule.walkRules(":root, :host", (rule) => {
2508
+ rule.walkDecls((decl) => {
2509
+ if (decl.prop.startsWith("--")) {
2510
+ variables.set(decl.prop, decl.value);
2511
+ }
2512
+ });
2513
+ });
2514
+ });
2515
+ return variables;
2516
+ }
2517
+ function evaluateCalc(expression) {
2518
+ const expr = expression.trim();
2519
+ const withUnitFirst = expr.match(
2520
+ /^([\d.]+)(rem|px|em|%|vh|vw)\s*([*/+-])\s*([\d.]+)$/
2521
+ );
2522
+ if (withUnitFirst) {
2523
+ const [, num1, unit, op, num2] = withUnitFirst;
2524
+ const result = evaluateOperation(parseFloat(num1), op, parseFloat(num2));
2525
+ if (!Number.isFinite(result)) {
2526
+ return null;
2527
+ }
2528
+ return formatNumber(result) + unit;
2529
+ }
2530
+ const withUnitSecond = expr.match(
2531
+ /^([\d.]+)\s*([*/+-])\s*([\d.]+)(rem|px|em|%|vh|vw)$/
2532
+ );
2533
+ if (withUnitSecond) {
2534
+ const [, num1, op, num2, unit] = withUnitSecond;
2535
+ const result = evaluateOperation(parseFloat(num1), op, parseFloat(num2));
2536
+ if (!Number.isFinite(result)) {
2537
+ return null;
2538
+ }
2539
+ return formatNumber(result) + unit;
2540
+ }
2541
+ const unitless = expr.match(/^([\d.]+)\s*([*/+-])\s*([\d.]+)$/);
2542
+ if (unitless) {
2543
+ const [, num1, op, num2] = unitless;
2544
+ const result = evaluateOperation(parseFloat(num1), op, parseFloat(num2));
2545
+ if (!Number.isFinite(result)) {
2546
+ return null;
2547
+ }
2548
+ return formatNumber(result);
2549
+ }
2550
+ return null;
2551
+ }
2552
+ function evaluateOperation(a, op, b) {
2553
+ switch (op) {
2554
+ case "*":
2555
+ return a * b;
2556
+ case "/":
2557
+ return a / b;
2558
+ case "+":
2559
+ return a + b;
2560
+ case "-":
2561
+ return a - b;
2562
+ default:
2563
+ return NaN;
2564
+ }
2565
+ }
2566
+ function formatNumber(num) {
2567
+ const rounded = Math.round(num * 1e4) / 1e4;
2568
+ return String(rounded);
2569
+ }
2570
+ function remToPx(value) {
2571
+ return value.replace(/([\d.]+)rem/g, (_, num) => {
2572
+ const px = parseFloat(num) * 16;
2573
+ return `${formatNumber(px)}px`;
2574
+ });
2575
+ }
2576
+ function findVarEnd(str, start) {
2577
+ let depth = 0;
2578
+ for (let i = start; i < str.length; i++) {
2579
+ if (str[i] === "(") {
2580
+ depth++;
2581
+ } else if (str[i] === ")") {
2582
+ depth--;
2583
+ if (depth === 0) {
2584
+ return i;
2585
+ }
2586
+ }
2587
+ }
2588
+ return -1;
2589
+ }
2590
+ function parseVar(str, startIndex) {
2591
+ const varStart = str.indexOf("var(", startIndex);
2592
+ if (varStart === -1) {
2593
+ return null;
2594
+ }
2595
+ const contentStart = varStart + 4;
2596
+ const end = findVarEnd(str, varStart);
2597
+ if (end === -1) {
2598
+ return null;
2599
+ }
2600
+ const content = str.slice(contentStart, end);
2601
+ let commaIndex = -1;
2602
+ let depth = 0;
2603
+ for (let i = 0; i < content.length; i++) {
2604
+ if (content[i] === "(") {
2605
+ depth++;
2606
+ } else if (content[i] === ")") {
2607
+ depth--;
2608
+ } else if (content[i] === "," && depth === 0) {
2609
+ commaIndex = i;
2610
+ break;
2611
+ }
2612
+ }
2613
+ if (commaIndex === -1) {
2614
+ return {
2615
+ end,
2616
+ fallback: null,
2617
+ varName: content.trim()
2618
+ };
2619
+ }
2620
+ return {
2621
+ end,
2622
+ fallback: content.slice(commaIndex + 1).trim(),
2623
+ varName: content.slice(0, commaIndex).trim()
2624
+ };
2625
+ }
2626
+ function resolveCssValue(value, variables, depth = 0) {
2627
+ if (depth > 10) {
2628
+ return value;
2629
+ }
2630
+ let resolved = value;
2631
+ let searchStart = 0;
2632
+ while (true) {
2633
+ const parsed = parseVar(resolved, searchStart);
2634
+ if (!parsed) {
2635
+ break;
2636
+ }
2637
+ const { end, fallback, varName } = parsed;
2638
+ const varStart = resolved.indexOf("var(", searchStart);
2639
+ let replacement;
2640
+ const varValue = variables.get(varName);
2641
+ if (varValue !== void 0) {
2642
+ replacement = resolveCssValue(varValue, variables, depth + 1);
2643
+ } else if (fallback) {
2644
+ replacement = resolveCssValue(fallback, variables, depth + 1);
2645
+ } else {
2646
+ searchStart = end + 1;
2647
+ continue;
2648
+ }
2649
+ resolved = resolved.slice(0, varStart) + replacement + resolved.slice(end + 1);
2650
+ }
2651
+ resolved = resolved.replace(
2652
+ /calc\(([^)]+)\)/g,
2653
+ (match, expression) => {
2654
+ const evaluated = evaluateCalc(expression);
2655
+ return evaluated ?? match;
2656
+ }
2657
+ );
2658
+ resolved = remToPx(resolved);
2659
+ return resolved;
2660
+ }
2661
+ function analyzeSelector(selector) {
2662
+ let inlineable = true;
2663
+ let className = null;
2664
+ const processor = selectorParser((selectors) => {
2665
+ if (selectors.nodes.length !== 1) {
2666
+ inlineable = false;
2667
+ return;
2668
+ }
2669
+ const selectorNode = selectors.nodes[0];
2670
+ if (selectorNode.nodes.length !== 1) {
2671
+ inlineable = false;
2672
+ return;
2673
+ }
2674
+ const node = selectorNode.nodes[0];
2675
+ if (node.type !== "class") {
2676
+ inlineable = false;
2677
+ return;
2678
+ }
2679
+ className = node.value;
2680
+ selectorNode.walk((n) => {
2681
+ if (n.type === "pseudo" || n.type === "combinator" || n.type === "nesting" || n.type === "attribute") {
2682
+ inlineable = false;
2683
+ }
2684
+ });
2685
+ });
2686
+ processor.processSync(selector);
2687
+ return { className, inlineable };
2688
+ }
2689
+ function parseCompiledCss(css) {
2690
+ const rules = [];
2691
+ const root = postcss.parse(css);
2692
+ const variables = extractCssVariables(css);
2693
+ function processRule(rule, insideAtRule) {
2694
+ const { className, inlineable } = analyzeSelector(rule.selector);
2695
+ if (!className) {
2696
+ return;
2697
+ }
2698
+ let hasNestedAtRule = false;
2699
+ rule.walkAtRules(() => {
2700
+ hasNestedAtRule = true;
2701
+ });
2702
+ const declarations = [];
2703
+ rule.each((node) => {
2704
+ if (node.type === "decl") {
2705
+ const resolvedValue = resolveCssValue(node.value, variables);
2706
+ declarations.push(`${node.prop}: ${resolvedValue}`);
2707
+ }
2708
+ });
2709
+ if (declarations.length === 0 && !hasNestedAtRule) {
2710
+ return;
2711
+ }
2712
+ rules.push({
2713
+ className,
2714
+ declarations: declarations.join("; "),
2715
+ inlineable: inlineable && !insideAtRule && !hasNestedAtRule,
2716
+ originalRule: rule.toString()
2717
+ });
2718
+ }
2719
+ root.walkAtRules("layer", (atRule) => {
2720
+ atRule.walkRules((rule) => {
2721
+ const parent = rule.parent;
2722
+ const insideNestedAtRule = parent?.type === "atrule" && parent.name !== "layer";
2723
+ processRule(rule, insideNestedAtRule);
2724
+ });
2725
+ atRule.walkAtRules((nested) => {
2726
+ if (nested.name !== "layer") {
2727
+ nested.walkRules((rule) => {
2728
+ processRule(rule, true);
2729
+ });
2730
+ }
2731
+ });
2732
+ });
2733
+ root.walkRules((rule) => {
2734
+ if (rule.parent?.type === "root") {
2735
+ processRule(rule, false);
2736
+ }
2737
+ });
2738
+ return rules;
2739
+ }
2740
+ function compileClasses(classes, compiler) {
2741
+ const compiledCss = compiler.build(classes);
2742
+ const parsedRules = parseCompiledCss(compiledCss);
2743
+ const inlineableStyles = /* @__PURE__ */ new Map();
2744
+ const residualRules = /* @__PURE__ */ new Map();
2745
+ for (const rule of parsedRules) {
2746
+ if (rule.inlineable) {
2747
+ inlineableStyles.set(rule.className, rule.declarations);
2748
+ } else {
2749
+ residualRules.set(rule.className, rule.originalRule);
2750
+ }
2751
+ }
2752
+ const inlineStyles = [];
2753
+ const remainingClasses = [];
2754
+ for (const cls of classes) {
2755
+ const style = inlineableStyles.get(cls);
2756
+ if (style) {
2757
+ inlineStyles.push(style);
2758
+ } else {
2759
+ remainingClasses.push(cls);
2760
+ }
2761
+ }
2762
+ return { inlineStyles, remainingClasses, residualRules };
2763
+ }
2764
+ function cssToJsxObject(cssDeclarations) {
2765
+ const props = cssDeclarations.flatMap((decl) => {
2766
+ return decl.split(";").map((d) => d.trim()).filter(Boolean);
2767
+ }).map((declaration) => {
2768
+ const colonIndex = declaration.indexOf(":");
2769
+ if (colonIndex === -1) {
2770
+ return null;
2771
+ }
2772
+ const prop = declaration.slice(0, colonIndex).trim();
2773
+ const value = declaration.slice(colonIndex + 1).trim();
2774
+ const camelProp = camelCase(prop);
2775
+ return `${camelProp}: '${value}'`;
2776
+ }).filter(Boolean);
2777
+ return `{ ${props.join(", ")} }`;
2778
+ }
2779
+ function computeLineReplacements(lineText, compiler, styleFormat = "html") {
2780
+ const replacements = /* @__PURE__ */ new Map();
2781
+ const allResidualRules = /* @__PURE__ */ new Map();
2782
+ const classAttrPattern = /(className|class)=(["'`])([^"'`]*)\2/g;
2783
+ let match;
2784
+ while ((match = classAttrPattern.exec(lineText)) !== null) {
2785
+ const fullMatch = match[0];
2786
+ const attrName = match[1];
2787
+ const quote = match[2];
2788
+ const classValue = match[3];
2789
+ const classes = classValue.split(/\s+/).filter(Boolean);
2790
+ if (classes.length === 0) {
2791
+ continue;
2792
+ }
2793
+ const { inlineStyles, remainingClasses, residualRules } = compileClasses(
2794
+ classes,
2795
+ compiler
2796
+ );
2797
+ for (const [className, rule] of residualRules) {
2798
+ allResidualRules.set(className, rule);
2799
+ }
2800
+ if (inlineStyles.length === 0) {
2801
+ continue;
2802
+ }
2803
+ let replacement;
2804
+ if (styleFormat === "jsx") {
2805
+ const jsxStyleObj = cssToJsxObject(inlineStyles);
2806
+ replacement = `style={${jsxStyleObj}}`;
2807
+ } else {
2808
+ replacement = `style=${quote}${inlineStyles.join("; ")}${quote}`;
2809
+ }
2810
+ if (remainingClasses.length > 0) {
2811
+ replacement += ` ${attrName}=${quote}${remainingClasses.join(" ")}${quote}`;
2812
+ }
2813
+ replacements.set(fullMatch, replacement);
2814
+ }
2815
+ return { replacements, residualRules: allResidualRules };
2816
+ }
2817
+ function transformSourceCode(source, compiler, styleFormat = "html") {
2818
+ const allResidualRules = /* @__PURE__ */ new Map();
2819
+ let classesDetected = false;
2820
+ const { replacements, residualRules } = computeLineReplacements(
2821
+ source,
2822
+ compiler,
2823
+ styleFormat
2824
+ );
2825
+ if (replacements.size > 0 || residualRules.size > 0) {
2826
+ classesDetected = true;
2827
+ }
2828
+ for (const [cls, rule] of residualRules) {
2829
+ allResidualRules.set(cls, rule);
2830
+ }
2831
+ let transformed = source;
2832
+ for (const [original, replacement] of replacements) {
2833
+ transformed = transformed.replaceAll(original, replacement);
2834
+ }
2835
+ return {
2836
+ classesDetected,
2837
+ residualRules: allResidualRules,
2838
+ source: transformed
2839
+ };
2840
+ }
2841
+ async function createShikiTailwindTransformer(options) {
2842
+ const {
2843
+ onClassesDetected,
2844
+ onResidualCss,
2845
+ styleFormat = "html",
2846
+ styles
2847
+ } = options;
2848
+ const compiler = await createCompiler(styles);
2849
+ return {
2850
+ name: "shiki-transformer-tailwind-to-inline",
2851
+ preprocess(code) {
2852
+ const { classesDetected, residualRules, source } = transformSourceCode(
2853
+ code,
2854
+ compiler,
2855
+ styleFormat
2856
+ );
2857
+ onClassesDetected?.(classesDetected);
2858
+ if (onResidualCss && residualRules.size > 0) {
2859
+ onResidualCss(residualRules);
2860
+ }
2861
+ return source;
2862
+ }
2863
+ };
2864
+ }
2865
+
2415
2866
  // src/angular-demo-plugin/angular-demo-plugin.ts
2416
2867
  var VIRTUAL_MODULE_ID2 = "\0virtual:angular-demo-registry";
2417
2868
  var LOG_PREFIX = "[angular-demo]";
@@ -2434,7 +2885,8 @@ function angularDemoPlugin({
2434
2885
  theme = {
2435
2886
  dark: quiCustomDarkTheme2,
2436
2887
  light: "github-light-high-contrast"
2437
- }
2888
+ },
2889
+ transformTailwindStyles
2438
2890
  } = {}) {
2439
2891
  let watcher = null;
2440
2892
  let devServer = null;
@@ -2461,7 +2913,7 @@ function angularDemoPlugin({
2461
2913
  if (!highlighter) {
2462
2914
  try {
2463
2915
  highlighter = await createHighlighter({
2464
- langs: ["angular-ts", "angular-html"],
2916
+ langs: ["angular-ts", "angular-html", "css"],
2465
2917
  themes: [theme.dark, theme.light]
2466
2918
  });
2467
2919
  logDev(`${chalk4.blue.bold(LOG_PREFIX)} Shiki highlighter initialized`);
@@ -2539,7 +2991,7 @@ function angularDemoPlugin({
2539
2991
  logDev(
2540
2992
  `${chalk4.blue.bold(LOG_PREFIX)} Processing Angular demo change: ${chalk4.cyan(file)}`
2541
2993
  );
2542
- const code = await readFile(file, "utf-8");
2994
+ const code = await readFile2(file, "utf-8");
2543
2995
  const demoInfo = await parseAngularDemo(file, code);
2544
2996
  if (!demoInfo || !isAngularDemoEntrypoint(file)) {
2545
2997
  const affectedDemos = await scanDemosForFileImport(file);
@@ -2605,7 +3057,7 @@ function angularDemoPlugin({
2605
3057
  const demoFiles = await glob2(demoPattern);
2606
3058
  demoRegistry.clear();
2607
3059
  for (const filePath of demoFiles) {
2608
- const code = await readFile(filePath, "utf-8");
3060
+ const code = await readFile2(filePath, "utf-8");
2609
3061
  const demoInfo = await parseAngularDemo(filePath, code);
2610
3062
  if (demoInfo) {
2611
3063
  demoRegistry.set(demoInfo.id, demoInfo);
@@ -2619,7 +3071,7 @@ function angularDemoPlugin({
2619
3071
  logDev(
2620
3072
  `${chalk4.blue.bold(LOG_PREFIX)} Reloading demo ${chalk4.cyan(demoId)} due to imported file change: ${chalk4.yellow(file)}`
2621
3073
  );
2622
- const code = await readFile(demo.filePath, "utf-8");
3074
+ const code = await readFile2(demo.filePath, "utf-8");
2623
3075
  const updatedDemo = await parseAngularDemo(demo.filePath, code);
2624
3076
  if (updatedDemo) {
2625
3077
  delete demoDimensionsCache[updatedDemo.id];
@@ -2631,29 +3083,44 @@ function angularDemoPlugin({
2631
3083
  }
2632
3084
  return affectedDemos;
2633
3085
  }
2634
- async function highlightCode(code, language = "angular-ts") {
3086
+ async function highlightCode(code, language = "angular-ts", options = {}) {
3087
+ const { onClassesDetected, onResidualCss } = options;
2635
3088
  if (!highlighter) {
2636
- return { codeWithoutSnippets: code, highlightedCode: code };
3089
+ return { full: code };
2637
3090
  }
2638
3091
  let previewCode = null;
2639
- let codeWithoutSnippets = "";
3092
+ const tailwindTransformers = [];
3093
+ if (transformTailwindStyles && onResidualCss) {
3094
+ const transformer = await createShikiTailwindTransformer({
3095
+ onClassesDetected: (detected) => {
3096
+ onClassesDetected?.(detected);
3097
+ },
3098
+ onResidualCss,
3099
+ styleFormat: "html",
3100
+ styles: dedent2`
3101
+ @layer theme, base, components, utilities;
3102
+ @import "tailwindcss/theme.css" layer(theme);
3103
+ @import "tailwindcss/utilities.css" layer(utilities);
3104
+ @import "@qualcomm-ui/tailwind-plugin/qui-strict.css";
3105
+ `
3106
+ });
3107
+ tailwindTransformers.push(transformer);
3108
+ }
2640
3109
  try {
2641
3110
  const highlightedCode = highlighter.codeToHtml(code, {
2642
3111
  ...defaultShikiOptions,
2643
3112
  lang: language,
2644
3113
  transformers: [
2645
3114
  ...getShikiTransformers(),
3115
+ ...tailwindTransformers,
2646
3116
  transformerPreviewBlock({
2647
- attributeName: null,
3117
+ attributeName: "data-preview",
2648
3118
  onComplete: (extractedPreview) => {
2649
- previewCode = extractedPreview ? removeCodeAnnotations(extractedPreview) : null;
3119
+ previewCode = extractedPreview;
2650
3120
  }
2651
3121
  }),
2652
3122
  transformerCodeAttribute({
2653
- attributeName: null,
2654
- onComplete: (formattedSource) => {
2655
- codeWithoutSnippets = formattedSource;
2656
- }
3123
+ attributeName: "data-code"
2657
3124
  }),
2658
3125
  {
2659
3126
  enforce: "post",
@@ -2665,22 +3132,20 @@ function angularDemoPlugin({
2665
3132
  ]
2666
3133
  });
2667
3134
  return {
2668
- codeWithoutSnippets,
2669
- highlightedCode,
2670
- highlightedPreview: previewCode ? extractPreviewFromHighlightedHtml(highlightedCode) : null,
2671
- previewCodeWithoutSnippets: previewCode
3135
+ full: highlightedCode,
3136
+ preview: previewCode ? extractPreviewFromHighlightedHtml(highlightedCode) : null
2672
3137
  };
2673
3138
  } catch (error) {
2674
3139
  console.warn(
2675
3140
  `${chalk4.blue.bold(LOG_PREFIX)} Failed to highlight code with ${language} language:`,
2676
3141
  error
2677
3142
  );
2678
- return { codeWithoutSnippets: code, highlightedCode: code };
3143
+ return { full: code };
2679
3144
  }
2680
3145
  }
2681
3146
  async function extractRelativeImports(filePath) {
2682
3147
  try {
2683
- let visit8 = function(node) {
3148
+ let visit9 = function(node) {
2684
3149
  if (ts.isImportDeclaration(node)) {
2685
3150
  const moduleSpecifier = node.moduleSpecifier;
2686
3151
  if (ts.isStringLiteral(moduleSpecifier)) {
@@ -2699,10 +3164,10 @@ function angularDemoPlugin({
2699
3164
  }
2700
3165
  }
2701
3166
  }
2702
- ts.forEachChild(node, visit8);
3167
+ ts.forEachChild(node, visit9);
2703
3168
  };
2704
- var visit7 = visit8;
2705
- const content = await readFile(filePath, "utf-8");
3169
+ var visit8 = visit9;
3170
+ const content = await readFile2(filePath, "utf-8");
2706
3171
  const sourceFile = ts.createSourceFile(
2707
3172
  filePath,
2708
3173
  content,
@@ -2711,7 +3176,7 @@ function angularDemoPlugin({
2711
3176
  ts.ScriptKind.TS
2712
3177
  );
2713
3178
  const relativeImports = [];
2714
- visit8(sourceFile);
3179
+ visit9(sourceFile);
2715
3180
  return relativeImports;
2716
3181
  } catch (error) {
2717
3182
  logDev(
@@ -2734,16 +3199,16 @@ function angularDemoPlugin({
2734
3199
  }
2735
3200
  function stripImports(code, fileName) {
2736
3201
  try {
2737
- let visit8 = function(node) {
3202
+ let visit9 = function(node) {
2738
3203
  if (ts.isImportDeclaration(node)) {
2739
3204
  importRanges.push({
2740
3205
  end: node.getEnd(),
2741
3206
  start: node.getFullStart()
2742
3207
  });
2743
3208
  }
2744
- ts.forEachChild(node, visit8);
3209
+ ts.forEachChild(node, visit9);
2745
3210
  };
2746
- var visit7 = visit8;
3211
+ var visit8 = visit9;
2747
3212
  const sourceFile = ts.createSourceFile(
2748
3213
  fileName,
2749
3214
  code,
@@ -2752,7 +3217,7 @@ function angularDemoPlugin({
2752
3217
  ts.ScriptKind.TS
2753
3218
  );
2754
3219
  const importRanges = [];
2755
- visit8(sourceFile);
3220
+ visit9(sourceFile);
2756
3221
  return importRanges.map((range) => {
2757
3222
  let endPos = range.end;
2758
3223
  if (code[endPos] === "\n") {
@@ -2785,24 +3250,51 @@ function angularDemoPlugin({
2785
3250
  const fileName = basename(filePath);
2786
3251
  const importsWithoutStrip = stripImports(code, filePath);
2787
3252
  const sourceCode = [];
3253
+ const aggregatedRules = /* @__PURE__ */ new Map();
2788
3254
  const mainSourceEntry = await buildAngularSourceEntry({
2789
3255
  code,
2790
3256
  fileName,
2791
3257
  filePath,
2792
3258
  language: "angular-ts"
2793
3259
  });
2794
- sourceCode.push(mainSourceEntry);
3260
+ sourceCode.push(mainSourceEntry.sourceCodeData);
3261
+ if (mainSourceEntry.residualRules) {
3262
+ for (const [className, rule] of mainSourceEntry.residualRules) {
3263
+ aggregatedRules.set(className, rule);
3264
+ }
3265
+ }
2795
3266
  if (templateUrl) {
2796
3267
  const templateEntry = await maybeBuildTemplateSourceEntry(
2797
3268
  templateUrl,
2798
3269
  filePath
2799
3270
  );
2800
3271
  if (templateEntry) {
2801
- sourceCode.push(templateEntry);
3272
+ sourceCode.push(templateEntry.sourceCodeData);
3273
+ if (templateEntry.residualRules) {
3274
+ for (const [className, rule] of templateEntry.residualRules) {
3275
+ aggregatedRules.set(className, rule);
3276
+ }
3277
+ }
2802
3278
  }
2803
3279
  }
2804
3280
  const importedEntries = await buildImportedSourceEntries(filePath);
2805
- sourceCode.push(...importedEntries);
3281
+ for (const entry of importedEntries) {
3282
+ sourceCode.push(entry.sourceCodeData);
3283
+ if (entry.residualRules) {
3284
+ for (const [className, rule] of entry.residualRules) {
3285
+ aggregatedRules.set(className, rule);
3286
+ }
3287
+ }
3288
+ }
3289
+ const aggregatedResidualCss = aggregatedRules.size > 0 ? [...aggregatedRules.values()].join("\n\n") : void 0;
3290
+ if (aggregatedResidualCss) {
3291
+ const cssHighlighted = await highlightCode(aggregatedResidualCss, "css");
3292
+ sourceCode.push({
3293
+ fileName: "styles.css",
3294
+ highlighted: cssHighlighted,
3295
+ type: "residual-css"
3296
+ });
3297
+ }
2806
3298
  return {
2807
3299
  componentClass,
2808
3300
  filePath: importPath.startsWith(".") ? importPath : `./${importPath}`,
@@ -2837,7 +3329,7 @@ function angularDemoPlugin({
2837
3329
  let templateUrl = null;
2838
3330
  let hasDefaultExport = false;
2839
3331
  const importsFromAst = [];
2840
- function visit7(node) {
3332
+ function visit8(node) {
2841
3333
  if (ts.isImportDeclaration(node)) {
2842
3334
  importsFromAst.push(node.getFullText(sourceFile).trim());
2843
3335
  }
@@ -2885,9 +3377,9 @@ function angularDemoPlugin({
2885
3377
  if (ts.isExportAssignment(node) && !node.isExportEquals) {
2886
3378
  hasDefaultExport = true;
2887
3379
  }
2888
- ts.forEachChild(node, visit7);
3380
+ ts.forEachChild(node, visit8);
2889
3381
  }
2890
- visit7(sourceFile);
3382
+ visit8(sourceFile);
2891
3383
  return {
2892
3384
  componentClass,
2893
3385
  hasDefaultExport,
@@ -2899,24 +3391,34 @@ function angularDemoPlugin({
2899
3391
  }
2900
3392
  async function buildAngularSourceEntry(params) {
2901
3393
  const { code, fileName, filePath, language } = params;
2902
- if (params.language === "angular-ts") {
2903
- }
2904
- const {
2905
- codeWithoutSnippets,
2906
- highlightedCode,
2907
- highlightedPreview,
2908
- previewCodeWithoutSnippets
2909
- } = await highlightCode(code, language);
3394
+ const baseResult = await highlightCode(code, language);
3395
+ let inlineResult;
3396
+ let classesDetected = false;
3397
+ let residualRules;
3398
+ if (transformTailwindStyles) {
3399
+ inlineResult = await highlightCode(code, language, {
3400
+ onClassesDetected: (detected) => {
3401
+ classesDetected = detected;
3402
+ },
3403
+ onResidualCss: (rules) => {
3404
+ residualRules = rules;
3405
+ }
3406
+ });
3407
+ }
2910
3408
  return {
2911
- fileName,
2912
- filePath,
2913
- highlighted: {
2914
- full: highlightedCode,
2915
- preview: highlightedPreview ?? null
2916
- },
2917
- raw: {
2918
- full: codeWithoutSnippets,
2919
- preview: previewCodeWithoutSnippets ?? null
3409
+ residualRules,
3410
+ sourceCodeData: {
3411
+ fileName,
3412
+ filePath,
3413
+ highlighted: {
3414
+ full: baseResult.full,
3415
+ preview: baseResult.preview
3416
+ },
3417
+ highlightedInline: classesDetected && inlineResult ? {
3418
+ full: inlineResult.full,
3419
+ preview: inlineResult.preview
3420
+ } : void 0,
3421
+ type: "file"
2920
3422
  }
2921
3423
  };
2922
3424
  }
@@ -2926,7 +3428,7 @@ function angularDemoPlugin({
2926
3428
  return null;
2927
3429
  }
2928
3430
  try {
2929
- const templateCode = await readFile(templatePath, "utf-8");
3431
+ const templateCode = await readFile2(templatePath, "utf-8");
2930
3432
  return buildAngularSourceEntry({
2931
3433
  code: templateCode,
2932
3434
  fileName: basename(templatePath),
@@ -2942,25 +3444,25 @@ function angularDemoPlugin({
2942
3444
  }
2943
3445
  }
2944
3446
  async function buildImportedSourceEntries(fromFilePath) {
2945
- const sourceCode = [];
3447
+ const entries = [];
2946
3448
  const relativeImports = await collectAllImports(fromFilePath);
2947
3449
  for (const resolvedPath of relativeImports) {
2948
3450
  try {
2949
- const importedCode = await readFile(resolvedPath, "utf-8");
3451
+ const importedCode = await readFile2(resolvedPath, "utf-8");
2950
3452
  const entry = await buildAngularSourceEntry({
2951
3453
  code: importedCode,
2952
3454
  fileName: basename(resolvedPath),
2953
3455
  filePath: resolvedPath,
2954
3456
  language: "angular-ts"
2955
3457
  });
2956
- sourceCode.push(entry);
3458
+ entries.push(entry);
2957
3459
  } catch (error) {
2958
3460
  logDev(
2959
3461
  `${chalk4.blue.bold(LOG_PREFIX)} ${chalk4.yellowBright("Failed to process relative import:")} ${chalk4.cyan(resolvedPath)}`
2960
3462
  );
2961
3463
  }
2962
3464
  }
2963
- return sourceCode;
3465
+ return entries;
2964
3466
  }
2965
3467
  function generateRegistryModule() {
2966
3468
  const demos = Array.from(demoRegistry.values());
@@ -3039,7 +3541,7 @@ export function getAngularDemoInfo(demoId) {
3039
3541
  function resolveRelativeImport2(source, fromFile) {
3040
3542
  const fromDir = dirname(fromFile);
3041
3543
  const resolved = resolve2(fromDir, source);
3042
- const extensions = [".ts", ".tsx", ".js", ".jsx"];
3544
+ const extensions = [".ts", ".js"];
3043
3545
  for (const ext of extensions) {
3044
3546
  const withExt = resolved + ext;
3045
3547
  if (existsSync(withExt)) {
@@ -3149,7 +3651,7 @@ export function getAngularDemoInfo(demoId) {
3149
3651
  });
3150
3652
  }
3151
3653
  async function handleAngularDemoUpdate(filePath) {
3152
- const code = await readFile(filePath, "utf-8");
3654
+ const code = await readFile2(filePath, "utf-8");
3153
3655
  const demoInfo = await parseAngularDemo(filePath, code);
3154
3656
  if (demoInfo) {
3155
3657
  demoRegistry.set(demoInfo.id, demoInfo);
@@ -3223,7 +3725,7 @@ function resolvePathAlias(source, pathAliases) {
3223
3725
  for (const alias of pathAliases) {
3224
3726
  if (alias.pattern.test(source)) {
3225
3727
  const resolvedPath = source.replace(alias.pattern, alias.replacement);
3226
- const extensions = [".ts", ".tsx", ".js", ".jsx"];
3728
+ const extensions = [".ts", ".js"];
3227
3729
  for (const ext of extensions) {
3228
3730
  const withExt = resolvedPath + ext;
3229
3731
  if (existsSync(withExt)) {
@@ -3255,55 +3757,6 @@ function resolveTemplateFile(templateUrl, fromFile) {
3255
3757
  }
3256
3758
  return resolved;
3257
3759
  }
3258
- function extractPreviewFromHighlightedHtml(highlightedHtml) {
3259
- const preMatch = highlightedHtml.match(/<pre([^>]*)>/);
3260
- const codeMatch = highlightedHtml.match(/<code([^>]*)>(.*?)<\/code>/s);
3261
- if (!preMatch || !codeMatch) {
3262
- return null;
3263
- }
3264
- const codeContent = codeMatch[2];
3265
- const parts = codeContent.split(/<span class="line/);
3266
- const previewLineParts = parts.slice(1).filter((part) => part.includes('data-preview-line="true"'));
3267
- const indents = previewLineParts.map((part) => {
3268
- const indentMatches = part.match(/<span class="indent">(.+?)<\/span>/g) || [];
3269
- let total = 0;
3270
- for (const match of indentMatches) {
3271
- const content = match.match(/<span class="indent">(.+?)<\/span>/);
3272
- if (content) {
3273
- total += content[1].length;
3274
- } else {
3275
- break;
3276
- }
3277
- }
3278
- return total;
3279
- });
3280
- const minIndent = Math.min(...indents.filter((n) => n > 0));
3281
- const previewLines = previewLineParts.map((part) => {
3282
- let processed = `<span class="line${part}`;
3283
- let remaining = minIndent;
3284
- while (remaining > 0 && processed.includes('<span class="indent">')) {
3285
- const before = processed;
3286
- processed = processed.replace(
3287
- /<span class="indent">(.+?)<\/span>/,
3288
- (match, spaces) => {
3289
- if (spaces.length <= remaining) {
3290
- remaining -= spaces.length;
3291
- return "";
3292
- } else {
3293
- const kept = spaces.substring(remaining);
3294
- remaining = 0;
3295
- return `<span class="indent">${kept}</span>`;
3296
- }
3297
- }
3298
- );
3299
- if (before === processed) {
3300
- break;
3301
- }
3302
- }
3303
- return processed;
3304
- });
3305
- return `<pre${preMatch[1]}><code${codeMatch[1]}>${previewLines.join("")}</code></pre>`;
3306
- }
3307
3760
 
3308
3761
  // src/react-demo-plugin/demo-plugin-constants.ts
3309
3762
  var VIRTUAL_MODULE_IDS = {
@@ -3341,7 +3794,7 @@ var NODE_BUILTINS = [
3341
3794
  // src/react-demo-plugin/demo-plugin-utils.ts
3342
3795
  import chalk5 from "chalk";
3343
3796
  import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
3344
- import { readFile as readFile2 } from "node:fs/promises";
3797
+ import { readFile as readFile3 } from "node:fs/promises";
3345
3798
  import { dirname as dirname2, join as join3, relative as relative2, resolve as resolve3, sep } from "node:path";
3346
3799
  import * as ts2 from "typescript";
3347
3800
  import { pascalCase } from "@qualcomm-ui/utils/change-case";
@@ -3358,7 +3811,7 @@ function createDemoName(filePath) {
3358
3811
  }
3359
3812
  async function extractFileImports(filePath) {
3360
3813
  try {
3361
- const content = await readFile2(filePath, "utf-8");
3814
+ const content = await readFile3(filePath, "utf-8");
3362
3815
  return extractImports(content, filePath);
3363
3816
  } catch (error) {
3364
3817
  console.log(
@@ -3378,7 +3831,7 @@ function extractImports(code, fileName) {
3378
3831
  );
3379
3832
  const thirdPartyImports = [];
3380
3833
  const relativeImports = [];
3381
- function visit7(node) {
3834
+ function visit8(node) {
3382
3835
  if (ts2.isImportDeclaration(node)) {
3383
3836
  const importSpec = parseImportDeclaration(node, fileName);
3384
3837
  if (importSpec) {
@@ -3389,9 +3842,9 @@ function extractImports(code, fileName) {
3389
3842
  }
3390
3843
  }
3391
3844
  }
3392
- ts2.forEachChild(node, visit7);
3845
+ ts2.forEachChild(node, visit8);
3393
3846
  }
3394
- visit7(sourceFile);
3847
+ visit8(sourceFile);
3395
3848
  return { relativeImports, thirdPartyImports };
3396
3849
  }
3397
3850
  function getScriptKind(fileName) {
@@ -3638,14 +4091,14 @@ function isDemoFile(filePath) {
3638
4091
  // src/react-demo-plugin/react-demo-plugin.ts
3639
4092
  import chalk6 from "chalk";
3640
4093
  import { glob as glob3 } from "glob";
3641
- import { readFile as readFile3 } from "node:fs/promises";
4094
+ import { readFile as readFile4 } from "node:fs/promises";
3642
4095
  import { basename as basename2, resolve as resolve4 } from "node:path";
3643
4096
  import { createHighlighter as createHighlighter2 } from "shiki";
3644
4097
  import * as ts3 from "typescript";
3645
4098
  import {
3646
4099
  quiCustomDarkTheme as quiCustomDarkTheme3
3647
4100
  } from "@qualcomm-ui/mdx-common";
3648
- import { dedent as dedent2 } from "@qualcomm-ui/utils/dedent";
4101
+ import { dedent as dedent3 } from "@qualcomm-ui/utils/dedent";
3649
4102
  var highlighter2 = null;
3650
4103
  var initializingHighlighter = false;
3651
4104
  var demoRegistry2 = /* @__PURE__ */ new Map();
@@ -3659,7 +4112,8 @@ function reactDemoPlugin({
3659
4112
  light: "github-light-high-contrast"
3660
4113
  },
3661
4114
  transformers = [],
3662
- transformLine
4115
+ transformLine,
4116
+ transformTailwindStyles
3663
4117
  } = {}) {
3664
4118
  const defaultShikiOptions = {
3665
4119
  defaultColor: "light-dark()",
@@ -3678,10 +4132,8 @@ function reactDemoPlugin({
3678
4132
  initializingHighlighter = true;
3679
4133
  try {
3680
4134
  highlighter2 = await createHighlighter2({
3681
- langs: ["tsx", "typescript"],
4135
+ langs: ["tsx", "typescript", "css"],
3682
4136
  themes: [theme.dark, theme.light]
3683
- }).finally(() => {
3684
- initializingHighlighter = false;
3685
4137
  });
3686
4138
  console.log(
3687
4139
  `${chalk6.magenta.bold(LOG_PREFIX2)} Shiki highlighter initialized`
@@ -3691,6 +4143,8 @@ function reactDemoPlugin({
3691
4143
  `${chalk6.magenta.bold(LOG_PREFIX2)} Failed to initialize highlighter:`,
3692
4144
  error
3693
4145
  );
4146
+ } finally {
4147
+ initializingHighlighter = false;
3694
4148
  }
3695
4149
  }
3696
4150
  await collectReactDemos();
@@ -3772,26 +4226,46 @@ function reactDemoPlugin({
3772
4226
  }
3773
4227
  }
3774
4228
  }
3775
- async function highlightCode(code) {
4229
+ async function highlightCode(code, options = {}) {
4230
+ const { extraTransformers = [], onResidualCss } = options;
3776
4231
  if (!highlighter2) {
3777
- return { codeWithoutSnippets: code, highlightedCode: code };
4232
+ return { full: code };
3778
4233
  }
3779
4234
  let previewCode = null;
3780
- let codeWithoutSnippets = "";
4235
+ const tailwindTransformers = [];
4236
+ if (transformTailwindStyles && onResidualCss) {
4237
+ const transformer = await createShikiTailwindTransformer({
4238
+ onClassesDetected: (detected) => {
4239
+ options.onClassesDetected?.(detected);
4240
+ },
4241
+ onResidualCss,
4242
+ styleFormat: "jsx",
4243
+ styles: dedent3`
4244
+ @layer theme, base, components, utilities;
4245
+ @import "tailwindcss/theme.css" layer(theme);
4246
+ @import "tailwindcss/utilities.css" layer(utilities);
4247
+ @import "@qualcomm-ui/tailwind-plugin/qui-strict.css";
4248
+ `
4249
+ });
4250
+ tailwindTransformers.push(transformer);
4251
+ } else if (extraTransformers.length > 0) {
4252
+ tailwindTransformers.push(...extraTransformers);
4253
+ }
3781
4254
  try {
3782
- const result = highlighter2.codeToHtml(code, {
4255
+ const highlightedCode = highlighter2.codeToHtml(code, {
3783
4256
  ...defaultShikiOptions,
3784
4257
  transformers: [
3785
4258
  ...getShikiTransformers(),
4259
+ ...transformers,
4260
+ ...tailwindTransformers,
3786
4261
  transformerPreviewBlock({
4262
+ attributeName: "data-preview",
3787
4263
  onComplete: (extractedPreview) => {
3788
- previewCode = extractedPreview ? removeCodeAnnotations(extractedPreview) : null;
4264
+ previewCode = extractedPreview;
3789
4265
  }
3790
4266
  }),
3791
4267
  transformerCodeAttribute({
3792
- onComplete: (formattedSource) => {
3793
- codeWithoutSnippets = formattedSource;
3794
- }
4268
+ attributeName: "data-code"
3795
4269
  }),
3796
4270
  {
3797
4271
  enforce: "post",
@@ -3803,38 +4277,16 @@ function reactDemoPlugin({
3803
4277
  ...transformers
3804
4278
  ]
3805
4279
  });
3806
- let highlightedPreview = null;
3807
- if (previewCode) {
3808
- highlightedPreview = highlighter2.codeToHtml(code, {
3809
- ...defaultShikiOptions,
3810
- transformers: [
3811
- ...getShikiTransformers(),
3812
- transformerPreviewBlock({
3813
- displayMode: "only-preview"
3814
- }),
3815
- {
3816
- enforce: "post",
3817
- name: "shiki-transformer-trim",
3818
- preprocess(code2) {
3819
- return code2.trim();
3820
- }
3821
- },
3822
- ...transformers
3823
- ]
3824
- });
3825
- }
3826
4280
  return {
3827
- codeWithoutSnippets,
3828
- highlightedCode: result,
3829
- highlightedPreview,
3830
- previewCodeWithoutSnippets: previewCode
4281
+ full: highlightedCode,
4282
+ preview: previewCode ? extractPreviewFromHighlightedHtml(highlightedCode) : null
3831
4283
  };
3832
4284
  } catch (error) {
3833
4285
  console.warn(
3834
4286
  `${chalk6.magenta.bold(LOG_PREFIX2)} Failed to highlight code:`,
3835
4287
  error
3836
4288
  );
3837
- return { codeWithoutSnippets: code, highlightedCode: code };
4289
+ return { full: code };
3838
4290
  }
3839
4291
  }
3840
4292
  async function collectReactDemos() {
@@ -3894,22 +4346,34 @@ function reactDemoPlugin({
3894
4346
  async function extractHighlightedCode(filePath, code) {
3895
4347
  try {
3896
4348
  const fileName = basename2(filePath);
3897
- const {
3898
- codeWithoutSnippets,
3899
- highlightedCode,
3900
- highlightedPreview,
3901
- previewCodeWithoutSnippets
3902
- } = await highlightCode(code);
4349
+ const baseResult = await highlightCode(code);
4350
+ let inlineResult;
4351
+ let classesDetected = false;
4352
+ let residualRules;
4353
+ if (transformTailwindStyles) {
4354
+ inlineResult = await highlightCode(code, {
4355
+ onClassesDetected: (detected) => {
4356
+ classesDetected = detected;
4357
+ },
4358
+ onResidualCss: (rules) => {
4359
+ residualRules = rules;
4360
+ }
4361
+ });
4362
+ }
3903
4363
  return {
3904
- fileName,
3905
- filePath,
3906
- highlighted: {
3907
- full: highlightedCode,
3908
- preview: highlightedPreview
3909
- },
3910
- raw: {
3911
- full: codeWithoutSnippets,
3912
- preview: previewCodeWithoutSnippets
4364
+ residualRules,
4365
+ sourceCodeData: {
4366
+ fileName,
4367
+ filePath,
4368
+ highlighted: {
4369
+ full: baseResult.full,
4370
+ preview: baseResult.preview
4371
+ },
4372
+ highlightedInline: classesDetected && inlineResult ? {
4373
+ full: inlineResult.full,
4374
+ preview: inlineResult.preview
4375
+ } : void 0,
4376
+ type: "file"
3913
4377
  }
3914
4378
  };
3915
4379
  } catch {
@@ -3918,36 +4382,55 @@ function reactDemoPlugin({
3918
4382
  }
3919
4383
  async function extractFileData(filePath) {
3920
4384
  try {
3921
- const code = await readFile3(filePath, "utf-8").then(transformLines);
4385
+ const code = await readFile4(filePath, "utf-8").then(transformLines);
3922
4386
  const imports = stripImports(code, filePath);
3923
4387
  const sourceCode = [];
3924
- const sourceCodeData = await extractHighlightedCode(filePath, code);
3925
- if (sourceCodeData) {
3926
- sourceCode.push(sourceCodeData);
4388
+ const aggregatedRules = /* @__PURE__ */ new Map();
4389
+ const extractedMain = await extractHighlightedCode(filePath, code);
4390
+ if (extractedMain) {
4391
+ sourceCode.push(extractedMain.sourceCodeData);
4392
+ if (extractedMain.residualRules) {
4393
+ for (const [className, rule] of extractedMain.residualRules) {
4394
+ aggregatedRules.set(className, rule);
4395
+ }
4396
+ }
3927
4397
  }
3928
4398
  const fileImports = await extractFileImports(filePath);
3929
4399
  if (fileImports) {
3930
4400
  for (const relativeImport of fileImports.relativeImports) {
3931
4401
  try {
3932
- const importedCode = await readFile3(
4402
+ const importedCode = await readFile4(
3933
4403
  relativeImport.resolvedPath,
3934
4404
  "utf-8"
3935
4405
  ).then(transformLines);
3936
- const sourceCodeData2 = await extractHighlightedCode(
4406
+ const extracted = await extractHighlightedCode(
3937
4407
  relativeImport.resolvedPath,
3938
4408
  importedCode
3939
4409
  );
3940
- if (sourceCodeData2) {
3941
- sourceCode.push(sourceCodeData2);
4410
+ if (extracted) {
4411
+ sourceCode.push(extracted.sourceCodeData);
4412
+ if (extracted.residualRules) {
4413
+ for (const [className, rule] of extracted.residualRules) {
4414
+ aggregatedRules.set(className, rule);
4415
+ }
4416
+ }
3942
4417
  }
3943
4418
  } catch {
3944
4419
  console.debug("Failed to process file", relativeImport.resolvedPath);
3945
4420
  }
3946
4421
  }
3947
4422
  }
4423
+ const aggregatedResidualCss = aggregatedRules.size > 0 ? [...aggregatedRules.values()].join("\n\n") : void 0;
4424
+ if (aggregatedResidualCss) {
4425
+ sourceCode.push({
4426
+ fileName: "styles.css",
4427
+ highlighted: await highlightCode(aggregatedResidualCss),
4428
+ type: "residual-css"
4429
+ });
4430
+ }
3948
4431
  return {
3949
4432
  demoName: createDemoName(filePath),
3950
- fileName: sourceCodeData?.fileName || basename2(filePath),
4433
+ fileName: extractedMain?.sourceCodeData.fileName || basename2(filePath),
3951
4434
  filePath,
3952
4435
  imports,
3953
4436
  sourceCode
@@ -3958,16 +4441,16 @@ function reactDemoPlugin({
3958
4441
  }
3959
4442
  function stripImports(code, fileName) {
3960
4443
  try {
3961
- let visit8 = function(node) {
4444
+ let visit9 = function(node) {
3962
4445
  if (ts3.isImportDeclaration(node)) {
3963
4446
  importRanges.push({
3964
4447
  end: node.getEnd(),
3965
4448
  start: node.getFullStart()
3966
4449
  });
3967
4450
  }
3968
- ts3.forEachChild(node, visit8);
4451
+ ts3.forEachChild(node, visit9);
3969
4452
  };
3970
- var visit7 = visit8;
4453
+ var visit8 = visit9;
3971
4454
  const sourceFile = ts3.createSourceFile(
3972
4455
  fileName,
3973
4456
  code,
@@ -3976,7 +4459,7 @@ function reactDemoPlugin({
3976
4459
  getScriptKind(fileName)
3977
4460
  );
3978
4461
  const importRanges = [];
3979
- visit8(sourceFile);
4462
+ visit9(sourceFile);
3980
4463
  return importRanges.map((range) => {
3981
4464
  let endPos = range.end;
3982
4465
  if (code[endPos] === "\n") {
@@ -3997,7 +4480,7 @@ ${entries}
3997
4480
  ])`;
3998
4481
  }
3999
4482
  function generateExportedFunctions() {
4000
- return dedent2`
4483
+ return dedent3`
4001
4484
  export function getDemo(demoName) {
4002
4485
  const demo = demoRegistry.get(demoName)
4003
4486
  if (!demo) {