@maizzle/framework 6.0.0-rc.23 → 6.0.0-rc.25

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 (143) hide show
  1. package/dist/build.js +11 -2
  2. package/dist/build.js.map +1 -1
  3. package/dist/components/Body.vue +12 -0
  4. package/dist/components/Button.vue +16 -29
  5. package/dist/components/CodeBlock.vue +5 -4
  6. package/dist/components/CodeInline.vue +9 -8
  7. package/dist/components/Column.vue +17 -4
  8. package/dist/components/Container.vue +7 -11
  9. package/dist/components/Hr.vue +1 -1
  10. package/dist/components/Img.vue +39 -22
  11. package/dist/components/Layout.vue +1 -1
  12. package/dist/components/Markdown.vue +51 -24
  13. package/dist/components/QrCode.vue +2 -2
  14. package/dist/components/Section.vue +9 -6
  15. package/dist/components/Text.vue +2 -2
  16. package/dist/components/utils.d.ts +25 -1
  17. package/dist/components/utils.d.ts.map +1 -1
  18. package/dist/components/utils.js +29 -1
  19. package/dist/components/utils.js.map +1 -1
  20. package/dist/components/utils.ts +46 -0
  21. package/dist/composables/renderContext.d.ts +1 -0
  22. package/dist/composables/renderContext.d.ts.map +1 -1
  23. package/dist/composables/renderContext.js +1 -1
  24. package/dist/composables/renderContext.js.map +1 -1
  25. package/dist/composables/useConfig.d.ts +7 -0
  26. package/dist/composables/useConfig.d.ts.map +1 -1
  27. package/dist/composables/useConfig.js +8 -1
  28. package/dist/composables/useConfig.js.map +1 -1
  29. package/dist/composables/useCurrentTemplate.d.ts.map +1 -1
  30. package/dist/composables/useEvent.d.ts.map +1 -1
  31. package/dist/composables/useFont.js.map +1 -1
  32. package/dist/composables/useOutputPath.d.ts +17 -0
  33. package/dist/composables/useOutputPath.d.ts.map +1 -0
  34. package/dist/composables/useOutputPath.js +23 -0
  35. package/dist/composables/useOutputPath.js.map +1 -0
  36. package/dist/config/index.js.map +1 -1
  37. package/dist/events/index.d.ts.map +1 -1
  38. package/dist/events/index.js.map +1 -1
  39. package/dist/index.d.ts +2 -1
  40. package/dist/index.js +2 -1
  41. package/dist/plaintext.js.map +1 -1
  42. package/dist/plugins/postcss/mergeMediaQueries.js.map +1 -1
  43. package/dist/plugins/postcss/pruneVars.js.map +1 -1
  44. package/dist/plugins/postcss/quoteFontFamilies.d.ts.map +1 -1
  45. package/dist/plugins/postcss/quoteFontFamilies.js.map +1 -1
  46. package/dist/plugins/postcss/removeDeclarations.js.map +1 -1
  47. package/dist/plugins/postcss/resolveProps.d.ts.map +1 -1
  48. package/dist/plugins/postcss/resolveProps.js +0 -3
  49. package/dist/plugins/postcss/resolveProps.js.map +1 -1
  50. package/dist/plugins/postcss/tailwindCleanup.js.map +1 -1
  51. package/dist/prepare.js +1 -1
  52. package/dist/prepare.js.map +1 -1
  53. package/dist/render/active.d.ts.map +1 -1
  54. package/dist/render/createRenderer.d.ts +1 -0
  55. package/dist/render/createRenderer.d.ts.map +1 -1
  56. package/dist/render/createRenderer.js +19 -7
  57. package/dist/render/createRenderer.js.map +1 -1
  58. package/dist/render/index.js +1 -1
  59. package/dist/render/index.js.map +1 -1
  60. package/dist/render/injectFonts.js.map +1 -1
  61. package/dist/render/plugins/codeBlockExtract.d.ts.map +1 -1
  62. package/dist/render/plugins/codeBlockExtract.js.map +1 -1
  63. package/dist/render/plugins/markdownExtract.d.ts.map +1 -1
  64. package/dist/render/plugins/markdownExtract.js.map +1 -1
  65. package/dist/render/plugins/rawExtract.d.ts.map +1 -1
  66. package/dist/render/plugins/rawExtract.js.map +1 -1
  67. package/dist/render/plugins/rowSourceLocation.d.ts.map +1 -1
  68. package/dist/render/plugins/rowSourceLocation.js.map +1 -1
  69. package/dist/serve.js +3 -3
  70. package/dist/serve.js.map +1 -1
  71. package/dist/server/compatibility.d.ts.map +1 -1
  72. package/dist/server/compatibility.js.map +1 -1
  73. package/dist/server/linter.js.map +1 -1
  74. package/dist/server/sfc-utils.js.map +1 -1
  75. package/dist/server/ui/App.vue +3 -4
  76. package/dist/server/ui/main.css +25 -0
  77. package/dist/server/ui/pages/Preview.vue +5 -1
  78. package/dist/tests/render/_helpers.d.ts.map +1 -1
  79. package/dist/tests/render/_helpers.js.map +1 -1
  80. package/dist/transformers/addAttributes.js +2 -3
  81. package/dist/transformers/addAttributes.js.map +1 -1
  82. package/dist/transformers/base.d.ts +1 -1
  83. package/dist/transformers/base.d.ts.map +1 -1
  84. package/dist/transformers/base.js +5 -10
  85. package/dist/transformers/base.js.map +1 -1
  86. package/dist/transformers/columnWidth.d.ts.map +1 -1
  87. package/dist/transformers/columnWidth.js +2 -7
  88. package/dist/transformers/columnWidth.js.map +1 -1
  89. package/dist/transformers/entities.js.map +1 -1
  90. package/dist/transformers/filters/defaults.js.map +1 -1
  91. package/dist/transformers/filters/index.js.map +1 -1
  92. package/dist/transformers/format.js.map +1 -1
  93. package/dist/transformers/imgWidth.d.ts +20 -0
  94. package/dist/transformers/imgWidth.d.ts.map +1 -0
  95. package/dist/transformers/imgWidth.js +76 -0
  96. package/dist/transformers/imgWidth.js.map +1 -0
  97. package/dist/transformers/index.d.ts.map +1 -1
  98. package/dist/transformers/index.js +2 -0
  99. package/dist/transformers/index.js.map +1 -1
  100. package/dist/transformers/inlineCss.d.ts +3 -2
  101. package/dist/transformers/inlineCss.d.ts.map +1 -1
  102. package/dist/transformers/inlineCss.js.map +1 -1
  103. package/dist/transformers/inlineLink.js +1 -1
  104. package/dist/transformers/inlineLink.js.map +1 -1
  105. package/dist/transformers/minify.js.map +1 -1
  106. package/dist/transformers/minifyCodeInline.js.map +1 -1
  107. package/dist/transformers/msoPlaceholders.d.ts.map +1 -1
  108. package/dist/transformers/msoPlaceholders.js +2 -7
  109. package/dist/transformers/msoPlaceholders.js.map +1 -1
  110. package/dist/transformers/purgeCss.js.map +1 -1
  111. package/dist/transformers/replaceStrings.js.map +1 -1
  112. package/dist/transformers/safeSelectors.js.map +1 -1
  113. package/dist/transformers/shorthandCss.js.map +1 -1
  114. package/dist/transformers/tailwindComponent.js.map +1 -1
  115. package/dist/transformers/tailwindcss.js +1 -1
  116. package/dist/transformers/tailwindcss.js.map +1 -1
  117. package/dist/transformers/urlQuery.js.map +1 -1
  118. package/dist/types/config.d.ts +5 -4
  119. package/dist/types/config.d.ts.map +1 -1
  120. package/dist/utils/ast/serializer.js.map +1 -1
  121. package/dist/utils/compileTailwindCss.js.map +1 -1
  122. package/dist/utils/componentSources.js.map +1 -1
  123. package/dist/utils/cssBox.d.ts.map +1 -1
  124. package/dist/utils/cssBox.js +2 -7
  125. package/dist/utils/cssBox.js.map +1 -1
  126. package/dist/utils/decodeStyleEntities.js.map +1 -1
  127. package/dist/utils/url.js.map +1 -1
  128. package/dist/utils/watchPaths.js.map +1 -1
  129. package/node_modules/@clack/core/CHANGELOG.md +30 -0
  130. package/node_modules/@clack/core/dist/index.d.mts +109 -3
  131. package/node_modules/@clack/core/dist/index.mjs +972 -17
  132. package/node_modules/@clack/core/package.json +2 -1
  133. package/node_modules/@clack/prompts/CHANGELOG.md +42 -0
  134. package/node_modules/@clack/prompts/README.md +30 -9
  135. package/node_modules/@clack/prompts/dist/index.d.mts +458 -27
  136. package/node_modules/@clack/prompts/dist/index.mjs +1378 -141
  137. package/node_modules/@clack/prompts/package.json +2 -2
  138. package/node_modules/tinyexec/package.json +4 -4
  139. package/package.json +8 -7
  140. package/dist/components/Overlap.vue +0 -156
  141. package/dist/server/ui/components/Markdown.vue +0 -17
  142. package/node_modules/@clack/core/dist/index.mjs.map +0 -1
  143. package/node_modules/@clack/prompts/dist/index.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"resolveProps.js","names":[],"sources":["../../../src/plugins/postcss/resolveProps.ts"],"sourcesContent":["/**\n * postcss-resolve-props\n *\n * Resolves CSS custom properties (var() references) and removes\n * the consumed declarations, producing flat CSS for email inlining.\n *\n * Resolution order per var():\n * 1. Local declaration in the same rule\n * 2. :root declaration (top-level only, not inside @media)\n * 3. @property initial-value (Tailwind v4 composable defaults)\n * 4. Fallback value from the var() itself\n *\n * Handles nested var() in both values and fallbacks.\n * Replaces postcss-custom-properties with correct support for\n * local var declarations (Tailwind composable pattern, user CSS).\n */\n\nimport type { AtRule, Plugin, Root, Rule } from 'postcss'\n\n/** Check if a rule's selector includes :root */\nfunction isRootRule(rule: Rule): boolean {\n const sel = rule.selector\n return sel === ':root' || sel.includes(':root')\n}\n\n/** Check if a rule is inside @layer (not @media or other at-rules) */\nfunction isInLayer(rule: Rule): boolean {\n return rule.parent?.type === 'atrule' && (rule.parent as any).name === 'layer'\n}\n\n/**\n * Parse the first var() call in a string by counting parens.\n * Returns null if no var() found.\n */\nfunction findVar(value: string, startFrom = 0): {\n start: number\n end: number\n name: string\n fallback: string | undefined\n} | null {\n const idx = value.indexOf('var(', startFrom)\n if (idx === -1) return null\n\n let depth = 1\n let i = idx + 4\n let commaIdx = -1\n\n while (i < value.length && depth > 0) {\n const ch = value.charCodeAt(i)\n if (ch === 40 /* ( */) depth++\n else if (ch === 41 /* ) */) {\n if (--depth === 0) break\n } else if (ch === 44 /* , */ && depth === 1 && commaIdx === -1) {\n commaIdx = i\n }\n i++\n }\n\n if (depth !== 0) return null\n\n const name = commaIdx === -1\n ? value.slice(idx + 4, i).trim()\n : value.slice(idx + 4, commaIdx).trim()\n\n const fallback = commaIdx === -1\n ? undefined\n : value.slice(commaIdx + 1, i)\n\n return { start: idx, end: i + 1, name, fallback }\n}\n\n/**\n * Resolve all var() references in a value string.\n * Iterates until no resolvable var() remain.\n */\nfunction resolveValue(\n value: string,\n localVars: Map<string, string>,\n rootVars: Map<string, string>,\n propertyDefaults?: Map<string, string>,\n): string {\n let result = value\n let safety = 10\n\n while (safety-- > 0) {\n const v = findVar(result)\n if (!v) break\n\n const resolved = localVars.get(v.name) ?? rootVars.get(v.name) ?? propertyDefaults?.get(v.name)\n\n if (resolved !== undefined) {\n result = result.slice(0, v.start) + resolved + result.slice(v.end)\n } else if (v.fallback !== undefined) {\n result = result.slice(0, v.start) + v.fallback.trim() + result.slice(v.end)\n } else {\n /**\n * Unresolvable with no fallback — skip past it to avoid infinite loop.\n * Try to find another var() after this one.\n */\n const next = findVar(result, v.end)\n if (!next) break\n\n // Process the rest of the string from after this var()\n const tail = resolveValue(result.slice(v.end), localVars, rootVars, propertyDefaults)\n result = result.slice(0, v.end) + tail\n break\n }\n }\n\n return result\n}\n\nexport default (): Plugin => {\n return {\n postcssPlugin: 'postcss-resolve-props',\n\n Once(root: Root) {\n // Pass 0: collect @property initial-value defaults (Tailwind v4 composable pattern)\n const propertyDefaults = new Map<string, string>()\n\n root.walkAtRules('property', (rule: AtRule) => {\n const name = rule.params.trim()\n if (!name.startsWith('--')) return\n\n rule.walkDecls('initial-value', (decl) => {\n propertyDefaults.set(name, decl.value)\n })\n })\n\n /**\n * Pass 1: collect :root vars (top-level and inside @layer). Skip\n * :root inside @media — those are for dark mode and should\n * stay.\n */\n const rootVars = new Map<string, string>()\n\n root.walkRules((rule) => {\n if (!isRootRule(rule)) return\n\n /**\n * Allow :root at top level or inside @layer (Tailwind theme vars).\n * Skip :root inside @media or other non-layer at-rules.\n */\n if (rule.parent?.type !== 'root' && !isInLayer(rule)) return\n\n rule.each((node) => {\n if (node.type === 'decl' && node.prop.startsWith('--')) {\n rootVars.set(node.prop, node.value)\n }\n })\n })\n\n // Resolve :root vars referencing other :root vars\n if (rootVars.size > 0) {\n let changed = true\n let iterations = 5\n\n while (changed && iterations-- > 0) {\n changed = false\n for (const [name, value] of rootVars) {\n if (!value.includes('var(')) continue\n const resolved = resolveValue(value, rootVars, rootVars, propertyDefaults)\n if (resolved !== value) {\n rootVars.set(name, resolved)\n changed = true\n }\n }\n }\n }\n\n // Pass 2: resolve var() in all rules\n root.walkRules((rule) => {\n /**\n * Skip :root inside @media — those vars are for dark mode etc. and\n * must stay in the <style> tag as-is. Allow :root at top\n * level and inside @layer (processed in pass 3).\n */\n if (isRootRule(rule)) {\n if (rule.parent?.type !== 'root' && !isInLayer(rule)) return\n }\n\n // Collect local --* declarations (walk into nested @media etc.)\n const localVars = new Map<string, string>()\n const localDecls: Declaration[] = []\n let hasVarRefs = false\n\n rule.walk((node) => {\n if (node.type !== 'decl') return\n\n if (node.prop.startsWith('--')) {\n localDecls.push(node)\n } else if (node.value.includes('var(')) {\n hasVarRefs = true\n }\n })\n\n // Skip rules with no var() references and no local vars to clean up\n if (!hasVarRefs && localDecls.length === 0) return\n\n // Build local vars map — resolve values that reference other locals or :root\n for (const decl of localDecls) {\n const value = decl.value.includes('var(')\n ? resolveValue(decl.value, localVars, rootVars, propertyDefaults)\n : decl.value\n\n localVars.set(decl.prop, value)\n }\n\n // Resolve var() in non-custom-property declarations (walk into nested @media etc.)\n if (hasVarRefs) {\n rule.walk((node) => {\n if (node.type !== 'decl') return\n if (node.prop.startsWith('--')) return\n if (!node.value.includes('var(')) return\n\n const resolved = resolveValue(node.value, localVars, rootVars, propertyDefaults)\n\n if (resolved !== node.value) {\n // Clean up: collapse whitespace, trim\n const cleaned = resolved.replace(/ +/g, ' ').trim()\n\n if (cleaned) {\n node.value = cleaned\n } else {\n // Value resolved to empty — remove the declaration\n node.remove()\n }\n }\n })\n }\n\n // Remove local --* declarations (consumed or no longer needed)\n for (const decl of localDecls) {\n decl.remove()\n }\n })\n\n // Remove @property rules (not supported in email clients)\n root.walkAtRules('property', (rule) => {\n rule.remove()\n })\n\n // Pass 3: clean up :root (same scope as pass 1)\n root.walkRules((rule) => {\n if (!isRootRule(rule)) return\n if (rule.parent?.type !== 'root' && !isInLayer(rule)) return\n\n rule.each((node) => {\n if (node.type === 'decl' && node.prop.startsWith('--')) {\n node.remove()\n }\n })\n\n if (rule.nodes?.length === 0) {\n rule.remove()\n }\n })\n },\n }\n}\n\nexport const postcss = true\n"],"mappings":"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"}
