@qualcomm-ui/mdx-vite 2.3.0 → 2.5.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.
Files changed (26) hide show
  1. package/README.md +1 -2
  2. package/dist/angular-demo-plugin/angular-demo-plugin.d.ts +7 -1
  3. package/dist/angular-demo-plugin/angular-demo-plugin.d.ts.map +1 -1
  4. package/dist/cli.js +12 -0
  5. package/dist/cli.js.map +2 -2
  6. package/dist/docs-plugin/docs-plugin.d.ts.map +1 -1
  7. package/dist/docs-plugin/internal/search-indexer.d.ts.map +1 -1
  8. package/dist/docs-plugin/internal/services/markdown/markdown.types.d.ts +5 -1
  9. package/dist/docs-plugin/internal/services/markdown/markdown.types.d.ts.map +1 -1
  10. package/dist/docs-plugin/shiki/internal/index.d.ts +2 -0
  11. package/dist/docs-plugin/shiki/internal/index.d.ts.map +1 -0
  12. package/dist/docs-plugin/shiki/internal/shiki-transformer-tailwind.d.ts +50 -0
  13. package/dist/docs-plugin/shiki/internal/shiki-transformer-tailwind.d.ts.map +1 -0
  14. package/dist/docs-plugin/shiki/internal/tests/shiki-transformer-tailwind.spec.d.ts +2 -0
  15. package/dist/docs-plugin/shiki/internal/tests/shiki-transformer-tailwind.spec.d.ts.map +1 -0
  16. package/dist/docs-plugin/shiki/shiki-transformer-preview-block.d.ts.map +1 -1
  17. package/dist/docs-plugin/shiki/utils.d.ts +1 -0
  18. package/dist/docs-plugin/shiki/utils.d.ts.map +1 -1
  19. package/dist/index.js +681 -185
  20. package/dist/index.js.map +4 -4
  21. package/dist/react-demo-plugin/demo-plugin-types.d.ts +7 -0
  22. package/dist/react-demo-plugin/demo-plugin-types.d.ts.map +1 -1
  23. package/dist/react-demo-plugin/react-demo-plugin.d.ts +1 -1
  24. package/dist/react-demo-plugin/react-demo-plugin.d.ts.map +1 -1
  25. package/dist/tsbuildinfo +1 -1
  26. package/package.json +8 -2
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";
@@ -1495,11 +1496,13 @@ var SearchIndexer = class {
1495
1496
  changed: {},
1496
1497
  filePath
1497
1498
  };
