@maizzle/framework 6.0.0-rc.18 → 6.0.0-rc.19

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 (221) hide show
  1. package/dist/build.d.ts.map +1 -1
  2. package/dist/build.js +10 -6
  3. package/dist/build.js.map +1 -1
  4. package/dist/components/Button.vue +17 -39
  5. package/dist/components/Container.vue +6 -6
  6. package/dist/components/Hr.vue +20 -120
  7. package/dist/components/Html.vue +19 -1
  8. package/dist/components/NotPlaintext.vue +14 -0
  9. package/dist/components/{Vml.vue → OutlookBg.vue} +1 -1
  10. package/dist/components/Plaintext.vue +14 -0
  11. package/dist/components/QrCode.vue +157 -0
  12. package/dist/components/Spacer.vue +28 -27
  13. package/dist/components/utils.js +1 -1
  14. package/dist/components/utils.js.map +1 -1
  15. package/dist/composables/defineConfig.js +1 -2
  16. package/dist/composables/defineConfig.js.map +1 -1
  17. package/dist/composables/renderContext.js +1 -1
  18. package/dist/composables/useBaseUrl.js +3 -4
  19. package/dist/composables/useBaseUrl.js.map +1 -1
  20. package/dist/composables/useConfig.js +1 -2
  21. package/dist/composables/useConfig.js.map +1 -1
  22. package/dist/composables/useDoctype.js +1 -2
  23. package/dist/composables/useDoctype.js.map +1 -1
  24. package/dist/composables/useEvent.js +1 -2
  25. package/dist/composables/useEvent.js.map +1 -1
  26. package/dist/composables/useFont.js +1 -2
  27. package/dist/composables/useFont.js.map +1 -1
  28. package/dist/composables/useOutlookFallback.js +1 -2
  29. package/dist/composables/useOutlookFallback.js.map +1 -1
  30. package/dist/composables/usePlaintext.d.ts +2 -0
  31. package/dist/composables/usePlaintext.d.ts.map +1 -1
  32. package/dist/composables/usePlaintext.js +2 -2
  33. package/dist/composables/usePlaintext.js.map +1 -1
  34. package/dist/composables/usePreheader.js +1 -2
  35. package/dist/composables/usePreheader.js.map +1 -1
  36. package/dist/composables/useTransformers.d.ts +3 -3
  37. package/dist/composables/useTransformers.js +4 -5
  38. package/dist/composables/useTransformers.js.map +1 -1
  39. package/dist/composables/useUrlQuery.js +3 -4
  40. package/dist/composables/useUrlQuery.js.map +1 -1
  41. package/dist/config/defaults.js +1 -1
  42. package/dist/config/index.js +1 -2
  43. package/dist/config/index.js.map +1 -1
  44. package/dist/events/index.js +1 -1
  45. package/dist/events/index.js.map +1 -1
  46. package/dist/index.d.ts +9 -9
  47. package/dist/index.js +4 -5
  48. package/dist/plaintext.js +3 -4
  49. package/dist/plaintext.js.map +1 -1
  50. package/dist/plugin.js +1 -2
  51. package/dist/plugin.js.map +1 -1
  52. package/dist/plugins/postcss/mergeMediaQueries.js +1 -2
  53. package/dist/plugins/postcss/mergeMediaQueries.js.map +1 -1
  54. package/dist/plugins/postcss/pruneVars.js +1 -1
  55. package/dist/plugins/postcss/pruneVars.js.map +1 -1
  56. package/dist/plugins/postcss/quoteFontFamilies.js +1 -1
  57. package/dist/plugins/postcss/quoteFontFamilies.js.map +1 -1
  58. package/dist/plugins/postcss/removeDeclarations.js +1 -1
  59. package/dist/plugins/postcss/removeDeclarations.js.map +1 -1
  60. package/dist/plugins/postcss/resolveMaizzleImports.js +1 -2
  61. package/dist/plugins/postcss/resolveMaizzleImports.js.map +1 -1
  62. package/dist/plugins/postcss/resolveProps.js +1 -1
  63. package/dist/plugins/postcss/resolveProps.js.map +1 -1
  64. package/dist/plugins/postcss/tailwindCleanup.js +1 -1
  65. package/dist/plugins/postcss/tailwindCleanup.js.map +1 -1
  66. package/dist/prepare.js +1 -2
  67. package/dist/prepare.js.map +1 -1
  68. package/dist/render/active.d.ts +8 -0
  69. package/dist/render/active.d.ts.map +1 -0
  70. package/dist/render/active.js +12 -0
  71. package/dist/render/active.js.map +1 -0
  72. package/dist/render/createRenderer.d.ts.map +1 -1
  73. package/dist/render/createRenderer.js +6 -9
  74. package/dist/render/createRenderer.js.map +1 -1
  75. package/dist/render/index.d.ts.map +1 -1
  76. package/dist/render/index.js +13 -6
  77. package/dist/render/index.js.map +1 -1
  78. package/dist/render/injectFonts.js +1 -2
  79. package/dist/render/injectFonts.js.map +1 -1
  80. package/dist/render/plugins/codeBlockExtract.js +1 -1
  81. package/dist/render/plugins/codeBlockExtract.js.map +1 -1
  82. package/dist/render/plugins/markdownExtract.js +1 -2
  83. package/dist/render/plugins/markdownExtract.js.map +1 -1
  84. package/dist/render/plugins/rawExtract.js +1 -1
  85. package/dist/render/plugins/rawExtract.js.map +1 -1
  86. package/dist/render/plugins/rowSourceLocation.js +1 -1
  87. package/dist/render/plugins/rowSourceLocation.js.map +1 -1
  88. package/dist/serve.d.ts +2 -0
  89. package/dist/serve.d.ts.map +1 -1
  90. package/dist/serve.js +12 -7
  91. package/dist/serve.js.map +1 -1
  92. package/dist/server/compatibility.js +1 -2
  93. package/dist/server/compatibility.js.map +1 -1
  94. package/dist/server/email.js +1 -2
  95. package/dist/server/email.js.map +1 -1
  96. package/dist/server/linter.js +1 -2
  97. package/dist/server/linter.js.map +1 -1
  98. package/dist/server/sfc-utils.js +1 -2
  99. package/dist/server/sfc-utils.js.map +1 -1
  100. package/dist/tests/render/_helpers.d.ts +6 -0
  101. package/dist/tests/render/_helpers.d.ts.map +1 -0
  102. package/dist/tests/render/_helpers.js +16 -0
  103. package/dist/tests/render/_helpers.js.map +1 -0
  104. package/dist/transformers/addAttributes.js +3 -4
  105. package/dist/transformers/addAttributes.js.map +1 -1
  106. package/dist/transformers/attributeToStyle.d.ts +27 -14
  107. package/dist/transformers/attributeToStyle.d.ts.map +1 -1
  108. package/dist/transformers/attributeToStyle.js +34 -20
  109. package/dist/transformers/attributeToStyle.js.map +1 -1
  110. package/dist/transformers/base.d.ts +66 -3
  111. package/dist/transformers/base.d.ts.map +1 -1
  112. package/dist/transformers/base.js +50 -24
  113. package/dist/transformers/base.js.map +1 -1
  114. package/dist/transformers/columnWidth.js +1 -2
  115. package/dist/transformers/columnWidth.js.map +1 -1
  116. package/dist/transformers/entities.d.ts +31 -2
  117. package/dist/transformers/entities.d.ts.map +1 -1
  118. package/dist/transformers/entities.js +39 -7
  119. package/dist/transformers/entities.js.map +1 -1
  120. package/dist/transformers/filters/defaults.js +1 -1
  121. package/dist/transformers/filters/defaults.js.map +1 -1
  122. package/dist/transformers/filters/index.d.ts +31 -10
  123. package/dist/transformers/filters/index.d.ts.map +1 -1
  124. package/dist/transformers/filters/index.js +36 -14
  125. package/dist/transformers/filters/index.js.map +1 -1
  126. package/dist/transformers/format.d.ts +14 -7
  127. package/dist/transformers/format.d.ts.map +1 -1
  128. package/dist/transformers/format.js +15 -11
  129. package/dist/transformers/format.js.map +1 -1
  130. package/dist/transformers/index.js +49 -29
  131. package/dist/transformers/index.js.map +1 -1
  132. package/dist/transformers/inlineCss.d.ts +84 -0
  133. package/dist/transformers/inlineCss.d.ts.map +1 -0
  134. package/dist/transformers/{inlineCSS.js → inlineCss.js} +24 -14
  135. package/dist/transformers/inlineCss.js.map +1 -0
  136. package/dist/transformers/inlineLink.d.ts +26 -5
  137. package/dist/transformers/inlineLink.d.ts.map +1 -1
  138. package/dist/transformers/inlineLink.js +31 -7
  139. package/dist/transformers/inlineLink.js.map +1 -1
  140. package/dist/transformers/minify.d.ts +13 -9
  141. package/dist/transformers/minify.d.ts.map +1 -1
  142. package/dist/transformers/minify.js +14 -13
  143. package/dist/transformers/minify.js.map +1 -1
  144. package/dist/transformers/msoPlaceholders.js +1 -2
  145. package/dist/transformers/msoPlaceholders.js.map +1 -1
  146. package/dist/transformers/purgeCss.d.ts +43 -0
  147. package/dist/transformers/purgeCss.d.ts.map +1 -0
  148. package/dist/transformers/{purgeCSS.js → purgeCss.js} +32 -25
  149. package/dist/transformers/purgeCss.js.map +1 -0
  150. package/dist/transformers/removeAttributes.d.ts +43 -20
  151. package/dist/transformers/removeAttributes.d.ts.map +1 -1
  152. package/dist/transformers/removeAttributes.js +34 -27
  153. package/dist/transformers/removeAttributes.js.map +1 -1
  154. package/dist/transformers/replaceStrings.js +1 -1
  155. package/dist/transformers/replaceStrings.js.map +1 -1
  156. package/dist/transformers/safeClassNames.js +1 -2
  157. package/dist/transformers/safeClassNames.js.map +1 -1
  158. package/dist/transformers/shorthandCss.d.ts +47 -0
  159. package/dist/transformers/shorthandCss.d.ts.map +1 -0
  160. package/dist/transformers/shorthandCss.js +61 -0
  161. package/dist/transformers/shorthandCss.js.map +1 -0
  162. package/dist/transformers/sixHex.d.ts +16 -7
  163. package/dist/transformers/sixHex.d.ts.map +1 -1
  164. package/dist/transformers/sixHex.js +21 -9
  165. package/dist/transformers/sixHex.js.map +1 -1
  166. package/dist/transformers/tailwindComponent.js +1 -2
  167. package/dist/transformers/tailwindComponent.js.map +1 -1
  168. package/dist/transformers/tailwindcss.js +1 -2
  169. package/dist/transformers/tailwindcss.js.map +1 -1
  170. package/dist/transformers/urlQuery.d.ts +26 -14
  171. package/dist/transformers/urlQuery.d.ts.map +1 -1
  172. package/dist/transformers/urlQuery.js +32 -20
  173. package/dist/transformers/urlQuery.js.map +1 -1
  174. package/dist/types/config.d.ts +71 -19
  175. package/dist/types/config.d.ts.map +1 -1
  176. package/dist/types/config.js +1 -1
  177. package/dist/types/index.d.ts +2 -2
  178. package/dist/types/index.js +1 -1
  179. package/dist/utils/ast/index.js +1 -2
  180. package/dist/utils/ast/parser.js +1 -2
  181. package/dist/utils/ast/parser.js.map +1 -1
  182. package/dist/utils/ast/serializer.js +1 -2
  183. package/dist/utils/ast/serializer.js.map +1 -1
  184. package/dist/utils/ast/walker.js +1 -1
  185. package/dist/utils/ast/walker.js.map +1 -1
  186. package/dist/utils/compileTailwindCss.js +1 -2
  187. package/dist/utils/compileTailwindCss.js.map +1 -1
  188. package/dist/utils/decodeStyleEntities.js +1 -1
  189. package/dist/utils/decodeStyleEntities.js.map +1 -1
  190. package/dist/utils/detect.js +1 -2
  191. package/dist/utils/detect.js.map +1 -1
  192. package/dist/utils/output-markers.d.ts +29 -0
  193. package/dist/utils/output-markers.d.ts.map +1 -0
  194. package/dist/utils/output-markers.js +68 -0
  195. package/dist/utils/output-markers.js.map +1 -0
  196. package/dist/utils/url.js +1 -2
  197. package/dist/utils/url.js.map +1 -1
  198. package/node_modules/maizzle/README.md +24 -0
  199. package/node_modules/maizzle/dist/commands/make/component.mjs +1 -1
  200. package/node_modules/maizzle/dist/commands/make/config.mjs +1 -1
  201. package/node_modules/maizzle/dist/commands/make/layout.mjs +3 -3
  202. package/node_modules/maizzle/dist/commands/make/scaffold.mjs +1 -1
  203. package/node_modules/maizzle/dist/commands/make/stubs/Layout.vue +146 -0
  204. package/node_modules/maizzle/dist/commands/make/stubs/component.vue +2 -4
  205. package/node_modules/maizzle/dist/commands/make/stubs/config.ts +1 -5
  206. package/node_modules/maizzle/dist/commands/make/template.mjs +1 -1
  207. package/node_modules/maizzle/dist/commands/new.mjs +29 -24
  208. package/node_modules/maizzle/dist/index.mjs +28 -8
  209. package/node_modules/maizzle/package.json +1 -1
  210. package/package.json +2 -2
  211. package/dist/transformers/inlineCSS.d.ts +0 -17
  212. package/dist/transformers/inlineCSS.d.ts.map +0 -1
  213. package/dist/transformers/inlineCSS.js.map +0 -1
  214. package/dist/transformers/purgeCSS.d.ts +0 -23
  215. package/dist/transformers/purgeCSS.d.ts.map +0 -1
  216. package/dist/transformers/purgeCSS.js.map +0 -1
  217. package/dist/transformers/shorthandCSS.d.ts +0 -24
  218. package/dist/transformers/shorthandCSS.d.ts.map +0 -1
  219. package/dist/transformers/shorthandCSS.js +0 -48
  220. package/dist/transformers/shorthandCSS.js.map +0 -1
  221. package/node_modules/maizzle/dist/commands/make/stubs/layout.vue +0 -39