1
+ {"version":3,"file":"resolveProps.js","names":[],"sources":["../../../src/plugins/postcss/resolveProps.ts"],"sourcesContent":["/**\n * postcss-resolve-props\n *\n * Resolves CSS custom properties (var() references) and removes\n * the consumed declarations, producing flat CSS for email inlining.\n *\n * Resolution order per var():\n * 1. Local declaration in the same rule\n * 2. :root declaration (top-level only, not inside @media)\n * 3. @property initial-value (Tailwind v4 composable defaults)\n * 4. Fallback value from the var() itself\n *\n * Handles nested var() in both values and fallbacks.\n * Replaces postcss-custom-properties with correct support for\n * local var declarations (Tailwind composable pattern, user CSS).\n */\n\nimport type { AtRule, Declaration, Plugin, Root, Rule } from 'postcss'\n\n/** Check if a rule's selector includes :root */\nfunction isRootRule(rule: Rule): boolean {\n const sel = rule.selector\n return sel === ':root' || sel.includes(':root')\n}\n\n/** Check if a rule is inside @layer (not @media or other at-rules) */\nfunction isInLayer(rule: Rule): boolean {\n return rule.parent?.type === 'atrule' && (rule.parent as any).name === 'layer'\n}\n\n/**\n * Parse the first var() call in a string by counting parens.\n * Returns null if no var() found.\n */\nfunction findVar(value: string, startFrom = 0): {\n start: number\n end: number\n name: string\n fallback: string | undefined\n} | null {\n const idx = value.indexOf('var(', startFrom)\n if (idx === -1) return null\n\n let depth = 1\n let i = idx + 4\n let commaIdx = -1\n\n while (i < value.length && depth > 0) {\n const ch = value.charCodeAt(i)\n if (ch === 40 /* ( */) depth++\n else if (ch === 41 /* ) */) {\n if (--depth === 0) break\n } else if (ch === 44 /* , */ && depth === 1 && commaIdx === -1) {\n commaIdx = i\n }\n i++\n }\n\n if (depth !== 0) return null\n\n const name = commaIdx === -1\n ? value.slice(idx + 4, i).trim()\n : value.slice(idx + 4, commaIdx).trim()\n\n const fallback = commaIdx === -1\n ? undefined\n : value.slice(commaIdx + 1, i)\n\n return { start: idx, end: i + 1, name, fallback }\n}\n\n/**\n * Resolve all var() references in a value string.\n * Iterates until no resolvable var() remain.\n */\nfunction resolveValue(\n value: string,\n localVars: Map<string, string>,\n rootVars: Map<string, string>,\n propertyDefaults?: Map<string, string>,\n): string {\n let result = value\n let safety = 10\n\n while (safety-- > 0) {\n const v = findVar(result)\n if (!v) break\n\n const resolved = localVars.get(v.name) ?? rootVars.get(v.name) ?? propertyDefaults?.get(v.name)\n\n if (resolved !== undefined) {\n result = result.slice(0, v.start) + resolved + result.slice(v.end)\n } else if (v.fallback !== undefined) {\n result = result.slice(0, v.start) + v.fallback.trim() + result.slice(v.end)\n } else {\n /**\n * Unresolvable with no fallback — skip past it to avoid infinite loop.\n * Try to find another var() after this one.\n */\n const next = findVar(result, v.end)\n if (!next) break\n\n // Process the rest of the string from after this var()\n const tail = resolveValue(result.slice(v.end), localVars, rootVars, propertyDefaults)\n result = result.slice(0, v.end) + tail\n break\n }\n }\n\n return result\n}\n\nexport default (): Plugin => {\n return {\n postcssPlugin: 'postcss-resolve-props',\n\n Once(root: Root) {\n // Pass 0: collect @property initial-value defaults (Tailwind v4 composable pattern)\n const propertyDefaults = new Map<string, string>()\n\n root.walkAtRules('property', (rule: AtRule) => {\n const name = rule.params.trim()\n if (!name.startsWith('--')) return\n\n rule.walkDecls('initial-value', (decl) => {\n propertyDefaults.set(name, decl.value)\n })\n })\n\n /**\n * Pass 1: collect :root vars (top-level and inside @layer). Skip\n * :root inside @media — those are for dark mode and should\n * stay.\n */\n const rootVars = new Map<string, string>()\n\n root.walkRules((rule) => {\n if (!isRootRule(rule)) return\n\n /**\n * Allow :root at top level or inside @layer (Tailwind theme vars).\n * Skip :root inside @media or other non-layer at-rules.\n */\n if (rule.parent?.type !== 'root' && !isInLayer(rule)) return\n\n rule.each((node) => {\n if (node.type === 'decl' && node.prop.startsWith('--')) {\n rootVars.set(node.prop, node.value)\n }\n })\n })\n\n // Resolve :root vars referencing other :root vars\n if (rootVars.size > 0) {\n let changed = true\n let iterations = 5\n\n while (changed && iterations-- > 0) {\n changed = false\n for (const [name, value] of rootVars) {\n if (!value.includes('var(')) continue\n const resolved = resolveValue(value, rootVars, rootVars, propertyDefaults)\n if (resolved !== value) {\n rootVars.set(name, resolved)\n changed = true\n }\n }\n }\n }\n\n // Pass 2: resolve var() in all rules\n root.walkRules((rule) => {\n /**\n * Skip :root inside @media — those vars are for dark mode etc. and\n * must stay in the <style> tag as-is. Allow :root at top\n * level and inside @layer (processed in pass 3).\n */\n if (isRootRule(rule)) {\n if (rule.parent?.type !== 'root' && !isInLayer(rule)) return\n }\n\n // Collect local --* declarations (walk into nested @media etc.)\n const localVars = new Map<string, string>()\n const localDecls: Declaration[] = []\n let hasVarRefs = false\n\n rule.walk((node) => {\n if (node.type !== 'decl') return\n\n if (node.prop.startsWith('--')) {\n localDecls.push(node)\n } else if (node.value.includes('var(')) {\n hasVarRefs = true\n }\n })\n\n // Skip rules with no var() references and no local vars to clean up\n if (!hasVarRefs && localDecls.length === 0) return\n\n // Build local vars map — resolve values that reference other locals or :root\n for (const decl of localDecls) {\n const value = decl.value.includes('var(')\n ? resolveValue(decl.value, localVars, rootVars, propertyDefaults)\n : decl.value\n\n localVars.set(decl.prop, value)\n }\n\n // Resolve var() in non-custom-property declarations (walk into nested @media etc.)\n if (hasVarRefs) {\n rule.walk((node) => {\n if (node.type !== 'decl') return\n if (node.prop.startsWith('--')) return\n if (!node.value.includes('var(')) return\n\n const resolved = resolveValue(node.value, localVars, rootVars, propertyDefaults)\n\n if (resolved !== node.value) {\n // Clean up: collapse whitespace, trim\n const cleaned = resolved.replace(/ +/g, ' ').trim()\n\n if (cleaned) {\n node.value = cleaned\n } else {\n // Value resolved to empty — remove the declaration\n node.remove()\n }\n }\n })\n }\n\n // Remove local --* declarations (consumed or no longer needed)\n for (const decl of localDecls) {\n decl.remove()\n }\n })\n\n // Remove @property rules (not supported in email clients)\n root.walkAtRules('property', (rule) => {\n rule.remove()\n })\n\n // Pass 3: drop now-empty :root rules (their vars were removed in pass 2)\n root.walkRules((rule) => {\n if (!isRootRule(rule)) return\n if (rule.parent?.type !== 'root' && !isInLayer(rule)) return\n\n if (rule.nodes?.length === 0) {\n rule.remove()\n }\n })\n },\n }\n}\n\nexport const postcss = true\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tailwindCleanup.js","names":[],"sources":["../../../src/plugins/postcss/tailwindCleanup.ts"],"sourcesContent":["import postcss from 'postcss'\nimport type { MaizzleConfig } from '../../types/config.ts'\n\nconst DEFAULT_SELECTORS = [':host', ':lang']\nconst DEFAULT_AT_RULES = ['layer', 'property']\n\n/**\n * Split a selector list on top-level commas only.\n *\n * Commas inside parenthesised groups like `:not(:is(:lang(ae), :lang(ar)))`\n * are not treated as selector separators.\n */\nfunction splitSelector(selector: string): string[] {\n const parts: string[] = []\n let depth = 0\n let start = 0\n\n for (let i = 0; i < selector.length; i++) {\n const ch = selector[i]\n if (ch === '(') depth++\n else if (ch === ')') depth--\n else if (ch === ',' && depth === 0) {\n parts.push(selector.slice(start, i).trim())\n start = i + 1\n }\n }\n\n parts.push(selector.slice(start).trim())\n\n return parts.filter(Boolean)\n}\n\n/**\n * Removes CSS rules whose every comma-separated selector part starts with\n * one of the configured prefixes (e.g. ':host', ':lang'). Rules with mixed\n * selectors have the unwanted parts stripped.\n *\n * Also removes entire at-rules by name (e.g. '@layer', '@property').\n *\n * Intended to clean up Tailwind's compiled output after lightningcss has\n * flattened all modern CSS syntax.\n */\nexport function tailwindCleanup(config: MaizzleConfig): postcss.Plugin[] {\n const selectors: string[] = config.postcss?.removeSelectors ?? DEFAULT_SELECTORS\n const atRules: string[] = config.postcss?.removeAtRules ?? DEFAULT_AT_RULES\n\n return [\n {\n postcssPlugin: 'tailwind-cleanup-selectors',\n Rule(rule) {\n const parts = splitSelector(rule.selector)\n const kept = parts.filter(p => !selectors.some(s => p === s || p.includes(`${s}(`)))\n if (kept.length === 0) {\n rule.remove()\n } else if (kept.length < parts.length) {\n rule.selector = kept.join(', ')\n }\n },\n },\n {\n postcssPlugin: 'tailwind-cleanup-at-rules',\n AtRule(rule) {\n if (!atRules.includes(rule.name)) return\n\n if (rule.nodes?.length) {\n // Unwrap: keep children, remove the at-rule wrapper\n rule.replaceWith(rule.nodes)\n } else {\n rule.remove()\n }\n },\n },\n {\n postcssPlugin: 'tailwind-cleanup-text-decoration',\n Declaration(decl) {\n if (decl.prop === 'text-decoration-line') {\n decl.prop = 'text-decoration'\n }\n },\n },\n ]\n}\n"],"mappings":";AAGA,MAAM,oBAAoB,CAAC,SAAS,OAAO;AAC3C,MAAM,mBAAmB,CAAC,SAAS,UAAU;;;;;;;AAQ7C,SAAS,cAAc,UAA4B;CACjD,MAAM,QAAkB,CAAC;CACzB,IAAI,QAAQ;CACZ,IAAI,QAAQ;CAEZ,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,KAAK,SAAS;EACpB,IAAI,OAAO,KAAK;OACX,IAAI,OAAO,KAAK;OAChB,IAAI,OAAO,OAAO,UAAU,GAAG;GAClC,MAAM,KAAK,SAAS,MAAM,OAAO,CAAC,EAAE,KAAK,CAAC;GAC1C,QAAQ,IAAI;EACd;CACF;CAEA,MAAM,KAAK,SAAS,MAAM,KAAK,EAAE,KAAK,CAAC;CAEvC,OAAO,MAAM,OAAO,OAAO;AAC7B;;;;;;;;;;;AAYA,SAAgB,gBAAgB,QAAyC;CACvE,MAAM,YAAsB,OAAO,SAAS,mBAAmB;CAC/D,MAAM,UAAoB,OAAO,SAAS,iBAAiB;CAE3D,OAAO;EACL;GACE,eAAe;GACf,KAAK,MAAM;IACT,MAAM,QAAQ,cAAc,KAAK,QAAQ;IACzC,MAAM,OAAO,MAAM,QAAO,MAAK,CAAC,UAAU,MAAK,MAAK,MAAM,KAAK,EAAE,SAAS,GAAG,EAAE,EAAE,CAAC,CAAC;IACnF,IAAI,KAAK,WAAW,GAClB,KAAK,OAAO;SACP,IAAI,KAAK,SAAS,MAAM,QAC7B,KAAK,WAAW,KAAK,KAAK,IAAI;GAElC;EACF;EACA;GACE,eAAe;GACf,OAAO,MAAM;IACX,IAAI,CAAC,QAAQ,SAAS,KAAK,IAAI,GAAG;IAElC,IAAI,KAAK,OAAO,QAEd,KAAK,YAAY,KAAK,KAAK;SAE3B,KAAK,OAAO;GAEhB;EACF;EACA;GACE,eAAe;GACf,YAAY,MAAM;IAChB,IAAI,KAAK,SAAS,wBAChB,KAAK,OAAO;GAEhB;EACF;CACF;AACF"}
1
+ {"version":3,"file":"tailwindCleanup.js","names":[],"sources":["../../../src/plugins/postcss/tailwindCleanup.ts"],"sourcesContent":["import postcss from 'postcss'\nimport type { MaizzleConfig } from '../../types/config.ts'\n\nconst DEFAULT_SELECTORS = [':host', ':lang']\nconst DEFAULT_AT_RULES = ['layer', 'property']\n\n/**\n * Split a selector list on top-level commas only.\n *\n * Commas inside parenthesised groups like `:not(:is(:lang(ae), :lang(ar)))`\n * are not treated as selector separators.\n */\nfunction splitSelector(selector: string): string[] {\n const parts: string[] = []\n let depth = 0\n let start = 0\n\n for (let i = 0; i < selector.length; i++) {\n const ch = selector[i]\n if (ch === '(') depth++\n else if (ch === ')') depth--\n else if (ch === ',' && depth === 0) {\n parts.push(selector.slice(start, i).trim())\n start = i + 1\n }\n }\n\n parts.push(selector.slice(start).trim())\n\n return parts.filter(Boolean)\n}\n\n/**\n * Removes CSS rules whose every comma-separated selector part starts with\n * one of the configured prefixes (e.g. ':host', ':lang'). Rules with mixed\n * selectors have the unwanted parts stripped.\n *\n * Also removes entire at-rules by name (e.g. '@layer', '@property').\n *\n * Intended to clean up Tailwind's compiled output after lightningcss has\n * flattened all modern CSS syntax.\n */\nexport function tailwindCleanup(config: MaizzleConfig): postcss.Plugin[] {\n const selectors: string[] = config.postcss?.removeSelectors ?? DEFAULT_SELECTORS\n const atRules: string[] = config.postcss?.removeAtRules ?? DEFAULT_AT_RULES\n\n return [\n {\n postcssPlugin: 'tailwind-cleanup-selectors',\n Rule(rule) {\n const parts = splitSelector(rule.selector)\n const kept = parts.filter(p => !selectors.some(s => p === s || p.includes(`${s}(`)))\n if (kept.length === 0) {\n rule.remove()\n } else if (kept.length < parts.length) {\n rule.selector = kept.join(', ')\n }\n },\n },\n {\n postcssPlugin: 'tailwind-cleanup-at-rules',\n AtRule(rule) {\n if (!atRules.includes(rule.name)) return\n\n if (rule.nodes?.length) {\n // Unwrap: keep children, remove the at-rule wrapper\n rule.replaceWith(rule.nodes)\n } else {\n rule.remove()\n }\n },\n },\n {\n postcssPlugin: 'tailwind-cleanup-text-decoration',\n Declaration(decl) {\n if (decl.prop === 'text-decoration-line') {\n decl.prop = 'text-decoration'\n }\n },\n },\n ]\n}\n"],"mappings":";AAGA,MAAM,oBAAoB,CAAC,SAAS,OAAO;AAC3C,MAAM,mBAAmB,CAAC,SAAS,UAAU;;;;;;;AAQ7C,SAAS,cAAc,UAA4B;CACjD,MAAM,QAAkB,CAAC;CACzB,IAAI,QAAQ;CACZ,IAAI,QAAQ;CAEZ,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,KAAK,SAAS;EACpB,IAAI,OAAO,KAAK;OACX,IAAI,OAAO,KAAK;OAChB,IAAI,OAAO,OAAO,UAAU,GAAG;GAClC,MAAM,KAAK,SAAS,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;GAC1C,QAAQ,IAAI;EACd;CACF;CAEA,MAAM,KAAK,SAAS,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC;CAEvC,OAAO,MAAM,OAAO,OAAO;AAC7B;;;;;;;;;;;AAYA,SAAgB,gBAAgB,QAAyC;CACvE,MAAM,YAAsB,OAAO,SAAS,mBAAmB;CAC/D,MAAM,UAAoB,OAAO,SAAS,iBAAiB;CAE3D,OAAO;EACL;GACE,eAAe;GACf,KAAK,MAAM;IACT,MAAM,QAAQ,cAAc,KAAK,QAAQ;IACzC,MAAM,OAAO,MAAM,QAAO,MAAK,CAAC,UAAU,MAAK,MAAK,MAAM,KAAK,EAAE,SAAS,GAAG,EAAE,EAAE,CAAC,CAAC;IACnF,IAAI,KAAK,WAAW,GAClB,KAAK,OAAO;SACP,IAAI,KAAK,SAAS,MAAM,QAC7B,KAAK,WAAW,KAAK,KAAK,IAAI;GAElC;EACF;EACA;GACE,eAAe;GACf,OAAO,MAAM;IACX,IAAI,CAAC,QAAQ,SAAS,KAAK,IAAI,GAAG;IAElC,IAAI,KAAK,OAAO,QAEd,KAAK,YAAY,KAAK,KAAK;SAE3B,KAAK,OAAO;GAEhB;EACF;EACA;GACE,eAAe;GACf,YAAY,MAAM;IAChB,IAAI,KAAK,SAAS,wBAChB,KAAK,OAAO;GAEhB;EACF;CACF;AACF"}
package/dist/prepare.js CHANGED
@@ -31,7 +31,7 @@ async function prepare(options = {}) {
31
31
  } finally {
32
32
  await renderer.close();
33
33
  }
34
- const dtsDir = isLaravel() ? resolve(process.cwd(), "resources/js/types/maizzle") : resolve(config.root, ".maizzle");
34
+ const dtsDir = isLaravel() ? resolve(process.cwd(), "resources/js/types/maizzle") : resolve(config.root ?? process.cwd(), ".maizzle");
35
35
  const displayPath = relative(process.cwd(), dtsDir) || dtsDir;
36
36
  spinner.stopAndPersist({
37
37
  symbol: "✅",
@@ -1 +1 @@
1
- {"version":3,"file":"prepare.js","names":[],"sources":["../src/prepare.ts"],"sourcesContent":["import { relative, resolve } from 'node:path'\nimport ora from 'ora'\nimport { resolveConfig } from './config/index.ts'\nimport { createRenderer } from './render/createRenderer.ts'\nimport { isLaravel } from './utils/detect.ts'\nimport { normalizeComponentSources } from './utils/componentSources.ts'\n\nexport interface PrepareOptions {\n /** Path to a Maizzle config file. */\n config?: string\n}\n\n/**\n * Generate IDE type definitions in `.maizzle/`\n * (`auto-imports.d.ts` and `components.d.ts`).\n *\n * Intended as a `postinstall` step so editors get autocompletion before the\n * user runs `dev` or `build`. Spins up the renderer with `dts: true`, runs\n * a trivial render to trigger the unplugin scans, then shuts down.\n */\nexport async function prepare(options: PrepareOptions = {}): Promise<void> {\n const spinner = ora({ text: 'Generating types...', spinner: 'circleHalves' }).start()\n\n const config = await resolveConfig(options.config)\n\n const renderer = await createRenderer({\n dts: true,\n markdown: config.markdown,\n root: config.root,\n componentDirs: normalizeComponentSources(config.components?.source, process.cwd()),\n vite: config.vite,\n })\n\n try {\n await renderer.render('<template><div></div></template>', config)\n } finally {\n await renderer.close()\n }\n\n const dtsDir = isLaravel()\n ? resolve(process.cwd(), 'resources/js/types/maizzle')\n : resolve(config.root, '.maizzle')\n const displayPath = relative(process.cwd(), dtsDir) || dtsDir\n\n spinner.stopAndPersist({\n symbol: '✅',\n text: `Types generated in ${displayPath}`,\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;AAoBA,eAAsB,QAAQ,UAA0B,CAAC,GAAkB;CACzE,MAAM,UAAU,IAAI;EAAE,MAAM;EAAuB,SAAS;CAAe,CAAC,EAAE,MAAM;CAEpF,MAAM,SAAS,MAAM,cAAc,QAAQ,MAAM;CAEjD,MAAM,WAAW,MAAM,eAAe;EACpC,KAAK;EACL,UAAU,OAAO;EACjB,MAAM,OAAO;EACb,eAAe,0BAA0B,OAAO,YAAY,QAAQ,QAAQ,IAAI,CAAC;EACjF,MAAM,OAAO;CACf,CAAC;CAED,IAAI;EACF,MAAM,SAAS,OAAO,oCAAoC,MAAM;CAClE,UAAU;EACR,MAAM,SAAS,MAAM;CACvB;CAEA,MAAM,SAAS,UAAU,IACrB,QAAQ,QAAQ,IAAI,GAAG,4BAA4B,IACnD,QAAQ,OAAO,MAAM,UAAU;CACnC,MAAM,cAAc,SAAS,QAAQ,IAAI,GAAG,MAAM,KAAK;CAEvD,QAAQ,eAAe;EACrB,QAAQ;EACR,MAAM,sBAAsB;CAC9B,CAAC;AACH"}
1
+ {"version":3,"file":"prepare.js","names":[],"sources":["../src/prepare.ts"],"sourcesContent":["import { relative, resolve } from 'node:path'\nimport ora from 'ora'\nimport { resolveConfig } from './config/index.ts'\nimport { createRenderer } from './render/createRenderer.ts'\nimport { isLaravel } from './utils/detect.ts'\nimport { normalizeComponentSources } from './utils/componentSources.ts'\n\nexport interface PrepareOptions {\n /** Path to a Maizzle config file. */\n config?: string\n}\n\n/**\n * Generate IDE type definitions in `.maizzle/`\n * (`auto-imports.d.ts` and `components.d.ts`).\n *\n * Intended as a `postinstall` step so editors get autocompletion before the\n * user runs `dev` or `build`. Spins up the renderer with `dts: true`, runs\n * a trivial render to trigger the unplugin scans, then shuts down.\n */\nexport async function prepare(options: PrepareOptions = {}): Promise<void> {\n const spinner = ora({ text: 'Generating types...', spinner: 'circleHalves' }).start()\n\n const config = await resolveConfig(options.config)\n\n const renderer = await createRenderer({\n dts: true,\n markdown: config.markdown,\n root: config.root,\n componentDirs: normalizeComponentSources(config.components?.source, process.cwd()),\n vite: config.vite,\n })\n\n try {\n await renderer.render('<template><div></div></template>', config)\n } finally {\n await renderer.close()\n }\n\n const dtsDir = isLaravel()\n ? resolve(process.cwd(), 'resources/js/types/maizzle')\n : resolve(config.root ?? process.cwd(), '.maizzle')\n const displayPath = relative(process.cwd(), dtsDir) || dtsDir\n\n spinner.stopAndPersist({\n symbol: '✅',\n text: `Types generated in ${displayPath}`,\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;AAoBA,eAAsB,QAAQ,UAA0B,CAAC,GAAkB;CACzE,MAAM,UAAU,IAAI;EAAE,MAAM;EAAuB,SAAS;CAAe,CAAC,CAAC,CAAC,MAAM;CAEpF,MAAM,SAAS,MAAM,cAAc,QAAQ,MAAM;CAEjD,MAAM,WAAW,MAAM,eAAe;EACpC,KAAK;EACL,UAAU,OAAO;EACjB,MAAM,OAAO;EACb,eAAe,0BAA0B,OAAO,YAAY,QAAQ,QAAQ,IAAI,CAAC;EACjF,MAAM,OAAO;CACf,CAAC;CAED,IAAI;EACF,MAAM,SAAS,OAAO,oCAAoC,MAAM;CAClE,UAAU;EACR,MAAM,SAAS,MAAM;CACvB;CAEA,MAAM,SAAS,UAAU,IACrB,QAAQ,QAAQ,IAAI,GAAG,4BAA4B,IACnD,QAAQ,OAAO,QAAQ,QAAQ,IAAI,GAAG,UAAU;CACpD,MAAM,cAAc,SAAS,QAAQ,IAAI,GAAG,MAAM,KAAK;CAEvD,QAAQ,eAAe;EACrB,QAAQ;EACR,MAAM,sBAAsB;CAC9B,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"active.d.ts","names":[],"sources":["../../src/render/active.ts"],"mappings":";;;iBAIgB,iBAAA,CAAkB,QAAyB,EAAf,QAAQ;AAAA,iBAIpC,iBAAA,CAAA,GAAqB,QAAQ"}
1
+ {"version":3,"file":"active.d.ts","names":[],"sources":["../../src/render/active.ts"],"mappings":";;;iBAIgB,iBAAA,CAAkB,QAAyB,EAAf,QAAQ;AAAA,iBAIpC,iBAAA,IAAqB,QAAQ"}
@@ -11,6 +11,7 @@ interface RenderedTemplate {
11
11
  templateConfig: MaizzleConfig;
12
12
  sfcEventHandlers: RenderContext['sfcEventHandlers'];
13
13
  plaintext?: RenderContext['plaintext'];
14
+ outputPath?: RenderContext['outputPath'];
14
15
  tailwindBlocks?: RenderContext['tailwindBlocks'];
15
16
  }
16
17
  interface Renderer {
@@ -1 +1 @@
1
- {"version":3,"file":"createRenderer.d.ts","names":[],"sources":["../../src/render/createRenderer.ts"],"mappings":";;;;;;;UAkCiB,gBAAA;EACf,IAAA;EACA,OAAA;EACA,cAAA,EAAgB,aAAA;EAChB,gBAAA,EAAkB,aAAA;EAClB,SAAA,GAAY,aAAA;EACZ,cAAA,GAAiB,aAAA;AAAA;AAAA,UAGF,QAAA;EACf,MAAA,CAAO,KAAA,WAAgB,SAAA,EAAW,MAAA,EAAQ,aAAA,GAAgB,OAAA,CAAQ,gBAAA;EAClE,UAAA,CAAW,QAAA,WAAmB,OAAA;EAC9B,aAAA,IAAiB,OAAA;EACjB,KAAA,IAAS,OAAA;AAAA;AAAA,UAGM,qBAAA;EAbC;EAehB,GAAA;EAdkB;EAgBlB,QAAA,GAAW,cAAA;EAfC;EAiBZ,IAAA;EAhBiB;;AAAa;AAGhC;EAkBE,aAAA,GAAgB,yBAAA;;EAEhB,IAAA,GAAO,YAAA;AAAA;;;;;;;iBASa,cAAA,CACpB,OAAA,GAAS,qBAAA,GACR,OAAA,CAAQ,QAAA"}
1
+ {"version":3,"file":"createRenderer.d.ts","names":[],"sources":["../../src/render/createRenderer.ts"],"mappings":";;;;;;;UAmCiB,gBAAA;EACf,IAAA;EACA,OAAA;EACA,cAAA,EAAgB,aAAA;EAChB,gBAAA,EAAkB,aAAA;EAClB,SAAA,GAAY,aAAA;EACZ,UAAA,GAAa,aAAA;EACb,cAAA,GAAiB,aAAA;AAAA;AAAA,UAGF,QAAA;EACf,MAAA,CAAO,KAAA,WAAgB,SAAA,EAAW,MAAA,EAAQ,aAAA,GAAgB,OAAA,CAAQ,gBAAA;EAClE,UAAA,CAAW,QAAA,WAAmB,OAAA;EAC9B,aAAA,IAAiB,OAAA;EACjB,KAAA,IAAS,OAAA;AAAA;AAAA,UAGM,qBAAA;EAdC;EAgBhB,GAAA;EAfkB;EAiBlB,QAAA,GAAW,cAAA;EAhBC;EAkBZ,IAAA;EAjBa;;;;EAsBb,aAAA,GAAgB,yBAAA;EAlBD;EAoBf,IAAA,GAAO,YAAA;AAAA;;;;;;;iBASa,cAAA,CACpB,OAAA,GAAS,qBAAA,GACR,OAAA,CAAQ,QAAA"}
@@ -6,6 +6,7 @@ import { rawExtract } from "./plugins/rawExtract.js";
6
6
  import { codeBlockExtract } from "./plugins/codeBlockExtract.js";
7
7
  import { markdownExtract } from "./plugins/markdownExtract.js";
8
8
  import { componentNameFromPath } from "../utils/componentSources.js";
9
+ import { shikiToCodeBlock } from "../components/utils.js";
9
10
  import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
10
11
  import { dirname, relative, resolve } from "node:path";
11
12
  import { glob, globSync } from "tinyglobby";
@@ -203,20 +204,30 @@ isCustomElement: (tag) => tag.startsWith("amp-") }
203
204
  });
204
205
  } },
205
206
  markdownSetup(md) {
206
- const wrapPre = (html) => `<table class="w-full"><tr><td class="max-w-0 mso-padding-alt-4">${html}</td></tr></table>\n`;
207
207
  const defaultFence = md.renderer.rules.fence;
208
- md.renderer.rules.fence = (...args) => {
209
- const result = defaultFence(...args);
210
- if (typeof result === "string") return wrapPre(result);
211
- return result.then(wrapPre);
212
- };
208
+ md.renderer.rules.fence = (...args) => Promise.resolve(defaultFence(...args)).then(shikiToCodeBlock);
213
209
  const defaultCodeBlock = md.renderer.rules.code_block;
214
- md.renderer.rules.code_block = (...args) => wrapPre(defaultCodeBlock(...args));
210
+ md.renderer.rules.code_block = (...args) => shikiToCodeBlock(defaultCodeBlock(...args));
215
211
  }
216
212
  })),
