@maizzle/framework 6.0.0-rc.3 → 6.0.0-rc.5

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 (52) hide show
  1. package/dist/build.mjs +3 -2
  2. package/dist/build.mjs.map +1 -1
  3. package/dist/components/Button.vue +65 -14
  4. package/dist/components/CodeBlock.vue +75 -0
  5. package/dist/components/CodeInline.vue +44 -0
  6. package/dist/components/Preview.vue +20 -0
  7. package/dist/composables/renderContext.d.mts +5 -0
  8. package/dist/composables/renderContext.d.mts.map +1 -1
  9. package/dist/composables/renderContext.mjs.map +1 -1
  10. package/dist/composables/usePreviewText.d.mts +24 -0
  11. package/dist/composables/usePreviewText.d.mts.map +1 -0
  12. package/dist/composables/usePreviewText.mjs +29 -0
  13. package/dist/composables/usePreviewText.mjs.map +1 -0
  14. package/dist/index.d.mts +3 -2
  15. package/dist/index.mjs +2 -1
  16. package/dist/render/createRenderer.d.mts +1 -0
  17. package/dist/render/createRenderer.d.mts.map +1 -1
  18. package/dist/render/createRenderer.mjs +60 -1
  19. package/dist/render/createRenderer.mjs.map +1 -1
  20. package/dist/render/index.mjs +3 -2
  21. package/dist/render/index.mjs.map +1 -1
  22. package/dist/serve.d.mts.map +1 -1
  23. package/dist/serve.mjs +31 -20
  24. package/dist/serve.mjs.map +1 -1
  25. package/dist/server/ui/pages/Preview.vue +11 -5
  26. package/dist/transformers/entities.d.mts.map +1 -1
  27. package/dist/transformers/entities.mjs +3 -0
  28. package/dist/transformers/entities.mjs.map +1 -1
  29. package/dist/transformers/filters/defaults.d.mts +6 -0
  30. package/dist/transformers/filters/defaults.d.mts.map +1 -0
  31. package/dist/transformers/filters/defaults.mjs +78 -0
  32. package/dist/transformers/filters/defaults.mjs.map +1 -0
  33. package/dist/transformers/filters/index.d.mts +22 -0
  34. package/dist/transformers/filters/index.d.mts.map +1 -0
  35. package/dist/transformers/filters/index.mjs +67 -0
  36. package/dist/transformers/filters/index.mjs.map +1 -0
  37. package/dist/transformers/index.d.mts +9 -8
  38. package/dist/transformers/index.d.mts.map +1 -1
  39. package/dist/transformers/index.mjs +15 -10
  40. package/dist/transformers/index.mjs.map +1 -1
  41. package/dist/transformers/tailwindcss.d.mts +6 -2
  42. package/dist/transformers/tailwindcss.d.mts.map +1 -1
  43. package/dist/transformers/tailwindcss.mjs +49 -21
  44. package/dist/transformers/tailwindcss.mjs.map +1 -1
  45. package/dist/types/config.d.mts +373 -14
  46. package/dist/types/config.d.mts.map +1 -1
  47. package/dist/types/index.d.mts +2 -2
  48. package/dist/utils/ast/serializer.d.mts +3 -2
  49. package/dist/utils/ast/serializer.d.mts.map +1 -1
  50. package/dist/utils/ast/serializer.mjs +24 -0
  51. package/dist/utils/ast/serializer.mjs.map +1 -1
  52. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/transformers/filters/index.ts"],"sourcesContent":["import { Text } from 'domhandler'\nimport type { ChildNode, Element } from 'domhandler'\nimport { parse, serialize } from '../../utils/ast/index.ts'\nimport { defaults } from './defaults.ts'\n\nexport type { FilterFunction } from './defaults.ts'\nexport type FiltersConfig = false | Record<string, (str: string, value: string) => string>\n\n/**\n * Process children before parents so nested filter elements work correctly.\n */\nfunction walkBottomUp(nodes: ChildNode[], callback: (node: ChildNode) => void): void {\n for (const node of [...nodes]) {\n if ('children' in node && node.children?.length) {\n walkBottomUp(node.children as ChildNode[], callback)\n }\n\n callback(node)\n }\n}\n\n/**\n * Filters transformer.\n *\n * Applies transformation functions to the content of elements that\n * have matching filter attributes. Multiple filters on the same element\n * are executed in the order the attributes are defined.\n *\n * Default filters include string manipulation (uppercase, lowercase, trim, etc.),\n * math operations (plus, minus, multiply, etc.), and more.\n *\n * Custom filters can be added via config, and will be merged with defaults.\n * Set config to `false` to disable all filters.\n */\nexport function filters(dom: ChildNode[], config: FiltersConfig = {}): ChildNode[] {\n if (config === false) return dom\n\n const allFilters = { ...defaults, ...config }\n const filterNames = new Set(Object.keys(allFilters))\n\n walkBottomUp(dom, (node) => {\n const el = node as Element\n\n if (!el.attribs) return\n\n // Collect matching filter attributes in source order\n const matched: Array<{ name: string; value: string }> = []\n\n for (const attr of Object.keys(el.attribs)) {\n if (filterNames.has(attr)) {\n matched.push({ name: attr, value: el.attribs[attr] })\n }\n }\n\n if (matched.length === 0) return\n\n // Serialize children to get innerHTML\n let content = serialize(el.children as ChildNode[])\n\n // Apply each filter in attribute order\n for (const { name, value } of matched) {\n content = allFilters[name](content, value)\n delete el.attribs[name]\n }\n\n // Replace children with the filtered content\n if (content === '') {\n el.children = []\n } else if (/<[a-z/!]/i.test(content)) {\n // Result contains HTML elements — parse back to DOM\n const newChildren = parse(content)\n\n for (const child of newChildren) {\n child.parent = el as any\n }\n\n el.children = newChildren as ChildNode[]\n } else {\n // Text-only result — create a text node directly to preserve entity strings\n const textNode = new Text(content)\n textNode.parent = el as any\n el.children = [textNode]\n }\n })\n\n return dom\n}\n"],"mappings":";;;;;;;;;;AAWA,SAAS,aAAa,OAAoB,UAA2C;AACnF,MAAK,MAAM,QAAQ,CAAC,GAAG,MAAM,EAAE;AAC7B,MAAI,cAAc,QAAQ,KAAK,UAAU,OACvC,cAAa,KAAK,UAAyB,SAAS;AAGtD,WAAS,KAAK;;;;;;;;;;;;;;;;AAiBlB,SAAgB,QAAQ,KAAkB,SAAwB,EAAE,EAAe;AACjF,KAAI,WAAW,MAAO,QAAO;CAE7B,MAAM,aAAa;EAAE,GAAG;EAAU,GAAG;EAAQ;CAC7C,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,WAAW,CAAC;AAEpD,cAAa,MAAM,SAAS;EAC1B,MAAM,KAAK;AAEX,MAAI,CAAC,GAAG,QAAS;EAGjB,MAAM,UAAkD,EAAE;AAE1D,OAAK,MAAM,QAAQ,OAAO,KAAK,GAAG,QAAQ,CACxC,KAAI,YAAY,IAAI,KAAK,CACvB,SAAQ,KAAK;GAAE,MAAM;GAAM,OAAO,GAAG,QAAQ;GAAO,CAAC;AAIzD,MAAI,QAAQ,WAAW,EAAG;EAG1B,IAAI,UAAU,UAAU,GAAG,SAAwB;AAGnD,OAAK,MAAM,EAAE,MAAM,WAAW,SAAS;AACrC,aAAU,WAAW,MAAM,SAAS,MAAM;AAC1C,UAAO,GAAG,QAAQ;;AAIpB,MAAI,YAAY,GACd,IAAG,WAAW,EAAE;WACP,YAAY,KAAK,QAAQ,EAAE;GAEpC,MAAM,cAAc,MAAM,QAAQ;AAElC,QAAK,MAAM,SAAS,YAClB,OAAM,SAAS;AAGjB,MAAG,WAAW;SACT;GAEL,MAAM,WAAW,IAAI,KAAK,QAAQ;AAClC,YAAS,SAAS;AAClB,MAAG,WAAW,CAAC,SAAS;;GAE1B;AAEF,QAAO"}
@@ -20,16 +20,17 @@ import { MaizzleConfig } from "../types/config.mjs";
20
20
  * 5. Remove attributes
21
21
  * 6. Shorthand CSS
22
22
  * 7. Add attributes
23
- * 8. Base URL
24
- * 9. URL query
25
- * 10. Purge CSS (serializes/parses internally around email-comb)
26
- * 11. Entities
23
+ * 8. Filters
24
+ * 9. Base URL
25
+ * 10. URL query
26
+ * 11. Purge CSS (serializes/parses internally around email-comb)
27
+ * 12. Entities
27
28
  * + Vue-generated comments stripped here (on serialized string)
28
- * 12. Replace strings
29
- * 13. Prettify
30
- * 14. Minify
29
+ * 13. Replace strings
30
+ * 14. Prettify
31
+ * 15. Minify
31
32
  */
32
- declare function runTransformers(html: string, config: MaizzleConfig, filePath?: string): Promise<string>;
33
+ declare function runTransformers(html: string, config: MaizzleConfig, filePath?: string, doctype?: string): Promise<string>;
33
34
  //#endregion
34
35
  export { runTransformers };
35
36
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/transformers/index.ts"],"mappings":";;;;;AA8CA;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAsB,eAAA,CACpB,IAAA,UACA,MAAA,EAAQ,aAAA,EACR,QAAA,YACC,OAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/transformers/index.ts"],"mappings":";;;;;AAgDA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAsB,eAAA,CACpB,IAAA,UACA,MAAA,EAAQ,aAAA,EACR,QAAA,WACA,OAAA,YACC,OAAA"}
@@ -9,6 +9,7 @@ import { inlineCSS } from "./inlineCSS.mjs";
9
9
  import { removeAttributes } from "./removeAttributes.mjs";
10
10
  import { shorthandCSS } from "./shorthandCSS.mjs";
11
11
  import { addAttributes } from "./addAttributes.mjs";
12
+ import { filters } from "./filters/index.mjs";
12
13
  import { base } from "./base.mjs";
13
14
  import { entities } from "./entities.mjs";
14
15
  import { urlQuery } from "./urlQuery.mjs";
@@ -37,16 +38,17 @@ import { minify } from "./minify.mjs";
37
38
  * 5. Remove attributes
38
39
  * 6. Shorthand CSS
39
40
  * 7. Add attributes
40
- * 8. Base URL
41
- * 9. URL query
42
- * 10. Purge CSS (serializes/parses internally around email-comb)
43
- * 11. Entities
41
+ * 8. Filters
42
+ * 9. Base URL
43
+ * 10. URL query
44
+ * 11. Purge CSS (serializes/parses internally around email-comb)
45
+ * 12. Entities
44
46
  * + Vue-generated comments stripped here (on serialized string)
45
- * 12. Replace strings
46
- * 13. Prettify
47
- * 14. Minify
47
+ * 13. Replace strings
48
+ * 14. Prettify
49
+ * 15. Minify
48
50
  */
49
- async function runTransformers(html, config, filePath) {
51
+ async function runTransformers(html, config, filePath, doctype) {
50
52
  let dom = parse(html);
51
53
  dom = await inlineLink(dom, filePath);
52
54
  dom = await tailwindcss(dom, config, filePath);
@@ -56,15 +58,18 @@ async function runTransformers(html, config, filePath) {
56
58
  dom = removeAttributes(dom, config.html?.attributes);
57
59
  dom = shorthandCSS(dom, config.css);
58
60
  dom = addAttributes(dom, config.html?.attributes);
61
+ dom = filters(dom, config.filters);
59
62
  dom = base(dom, config.url);
60
63
  dom = urlQuery(dom, config.url);
61
64
  dom = purgeCSS(dom, config.css);
62
65
  dom = entities(dom, config.html?.decodeEntities);
63
- let result = serialize(dom);
64
- result = result.replaceAll("<!--[-->", "").replaceAll("<!--]-->", "");
66
+ const isXhtml = doctype ? /xhtml/i.test(doctype) : false;
67
+ let result = serialize(dom, { selfClosingTags: isXhtml });
68
+ result = result.replaceAll("<!--[-->", "").replaceAll("<!--]-->", "").replaceAll("<!--teleport start anchor-->", "").replaceAll("<!--teleport anchor-->", "").replaceAll("<!--teleport start-->", "").replaceAll("<!--teleport end-->", "");
65
69
  result = replaceStrings(result, config);
66
70
  result = await format(result, config);
67
71
  result = minify(result, config);
72
+ if (!isXhtml) result = result.replace(/ \/>/g, ">");
68
73
  return result;
69
74
  }
70
75
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/transformers/index.ts"],"sourcesContent":["import { parse, serialize } from '../utils/ast/index.ts'\nimport { inlineLink } from './inlineLink.ts'\nimport { tailwindcss } from './tailwindcss.ts'\nimport { safeClassNames } from './safeClassNames.ts'\nimport { attributeToStyle } from './attributeToStyle.ts'\nimport { inlineCSS } from './inlineCSS.ts'\nimport { removeAttributes } from './removeAttributes.ts'\nimport { shorthandCSS } from './shorthandCSS.ts'\nimport { addAttributes } from './addAttributes.ts'\nimport { base } from './base.ts'\nimport { entities } from './entities.ts'\nimport { urlQuery } from './urlQuery.ts'\nimport { purgeCSS } from './purgeCSS.ts'\nimport { replaceStrings } from './replaceStrings.ts'\nimport { format } from './format.ts'\nimport { minify } from './minify.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\n\n/**\n * Run all Maizzle transformers on the rendered HTML.\n *\n * The HTML is parsed into a DOM once at the start and passed through all\n * DOM-based transformers as a shared `ChildNode[]`. After all DOM transformers\n * complete, the DOM is serialized back to a string exactly once.\n *\n * String-only transformers (those that rely on external tools that require a\n * raw HTML string) then run on the serialized output.\n *\n * Transformers run in a specific order:\n * 0. Inline link stylesheets — replace `<link rel=\"stylesheet\">` with `<style>` tags\n * 1. Tailwind CSS — compile CSS, lower syntax, optimize (cleanup + merge media queries)\n * 2. Safe class names\n * 3. Attribute to style\n * 4. CSS inliner\n * 5. Remove attributes\n * 6. Shorthand CSS\n * 7. Add attributes\n * 8. Base URL\n * 9. URL query\n * 10. Purge CSS (serializes/parses internally around email-comb)\n * 11. Entities\n * + Vue-generated comments stripped here (on serialized string)\n * 12. Replace strings\n * 13. Prettify\n * 14. Minify\n */\nexport async function runTransformers(\n html: string,\n config: MaizzleConfig,\n filePath?: string,\n): Promise<string> {\n // Parse once — all DOM transformers share this array\n let dom = parse(html)\n\n // 0. Inline <link> stylesheets\n dom = await inlineLink(dom, filePath)\n\n // 1. Tailwind CSS — always runs first\n dom = await tailwindcss(dom, config, filePath)\n\n // 2. Safe class names\n dom = safeClassNames(dom, config.css)\n\n // 3. Attribute to style\n dom = attributeToStyle(dom, config.css)\n\n // 4. CSS inliner (serializes/parses internally around juice)\n dom = inlineCSS(dom, config.css)\n\n // 5. Remove attributes\n dom = removeAttributes(dom, config.html?.attributes)\n\n // 6. Shorthand CSS\n dom = shorthandCSS(dom, config.css)\n\n // 7. Add attributes\n dom = addAttributes(dom, config.html?.attributes)\n\n // 8. Base URL (serializes/parses internally for VML/MSO regex passes)\n dom = base(dom, config.url)\n\n // 9. URL query\n dom = urlQuery(dom, config.url)\n\n // 10. Purge CSS (serializes/parses internally around email-comb)\n dom = purgeCSS(dom, config.css)\n\n // 11. Entities\n dom = entities(dom, config.html?.decodeEntities)\n\n // Serialize once — remaining transformers operate on the HTML string\n let result = serialize(dom)\n\n // Remove Vue-generated comments after serializing\n result = result\n .replaceAll('<!--[-->', '')\n .replaceAll('<!--]-->', '')\n\n // 12. Replace strings\n result = replaceStrings(result, config)\n\n // 13. Format\n result = await format(result, config)\n\n // 14. Minify\n result = minify(result, config)\n\n return result\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,eAAsB,gBACpB,MACA,QACA,UACiB;CAEjB,IAAI,MAAM,MAAM,KAAK;AAGrB,OAAM,MAAM,WAAW,KAAK,SAAS;AAGrC,OAAM,MAAM,YAAY,KAAK,QAAQ,SAAS;AAG9C,OAAM,eAAe,KAAK,OAAO,IAAI;AAGrC,OAAM,iBAAiB,KAAK,OAAO,IAAI;AAGvC,OAAM,UAAU,KAAK,OAAO,IAAI;AAGhC,OAAM,iBAAiB,KAAK,OAAO,MAAM,WAAW;AAGpD,OAAM,aAAa,KAAK,OAAO,IAAI;AAGnC,OAAM,cAAc,KAAK,OAAO,MAAM,WAAW;AAGjD,OAAM,KAAK,KAAK,OAAO,IAAI;AAG3B,OAAM,SAAS,KAAK,OAAO,IAAI;AAG/B,OAAM,SAAS,KAAK,OAAO,IAAI;AAG/B,OAAM,SAAS,KAAK,OAAO,MAAM,eAAe;CAGhD,IAAI,SAAS,UAAU,IAAI;AAG3B,UAAS,OACN,WAAW,YAAY,GAAG,CAC1B,WAAW,YAAY,GAAG;AAG7B,UAAS,eAAe,QAAQ,OAAO;AAGvC,UAAS,MAAM,OAAO,QAAQ,OAAO;AAGrC,UAAS,OAAO,QAAQ,OAAO;AAE/B,QAAO"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/transformers/index.ts"],"sourcesContent":["import { parse, serialize } from '../utils/ast/index.ts'\nimport { inlineLink } from './inlineLink.ts'\nimport { tailwindcss } from './tailwindcss.ts'\nimport { safeClassNames } from './safeClassNames.ts'\nimport { attributeToStyle } from './attributeToStyle.ts'\nimport { inlineCSS } from './inlineCSS.ts'\nimport { removeAttributes } from './removeAttributes.ts'\nimport { shorthandCSS } from './shorthandCSS.ts'\nimport { addAttributes } from './addAttributes.ts'\nimport { filters } from './filters/index.ts'\nimport { base } from './base.ts'\nimport { entities } from './entities.ts'\nimport { urlQuery } from './urlQuery.ts'\nimport { purgeCSS } from './purgeCSS.ts'\nimport { replaceStrings } from './replaceStrings.ts'\nimport { format } from './format.ts'\nimport { minify } from './minify.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\n\n/**\n * Run all Maizzle transformers on the rendered HTML.\n *\n * The HTML is parsed into a DOM once at the start and passed through all\n * DOM-based transformers as a shared `ChildNode[]`. After all DOM transformers\n * complete, the DOM is serialized back to a string exactly once.\n *\n * String-only transformers (those that rely on external tools that require a\n * raw HTML string) then run on the serialized output.\n *\n * Transformers run in a specific order:\n * 0. Inline link stylesheets — replace `<link rel=\"stylesheet\">` with `<style>` tags\n * 1. Tailwind CSS — compile CSS, lower syntax, optimize (cleanup + merge media queries)\n * 2. Safe class names\n * 3. Attribute to style\n * 4. CSS inliner\n * 5. Remove attributes\n * 6. Shorthand CSS\n * 7. Add attributes\n * 8. Filters\n * 9. Base URL\n * 10. URL query\n * 11. Purge CSS (serializes/parses internally around email-comb)\n * 12. Entities\n * + Vue-generated comments stripped here (on serialized string)\n * 13. Replace strings\n * 14. Prettify\n * 15. Minify\n */\nexport async function runTransformers(\n html: string,\n config: MaizzleConfig,\n filePath?: string,\n doctype?: string,\n): Promise<string> {\n // Parse once — all DOM transformers share this array\n let dom = parse(html)\n\n // 0. Inline <link> stylesheets\n dom = await inlineLink(dom, filePath)\n\n // 1. Tailwind CSS — always runs first\n dom = await tailwindcss(dom, config, filePath)\n\n // 2. Safe class names\n dom = safeClassNames(dom, config.css)\n\n // 3. Attribute to style\n dom = attributeToStyle(dom, config.css)\n\n // 4. CSS inliner (serializes/parses internally around juice)\n dom = inlineCSS(dom, config.css)\n\n // 5. Remove attributes\n dom = removeAttributes(dom, config.html?.attributes)\n\n // 6. Shorthand CSS\n dom = shorthandCSS(dom, config.css)\n\n // 7. Add attributes\n dom = addAttributes(dom, config.html?.attributes)\n\n // 8. Filters\n dom = filters(dom, config.filters)\n\n // 9. Base URL (serializes/parses internally for VML/MSO regex passes)\n dom = base(dom, config.url)\n\n // 10. URL query\n dom = urlQuery(dom, config.url)\n\n // 11. Purge CSS (serializes/parses internally around email-comb)\n dom = purgeCSS(dom, config.css)\n\n // 12. Entities\n dom = entities(dom, config.html?.decodeEntities)\n\n // Serialize once — remaining transformers operate on the HTML string\n const isXhtml = doctype ? /xhtml/i.test(doctype) : false\n let result = serialize(dom, { selfClosingTags: isXhtml })\n\n // Remove Vue-generated comments after serializing\n result = result\n .replaceAll('<!--[-->', '')\n .replaceAll('<!--]-->', '')\n .replaceAll('<!--teleport start anchor-->', '')\n .replaceAll('<!--teleport anchor-->', '')\n .replaceAll('<!--teleport start-->', '')\n .replaceAll('<!--teleport end-->', '')\n\n // 13. Replace strings\n result = replaceStrings(result, config)\n\n // 14. Format\n result = await format(result, config)\n\n // 15. Minify\n result = minify(result, config)\n\n // Strip self-closing slashes for HTML5 doctypes\n if (!isXhtml) {\n result = result.replace(/ \\/>/g, '>')\n }\n\n return result\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,eAAsB,gBACpB,MACA,QACA,UACA,SACiB;CAEjB,IAAI,MAAM,MAAM,KAAK;AAGrB,OAAM,MAAM,WAAW,KAAK,SAAS;AAGrC,OAAM,MAAM,YAAY,KAAK,QAAQ,SAAS;AAG9C,OAAM,eAAe,KAAK,OAAO,IAAI;AAGrC,OAAM,iBAAiB,KAAK,OAAO,IAAI;AAGvC,OAAM,UAAU,KAAK,OAAO,IAAI;AAGhC,OAAM,iBAAiB,KAAK,OAAO,MAAM,WAAW;AAGpD,OAAM,aAAa,KAAK,OAAO,IAAI;AAGnC,OAAM,cAAc,KAAK,OAAO,MAAM,WAAW;AAGjD,OAAM,QAAQ,KAAK,OAAO,QAAQ;AAGlC,OAAM,KAAK,KAAK,OAAO,IAAI;AAG3B,OAAM,SAAS,KAAK,OAAO,IAAI;AAG/B,OAAM,SAAS,KAAK,OAAO,IAAI;AAG/B,OAAM,SAAS,KAAK,OAAO,MAAM,eAAe;CAGhD,MAAM,UAAU,UAAU,SAAS,KAAK,QAAQ,GAAG;CACnD,IAAI,SAAS,UAAU,KAAK,EAAE,iBAAiB,SAAS,CAAC;AAGzD,UAAS,OACN,WAAW,YAAY,GAAG,CAC1B,WAAW,YAAY,GAAG,CAC1B,WAAW,gCAAgC,GAAG,CAC9C,WAAW,0BAA0B,GAAG,CACxC,WAAW,yBAAyB,GAAG,CACvC,WAAW,uBAAuB,GAAG;AAGxC,UAAS,eAAe,QAAQ,OAAO;AAGvC,UAAS,MAAM,OAAO,QAAQ,OAAO;AAGrC,UAAS,OAAO,QAAQ,OAAO;AAG/B,KAAI,CAAC,QACH,UAAS,OAAO,QAAQ,SAAS,IAAI;AAGvC,QAAO"}
@@ -8,8 +8,12 @@ import { ChildNode } from "domhandler";
8
8
  * Compiles CSS inside <style> tags in the DOM using
9
9
  * @tailwindcss/postcss, then lowers modern CSS syntax with lightningcss.
10
10
  *
11
- * Uses the AST walker to find <style> tags and decodes HTML entities
12
- * that Vue SSR encodes (e.g. &quot;) before CSS processing.
11
+ * Configures Tailwind sources to scan:
12
+ * - Rendered class attributes (via `@source inline`) for all classes from all components
13
+ * - User project files (via Tailwind's auto-detection from base/from path)
14
+ *
15
+ * User `@source` and `@source not directives` in style tags are preserved.
16
+ * Source directives are only added to style tags that import Tailwind.
13
17
  *
14
18
  * Runs as the first transformer in the pipeline so that subsequent
15
19
  * transformers (inliner, purge, etc.) work with fully compiled CSS.
@@ -1 +1 @@
1
- {"version":3,"file":"tailwindcss.d.mts","names":[],"sources":["../../src/transformers/tailwindcss.ts"],"mappings":";;;;;;AA6FA;;;;;;;;;;iBAAsB,WAAA,CAAY,GAAA,EAAK,SAAA,IAAa,MAAA,EAAQ,aAAA,EAAe,QAAA,YAAoB,OAAA,CAAQ,SAAA"}
1
+ {"version":3,"file":"tailwindcss.d.mts","names":[],"sources":["../../src/transformers/tailwindcss.ts"],"mappings":";;;;;;AAsJA;;;;;;;;;;;;;;iBAAsB,WAAA,CAAY,GAAA,EAAK,SAAA,IAAa,MAAA,EAAQ,aAAA,EAAe,QAAA,YAAoB,OAAA,CAAQ,SAAA"}
@@ -7,6 +7,7 @@ import { dirname, relative, resolve } from "node:path";
7
7
  import postcss from "postcss";
8
8
  import tailwindcssPostcss from "@tailwindcss/postcss";
9
9
  import customProperties from "postcss-custom-properties";
10
+ import postcssCalc from "postcss-calc";
10
11
  import safeParser from "postcss-safe-parser";
11
12
  import { transform } from "lightningcss";
12
13
 
@@ -19,6 +20,7 @@ function createProcessor(config) {
19
20
  optimize: false
20
21
  }),
21
22
  customProperties({ preserve: false }),
23
+ postcssCalc({}),
22
24
  pruneVars_default()
23
25
  ]);
24
26
  }
@@ -33,6 +35,16 @@ function decodeEntities(str) {
33
35
  return str.replace(/&quot;/g, "\"").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&#39;/g, "'").replace(/&apos;/g, "'");
34
36
  }
35
37
  /**
38
+ * Check if CSS content uses Tailwind features that require source scanning.
39
+ *
40
+ * Only CSS that imports Tailwind (or @maizzle/tailwindcss) needs @source
41
+ * directives. Plain CSS without Tailwind imports doesn't need scanning
42
+ * and would pass through @source directives unconsumed.
43
+ */
44
+ function usesTailwind(css) {
45
+ return /(@import\s+["'](tailwindcss|@maizzle\/tailwindcss)|@tailwind\s)/.test(css);
46
+ }
47
+ /**
36
48
  * Lower modern CSS syntax using lightningcss.
37
49
  *
38
50
  * Targets IE 1 to maximize syntax lowering — converts modern features
@@ -60,19 +72,44 @@ async function optimizeCss(css, config) {
60
72
  return (await postcss(plugins).process(css, { from: void 0 })).css;
61
73
  }
62
74
  /**
75
+ * Build @source directives for Tailwind CSS scanning.
76
+ *
77
+ * Configures two types of sources:
78
+ * 1. Exclusions for output dir and user-configured paths
79
+ * 2. Inline source with all class attribute values from the rendered DOM,
80
+ * capturing classes from all components (built-in + user), dynamic
81
+ * expressions, and the template itself — Tailwind's scanner handles
82
+ * the actual class extraction from these raw values
83
+ */
84
+ function buildSourceDirectives(dom, config, fromDir) {
85
+ const directives = [];
86
+ const excludePaths = [resolve(config.output?.path ?? "dist"), ...(config.css?.exclude ?? []).map((p) => resolve(p))];
87
+ for (const p of excludePaths) directives.push(`@source not "${relative(fromDir, resolve(p))}";`);
88
+ const classes = [];
89
+ walk(dom, (n) => {
90
+ const cls = n.attribs?.class;
91
+ if (cls) classes.push(cls);
92
+ });
93
+ if (classes.length) directives.push(`@source inline("${classes.join(" ")}");`);
94
+ return directives.join("\n");
95
+ }
96
+ /**
63
97
  * Tailwind CSS transformer.
64
98
  *
65
99
  * Compiles CSS inside <style> tags in the DOM using
66
100
  * @tailwindcss/postcss, then lowers modern CSS syntax with lightningcss.
67
101
  *
68
- * Uses the AST walker to find <style> tags and decodes HTML entities
69
- * that Vue SSR encodes (e.g. &quot;) before CSS processing.
102
+ * Configures Tailwind sources to scan:
103
+ * - Rendered class attributes (via `@source inline`) for all classes from all components
104
+ * - User project files (via Tailwind's auto-detection from base/from path)
105
+ *
106
+ * User `@source` and `@source not directives` in style tags are preserved.
107
+ * Source directives are only added to style tags that import Tailwind.
70
108
  *
71
109
  * Runs as the first transformer in the pipeline so that subsequent
72
110
  * transformers (inliner, purge, etc.) work with fully compiled CSS.
73
111
  */
74
112
  async function tailwindcss(dom, config, filePath) {
75
- const sourceNotPaths = [resolve(config.output?.path ?? "dist"), ...(config.css?.exclude ?? []).map((p) => resolve(p))];
76
113
  const styleTags = [];
77
114
  walk(dom, (node) => {
78
115
  if (node.name !== "style") return;
@@ -89,33 +126,24 @@ async function tailwindcss(dom, config, filePath) {
89
126
  }
90
127
  const rawContent = el.children.filter((child) => child.type === "text").map((child) => child.data).join("");
91
128
  if (!rawContent.trim()) return;
92
- let cssContent = decodeEntities(rawContent);
93
- if (filePath) {
94
- if (sourceNotPaths.length) {
95
- const fileDir = dirname(filePath);
96
- const exclusions = sourceNotPaths.map((p) => `@source not "${relative(fileDir, resolve(p))}";`).join("\n");
97
- cssContent = `${cssContent}\n${exclusions}`;
98
- }
99
- } else {
100
- const classes = [];
101
- walk(dom, (n) => {
102
- const cls = n.attribs?.class;
103
- if (cls) classes.push(cls);
104
- });
105
- if (classes.length) cssContent = `${cssContent}\n@source inline("${classes.join(" ")}");`;
106
- }
107
129
  styleTags.push({
108
130
  node: el,
109
- cssContent
131
+ cssContent: decodeEntities(rawContent)
110
132
  });
111
133
  });
134
+ if (!styleTags.length) return dom;
135
+ const fromPath = filePath ?? resolve(process.cwd(), "template.vue");
136
+ const fromDir = dirname(fromPath);
137
+ const sourceDirectives = styleTags.some(({ cssContent }) => usesTailwind(cssContent)) ? buildSourceDirectives(dom, config, fromDir) : "";
138
+ const processor = createProcessor(config);
112
139
  for (let i = 0; i < styleTags.length; i++) {
113
140
  const { node, cssContent } = styleTags[i];
141
+ const fullCss = usesTailwind(cssContent) ? `${cssContent}\n${sourceDirectives}` : cssContent;
114
142
  try {
115
143
  node.children = [{
116
144
  type: "text",
117
- data: await optimizeCss(lowerSyntax((await createProcessor(config).process(cssContent, {
118
- from: `${filePath ?? resolve(process.cwd(), "template.vue")}?style=${i}`,
145
+ data: await optimizeCss(lowerSyntax((await processor.process(fullCss, {
146
+ from: `${fromPath}?style=${i}`,
119
147
  parser: safeParser
120
148
  })).css), config),
121
149
  parent: node
@@ -1 +1 @@
1
- {"version":3,"file":"tailwindcss.mjs","names":["pruneVars"],"sources":["../../src/transformers/tailwindcss.ts"],"sourcesContent":["import postcss from 'postcss'\nimport tailwindcssPostcss from '@tailwindcss/postcss'\nimport customProperties from 'postcss-custom-properties'\nimport pruneVars from '../plugins/postcss/pruneVars.ts'\nimport safeParser from 'postcss-safe-parser'\nimport { transform } from 'lightningcss'\nimport { resolve, dirname, relative } from 'node:path'\nimport type { ChildNode, Element } from 'domhandler'\nimport { walk } from '../utils/ast/index.ts'\nimport { tailwindCleanup } from '../plugins/postcss/tailwindCleanup.ts'\nimport { mergeMediaQueries } from '../plugins/postcss/mergeMediaQueries.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\n\nfunction createProcessor(config: MaizzleConfig) {\n return postcss([\n tailwindcssPostcss({\n base: config.css?.base,\n transformAssetUrls: false,\n optimize: false, // we run Lightning CSS manually\n }),\n customProperties({\n preserve: false,\n }),\n pruneVars(),\n ])\n}\n\n/**\n * Decode HTML entities that Vue SSR encodes inside <style> tags.\n *\n * Vue's renderToString HTML-encodes quotes and other characters\n * inside <style> tags within templates, breaking CSS like\n * `@import \"@maizzle/tailwindcss\"` → `@import &quot;...&quot;`\n */\nfunction decodeEntities(str: string): string {\n return str\n .replace(/&quot;/g, '\"')\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&#39;/g, \"'\")\n .replace(/&apos;/g, \"'\")\n}\n\n/**\n * Lower modern CSS syntax using lightningcss.\n *\n * Targets IE 1 to maximize syntax lowering — converts modern features\n * like nesting, oklch(), color-mix(), @property, etc. into simple CSS\n * that email clients can understand.\n */\nfunction lowerSyntax(css: string): string {\n const result = transform({\n filename: 'email.css',\n code: Buffer.from(css),\n minify: false,\n targets: {\n ie: 4 << 5,\n },\n })\n\n return result.code.toString()\n}\n\n/**\n * Run cleanup and media query merging on the compiled CSS.\n *\n * Removes unwanted selectors (:host, :lang) and at-rules (@layer, @property),\n * then sorts and merges media queries.\n */\nasync function optimizeCss(css: string, config: MaizzleConfig): Promise<string> {\n const plugins: postcss.Plugin[] = [...tailwindCleanup(config)]\n\n const mediaPlugin = mergeMediaQueries(config)\n if (mediaPlugin) plugins.push(mediaPlugin)\n\n const result = await postcss(plugins).process(css, { from: undefined })\n\n return result.css\n}\n\n/**\n * Tailwind CSS transformer.\n *\n * Compiles CSS inside <style> tags in the DOM using\n * @tailwindcss/postcss, then lowers modern CSS syntax with lightningcss.\n *\n * Uses the AST walker to find <style> tags and decodes HTML entities\n * that Vue SSR encodes (e.g. &quot;) before CSS processing.\n *\n * Runs as the first transformer in the pipeline so that subsequent\n * transformers (inliner, purge, etc.) work with fully compiled CSS.\n */\nexport async function tailwindcss(dom: ChildNode[], config: MaizzleConfig, filePath?: string): Promise<ChildNode[]> {\n const sourceNotPaths = [\n resolve(config.output?.path ?? 'dist'),\n ...(config.css?.exclude ?? []).map(p => resolve(p)),\n ]\n\n const styleTags: { node: Element; cssContent: string }[] = []\n\n walk(dom, (node) => {\n if ((node as Element).name !== 'style') return\n\n const el = node as Element\n const attrs = el.attribs || {}\n\n // Skip marked style tags, but remove the marker attribute first\n const markerAttr = ['raw', 'embed', 'data-embed'].find(attr => attr in attrs)\n if (markerAttr) {\n delete el.attribs[markerAttr]\n return\n }\n\n // Get text content from children and decode HTML entities\n const rawContent = el.children\n .filter(child => child.type === 'text')\n .map(child => (child as any).data)\n .join('')\n\n if (!rawContent.trim()) return\n\n let cssContent = decodeEntities(rawContent)\n\n if (filePath) {\n if (sourceNotPaths.length) {\n const fileDir = dirname(filePath)\n const exclusions = sourceNotPaths\n .map(p => `@source not \"${relative(fileDir, resolve(p))}\";`)\n .join('\\n')\n\n cssContent = `${cssContent}\\n${exclusions}`\n }\n } else {\n // No file path (e.g. component input) — extract classes from the DOM\n // and tell Tailwind to scan them via @source inline()\n const classes: string[] = []\n walk(dom, (n) => {\n const cls = (n as Element).attribs?.class\n if (cls) classes.push(cls)\n })\n\n if (classes.length) {\n cssContent = `${cssContent}\\n@source inline(\"${classes.join(' ')}\");`\n }\n }\n\n styleTags.push({ node: el, cssContent })\n })\n\n for (let i = 0; i < styleTags.length; i++) {\n const { node, cssContent } = styleTags[i]\n try {\n const processor = createProcessor(config)\n const result = await processor.process(\n cssContent,\n {\n from: `${filePath ?? resolve(process.cwd(), 'template.vue')}?style=${i}`,\n parser: safeParser,\n }\n )\n\n const lowered = lowerSyntax(result.css)\n const optimized = await optimizeCss(lowered, config)\n\n // Replace the style tag's children with the compiled CSS\n node.children = [{\n type: 'text',\n data: optimized,\n parent: node,\n } as any]\n } catch {\n // If CSS processing fails, still replace with decoded content\n // so HTML entities don't break the CSS\n node.children = [{\n type: 'text',\n data: cssContent,\n parent: node,\n } as any]\n }\n }\n\n return dom\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAS,gBAAgB,QAAuB;AAC9C,QAAO,QAAQ;EACb,mBAAmB;GACjB,MAAM,OAAO,KAAK;GAClB,oBAAoB;GACpB,UAAU;GACX,CAAC;EACF,iBAAiB,EACf,UAAU,OACX,CAAC;EACFA,mBAAW;EACZ,CAAC;;;;;;;;;AAUJ,SAAS,eAAe,KAAqB;AAC3C,QAAO,IACJ,QAAQ,WAAW,KAAI,CACvB,QAAQ,UAAU,IAAI,CACtB,QAAQ,SAAS,IAAI,CACrB,QAAQ,SAAS,IAAI,CACrB,QAAQ,UAAU,IAAI,CACtB,QAAQ,WAAW,IAAI;;;;;;;;;AAU5B,SAAS,YAAY,KAAqB;AAUxC,QATe,UAAU;EACvB,UAAU;EACV,MAAM,OAAO,KAAK,IAAI;EACtB,QAAQ;EACR,SAAS,EACP,IAAI,KACL;EACF,CAAC,CAEY,KAAK,UAAU;;;;;;;;AAS/B,eAAe,YAAY,KAAa,QAAwC;CAC9E,MAAM,UAA4B,CAAC,GAAG,gBAAgB,OAAO,CAAC;CAE9D,MAAM,cAAc,kBAAkB,OAAO;AAC7C,KAAI,YAAa,SAAQ,KAAK,YAAY;AAI1C,SAFe,MAAM,QAAQ,QAAQ,CAAC,QAAQ,KAAK,EAAE,MAAM,QAAW,CAAC,EAEzD;;;;;;;;;;;;;;AAehB,eAAsB,YAAY,KAAkB,QAAuB,UAAyC;CAClH,MAAM,iBAAiB,CACrB,QAAQ,OAAO,QAAQ,QAAQ,OAAO,EACtC,IAAI,OAAO,KAAK,WAAW,EAAE,EAAE,KAAI,MAAK,QAAQ,EAAE,CAAC,CACpD;CAED,MAAM,YAAqD,EAAE;AAE7D,MAAK,MAAM,SAAS;AAClB,MAAK,KAAiB,SAAS,QAAS;EAExC,MAAM,KAAK;EACX,MAAM,QAAQ,GAAG,WAAW,EAAE;EAG9B,MAAM,aAAa;GAAC;GAAO;GAAS;GAAa,CAAC,MAAK,SAAQ,QAAQ,MAAM;AAC7E,MAAI,YAAY;AACd,UAAO,GAAG,QAAQ;AAClB;;EAIF,MAAM,aAAa,GAAG,SACnB,QAAO,UAAS,MAAM,SAAS,OAAO,CACtC,KAAI,UAAU,MAAc,KAAK,CACjC,KAAK,GAAG;AAEX,MAAI,CAAC,WAAW,MAAM,CAAE;EAExB,IAAI,aAAa,eAAe,WAAW;AAE3C,MAAI,UACF;OAAI,eAAe,QAAQ;IACzB,MAAM,UAAU,QAAQ,SAAS;IACjC,MAAM,aAAa,eAChB,KAAI,MAAK,gBAAgB,SAAS,SAAS,QAAQ,EAAE,CAAC,CAAC,IAAI,CAC3D,KAAK,KAAK;AAEb,iBAAa,GAAG,WAAW,IAAI;;SAE5B;GAGL,MAAM,UAAoB,EAAE;AAC5B,QAAK,MAAM,MAAM;IACf,MAAM,MAAO,EAAc,SAAS;AACpC,QAAI,IAAK,SAAQ,KAAK,IAAI;KAC1B;AAEF,OAAI,QAAQ,OACV,cAAa,GAAG,WAAW,oBAAoB,QAAQ,KAAK,IAAI,CAAC;;AAIrE,YAAU,KAAK;GAAE,MAAM;GAAI;GAAY,CAAC;GACxC;AAEF,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;EACzC,MAAM,EAAE,MAAM,eAAe,UAAU;AACvC,MAAI;AAcF,QAAK,WAAW,CAAC;IACf,MAAM;IACN,MALgB,MAAM,YADR,aARD,MADG,gBAAgB,OAAO,CACV,QAC7B,YACA;KACE,MAAM,GAAG,YAAY,QAAQ,QAAQ,KAAK,EAAE,eAAe,CAAC,SAAS;KACrE,QAAQ;KACT,CACF,EAEkC,IAAI,EACM,OAAO;IAMlD,QAAQ;IACT,CAAQ;UACH;AAGN,QAAK,WAAW,CAAC;IACf,MAAM;IACN,MAAM;IACN,QAAQ;IACT,CAAQ;;;AAIb,QAAO"}
1
+ {"version":3,"file":"tailwindcss.mjs","names":["pruneVars"],"sources":["../../src/transformers/tailwindcss.ts"],"sourcesContent":["import postcss from 'postcss'\nimport tailwindcssPostcss from '@tailwindcss/postcss'\nimport customProperties from 'postcss-custom-properties'\nimport postcssCalc from 'postcss-calc'\nimport pruneVars from '../plugins/postcss/pruneVars.ts'\nimport safeParser from 'postcss-safe-parser'\nimport { transform } from 'lightningcss'\nimport { resolve, dirname, relative } from 'node:path'\nimport type { ChildNode, Element } from 'domhandler'\nimport { walk } from '../utils/ast/index.ts'\nimport { tailwindCleanup } from '../plugins/postcss/tailwindCleanup.ts'\nimport { mergeMediaQueries } from '../plugins/postcss/mergeMediaQueries.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\n\nfunction createProcessor(config: MaizzleConfig) {\n return postcss([\n tailwindcssPostcss({\n base: config.css?.base,\n transformAssetUrls: false,\n optimize: false, // we run Lightning CSS manually\n }),\n customProperties({\n preserve: false,\n }),\n postcssCalc({}),\n pruneVars(),\n ])\n}\n\n/**\n * Decode HTML entities that Vue SSR encodes inside <style> tags.\n *\n * Vue's renderToString HTML-encodes quotes and other characters\n * inside <style> tags within templates, breaking CSS like\n * `@import \"@maizzle/tailwindcss\"` → `@import &quot;...&quot;`\n */\nfunction decodeEntities(str: string): string {\n return str\n .replace(/&quot;/g, '\"')\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&#39;/g, \"'\")\n .replace(/&apos;/g, \"'\")\n}\n\n/**\n * Check if CSS content uses Tailwind features that require source scanning.\n *\n * Only CSS that imports Tailwind (or @maizzle/tailwindcss) needs @source\n * directives. Plain CSS without Tailwind imports doesn't need scanning\n * and would pass through @source directives unconsumed.\n */\nfunction usesTailwind(css: string): boolean {\n return /(@import\\s+[\"'](tailwindcss|@maizzle\\/tailwindcss)|@tailwind\\s)/.test(css)\n}\n\n/**\n * Lower modern CSS syntax using lightningcss.\n *\n * Targets IE 1 to maximize syntax lowering — converts modern features\n * like nesting, oklch(), color-mix(), @property, etc. into simple CSS\n * that email clients can understand.\n */\nfunction lowerSyntax(css: string): string {\n const result = transform({\n filename: 'email.css',\n code: Buffer.from(css),\n minify: false,\n targets: {\n ie: 4 << 5,\n },\n })\n\n return result.code.toString()\n}\n\n/**\n * Run cleanup and media query merging on the compiled CSS.\n *\n * Removes unwanted selectors (:host, :lang) and at-rules (@layer, @property),\n * then sorts and merges media queries.\n */\nasync function optimizeCss(css: string, config: MaizzleConfig): Promise<string> {\n const plugins: postcss.Plugin[] = [...tailwindCleanup(config)]\n\n const mediaPlugin = mergeMediaQueries(config)\n if (mediaPlugin) plugins.push(mediaPlugin)\n\n const result = await postcss(plugins).process(css, { from: undefined })\n\n return result.css\n}\n\n/**\n * Build @source directives for Tailwind CSS scanning.\n *\n * Configures two types of sources:\n * 1. Exclusions for output dir and user-configured paths\n * 2. Inline source with all class attribute values from the rendered DOM,\n * capturing classes from all components (built-in + user), dynamic\n * expressions, and the template itself — Tailwind's scanner handles\n * the actual class extraction from these raw values\n */\nfunction buildSourceDirectives(dom: ChildNode[], config: MaizzleConfig, fromDir: string): string {\n const directives: string[] = []\n\n // Exclude output dir and user-configured paths\n const excludePaths = [\n resolve(config.output?.path ?? 'dist'),\n ...(config.css?.exclude ?? []).map(p => resolve(p)),\n ]\n\n for (const p of excludePaths) {\n directives.push(`@source not \"${relative(fromDir, resolve(p))}\";`)\n }\n\n // Inline source: collect all class attribute values from the rendered DOM.\n // After Vue SSR, the DOM contains every class from every component\n // (built-in framework components, user components, dynamic bindings).\n // We pass these raw values to Tailwind's scanner via @source inline().\n const classes: string[] = []\n walk(dom, (n) => {\n const cls = (n as Element).attribs?.class\n if (cls) classes.push(cls)\n })\n\n if (classes.length) {\n directives.push(`@source inline(\"${classes.join(' ')}\");`)\n }\n\n return directives.join('\\n')\n}\n\n/**\n * Tailwind CSS transformer.\n *\n * Compiles CSS inside <style> tags in the DOM using\n * @tailwindcss/postcss, then lowers modern CSS syntax with lightningcss.\n *\n * Configures Tailwind sources to scan:\n * - Rendered class attributes (via `@source inline`) for all classes from all components\n * - User project files (via Tailwind's auto-detection from base/from path)\n *\n * User `@source` and `@source not directives` in style tags are preserved.\n * Source directives are only added to style tags that import Tailwind.\n *\n * Runs as the first transformer in the pipeline so that subsequent\n * transformers (inliner, purge, etc.) work with fully compiled CSS.\n */\nexport async function tailwindcss(dom: ChildNode[], config: MaizzleConfig, filePath?: string): Promise<ChildNode[]> {\n const styleTags: { node: Element; cssContent: string }[] = []\n\n walk(dom, (node) => {\n if ((node as Element).name !== 'style') return\n\n const el = node as Element\n const attrs = el.attribs || {}\n\n // Skip marked style tags, but remove the marker attribute first\n const markerAttr = ['raw', 'embed', 'data-embed'].find(attr => attr in attrs)\n if (markerAttr) {\n delete el.attribs[markerAttr]\n return\n }\n\n // Get text content from children and decode HTML entities\n const rawContent = el.children\n .filter(child => child.type === 'text')\n .map(child => (child as any).data)\n .join('')\n\n if (!rawContent.trim()) return\n\n styleTags.push({ node: el, cssContent: decodeEntities(rawContent) })\n })\n\n if (!styleTags.length) return dom\n\n const fromPath = filePath ?? resolve(process.cwd(), 'template.vue')\n const fromDir = dirname(fromPath)\n\n // Only compute source directives if at least one style tag uses Tailwind\n const hasTailwindStyles = styleTags.some(({ cssContent }) => usesTailwind(cssContent))\n const sourceDirectives = hasTailwindStyles\n ? buildSourceDirectives(dom, config, fromDir)\n : ''\n\n // Create processor once — reused for all style tags in this template\n const processor = createProcessor(config)\n\n for (let i = 0; i < styleTags.length; i++) {\n const { node, cssContent } = styleTags[i]\n\n // Only add source directives to style tags that import Tailwind —\n // plain CSS doesn't need them and @tailwindcss/postcss would leave\n // the directives unconsumed in the output\n const fullCss = usesTailwind(cssContent)\n ? `${cssContent}\\n${sourceDirectives}`\n : cssContent\n\n try {\n const result = await processor.process(\n fullCss,\n {\n from: `${fromPath}?style=${i}`,\n parser: safeParser,\n }\n )\n\n const lowered = lowerSyntax(result.css)\n const optimized = await optimizeCss(lowered, config)\n\n // Replace the style tag's children with the compiled CSS\n node.children = [{\n type: 'text',\n data: optimized,\n parent: node,\n } as any]\n } catch {\n // If CSS processing fails, still replace with decoded content\n // so HTML entities don't break the CSS\n node.children = [{\n type: 'text',\n data: cssContent,\n parent: node,\n } as any]\n }\n }\n\n return dom\n}\n"],"mappings":"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"}