@@ -1 +1 @@
1
- {"version":3,"file":"walker.js","names":[],"sources":["../../../src/utils/ast/walker.ts"],"sourcesContent":["import type { ChildNode } from 'domhandler'\n\nexport function walk(ast: ChildNode[], callback: (node: ChildNode) => void): void {\n function traverse(node: ChildNode) {\n callback(node)\n\n if ('children' in node && node.children && node.children.length > 0) {\n for (const child of node.children) {\n traverse(child)\n }\n }\n }\n\n for (const node of ast) {\n traverse(node)\n }\n}\n"],"mappings":";AAEA,SAAgB,KAAK,KAAkB,UAA2C;CAChF,SAAS,SAAS,MAAiB;AACjC,WAAS,KAAK;AAEd,MAAI,cAAc,QAAQ,KAAK,YAAY,KAAK,SAAS,SAAS,EAChE,MAAK,MAAM,SAAS,KAAK,SACvB,UAAS,MAAM;;AAKrB,MAAK,MAAM,QAAQ,IACjB,UAAS,KAAK"}
1
+ {"version":3,"file":"walker.js","names":[],"sources":["../../../src/utils/ast/walker.ts"],"sourcesContent":["import type { ChildNode } from 'domhandler'\n\nexport function walk(ast: ChildNode[], callback: (node: ChildNode) => void): void {\n function traverse(node: ChildNode) {\n callback(node)\n\n if ('children' in node && node.children && node.children.length > 0) {\n for (const child of node.children) {\n traverse(child)\n }\n }\n }\n\n for (const node of ast) {\n traverse(node)\n }\n}\n"],"mappings":";AAEA,SAAgB,KAAK,KAAkB,UAA2C;CAChF,SAAS,SAAS,MAAiB;EACjC,SAAS,KAAK;EAEd,IAAI,cAAc,QAAQ,KAAK,YAAY,KAAK,SAAS,SAAS,GAChE,KAAK,MAAM,SAAS,KAAK,UACvB,SAAS,MAAM;;CAKrB,KAAK,MAAM,QAAQ,KACjB,SAAS,KAAK"}
@@ -9,7 +9,6 @@ import tailwindcssPostcss from "@tailwindcss/postcss";
9
9
  import postcssCalc from "postcss-calc";