1499
+ let previousPage = void 0;
1498
1500
  if (!cached) {
1499
1501
  const previousData = this.fileCache.readCache(filePath);
1500
1502
  if (previousData) {
1501
1503
  const cachedFm = JSON.stringify(previousData.frontmatter);
1502
1504
  const currentFm = JSON.stringify(frontmatter);
1505
+ previousPage = previousData.page;
1503
1506
  if (cachedFm !== currentFm) {
1504
1507
  metadata.changed.frontmatter = true;
1505
1508
  }
@@ -1527,6 +1530,16 @@ var SearchIndexer = class {
1527
1530
  return { metadata, pageSections: [defaultSection] };
1528
1531
  }
1529
1532
  const { sections, toc } = indexedPage;
1533
+ if (previousPage) {
1534
+ for (let i = 0; i < toc.length; i++) {
1535
+ const previousHeading = previousPage.toc[i];
1536
+ const currentHeading = toc[i];
1537
+ if (previousHeading?.id !== currentHeading.id) {
1538
+ metadata.changed.toc = true;
1539
+ break;
1540
+ }
1541
+ }
1542
+ }
1530
1543
  if (toc.length) {
1531
1544
  this._pageMap[defaultSection.pathname].toc = toc;
1532
1545
  }
@@ -1805,6 +1818,7 @@ function quiDocsPlugin(opts) {
1805
1818
  }
1806
1819
  const virtualModule = server.moduleGraph.getModuleById(VIRTUAL_MODULE_ID);
1807
1820
  if (virtualModule) {
1821
+ server.moduleGraph.invalidateModule(virtualModule);
1808
1822
  server.ws.send({
1809
1823
  data: state.siteData,
1810
1824
  event: "qui-docs-plugin:refresh-site-data",
@@ -2240,6 +2254,55 @@ function removeCodeAnnotations(code) {
2240
2254
  return true;
2241
2255
  }).map(({ processed }) => processed).join("\n");
2242
2256
  }
2257
+ function extractPreviewFromHighlightedHtml(highlightedHtml) {
2258
+ const preMatch = highlightedHtml.match(/<pre((?:\s+[\w-]+="[^"]*")*)>/);
2259
+ const codeMatch = highlightedHtml.match(/<code([^>]*)>(.*?)<\/code>/s);
2260
+ if (!preMatch || !codeMatch) {
2261
+ return null;
2262
+ }
2263
+ const codeContent = codeMatch[2];
2264
+ const parts = codeContent.split(/<span class="line/);
2265
+ const previewLineParts = parts.slice(1).filter((part) => part.includes('data-preview-line="true"'));
2266
+ const indents = previewLineParts.map((part) => {
2267
+ const indentMatches = part.match(/<span class="indent">(.+?)<\/span>/g) || [];
2268
+ let total = 0;
2269
+ for (const match of indentMatches) {
2270
+ const content = match.match(/<span class="indent">(.+?)<\/span>/);
2271
+ if (content) {
2272
+ total += content[1].length;
2273
+ } else {
2274
+ break;
2275
+ }
2276
+ }
2277
+ return total;
2278
+ });
2279
+ const minIndent = Math.min(...indents.filter((n) => n > 0));
2280
+ const previewLines = previewLineParts.map((part) => {
2281
+ let processed = `<span class="line${part}`;
2282
+ let remaining = minIndent;
2283
+ while (remaining > 0 && processed.includes('<span class="indent">')) {
2284
+ const before = processed;
2285
+ processed = processed.replace(
2286
+ /<span class="indent">(.+?)<\/span>/,
2287
+ (match, spaces) => {
2288
+ if (spaces.length <= remaining) {
2289
+ remaining -= spaces.length;
2290
+ return "";
2291
+ } else {
2292
+ const kept = spaces.substring(remaining);
2293
+ remaining = 0;
2294
+ return `<span class="indent">${kept}</span>`;
2295
+ }
2296
+ }
2297
+ );
2298
+ if (before === processed) {
2299
+ break;
2300
+ }
2301
+ }
2302
+ return processed;
2303
+ });
2304
+ return `<pre${preMatch[1]}><code${codeMatch[1]}>${previewLines.join("")}</code></pre>`;
2305
+ }
2243
2306
 
2244
2307
  // src/docs-plugin/shiki/shiki-transformer-code-attribute.ts
2245
2308
  function transformerCodeAttribute(opts = { attributeName: "data-code" }) {
@@ -2315,10 +2378,11 @@ function transformerPreviewBlock(options = {
2315
2378
  },
2316
2379
  name: "transformer-preview-block",
2317
2380
  pre(node) {
2318
- if (previewContent && options.attributeName != null) {
2319
- node.properties[options.attributeName] = previewContent;
2381
+ const content = previewContent ? removeCodeAnnotations(previewContent) : null;
2382
+ if (content && options.attributeName != null) {
2383
+ node.properties[options.attributeName] = content;
2320
2384
  }
2321
- options.onComplete?.(previewContent || null);
2385
+ options.onComplete?.(content || null);
2322
2386
  },
2323
2387
  preprocess(code) {
2324
2388
  previewContent = null;
@@ -2412,6 +2476,406 @@ function getRemarkPlugins() {
2412
2476
  ];
2413
2477
  }
2414
2478
 
2479
+ // src/docs-plugin/shiki/internal/shiki-transformer-tailwind.ts
2480
+ import { fromHtml as fromHtml2 } from "hast-util-from-html";
2481
+ import { toHtml } from "hast-util-to-html";
2482
+ import { readFile } from "node:fs/promises";
2483
+ import postcss from "postcss";
2484
+ import selectorParser from "postcss-selector-parser";
2485
+ import { compile } from "tailwindcss";
2486
+ import { visit as visit7 } from "unist-util-visit";
2487
+ import { camelCase } from "@qualcomm-ui/utils/change-case";
2488
+ async function loadStylesheetContent(id) {
2489
+ const resolveId = id === "tailwindcss" ? "tailwindcss/index.css" : id;
2490
+ let resolvedPath;
2491
+ if (typeof createRequire !== "undefined") {
2492
+ resolvedPath = createRequire(import.meta.url).resolve(resolveId);
2493
+ } else {
2494
+ const createRequire2 = await import("node:module").then(
2495
+ (module) => module.createRequire
2496
+ );
2497
+ resolvedPath = createRequire2(import.meta.url).resolve(resolveId);
2498
+ }
2499
+ return readFile(resolvedPath, "utf-8");
2500
+ }
2501
+ async function createCompiler(styles) {
2502
+ return compile(styles, {
2503
+ loadStylesheet: async (id, base) => {
2504
+ const content = await loadStylesheetContent(id);
2505
+ return {
2506
+ base,
2507
+ content,
2508
+ path: `virtual:${id}`
2509
+ };
2510
+ }
2511
+ });
2512
+ }
2513
+ function extractCssVariables(css) {
2514
+ const variables = /* @__PURE__ */ new Map();
2515
+ const root = postcss.parse(css);
2516
+ root.walkAtRules("layer", (atRule) => {
2517
+ if (atRule.params !== "theme") {
2518
+ return;
2519
+ }
2520
+ atRule.walkRules(":root, :host", (rule) => {
2521
+ rule.walkDecls((decl) => {
2522
+ if (decl.prop.startsWith("--")) {
2523
+ variables.set(decl.prop, decl.value);
2524
+ }
2525
+ });
2526
+ });
2527
+ });
2528
+ return variables;
2529
+ }
2530
+ function evaluateCalc(expression) {
2531
+ const expr = expression.trim();
2532
+ const withUnitFirst = expr.match(
2533
+ /^([\d.]+)(rem|px|em|%|vh|vw)\s*([*/+-])\s*([\d.]+)$/
2534
+ );
2535
+ if (withUnitFirst) {
2536
+ const [, num1, unit, op, num2] = withUnitFirst;
2537
+ const result = evaluateOperation(parseFloat(num1), op, parseFloat(num2));
2538
+ if (!Number.isFinite(result)) {
2539
+ return null;
2540
+ }
2541
+ return formatNumber(result) + unit;
2542
+ }
2543
+ const withUnitSecond = expr.match(
2544
+ /^([\d.]+)\s*([*/+-])\s*([\d.]+)(rem|px|em|%|vh|vw)$/
2545
+ );
2546
+ if (withUnitSecond) {
2547
+ const [, num1, op, num2, unit] = withUnitSecond;
2548
+ const result = evaluateOperation(parseFloat(num1), op, parseFloat(num2));
2549
+ if (!Number.isFinite(result)) {
2550
+ return null;
2551
+ }
2552
+ return formatNumber(result) + unit;
2553
+ }
2554
+ const unitless = expr.match(/^([\d.]+)\s*([*/+-])\s*([\d.]+)$/);
2555
+ if (unitless) {
2556
+ const [, num1, op, num2] = unitless;
2557
+ const result = evaluateOperation(parseFloat(num1), op, parseFloat(num2));
2558
+ if (!Number.isFinite(result)) {
2559
+ return null;
2560
+ }
2561
+ return formatNumber(result);
2562
+ }
2563
+ return null;
2564
+ }
2565
+ function evaluateOperation(a, op, b) {
2566
+ switch (op) {
2567
+ case "*":
2568
+ return a * b;
2569
+ case "/":
2570
+ return a / b;
2571
+ case "+":
2572
+ return a + b;
2573
+ case "-":
2574
+ return a - b;
2575
+ default:
2576
+ return NaN;
2577
+ }
2578
+ }
2579
+ function formatNumber(num) {
2580
+ const rounded = Math.round(num * 1e4) / 1e4;
2581
+ return String(rounded);
2582
+ }
2583
+ function remToPx(value) {
2584
+ return value.replace(/([\d.]+)rem/g, (_, num) => {
2585
+ const px = parseFloat(num) * 16;
2586
+ return `${formatNumber(px)}px`;
2587
+ });
2588
+ }
2589
+ function findVarEnd(str, start) {
2590
+ let depth = 0;
2591
+ for (let i = start; i < str.length; i++) {
2592
+ if (str[i] === "(") {
2593
+ depth++;
2594
+ } else if (str[i] === ")") {
2595
+ depth--;
2596
+ if (depth === 0) {
2597
+ return i;
2598
+ }
2599
+ }
2600
+ }
2601
+ return -1;
2602
+ }
2603
+ function parseVar(str, startIndex) {
2604
+ const varStart = str.indexOf("var(", startIndex);
2605
+ if (varStart === -1) {
2606
+ return null;
2607
+ }
2608
+ const contentStart = varStart + 4;
2609
+ const end = findVarEnd(str, varStart);
2610
+ if (end === -1) {
2611
+ return null;
2612
+ }
2613
+ const content = str.slice(contentStart, end);
2614
+ let commaIndex = -1;
2615
+ let depth = 0;
2616
+ for (let i = 0; i < content.length; i++) {
2617
+ if (content[i] === "(") {
2618
+ depth++;
2619
+ } else if (content[i] === ")") {
2620
+ depth--;
2621
+ } else if (content[i] === "," && depth === 0) {
2622
+ commaIndex = i;
2623
+ break;
2624
+ }
2625
+ }
2626
+ if (commaIndex === -1) {
2627
+ return {
2628
+ end,
2629
+ fallback: null,
2630
+ varName: content.trim()
2631
+ };
2632
+ }
2633
+ return {
2634
+ end,
2635
+ fallback: content.slice(commaIndex + 1).trim(),
2636
+ varName: content.slice(0, commaIndex).trim()
2637
+ };
2638
+ }
2639
+ function resolveCssValue(value, variables, depth = 0) {
2640
+ if (depth > 10) {
2641
+ return value;
2642
+ }
2643
+ let resolved = value;
2644
+ let searchStart = 0;
2645
+ while (true) {
2646
+ const parsed = parseVar(resolved, searchStart);
2647
+ if (!parsed) {
2648
+ break;
2649
+ }
2650
+ const { end, fallback, varName } = parsed;
2651
+ const varStart = resolved.indexOf("var(", searchStart);
2652
+ let replacement;
2653
+ const varValue = variables.get(varName);
2654
+ if (varValue !== void 0) {
2655
+ replacement = resolveCssValue(varValue, variables, depth + 1);
2656
+ } else if (fallback) {
2657
+ replacement = resolveCssValue(fallback, variables, depth + 1);
2658
+ } else {
2659
+ searchStart = end + 1;
2660
+ continue;
2661
+ }
2662
+ resolved = resolved.slice(0, varStart) + replacement + resolved.slice(end + 1);
2663
+ }
2664
+ resolved = resolved.replace(
2665
+ /calc\(([^)]+)\)/g,
2666
+ (match, expression) => {
2667
+ const evaluated = evaluateCalc(expression);
2668
+ return evaluated ?? match;
2669
+ }
2670
+ );
2671
+ resolved = remToPx(resolved);
2672
+ return resolved;
2673
+ }
2674
+ function analyzeSelector(selector) {
2675
+ let inlineable = true;
2676
+ let className = null;
2677
+ const processor = selectorParser((selectors) => {
2678
+ if (selectors.nodes.length !== 1) {
2679
+ inlineable = false;
2680
+ return;
2681
+ }
2682
+ const selectorNode = selectors.nodes[0];
2683
+ if (selectorNode.nodes.length !== 1) {
2684
+ inlineable = false;
2685
+ return;
2686
+ }
2687
+ const node = selectorNode.nodes[0];
2688
+ if (node.type !== "class") {
2689
+ inlineable = false;
2690
+ return;
2691
+ }
2692
+ className = node.value;
2693
+ selectorNode.walk((n) => {
2694
+ if (n.type === "pseudo" || n.type === "combinator" || n.type === "nesting" || n.type === "attribute") {
2695
+ inlineable = false;
2696
+ }
2697
+ });
2698
+ });
2699
+ processor.processSync(selector);
2700
+ return { className, inlineable };
2701
+ }
2702
+ function parseCompiledCss(css) {
2703
+ const rules = [];
2704
+ const root = postcss.parse(css);
2705
+ const variables = extractCssVariables(css);
2706
+ function processRule(rule, insideAtRule) {
2707
+ const { className, inlineable } = analyzeSelector(rule.selector);
2708
+ if (!className) {
2709
+ return;
2710
+ }
2711
+ let hasNestedAtRule = false;
2712
+ rule.walkAtRules(() => {
2713
+ hasNestedAtRule = true;
2714
+ });
2715
+ const declarations = [];
2716
+ rule.each((node) => {
2717
+ if (node.type === "decl") {
2718
+ const resolvedValue = resolveCssValue(node.value, variables);
2719
+ declarations.push(`${node.prop}: ${resolvedValue}`);
2720
+ }
2721
+ });
2722
+ if (declarations.length === 0 && !hasNestedAtRule) {
2723
+ return;
2724
+ }
2725
+ rules.push({
2726
+ className,
2727
+ declarations: declarations.join("; "),
2728
+ inlineable: inlineable && !insideAtRule && !hasNestedAtRule,
2729
+ originalRule: rule.toString()
2730
+ });
2731
+ }
2732
+ root.walkAtRules("layer", (atRule) => {
2733
+ atRule.walkRules((rule) => {
2734
+ const parent = rule.parent;
2735
+ const insideNestedAtRule = parent?.type === "atrule" && parent.name !== "layer";
2736
+ processRule(rule, insideNestedAtRule);
2737
+ });
2738
+ atRule.walkAtRules((nested) => {
2739
+ if (nested.name !== "layer") {
2740
+ nested.walkRules((rule) => {
2741
+ processRule(rule, true);
2742
+ });
2743
+ }
2744
+ });
2745
+ });
2746
+ root.walkRules((rule) => {
2747
+ if (rule.parent?.type === "root") {
2748
+ processRule(rule, false);
2749
+ }
2750
+ });
2751
+ return rules;
2752
+ }
2753
+ function compileClasses(classes, compiler) {
2754
+ const compiledCss = compiler.build(classes);
2755
+ const parsedRules = parseCompiledCss(compiledCss);
2756
+ const inlineableStyles = /* @__PURE__ */ new Map();
2757
+ const residualRules = /* @__PURE__ */ new Map();
2758
+ for (const rule of parsedRules) {
2759
+ if (rule.inlineable) {
2760
+ inlineableStyles.set(rule.className, rule.declarations);
2761
+ } else {
2762
+ residualRules.set(rule.className, rule.originalRule);
2763
+ }
2764
+ }
2765
+ const inlineStyles = [];
2766
+ const remainingClasses = [];
2767
+ for (const cls of classes) {
2768
+ const style = inlineableStyles.get(cls);
2769
+ if (style) {
2770
+ inlineStyles.push(style);
2771
+ } else {
2772
+ remainingClasses.push(cls);
2773
+ }
2774
+ }
2775
+ return { inlineStyles, remainingClasses, residualRules };
2776
+ }
2777
+ function cssToJsxObject(cssDeclarations) {
2778
+ const props = cssDeclarations.flatMap((decl) => {
2779
+ return decl.split(";").map((d) => d.trim()).filter(Boolean);
2780
+ }).map((declaration) => {
2781
+ const colonIndex = declaration.indexOf(":");
2782
+ if (colonIndex === -1) {
2783
+ return null;
2784
+ }
2785
+ const prop = declaration.slice(0, colonIndex).trim();
2786
+ const value = declaration.slice(colonIndex + 1).trim();
2787
+ const camelProp = camelCase(prop);
2788
+ return `${camelProp}: '${value}'`;
2789
+ }).filter(Boolean);
2790
+ return `{ ${props.join(", ")} }`;
2791
+ }
2792
+ function computeLineReplacements(lineText, compiler, styleFormat = "html") {
2793
+ const replacements = /* @__PURE__ */ new Map();
2794
+ const allResidualRules = /* @__PURE__ */ new Map();
2795
+ const classAttrPattern = /(className|class)=(["'`])([^"'`]*)\2/g;
2796
+ let match;
2797
+ while ((match = classAttrPattern.exec(lineText)) !== null) {
2798
+ const fullMatch = match[0];
2799
+ const attrName = match[1];
2800
+ const quote = match[2];
2801
+ const classValue = match[3];
2802
+ const classes = classValue.split(/\s+/).filter(Boolean);
2803
+ if (classes.length === 0) {
2804
+ continue;
2805
+ }
2806
+ const { inlineStyles, remainingClasses, residualRules } = compileClasses(
2807
+ classes,
2808
+ compiler
2809
+ );
2810
+ for (const [className, rule] of residualRules) {
2811
+ allResidualRules.set(className, rule);
2812
+ }
2813
+ if (inlineStyles.length === 0) {
2814
+ continue;
2815
+ }
2816
+ let replacement;
2817
+ if (styleFormat === "jsx") {
2818
+ const jsxStyleObj = cssToJsxObject(inlineStyles);
2819
+ replacement = `style={${jsxStyleObj}}`;
2820
+ } else {
2821
+ replacement = `style=${quote}${inlineStyles.join("; ")}${quote}`;
2822
+ }
2823
+ if (remainingClasses.length > 0) {
2824
+ replacement += ` ${attrName}=${quote}${remainingClasses.join(" ")}${quote}`;
2825
+ }
2826
+ replacements.set(fullMatch, replacement);
2827
+ }
2828
+ return { replacements, residualRules: allResidualRules };
2829
+ }
2830
+ function transformSourceCode(source, compiler, styleFormat = "html") {
2831
+ const allResidualRules = /* @__PURE__ */ new Map();
2832
+ let classesDetected = false;
2833
+ const { replacements, residualRules } = computeLineReplacements(
2834
+ source,
2835
+ compiler,
2836
+ styleFormat
2837
+ );
2838
+ if (replacements.size > 0 || residualRules.size > 0) {
2839
+ classesDetected = true;
2840
+ }
2841
+ for (const [cls, rule] of residualRules) {
2842
+ allResidualRules.set(cls, rule);
2843
+ }
2844
+ let transformed = source;
2845
+ for (const [original, replacement] of replacements) {
2846
+ transformed = transformed.replaceAll(original, replacement);
2847
+ }
2848
+ return {
2849
+ classesDetected,
2850
+ residualRules: allResidualRules,
2851
+ source: transformed
2852
+ };
2853
+ }
2854
+ async function createShikiTailwindTransformer(options) {
2855
+ const {
2856
+ onClassesDetected,
2857
+ onResidualCss,
2858
+ styleFormat = "html",
2859
+ styles
2860
+ } = options;
2861
+ const compiler = await createCompiler(styles);
2862
+ return {
2863
+ name: "shiki-transformer-tailwind-to-inline",
2864
+ preprocess(code) {
2865
+ const { classesDetected, residualRules, source } = transformSourceCode(
2866
+ code,
2867
+ compiler,
2868
+ styleFormat
2869
+ );
2870
+ onClassesDetected?.(classesDetected);
2871
+ if (onResidualCss && residualRules.size > 0) {
2872
+ onResidualCss(residualRules);
2873
+ }
2874
+ return source;
2875
+ }
2876
+ };
2877
+ }
2878
+
2415
2879
  // src/angular-demo-plugin/angular-demo-plugin.ts
2416
2880
  var VIRTUAL_MODULE_ID2 = "\0virtual:angular-demo-registry";
2417
2881
  var LOG_PREFIX = "[angular-demo]";
@@ -2434,7 +2898,8 @@ function angularDemoPlugin({
2434
2898
  theme = {
2435
2899
  dark: quiCustomDarkTheme2,
2436
2900
  light: "github-light-high-contrast"
2437
- }
2901
+ },
2902
+ transformTailwindStyles
2438
2903
  } = {}) {
2439
2904
  let watcher = null;
2440
2905
  let devServer = null;
@@ -2461,7 +2926,7 @@ function angularDemoPlugin({
2461
2926
  if (!highlighter) {
2462
2927
  try {
2463
2928
  highlighter = await createHighlighter({
2464
- langs: ["angular-ts", "angular-html"],
2929
+ langs: ["angular-ts", "angular-html", "css"],
2465
2930
  themes: [theme.dark, theme.light]
2466
2931
  });
2467
2932
  logDev(`${chalk4.blue.bold(LOG_PREFIX)} Shiki highlighter initialized`);
@@ -2539,7 +3004,7 @@ function angularDemoPlugin({
2539
3004
  logDev(
2540
3005
  `${chalk4.blue.bold(LOG_PREFIX)} Processing Angular demo change: ${chalk4.cyan(file)}`
2541
3006
  );
2542
- const code = await readFile(file, "utf-8");
3007
+ const code = await readFile2(file, "utf-8");
2543
3008
  const demoInfo = await parseAngularDemo(file, code);
2544
3009
  if (!demoInfo || !isAngularDemoEntrypoint(file)) {
2545
3010
  const affectedDemos = await scanDemosForFileImport(file);
@@ -2605,7 +3070,7 @@ function angularDemoPlugin({
2605
3070
  const demoFiles = await glob2(demoPattern);
2606
3071
  demoRegistry.clear();
2607
3072
  for (const filePath of demoFiles) {
2608
- const code = await readFile(filePath, "utf-8");
3073
+ const code = await readFile2(filePath, "utf-8");
2609
3074
  const demoInfo = await parseAngularDemo(filePath, code);
2610
3075
  if (demoInfo) {
2611
3076
  demoRegistry.set(demoInfo.id, demoInfo);
@@ -2619,7 +3084,7 @@ function angularDemoPlugin({
2619
3084
  logDev(
2620
3085
  `${chalk4.blue.bold(LOG_PREFIX)} Reloading demo ${chalk4.cyan(demoId)} due to imported file change: ${chalk4.yellow(file)}`
2621
3086
  );
2622
- const code = await readFile(demo.filePath, "utf-8");
3087
+ const code = await readFile2(demo.filePath, "utf-8");
2623
3088
  const updatedDemo = await parseAngularDemo(demo.filePath, code);
2624
3089
  if (updatedDemo) {
2625
3090
  delete demoDimensionsCache[updatedDemo.id];
@@ -2631,29 +3096,44 @@ function angularDemoPlugin({
2631
3096
  }
2632
3097
  return affectedDemos;
2633
3098
  }
2634
- async function highlightCode(code, language = "angular-ts") {
3099
+ async function highlightCode(code, language = "angular-ts", options = {}) {
3100
+ const { onClassesDetected, onResidualCss } = options;
2635
3101
  if (!highlighter) {
2636
- return { codeWithoutSnippets: code, highlightedCode: code };
3102
+ return { full: code };
2637
3103
  }
2638
3104
  let previewCode = null;
2639
- let codeWithoutSnippets = "";
3105
+ const tailwindTransformers = [];
3106
+ if (transformTailwindStyles && onResidualCss) {
3107
+ const transformer = await createShikiTailwindTransformer({
3108
+ onClassesDetected: (detected) => {
3109
+ onClassesDetected?.(detected);
3110
+ },
3111
+ onResidualCss,
3112
+ styleFormat: "html",
3113
+ styles: dedent2`
3114
+ @layer theme, base, components, utilities;
3115
+ @import "tailwindcss/theme.css" layer(theme);
3116
+ @import "tailwindcss/utilities.css" layer(utilities);
3117
+ @import "@qualcomm-ui/tailwind-plugin/qui-strict.css";
3118
+ `
3119
+ });
3120
+ tailwindTransformers.push(transformer);
3121
+ }
2640
3122
  try {
2641
3123
  const highlightedCode = highlighter.codeToHtml(code, {
2642
3124
  ...defaultShikiOptions,
2643
3125
  lang: language,
2644
3126
  transformers: [
2645
3127
  ...getShikiTransformers(),
3128
+ ...tailwindTransformers,
2646
3129
  transformerPreviewBlock({
2647
- attributeName: null,
3130
+ attributeName: "data-preview",
2648
3131
  onComplete: (extractedPreview) => {
2649
- previewCode = extractedPreview ? removeCodeAnnotations(extractedPreview) : null;
3132
+ previewCode = extractedPreview;
2650
3133
  }
2651
3134
  }),
2652
3135
  transformerCodeAttribute({
2653
- attributeName: null,
2654
- onComplete: (formattedSource) => {
2655
- codeWithoutSnippets = formattedSource;
2656
- }
3136
+ attributeName: "data-code"
2657
3137
  }),
2658
3138
  {
2659
3139
  enforce: "post",
@@ -2665,22 +3145,20 @@ function angularDemoPlugin({
2665
3145
  ]
2666
3146
  });
2667
3147
  return {
2668
- codeWithoutSnippets,
2669
- highlightedCode,
2670
- highlightedPreview: previewCode ? extractPreviewFromHighlightedHtml(highlightedCode) : null,
2671
- previewCodeWithoutSnippets: previewCode
3148
+ full: highlightedCode,
3149
+ preview: previewCode ? extractPreviewFromHighlightedHtml(highlightedCode) : null
2672
3150
  };
2673
3151
  } catch (error) {
2674
3152
  console.warn(
2675
3153
  `${chalk4.blue.bold(LOG_PREFIX)} Failed to highlight code with ${language} language:`,
2676
3154
  error
2677
3155
  );
2678
- return { codeWithoutSnippets: code, highlightedCode: code };
3156
+ return { full: code };
2679
3157
  }
2680
3158
  }
2681
3159
  async function extractRelativeImports(filePath) {
2682
3160
  try {
2683
- let visit8 = function(node) {
3161
+ let visit9 = function(node) {
2684
3162
  if (ts.isImportDeclaration(node)) {
2685
3163
  const moduleSpecifier = node.moduleSpecifier;
2686
3164
  if (ts.isStringLiteral(moduleSpecifier)) {
@@ -2699,10 +3177,10 @@ function angularDemoPlugin({
2699
3177
  }
2700
3178
  }
2701
3179
  }
2702
- ts.forEachChild(node, visit8);
3180
+ ts.forEachChild(node, visit9);
2703
3181
  };
2704
- var visit7 = visit8;
2705
- const content = await readFile(filePath, "utf-8");
3182
+ var visit8 = visit9;
3183
+ const content = await readFile2(filePath, "utf-8");
2706
3184
  const sourceFile = ts.createSourceFile(
2707
3185
  filePath,
2708
3186
  content,
@@ -2711,7 +3189,7 @@ function angularDemoPlugin({
2711
3189
  ts.ScriptKind.TS
2712
3190
  );
2713
3191
  const relativeImports = [];
2714
- visit8(sourceFile);
3192
+ visit9(sourceFile);
2715
3193
  return relativeImports;
2716
3194
  } catch (error) {
2717
3195
  logDev(
@@ -2734,16 +3212,16 @@ function angularDemoPlugin({
2734
3212
  }
2735
3213
  function stripImports(code, fileName) {
2736
3214
  try {
2737
- let visit8 = function(node) {
3215
+ let visit9 = function(node) {
2738
3216
  if (ts.isImportDeclaration(node)) {
2739
3217
  importRanges.push({
2740
3218
  end: node.getEnd(),
2741
3219
  start: node.getFullStart()
2742
3220
  });
2743
3221
  }
2744
- ts.forEachChild(node, visit8);
3222
+ ts.forEachChild(node, visit9);
2745
3223
  };
2746
- var visit7 = visit8;
3224
+ var visit8 = visit9;
2747
3225
  const sourceFile = ts.createSourceFile(
2748
3226
  fileName,
2749
3227
  code,
@@ -2752,7 +3230,7 @@ function angularDemoPlugin({
2752
3230
  ts.ScriptKind.TS
2753
3231
  );
2754
3232
  const importRanges = [];
2755
- visit8(sourceFile);
3233
+ visit9(sourceFile);
2756
3234
  return importRanges.map((range) => {
2757
3235
  let endPos = range.end;
2758
3236
  if (code[endPos] === "\n") {
@@ -2785,24 +3263,51 @@ function angularDemoPlugin({
2785
3263
  const fileName = basename(filePath);
2786
3264
  const importsWithoutStrip = stripImports(code, filePath);
2787
3265
  const sourceCode = [];
3266
+ const aggregatedRules = /* @__PURE__ */ new Map();
2788
3267
  const mainSourceEntry = await buildAngularSourceEntry({
2789
3268
  code,
2790
3269
  fileName,
2791
3270
  filePath,
2792
3271
  language: "angular-ts"
2793
3272
  });
2794
- sourceCode.push(mainSourceEntry);
3273
+ sourceCode.push(mainSourceEntry.sourceCodeData);
3274
+ if (mainSourceEntry.residualRules) {
3275
+ for (const [className, rule] of mainSourceEntry.residualRules) {
3276
+ aggregatedRules.set(className, rule);
3277
+ }
3278
+ }
2795
3279
  if (templateUrl) {
2796
3280
  const templateEntry = await maybeBuildTemplateSourceEntry(
2797
3281
  templateUrl,
2798
3282
  filePath
2799
3283
  );
2800
3284
  if (templateEntry) {
2801
- sourceCode.push(templateEntry);
3285
+ sourceCode.push(templateEntry.sourceCodeData);
3286
+ if (templateEntry.residualRules) {
3287
+ for (const [className, rule] of templateEntry.residualRules) {
3288
+ aggregatedRules.set(className, rule);
3289
+ }
3290
+ }
2802
3291
  }
2803
3292
  }
2804
3293
  const importedEntries = await buildImportedSourceEntries(filePath);
2805
- sourceCode.push(...importedEntries);
3294
+ for (const entry of importedEntries) {
3295
+ sourceCode.push(entry.sourceCodeData);
3296
+ if (entry.residualRules) {
3297
+ for (const [className, rule] of entry.residualRules) {
3298
+ aggregatedRules.set(className, rule);
3299
+ }
3300
+ }
3301
+ }
3302
+ const aggregatedResidualCss = aggregatedRules.size > 0 ? [...aggregatedRules.values()].join("\n\n") : void 0;
3303
+ if (aggregatedResidualCss) {
3304
+ const cssHighlighted = await highlightCode(aggregatedResidualCss, "css");
3305
+ sourceCode.push({
3306
+ fileName: "styles.css",
3307
+ highlighted: cssHighlighted,
3308
+ type: "residual-css"
3309
+ });
3310
+ }
2806
3311
  return {
2807
3312
  componentClass,
2808
3313
  filePath: importPath.startsWith(".") ? importPath : `./${importPath}`,
@@ -2837,7 +3342,7 @@ function angularDemoPlugin({
2837
3342
  let templateUrl = null;
2838
3343
  let hasDefaultExport = false;
2839
3344
  const importsFromAst = [];
2840
- function visit7(node) {
3345
+ function visit8(node) {
2841
3346
  if (ts.isImportDeclaration(node)) {
2842
3347
  importsFromAst.push(node.getFullText(sourceFile).trim());
2843
3348
  }
@@ -2885,9 +3390,9 @@ function angularDemoPlugin({
2885
3390
  if (ts.isExportAssignment(node) && !node.isExportEquals) {
2886
3391
  hasDefaultExport = true;
2887
3392
  }
2888
- ts.forEachChild(node, visit7);
3393
+ ts.forEachChild(node, visit8);
2889
3394
  }
2890
- visit7(sourceFile);
3395
+ visit8(sourceFile);
2891
3396
  return {
2892
3397
  componentClass,
2893
3398
  hasDefaultExport,
@@ -2899,24 +3404,34 @@ function angularDemoPlugin({
2899
3404
  }
2900
3405
  async function buildAngularSourceEntry(params) {
2901
3406
  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);
3407
+ const baseResult = await highlightCode(code, language);
3408
+ let inlineResult;
3409
+ let classesDetected = false;
3410
+ let residualRules;
3411
+ if (transformTailwindStyles) {
3412
+ inlineResult = await highlightCode(code, language, {
3413
+ onClassesDetected: (detected) => {
3414
+ classesDetected = detected;
3415
+ },
3416
+ onResidualCss: (rules) => {
3417
+ residualRules = rules;
3418
+ }
3419
+ });
3420
+ }
2910
3421
  return {
2911
- fileName,
2912
- filePath,
2913
- highlighted: {
2914
- full: highlightedCode,
2915
- preview: highlightedPreview ?? null
2916
- },
2917
- raw: {
2918
- full: codeWithoutSnippets,
2919
- preview: previewCodeWithoutSnippets ?? null
3422
+ residualRules,
3423
+ sourceCodeData: {
3424
+ fileName,
3425
+ filePath,
3426
+ highlighted: {
3427
+ full: baseResult.full,
3428
+ preview: baseResult.preview
3429
+ },
3430
+ highlightedInline: classesDetected && inlineResult ? {
3431
+ full: inlineResult.full,
3432
+ preview: inlineResult.preview
3433
+ } : void 0,
3434
+ type: "file"
2920
3435
  }
2921
3436
  };
2922
3437
  }
@@ -2926,7 +3441,7 @@ function angularDemoPlugin({
2926
3441
  return null;
2927
3442
  }
2928
3443
  try {
2929
- const templateCode = await readFile(templatePath, "utf-8");
3444
+ const templateCode = await readFile2(templatePath, "utf-8");
2930
3445
  return buildAngularSourceEntry({
2931
3446
  code: templateCode,
2932
3447
  fileName: basename(templatePath),
@@ -2942,25 +3457,25 @@ function angularDemoPlugin({
2942
3457
  }
2943
3458
  }
2944
3459
  async function buildImportedSourceEntries(fromFilePath) {
2945
- const sourceCode = [];
3460
+ const entries = [];
2946
3461
  const relativeImports = await collectAllImports(fromFilePath);
2947
3462
  for (const resolvedPath of relativeImports) {
2948
3463
  try {
2949
- const importedCode = await readFile(resolvedPath, "utf-8");
3464
+ const importedCode = await readFile2(resolvedPath, "utf-8");
2950
3465
  const entry = await buildAngularSourceEntry({
2951
3466
  code: importedCode,
2952
3467
  fileName: basename(resolvedPath),
2953
3468
  filePath: resolvedPath,
2954
3469
  language: "angular-ts"
2955
3470
  });
2956
- sourceCode.push(entry);
3471
+ entries.push(entry);
2957
3472
  } catch (error) {
2958
3473
  logDev(
2959
3474
  `${chalk4.blue.bold(LOG_PREFIX)} ${chalk4.yellowBright("Failed to process relative import:")} ${chalk4.cyan(resolvedPath)}`
2960
3475
  );
2961
3476
  }
2962
3477
  }
2963
- return sourceCode;
3478
+ return entries;
2964
3479
  }
2965
3480
  function generateRegistryModule() {
2966
3481
  const demos = Array.from(demoRegistry.values());
@@ -3039,7 +3554,7 @@ export function getAngularDemoInfo(demoId) {
3039
3554
  function resolveRelativeImport2(source, fromFile) {
3040
3555
  const fromDir = dirname(fromFile);
3041
3556
  const resolved = resolve2(fromDir, source);
3042
- const extensions = [".ts", ".tsx", ".js", ".jsx"];
3557
+ const extensions = [".ts", ".js"];
3043
3558
  for (const ext of extensions) {
3044
3559
  const withExt = resolved + ext;
3045
3560
  if (existsSync(withExt)) {
@@ -3149,7 +3664,7 @@ export function getAngularDemoInfo(demoId) {
3149
3664
  });
3150
3665
  }
3151
3666
  async function handleAngularDemoUpdate(filePath) {
3152
- const code = await readFile(filePath, "utf-8");
3667
+ const code = await readFile2(filePath, "utf-8");
3153
3668
  const demoInfo = await parseAngularDemo(filePath, code);
3154
3669
  if (demoInfo) {
3155
3670
  demoRegistry.set(demoInfo.id, demoInfo);
@@ -3223,7 +3738,7 @@ function resolvePathAlias(source, pathAliases) {
3223
3738
  for (const alias of pathAliases) {
3224
3739
  if (alias.pattern.test(source)) {
3225
3740
  const resolvedPath = source.replace(alias.pattern, alias.replacement);
3226
- const extensions = [".ts", ".tsx", ".js", ".jsx"];
3741
+ const extensions = [".ts", ".js"];
3227
3742
  for (const ext of extensions) {
3228
3743
  const withExt = resolvedPath + ext;
3229
3744
  if (existsSync(withExt)) {
@@ -3255,55 +3770,6 @@ function resolveTemplateFile(templateUrl, fromFile) {
3255
3770
  }
3256
3771
  return resolved;
3257
3772
  }
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
3773
 
3308
3774
  // src/react-demo-plugin/demo-plugin-constants.ts
3309
3775
  var VIRTUAL_MODULE_IDS = {
@@ -3341,7 +3807,7 @@ var NODE_BUILTINS = [
3341
3807
  // src/react-demo-plugin/demo-plugin-utils.ts
3342
3808
  import chalk5 from "chalk";
3343
3809
  import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
3344
- import { readFile as readFile2 } from "node:fs/promises";
3810
+ import { readFile as readFile3 } from "node:fs/promises";
3345
3811
  import { dirname as dirname2, join as join3, relative as relative2, resolve as resolve3, sep } from "node:path";
3346
3812
  import * as ts2 from "typescript";
3347
3813
  import { pascalCase } from "@qualcomm-ui/utils/change-case";
@@ -3358,7 +3824,7 @@ function createDemoName(filePath) {
3358
3824
  }
3359
3825
  async function extractFileImports(filePath) {
3360
3826
  try {
3361
- const content = await readFile2(filePath, "utf-8");
3827
+ const content = await readFile3(filePath, "utf-8");
3362
3828
  return extractImports(content, filePath);
3363
3829
  } catch (error) {
3364
3830
  console.log(
@@ -3378,7 +3844,7 @@ function extractImports(code, fileName) {
3378
3844
  );
3379
3845
  const thirdPartyImports = [];
3380
3846
  const relativeImports = [];
3381
- function visit7(node) {
3847
+ function visit8(node) {
3382
3848
  if (ts2.isImportDeclaration(node)) {
3383
3849
  const importSpec = parseImportDeclaration(node, fileName);
3384
3850
  if (importSpec) {
@@ -3389,9 +3855,9 @@ function extractImports(code, fileName) {
3389
3855
  }
3390
3856
  }
3391
3857
  }
3392
- ts2.forEachChild(node, visit7);
3858
+ ts2.forEachChild(node, visit8);
3393
3859
  }
3394
- visit7(sourceFile);
3860
+ visit8(sourceFile);
3395
3861
  return { relativeImports, thirdPartyImports };
3396
3862
  }
3397
3863
  function getScriptKind(fileName) {
@@ -3638,14 +4104,14 @@ function isDemoFile(filePath) {
3638
4104
  // src/react-demo-plugin/react-demo-plugin.ts
3639
4105
  import chalk6 from "chalk";
3640
4106
  import { glob as glob3 } from "glob";
3641
- import { readFile as readFile3 } from "node:fs/promises";
4107
+ import { readFile as readFile4 } from "node:fs/promises";
3642
4108
  import { basename as basename2, resolve as resolve4 } from "node:path";
3643
4109
  import { createHighlighter as createHighlighter2 } from "shiki";
3644
4110
  import * as ts3 from "typescript";
3645
4111
  import {
3646
4112
  quiCustomDarkTheme as quiCustomDarkTheme3
3647
4113
  } from "@qualcomm-ui/mdx-common";
3648
- import { dedent as dedent2 } from "@qualcomm-ui/utils/dedent";
4114
+ import { dedent as dedent3 } from "@qualcomm-ui/utils/dedent";
3649
4115
  var highlighter2 = null;
3650
4116
  var initializingHighlighter = false;
3651
4117
  var demoRegistry2 = /* @__PURE__ */ new Map();
@@ -3659,7 +4125,8 @@ function reactDemoPlugin({
3659
4125
  light: "github-light-high-contrast"
3660
4126
  },
3661
4127
  transformers = [],
3662
- transformLine
4128
+ transformLine,
4129
+ transformTailwindStyles
3663
4130
  } = {}) {
3664
4131
  const defaultShikiOptions = {
3665
4132
  defaultColor: "light-dark()",
@@ -3678,10 +4145,8 @@ function reactDemoPlugin({
3678
4145
  initializingHighlighter = true;
3679
4146
  try {
3680
4147
  highlighter2 = await createHighlighter2({
3681
- langs: ["tsx", "typescript"],
4148
+ langs: ["tsx", "typescript", "css"],
3682
4149
  themes: [theme.dark, theme.light]
3683
- }).finally(() => {
3684
- initializingHighlighter = false;
3685
4150
  });
3686
4151
  console.log(
3687
4152
  `${chalk6.magenta.bold(LOG_PREFIX2)} Shiki highlighter initialized`
@@ -3691,6 +4156,8 @@ function reactDemoPlugin({
3691
4156
  `${chalk6.magenta.bold(LOG_PREFIX2)} Failed to initialize highlighter:`,
3692
4157
  error
3693
4158
  );
4159
+ } finally {
4160
+ initializingHighlighter = false;
3694
4161
  }
3695
4162
  }
3696
4163
  await collectReactDemos();
@@ -3772,26 +4239,46 @@ function reactDemoPlugin({
3772
4239
  }
3773
4240
  }
3774
4241
  }
3775
- async function highlightCode(code) {
4242
+ async function highlightCode(code, options = {}) {
4243
+ const { extraTransformers = [], onResidualCss } = options;
3776
4244
  if (!highlighter2) {
3777
- return { codeWithoutSnippets: code, highlightedCode: code };
4245
+ return { full: code };
3778
4246
  }
3779
4247
  let previewCode = null;
3780
- let codeWithoutSnippets = "";
4248
+ const tailwindTransformers = [];
4249
+ if (transformTailwindStyles && onResidualCss) {
4250
+ const transformer = await createShikiTailwindTransformer({
4251
+ onClassesDetected: (detected) => {
4252
+ options.onClassesDetected?.(detected);
4253
+ },
4254
+ onResidualCss,
4255
+ styleFormat: "jsx",
4256
+ styles: dedent3`
4257
+ @layer theme, base, components, utilities;
4258
+ @import "tailwindcss/theme.css" layer(theme);
4259
+ @import "tailwindcss/utilities.css" layer(utilities);
4260
+ @import "@qualcomm-ui/tailwind-plugin/qui-strict.css";
4261
+ `
4262
+ });
4263
+ tailwindTransformers.push(transformer);
4264
+ } else if (extraTransformers.length > 0) {
4265
+ tailwindTransformers.push(...extraTransformers);
4266
+ }
3781
4267
  try {
3782
- const result = highlighter2.codeToHtml(code, {
4268
+ const highlightedCode = highlighter2.codeToHtml(code, {
3783
4269
  ...defaultShikiOptions,
3784
4270
  transformers: [
3785
4271
  ...getShikiTransformers(),
4272
+ ...transformers,
4273
+ ...tailwindTransformers,
3786
4274
  transformerPreviewBlock({
4275
+ attributeName: "data-preview",
3787
4276
  onComplete: (extractedPreview) => {
3788
- previewCode = extractedPreview ? removeCodeAnnotations(extractedPreview) : null;
4277
+ previewCode = extractedPreview;
3789
4278
  }
3790
4279
  }),
3791
4280
  transformerCodeAttribute({
3792
- onComplete: (formattedSource) => {
3793
- codeWithoutSnippets = formattedSource;
3794
- }
4281
+ attributeName: "data-code"
3795
4282
  }),
3796
4283
  {
3797
4284
  enforce: "post",
@@ -3803,38 +4290,16 @@ function reactDemoPlugin({
3803
4290
  ...transformers
3804
4291
  ]
3805
4292
  });
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
4293
  return {
3827
- codeWithoutSnippets,
3828
- highlightedCode: result,
3829
- highlightedPreview,
3830
- previewCodeWithoutSnippets: previewCode
4294
+ full: highlightedCode,
4295
+ preview: previewCode ? extractPreviewFromHighlightedHtml(highlightedCode) : null
3831
4296
  };
3832
4297
  } catch (error) {
3833
4298
  console.warn(
3834
4299
  `${chalk6.magenta.bold(LOG_PREFIX2)} Failed to highlight code:`,
3835
4300
  error
3836
4301
  );
3837
- return { codeWithoutSnippets: code, highlightedCode: code };
4302
+ return { full: code };
3838
4303
  }
3839
4304
  }
3840
4305
  async function collectReactDemos() {
@@ -3894,22 +4359,34 @@ function reactDemoPlugin({
3894
4359
  async function extractHighlightedCode(filePath, code) {
3895
4360
  try {
3896
4361
  const fileName = basename2(filePath);
3897
- const {
3898
- codeWithoutSnippets,
3899
- highlightedCode,
3900
- highlightedPreview,
3901
- previewCodeWithoutSnippets
3902
- } = await highlightCode(code);
4362
+ const baseResult = await highlightCode(code);
4363
+ let inlineResult;
4364
+ let classesDetected = false;
4365
+ let residualRules;
4366
+ if (transformTailwindStyles) {
4367
+ inlineResult = await highlightCode(code, {
4368
+ onClassesDetected: (detected) => {
4369
+ classesDetected = detected;
4370
+ },
4371
+ onResidualCss: (rules) => {
4372
+ residualRules = rules;
4373
+ }
4374
+ });
4375
+ }
3903
4376
  return {
3904
- fileName,
3905
- filePath,
3906
- highlighted: {
3907
- full: highlightedCode,
3908
- preview: highlightedPreview
3909
- },
3910
- raw: {
3911
- full: codeWithoutSnippets,
3912
- preview: previewCodeWithoutSnippets
4377
+ residualRules,
4378
+ sourceCodeData: {
4379
+ fileName,
4380
+ filePath,
4381
+ highlighted: {
4382
+ full: baseResult.full,
4383
+ preview: baseResult.preview
4384
+ },
4385
+ highlightedInline: classesDetected && inlineResult ? {
4386
+ full: inlineResult.full,
4387
+ preview: inlineResult.preview
4388
+ } : void 0,
4389
+ type: "file"
3913
4390
  }
3914
4391
  };
3915
4392
  } catch {
@@ -3918,36 +4395,55 @@ function reactDemoPlugin({
3918
4395
  }
3919
4396
  async function extractFileData(filePath) {
3920
4397
  try {
3921
- const code = await readFile3(filePath, "utf-8").then(transformLines);
4398
+ const code = await readFile4(filePath, "utf-8").then(transformLines);
3922
4399
  const imports = stripImports(code, filePath);
3923
4400
  const sourceCode = [];
3924
- const sourceCodeData = await extractHighlightedCode(filePath, code);
3925
- if (sourceCodeData) {
3926
- sourceCode.push(sourceCodeData);
4401
+ const aggregatedRules = /* @__PURE__ */ new Map();
4402
+ const extractedMain = await extractHighlightedCode(filePath, code);
4403
+ if (extractedMain) {
4404
+ sourceCode.push(extractedMain.sourceCodeData);
4405
+ if (extractedMain.residualRules) {
4406
+ for (const [className, rule] of extractedMain.residualRules) {
4407
+ aggregatedRules.set(className, rule);
4408
+ }
4409
+ }
3927
4410
  }
3928
4411
  const fileImports = await extractFileImports(filePath);
3929
4412
  if (fileImports) {
3930
4413
  for (const relativeImport of fileImports.relativeImports) {
3931
4414
  try {
3932
- const importedCode = await readFile3(
4415
+ const importedCode = await readFile4(
3933
4416
  relativeImport.resolvedPath,
3934
4417
  "utf-8"
3935
4418
  ).then(transformLines);
3936
- const sourceCodeData2 = await extractHighlightedCode(
4419
+ const extracted = await extractHighlightedCode(
3937
4420
  relativeImport.resolvedPath,
3938
4421
  importedCode
3939
4422
  );
3940
- if (sourceCodeData2) {
3941
- sourceCode.push(sourceCodeData2);
4423
+ if (extracted) {
4424
+ sourceCode.push(extracted.sourceCodeData);
4425
+ if (extracted.residualRules) {
4426
+ for (const [className, rule] of extracted.residualRules) {
4427
+ aggregatedRules.set(className, rule);
4428
+ }
4429
+ }
3942
4430
  }
3943
4431
  } catch {
3944
4432
  console.debug("Failed to process file", relativeImport.resolvedPath);
3945
4433
  }
3946
4434
  }
3947
4435
  }
4436
+ const aggregatedResidualCss = aggregatedRules.size > 0 ? [...aggregatedRules.values()].join("\n\n") : void 0;
4437
+ if (aggregatedResidualCss) {
4438
+ sourceCode.push({
4439
+ fileName: "styles.css",
4440
+ highlighted: await highlightCode(aggregatedResidualCss),
4441
+ type: "residual-css"
4442
+ });
4443
+ }
3948
4444
  return {
3949
4445
  demoName: createDemoName(filePath),
3950
- fileName: sourceCodeData?.fileName || basename2(filePath),
4446
+ fileName: extractedMain?.sourceCodeData.fileName || basename2(filePath),
3951
4447
  filePath,
3952
4448
  imports,
3953
4449
  sourceCode
@@ -3958,16 +4454,16 @@ function reactDemoPlugin({
3958
4454
  }
3959
4455
  function stripImports(code, fileName) {
3960
4456
  try {
3961
- let visit8 = function(node) {
4457
+ let visit9 = function(node) {
3962
4458
  if (ts3.isImportDeclaration(node)) {
3963
4459
  importRanges.push({
3964
4460
  end: node.getEnd(),
3965
4461
  start: node.getFullStart()
3966
4462
  });
3967
4463
  }
3968
- ts3.forEachChild(node, visit8);
4464
+ ts3.forEachChild(node, visit9);
3969
4465
  };
3970
- var visit7 = visit8;
4466
+ var visit8 = visit9;
3971
4467
  const sourceFile = ts3.createSourceFile(
3972
4468
  fileName,
3973
4469
  code,
@@ -3976,7 +4472,7 @@ function reactDemoPlugin({
3976
4472
  getScriptKind(fileName)
3977
4473
  );
3978
4474
  const importRanges = [];
3979
- visit8(sourceFile);
4475
+ visit9(sourceFile);
3980
4476
  return importRanges.map((range) => {
3981
4477
  let endPos = range.end;
3982
4478
  if (code[endPos] === "\n") {
@@ -3997,7 +4493,7 @@ ${entries}
3997
4493
  ])`;
3998
4494
  }
3999
4495
  function generateExportedFunctions() {
4000
- return dedent2`
4496
+ return dedent3`
4001
4497
  export function getDemo(demoName) {
4002
4498
  const demo = demoRegistry.get(demoName)
4003
4499
  if (!demo) {