217
213
  AutoImport({
218
214
  dirs: [resolve(__dirname, "../composables"), resolve(__dirname, "../filters")],
219
215
  imports: ["vue", unheadVueComposablesImports],
216
+ /**
217
+ * unplugin-auto-import's default `include` doesn't match `.md`, so
218
+ * auto-imports (Vue, unhead and Maizzle composables/filters) were
219
+ * never injected into Markdown templates — `useConfig()` and friends
220
+ * threw at runtime. Extend the default list with `.md` (and its
221
+ * `?vue` script sub-requests) to mirror the `.md` coverage the
222
+ * Components plugin already declares below.
223
+ */
224
+ include: [
225
+ /\.[jt]sx?$/,
226
+ /\.vue$/,
227
+ /\.vue\?vue/,
228
+ /\.md$/,
229
+ /\.md\?vue/
230
+ ],
220
231
  dts: dts ? resolve(dtsDir, "auto-imports.d.ts") : false
221
232
  }),
222
233
  Components({
@@ -377,6 +388,7 @@ isCustomElement: (tag) => tag.startsWith("amp-") }
377
388
  templateConfig: renderContext.sfcConfig ? defu$1(renderContext.sfcConfig, config) : config,
378
389
  sfcEventHandlers: renderContext.sfcEventHandlers,
379
390
  plaintext: renderContext.plaintext,
391
+ outputPath: renderContext.outputPath,
380
392
  tailwindBlocks: renderContext.tailwindBlocks
381
393
  };
382
394
  },