10
10
  import safeParser from "postcss-safe-parser";
11
11
  import { transform } from "lightningcss";
12
-
13
12
  //#region src/utils/compileTailwindCss.ts
14
13
  function createTailwindProcessor(config) {
15
14
  return postcss([
@@ -49,7 +48,7 @@ async function compileTailwindCss(cssInput, config, from) {
49
48
  parser: safeParser
50
49
  })).css), config);
51
50
  }
52
-
53
51
  //#endregion
54
52
  export { compileTailwindCss, createTailwindProcessor, lowerCssSyntax, optimizeTailwindCss };
53
+
55
54
  //# sourceMappingURL=compileTailwindCss.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compileTailwindCss.js","names":["resolveProps","pruneVars"],"sources":["../../src/utils/compileTailwindCss.ts"],"sourcesContent":["import postcss from 'postcss'\nimport tailwindcssPostcss from '@tailwindcss/postcss'\nimport postcssCalc from 'postcss-calc'\nimport safeParser from 'postcss-safe-parser'\nimport { transform } from 'lightningcss'\nimport resolveProps from '../plugins/postcss/resolveProps.ts'\nimport pruneVars from '../plugins/postcss/pruneVars.ts'\nimport { tailwindCleanup } from '../plugins/postcss/tailwindCleanup.ts'\nimport { mergeMediaQueries } from '../plugins/postcss/mergeMediaQueries.ts'\nimport { quoteFontFamilies } from '../plugins/postcss/quoteFontFamilies.ts'\nimport { resolveMaizzleImports } from '../plugins/postcss/resolveMaizzleImports.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\n\nexport function createTailwindProcessor(config: MaizzleConfig) {\n return postcss([\n // Must run before @tailwindcss/postcss so it sees absolute import paths\n resolveMaizzleImports(),\n tailwindcssPostcss({\n base: config.css?.base,\n transformAssetUrls: false,\n optimize: false,\n }),\n resolveProps(),\n postcssCalc({}),\n pruneVars(),\n ])\n}\n\nexport function lowerCssSyntax(css: string): string {\n const result = transform({\n filename: 'email.css',\n code: Buffer.from(css),\n minify: false,\n targets: { ie: 4 << 5 },\n })\n\n return result.code.toString()\n}\n\nexport async function optimizeTailwindCss(css: string, config: MaizzleConfig): Promise<string> {\n const plugins: postcss.Plugin[] = [...tailwindCleanup(config), quoteFontFamilies()]\n\n const mediaPlugin = mergeMediaQueries(config)\n if (mediaPlugin) plugins.push(mediaPlugin)\n\n const result = await postcss(plugins).process(css, { from: undefined })\n\n return result.css\n}\n\n/**\n * Compile a Tailwind CSS source string into final email-safe CSS:\n * runs @tailwindcss/postcss, lowers modern syntax via lightningcss,\n * then applies cleanup + media-query merging.\n */\nexport async function compileTailwindCss(\n cssInput: string,\n config: MaizzleConfig,\n from: string,\n): Promise<string> {\n const processor = createTailwindProcessor(config)\n const result = await processor.process(cssInput, { from, parser: safeParser })\n const lowered = lowerCssSyntax(result.css)\n return optimizeTailwindCss(lowered, config)\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,wBAAwB,QAAuB;AAC7D,QAAO,QAAQ;EAEb,uBAAuB;EACvB,mBAAmB;GACjB,MAAM,OAAO,KAAK;GAClB,oBAAoB;GACpB,UAAU;GACX,CAAC;EACFA,sBAAc;EACd,YAAY,EAAE,CAAC;EACfC,mBAAW;EACZ,CAAC;;AAGJ,SAAgB,eAAe,KAAqB;AAQlD,QAPe,UAAU;EACvB,UAAU;EACV,MAAM,OAAO,KAAK,IAAI;EACtB,QAAQ;EACR,SAAS,EAAE,IAAI,KAAQ;EACxB,CAAC,CAEY,KAAK,UAAU;;AAG/B,eAAsB,oBAAoB,KAAa,QAAwC;CAC7F,MAAM,UAA4B,CAAC,GAAG,gBAAgB,OAAO,EAAE,mBAAmB,CAAC;CAEnF,MAAM,cAAc,kBAAkB,OAAO;AAC7C,KAAI,YAAa,SAAQ,KAAK,YAAY;AAI1C,SAFe,MAAM,QAAQ,QAAQ,CAAC,QAAQ,KAAK,EAAE,MAAM,QAAW,CAAC,EAEzD;;;;;;;AAQhB,eAAsB,mBACpB,UACA,QACA,MACiB;AAIjB,QAAO,oBADS,gBADD,MADG,wBAAwB,OAAO,CAClB,QAAQ,UAAU;EAAE;EAAM,QAAQ;EAAY,CAAC,EACxC,IAAI,EACN,OAAO"}