@@ -1 +1 @@
1
- {"version":3,"file":"createRenderer.js","names":["relPath","merge"],"sources":["../../src/render/createRenderer.ts"],"sourcesContent":["import { dirname, relative as relPath, resolve } from 'node:path'\nimport { mkdirSync, writeFileSync, existsSync, rmSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { isLaravel } from '../utils/detect.ts'\nimport { rowSourceLocation } from './plugins/rowSourceLocation.ts'\nimport { rawExtract } from './plugins/rawExtract.ts'\nimport { codeBlockExtract } from './plugins/codeBlockExtract.ts'\nimport { markdownExtract } from './plugins/markdownExtract.ts'\nimport { createServer, mergeConfig, type InlineConfig, type Plugin } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport Markdown from 'unplugin-vue-markdown/vite'\nimport AutoImport from 'unplugin-auto-import/vite'\nimport Components from 'unplugin-vue-components/vite'\nimport { unheadVueComposablesImports } from '@unhead/vue'\nimport { defu as merge } from 'defu'\nimport { glob, globSync } from 'tinyglobby'\nimport { createSSRApp } from 'vue'\nimport { renderToString } from 'vue/server-renderer'\nimport { createHead } from '@unhead/vue/server'\nimport { MaizzleConfigKey } from '../composables/useConfig.ts'\nimport { RenderContextKey } from '../composables/renderContext.ts'\nimport { componentNameFromPath, type NormalizedComponentSource } from '../utils/componentSources.ts'\nimport type { Component, InjectionKey } from 'vue'\nimport type { MaizzleConfig, MarkdownConfig } from '../types/index.ts'\nimport type { MarkdownExit } from 'markdown-exit'\nimport type { RenderContext } from '../composables/renderContext.ts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nconst vuePkgDir = dirname(fileURLToPath(import.meta.resolve('vue/package.json')))\nconst vueServerRendererPkgDir = dirname(fileURLToPath(import.meta.resolve('@vue/server-renderer/package.json')))\nconst unheadVuePkgDir = resolve(dirname(fileURLToPath(import.meta.resolve('@unhead/vue'))), '..')\nconst vueRouterPkgDir = dirname(fileURLToPath(import.meta.resolve('vue-router/package.json')))\n\nexport interface RenderedTemplate {\n html: string\n doctype?: string\n templateConfig: MaizzleConfig\n sfcEventHandlers: RenderContext['sfcEventHandlers']\n plaintext?: RenderContext['plaintext']\n tailwindBlocks?: RenderContext['tailwindBlocks']\n}\n\nexport interface Renderer {\n render(input: string | Component, config: MaizzleConfig): Promise<RenderedTemplate>\n invalidate(filePath: string): Promise<void>\n invalidateAll(): Promise<void>\n close(): Promise<void>\n}\n\nexport interface CreateRendererOptions {\n /** Generate .d.ts files for auto-imports and components (default: false) */\n dts?: boolean\n /** Options passed to unplugin-vue-markdown */\n markdown?: MarkdownConfig\n /** Root directory for resolving user component dirs and .d.ts output */\n root?: string\n /**\n * Additional component sources to register for auto-import. Already\n * normalized — pass through `normalizeComponentSources()` first.\n */\n componentDirs?: NormalizedComponentSource[]\n /** User Vite config options to merge into the internal SSR server */\n vite?: InlineConfig\n}\n\n/**\n * Lightweight Vite SSR loader for rendering Vue SFC email templates.\n *\n * Uses only Vue + unplugin for component/auto-import resolution.\n * Tailwind CSS compilation is handled by the transformer pipeline.\n */\nexport async function createRenderer(\n options: CreateRendererOptions = {},\n): Promise<Renderer> {\n const { dts = false, markdown: markdownOptionsRaw, root = process.cwd(), componentDirs = [], vite: userViteConfig } = options\n const { shikiTheme = 'github-light', ...markdownOptions } = markdownOptionsRaw ?? {}\n\n /**\n * Sources without an explicit prefix get registered via unplugin's `dirs`\n * (folder name auto-namespaces). Sources with an explicit `prefix` are\n * registered through a custom resolver below so we fully control naming.\n */\n const dirSources = componentDirs.filter(s => s.prefix === undefined)\n const prefixedSources = componentDirs.filter(s => s.prefix !== undefined)\n\n /**\n * Absolute component dirs — used to skip auto-wrapping `.md` files that\n * are imported as reusable components (vs. entry-point email templates).\n */\n const componentDirsAbs = [resolve(root, 'components'), ...componentDirs.map(s => s.path)]\n\n const dtsDir = isLaravel()\n ? resolve(process.cwd(), 'resources/js/types/maizzle')\n : resolve(root, '.maizzle')\n\n /**\n * Built-in framework components live at this path. When a user provides\n * a top-level file with the same (PascalCased) basename, drop the\n * built-in from unplugin's scan so the user's component is the only\n * candidate. This avoids the \"naming conflicts\" warning and the\n * alphabetical-glob ordering pitfall that decides who wins when\n * both are present in `dirs`.\n */\n const frameworkComponentsDir = resolve(__dirname, '../components')\n\n function topLevelBasenamesLower(dir: string): Set<string> {\n if (!existsSync(dir)) return new Set()\n const files = globSync(['*.vue', '*.md'], { cwd: dir, absolute: false })\n return new Set(files.map(f => f.replace(/\\.(vue|md)$/, '').toLowerCase()))\n }\n\n const frameworkFiles = globSync(['*.vue', '*.md'], { cwd: frameworkComponentsDir, absolute: false })\n const frameworkByLower = new Map(\n frameworkFiles.map(f => [f.replace(/\\.(vue|md)$/, '').toLowerCase(), f]),\n )\n\n const shadowedNames = new Set<string>()\n for (const dir of [resolve(root, 'components'), ...dirSources.map(s => s.path)]) {\n for (const lower of topLevelBasenamesLower(dir)) {\n if (frameworkByLower.has(lower)) shadowedNames.add(lower)\n }\n }\n\n const frameworkExcludes = [...shadowedNames]\n .map(lower => `${frameworkComponentsDir}/${frameworkByLower.get(lower)}`)\n\n /**\n * Pre-scanned name → absolute-path map for prefixed sources. Rebuilt\n * on file add/unlink via the watcher hook plugin further down. Drives\n * the runtime resolver and the d.ts we emit for IDE autocompletion.\n */\n const prefixedNameMap = new Map<string, string>()\n\n async function scanPrefixedSources(): Promise<void> {\n prefixedNameMap.clear()\n const seen = new Map<string, string>()\n for (const source of prefixedSources) {\n const files = await glob(['**/*.vue', '**/*.md'], { cwd: source.path, absolute: true })\n for (const file of files) {\n const name = componentNameFromPath({\n filePath: file,\n dirRoot: source.path,\n prefix: source.prefix,\n pathPrefix: source.pathPrefix,\n })\n const existing = seen.get(name)\n if (existing && existing !== file) {\n throw new Error(\n `[maizzle] Component name collision: \"${name}\" resolved from both \"${existing}\" and \"${file}\". `\n + 'Rename one of the files or split them into separate sources with distinct prefixes.',\n )\n }\n seen.set(name, file)\n prefixedNameMap.set(name, file)\n }\n }\n }\n\n await scanPrefixedSources()\n\n const prefixedResolver = (name: string) => prefixedNameMap.get(name)\n\n /**\n * unplugin-vue-components' own d.ts only covers components found via\n * `dirs`; its `types` option emits named-import entries which break\n * for SFC `default` exports. Write a sibling d.ts for prefixed\n * sources so editors get correct autocompletion via TypeScript\n * interface merging on `vue.GlobalComponents`.\n */\n const prefixedDtsPath = resolve(dtsDir, 'prefixed-components.d.ts')\n\n function writePrefixedDts(): void {\n if (!dts) return\n if (prefixedNameMap.size === 0) {\n if (existsSync(prefixedDtsPath)) rmSync(prefixedDtsPath)\n return\n }\n const dtsBase = dirname(prefixedDtsPath)\n mkdirSync(dtsBase, { recursive: true })\n const lines = Array.from(prefixedNameMap.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, file]) => {\n const relativePath = relPath(dtsBase, file).replace(/\\\\/g, '/')\n const importPath = relativePath.startsWith('.') ? relativePath : `./${relativePath}`\n return ` ${name}: typeof import('${importPath}')['default']`\n })\n .join('\\n')\n writeFileSync(\n prefixedDtsPath,\n `/* eslint-disable */\\n// @ts-nocheck\\n// biome-ignore lint: disable\\n// oxlint-disable\\n// Generated by Maizzle for prefixed component sources\\n\\nexport {}\\n\\n/* prettier-ignore */\\ndeclare module 'vue' {\\n export interface GlobalComponents {\\n${lines}\\n }\\n}\\n`,\n )\n }\n\n writePrefixedDts()\n\n /**\n * Watches prefixed source dirs and rebuilds {@link prefixedNameMap} when\n * files are added/removed. Vite's watcher already covers `dirSources`\n * via unplugin-vue-components' own filesystem hooks.\n */\n const prefixedSourceWatcher: Plugin | null = prefixedSources.length > 0\n ? {\n name: 'maizzle:prefixed-component-watcher',\n configureServer(server) {\n for (const source of prefixedSources) {\n server.watcher.add(source.path)\n }\n const refresh = async (file: string) => {\n if (!prefixedSources.some(s => file.startsWith(`${s.path}/`))) return\n if (!/\\.(vue|md)$/.test(file)) return\n await scanPrefixedSources()\n writePrefixedDts()\n }\n server.watcher.on('add', refresh)\n server.watcher.on('unlink', refresh)\n },\n }\n : null\n\n const VIRTUAL_SFC_ID = 'virtual:maizzle-sfc.vue'\n let virtualSfcSource = ''\n\n /**\n * Never load the host project's vite.config.ts here. Doing so pulls\n * every host plugin (Nitro, TanStack Start, the Maizzle plugin\n * itself, …) into this isolated SSR pipeline, where they override\n * env factories, re-trigger configureServer hooks, and break\n * Vite's hot channel wiring. Users who need extra Vite plugins\n * for SSR pass them explicitly via the `vite` option.\n */\n const maizzleConfig: InlineConfig = {\n configFile: false,\n plugins: [\n rawExtract(),\n codeBlockExtract(),\n markdownExtract(),\n rowSourceLocation(),\n {\n name: 'maizzle:virtual-sfc',\n resolveId(id) {\n if (id === VIRTUAL_SFC_ID) return id\n },\n load(id) {\n if (id === VIRTUAL_SFC_ID) return virtualSfcSource\n },\n },\n vue({\n include: [/\\.vue$/, /\\.md$/],\n template: {\n transformAssetUrls: false,\n compilerOptions: {\n /**\n * AMP4Email tags (<amp-carousel>, <amp-img>, <amp-list> ...)\n * render verbatim — skip the component resolver. Users who\n * want to wrap an amp tag in a Vue component should register\n * it under a PascalCase name (e.g. `components/AmpCarousel.vue`\n * → `<AmpCarousel>`).\n */\n isCustomElement: (tag: string) => tag.startsWith('amp-'),\n },\n },\n }),\n Markdown(merge(markdownOptions ?? {}, {\n headEnabled: true,\n wrapperDiv: false,\n wrapperClasses: 'prose',\n wrapperComponent: (id: string, raw: string) => {\n const fm = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---/)?.[1]\n const layout = fm?.match(/^[ \\t]*layout[ \\t]*:[ \\t]*['\"]?([A-Za-z][\\w-]*|false|none)['\"]?[ \\t]*$/m)?.[1]\n if (layout === 'false' || layout === 'none') return null\n if (layout) return layout\n /**\n * No `layout:` set — default to the built-in `MarkdownLayout`\n * for entry-template `.md` files. Skip for `.md` files inside\n * component dirs, which are reusable fragments imported into\n * other templates.\n */\n const inComponentDir = componentDirsAbs.some(d => id === d || id.startsWith(`${d}/`))\n return inComponentDir ? null : 'MarkdownLayout'\n },\n markdownOptions: {\n async highlight(code: string, lang: string) {\n const { codeToHtml } = await import('shiki')\n return codeToHtml(code, { lang, theme: shikiTheme })\n },\n },\n markdownSetup(md: MarkdownExit) {\n const wrapPre = (html: string) =>\n `<table class=\"w-full\"><tr><td class=\"max-w-0 mso-padding-alt-4\">${html}</td></tr></table>\\n`\n\n const defaultFence = md.renderer.rules.fence!\n md.renderer.rules.fence = (...args) => {\n const result = defaultFence(...args)\n if (typeof result === 'string') return wrapPre(result)\n return result.then(wrapPre)\n }\n\n const defaultCodeBlock = md.renderer.rules.code_block!\n md.renderer.rules.code_block = (...args) => wrapPre(defaultCodeBlock(...args) as string)\n },\n })),\n AutoImport({\n dirs: [\n resolve(__dirname, '../composables'),\n resolve(__dirname, '../filters'),\n ],\n imports: ['vue', unheadVueComposablesImports],\n dts: dts ? resolve(dtsDir, 'auto-imports.d.ts') : false,\n }),\n Components({\n extensions: ['vue', 'md'],\n include: [/\\.vue$/, /\\.vue\\?vue/, /\\.md$/],\n dirs: [\n frameworkComponentsDir,\n resolve(root, 'components'),\n ...dirSources.map(s => s.path),\n ],\n /**\n * Drop built-in component files whose name the user has shadowed.\n * This makes the user's version the only match — no \"naming\n * conflicts\" warning, no glob-ordering games.\n */\n globsExclude: frameworkExcludes,\n directoryAsNamespace: true,\n collapseSamePrefixes: true,\n resolvers: prefixedSources.length > 0 ? [prefixedResolver] : undefined,\n dts: dts ? resolve(dtsDir, 'components.d.ts') : false,\n }),\n ...(prefixedSourceWatcher ? [prefixedSourceWatcher] : []),\n ],\n resolve: {\n alias: {\n 'vue/server-renderer': resolve(vueServerRendererPkgDir, 'dist/server-renderer.esm-bundler.js'),\n 'vue': resolve(vuePkgDir, 'dist/vue.runtime.esm-bundler.js'),\n 'vue-router': vueRouterPkgDir,\n '@unhead/vue/server': resolve(unheadVuePkgDir, 'dist/server.mjs'),\n '@unhead/vue': resolve(unheadVuePkgDir, 'dist/index.mjs'),\n },\n },\n server: {\n middlewareMode: true,\n hmr: false,\n /**\n * Watcher is required so unplugin-vue-components and unplugin-auto-import\n * detect added/removed component files and rewrite their .d.ts on the fly.\n * (We only render via SSR — HMR is off, but chokidar still drives plugins.)\n */\n fs: {\n allow: [process.cwd(), root, ...componentDirs.map(s => s.path), vuePkgDir, vueServerRendererPkgDir, unheadVuePkgDir, vueRouterPkgDir],\n },\n },\n appType: 'custom',\n logLevel: 'silent',\n optimizeDeps: {\n noDiscovery: true,\n },\n }\n\n /**\n * Merge user's vite config (from config.vite) under Maizzle's config.\n * mergeConfig(a, b) → b overrides a for scalars, arrays concatenate.\n * This ensures Maizzle's critical settings (middlewareMode, appType,\n * etc.) always win, while user plugins and other options remain.\n */\n const finalConfig = userViteConfig\n ? mergeConfig(userViteConfig, maizzleConfig)\n : maizzleConfig\n\n const server = await createServer(finalConfig)\n\n return {\n async render(input: string | Component, config: MaizzleConfig): Promise<RenderedTemplate> {\n let component: Component\n let configKey: InjectionKey<MaizzleConfig>\n let contextKey: InjectionKey<RenderContext>\n\n if (typeof input === 'string') {\n /**\n * String input goes through Vite — must use ssrLoadModule for\n * injection keys so they share the same module instance as SFC.\n */\n const configModule = await server.ssrLoadModule(resolve(__dirname, '../composables/useConfig'))\n const contextModule = await server.ssrLoadModule(resolve(__dirname, '../composables/renderContext'))\n configKey = configModule.MaizzleConfigKey\n contextKey = contextModule.RenderContextKey\n\n if (input.includes('<template') || input.includes('<script')) {\n virtualSfcSource = input\n const mod = server.moduleGraph.getModuleById(VIRTUAL_SFC_ID)\n if (mod) server.moduleGraph.invalidateModule(mod)\n component = (await server.ssrLoadModule(VIRTUAL_SFC_ID)).default\n } else {\n component = (await server.ssrLoadModule(input)).default\n }\n } else {\n // Pre-compiled component — use directly imported keys\n component = input\n configKey = MaizzleConfigKey\n contextKey = RenderContextKey\n }\n\n const renderContext: RenderContext = {\n doctype: undefined,\n sfcConfig: undefined,\n sfcEventHandlers: [],\n }\n\n const head = createHead({ disableDefaults: true })\n const app = createSSRApp(component)\n app.use(head)\n\n // Register user Vue plugins, directives, and global properties\n if (config.vue) {\n const plugins = typeof config.vue.plugins === 'function'\n ? config.vue.plugins()\n : config.vue.plugins ?? []\n for (const plugin of plugins) {\n app.use(plugin)\n }\n for (const [name, directive] of Object.entries(config.vue.directives ?? {})) {\n app.directive(name, directive)\n }\n Object.assign(app.config.globalProperties, config.vue.globalProperties)\n }\n\n app.provide(configKey, config)\n app.provide(contextKey, renderContext)\n\n const ssrContext: Record<string, any> = {}\n let html: string = await renderToString(app, ssrContext)\n\n const { headTags, bodyTags, bodyTagsOpen, htmlAttrs, bodyAttrs } = head.render()\n\n // Inject head entries into the rendered HTML\n if (htmlAttrs) {\n html = html.replace(/<html([^>]*)>/, `<html$1 ${htmlAttrs}>`)\n }\n if (headTags) {\n html = html.replace('</head>', `${headTags}\\n</head>`)\n }\n if (bodyAttrs) {\n html = html.replace(/<body([^>]*)>/, `<body$1 ${bodyAttrs}>`)\n }\n if (bodyTagsOpen) {\n html = html.replace(/<body([^>]*)>/, `<body$1>\\n${bodyTagsOpen}`)\n }\n if (bodyTags) {\n html = html.replace('</body>', `${bodyTags}\\n</body>`)\n }\n\n // Inject SSR teleport content into their target elements\n const hasTeleports = ssrContext.teleports && Object.keys(ssrContext.teleports).length > 0\n const hasFonts = (renderContext.fonts?.length ?? 0) > 0\n\n if (hasTeleports || hasFonts) {\n const { parse: parseDom, serialize: serializeDom, walk } = await import('../utils/ast/index.ts')\n let dom = parseDom(html)\n\n if (hasTeleports) {\n for (const [rawTarget, content] of Object.entries(ssrContext.teleports) as [string, string][]) {\n if (!content) continue\n\n const prepend = rawTarget.endsWith(':start')\n const target = prepend ? rawTarget.slice(0, -6) : rawTarget\n const targetChildren = parseDom(content)\n\n walk(dom, (node) => {\n const el = node as import('domhandler').Element\n\n if (!el.name) return\n\n const matched\n = target === el.name\n || (target.startsWith('#') && el.attribs?.id === target.slice(1))\n || (target.startsWith('.') && el.attribs?.class?.split(/\\s+/).includes(target.slice(1)))\n\n if (matched) {\n for (const child of targetChildren) {\n child.parent = el as any\n }\n\n el.children = prepend\n ? [...targetChildren, ...(el.children || [])] as any\n : [...(el.children || []), ...targetChildren] as any\n }\n })\n }\n }\n\n if (hasFonts) {\n const { injectFonts } = await import('./injectFonts.ts')\n injectFonts(dom, renderContext.fonts!, parseDom, walk)\n }\n\n html = serializeDom(dom)\n }\n\n // Inject preheader text from usePreheader() composable\n if (renderContext.preheader) {\n const { text, fillerCount } = renderContext.preheader\n const filler = '\\u2007\\uFEFF\\u034F '.repeat(fillerCount)\n const previewHtml = `<div style=\"display:none\">${text}${filler}\\u00A0</div>`\n html = html.replace(/<body([^>]*)>/, `<body$1>${previewHtml}`)\n }\n\n /**\n * Strip Vue SSR fragment markers + teleport anchor comments. These\n * are rendering hygiene, not transformer concerns — must run\n * regardless of `useTransformers` state. Fragment markers contain\n * `-->`, which would prematurely terminate MSO conditional\n * comments downstream.\n */\n html = html\n .replaceAll('<!--[-->', '')\n .replaceAll('<!--]-->', '')\n .replaceAll('<!--teleport start anchor-->', '')\n .replaceAll('<!--teleport anchor-->', '')\n .replaceAll('<!--teleport start-->', '')\n .replaceAll('<!--teleport end-->', '')\n\n return {\n html,\n doctype: renderContext.doctype,\n /**\n * Layer sfcConfig over config — sfcConfig is a partial override\n * emitted by composables (defineConfig, useTransformers, etc.).\n * A naive replacement (`sfcConfig ?? config`) drops defaults\n * from the resolved config when the SFC only sets a single\n * key, since the composables' inject() of globalConfig can\n * return `{}` in dev when ssrLoadModule and the SFC's\n * auto-imported module resolve to different module\n * instances (different Symbols).\n */\n templateConfig: renderContext.sfcConfig ? merge(renderContext.sfcConfig, config) : config,\n sfcEventHandlers: renderContext.sfcEventHandlers,\n plaintext: renderContext.plaintext,\n tailwindBlocks: renderContext.tailwindBlocks,\n }\n },\n\n async invalidate(filePath: string): Promise<void> {\n const mod = await server.moduleGraph.getModuleByUrl(filePath)\n if (mod) {\n server.moduleGraph.invalidateModule(mod)\n }\n },\n\n async invalidateAll(): Promise<void> {\n for (const mod of server.moduleGraph.idToModuleMap.values()) {\n server.moduleGraph.invalidateModule(mod)\n }\n },\n\n async close(): Promise<void> {\n await server.close()\n /**\n * unplugin-auto-import schedules a 500ms-throttled, fire-and-forget\n * d.ts write on its first scan. server.close() doesn't drain that\n * pending write, so callers tearing down the working dir right\n * after close (tests, ephemeral build pipelines) can race the\n * mkdir against a missing parent directory. Wait one throttle\n * window past close so the lingering write resolves while\n * the dir still exists.\n */\n if (dts) {\n await new Promise(resolve => setTimeout(resolve, 600))\n }\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"createRenderer.js","names":["relPath","merge"],"sources":["../../src/render/createRenderer.ts"],"sourcesContent":["import { dirname, relative as relPath, resolve } from 'node:path'\nimport { mkdirSync, writeFileSync, existsSync, rmSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { isLaravel } from '../utils/detect.ts'\nimport { rowSourceLocation } from './plugins/rowSourceLocation.ts'\nimport { rawExtract } from './plugins/rawExtract.ts'\nimport { codeBlockExtract } from './plugins/codeBlockExtract.ts'\nimport { markdownExtract } from './plugins/markdownExtract.ts'\nimport { createServer, mergeConfig, type InlineConfig, type Plugin } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport Markdown from 'unplugin-vue-markdown/vite'\nimport AutoImport from 'unplugin-auto-import/vite'\nimport Components from 'unplugin-vue-components/vite'\nimport { unheadVueComposablesImports } from '@unhead/vue'\nimport { defu as merge } from 'defu'\nimport { glob, globSync } from 'tinyglobby'\nimport { createSSRApp } from 'vue'\nimport { renderToString } from 'vue/server-renderer'\nimport { createHead } from '@unhead/vue/server'\nimport { MaizzleConfigKey } from '../composables/useConfig.ts'\nimport { RenderContextKey } from '../composables/renderContext.ts'\nimport { componentNameFromPath, type NormalizedComponentSource } from '../utils/componentSources.ts'\nimport { shikiToCodeBlock } from '../components/utils.ts'\nimport type { Component, InjectionKey } from 'vue'\nimport type { MaizzleConfig, MarkdownConfig } from '../types/index.ts'\nimport type { MarkdownExit } from 'markdown-exit'\nimport type { RenderContext } from '../composables/renderContext.ts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nconst vuePkgDir = dirname(fileURLToPath(import.meta.resolve('vue/package.json')))\nconst vueServerRendererPkgDir = dirname(fileURLToPath(import.meta.resolve('@vue/server-renderer/package.json')))\nconst unheadVuePkgDir = resolve(dirname(fileURLToPath(import.meta.resolve('@unhead/vue'))), '..')\nconst vueRouterPkgDir = dirname(fileURLToPath(import.meta.resolve('vue-router/package.json')))\n\nexport interface RenderedTemplate {\n html: string\n doctype?: string\n templateConfig: MaizzleConfig\n sfcEventHandlers: RenderContext['sfcEventHandlers']\n plaintext?: RenderContext['plaintext']\n outputPath?: RenderContext['outputPath']\n tailwindBlocks?: RenderContext['tailwindBlocks']\n}\n\nexport interface Renderer {\n render(input: string | Component, config: MaizzleConfig): Promise<RenderedTemplate>\n invalidate(filePath: string): Promise<void>\n invalidateAll(): Promise<void>\n close(): Promise<void>\n}\n\nexport interface CreateRendererOptions {\n /** Generate .d.ts files for auto-imports and components (default: false) */\n dts?: boolean\n /** Options passed to unplugin-vue-markdown */\n markdown?: MarkdownConfig\n /** Root directory for resolving user component dirs and .d.ts output */\n root?: string\n /**\n * Additional component sources to register for auto-import. Already\n * normalized — pass through `normalizeComponentSources()` first.\n */\n componentDirs?: NormalizedComponentSource[]\n /** User Vite config options to merge into the internal SSR server */\n vite?: InlineConfig\n}\n\n/**\n * Lightweight Vite SSR loader for rendering Vue SFC email templates.\n *\n * Uses only Vue + unplugin for component/auto-import resolution.\n * Tailwind CSS compilation is handled by the transformer pipeline.\n */\nexport async function createRenderer(\n options: CreateRendererOptions = {},\n): Promise<Renderer> {\n const { dts = false, markdown: markdownOptionsRaw, root = process.cwd(), componentDirs = [], vite: userViteConfig } = options\n const { shikiTheme = 'github-light', ...markdownOptions } = markdownOptionsRaw ?? {}\n\n /**\n * Sources without an explicit prefix get registered via unplugin's `dirs`\n * (folder name auto-namespaces). Sources with an explicit `prefix` are\n * registered through a custom resolver below so we fully control naming.\n */\n const dirSources = componentDirs.filter(s => s.prefix === undefined)\n const prefixedSources = componentDirs.filter(s => s.prefix !== undefined)\n\n /**\n * Absolute component dirs — used to skip auto-wrapping `.md` files that\n * are imported as reusable components (vs. entry-point email templates).\n */\n const componentDirsAbs = [resolve(root, 'components'), ...componentDirs.map(s => s.path)]\n\n const dtsDir = isLaravel()\n ? resolve(process.cwd(), 'resources/js/types/maizzle')\n : resolve(root, '.maizzle')\n\n /**\n * Built-in framework components live at this path. When a user provides\n * a top-level file with the same (PascalCased) basename, drop the\n * built-in from unplugin's scan so the user's component is the only\n * candidate. This avoids the \"naming conflicts\" warning and the\n * alphabetical-glob ordering pitfall that decides who wins when\n * both are present in `dirs`.\n */\n const frameworkComponentsDir = resolve(__dirname, '../components')\n\n function topLevelBasenamesLower(dir: string): Set<string> {\n if (!existsSync(dir)) return new Set()\n const files = globSync(['*.vue', '*.md'], { cwd: dir, absolute: false })\n return new Set(files.map(f => f.replace(/\\.(vue|md)$/, '').toLowerCase()))\n }\n\n const frameworkFiles = globSync(['*.vue', '*.md'], { cwd: frameworkComponentsDir, absolute: false })\n const frameworkByLower = new Map(\n frameworkFiles.map(f => [f.replace(/\\.(vue|md)$/, '').toLowerCase(), f]),\n )\n\n const shadowedNames = new Set<string>()\n for (const dir of [resolve(root, 'components'), ...dirSources.map(s => s.path)]) {\n for (const lower of topLevelBasenamesLower(dir)) {\n if (frameworkByLower.has(lower)) shadowedNames.add(lower)\n }\n }\n\n const frameworkExcludes = [...shadowedNames]\n .map(lower => `${frameworkComponentsDir}/${frameworkByLower.get(lower)}`)\n\n /**\n * Pre-scanned name → absolute-path map for prefixed sources. Rebuilt\n * on file add/unlink via the watcher hook plugin further down. Drives\n * the runtime resolver and the d.ts we emit for IDE autocompletion.\n */\n const prefixedNameMap = new Map<string, string>()\n\n async function scanPrefixedSources(): Promise<void> {\n prefixedNameMap.clear()\n const seen = new Map<string, string>()\n for (const source of prefixedSources) {\n const files = await glob(['**/*.vue', '**/*.md'], { cwd: source.path, absolute: true })\n for (const file of files) {\n const name = componentNameFromPath({\n filePath: file,\n dirRoot: source.path,\n prefix: source.prefix,\n pathPrefix: source.pathPrefix,\n })\n const existing = seen.get(name)\n if (existing && existing !== file) {\n throw new Error(\n `[maizzle] Component name collision: \"${name}\" resolved from both \"${existing}\" and \"${file}\". `\n + 'Rename one of the files or split them into separate sources with distinct prefixes.',\n )\n }\n seen.set(name, file)\n prefixedNameMap.set(name, file)\n }\n }\n }\n\n await scanPrefixedSources()\n\n const prefixedResolver = (name: string) => prefixedNameMap.get(name)\n\n /**\n * unplugin-vue-components' own d.ts only covers components found via\n * `dirs`; its `types` option emits named-import entries which break\n * for SFC `default` exports. Write a sibling d.ts for prefixed\n * sources so editors get correct autocompletion via TypeScript\n * interface merging on `vue.GlobalComponents`.\n */\n const prefixedDtsPath = resolve(dtsDir, 'prefixed-components.d.ts')\n\n function writePrefixedDts(): void {\n if (!dts) return\n if (prefixedNameMap.size === 0) {\n if (existsSync(prefixedDtsPath)) rmSync(prefixedDtsPath)\n return\n }\n const dtsBase = dirname(prefixedDtsPath)\n mkdirSync(dtsBase, { recursive: true })\n const lines = Array.from(prefixedNameMap.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, file]) => {\n const relativePath = relPath(dtsBase, file).replace(/\\\\/g, '/')\n const importPath = relativePath.startsWith('.') ? relativePath : `./${relativePath}`\n return ` ${name}: typeof import('${importPath}')['default']`\n })\n .join('\\n')\n writeFileSync(\n prefixedDtsPath,\n `/* eslint-disable */\\n// @ts-nocheck\\n// biome-ignore lint: disable\\n// oxlint-disable\\n// Generated by Maizzle for prefixed component sources\\n\\nexport {}\\n\\n/* prettier-ignore */\\ndeclare module 'vue' {\\n export interface GlobalComponents {\\n${lines}\\n }\\n}\\n`,\n )\n }\n\n writePrefixedDts()\n\n /**\n * Watches prefixed source dirs and rebuilds {@link prefixedNameMap} when\n * files are added/removed. Vite's watcher already covers `dirSources`\n * via unplugin-vue-components' own filesystem hooks.\n */\n const prefixedSourceWatcher: Plugin | null = prefixedSources.length > 0\n ? {\n name: 'maizzle:prefixed-component-watcher',\n configureServer(server) {\n for (const source of prefixedSources) {\n server.watcher.add(source.path)\n }\n const refresh = async (file: string) => {\n if (!prefixedSources.some(s => file.startsWith(`${s.path}/`))) return\n if (!/\\.(vue|md)$/.test(file)) return\n await scanPrefixedSources()\n writePrefixedDts()\n }\n server.watcher.on('add', refresh)\n server.watcher.on('unlink', refresh)\n },\n }\n : null\n\n const VIRTUAL_SFC_ID = 'virtual:maizzle-sfc.vue'\n let virtualSfcSource = ''\n\n /**\n * Never load the host project's vite.config.ts here. Doing so pulls\n * every host plugin (Nitro, TanStack Start, the Maizzle plugin\n * itself, …) into this isolated SSR pipeline, where they override\n * env factories, re-trigger configureServer hooks, and break\n * Vite's hot channel wiring. Users who need extra Vite plugins\n * for SSR pass them explicitly via the `vite` option.\n */\n const maizzleConfig: InlineConfig = {\n configFile: false,\n plugins: [\n rawExtract(),\n codeBlockExtract(),\n markdownExtract(),\n rowSourceLocation(),\n {\n name: 'maizzle:virtual-sfc',\n resolveId(id) {\n if (id === VIRTUAL_SFC_ID) return id\n },\n load(id) {\n if (id === VIRTUAL_SFC_ID) return virtualSfcSource\n },\n },\n vue({\n include: [/\\.vue$/, /\\.md$/],\n template: {\n transformAssetUrls: false,\n compilerOptions: {\n /**\n * AMP4Email tags (<amp-carousel>, <amp-img>, <amp-list> ...)\n * render verbatim — skip the component resolver. Users who\n * want to wrap an amp tag in a Vue component should register\n * it under a PascalCase name (e.g. `components/AmpCarousel.vue`\n * → `<AmpCarousel>`).\n */\n isCustomElement: (tag: string) => tag.startsWith('amp-'),\n },\n },\n }),\n Markdown(merge(markdownOptions ?? {}, {\n headEnabled: true,\n wrapperDiv: false,\n wrapperClasses: 'prose',\n wrapperComponent: (id: string, raw: string) => {\n const fm = raw.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---/)?.[1]\n const layout = fm?.match(/^[ \\t]*layout[ \\t]*:[ \\t]*['\"]?([A-Za-z][\\w-]*|false|none)['\"]?[ \\t]*$/m)?.[1]\n if (layout === 'false' || layout === 'none') return null\n if (layout) return layout\n /**\n * No `layout:` set — default to the built-in `MarkdownLayout`\n * for entry-template `.md` files. Skip for `.md` files inside\n * component dirs, which are reusable fragments imported into\n * other templates.\n */\n const inComponentDir = componentDirsAbs.some(d => id === d || id.startsWith(`${d}/`))\n return inComponentDir ? null : 'MarkdownLayout'\n },\n markdownOptions: {\n async highlight(code: string, lang: string) {\n const { codeToHtml } = await import('shiki')\n return codeToHtml(code, { lang, theme: shikiTheme })\n },\n },\n markdownSetup(md: MarkdownExit) {\n const defaultFence = md.renderer.rules.fence!\n md.renderer.rules.fence = (...args) =>\n Promise.resolve(defaultFence(...args)).then(shikiToCodeBlock)\n\n const defaultCodeBlock = md.renderer.rules.code_block!\n md.renderer.rules.code_block = (...args) => shikiToCodeBlock(defaultCodeBlock(...args) as string)\n },\n })),\n AutoImport({\n dirs: [\n resolve(__dirname, '../composables'),\n resolve(__dirname, '../filters'),\n ],\n imports: ['vue', unheadVueComposablesImports],\n /**\n * unplugin-auto-import's default `include` doesn't match `.md`, so\n * auto-imports (Vue, unhead and Maizzle composables/filters) were\n * never injected into Markdown templates — `useConfig()` and friends\n * threw at runtime. Extend the default list with `.md` (and its\n * `?vue` script sub-requests) to mirror the `.md` coverage the\n * Components plugin already declares below.\n */\n include: [/\\.[jt]sx?$/, /\\.vue$/, /\\.vue\\?vue/, /\\.md$/, /\\.md\\?vue/],\n dts: dts ? resolve(dtsDir, 'auto-imports.d.ts') : false,\n }),\n Components({\n extensions: ['vue', 'md'],\n include: [/\\.vue$/, /\\.vue\\?vue/, /\\.md$/],\n dirs: [\n frameworkComponentsDir,\n resolve(root, 'components'),\n ...dirSources.map(s => s.path),\n ],\n /**\n * Drop built-in component files whose name the user has shadowed.\n * This makes the user's version the only match — no \"naming\n * conflicts\" warning, no glob-ordering games.\n */\n globsExclude: frameworkExcludes,\n directoryAsNamespace: true,\n collapseSamePrefixes: true,\n resolvers: prefixedSources.length > 0 ? [prefixedResolver] : undefined,\n dts: dts ? resolve(dtsDir, 'components.d.ts') : false,\n }),\n ...(prefixedSourceWatcher ? [prefixedSourceWatcher] : []),\n ],\n resolve: {\n alias: {\n 'vue/server-renderer': resolve(vueServerRendererPkgDir, 'dist/server-renderer.esm-bundler.js'),\n 'vue': resolve(vuePkgDir, 'dist/vue.runtime.esm-bundler.js'),\n 'vue-router': vueRouterPkgDir,\n '@unhead/vue/server': resolve(unheadVuePkgDir, 'dist/server.mjs'),\n '@unhead/vue': resolve(unheadVuePkgDir, 'dist/index.mjs'),\n },\n },\n server: {\n middlewareMode: true,\n hmr: false,\n /**\n * Watcher is required so unplugin-vue-components and unplugin-auto-import\n * detect added/removed component files and rewrite their .d.ts on the fly.\n * (We only render via SSR — HMR is off, but chokidar still drives plugins.)\n */\n fs: {\n allow: [process.cwd(), root, ...componentDirs.map(s => s.path), vuePkgDir, vueServerRendererPkgDir, unheadVuePkgDir, vueRouterPkgDir],\n },\n },\n appType: 'custom',\n logLevel: 'silent',\n optimizeDeps: {\n noDiscovery: true,\n },\n }\n\n /**\n * Merge user's vite config (from config.vite) under Maizzle's config.\n * mergeConfig(a, b) → b overrides a for scalars, arrays concatenate.\n * This ensures Maizzle's critical settings (middlewareMode, appType,\n * etc.) always win, while user plugins and other options remain.\n */\n const finalConfig = userViteConfig\n ? mergeConfig(userViteConfig, maizzleConfig)\n : maizzleConfig\n\n const server = await createServer(finalConfig)\n\n return {\n async render(input: string | Component, config: MaizzleConfig): Promise<RenderedTemplate> {\n let component: Component\n let configKey: InjectionKey<MaizzleConfig>\n let contextKey: InjectionKey<RenderContext>\n\n if (typeof input === 'string') {\n /**\n * String input goes through Vite — must use ssrLoadModule for\n * injection keys so they share the same module instance as SFC.\n */\n const configModule = await server.ssrLoadModule(resolve(__dirname, '../composables/useConfig'))\n const contextModule = await server.ssrLoadModule(resolve(__dirname, '../composables/renderContext'))\n configKey = configModule.MaizzleConfigKey\n contextKey = contextModule.RenderContextKey\n\n if (input.includes('<template') || input.includes('<script')) {\n virtualSfcSource = input\n const mod = server.moduleGraph.getModuleById(VIRTUAL_SFC_ID)\n if (mod) server.moduleGraph.invalidateModule(mod)\n component = (await server.ssrLoadModule(VIRTUAL_SFC_ID)).default\n } else {\n component = (await server.ssrLoadModule(input)).default\n }\n } else {\n // Pre-compiled component — use directly imported keys\n component = input\n configKey = MaizzleConfigKey\n contextKey = RenderContextKey\n }\n\n const renderContext: RenderContext = {\n doctype: undefined,\n sfcConfig: undefined,\n sfcEventHandlers: [],\n }\n\n const head = createHead({ disableDefaults: true })\n const app = createSSRApp(component)\n app.use(head)\n\n // Register user Vue plugins, directives, and global properties\n if (config.vue) {\n const plugins = typeof config.vue.plugins === 'function'\n ? config.vue.plugins()\n : config.vue.plugins ?? []\n for (const plugin of plugins) {\n app.use(plugin)\n }\n for (const [name, directive] of Object.entries(config.vue.directives ?? {})) {\n app.directive(name, directive)\n }\n Object.assign(app.config.globalProperties, config.vue.globalProperties)\n }\n\n app.provide(configKey, config)\n app.provide(contextKey, renderContext)\n\n const ssrContext: Record<string, any> = {}\n let html: string = await renderToString(app, ssrContext)\n\n const { headTags, bodyTags, bodyTagsOpen, htmlAttrs, bodyAttrs } = head.render()\n\n // Inject head entries into the rendered HTML\n if (htmlAttrs) {\n html = html.replace(/<html([^>]*)>/, `<html$1 ${htmlAttrs}>`)\n }\n if (headTags) {\n html = html.replace('</head>', `${headTags}\\n</head>`)\n }\n if (bodyAttrs) {\n html = html.replace(/<body([^>]*)>/, `<body$1 ${bodyAttrs}>`)\n }\n if (bodyTagsOpen) {\n html = html.replace(/<body([^>]*)>/, `<body$1>\\n${bodyTagsOpen}`)\n }\n if (bodyTags) {\n html = html.replace('</body>', `${bodyTags}\\n</body>`)\n }\n\n // Inject SSR teleport content into their target elements\n const hasTeleports = ssrContext.teleports && Object.keys(ssrContext.teleports).length > 0\n const hasFonts = (renderContext.fonts?.length ?? 0) > 0\n\n if (hasTeleports || hasFonts) {\n const { parse: parseDom, serialize: serializeDom, walk } = await import('../utils/ast/index.ts')\n let dom = parseDom(html)\n\n if (hasTeleports) {\n for (const [rawTarget, content] of Object.entries(ssrContext.teleports) as [string, string][]) {\n if (!content) continue\n\n const prepend = rawTarget.endsWith(':start')\n const target = prepend ? rawTarget.slice(0, -6) : rawTarget\n const targetChildren = parseDom(content)\n\n walk(dom, (node) => {\n const el = node as import('domhandler').Element\n\n if (!el.name) return\n\n const matched\n = target === el.name\n || (target.startsWith('#') && el.attribs?.id === target.slice(1))\n || (target.startsWith('.') && el.attribs?.class?.split(/\\s+/).includes(target.slice(1)))\n\n if (matched) {\n for (const child of targetChildren) {\n child.parent = el as any\n }\n\n el.children = prepend\n ? [...targetChildren, ...(el.children || [])] as any\n : [...(el.children || []), ...targetChildren] as any\n }\n })\n }\n }\n\n if (hasFonts) {\n const { injectFonts } = await import('./injectFonts.ts')\n injectFonts(dom, renderContext.fonts!, parseDom, walk)\n }\n\n html = serializeDom(dom)\n }\n\n // Inject preheader text from usePreheader() composable\n if (renderContext.preheader) {\n const { text, fillerCount } = renderContext.preheader\n const filler = '\\u2007\\uFEFF\\u034F '.repeat(fillerCount)\n const previewHtml = `<div style=\"display:none\">${text}${filler}\\u00A0</div>`\n html = html.replace(/<body([^>]*)>/, `<body$1>${previewHtml}`)\n }\n\n /**\n * Strip Vue SSR fragment markers + teleport anchor comments. These\n * are rendering hygiene, not transformer concerns — must run\n * regardless of `useTransformers` state. Fragment markers contain\n * `-->`, which would prematurely terminate MSO conditional\n * comments downstream.\n */\n html = html\n .replaceAll('<!--[-->', '')\n .replaceAll('<!--]-->', '')\n .replaceAll('<!--teleport start anchor-->', '')\n .replaceAll('<!--teleport anchor-->', '')\n .replaceAll('<!--teleport start-->', '')\n .replaceAll('<!--teleport end-->', '')\n\n return {\n html,\n doctype: renderContext.doctype,\n /**\n * Layer sfcConfig over config — sfcConfig is a partial override\n * emitted by composables (defineConfig, useTransformers, etc.).\n * A naive replacement (`sfcConfig ?? config`) drops defaults\n * from the resolved config when the SFC only sets a single\n * key, since the composables' inject() of globalConfig can\n * return `{}` in dev when ssrLoadModule and the SFC's\n * auto-imported module resolve to different module\n * instances (different Symbols).\n */\n templateConfig: renderContext.sfcConfig ? merge(renderContext.sfcConfig, config) : config,\n sfcEventHandlers: renderContext.sfcEventHandlers,\n plaintext: renderContext.plaintext,\n outputPath: renderContext.outputPath,\n tailwindBlocks: renderContext.tailwindBlocks,\n }\n },\n\n async invalidate(filePath: string): Promise<void> {\n const mod = await server.moduleGraph.getModuleByUrl(filePath)\n if (mod) {\n server.moduleGraph.invalidateModule(mod)\n }\n },\n\n async invalidateAll(): Promise<void> {\n for (const mod of server.moduleGraph.idToModuleMap.values()) {\n server.moduleGraph.invalidateModule(mod)\n }\n },\n\n async close(): Promise<void> {\n await server.close()\n /**\n * unplugin-auto-import schedules a 500ms-throttled, fire-and-forget\n * d.ts write on its first scan. server.close() doesn't drain that\n * pending write, so callers tearing down the working dir right\n * after close (tests, ephemeral build pipelines) can race the\n * mkdir against a missing parent directory. Wait one throttle\n * window past close so the lingering write resolves while\n * the dir still exists.\n */\n if (dts) {\n await new Promise(resolve => setTimeout(resolve, 600))\n }\n },\n }\n}\n"],"mappings":"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"}
@@ -35,7 +35,7 @@ async function render(template, config) {
35
35
  let html = rendered.html;
36
36
  const doctype = rendered.doctype ?? rendered.templateConfig.doctype ?? "<!DOCTYPE html>";
37
37
  if (rendered.templateConfig.useTransformers !== false) html = await runTransformers(html, rendered.templateConfig, isFile ? resolve(template) : void 0, doctype, rendered.tailwindBlocks);
38
- html = `${doctype}\n${html}`;
38
+ if (doctype) html = `${doctype}\n${html}`;
39
39
  const globalPlaintext = rendered.templateConfig.plaintext;
40
40
  const sfcPlaintext = rendered.plaintext;
41
41
  let plaintextResult;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/render/index.ts"],"sourcesContent":["import { resolve, extname } from 'node:path'\nimport { resolveConfig } from '../config/index.ts'\nimport { runTransformers } from '../transformers/index.ts'\nimport { createPlaintext } from '../plaintext.ts'\nimport { stripForHtml, stripForPlaintext } from '../utils/output-markers.ts'\nimport defu from 'defu'\nimport type { Component } from 'vue'\nimport type { MaizzleConfig } from '../types/index.ts'\nimport { createRenderer } from './createRenderer.ts'\nimport { getActiveRenderer } from './active.ts'\nimport { normalizeComponentSources } from '../utils/componentSources.ts'\n\nexport type { Renderer, RenderedTemplate, CreateRendererOptions } from './createRenderer.ts'\nexport { createRenderer } from './createRenderer.ts'\n\nexport interface RenderResult {\n html: string\n config: MaizzleConfig\n plaintext?: string\n}\n\n/**\n * Render a Vue SFC email template to a fully-transformed HTML string.\n * Accepts a file path or a raw SFC source string.\n */\nexport async function render(\n template: string | Component,\n config?: Partial<MaizzleConfig>,\n): Promise<RenderResult> {\n if (template == null) {\n throw new Error(\n `render() received ${template}. If you used \\`import X from './x.vue'\\`, Node cannot load .vue files natively — pass the path string instead: render('./x.vue').`,\n )\n }\n if (typeof template !== 'string' && typeof template !== 'object' && typeof template !== 'function') {\n throw new TypeError(\n `render() expected a file path or SFC source string, got ${typeof template}.`,\n )\n }\n\n const resolvedConfig = await resolveConfig(config)\n\n /**\n * Reuse a renderer started by the Vite plugin when one is active.\n * Spinning up a fresh Vite SSR server inside a host Vite dev process\n * (e.g. TanStack Start) collides on env wiring and throws\n * \"outsideEmitter undefined\".\n */\n const active = getActiveRenderer()\n const renderer = active ?? await createRenderer({\n markdown: resolvedConfig.markdown,\n root: resolvedConfig.root,\n componentDirs: normalizeComponentSources(resolvedConfig.components?.source, process.cwd()),\n vite: resolvedConfig.vite,\n })\n\n try {\n const isFile = typeof template === 'string'\n && ['.vue', '.md'].includes(extname(template))\n && !template.includes('\\n')\n\n const rendered = await renderer.render(isFile ? resolve(template) : template, resolvedConfig)\n let html = rendered.html\n\n const doctype = rendered.doctype ?? rendered.templateConfig.doctype ?? '<!DOCTYPE html>'\n\n if (rendered.templateConfig.useTransformers !== false) {\n html = await runTransformers(html, rendered.templateConfig, isFile ? resolve(template) : undefined, doctype, rendered.tailwindBlocks)\n }\n html = `${doctype}\\n${html}`\n\n const globalPlaintext = rendered.templateConfig.plaintext\n const sfcPlaintext = rendered.plaintext\n\n let plaintextResult: string | undefined\n\n if (globalPlaintext || sfcPlaintext) {\n const globalCfg = typeof globalPlaintext === 'object' ? globalPlaintext : {}\n const stripOptions = defu(sfcPlaintext?.options, globalCfg.options)\n plaintextResult = createPlaintext(stripForPlaintext(html), stripOptions)\n }\n\n return { html: stripForHtml(html), config: rendered.templateConfig, plaintext: plaintextResult }\n } finally {\n if (!active) await renderer.close()\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAyBA,eAAsB,OACpB,UACA,QACuB;CACvB,IAAI,YAAY,MACd,MAAM,IAAI,MACR,qBAAqB,SAAS,mIAChC;CAEF,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,YAAY,OAAO,aAAa,YACtF,MAAM,IAAI,UACR,2DAA2D,OAAO,SAAS,EAC7E;CAGF,MAAM,iBAAiB,MAAM,cAAc,MAAM;;;;;;;CAQjD,MAAM,SAAS,kBAAkB;CACjC,MAAM,WAAW,UAAU,MAAM,eAAe;EAC9C,UAAU,eAAe;EACzB,MAAM,eAAe;EACrB,eAAe,0BAA0B,eAAe,YAAY,QAAQ,QAAQ,IAAI,CAAC;EACzF,MAAM,eAAe;CACvB,CAAC;CAED,IAAI;EACF,MAAM,SAAS,OAAO,aAAa,YAC9B,CAAC,QAAQ,KAAK,EAAE,SAAS,QAAQ,QAAQ,CAAC,KAC1C,CAAC,SAAS,SAAS,IAAI;EAE5B,MAAM,WAAW,MAAM,SAAS,OAAO,SAAS,QAAQ,QAAQ,IAAI,UAAU,cAAc;EAC5F,IAAI,OAAO,SAAS;EAEpB,MAAM,UAAU,SAAS,WAAW,SAAS,eAAe,WAAW;EAEvE,IAAI,SAAS,eAAe,oBAAoB,OAC9C,OAAO,MAAM,gBAAgB,MAAM,SAAS,gBAAgB,SAAS,QAAQ,QAAQ,IAAI,KAAA,GAAW,SAAS,SAAS,cAAc;EAEtI,OAAO,GAAG,QAAQ,IAAI;EAEtB,MAAM,kBAAkB,SAAS,eAAe;EAChD,MAAM,eAAe,SAAS;EAE9B,IAAI;EAEJ,IAAI,mBAAmB,cAAc;GACnC,MAAM,YAAY,OAAO,oBAAoB,WAAW,kBAAkB,CAAC;GAC3E,MAAM,eAAe,KAAK,cAAc,SAAS,UAAU,OAAO;GAClE,kBAAkB,gBAAgB,kBAAkB,IAAI,GAAG,YAAY;EACzE;EAEA,OAAO;GAAE,MAAM,aAAa,IAAI;GAAG,QAAQ,SAAS;GAAgB,WAAW;EAAgB;CACjG,UAAU;EACR,IAAI,CAAC,QAAQ,MAAM,SAAS,MAAM;CACpC;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/render/index.ts"],"sourcesContent":["import { resolve, extname } from 'node:path'\nimport { resolveConfig } from '../config/index.ts'\nimport { runTransformers } from '../transformers/index.ts'\nimport { createPlaintext } from '../plaintext.ts'\nimport { stripForHtml, stripForPlaintext } from '../utils/output-markers.ts'\nimport defu from 'defu'\nimport type { Component } from 'vue'\nimport type { MaizzleConfig } from '../types/index.ts'\nimport { createRenderer } from './createRenderer.ts'\nimport { getActiveRenderer } from './active.ts'\nimport { normalizeComponentSources } from '../utils/componentSources.ts'\n\nexport type { Renderer, RenderedTemplate, CreateRendererOptions } from './createRenderer.ts'\nexport { createRenderer } from './createRenderer.ts'\n\nexport interface RenderResult {\n html: string\n config: MaizzleConfig\n plaintext?: string\n}\n\n/**\n * Render a Vue SFC email template to a fully-transformed HTML string.\n * Accepts a file path or a raw SFC source string.\n */\nexport async function render(\n template: string | Component,\n config?: Partial<MaizzleConfig>,\n): Promise<RenderResult> {\n if (template == null) {\n throw new Error(\n `render() received ${template}. If you used \\`import X from './x.vue'\\`, Node cannot load .vue files natively — pass the path string instead: render('./x.vue').`,\n )\n }\n if (typeof template !== 'string' && typeof template !== 'object' && typeof template !== 'function') {\n throw new TypeError(\n `render() expected a file path or SFC source string, got ${typeof template}.`,\n )\n }\n\n const resolvedConfig = await resolveConfig(config)\n\n /**\n * Reuse a renderer started by the Vite plugin when one is active.\n * Spinning up a fresh Vite SSR server inside a host Vite dev process\n * (e.g. TanStack Start) collides on env wiring and throws\n * \"outsideEmitter undefined\".\n */\n const active = getActiveRenderer()\n const renderer = active ?? await createRenderer({\n markdown: resolvedConfig.markdown,\n root: resolvedConfig.root,\n componentDirs: normalizeComponentSources(resolvedConfig.components?.source, process.cwd()),\n vite: resolvedConfig.vite,\n })\n\n try {\n const isFile = typeof template === 'string'\n && ['.vue', '.md'].includes(extname(template))\n && !template.includes('\\n')\n\n const rendered = await renderer.render(isFile ? resolve(template) : template, resolvedConfig)\n let html = rendered.html\n\n const doctype = rendered.doctype ?? rendered.templateConfig.doctype ?? '<!DOCTYPE html>'\n\n if (rendered.templateConfig.useTransformers !== false) {\n html = await runTransformers(html, rendered.templateConfig, isFile ? resolve(template) : undefined, doctype, rendered.tailwindBlocks)\n }\n if (doctype) html = `${doctype}\\n${html}`\n\n const globalPlaintext = rendered.templateConfig.plaintext\n const sfcPlaintext = rendered.plaintext\n\n let plaintextResult: string | undefined\n\n if (globalPlaintext || sfcPlaintext) {\n const globalCfg = typeof globalPlaintext === 'object' ? globalPlaintext : {}\n const stripOptions = defu(sfcPlaintext?.options, globalCfg.options)\n plaintextResult = createPlaintext(stripForPlaintext(html), stripOptions)\n }\n\n return { html: stripForHtml(html), config: rendered.templateConfig, plaintext: plaintextResult }\n } finally {\n if (!active) await renderer.close()\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAyBA,eAAsB,OACpB,UACA,QACuB;CACvB,IAAI,YAAY,MACd,MAAM,IAAI,MACR,qBAAqB,SAAS,mIAChC;CAEF,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,YAAY,OAAO,aAAa,YACtF,MAAM,IAAI,UACR,2DAA2D,OAAO,SAAS,EAC7E;CAGF,MAAM,iBAAiB,MAAM,cAAc,MAAM;;;;;;;CAQjD,MAAM,SAAS,kBAAkB;CACjC,MAAM,WAAW,UAAU,MAAM,eAAe;EAC9C,UAAU,eAAe;EACzB,MAAM,eAAe;EACrB,eAAe,0BAA0B,eAAe,YAAY,QAAQ,QAAQ,IAAI,CAAC;EACzF,MAAM,eAAe;CACvB,CAAC;CAED,IAAI;EACF,MAAM,SAAS,OAAO,aAAa,YAC9B,CAAC,QAAQ,KAAK,CAAC,CAAC,SAAS,QAAQ,QAAQ,CAAC,KAC1C,CAAC,SAAS,SAAS,IAAI;EAE5B,MAAM,WAAW,MAAM,SAAS,OAAO,SAAS,QAAQ,QAAQ,IAAI,UAAU,cAAc;EAC5F,IAAI,OAAO,SAAS;EAEpB,MAAM,UAAU,SAAS,WAAW,SAAS,eAAe,WAAW;EAEvE,IAAI,SAAS,eAAe,oBAAoB,OAC9C,OAAO,MAAM,gBAAgB,MAAM,SAAS,gBAAgB,SAAS,QAAQ,QAAQ,IAAI,KAAA,GAAW,SAAS,SAAS,cAAc;EAEtI,IAAI,SAAS,OAAO,GAAG,QAAQ,IAAI;EAEnC,MAAM,kBAAkB,SAAS,eAAe;EAChD,MAAM,eAAe,SAAS;EAE9B,IAAI;EAEJ,IAAI,mBAAmB,cAAc;GACnC,MAAM,YAAY,OAAO,oBAAoB,WAAW,kBAAkB,CAAC;GAC3E,MAAM,eAAe,KAAK,cAAc,SAAS,UAAU,OAAO;GAClE,kBAAkB,gBAAgB,kBAAkB,IAAI,GAAG,YAAY;EACzE;EAEA,OAAO;GAAE,MAAM,aAAa,IAAI;GAAG,QAAQ,SAAS;GAAgB,WAAW;EAAgB;CACjG,UAAU;EACR,IAAI,CAAC,QAAQ,MAAM,SAAS,MAAM;CACpC;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"injectFonts.js","names":[],"sources":["../../src/render/injectFonts.ts"],"sourcesContent":["import type { ChildNode, Element } from 'domhandler'\nimport type { FontRegistration } from '../composables/renderContext.ts'\nimport { decodeStyleEntities } from '../utils/decodeStyleEntities.ts'\n\nconst TAILWIND_IMPORT_RE = /((@import|@reference)\\s+[\"'](tailwindcss|@maizzle\\/tailwindcss)|@tailwind\\s)/\n\nfunction getText(el: Element): string {\n return (el.children || [])\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.data)\n .join('')\n}\n\n/**\n * Inject font `<link>` tags into `<head>` and merge `@theme` declarations\n * into the template's existing Tailwind `<style>` block (so the\n * `font-{slug}` utilities are generated in the same compilation unit\n * as the Tailwind import). Without a Tailwind import, emits plain\n * `.font-{slug}` class rules so the utility still works.\n */\nexport function injectFonts(\n dom: ChildNode[],\n fonts: FontRegistration[],\n parseDom: (html: string) => ChildNode[],\n walk: (ast: ChildNode[], cb: (n: ChildNode) => void) => void,\n): void {\n if (!fonts.length) return\n\n let head: Element | undefined\n let tailwindStyle: Element | undefined\n\n walk(dom, (node) => {\n const el = node as Element\n if (!el.name) return\n if (el.name === 'head' && !head) head = el\n if (el.name === 'style' && !tailwindStyle && TAILWIND_IMPORT_RE.test(decodeStyleEntities(getText(el)))) {\n tailwindStyle = el\n }\n })\n\n if (!head) return\n\n const linkHtml = fonts\n .map(f => `<link href=\"${f.url}\" rel=\"stylesheet\" media=\"screen\">`)\n .join('')\n const linkNodes = parseDom(linkHtml)\n for (const child of linkNodes) {\n (child as any).parent = head\n }\n head.children = [...(head.children || []), ...linkNodes] as any\n\n if (tailwindStyle) {\n const themeDecls = fonts\n .map(f => ` --font-${f.slug}: ${f.declaration};`)\n .join('\\n')\n const existing = getText(tailwindStyle)\n tailwindStyle.children = [{\n type: 'text',\n data: `${existing}\\n@theme {\\n${themeDecls}\\n}\\n`,\n parent: tailwindStyle,\n } as any]\n } else {\n const classRules = fonts\n .map(f => `.font-${f.slug} { font-family: ${f.declaration}; }`)\n .join('\\n')\n const styleNodes = parseDom(`<style>\\n${classRules}\\n</style>`)\n for (const child of styleNodes) {\n (child as any).parent = head\n }\n head.children = [...(head.children || []), ...styleNodes] as any\n }\n}\n"],"mappings":";;AAIA,MAAM,qBAAqB;AAE3B,SAAS,QAAQ,IAAqB;CACpC,QAAQ,GAAG,YAAY,CAAC,GACrB,QAAQ,MAAW,EAAE,SAAS,MAAM,EACpC,KAAK,MAAW,EAAE,IAAI,EACtB,KAAK,EAAE;AACZ;;;;;;;;AASA,SAAgB,YACd,KACA,OACA,UACA,MACM;CACN,IAAI,CAAC,MAAM,QAAQ;CAEnB,IAAI;CACJ,IAAI;CAEJ,KAAK,MAAM,SAAS;EAClB,MAAM,KAAK;EACX,IAAI,CAAC,GAAG,MAAM;EACd,IAAI,GAAG,SAAS,UAAU,CAAC,MAAM,OAAO;EACxC,IAAI,GAAG,SAAS,WAAW,CAAC,iBAAiB,mBAAmB,KAAK,oBAAoB,QAAQ,EAAE,CAAC,CAAC,GACnG,gBAAgB;CAEpB,CAAC;CAED,IAAI,CAAC,MAAM;CAKX,MAAM,YAAY,SAHD,MACd,KAAI,MAAK,eAAe,EAAE,IAAI,mCAAmC,EACjE,KAAK,EAC0B,CAAC;CACnC,KAAK,MAAM,SAAS,WAClB,MAAe,SAAS;CAE1B,KAAK,WAAW,CAAC,GAAI,KAAK,YAAY,CAAC,GAAI,GAAG,SAAS;CAEvD,IAAI,eAAe;EACjB,MAAM,aAAa,MAChB,KAAI,MAAK,YAAY,EAAE,KAAK,IAAI,EAAE,YAAY,EAAE,EAChD,KAAK,IAAI;EACZ,MAAM,WAAW,QAAQ,aAAa;EACtC,cAAc,WAAW,CAAC;GACxB,MAAM;GACN,MAAM,GAAG,SAAS,cAAc,WAAW;GAC3C,QAAQ;EACV,CAAQ;CACV,OAAO;EAIL,MAAM,aAAa,SAAS,YAHT,MAChB,KAAI,MAAK,SAAS,EAAE,KAAK,kBAAkB,EAAE,YAAY,IAAI,EAC7D,KAAK,IACyC,EAAE,WAAW;EAC9D,KAAK,MAAM,SAAS,YAClB,MAAe,SAAS;EAE1B,KAAK,WAAW,CAAC,GAAI,KAAK,YAAY,CAAC,GAAI,GAAG,UAAU;CAC1D;AACF"}
1
+ {"version":3,"file":"injectFonts.js","names":[],"sources":["../../src/render/injectFonts.ts"],"sourcesContent":["import type { ChildNode, Element } from 'domhandler'\nimport type { FontRegistration } from '../composables/renderContext.ts'\nimport { decodeStyleEntities } from '../utils/decodeStyleEntities.ts'\n\nconst TAILWIND_IMPORT_RE = /((@import|@reference)\\s+[\"'](tailwindcss|@maizzle\\/tailwindcss)|@tailwind\\s)/\n\nfunction getText(el: Element): string {\n return (el.children || [])\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.data)\n .join('')\n}\n\n/**\n * Inject font `<link>` tags into `<head>` and merge `@theme` declarations\n * into the template's existing Tailwind `<style>` block (so the\n * `font-{slug}` utilities are generated in the same compilation unit\n * as the Tailwind import). Without a Tailwind import, emits plain\n * `.font-{slug}` class rules so the utility still works.\n */\nexport function injectFonts(\n dom: ChildNode[],\n fonts: FontRegistration[],\n parseDom: (html: string) => ChildNode[],\n walk: (ast: ChildNode[], cb: (n: ChildNode) => void) => void,\n): void {\n if (!fonts.length) return\n\n let head: Element | undefined\n let tailwindStyle: Element | undefined\n\n walk(dom, (node) => {\n const el = node as Element\n if (!el.name) return\n if (el.name === 'head' && !head) head = el\n if (el.name === 'style' && !tailwindStyle && TAILWIND_IMPORT_RE.test(decodeStyleEntities(getText(el)))) {\n tailwindStyle = el\n }\n })\n\n if (!head) return\n\n const linkHtml = fonts\n .map(f => `<link href=\"${f.url}\" rel=\"stylesheet\" media=\"screen\">`)\n .join('')\n const linkNodes = parseDom(linkHtml)\n for (const child of linkNodes) {\n (child as any).parent = head\n }\n head.children = [...(head.children || []), ...linkNodes] as any\n\n if (tailwindStyle) {\n const themeDecls = fonts\n .map(f => ` --font-${f.slug}: ${f.declaration};`)\n .join('\\n')\n const existing = getText(tailwindStyle)\n tailwindStyle.children = [{\n type: 'text',\n data: `${existing}\\n@theme {\\n${themeDecls}\\n}\\n`,\n parent: tailwindStyle,\n } as any]\n } else {\n const classRules = fonts\n .map(f => `.font-${f.slug} { font-family: ${f.declaration}; }`)\n .join('\\n')\n const styleNodes = parseDom(`<style>\\n${classRules}\\n</style>`)\n for (const child of styleNodes) {\n (child as any).parent = head\n }\n head.children = [...(head.children || []), ...styleNodes] as any\n }\n}\n"],"mappings":";;AAIA,MAAM,qBAAqB;AAE3B,SAAS,QAAQ,IAAqB;CACpC,QAAQ,GAAG,YAAY,CAAC,EAAA,CACrB,QAAQ,MAAW,EAAE,SAAS,MAAM,CAAC,CACrC,KAAK,MAAW,EAAE,IAAI,CAAC,CACvB,KAAK,EAAE;AACZ;;;;;;;;AASA,SAAgB,YACd,KACA,OACA,UACA,MACM;CACN,IAAI,CAAC,MAAM,QAAQ;CAEnB,IAAI;CACJ,IAAI;CAEJ,KAAK,MAAM,SAAS;EAClB,MAAM,KAAK;EACX,IAAI,CAAC,GAAG,MAAM;EACd,IAAI,GAAG,SAAS,UAAU,CAAC,MAAM,OAAO;EACxC,IAAI,GAAG,SAAS,WAAW,CAAC,iBAAiB,mBAAmB,KAAK,oBAAoB,QAAQ,EAAE,CAAC,CAAC,GACnG,gBAAgB;CAEpB,CAAC;CAED,IAAI,CAAC,MAAM;CAKX,MAAM,YAAY,SAHD,MACd,KAAI,MAAK,eAAe,EAAE,IAAI,mCAAmC,CAAC,CAClE,KAAK,EAC0B,CAAC;CACnC,KAAK,MAAM,SAAS,WAClB,MAAe,SAAS;CAE1B,KAAK,WAAW,CAAC,GAAI,KAAK,YAAY,CAAC,GAAI,GAAG,SAAS;CAEvD,IAAI,eAAe;EACjB,MAAM,aAAa,MAChB,KAAI,MAAK,YAAY,EAAE,KAAK,IAAI,EAAE,YAAY,EAAE,CAAC,CACjD,KAAK,IAAI;EACZ,MAAM,WAAW,QAAQ,aAAa;EACtC,cAAc,WAAW,CAAC;GACxB,MAAM;GACN,MAAM,GAAG,SAAS,cAAc,WAAW;GAC3C,QAAQ;EACV,CAAQ;CACV,OAAO;EAIL,MAAM,aAAa,SAAS,YAHT,MAChB,KAAI,MAAK,SAAS,EAAE,KAAK,kBAAkB,EAAE,YAAY,IAAI,CAAC,CAC9D,KAAK,IACyC,EAAE,WAAW;EAC9D,KAAK,MAAM,SAAS,YAClB,MAAe,SAAS;EAE1B,KAAK,WAAW,CAAC,GAAI,KAAK,YAAY,CAAC,GAAI,GAAG,UAAU;CAC1D;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"codeBlockExtract.d.ts","names":[],"sources":["../../../src/render/plugins/codeBlockExtract.ts"],"mappings":";;;;;AASA;;;;AAA0C;iBAA1B,gBAAA,CAAA,GAAoB,MAAM"}
1
+ {"version":3,"file":"codeBlockExtract.d.ts","names":[],"sources":["../../../src/render/plugins/codeBlockExtract.ts"],"mappings":";;;;;AASA;;;;AAA0C;iBAA1B,gBAAA,IAAoB,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"codeBlockExtract.js","names":[],"sources":["../../../src/render/plugins/codeBlockExtract.ts"],"sourcesContent":["import type { Plugin } from 'vite'\n\n/**\n * Vite plugin that extracts raw slot content from <CodeBlock> tags\n * and passes it as a :code prop before Vue compiles the template.\n *\n * This lets users write HTML naturally inside CodeBlock slots without\n * Vue attempting to compile it as template syntax.\n */\nexport function codeBlockExtract(): Plugin {\n // Matches <CodeBlock ...>content</CodeBlock> (and kebab-case <code-block>)\n const re = /<(CodeBlock|code-block)((?:\\s[^>]*?)?)>([\\s\\S]*?)<\\/\\1>/g\n\n return {\n name: 'maizzle:code-block-extract',\n enforce: 'pre',\n transform(code: string, id: string) {\n if (!id.endsWith('.vue') && !id.endsWith('.md')) return\n if (!code.includes('CodeBlock') && !code.includes('code-block')) return\n\n const transformed = code.replace(re, (_match, tag, attrs, content) => {\n // Skip if already has a :code or v-bind:code prop\n if (/(?:^|\\s):code\\b/.test(attrs) || /v-bind:code\\b/.test(attrs)) return _match\n\n /**\n * Strip leading/trailing blank lines, then dedent based on\n * the minimum indent of non-empty lines (à la min-indent).\n */\n const stripped = content.replace(/^\\n+/, '').replace(/\\s+$/, '')\n if (!stripped) return _match\n\n const minIndent = stripped.match(/^[ \\t]*(?=\\S)/gm)\n ?.reduce((min: number, ws: string) => Math.min(min, ws.length), Infinity) ?? 0\n\n const dedented = minIndent > 0\n ? stripped.replace(new RegExp(`^[ \\\\t]{${minIndent}}`, 'gm'), '')\n : stripped\n\n // HTML-escape for safe embedding in a static attribute value.\n const escaped = dedented\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n\n return `<${tag}${attrs} code=\"${escaped}\" />`\n })\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,mBAA2B;CAEzC,MAAM,KAAK;CAEX,OAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAc,IAAY;GAClC,IAAI,CAAC,GAAG,SAAS,MAAM,KAAK,CAAC,GAAG,SAAS,KAAK,GAAG;GACjD,IAAI,CAAC,KAAK,SAAS,WAAW,KAAK,CAAC,KAAK,SAAS,YAAY,GAAG;GAEjE,MAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,KAAK,OAAO,YAAY;IAEpE,IAAI,kBAAkB,KAAK,KAAK,KAAK,gBAAgB,KAAK,KAAK,GAAG,OAAO;;;;;IAMzE,MAAM,WAAW,QAAQ,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE;IAC/D,IAAI,CAAC,UAAU,OAAO;IAEtB,MAAM,YAAY,SAAS,MAAM,iBAAiB,GAC9C,QAAQ,KAAa,OAAe,KAAK,IAAI,KAAK,GAAG,MAAM,GAAG,QAAQ,KAAK;IAa/E,OAAO,IAAI,MAAM,MAAM,UAXN,YAAY,IACzB,SAAS,QAAQ,IAAI,OAAO,WAAW,UAAU,IAAI,IAAI,GAAG,EAAE,IAC9D,UAID,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAEqB,EAAE;GAC1C,CAAC;GAED,IAAI,gBAAgB,MAClB,OAAO;IAAE,MAAM;IAAa,KAAK;GAAK;EAE1C;CACF;AACF"}
1
+ {"version":3,"file":"codeBlockExtract.js","names":[],"sources":["../../../src/render/plugins/codeBlockExtract.ts"],"sourcesContent":["import type { Plugin } from 'vite'\n\n/**\n * Vite plugin that extracts raw slot content from <CodeBlock> tags\n * and passes it as a :code prop before Vue compiles the template.\n *\n * This lets users write HTML naturally inside CodeBlock slots without\n * Vue attempting to compile it as template syntax.\n */\nexport function codeBlockExtract(): Plugin {\n // Matches <CodeBlock ...>content</CodeBlock> (and kebab-case <code-block>)\n const re = /<(CodeBlock|code-block)((?:\\s[^>]*?)?)>([\\s\\S]*?)<\\/\\1>/g\n\n return {\n name: 'maizzle:code-block-extract',\n enforce: 'pre',\n transform(code: string, id: string) {\n if (!id.endsWith('.vue') && !id.endsWith('.md')) return\n if (!code.includes('CodeBlock') && !code.includes('code-block')) return\n\n const transformed = code.replace(re, (_match, tag, attrs, content) => {\n // Skip if already has a :code or v-bind:code prop\n if (/(?:^|\\s):code\\b/.test(attrs) || /v-bind:code\\b/.test(attrs)) return _match\n\n /**\n * Strip leading/trailing blank lines, then dedent based on\n * the minimum indent of non-empty lines (à la min-indent).\n */\n const stripped = content.replace(/^\\n+/, '').replace(/\\s+$/, '')\n if (!stripped) return _match\n\n const minIndent = stripped.match(/^[ \\t]*(?=\\S)/gm)\n ?.reduce((min: number, ws: string) => Math.min(min, ws.length), Infinity) ?? 0\n\n const dedented = minIndent > 0\n ? stripped.replace(new RegExp(`^[ \\\\t]{${minIndent}}`, 'gm'), '')\n : stripped\n\n // HTML-escape for safe embedding in a static attribute value.\n const escaped = dedented\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n\n return `<${tag}${attrs} code=\"${escaped}\" />`\n })\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,mBAA2B;CAEzC,MAAM,KAAK;CAEX,OAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAc,IAAY;GAClC,IAAI,CAAC,GAAG,SAAS,MAAM,KAAK,CAAC,GAAG,SAAS,KAAK,GAAG;GACjD,IAAI,CAAC,KAAK,SAAS,WAAW,KAAK,CAAC,KAAK,SAAS,YAAY,GAAG;GAEjE,MAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,KAAK,OAAO,YAAY;IAEpE,IAAI,kBAAkB,KAAK,KAAK,KAAK,gBAAgB,KAAK,KAAK,GAAG,OAAO;;;;;IAMzE,MAAM,WAAW,QAAQ,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;IAC/D,IAAI,CAAC,UAAU,OAAO;IAEtB,MAAM,YAAY,SAAS,MAAM,iBAAiB,CAAC,EAC/C,QAAQ,KAAa,OAAe,KAAK,IAAI,KAAK,GAAG,MAAM,GAAG,QAAQ,KAAK;IAa/E,OAAO,IAAI,MAAM,MAAM,UAXN,YAAY,IACzB,SAAS,QAAQ,IAAI,OAAO,WAAW,UAAU,IAAI,IAAI,GAAG,EAAE,IAC9D,SAAA,CAID,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,QAAQ,CAAC,CACvB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAEqB,EAAE;GAC1C,CAAC;GAED,IAAI,gBAAgB,MAClB,OAAO;IAAE,MAAM;IAAa,KAAK;GAAK;EAE1C;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"markdownExtract.d.ts","names":[],"sources":["../../../src/render/plugins/markdownExtract.ts"],"mappings":";;;;;AASA;;;iBAAgB,eAAA,CAAA,GAAmB,MAAM"}
1
+ {"version":3,"file":"markdownExtract.d.ts","names":[],"sources":["../../../src/render/plugins/markdownExtract.ts"],"mappings":";;;;;AASA;;;iBAAgB,eAAA,IAAmB,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"markdownExtract.js","names":[],"sources":["../../../src/render/plugins/markdownExtract.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path'\nimport { readFileSync } from 'node:fs'\nimport type { Plugin } from 'vite'\n\n/**\n * Vite plugin that pre-processes <Markdown> tags:\n * - Extracts slot content, dedents it, and passes as :content prop\n * - Resolves `src` prop to read file contents at build time\n */\nexport function markdownExtract(): Plugin {\n const re = /<(Markdown|markdown)((?:\\s[^>]*?)?)>([\\s\\S]*?)<\\/\\1>/g\n const selfClosingRe = /<(Markdown|markdown)((?:\\s[^>]*?\\bsrc\\s*=\\s*\"[^\"]*\"[^>]*?))\\/>/g\n\n return {\n name: 'maizzle:markdown-extract',\n enforce: 'pre',\n transform(code: string, id: string) {\n if (!id.endsWith('.vue') && !id.endsWith('.md')) return\n if (!code.includes('Markdown') && !code.includes('markdown')) return\n\n let transformed = code\n\n // Handle <Markdown>content</Markdown>\n transformed = transformed.replace(re, (_match, tag, attrs, content) => {\n if (/(?:^|\\s):content\\b/.test(attrs) || /v-bind:content\\b/.test(attrs)) return _match\n\n const stripped = content.replace(/^\\n+/, '').replace(/\\s+$/, '')\n if (!stripped) return _match\n\n const minIndent = stripped.match(/^[ \\t]*(?=\\S)/gm)\n ?.reduce((min: number, ws: string) => Math.min(min, ws.length), Infinity) ?? 0\n\n const dedented = minIndent > 0\n ? stripped.replace(new RegExp(`^[ \\\\t]{${minIndent}}`, 'gm'), '')\n : stripped\n\n const escaped = dedented\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n\n return `<${tag}${attrs} content=\"${escaped}\" />`\n })\n\n // Handle <Markdown src=\"./file.md\" /> — resolve and inline file content\n transformed = transformed.replace(selfClosingRe, (_match, tag, attrs) => {\n const srcMatch = attrs.match(/\\bsrc\\s*=\\s*\"([^\"]*)\"/)\n if (!srcMatch) return _match\n\n const srcPath = srcMatch[1]\n const resolvedPath = resolve(dirname(id), srcPath)\n\n let fileContent: string\n try {\n fileContent = readFileSync(resolvedPath, 'utf-8').trim()\n } catch {\n return _match\n }\n\n // Remove src prop, add content prop\n const cleanAttrs = attrs.replace(/\\s*\\bsrc\\s*=\\s*\"[^\"]*\"/, '')\n const escaped = fileContent\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n\n return `<${tag}${cleanAttrs} content=\"${escaped}\" />`\n })\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,kBAA0B;CACxC,MAAM,KAAK;CACX,MAAM,gBAAgB;CAEtB,OAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAc,IAAY;GAClC,IAAI,CAAC,GAAG,SAAS,MAAM,KAAK,CAAC,GAAG,SAAS,KAAK,GAAG;GACjD,IAAI,CAAC,KAAK,SAAS,UAAU,KAAK,CAAC,KAAK,SAAS,UAAU,GAAG;GAE9D,IAAI,cAAc;GAGlB,cAAc,YAAY,QAAQ,KAAK,QAAQ,KAAK,OAAO,YAAY;IACrE,IAAI,qBAAqB,KAAK,KAAK,KAAK,mBAAmB,KAAK,KAAK,GAAG,OAAO;IAE/E,MAAM,WAAW,QAAQ,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE;IAC/D,IAAI,CAAC,UAAU,OAAO;IAEtB,MAAM,YAAY,SAAS,MAAM,iBAAiB,GAC9C,QAAQ,KAAa,OAAe,KAAK,IAAI,KAAK,GAAG,MAAM,GAAG,QAAQ,KAAK;IAY/E,OAAO,IAAI,MAAM,MAAM,aAVN,YAAY,IACzB,SAAS,QAAQ,IAAI,OAAO,WAAW,UAAU,IAAI,IAAI,GAAG,EAAE,IAC9D,UAGD,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAEwB,EAAE;GAC7C,CAAC;GAGD,cAAc,YAAY,QAAQ,gBAAgB,QAAQ,KAAK,UAAU;IACvE,MAAM,WAAW,MAAM,MAAM,uBAAuB;IACpD,IAAI,CAAC,UAAU,OAAO;IAEtB,MAAM,UAAU,SAAS;IACzB,MAAM,eAAe,QAAQ,QAAQ,EAAE,GAAG,OAAO;IAEjD,IAAI;IACJ,IAAI;KACF,cAAc,aAAa,cAAc,OAAO,EAAE,KAAK;IACzD,QAAQ;KACN,OAAO;IACT;IAUA,OAAO,IAAI,MAPQ,MAAM,QAAQ,0BAA0B,EAOjC,EAAE,YANZ,YACb,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAE6B,EAAE;GAClD,CAAC;GAED,IAAI,gBAAgB,MAClB,OAAO;IAAE,MAAM;IAAa,KAAK;GAAK;EAE1C;CACF;AACF"}
1
+ {"version":3,"file":"markdownExtract.js","names":[],"sources":["../../../src/render/plugins/markdownExtract.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path'\nimport { readFileSync } from 'node:fs'\nimport type { Plugin } from 'vite'\n\n/**\n * Vite plugin that pre-processes <Markdown> tags:\n * - Extracts slot content, dedents it, and passes as :content prop\n * - Resolves `src` prop to read file contents at build time\n */\nexport function markdownExtract(): Plugin {\n const re = /<(Markdown|markdown)((?:\\s[^>]*?)?)>([\\s\\S]*?)<\\/\\1>/g\n const selfClosingRe = /<(Markdown|markdown)((?:\\s[^>]*?\\bsrc\\s*=\\s*\"[^\"]*\"[^>]*?))\\/>/g\n\n return {\n name: 'maizzle:markdown-extract',\n enforce: 'pre',\n transform(code: string, id: string) {\n if (!id.endsWith('.vue') && !id.endsWith('.md')) return\n if (!code.includes('Markdown') && !code.includes('markdown')) return\n\n let transformed = code\n\n // Handle <Markdown>content</Markdown>\n transformed = transformed.replace(re, (_match, tag, attrs, content) => {\n if (/(?:^|\\s):content\\b/.test(attrs) || /v-bind:content\\b/.test(attrs)) return _match\n\n const stripped = content.replace(/^\\n+/, '').replace(/\\s+$/, '')\n if (!stripped) return _match\n\n const minIndent = stripped.match(/^[ \\t]*(?=\\S)/gm)\n ?.reduce((min: number, ws: string) => Math.min(min, ws.length), Infinity) ?? 0\n\n const dedented = minIndent > 0\n ? stripped.replace(new RegExp(`^[ \\\\t]{${minIndent}}`, 'gm'), '')\n : stripped\n\n const escaped = dedented\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n\n return `<${tag}${attrs} content=\"${escaped}\" />`\n })\n\n // Handle <Markdown src=\"./file.md\" /> — resolve and inline file content\n transformed = transformed.replace(selfClosingRe, (_match, tag, attrs) => {\n const srcMatch = attrs.match(/\\bsrc\\s*=\\s*\"([^\"]*)\"/)\n if (!srcMatch) return _match\n\n const srcPath = srcMatch[1]\n const resolvedPath = resolve(dirname(id), srcPath)\n\n let fileContent: string\n try {\n fileContent = readFileSync(resolvedPath, 'utf-8').trim()\n } catch {\n return _match\n }\n\n // Remove src prop, add content prop\n const cleanAttrs = attrs.replace(/\\s*\\bsrc\\s*=\\s*\"[^\"]*\"/, '')\n const escaped = fileContent\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n\n return `<${tag}${cleanAttrs} content=\"${escaped}\" />`\n })\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,kBAA0B;CACxC,MAAM,KAAK;CACX,MAAM,gBAAgB;CAEtB,OAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAc,IAAY;GAClC,IAAI,CAAC,GAAG,SAAS,MAAM,KAAK,CAAC,GAAG,SAAS,KAAK,GAAG;GACjD,IAAI,CAAC,KAAK,SAAS,UAAU,KAAK,CAAC,KAAK,SAAS,UAAU,GAAG;GAE9D,IAAI,cAAc;GAGlB,cAAc,YAAY,QAAQ,KAAK,QAAQ,KAAK,OAAO,YAAY;IACrE,IAAI,qBAAqB,KAAK,KAAK,KAAK,mBAAmB,KAAK,KAAK,GAAG,OAAO;IAE/E,MAAM,WAAW,QAAQ,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;IAC/D,IAAI,CAAC,UAAU,OAAO;IAEtB,MAAM,YAAY,SAAS,MAAM,iBAAiB,CAAC,EAC/C,QAAQ,KAAa,OAAe,KAAK,IAAI,KAAK,GAAG,MAAM,GAAG,QAAQ,KAAK;IAY/E,OAAO,IAAI,MAAM,MAAM,aAVN,YAAY,IACzB,SAAS,QAAQ,IAAI,OAAO,WAAW,UAAU,IAAI,IAAI,GAAG,EAAE,IAC9D,SAAA,CAGD,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,QAAQ,CAAC,CACvB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAEwB,EAAE;GAC7C,CAAC;GAGD,cAAc,YAAY,QAAQ,gBAAgB,QAAQ,KAAK,UAAU;IACvE,MAAM,WAAW,MAAM,MAAM,uBAAuB;IACpD,IAAI,CAAC,UAAU,OAAO;IAEtB,MAAM,UAAU,SAAS;IACzB,MAAM,eAAe,QAAQ,QAAQ,EAAE,GAAG,OAAO;IAEjD,IAAI;IACJ,IAAI;KACF,cAAc,aAAa,cAAc,OAAO,CAAC,CAAC,KAAK;IACzD,QAAQ;KACN,OAAO;IACT;IAUA,OAAO,IAAI,MAPQ,MAAM,QAAQ,0BAA0B,EAOjC,EAAE,YANZ,YACb,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,QAAQ,CAAC,CACvB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAE6B,EAAE;GAClD,CAAC;GAED,IAAI,gBAAgB,MAClB,OAAO;IAAE,MAAM;IAAa,KAAK;GAAK;EAE1C;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"rawExtract.d.ts","names":[],"sources":["../../../src/render/plugins/rawExtract.ts"],"mappings":";;;;;AASA;;;;AAAoC;iBAApB,UAAA,CAAA,GAAc,MAAM"}
1
+ {"version":3,"file":"rawExtract.d.ts","names":[],"sources":["../../../src/render/plugins/rawExtract.ts"],"mappings":";;;;;AASA;;;;AAAoC;iBAApB,UAAA,IAAc,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"rawExtract.js","names":[],"sources":["../../../src/render/plugins/rawExtract.ts"],"sourcesContent":["import type { Plugin } from 'vite'\n\n/**\n * Vite plugin that extracts raw slot content from <Raw> tags\n * and passes it as a :content prop before Vue compiles the template.\n *\n * Lets users write content (including `{{ }}` interpolation syntax used\n * by ESPs / Handlebars / Liquid) inside <Raw> without Vue parsing it.\n */\nexport function rawExtract(): Plugin {\n const re = /<(Raw)((?:\\s[^>]*?)?)>([\\s\\S]*?)<\\/\\1>/g\n\n return {\n name: 'maizzle:raw-extract',\n enforce: 'pre',\n transform(code: string, id: string) {\n if (!id.endsWith('.vue') && !id.endsWith('.md')) return\n if (!code.includes('Raw')) return\n\n const transformed = code.replace(re, (_match, tag, attrs, content) => {\n if (/(?:^|\\s):content\\b/.test(attrs) || /v-bind:content\\b/.test(attrs)) return _match\n\n const stripped = content.replace(/^\\n+/, '').replace(/\\s+$/, '')\n if (!stripped) return _match\n\n const minIndent = stripped.match(/^[ \\t]*(?=\\S)/gm)\n ?.reduce((min: number, ws: string) => Math.min(min, ws.length), Infinity) ?? 0\n\n const dedented = minIndent > 0\n ? stripped.replace(new RegExp(`^[ \\\\t]{${minIndent}}`, 'gm'), '')\n : stripped\n\n const escaped = dedented\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n\n return `<${tag}${attrs} content=\"${escaped}\" />`\n })\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,aAAqB;CACnC,MAAM,KAAK;CAEX,OAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAc,IAAY;GAClC,IAAI,CAAC,GAAG,SAAS,MAAM,KAAK,CAAC,GAAG,SAAS,KAAK,GAAG;GACjD,IAAI,CAAC,KAAK,SAAS,KAAK,GAAG;GAE3B,MAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,KAAK,OAAO,YAAY;IACpE,IAAI,qBAAqB,KAAK,KAAK,KAAK,mBAAmB,KAAK,KAAK,GAAG,OAAO;IAE/E,MAAM,WAAW,QAAQ,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,EAAE;IAC/D,IAAI,CAAC,UAAU,OAAO;IAEtB,MAAM,YAAY,SAAS,MAAM,iBAAiB,GAC9C,QAAQ,KAAa,OAAe,KAAK,IAAI,KAAK,GAAG,MAAM,GAAG,QAAQ,KAAK;IAY/E,OAAO,IAAI,MAAM,MAAM,aAVN,YAAY,IACzB,SAAS,QAAQ,IAAI,OAAO,WAAW,UAAU,IAAI,IAAI,GAAG,EAAE,IAC9D,UAGD,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAEwB,EAAE;GAC7C,CAAC;GAED,IAAI,gBAAgB,MAClB,OAAO;IAAE,MAAM;IAAa,KAAK;GAAK;EAE1C;CACF;AACF"}
1
+ {"version":3,"file":"rawExtract.js","names":[],"sources":["../../../src/render/plugins/rawExtract.ts"],"sourcesContent":["import type { Plugin } from 'vite'\n\n/**\n * Vite plugin that extracts raw slot content from <Raw> tags\n * and passes it as a :content prop before Vue compiles the template.\n *\n * Lets users write content (including `{{ }}` interpolation syntax used\n * by ESPs / Handlebars / Liquid) inside <Raw> without Vue parsing it.\n */\nexport function rawExtract(): Plugin {\n const re = /<(Raw)((?:\\s[^>]*?)?)>([\\s\\S]*?)<\\/\\1>/g\n\n return {\n name: 'maizzle:raw-extract',\n enforce: 'pre',\n transform(code: string, id: string) {\n if (!id.endsWith('.vue') && !id.endsWith('.md')) return\n if (!code.includes('Raw')) return\n\n const transformed = code.replace(re, (_match, tag, attrs, content) => {\n if (/(?:^|\\s):content\\b/.test(attrs) || /v-bind:content\\b/.test(attrs)) return _match\n\n const stripped = content.replace(/^\\n+/, '').replace(/\\s+$/, '')\n if (!stripped) return _match\n\n const minIndent = stripped.match(/^[ \\t]*(?=\\S)/gm)\n ?.reduce((min: number, ws: string) => Math.min(min, ws.length), Infinity) ?? 0\n\n const dedented = minIndent > 0\n ? stripped.replace(new RegExp(`^[ \\\\t]{${minIndent}}`, 'gm'), '')\n : stripped\n\n const escaped = dedented\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n\n return `<${tag}${attrs} content=\"${escaped}\" />`\n })\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n"],"mappings":";;;;;;;;AASA,SAAgB,aAAqB;CACnC,MAAM,KAAK;CAEX,OAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAc,IAAY;GAClC,IAAI,CAAC,GAAG,SAAS,MAAM,KAAK,CAAC,GAAG,SAAS,KAAK,GAAG;GACjD,IAAI,CAAC,KAAK,SAAS,KAAK,GAAG;GAE3B,MAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,KAAK,OAAO,YAAY;IACpE,IAAI,qBAAqB,KAAK,KAAK,KAAK,mBAAmB,KAAK,KAAK,GAAG,OAAO;IAE/E,MAAM,WAAW,QAAQ,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;IAC/D,IAAI,CAAC,UAAU,OAAO;IAEtB,MAAM,YAAY,SAAS,MAAM,iBAAiB,CAAC,EAC/C,QAAQ,KAAa,OAAe,KAAK,IAAI,KAAK,GAAG,MAAM,GAAG,QAAQ,KAAK;IAY/E,OAAO,IAAI,MAAM,MAAM,aAVN,YAAY,IACzB,SAAS,QAAQ,IAAI,OAAO,WAAW,UAAU,IAAI,IAAI,GAAG,EAAE,IAC9D,SAAA,CAGD,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,QAAQ,CAAC,CACvB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAEwB,EAAE;GAC7C,CAAC;GAED,IAAI,gBAAgB,MAClB,OAAO;IAAE,MAAM;IAAa,KAAK;GAAK;EAE1C;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"rowSourceLocation.d.ts","names":[],"sources":["../../../src/render/plugins/rowSourceLocation.ts"],"mappings":";;;;;AAaA;;;;AAA2C;;;;;iBAA3B,iBAAA,CAAA,GAAqB,MAAM"}
1
+ {"version":3,"file":"rowSourceLocation.d.ts","names":[],"sources":["../../../src/render/plugins/rowSourceLocation.ts"],"mappings":";;;;;AAaA;;;;AAA2C;;;;;iBAA3B,iBAAA,IAAqB,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"rowSourceLocation.js","names":[],"sources":["../../../src/render/plugins/rowSourceLocation.ts"],"sourcesContent":["import type { Plugin } from 'vite'\n\n/**\n * Vite plugin that injects `data-maizzle-loc=\"<file>:<line>\"` into every\n * `<Row>`/`<row>` opening tag in user templates.\n *\n * Used by Row.vue's runtime to point the user at the exact line in their\n * template when they misuse Row (e.g. without a Column child).\n *\n * Only transforms inside `<template>` blocks of SFCs (or the entire file\n * for `.md` templates) so `<Row>` mentions in `<script>` blocks (e.g. in\n * string literals or comments) are left untouched.\n */\nexport function rowSourceLocation(): Plugin {\n const tagRe = /(<(?:Row|row))(\\b[^>]*?)(\\/?>)/g\n\n function injectLoc(html: string, htmlOffset: number, fullCode: string, id: string): string {\n return html.replace(tagRe, (match, tag, attrs, end, localOffset: number) => {\n if (/\\bdata-maizzle-loc\\s*=/.test(attrs)) return match\n const absoluteOffset = htmlOffset + localOffset\n const line = fullCode.slice(0, absoluteOffset).split('\\n').length\n return `${tag}${attrs} data-maizzle-loc=\"${id}:${line}\"${end}`\n })\n }\n\n return {\n name: 'maizzle:row-loc',\n enforce: 'pre',\n transform(code, id) {\n const isVue = id.endsWith('.vue')\n const isMd = id.endsWith('.md')\n if (!isVue && !isMd) return\n if (!code.includes('<Row') && !code.includes('<row')) return\n\n let transformed: string\n\n if (isVue) {\n /**\n * Replace inside every <template>...</template> block, leaving\n * <script> and <style> blocks alone.\n */\n const templateBlock = /(<template\\b[^>]*>)([\\s\\S]*?)(<\\/template>)/g\n transformed = code.replace(templateBlock, (_match, open, inner, close, offset: number) => {\n const innerOffset = offset + open.length\n return open + injectLoc(inner, innerOffset, code, id) + close\n })\n } else {\n transformed = injectLoc(code, 0, code, id)\n }\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;AAaA,SAAgB,oBAA4B;CAC1C,MAAM,QAAQ;CAEd,SAAS,UAAU,MAAc,YAAoB,UAAkB,IAAoB;EACzF,OAAO,KAAK,QAAQ,QAAQ,OAAO,KAAK,OAAO,KAAK,gBAAwB;GAC1E,IAAI,yBAAyB,KAAK,KAAK,GAAG,OAAO;GACjD,MAAM,iBAAiB,aAAa;GAEpC,OAAO,GAAG,MAAM,MAAM,qBAAqB,GAAG,GADjC,SAAS,MAAM,GAAG,cAAc,EAAE,MAAM,IAAI,EAAE,OACL,GAAG;EAC3D,CAAC;CACH;CAEA,OAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAM,IAAI;GAClB,MAAM,QAAQ,GAAG,SAAS,MAAM;GAChC,MAAM,OAAO,GAAG,SAAS,KAAK;GAC9B,IAAI,CAAC,SAAS,CAAC,MAAM;GACrB,IAAI,CAAC,KAAK,SAAS,MAAM,KAAK,CAAC,KAAK,SAAS,MAAM,GAAG;GAEtD,IAAI;GAEJ,IAAI,OAMF,cAAc,KAAK,QAAQ,iDAAgB,QAAQ,MAAM,OAAO,OAAO,WAAmB;IAExF,OAAO,OAAO,UAAU,OADJ,SAAS,KAAK,QACU,MAAM,EAAE,IAAI;GAC1D,CAAC;QAED,cAAc,UAAU,MAAM,GAAG,MAAM,EAAE;GAG3C,IAAI,gBAAgB,MAClB,OAAO;IAAE,MAAM;IAAa,KAAK;GAAK;EAE1C;CACF;AACF"}
1
+ {"version":3,"file":"rowSourceLocation.js","names":[],"sources":["../../../src/render/plugins/rowSourceLocation.ts"],"sourcesContent":["import type { Plugin } from 'vite'\n\n/**\n * Vite plugin that injects `data-maizzle-loc=\"<file>:<line>\"` into every\n * `<Row>`/`<row>` opening tag in user templates.\n *\n * Used by Row.vue's runtime to point the user at the exact line in their\n * template when they misuse Row (e.g. without a Column child).\n *\n * Only transforms inside `<template>` blocks of SFCs (or the entire file\n * for `.md` templates) so `<Row>` mentions in `<script>` blocks (e.g. in\n * string literals or comments) are left untouched.\n */\nexport function rowSourceLocation(): Plugin {\n const tagRe = /(<(?:Row|row))(\\b[^>]*?)(\\/?>)/g\n\n function injectLoc(html: string, htmlOffset: number, fullCode: string, id: string): string {\n return html.replace(tagRe, (match, tag, attrs, end, localOffset: number) => {\n if (/\\bdata-maizzle-loc\\s*=/.test(attrs)) return match\n const absoluteOffset = htmlOffset + localOffset\n const line = fullCode.slice(0, absoluteOffset).split('\\n').length\n return `${tag}${attrs} data-maizzle-loc=\"${id}:${line}\"${end}`\n })\n }\n\n return {\n name: 'maizzle:row-loc',\n enforce: 'pre',\n transform(code, id) {\n const isVue = id.endsWith('.vue')\n const isMd = id.endsWith('.md')\n if (!isVue && !isMd) return\n if (!code.includes('<Row') && !code.includes('<row')) return\n\n let transformed: string\n\n if (isVue) {\n /**\n * Replace inside every <template>...</template> block, leaving\n * <script> and <style> blocks alone.\n */\n const templateBlock = /(<template\\b[^>]*>)([\\s\\S]*?)(<\\/template>)/g\n transformed = code.replace(templateBlock, (_match, open, inner, close, offset: number) => {\n const innerOffset = offset + open.length\n return open + injectLoc(inner, innerOffset, code, id) + close\n })\n } else {\n transformed = injectLoc(code, 0, code, id)\n }\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;AAaA,SAAgB,oBAA4B;CAC1C,MAAM,QAAQ;CAEd,SAAS,UAAU,MAAc,YAAoB,UAAkB,IAAoB;EACzF,OAAO,KAAK,QAAQ,QAAQ,OAAO,KAAK,OAAO,KAAK,gBAAwB;GAC1E,IAAI,yBAAyB,KAAK,KAAK,GAAG,OAAO;GACjD,MAAM,iBAAiB,aAAa;GAEpC,OAAO,GAAG,MAAM,MAAM,qBAAqB,GAAG,GADjC,SAAS,MAAM,GAAG,cAAc,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,OACL,GAAG;EAC3D,CAAC;CACH;CAEA,OAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAM,IAAI;GAClB,MAAM,QAAQ,GAAG,SAAS,MAAM;GAChC,MAAM,OAAO,GAAG,SAAS,KAAK;GAC9B,IAAI,CAAC,SAAS,CAAC,MAAM;GACrB,IAAI,CAAC,KAAK,SAAS,MAAM,KAAK,CAAC,KAAK,SAAS,MAAM,GAAG;GAEtD,IAAI;GAEJ,IAAI,OAMF,cAAc,KAAK,QAAQ,iDAAgB,QAAQ,MAAM,OAAO,OAAO,WAAmB;IAExF,OAAO,OAAO,UAAU,OADJ,SAAS,KAAK,QACU,MAAM,EAAE,IAAI;GAC1D,CAAC;QAED,cAAc,UAAU,MAAM,GAAG,MAAM,EAAE;GAG3C,IAAI,gBAAgB,MAClB,OAAO;IAAE,MAAM;IAAa,KAAK;GAAK;EAE1C;CACF;AACF"}
package/dist/serve.js CHANGED
@@ -299,7 +299,7 @@ async function serveRenderedTemplate(url, config, renderer, res) {
299
299
  const templateConfig = rendered.templateConfig;
300
300
  const doctype = rendered.doctype ?? templateConfig.doctype ?? "<!DOCTYPE html>";
301
301
  html = await runTransformers(html, templateConfig, absolutePath, doctype, rendered.tailwindBlocks);
302
- html = `${doctype}\n${html}`;
302
+ if (doctype) html = `${doctype}\n${html}`;
303
303
  res.setHeader("Content-Type", "text/html");
304
304
  res.end(stripForHtml(html));
305
305
  } catch (error) {
@@ -334,7 +334,7 @@ async function serveHighlightedSource(url, config, renderer, res) {
334
334
  const templateConfig = rendered.templateConfig;
335
335
  const doctype = rendered.doctype ?? templateConfig.doctype ?? "<!DOCTYPE html>";
336
336
  html = await runTransformers(html, templateConfig, absolutePath, doctype, rendered.tailwindBlocks);
337
- html = stripForHtml(`${doctype}\n${html}`);
337
+ html = stripForHtml(doctype ? `${doctype}\n${html}` : html);
338
338
  const highlighted = (await getHighlighter()).codeToHtml(html, {
339
339
  lang: "html",
340
340
  theme: "laserwave",
@@ -502,7 +502,7 @@ async function serveEmailEndpoint(url, req, res, config, renderer) {
502
502
  const templateConfig = rendered.templateConfig;
503
503
  const doctype = rendered.doctype ?? templateConfig.doctype ?? "<!DOCTYPE html>";
504
504
  html = await runTransformers(html, templateConfig, absolutePath, doctype, rendered.tailwindBlocks);
505
- html = `${doctype}\n${html}`;
505
+ if (doctype) html = `${doctype}\n${html}`;
506
506
  const text = createPlaintext(stripForPlaintext(html));
507
507
  html = stripForHtml(html);
508
508
  const result = await sendEmail({