1
+ {"version":3,"file":"compileTailwindCss.js","names":["resolveProps","pruneVars"],"sources":["../../src/utils/compileTailwindCss.ts"],"sourcesContent":["import postcss from 'postcss'\nimport tailwindcssPostcss from '@tailwindcss/postcss'\nimport postcssCalc from 'postcss-calc'\nimport safeParser from 'postcss-safe-parser'\nimport { transform } from 'lightningcss'\nimport resolveProps from '../plugins/postcss/resolveProps.ts'\nimport pruneVars from '../plugins/postcss/pruneVars.ts'\nimport { tailwindCleanup } from '../plugins/postcss/tailwindCleanup.ts'\nimport { mergeMediaQueries } from '../plugins/postcss/mergeMediaQueries.ts'\nimport { quoteFontFamilies } from '../plugins/postcss/quoteFontFamilies.ts'\nimport { resolveMaizzleImports } from '../plugins/postcss/resolveMaizzleImports.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\n\nexport function createTailwindProcessor(config: MaizzleConfig) {\n return postcss([\n // Must run before @tailwindcss/postcss so it sees absolute import paths\n resolveMaizzleImports(),\n tailwindcssPostcss({\n base: config.css?.base,\n transformAssetUrls: false,\n optimize: false,\n }),\n resolveProps(),\n postcssCalc({}),\n pruneVars(),\n ])\n}\n\nexport function lowerCssSyntax(css: string): string {\n const result = transform({\n filename: 'email.css',\n code: Buffer.from(css),\n minify: false,\n targets: { ie: 4 << 5 },\n })\n\n return result.code.toString()\n}\n\nexport async function optimizeTailwindCss(css: string, config: MaizzleConfig): Promise<string> {\n const plugins: postcss.Plugin[] = [...tailwindCleanup(config), quoteFontFamilies()]\n\n const mediaPlugin = mergeMediaQueries(config)\n if (mediaPlugin) plugins.push(mediaPlugin)\n\n const result = await postcss(plugins).process(css, { from: undefined })\n\n return result.css\n}\n\n/**\n * Compile a Tailwind CSS source string into final email-safe CSS:\n * runs @tailwindcss/postcss, lowers modern syntax via lightningcss,\n * then applies cleanup + media-query merging.\n */\nexport async function compileTailwindCss(\n cssInput: string,\n config: MaizzleConfig,\n from: string,\n): Promise<string> {\n const processor = createTailwindProcessor(config)\n const result = await processor.process(cssInput, { from, parser: safeParser })\n const lowered = lowerCssSyntax(result.css)\n return optimizeTailwindCss(lowered, config)\n}\n"],"mappings":";;;;;;;;;;;;AAaA,SAAgB,wBAAwB,QAAuB;CAC7D,OAAO,QAAQ;EAEb,uBAAuB;EACvB,mBAAmB;GACjB,MAAM,OAAO,KAAK;GAClB,oBAAoB;GACpB,UAAU;GACX,CAAC;EACFA,sBAAc;EACd,YAAY,EAAE,CAAC;EACfC,mBAAW;EACZ,CAAC;;AAGJ,SAAgB,eAAe,KAAqB;CAQlD,OAPe,UAAU;EACvB,UAAU;EACV,MAAM,OAAO,KAAK,IAAI;EACtB,QAAQ;EACR,SAAS,EAAE,IAAI,KAAQ;EACxB,CAEY,CAAC,KAAK,UAAU;;AAG/B,eAAsB,oBAAoB,KAAa,QAAwC;CAC7F,MAAM,UAA4B,CAAC,GAAG,gBAAgB,OAAO,EAAE,mBAAmB,CAAC;CAEnF,MAAM,cAAc,kBAAkB,OAAO;CAC7C,IAAI,aAAa,QAAQ,KAAK,YAAY;CAI1C,QAAO,MAFc,QAAQ,QAAQ,CAAC,QAAQ,KAAK,EAAE,MAAM,KAAA,GAAW,CAAC,EAEzD;;;;;;;AAQhB,eAAsB,mBACpB,UACA,QACA,MACiB;CAIjB,OAAO,oBADS,gBAAe,MAFb,wBAAwB,OACZ,CAAC,QAAQ,UAAU;EAAE;EAAM,QAAQ;EAAY,CAAC,EACxC,IACJ,EAAE,OAAO"}
@@ -12,7 +12,7 @@
12
12
  function decodeStyleEntities(s) {
13
13
  return s.replace(/&quot;/g, "\"").replace(/&#39;/g, "'").replace(/&apos;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
14
14
  }
15
-
16
15
  //#endregion
17
16
  export { decodeStyleEntities };
17
+
18
18
  //# sourceMappingURL=decodeStyleEntities.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decodeStyleEntities.js","names":[],"sources":["../../src/utils/decodeStyleEntities.ts"],"sourcesContent":["/**\n * Decode HTML entities that Vue SSR encodes inside `<style>` tags.\n *\n * Vue's `renderToString` HTML-encodes quotes and angle brackets within\n * style elements in templates, breaking CSS like\n * `@import \"tailwindcss\"` → `@import &quot;tailwindcss&quot;`.\n *\n * `&amp;` is decoded last so previously-decoded entities are not\n * re-processed.\n */\nexport function decodeStyleEntities(s: string): string {\n return s\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&apos;/g, \"'\")\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&amp;/g, '&')\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAgB,oBAAoB,GAAmB;AACrD,QAAO,EACJ,QAAQ,WAAW,KAAI,CACvB,QAAQ,UAAU,IAAI,CACtB,QAAQ,WAAW,IAAI,CACvB,QAAQ,SAAS,IAAI,CACrB,QAAQ,SAAS,IAAI,CACrB,QAAQ,UAAU,IAAI"}
1
+ {"version":3,"file":"decodeStyleEntities.js","names":[],"sources":["../../src/utils/decodeStyleEntities.ts"],"sourcesContent":["/**\n * Decode HTML entities that Vue SSR encodes inside `<style>` tags.\n *\n * Vue's `renderToString` HTML-encodes quotes and angle brackets within\n * style elements in templates, breaking CSS like\n * `@import \"tailwindcss\"` → `@import &quot;tailwindcss&quot;`.\n *\n * `&amp;` is decoded last so previously-decoded entities are not\n * re-processed.\n */\nexport function decodeStyleEntities(s: string): string {\n return s\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&apos;/g, \"'\")\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&amp;/g, '&')\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAgB,oBAAoB,GAAmB;CACrD,OAAO,EACJ,QAAQ,WAAW,KAAI,CACvB,QAAQ,UAAU,IAAI,CACtB,QAAQ,WAAW,IAAI,CACvB,QAAQ,SAAS,IAAI,CACrB,QAAQ,SAAS,IAAI,CACrB,QAAQ,UAAU,IAAI"}
@@ -1,11 +1,10 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
-
4
3
  //#region src/utils/detect.ts
5
4
  function isLaravel(cwd = process.cwd()) {
6
5
  return existsSync(resolve(cwd, "artisan"));
7
6
  }
8
-
9
7
  //#endregion
10
8
  export { isLaravel };
9
+
11
10
  //# sourceMappingURL=detect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"detect.js","names":[],"sources":["../../src/utils/detect.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nexport function isLaravel(cwd: string = process.cwd()): boolean {\n return existsSync(resolve(cwd, 'artisan'))\n}\n"],"mappings":";;;;AAGA,SAAgB,UAAU,MAAc,QAAQ,KAAK,EAAW;AAC9D,QAAO,WAAW,QAAQ,KAAK,UAAU,CAAC"}
1
+ {"version":3,"file":"detect.js","names":[],"sources":["../../src/utils/detect.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nexport function isLaravel(cwd: string = process.cwd()): boolean {\n return existsSync(resolve(cwd, 'artisan'))\n}\n"],"mappings":";;;AAGA,SAAgB,UAAU,MAAc,QAAQ,KAAK,EAAW;CAC9D,OAAO,WAAW,QAAQ,KAAK,UAAU,CAAC"}
@@ -0,0 +1,29 @@
1
+ //#region src/utils/output-markers.d.ts
2
+ /**
3
+ * Sentinel attributes the `<Plaintext>` and `<NotPlaintext>`
4
+ * components stamp on their wrapper `<div>` so the build step
5
+ * can route their slot content to one output and remove it
6
+ * entirely from the other.
7
+ */
8
+ declare const PLAINTEXT_ONLY_ATTR = "data-maizzle-plaintext-only";
9
+ declare const HTML_ONLY_ATTR = "data-maizzle-html-only";
10
+ /**
11
+ * Strip output markers for the HTML output: drop plaintext-only
12
+ * subtrees entirely, unwrap html-only wrappers (keep children).
13
+ *
14
+ * When no markers are present, the input is returned unchanged so
15
+ * the post-transformer formatting (prettify, XHTML self-closing
16
+ * slashes, etc.) survives intact for the typical case.
17
+ */
18
+ declare function stripForHtml(html: string): string;
19
+ /**
20
+ * Strip output markers for the plaintext source: drop html-only
21
+ * subtrees entirely, unwrap plaintext-only wrappers (keep children).
22
+ *
23
+ * The result is fed to `createPlaintext`, which then strips all
24
+ * remaining tags via `string-strip-html`.
25
+ */
26
+ declare function stripForPlaintext(html: string): string;
27
+ //#endregion
28
+ export { HTML_ONLY_ATTR, PLAINTEXT_ONLY_ATTR, stripForHtml, stripForPlaintext };
29
+ //# sourceMappingURL=output-markers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-markers.d.ts","names":[],"sources":["../../src/utils/output-markers.ts"],"mappings":";;AASA;;;;;cAAa,mBAAA;AAAA,cACA,cAAA;;;;AA+Cb;;;;;iBAAgB,YAAA,CAAa,IAAA;;;;;;;;iBAgBb,iBAAA,CAAkB,IAAA"}
@@ -0,0 +1,68 @@
1
+ import { parse } from "./ast/parser.js";
2
+ import { serialize } from "./ast/serializer.js";
3
+ import "./ast/index.js";
4
+ //#region src/utils/output-markers.ts
5
+ /**
6
+ * Sentinel attributes the `<Plaintext>` and `<NotPlaintext>`
7
+ * components stamp on their wrapper `<div>` so the build step
8
+ * can route their slot content to one output and remove it
9
+ * entirely from the other.
10
+ */
11
+ const PLAINTEXT_ONLY_ATTR = "data-maizzle-plaintext-only";
12
+ const HTML_ONLY_ATTR = "data-maizzle-html-only";
13
+ const isElement = (n) => n.type === "tag";
14
+ /**
15
+ * Apply marker rules to a forest of nodes.
16
+ *
17
+ * - `drop` removes the matched element and its descendants.
18
+ * - `unwrap` removes the wrapper element but keeps its children,
19
+ * splicing them into the parent's child list in place.
20
+ *
21
+ * Non-matching elements recurse so nested markers are handled.
22
+ */
23
+ function applyRules(nodes, rules) {
24
+ const out = [];
25
+ for (const n of nodes) {
26
+ if (isElement(n)) {
27
+ const match = rules.find(([attr]) => n.attribs?.[attr] !== void 0);
28
+ if (match) {
29
+ const [, op] = match;
30
+ if (op === "drop") continue;
31
+ out.push(...applyRules(n.children ?? [], rules));
32
+ continue;
33
+ }
34
+ if (n.children?.length) n.children = applyRules(n.children, rules);
35
+ }
36
+ out.push(n);
37
+ }
38
+ return out;
39
+ }
40
+ const hasMarkers = (html) => html.includes("data-maizzle-plaintext-only") || html.includes("data-maizzle-html-only");
41
+ /**
42
+ * Strip output markers for the HTML output: drop plaintext-only
43
+ * subtrees entirely, unwrap html-only wrappers (keep children).
44
+ *
45
+ * When no markers are present, the input is returned unchanged so
46
+ * the post-transformer formatting (prettify, XHTML self-closing
47
+ * slashes, etc.) survives intact for the typical case.
48
+ */
49
+ function stripForHtml(html) {
50
+ if (!hasMarkers(html)) return html;
51
+ const isXhtml = /<!DOCTYPE\s+[^>]*xhtml/i.test(html);
52
+ return serialize(applyRules(parse(html), [[PLAINTEXT_ONLY_ATTR, "drop"], [HTML_ONLY_ATTR, "unwrap"]]), { selfClosingTags: isXhtml });
53
+ }
54
+ /**
55
+ * Strip output markers for the plaintext source: drop html-only
56
+ * subtrees entirely, unwrap plaintext-only wrappers (keep children).
57
+ *
58
+ * The result is fed to `createPlaintext`, which then strips all
59
+ * remaining tags via `string-strip-html`.
60
+ */
61
+ function stripForPlaintext(html) {
62
+ if (!hasMarkers(html)) return html;
63
+ return serialize(applyRules(parse(html), [[HTML_ONLY_ATTR, "drop"], [PLAINTEXT_ONLY_ATTR, "unwrap"]]));
64
+ }
65
+ //#endregion
66
+ export { HTML_ONLY_ATTR, PLAINTEXT_ONLY_ATTR, stripForHtml, stripForPlaintext };
67
+
68
+ //# sourceMappingURL=output-markers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-markers.js","names":[],"sources":["../../src/utils/output-markers.ts"],"sourcesContent":["import { parse, serialize } from './ast/index.ts'\nimport type { ChildNode, Element } from 'domhandler'\n\n/**\n * Sentinel attributes the `<Plaintext>` and `<NotPlaintext>`\n * components stamp on their wrapper `<div>` so the build step\n * can route their slot content to one output and remove it\n * entirely from the other.\n */\nexport const PLAINTEXT_ONLY_ATTR = 'data-maizzle-plaintext-only'\nexport const HTML_ONLY_ATTR = 'data-maizzle-html-only'\n\ntype Op = 'drop' | 'unwrap'\ntype Rule = readonly [attr: string, op: Op]\n\nconst isElement = (n: ChildNode): n is Element => n.type === 'tag'\n\n/**\n * Apply marker rules to a forest of nodes.\n *\n * - `drop` removes the matched element and its descendants.\n * - `unwrap` removes the wrapper element but keeps its children,\n * splicing them into the parent's child list in place.\n *\n * Non-matching elements recurse so nested markers are handled.\n */\nfunction applyRules(nodes: ChildNode[], rules: readonly Rule[]): ChildNode[] {\n const out: ChildNode[] = []\n for (const n of nodes) {\n if (isElement(n)) {\n const match = rules.find(([attr]) => n.attribs?.[attr] !== undefined)\n if (match) {\n const [, op] = match\n if (op === 'drop') continue\n out.push(...applyRules((n.children ?? []) as ChildNode[], rules))\n continue\n }\n if (n.children?.length) {\n n.children = applyRules(n.children as ChildNode[], rules) as Element['children']\n }\n }\n out.push(n)\n }\n return out\n}\n\nconst hasMarkers = (html: string): boolean =>\n html.includes(PLAINTEXT_ONLY_ATTR) || html.includes(HTML_ONLY_ATTR)\n\n/**\n * Strip output markers for the HTML output: drop plaintext-only\n * subtrees entirely, unwrap html-only wrappers (keep children).\n *\n * When no markers are present, the input is returned unchanged so\n * the post-transformer formatting (prettify, XHTML self-closing\n * slashes, etc.) survives intact for the typical case.\n */\nexport function stripForHtml(html: string): string {\n if (!hasMarkers(html)) return html\n const isXhtml = /<!DOCTYPE\\s+[^>]*xhtml/i.test(html)\n return serialize(applyRules(parse(html), [\n [PLAINTEXT_ONLY_ATTR, 'drop'],\n [HTML_ONLY_ATTR, 'unwrap'],\n ]), { selfClosingTags: isXhtml })\n}\n\n/**\n * Strip output markers for the plaintext source: drop html-only\n * subtrees entirely, unwrap plaintext-only wrappers (keep children).\n *\n * The result is fed to `createPlaintext`, which then strips all\n * remaining tags via `string-strip-html`.\n */\nexport function stripForPlaintext(html: string): string {\n if (!hasMarkers(html)) return html\n return serialize(applyRules(parse(html), [\n [HTML_ONLY_ATTR, 'drop'],\n [PLAINTEXT_ONLY_ATTR, 'unwrap'],\n ]))\n}\n"],"mappings":";;;;;;;;;;AASA,MAAa,sBAAsB;AACnC,MAAa,iBAAiB;AAK9B,MAAM,aAAa,MAA+B,EAAE,SAAS;;;;;;;;;;AAW7D,SAAS,WAAW,OAAoB,OAAqC;CAC3E,MAAM,MAAmB,EAAE;CAC3B,KAAK,MAAM,KAAK,OAAO;EACrB,IAAI,UAAU,EAAE,EAAE;GAChB,MAAM,QAAQ,MAAM,MAAM,CAAC,UAAU,EAAE,UAAU,UAAU,KAAA,EAAU;GACrE,IAAI,OAAO;IACT,MAAM,GAAG,MAAM;IACf,IAAI,OAAO,QAAQ;IACnB,IAAI,KAAK,GAAG,WAAY,EAAE,YAAY,EAAE,EAAkB,MAAM,CAAC;IACjE;;GAEF,IAAI,EAAE,UAAU,QACd,EAAE,WAAW,WAAW,EAAE,UAAyB,MAAM;;EAG7D,IAAI,KAAK,EAAE;;CAEb,OAAO;;AAGT,MAAM,cAAc,SAClB,KAAK,SAAA,8BAA6B,IAAI,KAAK,SAAA,yBAAwB;;;;;;;;;AAUrE,SAAgB,aAAa,MAAsB;CACjD,IAAI,CAAC,WAAW,KAAK,EAAE,OAAO;CAC9B,MAAM,UAAU,0BAA0B,KAAK,KAAK;CACpD,OAAO,UAAU,WAAW,MAAM,KAAK,EAAE,CACvC,CAAC,qBAAqB,OAAO,EAC7B,CAAC,gBAAgB,SAAS,CAC3B,CAAC,EAAE,EAAE,iBAAiB,SAAS,CAAC;;;;;;;;;AAUnC,SAAgB,kBAAkB,MAAsB;CACtD,IAAI,CAAC,WAAW,KAAK,EAAE,OAAO;CAC9B,OAAO,UAAU,WAAW,MAAM,KAAK,EAAE,CACvC,CAAC,gBAAgB,OAAO,EACxB,CAAC,qBAAqB,SAAS,CAChC,CAAC,CAAC"}
package/dist/utils/url.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import isUrl from "is-url-superb";
2
-
3
2
  //#region src/utils/url.ts
4
3
  const defaultTags = {
5
4
  a: ["href"],
@@ -26,7 +25,7 @@ function processSrcset(srcset, baseUrl) {
26
25
  return parts.join(" ");
27
26
  }).join(", ");
28
27
  }
29
-
30
28
  //#endregion
31
29
  export { defaultTags, isAbsoluteUrl, processSrcset, urlAttributes };
30
+
32
31
  //# sourceMappingURL=url.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"url.js","names":[],"sources":["../../src/utils/url.ts"],"sourcesContent":["import isUrl from 'is-url-superb'\n\nexport const defaultTags: Record<string, string[]> = {\n a: ['href'],\n img: ['src', 'srcset'],\n video: ['src', 'poster'],\n source: ['src', 'srcset'],\n link: ['href'],\n script: ['src'],\n object: ['data'],\n embed: ['src'],\n iframe: ['src'],\n 'v:image': ['src'],\n 'v:fill': ['src'],\n}\n\nexport const urlAttributes = [...new Set(Object.values(defaultTags).flat())]\n\nexport function isAbsoluteUrl(url: string): boolean {\n if (!url) return true\n\n return url.startsWith('//') || url.startsWith('#') || url.startsWith('?') || isUrl(url)\n}\n\nexport function processSrcset(srcset: string, baseUrl: string): string {\n return srcset.split(',').map(entry => {\n const parts = entry.trim().split(/\\s+/)\n\n if (parts[0] && !isAbsoluteUrl(parts[0])) {\n parts[0] = baseUrl + parts[0]\n }\n\n return parts.join(' ')\n }).join(', ')\n}\n"],"mappings":";;;AAEA,MAAa,cAAwC;CACnD,GAAG,CAAC,OAAO;CACX,KAAK,CAAC,OAAO,SAAS;CACtB,OAAO,CAAC,OAAO,SAAS;CACxB,QAAQ,CAAC,OAAO,SAAS;CACzB,MAAM,CAAC,OAAO;CACd,QAAQ,CAAC,MAAM;CACf,QAAQ,CAAC,OAAO;CAChB,OAAO,CAAC,MAAM;CACd,QAAQ,CAAC,MAAM;CACf,WAAW,CAAC,MAAM;CAClB,UAAU,CAAC,MAAM;CAClB;AAED,MAAa,gBAAgB,CAAC,GAAG,IAAI,IAAI,OAAO,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;AAE5E,SAAgB,cAAc,KAAsB;AAClD,KAAI,CAAC,IAAK,QAAO;AAEjB,QAAO,IAAI,WAAW,KAAK,IAAI,IAAI,WAAW,IAAI,IAAI,IAAI,WAAW,IAAI,IAAI,MAAM,IAAI;;AAGzF,SAAgB,cAAc,QAAgB,SAAyB;AACrE,QAAO,OAAO,MAAM,IAAI,CAAC,KAAI,UAAS;EACpC,MAAM,QAAQ,MAAM,MAAM,CAAC,MAAM,MAAM;AAEvC,MAAI,MAAM,MAAM,CAAC,cAAc,MAAM,GAAG,CACtC,OAAM,KAAK,UAAU,MAAM;AAG7B,SAAO,MAAM,KAAK,IAAI;GACtB,CAAC,KAAK,KAAK"}
1
+ {"version":3,"file":"url.js","names":[],"sources":["../../src/utils/url.ts"],"sourcesContent":["import isUrl from 'is-url-superb'\n\nexport const defaultTags: Record<string, string[]> = {\n a: ['href'],\n img: ['src', 'srcset'],\n video: ['src', 'poster'],\n source: ['src', 'srcset'],\n link: ['href'],\n script: ['src'],\n object: ['data'],\n embed: ['src'],\n iframe: ['src'],\n 'v:image': ['src'],\n 'v:fill': ['src'],\n}\n\nexport const urlAttributes = [...new Set(Object.values(defaultTags).flat())]\n\nexport function isAbsoluteUrl(url: string): boolean {\n if (!url) return true\n\n return url.startsWith('//') || url.startsWith('#') || url.startsWith('?') || isUrl(url)\n}\n\nexport function processSrcset(srcset: string, baseUrl: string): string {\n return srcset.split(',').map(entry => {\n const parts = entry.trim().split(/\\s+/)\n\n if (parts[0] && !isAbsoluteUrl(parts[0])) {\n parts[0] = baseUrl + parts[0]\n }\n\n return parts.join(' ')\n }).join(', ')\n}\n"],"mappings":";;AAEA,MAAa,cAAwC;CACnD,GAAG,CAAC,OAAO;CACX,KAAK,CAAC,OAAO,SAAS;CACtB,OAAO,CAAC,OAAO,SAAS;CACxB,QAAQ,CAAC,OAAO,SAAS;CACzB,MAAM,CAAC,OAAO;CACd,QAAQ,CAAC,MAAM;CACf,QAAQ,CAAC,OAAO;CAChB,OAAO,CAAC,MAAM;CACd,QAAQ,CAAC,MAAM;CACf,WAAW,CAAC,MAAM;CAClB,UAAU,CAAC,MAAM;CAClB;AAED,MAAa,gBAAgB,CAAC,GAAG,IAAI,IAAI,OAAO,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;AAE5E,SAAgB,cAAc,KAAsB;CAClD,IAAI,CAAC,KAAK,OAAO;CAEjB,OAAO,IAAI,WAAW,KAAK,IAAI,IAAI,WAAW,IAAI,IAAI,IAAI,WAAW,IAAI,IAAI,MAAM,IAAI;;AAGzF,SAAgB,cAAc,QAAgB,SAAyB;CACrE,OAAO,OAAO,MAAM,IAAI,CAAC,KAAI,UAAS;EACpC,MAAM,QAAQ,MAAM,MAAM,CAAC,MAAM,MAAM;EAEvC,IAAI,MAAM,MAAM,CAAC,cAAc,MAAM,GAAG,EACtC,MAAM,KAAK,UAAU,MAAM;EAG7B,OAAO,MAAM,KAAK,IAAI;GACtB,CAAC,KAAK,KAAK"}
@@ -78,6 +78,8 @@ Start a local development server:
78
78
  maizzle serve
79
79
  ```
80
80
 
81
+ `maizzle dev` is an alias for `maizzle serve`.
82
+
81
83
  ### Build
82
84
 
83
85
  Build emails for production:
@@ -86,6 +88,28 @@ Build emails for production:
86
88
  maizzle build
87
89
  ```
88
90
 
91
+ #### Options
92
+
93
+ | Option | Description |
94
+ |--------|-------------|
95
+ | `-c, --config <path>` | Path to a Maizzle config file |
96
+ | `-o, --output <path>` | Output directory |
97
+ | `--dir <path>` | Source directory for email templates |
98
+ | `--ext <extension>` | Output file extension |
99
+ | `--pretty` | Pretty-print HTML output |
100
+ | `--minify` | Minify HTML output |
101
+ | `--plaintext` | Generate plaintext versions alongside HTML |
102
+
103
+ When `-c, --config` is set, the override flags (`-o`, `--dir`, `--ext`, `--pretty`, `--minify`, `--plaintext`) are ignored — your config file is used as-is.
104
+
105
+ ### Prepare
106
+
107
+ Generate IDE type definitions in `.maizzle/`. Run after adding new components or composables when you want auto-import types to update without starting the dev server:
108
+
109
+ ```bash
110
+ maizzle prepare
111
+ ```
112
+
89
113
  ### Scaffolding
90
114
 
91
115
  You may use the `make` command to scaffold new files for your project.
@@ -19,9 +19,9 @@ async function makeComponent(filePath) {
19
19
  path: () => p.text({
20
20
  message: "Directory to place it in",
21
21
  placeholder: "./components",
22
+ initialValue: "./components",
22
23
  validate: (value) => {
23
24
  if (!value) return "Please enter a path.";
24
- if (value[0] !== ".") return "Please enter a relative path.";
25
25
  }
26
26
  })
27
27
  }, { onCancel });
@@ -13,7 +13,7 @@ async function makeConfig(name) {
13
13
  placeholder: "production",
14
14
  validate: (value) => {
15
15
  if (!value) return "Please enter a config name.";
16
- if (value.includes(" ")) return "Use - instead of spaces.";
16
+ if (value.includes(" ")) return "Use - or . instead of spaces.";
17
17
  }
18
18
  }) }, { onCancel })).name}.config.ts`, "config.ts");
19
19
  }
@@ -4,7 +4,7 @@ import * as p from "@clack/prompts";
4
4
  //#region src/commands/make/layout.ts
5
5
  async function makeLayout(filePath) {
6
6
  if (filePath) {
7
- await scaffold(filePath, "layout.vue");
7
+ await scaffold(filePath, "Layout.vue");
8
8
  return;
9
9
  }
10
10
  p.intro(`${color.bgCyan(color.black(" maizzle make:layout "))}`);
@@ -19,13 +19,13 @@ async function makeLayout(filePath) {
19
19
  path: () => p.text({
20
20
  message: "Directory to place it in",
21
21
  placeholder: "./components",
22
+ initialValue: "./components",
22
23
  validate: (value) => {
23
24
  if (!value) return "Please enter a path.";
24
- if (value[0] !== ".") return "Please enter a relative path.";
25
25
  }
26
26
  })
27
27
  }, { onCancel });
28
- await scaffold(`${result.path}/${result.filename}`, "layout.vue");
28
+ await scaffold(`${result.path}/${result.filename}`, "Layout.vue");
29
29
  }
30
30
  //#endregion
31
31
  export { makeLayout as default };
@@ -21,7 +21,7 @@ async function scaffold(filePath, stubName) {
21
21
  }
22
22
  }
23
23
  function onCancel() {
24
- p.cancel("Cancelled.");
24
+ p.cancel("Canceled.");
25
25
  process.exit(0);
26
26
  }
27
27
  //#endregion
@@ -0,0 +1,146 @@
1
+ <script setup lang="ts">
2
+ import { computed, useAttrs, createStaticVNode, type PropType } from 'vue'
3
+ import { twMerge } from 'tailwind-merge'
4
+ import { outlookFallbackProp } from './utils.ts'
5
+ import { useOutlookFallback } from '../composables/useOutlookFallback'
6
+
7
+ defineOptions({ inheritAttrs: false })
8
+
9
+ const props = defineProps({
10
+ /**
11
+ * Classes to add to the `<body>` tag.
12
+ */
13
+ bodyClass: {
14
+ type: String,
15
+ default: ''
16
+ },
17
+ /**
18
+ * Language code for the `lang` and `xml:lang` attributes.
19
+ *
20
+ * @default 'en'
21
+ */
22
+ lang: {
23
+ type: String,
24
+ default: 'en'
25
+ },
26
+ /**
27
+ * Text direction.
28
+ *
29
+ * @default 'ltr'
30
+ */
31
+ dir: {
32
+ type: String as PropType<'ltr' | 'rtl'>,
33
+ default: 'ltr'
34
+ },
35
+ /**
36
+ * Render an empty `<head>` before the main head element.
37
+ *
38
+ * This is a workaround for Yahoo! Mail on Android, which
39
+ * strips the first `<head>` element it finds.
40
+ *
41
+ * @default false
42
+ */
43
+ doubleHead: {
44
+ type: [Boolean, String],
45
+ default: false
46
+ },
47
+ /**
48
+ * Accessible label for the email article wrapper.
49
+ *
50
+ * Used as the `aria-label` on the inner `<div role="article">`.
51
+ *
52
+ * @example 'Order confirmation'
53
+ */
54
+ ariaLabel: {
55
+ type: String,
56
+ default: undefined
57
+ },
58
+ /**
59
+ * Toggle Outlook (MSO) and VML fallback markup for this
60
+ * component and all descendants.
61
+ *
62
+ * When `false`, skips MSO ghost tables, VML shapes,
63
+ * `xmlns:v`/`xmlns:o` attributes, and mso-specific CSS
64
+ * in all built-in components.
65
+ *
66
+ * @default true
67
+ */
68
+ outlookFallback: outlookFallbackProp,
69
+ })
70
+
71
+ const outlookFallback = useOutlookFallback(props.outlookFallback)
72
+
73
+ const attrs = useAttrs()
74
+ const bodyMergedClass = computed(() => twMerge('m-0 p-0 size-full [word-break:break-word]', props.bodyClass))
75
+ const articleMergedClass = computed(() => twMerge('[font-size:max(16px,1rem)] font-inter', attrs.class as string))
76
+
77
+ const EmptyHead = () => createStaticVNode('<head></head>', 1)
78
+
79
+ const MsoHead = () => createStaticVNode(
80
+ `<!--[if mso]>
81
+ <style>
82
+ td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;}
83
+ .mso-break-all {word-break: break-all;}
84
+ </style>
85
+ <![endif]-->`,
86
+ 1
87
+ )
88
+
89
+ const msoBody = `<!--[if mso]>
90
+ <xml>
91
+ <o:OfficeDocumentSettings>
92
+ <o:PixelsPerInch>96</o:PixelsPerInch>
93
+ </o:OfficeDocumentSettings>
94
+ <w:WordDocument>
95
+ <w:DontUseAdvancedTypographyReadingMail />
96
+ </w:WordDocument>
97
+ </xml>
98
+ <![endif]-->`
99
+
100
+ const htmlXmlns = computed(() => outlookFallback ? {
101
+ 'xmlns:v': 'urn:schemas-microsoft-com:vml',
102
+ 'xmlns:o': 'urn:schemas-microsoft-com:office:office',
103
+ } : {})
104
+ </script>
105
+
106
+ <template>
107
+ <html :lang="lang" :dir="dir" v-bind="htmlXmlns">
108
+ <EmptyHead v-if="props.doubleHead === true || props.doubleHead === 'true'" />
109
+
110
+ <head>
111
+ <meta charset="utf-8">
112
+ <meta name="x-apple-disable-message-reformatting">
113
+ <meta name="viewport" content="width=device-width, initial-scale=1">
114
+ <meta name="format-detection" content="telephone=no, date=no, address=no, email=no, url=no">
115
+ <meta name="color-scheme" content="light dark">
116
+ <meta name="supported-color-schemes" content="light dark">
117
+ <MsoHead v-if="outlookFallback" />
118
+ <link rel="preconnect" href="https://fonts.googleapis.com">
119
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous">
120
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet" media="screen">
121
+ <style>
122
+ @import "@maizzle/tailwindcss";
123
+
124
+ img {
125
+ @apply max-w-full align-middle;
126
+ }
127
+
128
+ </style>
129
+ </head>
130
+
131
+ <body :xml:lang="outlookFallback ? lang : null" :class="bodyMergedClass">
132
+ <span v-if="outlookFallback" style="display: none" v-html="msoBody"></span>
133
+ <div
134
+ role="article"
135
+ aria-roledescription="email"
136
+ :aria-label="ariaLabel"
137
+ :lang="lang"
138
+ :dir="dir"
139
+ style="font-size: medium;"
140
+ :class="articleMergedClass">
141
+ <slot />
142
+ </div>
143
+ </body>
144
+
145
+ </html>
146
+ </template>
@@ -1,7 +1,5 @@
1
- <script setup lang="ts">
2
- defineProps({
3
- //
4
- })
1
+ <script setup>
2
+
5
3
  </script>
6
4
 
7
5
  <template>
@@ -1,9 +1,5 @@
1
1
  import { defineConfig } from '@maizzle/framework'
2
2
 
3
3
  export default defineConfig({
4
- css: {
5
- purge: true,
6
- inline: true,
7
- shorthand: true,
8
- },
4
+
9
5
  })
@@ -19,9 +19,9 @@ async function makeTemplate(filePath) {
19
19
  path: () => p.text({
20
20
  message: "Directory to place it in",
21
21
  placeholder: "./emails",
22
+ initialValue: "./emails",
22
23
  validate: (value) => {
23
24
  if (!value) return "Please enter a path.";
24
- if (value[0] !== ".") return "Please enter a relative path.";
25
25
  }
26
26
  })
27
27
  }, { onCancel });
@@ -160,9 +160,9 @@ async function newProject(starterArg, dirArg, options = {}) {
160
160
  path: () => p.text({
161
161
  message: "Where should we create your project?",
162
162
  placeholder: "./maizzle",
163
+ initialValue: "./maizzle",
163
164
  validate: (value) => {
164
165
  if (!value) return "Please enter a path.";
165
- if (value[0] !== ".") return "Please enter a relative path.";
166
166
  if (existsSync(value)) return "That directory already exists. Please enter a different path.";
167
167
  }
168
168
  }),
@@ -216,32 +216,37 @@ async function newProject(starterArg, dirArg, options = {}) {
216
216
  process.exit(0);
217
217
  } });
218
218
  }
219
- const spinner = p.spinner();
220
- spinner.start("Creating project");
221
219
  const starter = starters.find((s) => s.value === project.starter);
222
220
  const source = starter ? starter.path : project.starter;
223
- await downloadTemplate(source.includes(":") ? source : `gh:${source}`, { dir: project.path });
224
- await rm(`${project.path}/.github`, {
225
- recursive: true,
226
- force: true
227
- });
228
- spinner.stop(`Created project in ${project.path}`);
229
- if (project.install) {
230
- try {
231
- execSync(`${project.pm} --version`, { stdio: "ignore" });
232
- } catch {
233
- p.log.error(`${project.pm} is not installed. Please install it first.`);
234
- process.exit(1);
235
- }
236
- spinner.start("Installing dependencies");
237
- const startTime = Date.now();
238
- await installDependencies({
239
- cwd: project.path,
240
- silent: true,
241
- packageManager: project.pm
242
- });
243
- spinner.stop(`Installed dependencies ${color.gray((Date.now() - startTime) / 1e3 + "s")}`);
221
+ if (project.install) try {
222
+ execSync(`${project.pm} --version`, { stdio: "ignore" });
223
+ } catch {
224
+ p.log.error(`${project.pm} is not installed. Please install it first.`);
225
+ process.exit(1);
244
226
  }
227
+ await p.tasks([{
228
+ title: "Creating project",
229
+ task: async () => {
230
+ await downloadTemplate(source.includes(":") ? source : `gh:${source}`, { dir: project.path });
231
+ await rm(`${project.path}/.github`, {
232
+ recursive: true,
233
+ force: true
234
+ });
235
+ return `Created project in ${project.path}`;
236
+ }
237
+ }, {
238
+ title: "Installing dependencies",
239
+ enabled: project.install,
240
+ task: async () => {
241
+ const startTime = Date.now();
242
+ await installDependencies({
243
+ cwd: project.path,
244
+ silent: true,
245
+ packageManager: project.pm
246
+ });
247
+ return `Installed dependencies ${color.gray((Date.now() - startTime) / 1e3 + "s")}`;
248
+ }
249
+ }]);
245
250
  const pm = project.pm || "npm";
246
251
  const runCmd = pm === "yarn" ? "yarn dev" : `${pm} run dev`;
247
252
  const nextSteps = `cd ${project.path} \n\n${project.install ? "" : `${pm} install\n\n`}${runCmd}`;
@@ -7,22 +7,42 @@ import { Command } from "commander";
7
7
  //#region src/index.ts
8
8
  async function bootstrap(framework) {
9
9
  const program = new Command();
10
- program.name("maizzle").description("Maizzle CLI").version("1.0.0");
10
+ program.name("maizzle").description("Maizzle CLI").version("1.2.0");
11
11
  if (framework) {
12
- program.command("serve").description("Start the Maizzle dev server with HMR").option("-c, --config <path>", "Path to maizzle config file").option("-p, --port <number>", "Dev server port").option("--host [address]", "Expose on network").action(async (options) => {
12
+ program.command("serve").alias("dev").description("Start the Maizzle dev server with HMR").option("-c, --config <path>", "Path to a Maizzle config file").option("-p, --port <number>", "Dev server port").option("--host [address]", "Expose on network").action(async (options) => {
13
13
  await framework.serve({
14
14
  config: options.config,
15
15
  port: options.port ? parseInt(options.port, 10) : void 0,
16
16
  host: options.host
17
17
  });
18
18
  });
19
- program.command("build").description("Build email templates to HTML").option("-c, --config <path>", "Path to maizzle config file").option("-o, --output <path>", "Output directory").action(async (options) => {
20
- await framework.build({
21
- config: options.config,
22
- output: options.output
23
- });
19
+ program.command("build").description("Build email templates to HTML").option("-c, --config <path>", "Path to a Maizzle config file").option("-o, --output <path>", "Output directory").option("--pretty", "Pretty-print HTML output").option("--minify", "Minify HTML output").option("--plaintext", "Generate plaintext versions").option("--dir <path>", "Source directory for email templates").option("--ext <extension>", "Output file extension").action(async (options) => {
20
+ if (options.config) {
21
+ await framework.build(options.config);
22
+ return;
23
+ }
24
+ const overrides = {};
25
+ if (options.output) overrides.output = {
26
+ ...overrides.output,
27
+ path: options.output
28
+ };
29
+ if (options.ext) overrides.output = {
30
+ ...overrides.output,
31
+ extension: options.ext
32
+ };
33
+ if (options.pretty) overrides.html = {
34
+ ...overrides.html,
35
+ format: true
36
+ };
37
+ if (options.minify) overrides.html = {
38
+ ...overrides.html,
39
+ minify: true
40
+ };
41
+ if (options.plaintext) overrides.plaintext = true;
42
+ if (options.dir) overrides.content = [`${options.dir}/**/*.{vue,md}`];
43
+ await framework.build(Object.keys(overrides).length ? overrides : void 0);
24
44
  });
25
- program.command("prepare").description("Generate IDE type definitions in .maizzle/").option("-c, --config <path>", "Path to maizzle config file").action(async (options) => {
45
+ program.command("prepare").description("Generate IDE type definitions in .maizzle/").option("-c, --config <path>", "Path to a Maizzle config file").action(async (options) => {
26
46
  await framework.prepare({ config: options.config });
27
47
  });
28
48
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maizzle",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "CLI tool for the Maizzle Email Framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maizzle/framework",
3
- "version": "6.0.0-rc.18",
3
+ "version": "6.0.0-rc.19",
4
4
  "description": "Maizzle is a framework that helps you quickly build HTML emails with Tailwind CSS.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -94,7 +94,7 @@
94
94
  "oxlint": "^1.50.0",
95
95
  "shiki": "^4.0.2",
96
96
  "tailwind-merge": "^3.5.0",
97
- "tsdown": "^0.20.3",
97
+ "tsdown": "^0.22.0",
98
98
  "vitest": "^4.0.18",
99
99
  "vue": "^3.5.28"
100
100
  },