@maizzle/framework 6.0.0-rc.20 → 6.0.0-rc.22

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 (94) hide show
  1. package/dist/build.d.ts.map +1 -1
  2. package/dist/build.js +53 -43
  3. package/dist/build.js.map +1 -1
  4. package/dist/components/Body.vue +1 -1
  5. package/dist/components/CodeBlock.vue +1 -1
  6. package/dist/components/CodeInline.vue +72 -2
  7. package/dist/components/Column.vue +2 -1
  8. package/dist/components/Container.vue +1 -11
  9. package/dist/components/Img.vue +199 -4
  10. package/dist/components/Preheader.vue +33 -5
  11. package/dist/components/Section.vue +9 -14
  12. package/dist/components/Text.vue +1 -1
  13. package/dist/composables/defineConfig.d.ts +3 -4
  14. package/dist/composables/defineConfig.d.ts.map +1 -1
  15. package/dist/composables/defineConfig.js +3 -4
  16. package/dist/composables/defineConfig.js.map +1 -1
  17. package/dist/composables/renderContext.d.ts +1 -2
  18. package/dist/composables/renderContext.d.ts.map +1 -1
  19. package/dist/composables/renderContext.js.map +1 -1
  20. package/dist/composables/useCurrentTemplate.d.ts +31 -0
  21. package/dist/composables/useCurrentTemplate.d.ts.map +1 -0
  22. package/dist/composables/useCurrentTemplate.js +35 -0
  23. package/dist/composables/useCurrentTemplate.js.map +1 -0
  24. package/dist/composables/useEvent.d.ts +1 -1
  25. package/dist/composables/useEvent.js +1 -1
  26. package/dist/composables/useEvent.js.map +1 -1
  27. package/dist/composables/usePreheader.d.ts +6 -5
  28. package/dist/composables/usePreheader.d.ts.map +1 -1
  29. package/dist/composables/usePreheader.js +3 -3
  30. package/dist/composables/usePreheader.js.map +1 -1
  31. package/dist/composables/useTransformers.d.ts +1 -1
  32. package/dist/composables/useTransformers.js +1 -1
  33. package/dist/composables/useTransformers.js.map +1 -1
  34. package/dist/events/index.d.ts +20 -8
  35. package/dist/events/index.d.ts.map +1 -1
  36. package/dist/events/index.js +5 -8
  37. package/dist/events/index.js.map +1 -1
  38. package/dist/index.d.ts +3 -2
  39. package/dist/index.js +3 -2
  40. package/dist/render/createRenderer.js +4 -3
  41. package/dist/render/createRenderer.js.map +1 -1
  42. package/dist/serve.d.ts.map +1 -1
  43. package/dist/serve.js +3 -2
  44. package/dist/serve.js.map +1 -1
  45. package/dist/transformers/addAttributes.d.ts +18 -8
  46. package/dist/transformers/addAttributes.d.ts.map +1 -1
  47. package/dist/transformers/addAttributes.js +22 -8
  48. package/dist/transformers/addAttributes.js.map +1 -1
  49. package/dist/transformers/columnWidth.d.ts.map +1 -1
  50. package/dist/transformers/columnWidth.js +136 -150
  51. package/dist/transformers/columnWidth.js.map +1 -1
  52. package/dist/transformers/entities.d.ts.map +1 -1
  53. package/dist/transformers/entities.js +1 -0
  54. package/dist/transformers/entities.js.map +1 -1
  55. package/dist/transformers/index.d.ts.map +1 -1
  56. package/dist/transformers/index.js +7 -5
  57. package/dist/transformers/index.js.map +1 -1
  58. package/dist/transformers/inlineCss.js +2 -7
  59. package/dist/transformers/inlineCss.js.map +1 -1
  60. package/dist/transformers/inlineLink.js +2 -2
  61. package/dist/transformers/inlineLink.js.map +1 -1
  62. package/dist/transformers/minifyCodeInline.d.ts +29 -0
  63. package/dist/transformers/minifyCodeInline.d.ts.map +1 -0
  64. package/dist/transformers/minifyCodeInline.js +36 -0
  65. package/dist/transformers/minifyCodeInline.js.map +1 -0
  66. package/dist/transformers/msoPlaceholders.d.ts +10 -5
  67. package/dist/transformers/msoPlaceholders.d.ts.map +1 -1
  68. package/dist/transformers/msoPlaceholders.js +38 -7
  69. package/dist/transformers/msoPlaceholders.js.map +1 -1
  70. package/dist/transformers/safeSelectors.d.ts +37 -0
  71. package/dist/transformers/safeSelectors.d.ts.map +1 -0
  72. package/dist/transformers/{safeClassNames.js → safeSelectors.js} +24 -5
  73. package/dist/transformers/safeSelectors.js.map +1 -0
  74. package/dist/transformers/shorthandCss.js +38 -7
  75. package/dist/transformers/shorthandCss.js.map +1 -1
  76. package/dist/transformers/tailwindComponent.js +1 -1
  77. package/dist/transformers/tailwindComponent.js.map +1 -1
  78. package/dist/types/config.d.ts +19 -9
  79. package/dist/types/config.d.ts.map +1 -1
  80. package/dist/utils/ast/serializer.d.ts.map +1 -1
  81. package/dist/utils/ast/serializer.js +27 -17
  82. package/dist/utils/ast/serializer.js.map +1 -1
  83. package/dist/utils/cssBox.d.ts +42 -0
  84. package/dist/utils/cssBox.d.ts.map +1 -0
  85. package/dist/utils/cssBox.js +156 -0
  86. package/dist/utils/cssBox.js.map +1 -0
  87. package/dist/utils/watchPaths.d.ts +11 -0
  88. package/dist/utils/watchPaths.d.ts.map +1 -0
  89. package/dist/utils/watchPaths.js +19 -0
  90. package/dist/utils/watchPaths.js.map +1 -0
  91. package/package.json +1 -1
  92. package/dist/transformers/safeClassNames.d.ts +0 -22
  93. package/dist/transformers/safeClassNames.d.ts.map +0 -1
  94. package/dist/transformers/safeClassNames.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"tailwindComponent.js","names":[],"sources":["../../src/transformers/tailwindComponent.ts"],"sourcesContent":["import { resolve } from 'node:path'\nimport type { ChildNode, Element, Comment } from 'domhandler'\nimport { walk } from '../utils/ast/index.ts'\nimport { compileTailwindCss } from '../utils/compileTailwindCss.ts'\nimport type { TailwindBlock } from '../composables/renderContext.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\n\nconst DEFAULT_SEED = '@import \"@maizzle/tailwindcss\";'\n\ninterface BlockMeta {\n id: string\n configCss?: string\n nested: boolean\n classes: Set<string>\n}\n\nconst OPEN_RE = /^mz-tw:(\\S+)$/\nconst CLOSE_RE = /^\\/mz-tw:(\\S+)$/\n\n/**\n * Compile Tailwind CSS for each top-level <Tailwind> block in the render\n * context. Nested <Tailwind> instances are flattened: their classes flow\n * up to the outermost block, their `#config` slot (if any) is ignored.\n * One <style> per outermost block is appended to <head>; marker comments\n * are stripped after.\n */\nexport async function tailwindComponent(\n dom: ChildNode[],\n blocks: TailwindBlock[],\n config: MaizzleConfig,\n filePath?: string,\n): Promise<ChildNode[]> {\n if (!blocks.length) return dom\n\n const map = new Map<string, BlockMeta>()\n for (const b of blocks) {\n map.set(b.id, { id: b.id, configCss: b.css, nested: false, classes: new Set() })\n }\n\n const stack: string[] = []\n const markers: Comment[] = []\n\n walk(dom, (node) => {\n if (node.type === 'comment') {\n const data = (node as Comment).data\n const open = data.match(OPEN_RE)\n const close = data.match(CLOSE_RE)\n if (open) {\n const id = open[1]\n const meta = map.get(id)\n if (meta && stack.length > 0) meta.nested = true\n if (meta) stack.push(id)\n markers.push(node as Comment)\n } else if (close) {\n const id = close[1]\n if (stack[stack.length - 1] === id) stack.pop()\n markers.push(node as Comment)\n }\n return\n }\n\n const el = node as Element\n // Always assign to the OUTERMOST active marker (stack[0]) so nested\n // <Tailwind> blocks merge their classes into the parent's scope.\n if (el.attribs?.class && stack.length > 0) {\n map.get(stack[0])!.classes.add(el.attribs.class)\n }\n })\n\n const fromPath = filePath ?? resolve(process.cwd(), 'template.vue')\n\n let head: Element | undefined\n walk(dom, (n) => {\n if (!head && (n as Element).name === 'head') head = n as Element\n })\n\n if (!head) {\n throw new Error('`Tailwind` component requires `Head` component to be present in the template.')\n }\n\n // Compile + inject one <style raw> per outermost block. `raw` opts the\n // existing tailwindcss transformer out of recompiling already-compiled CSS.\n for (const meta of map.values()) {\n if (meta.nested) continue\n\n const cssInput = buildCssInput(meta.configCss, meta.classes)\n const css = (await compileTailwindCss(cssInput, config, `${fromPath}?tw=${meta.id}`)).trim()\n if (!css) continue\n\n const styleNode: Element = {\n type: 'tag',\n name: 'style',\n attribs: { raw: '' },\n children: [],\n parent: head,\n prev: null,\n next: null,\n } as any\n\n const textNode = {\n type: 'text',\n data: css,\n parent: styleNode,\n prev: null,\n next: null,\n } as any\n\n styleNode.children = [textNode]\n head.children.push(styleNode)\n }\n\n // Strip marker comments from their parents\n for (const c of markers) {\n const parent = c.parent as Element | null\n if (!parent?.children) continue\n const i = parent.children.indexOf(c)\n if (i >= 0) parent.children.splice(i, 1)\n }\n\n return dom\n}\n\nfunction buildCssInput(configCss: string | undefined, classes: Set<string>): string {\n const seed = configCss ?? DEFAULT_SEED\n\n if (!classes.size) return seed\n\n const inline = [...classes].join(' ').replace(/\"/g, '\\\\\"')\n return `${seed}\\n@source inline(\"${inline}\");`\n}\n"],"mappings":";;;;;AAOA,MAAM,eAAe;AASrB,MAAM,UAAU;AAChB,MAAM,WAAW;;;;;;;;AASjB,eAAsB,kBACpB,KACA,QACA,QACA,UACsB;CACtB,IAAI,CAAC,OAAO,QAAQ,OAAO;CAE3B,MAAM,sBAAM,IAAI,KAAwB;CACxC,KAAK,MAAM,KAAK,QACd,IAAI,IAAI,EAAE,IAAI;EAAE,IAAI,EAAE;EAAI,WAAW,EAAE;EAAK,QAAQ;EAAO,yBAAS,IAAI,KAAK;EAAE,CAAC;CAGlF,MAAM,QAAkB,EAAE;CAC1B,MAAM,UAAqB,EAAE;CAE7B,KAAK,MAAM,SAAS;EAClB,IAAI,KAAK,SAAS,WAAW;GAC3B,MAAM,OAAQ,KAAiB;GAC/B,MAAM,OAAO,KAAK,MAAM,QAAQ;GAChC,MAAM,QAAQ,KAAK,MAAM,SAAS;GAClC,IAAI,MAAM;IACR,MAAM,KAAK,KAAK;IAChB,MAAM,OAAO,IAAI,IAAI,GAAG;IACxB,IAAI,QAAQ,MAAM,SAAS,GAAG,KAAK,SAAS;IAC5C,IAAI,MAAM,MAAM,KAAK,GAAG;IACxB,QAAQ,KAAK,KAAgB;UACxB,IAAI,OAAO;IAChB,MAAM,KAAK,MAAM;IACjB,IAAI,MAAM,MAAM,SAAS,OAAO,IAAI,MAAM,KAAK;IAC/C,QAAQ,KAAK,KAAgB;;GAE/B;;EAGF,MAAM,KAAK;EAGX,IAAI,GAAG,SAAS,SAAS,MAAM,SAAS,GACtC,IAAI,IAAI,MAAM,GAAG,CAAE,QAAQ,IAAI,GAAG,QAAQ,MAAM;GAElD;CAEF,MAAM,WAAW,YAAY,QAAQ,QAAQ,KAAK,EAAE,eAAe;CAEnE,IAAI;CACJ,KAAK,MAAM,MAAM;EACf,IAAI,CAAC,QAAS,EAAc,SAAS,QAAQ,OAAO;GACpD;CAEF,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,gFAAgF;CAKlG,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE;EAC/B,IAAI,KAAK,QAAQ;EAGjB,MAAM,OAAO,MAAM,mBADF,cAAc,KAAK,WAAW,KAAK,QACN,EAAE,QAAQ,GAAG,SAAS,MAAM,KAAK,KAAK,EAAE,MAAM;EAC5F,IAAI,CAAC,KAAK;EAEV,MAAM,YAAqB;GACzB,MAAM;GACN,MAAM;GACN,SAAS,EAAE,KAAK,IAAI;GACpB,UAAU,EAAE;GACZ,QAAQ;GACR,MAAM;GACN,MAAM;GACP;EAUD,UAAU,WAAW,CAAC;GAPpB,MAAM;GACN,MAAM;GACN,QAAQ;GACR,MAAM;GACN,MAAM;GAGsB,CAAC;EAC/B,KAAK,SAAS,KAAK,UAAU;;CAI/B,KAAK,MAAM,KAAK,SAAS;EACvB,MAAM,SAAS,EAAE;EACjB,IAAI,CAAC,QAAQ,UAAU;EACvB,MAAM,IAAI,OAAO,SAAS,QAAQ,EAAE;EACpC,IAAI,KAAK,GAAG,OAAO,SAAS,OAAO,GAAG,EAAE;;CAG1C,OAAO;;AAGT,SAAS,cAAc,WAA+B,SAA8B;CAClF,MAAM,OAAO,aAAa;CAE1B,IAAI,CAAC,QAAQ,MAAM,OAAO;CAG1B,OAAO,GAAG,KAAK,oBADA,CAAC,GAAG,QAAQ,CAAC,KAAK,IAAI,CAAC,QAAQ,MAAM,OACX,CAAC"}
1
+ {"version":3,"file":"tailwindComponent.js","names":[],"sources":["../../src/transformers/tailwindComponent.ts"],"sourcesContent":["import { resolve } from 'node:path'\nimport type { ChildNode, Element, Comment } from 'domhandler'\nimport { walk } from '../utils/ast/index.ts'\nimport { compileTailwindCss } from '../utils/compileTailwindCss.ts'\nimport type { TailwindBlock } from '../composables/renderContext.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\n\nconst DEFAULT_SEED = '@import \"@maizzle/tailwindcss\" source(none);'\n\ninterface BlockMeta {\n id: string\n configCss?: string\n nested: boolean\n classes: Set<string>\n}\n\nconst OPEN_RE = /^mz-tw:(\\S+)$/\nconst CLOSE_RE = /^\\/mz-tw:(\\S+)$/\n\n/**\n * Compile Tailwind CSS for each top-level <Tailwind> block in the render\n * context. Nested <Tailwind> instances are flattened: their classes flow\n * up to the outermost block, their `#config` slot (if any) is ignored.\n * One <style> per outermost block is appended to <head>; marker comments\n * are stripped after.\n */\nexport async function tailwindComponent(\n dom: ChildNode[],\n blocks: TailwindBlock[],\n config: MaizzleConfig,\n filePath?: string,\n): Promise<ChildNode[]> {\n if (!blocks.length) return dom\n\n const map = new Map<string, BlockMeta>()\n for (const b of blocks) {\n map.set(b.id, { id: b.id, configCss: b.css, nested: false, classes: new Set() })\n }\n\n const stack: string[] = []\n const markers: Comment[] = []\n\n walk(dom, (node) => {\n if (node.type === 'comment') {\n const data = (node as Comment).data\n const open = data.match(OPEN_RE)\n const close = data.match(CLOSE_RE)\n if (open) {\n const id = open[1]\n const meta = map.get(id)\n if (meta && stack.length > 0) meta.nested = true\n if (meta) stack.push(id)\n markers.push(node as Comment)\n } else if (close) {\n const id = close[1]\n if (stack[stack.length - 1] === id) stack.pop()\n markers.push(node as Comment)\n }\n return\n }\n\n const el = node as Element\n // Always assign to the OUTERMOST active marker (stack[0]) so nested\n // <Tailwind> blocks merge their classes into the parent's scope.\n if (el.attribs?.class && stack.length > 0) {\n map.get(stack[0])!.classes.add(el.attribs.class)\n }\n })\n\n const fromPath = filePath ?? resolve(process.cwd(), 'template.vue')\n\n let head: Element | undefined\n walk(dom, (n) => {\n if (!head && (n as Element).name === 'head') head = n as Element\n })\n\n if (!head) {\n throw new Error('`Tailwind` component requires `Head` component to be present in the template.')\n }\n\n // Compile + inject one <style raw> per outermost block. `raw` opts the\n // existing tailwindcss transformer out of recompiling already-compiled CSS.\n for (const meta of map.values()) {\n if (meta.nested) continue\n\n const cssInput = buildCssInput(meta.configCss, meta.classes)\n const css = (await compileTailwindCss(cssInput, config, `${fromPath}?tw=${meta.id}`)).trim()\n if (!css) continue\n\n const styleNode: Element = {\n type: 'tag',\n name: 'style',\n attribs: { raw: '' },\n children: [],\n parent: head,\n prev: null,\n next: null,\n } as any\n\n const textNode = {\n type: 'text',\n data: css,\n parent: styleNode,\n prev: null,\n next: null,\n } as any\n\n styleNode.children = [textNode]\n head.children.push(styleNode)\n }\n\n // Strip marker comments from their parents\n for (const c of markers) {\n const parent = c.parent as Element | null\n if (!parent?.children) continue\n const i = parent.children.indexOf(c)\n if (i >= 0) parent.children.splice(i, 1)\n }\n\n return dom\n}\n\nfunction buildCssInput(configCss: string | undefined, classes: Set<string>): string {\n const seed = configCss ?? DEFAULT_SEED\n\n if (!classes.size) return seed\n\n const inline = [...classes].join(' ').replace(/\"/g, '\\\\\"')\n return `${seed}\\n@source inline(\"${inline}\");`\n}\n"],"mappings":";;;;;AAOA,MAAM,eAAe;AASrB,MAAM,UAAU;AAChB,MAAM,WAAW;;;;;;;;AASjB,eAAsB,kBACpB,KACA,QACA,QACA,UACsB;CACtB,IAAI,CAAC,OAAO,QAAQ,OAAO;CAE3B,MAAM,sBAAM,IAAI,KAAwB;CACxC,KAAK,MAAM,KAAK,QACd,IAAI,IAAI,EAAE,IAAI;EAAE,IAAI,EAAE;EAAI,WAAW,EAAE;EAAK,QAAQ;EAAO,yBAAS,IAAI,KAAK;EAAE,CAAC;CAGlF,MAAM,QAAkB,EAAE;CAC1B,MAAM,UAAqB,EAAE;CAE7B,KAAK,MAAM,SAAS;EAClB,IAAI,KAAK,SAAS,WAAW;GAC3B,MAAM,OAAQ,KAAiB;GAC/B,MAAM,OAAO,KAAK,MAAM,QAAQ;GAChC,MAAM,QAAQ,KAAK,MAAM,SAAS;GAClC,IAAI,MAAM;IACR,MAAM,KAAK,KAAK;IAChB,MAAM,OAAO,IAAI,IAAI,GAAG;IACxB,IAAI,QAAQ,MAAM,SAAS,GAAG,KAAK,SAAS;IAC5C,IAAI,MAAM,MAAM,KAAK,GAAG;IACxB,QAAQ,KAAK,KAAgB;UACxB,IAAI,OAAO;IAChB,MAAM,KAAK,MAAM;IACjB,IAAI,MAAM,MAAM,SAAS,OAAO,IAAI,MAAM,KAAK;IAC/C,QAAQ,KAAK,KAAgB;;GAE/B;;EAGF,MAAM,KAAK;EAGX,IAAI,GAAG,SAAS,SAAS,MAAM,SAAS,GACtC,IAAI,IAAI,MAAM,GAAG,CAAE,QAAQ,IAAI,GAAG,QAAQ,MAAM;GAElD;CAEF,MAAM,WAAW,YAAY,QAAQ,QAAQ,KAAK,EAAE,eAAe;CAEnE,IAAI;CACJ,KAAK,MAAM,MAAM;EACf,IAAI,CAAC,QAAS,EAAc,SAAS,QAAQ,OAAO;GACpD;CAEF,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,gFAAgF;CAKlG,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE;EAC/B,IAAI,KAAK,QAAQ;EAGjB,MAAM,OAAO,MAAM,mBADF,cAAc,KAAK,WAAW,KAAK,QACN,EAAE,QAAQ,GAAG,SAAS,MAAM,KAAK,KAAK,EAAE,MAAM;EAC5F,IAAI,CAAC,KAAK;EAEV,MAAM,YAAqB;GACzB,MAAM;GACN,MAAM;GACN,SAAS,EAAE,KAAK,IAAI;GACpB,UAAU,EAAE;GACZ,QAAQ;GACR,MAAM;GACN,MAAM;GACP;EAUD,UAAU,WAAW,CAAC;GAPpB,MAAM;GACN,MAAM;GACN,QAAQ;GACR,MAAM;GACN,MAAM;GAGsB,CAAC;EAC/B,KAAK,SAAS,KAAK,UAAU;;CAI/B,KAAK,MAAM,KAAK,SAAS;EACvB,MAAM,SAAS,EAAE;EACjB,IAAI,CAAC,QAAQ,UAAU;EACvB,MAAM,IAAI,OAAO,SAAS,QAAQ,EAAE;EACpC,IAAI,KAAK,GAAG,OAAO,SAAS,OAAO,GAAG,EAAE;;CAG1C,OAAO;;AAGT,SAAS,cAAc,WAA+B,SAA8B;CAClF,MAAM,OAAO,aAAa;CAE1B,IAAI,CAAC,QAAQ,MAAM,OAAO;CAG1B,OAAO,GAAG,KAAK,oBADA,CAAC,GAAG,QAAQ,CAAC,KAAK,IAAI,CAAC,QAAQ,MAAM,OACX,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { RemoveValue } from "../plugins/postcss/removeDeclarations.js";
2
+ import { TemplateInfo } from "../events/index.js";
2
3
  import { Directive, Plugin } from "vue";
3
4
  import { Options } from "juice";
4
5
  import * as _$oxfmt from "oxfmt";
@@ -374,8 +375,15 @@ interface MarkdownConfig extends Options$1 {
374
375
  shikiTheme?: _$shiki.BundledTheme;
375
376
  }
376
377
  interface VueConfig {
377
- /** Vue plugins to register on the app instance before rendering. */
378
- plugins?: Plugin[];
378
+ /**
379
+ * Vue plugins to register on the app instance before rendering.
380
+ *
381
+ * Pass a factory (`() => Plugin[]`) for stateful plugins like vue-i18n
382
+ * or Pinia to get a fresh instance per render — otherwise state leaks
383
+ * across templates (e.g. one template setting `locale.value = 'fr'`
384
+ * affects the next render).
385
+ */
386
+ plugins?: Plugin[] | (() => Plugin[]);
379
387
  /** Custom Vue directives to register globally. */
380
388
  directives?: Record<string, Directive>;
381
389
  /** Properties added to `app.config.globalProperties`, available in all templates. */
@@ -387,7 +395,7 @@ interface VueConfig {
387
395
  * - `false` skips the listed transformer.
388
396
  * - `true` force-enables it for this run (only meaningful for boolean-driven
389
397
  * transformers: inlineCss, purgeCss, prettify, minify, shorthandCss,
390
- * sixHex, safeClassNames, entities). Layers on the matching
398
+ * sixHex, safeSelectors, entities). Layers on the matching
391
399
  * `css.*` / `html.*` config slice.
392
400
  * - missing keys keep their default behavior.
393
401
  *
@@ -400,7 +408,7 @@ interface VueConfig {
400
408
  * or framework state, not user opt-in.
401
409
  */
402
410
  interface TransformerToggles {
403
- safeClassNames?: boolean;
411
+ safeSelectors?: boolean;
404
412
  attributeToStyle?: boolean;
405
413
  inlineCss?: boolean;
406
414
  removeAttributes?: boolean;
@@ -688,7 +696,9 @@ interface MaizzleConfig {
688
696
  *
689
697
  * @example
690
698
  * vue: {
691
- * plugins: [createI18n({ locale: 'en', messages })],
699
+ * // Use a factory for stateful plugins (vue-i18n, Pinia, vue-router)
700
+ * // so each render gets a fresh instance.
701
+ * plugins: () => [createI18n({ locale: 'en', messages })],
692
702
  * directives: { focus: vFocus },
693
703
  * globalProperties: { $format: dateFormat },
694
704
  * }
@@ -698,21 +708,21 @@ interface MaizzleConfig {
698
708
  beforeCreate?: (params: {
699
709
  config: MaizzleConfig;
700
710
  }) => void | Promise<void>;
701
- /** Called before each template is rendered. Return a string to replace the template source. */
711
+ /** Called before each template is rendered. Return a string to replace `template.source`. */
702
712
  beforeRender?: (params: {
703
713
  config: MaizzleConfig;
704
- template: string;
714
+ template: TemplateInfo;
705
715
  }) => string | void | Promise<string | void>;
706
716
  /** Called after each template is rendered but before transformers run. Return a string to replace the output HTML. */
707
717
  afterRender?: (params: {
708
718
  config: MaizzleConfig;
709
- template: string;
719
+ template: TemplateInfo;
710
720
  html: string;
711
721
  }) => string | void | Promise<string | void>;
712
722
  /** Called after transformers have run on each template. Return a string to replace the output HTML. */
713
723
  afterTransform?: (params: {
714
724
  config: MaizzleConfig;
715
- template: string;
725
+ template: TemplateInfo;
716
726
  html: string;
717
727
  }) => string | void | Promise<string | void>;
718
728
  /** Called after all templates have been built. */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","names":[],"sources":["../../src/types/config.ts"],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","names":[],"sources":["../../src/types/config.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.d.ts","names":[],"sources":["../../../src/utils/ast/serializer.ts"],"mappings":";;;;iBA6BgB,SAAA,CAAU,GAAA,EAAK,SAAA,IAAa,OAAA,GAAU,oBAAA"}
1
+ {"version":3,"file":"serializer.d.ts","names":[],"sources":["../../../src/utils/ast/serializer.ts"],"mappings":";;;;iBA0CgB,SAAA,CAAU,GAAA,EAAK,SAAA,IAAa,OAAA,GAAU,oBAAA"}
@@ -1,30 +1,40 @@
1
- import { walk } from "./walker.js";
2
1
  import render from "dom-serializer";
3
2
  //#region src/utils/ast/serializer.ts
3
+ const RAW_TEXT_ELEMENTS = new Set(["script", "style"]);
4
4
  /**
5
- * Re-encode < and > as entities in text nodes inside <code> elements.
5
+ * Re-encode `<` and `>` in text nodes before serialization.
6
6
  *
7
- * The DOM parser decodes entities like &#x3C; into raw < in text nodes.
8
- * With encodeEntities: false the serializer outputs them as-is, which
9
- * creates broken HTML (e.g. </a> inside a code block closes the real tag).
7
+ * The parser decodes entities like `&lt;` into raw `<` in text nodes. With
8
+ * `encodeEntities: false` (needed so other transformers can emit entity
9
+ * strings such as `&nbsp;` verbatim), the serializer would write those raw
10
+ * `<` characters as-is, turning escaped text (e.g. a Vue `{{ html }}`
11
+ * interpolation containing `<p>...</p>`) into real DOM downstream.
10
12
  *
11
- * We selectively fix this for <code> contents only, so the rest of the
12
- * document (where encodeEntities: false is needed) is unaffected.
13
+ * `&` is intentionally not re-encoded: the `entities` transformer writes
14
+ * literal entity strings (`&nbsp;`, `&mdash;`) directly into text-node data
15
+ * and relies on the serializer leaving them alone.
16
+ *
17
+ * Skip `<script>` / `<style>` — their children are raw-text containers, and
18
+ * CSS/JS legitimately contains `<` and `>`.
13
19
  */
14
- function encodeCodeTextNodes(dom) {
15
- walk(dom, (node) => {
16
- const el = node;
17
- if (el.name !== "code") return;
18
- walk(el.children ?? [], (child) => {
19
- if (child.type === "text") {
20
- const text = child;
20
+ function encodeTextNodes(dom, inRawText = false) {
21
+ for (const node of dom) {
22
+ if (node.type === "text") {
23
+ if (!inRawText) {
24
+ const text = node;
21
25
  text.data = text.data.replace(/</g, "&lt;").replace(/>/g, "&gt;");
22
26
  }
23
- });
24
- });
27
+ continue;
28
+ }
29
+ if ("children" in node && node.children?.length) {
30
+ const el = node;
31
+ const nextRaw = inRawText || RAW_TEXT_ELEMENTS.has(el.name);
32
+ encodeTextNodes(el.children, nextRaw);
33
+ }
34
+ }
25
35
  }
26
36
  function serialize(dom, options) {
27
- encodeCodeTextNodes(dom);
37
+ encodeTextNodes(dom);
28
38
  return render(dom, {
29
39
  encodeEntities: false,
30
40
  ...options
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.js","names":[],"sources":["../../../src/utils/ast/serializer.ts"],"sourcesContent":["import render from 'dom-serializer'\nimport type { ChildNode } from 'domhandler'\nimport type { DomSerializerOptions } from 'dom-serializer'\nimport { walk } from './walker.ts'\n\n/**\n * Re-encode < and > as entities in text nodes inside <code> elements.\n *\n * The DOM parser decodes entities like &#x3C; into raw < in text nodes.\n * With encodeEntities: false the serializer outputs them as-is, which\n * creates broken HTML (e.g. </a> inside a code block closes the real tag).\n *\n * We selectively fix this for <code> contents only, so the rest of the\n * document (where encodeEntities: false is needed) is unaffected.\n */\nfunction encodeCodeTextNodes(dom: ChildNode[]): void {\n walk(dom, (node) => {\n const el = node as import('domhandler').Element\n if (el.name !== 'code') return\n\n walk(el.children ?? [], (child) => {\n if (child.type === 'text') {\n const text = child as import('domhandler').Text\n text.data = text.data.replace(/</g, '&lt;').replace(/>/g, '&gt;')\n }\n })\n })\n}\n\nexport function serialize(dom: ChildNode[], options?: DomSerializerOptions): string {\n encodeCodeTextNodes(dom)\n return render(dom, { encodeEntities: false, ...options })\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,SAAS,oBAAoB,KAAwB;CACnD,KAAK,MAAM,SAAS;EAClB,MAAM,KAAK;EACX,IAAI,GAAG,SAAS,QAAQ;EAExB,KAAK,GAAG,YAAY,EAAE,GAAG,UAAU;GACjC,IAAI,MAAM,SAAS,QAAQ;IACzB,MAAM,OAAO;IACb,KAAK,OAAO,KAAK,KAAK,QAAQ,MAAM,OAAO,CAAC,QAAQ,MAAM,OAAO;;IAEnE;GACF;;AAGJ,SAAgB,UAAU,KAAkB,SAAwC;CAClF,oBAAoB,IAAI;CACxB,OAAO,OAAO,KAAK;EAAE,gBAAgB;EAAO,GAAG;EAAS,CAAC"}
1
+ {"version":3,"file":"serializer.js","names":[],"sources":["../../../src/utils/ast/serializer.ts"],"sourcesContent":["import render from 'dom-serializer'\nimport type { ChildNode, Element, Text } from 'domhandler'\nimport type { DomSerializerOptions } from 'dom-serializer'\n\nconst RAW_TEXT_ELEMENTS = new Set(['script', 'style'])\n\n/**\n * Re-encode `<` and `>` in text nodes before serialization.\n *\n * The parser decodes entities like `&lt;` into raw `<` in text nodes. With\n * `encodeEntities: false` (needed so other transformers can emit entity\n * strings such as `&nbsp;` verbatim), the serializer would write those raw\n * `<` characters as-is, turning escaped text (e.g. a Vue `{{ html }}`\n * interpolation containing `<p>...</p>`) into real DOM downstream.\n *\n * `&` is intentionally not re-encoded: the `entities` transformer writes\n * literal entity strings (`&nbsp;`, `&mdash;`) directly into text-node data\n * and relies on the serializer leaving them alone.\n *\n * Skip `<script>` / `<style>` — their children are raw-text containers, and\n * CSS/JS legitimately contains `<` and `>`.\n */\nfunction encodeTextNodes(dom: ChildNode[], inRawText = false): void {\n for (const node of dom) {\n if (node.type === 'text') {\n if (!inRawText) {\n const text = node as Text\n text.data = text.data\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n }\n continue\n }\n\n if ('children' in node && (node as Element).children?.length) {\n const el = node as Element\n const nextRaw = inRawText || RAW_TEXT_ELEMENTS.has(el.name)\n encodeTextNodes(el.children as ChildNode[], nextRaw)\n }\n }\n}\n\nexport function serialize(dom: ChildNode[], options?: DomSerializerOptions): string {\n encodeTextNodes(dom)\n return render(dom, { encodeEntities: false, ...options })\n}\n"],"mappings":";;AAIA,MAAM,oBAAoB,IAAI,IAAI,CAAC,UAAU,QAAQ,CAAC;;;;;;;;;;;;;;;;;AAkBtD,SAAS,gBAAgB,KAAkB,YAAY,OAAa;CAClE,KAAK,MAAM,QAAQ,KAAK;EACtB,IAAI,KAAK,SAAS,QAAQ;GACxB,IAAI,CAAC,WAAW;IACd,MAAM,OAAO;IACb,KAAK,OAAO,KAAK,KACd,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,OAAO;;GAE1B;;EAGF,IAAI,cAAc,QAAS,KAAiB,UAAU,QAAQ;GAC5D,MAAM,KAAK;GACX,MAAM,UAAU,aAAa,kBAAkB,IAAI,GAAG,KAAK;GAC3D,gBAAgB,GAAG,UAAyB,QAAQ;;;;AAK1D,SAAgB,UAAU,KAAkB,SAAwC;CAClF,gBAAgB,IAAI;CACpB,OAAO,OAAO,KAAK;EAAE,gBAAgB;EAAO,GAAG;EAAS,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { Root } from "postcss";
2
+
3
+ //#region src/utils/cssBox.d.ts
4
+ declare const NO_BORDER_STYLES: Set<string>;
5
+ /**
6
+ * Parse a length token into px. Handles `Npx`, `Nrem`, `Nem`, `Npt`, and
7
+ * unitless N (treated as px). Returns null for percentages, calc(),
8
+ * keywords, or anything that doesn't reduce to a concrete length.
9
+ */
10
+ declare function lengthToPx(value: string): number | null;
11
+ /**
12
+ * Expand a 1-4 token CSS shorthand (T R B L) into a left/right pair:
13
+ * 1: all sides
14
+ * 2: TB RL
15
+ * 3: T RL B
16
+ * 4: T R B L
17
+ */
18
+ declare function shorthandSides(value: string): {
19
+ left?: string;
20
+ right?: string;
21
+ };
22
+ /**
23
+ * Read horizontal padding (left + right) px from a parsed style root.
24
+ * Percentages are skipped since they'd need a known container width.
25
+ */
26
+ declare function horizontalPaddingPx(root: Root): number;
27
+ /**
28
+ * Extract a px length from a CSS border shorthand (e.g. `1px solid red` → 1).
29
+ * Returns null when the value indicates no border (`none` or `hidden`).
30
+ * Defaults to 3px (CSS `medium`) when a visible style is set but no
31
+ * explicit width token is present in the shorthand value.
32
+ */
33
+ declare function shorthandBorderWidthPx(value: string): number | null;
34
+ /**
35
+ * Read horizontal border widths (left + right) px from a parsed style root.
36
+ * Per-side `border-style: none|hidden` overrides count as zero
37
+ * contribution. Returns total px or 0 when nothing resolves.
38
+ */
39
+ declare function horizontalBorderPx(root: Root): number;
40
+ //#endregion
41
+ export { NO_BORDER_STYLES, horizontalBorderPx, horizontalPaddingPx, lengthToPx, shorthandBorderWidthPx, shorthandSides };
42
+ //# sourceMappingURL=cssBox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cssBox.d.ts","names":[],"sources":["../../src/utils/cssBox.ts"],"mappings":";;;cAEa,gBAAA,EAAgB,GAAA;;AAA7B;;;;iBAOgB,UAAA,CAAW,KAAA;AAA3B;;;;;AAoBA;;AApBA,iBAoBgB,cAAA,CAAe,KAAA;EAAkB,IAAA;EAAe,KAAA;AAAA;;;;AAehE;iBAAgB,mBAAA,CAAoB,IAAA,EAAM,IAAA;;;;AA8B1C;;;iBAAgB,sBAAA,CAAuB,KAAA;;AAevC;;;;iBAAgB,kBAAA,CAAmB,IAAA,EAAM,IAAA"}
@@ -0,0 +1,156 @@
1
+ //#region src/utils/cssBox.ts
2
+ const NO_BORDER_STYLES = new Set(["none", "hidden"]);
3
+ /**
4
+ * Parse a length token into px. Handles `Npx`, `Nrem`, `Nem`, `Npt`, and
5
+ * unitless N (treated as px). Returns null for percentages, calc(),
6
+ * keywords, or anything that doesn't reduce to a concrete length.
7
+ */
8
+ function lengthToPx(value) {
9
+ const m = value.trim().match(/^([\d.]+)(px|rem|em|pt)?$/i);
10
+ if (!m) return null;
11
+ const n = parseFloat(m[1]);
12
+ switch ((m[2] || "px").toLowerCase()) {
13
+ case "px": return n;
14
+ case "rem":
15
+ case "em": return n * 16;
16
+ case "pt": return n * 1.333;
17
+ default: return null;
18
+ }
19
+ }
20
+ /**
21
+ * Expand a 1-4 token CSS shorthand (T R B L) into a left/right pair:
22
+ * 1: all sides
23
+ * 2: TB RL
24
+ * 3: T RL B
25
+ * 4: T R B L
26
+ */
27
+ function shorthandSides(value) {
28
+ const parts = value.trim().split(/\s+/);
29
+ switch (parts.length) {
30
+ case 1: return {
31
+ left: parts[0],
32
+ right: parts[0]
33
+ };
34
+ case 2:
35
+ case 3: return {
36
+ left: parts[1],
37
+ right: parts[1]
38
+ };
39
+ case 4: return {
40
+ left: parts[3],
41
+ right: parts[1]
42
+ };
43
+ default: return {};
44
+ }
45
+ }
46
+ /**
47
+ * Read horizontal padding (left + right) px from a parsed style root.
48
+ * Percentages are skipped since they'd need a known container width.
49
+ */
50
+ function horizontalPaddingPx(root) {
51
+ let left = null;
52
+ let right = null;
53
+ root.walkDecls((d) => {
54
+ switch (d.prop) {
55
+ case "padding": {
56
+ const { left: l, right: r } = shorthandSides(d.value);
57
+ if (l) left = lengthToPx(l);
58
+ if (r) right = lengthToPx(r);
59
+ break;
60
+ }
61
+ case "padding-left":
62
+ left = lengthToPx(d.value);
63
+ break;
64
+ case "padding-right":
65
+ right = lengthToPx(d.value);
66
+ break;
67
+ }
68
+ });
69
+ return (left ?? 0) + (right ?? 0);
70
+ }
71
+ /**
72
+ * Extract a px length from a CSS border shorthand (e.g. `1px solid red` → 1).
73
+ * Returns null when the value indicates no border (`none` or `hidden`).
74
+ * Defaults to 3px (CSS `medium`) when a visible style is set but no
75
+ * explicit width token is present in the shorthand value.
76
+ */
77
+ function shorthandBorderWidthPx(value) {
78
+ const tokens = value.trim().split(/\s+/);
79
+ if (tokens.some((t) => NO_BORDER_STYLES.has(t.toLowerCase()))) return null;
80
+ for (const t of tokens) {
81
+ const px = lengthToPx(t);
82
+ if (px != null) return px;
83
+ }
84
+ return 3;
85
+ }
86
+ /**
87
+ * Read horizontal border widths (left + right) px from a parsed style root.
88
+ * Per-side `border-style: none|hidden` overrides count as zero
89
+ * contribution. Returns total px or 0 when nothing resolves.
90
+ */
91
+ function horizontalBorderPx(root) {
92
+ let left = null;
93
+ let right = null;
94
+ let leftNone = false;
95
+ let rightNone = false;
96
+ root.walkDecls((d) => {
97
+ switch (d.prop) {
98
+ case "border": {
99
+ const w = shorthandBorderWidthPx(d.value);
100
+ if (w == null) leftNone = rightNone = true;
101
+ else {
102
+ left = right = w;
103
+ leftNone = rightNone = false;
104
+ }
105
+ break;
106
+ }
107
+ case "border-width": {
108
+ const { left: l, right: r } = shorthandSides(d.value);
109
+ if (l) left = lengthToPx(l) ?? left;
110
+ if (r) right = lengthToPx(r) ?? right;
111
+ break;
112
+ }
113
+ case "border-style": {
114
+ const { left: l, right: r } = shorthandSides(d.value);
115
+ if (l && NO_BORDER_STYLES.has(l.toLowerCase())) leftNone = true;
116
+ if (r && NO_BORDER_STYLES.has(r.toLowerCase())) rightNone = true;
117
+ break;
118
+ }
119
+ case "border-left": {
120
+ const w = shorthandBorderWidthPx(d.value);
121
+ if (w == null) leftNone = true;
122
+ else {
123
+ left = w;
124
+ leftNone = false;
125
+ }
126
+ break;
127
+ }
128
+ case "border-right": {
129
+ const w = shorthandBorderWidthPx(d.value);
130
+ if (w == null) rightNone = true;
131
+ else {
132
+ right = w;
133
+ rightNone = false;
134
+ }
135
+ break;
136
+ }
137
+ case "border-left-width":
138
+ left = lengthToPx(d.value) ?? left;
139
+ break;
140
+ case "border-right-width":
141
+ right = lengthToPx(d.value) ?? right;
142
+ break;
143
+ case "border-left-style":
144
+ if (NO_BORDER_STYLES.has(d.value.trim().toLowerCase())) leftNone = true;
145
+ break;
146
+ case "border-right-style":
147
+ if (NO_BORDER_STYLES.has(d.value.trim().toLowerCase())) rightNone = true;
148
+ break;
149
+ }
150
+ });
151
+ return (leftNone ? 0 : left ?? 0) + (rightNone ? 0 : right ?? 0);
152
+ }
153
+ //#endregion
154
+ export { NO_BORDER_STYLES, horizontalBorderPx, horizontalPaddingPx, lengthToPx, shorthandBorderWidthPx, shorthandSides };
155
+
156
+ //# sourceMappingURL=cssBox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cssBox.js","names":[],"sources":["../../src/utils/cssBox.ts"],"sourcesContent":["import type { Root } from 'postcss'\n\nexport const NO_BORDER_STYLES = new Set(['none', 'hidden'])\n\n/**\n * Parse a length token into px. Handles `Npx`, `Nrem`, `Nem`, `Npt`, and\n * unitless N (treated as px). Returns null for percentages, calc(),\n * keywords, or anything that doesn't reduce to a concrete length.\n */\nexport function lengthToPx(value: string): number | null {\n const m = value.trim().match(/^([\\d.]+)(px|rem|em|pt)?$/i)\n if (!m) return null\n const n = parseFloat(m[1])\n switch ((m[2] || 'px').toLowerCase()) {\n case 'px': return n\n case 'rem':\n case 'em': return n * 16\n case 'pt': return n * 1.333\n default: return null\n }\n}\n\n/**\n * Expand a 1-4 token CSS shorthand (T R B L) into a left/right pair:\n * 1: all sides\n * 2: TB RL\n * 3: T RL B\n * 4: T R B L\n */\nexport function shorthandSides(value: string): { left?: string; right?: string } {\n const parts = value.trim().split(/\\s+/)\n switch (parts.length) {\n case 1: return { left: parts[0], right: parts[0] }\n case 2:\n case 3: return { left: parts[1], right: parts[1] }\n case 4: return { left: parts[3], right: parts[1] }\n default: return {}\n }\n}\n\n/**\n * Read horizontal padding (left + right) px from a parsed style root.\n * Percentages are skipped since they'd need a known container width.\n */\nexport function horizontalPaddingPx(root: Root): number {\n let left: number | null = null\n let right: number | null = null\n\n root.walkDecls((d) => {\n switch (d.prop) {\n case 'padding': {\n const { left: l, right: r } = shorthandSides(d.value)\n if (l) left = lengthToPx(l)\n if (r) right = lengthToPx(r)\n break\n }\n case 'padding-left':\n left = lengthToPx(d.value)\n break\n case 'padding-right':\n right = lengthToPx(d.value)\n break\n }\n })\n\n return (left ?? 0) + (right ?? 0)\n}\n\n/**\n * Extract a px length from a CSS border shorthand (e.g. `1px solid red` → 1).\n * Returns null when the value indicates no border (`none` or `hidden`).\n * Defaults to 3px (CSS `medium`) when a visible style is set but no\n * explicit width token is present in the shorthand value.\n */\nexport function shorthandBorderWidthPx(value: string): number | null {\n const tokens = value.trim().split(/\\s+/)\n if (tokens.some((t) => NO_BORDER_STYLES.has(t.toLowerCase()))) return null\n for (const t of tokens) {\n const px = lengthToPx(t)\n if (px != null) return px\n }\n return 3\n}\n\n/**\n * Read horizontal border widths (left + right) px from a parsed style root.\n * Per-side `border-style: none|hidden` overrides count as zero\n * contribution. Returns total px or 0 when nothing resolves.\n */\nexport function horizontalBorderPx(root: Root): number {\n let left: number | null = null\n let right: number | null = null\n let leftNone = false\n let rightNone = false\n\n root.walkDecls((d) => {\n switch (d.prop) {\n case 'border': {\n const w = shorthandBorderWidthPx(d.value)\n if (w == null) {\n leftNone = rightNone = true\n }\n else {\n left = right = w\n leftNone = rightNone = false\n }\n break\n }\n case 'border-width': {\n const { left: l, right: r } = shorthandSides(d.value)\n if (l) left = lengthToPx(l) ?? left\n if (r) right = lengthToPx(r) ?? right\n break\n }\n case 'border-style': {\n const { left: l, right: r } = shorthandSides(d.value)\n if (l && NO_BORDER_STYLES.has(l.toLowerCase())) leftNone = true\n if (r && NO_BORDER_STYLES.has(r.toLowerCase())) rightNone = true\n break\n }\n case 'border-left': {\n const w = shorthandBorderWidthPx(d.value)\n if (w == null) leftNone = true\n else { left = w; leftNone = false }\n break\n }\n case 'border-right': {\n const w = shorthandBorderWidthPx(d.value)\n if (w == null) rightNone = true\n else { right = w; rightNone = false }\n break\n }\n case 'border-left-width':\n left = lengthToPx(d.value) ?? left\n break\n case 'border-right-width':\n right = lengthToPx(d.value) ?? right\n break\n case 'border-left-style':\n if (NO_BORDER_STYLES.has(d.value.trim().toLowerCase())) leftNone = true\n break\n case 'border-right-style':\n if (NO_BORDER_STYLES.has(d.value.trim().toLowerCase())) rightNone = true\n break\n }\n })\n\n return (leftNone ? 0 : (left ?? 0)) + (rightNone ? 0 : (right ?? 0))\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ //#region src/utils/watchPaths.d.ts
2
+ /**
3
+ * Build a predicate that tells whether an absolute file path emitted by
4
+ * chokidar matches any of the given globs. Patterns are interpreted as
5
+ * project-relative; a leading `./` is stripped so user-supplied globs like
6
+ * `./locales/**` behave identically to `locales/**`.
7
+ */
8
+ declare function createWatchedFileMatcher(patterns: string[], cwd: string): (file: string) => boolean;
9
+ //#endregion
10
+ export { createWatchedFileMatcher };
11
+ //# sourceMappingURL=watchPaths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watchPaths.d.ts","names":[],"sources":["../../src/utils/watchPaths.ts"],"mappings":";;AAQA;;;;;iBAAgB,wBAAA,CAAyB,QAAA,YAAoB,GAAA,YAEnD,IAAA"}
@@ -0,0 +1,19 @@
1
+ import { matchesGlob, relative } from "node:path";
2
+ //#region src/utils/watchPaths.ts
3
+ /**
4
+ * Build a predicate that tells whether an absolute file path emitted by
5
+ * chokidar matches any of the given globs. Patterns are interpreted as
6
+ * project-relative; a leading `./` is stripped so user-supplied globs like
7
+ * `./locales/**` behave identically to `locales/**`.
8
+ */
9
+ function createWatchedFileMatcher(patterns, cwd) {
10
+ const normalized = patterns.map((p) => p.replace(/^\.\//, ""));
11
+ return (file) => {
12
+ const rel = relative(cwd, file).replace(/\\/g, "/");
13
+ return normalized.some((p) => matchesGlob(rel, p));
14
+ };
15
+ }
16
+ //#endregion
17
+ export { createWatchedFileMatcher };
18
+
19
+ //# sourceMappingURL=watchPaths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watchPaths.js","names":[],"sources":["../../src/utils/watchPaths.ts"],"sourcesContent":["import { matchesGlob, relative } from 'node:path'\n\n/**\n * Build a predicate that tells whether an absolute file path emitted by\n * chokidar matches any of the given globs. Patterns are interpreted as\n * project-relative; a leading `./` is stripped so user-supplied globs like\n * `./locales/**` behave identically to `locales/**`.\n */\nexport function createWatchedFileMatcher(patterns: string[], cwd: string) {\n const normalized = patterns.map(p => p.replace(/^\\.\\//, ''))\n return (file: string) => {\n const rel = relative(cwd, file).replace(/\\\\/g, '/')\n return normalized.some(p => matchesGlob(rel, p))\n }\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,yBAAyB,UAAoB,KAAa;CACxE,MAAM,aAAa,SAAS,KAAI,MAAK,EAAE,QAAQ,SAAS,GAAG,CAAC;CAC5D,QAAQ,SAAiB;EACvB,MAAM,MAAM,SAAS,KAAK,KAAK,CAAC,QAAQ,OAAO,IAAI;EACnD,OAAO,WAAW,MAAK,MAAK,YAAY,KAAK,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maizzle/framework",
3
- "version": "6.0.0-rc.20",
3
+ "version": "6.0.0-rc.22",
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",
@@ -1,22 +0,0 @@
1
- import { CssConfig } from "../types/config.js";
2
- import { ChildNode } from "domhandler";
3
-
4
- //#region src/transformers/safeClassNames.d.ts
5
- /**
6
- * Safe class names transformer.
7
- *
8
- * Replaces unsafe characters (`:`, `/`, `[`, `]`, etc.) in:
9
- * - CSS selectors inside `<style>` tags
10
- * - HTML `class` attributes
11
- *
12
- * This makes Tailwind utility classes like `sm:text-base` safe for
13
- * email clients that cannot handle escaped characters in class names.
14
- *
15
- * Enabled by default. Disable by setting `css.safe` to `false`.
16
- * Customize replacements by passing a `Record<string, string>` — user
17
- * values are merged on top of the defaults.
18
- */
19
- declare function safeClassNames(dom: ChildNode[], config?: CssConfig): ChildNode[];
20
- //#endregion
21
- export { safeClassNames };
22
- //# sourceMappingURL=safeClassNames.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"safeClassNames.d.ts","names":[],"sources":["../../src/transformers/safeClassNames.ts"],"mappings":";;;;;;AAsGA;;;;;;;;;;;;iBAAgB,cAAA,CAAe,GAAA,EAAK,SAAA,IAAa,MAAA,GAAQ,SAAA,GAAiB,SAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"safeClassNames.js","names":[],"sources":["../../src/transformers/safeClassNames.ts"],"sourcesContent":["import postcss from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport type { ChildNode, Element } from 'domhandler'\nimport { walk } from '../utils/ast/index.ts'\nimport type { CssConfig } from '../types/config.ts'\n\nconst DEFAULT_REPLACEMENTS: Record<string, string> = {\n ':': '-',\n '/': '-',\n '%': 'pc',\n '.': '_',\n ',': '_',\n '#': '_',\n '[': '',\n ']': '',\n '(': '',\n ')': '',\n '{': '',\n '}': '',\n '!': '-i',\n '&': 'and-',\n '<': 'lt-',\n '=': 'eq-',\n '>': 'gt-',\n '|': 'or-',\n '@': 'at-',\n '?': 'q-',\n '\\\\': '-',\n '\"': '-',\n \"'\": '-',\n '*': '-',\n '+': '-',\n ';': '-',\n '^': '-',\n '`': '-',\n '~': '-',\n '$': '-',\n}\n\nfunction escapeForRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/**\n * Replace escaped special characters in CSS selectors.\n *\n * Tailwind generates selectors like `.sm\\:text-base`. This function\n * replaces the `\\:` with `-` (or whatever the configured replacement is)\n * so the selector becomes `.sm-text-base`, which is safe for email clients.\n */\nfunction processCssSelectors(css: string, replacements: Record<string, string>): string {\n // Matches \\<char> in CSS selectors — e.g. \\: \\/ \\. \\[ etc.\n const selectorRegex = new RegExp(\n `\\\\\\\\(${Object.keys(replacements).map(escapeForRegex).join('|')})`,\n 'g',\n )\n\n return postcss([\n (root: postcss.Root) => {\n root.walkRules((rule: postcss.Rule) => {\n rule.selector = rule.selector\n .replace(selectorRegex, (_matched, char) => replacements[char] ?? _matched)\n // Handle CSS unicode escape for comma (\\2c → _)\n .replaceAll('\\\\2c ', '_')\n })\n },\n ]).process(css, { parser: safeParser }).css\n}\n\n/**\n * Replace unsafe special characters in a class attribute value.\n *\n * Splits on whitespace and replaces each char from the replacements map\n * in each class token individually.\n */\nfunction processClassAttr(classStr: string, replacements: Record<string, string>): string {\n return classStr\n .split(/\\s+/)\n .filter(Boolean)\n .map((cls) => {\n for (const [from, to] of Object.entries(replacements)) {\n cls = cls.split(from).join(to)\n }\n return cls\n })\n .join(' ')\n}\n\n/**\n * Safe class names transformer.\n *\n * Replaces unsafe characters (`:`, `/`, `[`, `]`, etc.) in:\n * - CSS selectors inside `<style>` tags\n * - HTML `class` attributes\n *\n * This makes Tailwind utility classes like `sm:text-base` safe for\n * email clients that cannot handle escaped characters in class names.\n *\n * Enabled by default. Disable by setting `css.safe` to `false`.\n * Customize replacements by passing a `Record<string, string>` — user\n * values are merged on top of the defaults.\n */\nexport function safeClassNames(dom: ChildNode[], config: CssConfig = {}): ChildNode[] {\n const option = config.safe ?? true\n\n if (!option) return dom\n\n const replacements: Record<string, string> =\n option && typeof option === 'object'\n ? { ...DEFAULT_REPLACEMENTS, ...option }\n : DEFAULT_REPLACEMENTS\n\n walk(dom, (node) => {\n const el = node as Element\n\n // Process CSS selectors inside <style> tags\n if (el.name === 'style' && el.children?.length) {\n const text = el.children.find((c) => c.type === 'text') as any\n if (text?.data?.trim()) {\n text.data = processCssSelectors(text.data, replacements)\n }\n }\n\n // Replace special chars in class attributes\n if ('attribs' in el && el.attribs?.class) {\n el.attribs.class = processClassAttr(el.attribs.class, replacements)\n }\n })\n\n return dom\n}\n"],"mappings":";;;;;AAMA,MAAM,uBAA+C;CACnD,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;AAED,SAAS,eAAe,GAAmB;CACzC,OAAO,EAAE,QAAQ,uBAAuB,OAAO;;;;;;;;;AAUjD,SAAS,oBAAoB,KAAa,cAA8C;CAEtF,MAAM,gBAAgB,IAAI,OACxB,QAAQ,OAAO,KAAK,aAAa,CAAC,IAAI,eAAe,CAAC,KAAK,IAAI,CAAC,IAChE,IACD;CAED,OAAO,QAAQ,EACZ,SAAuB;EACtB,KAAK,WAAW,SAAuB;GACrC,KAAK,WAAW,KAAK,SAClB,QAAQ,gBAAgB,UAAU,SAAS,aAAa,SAAS,SAAS,CAE1E,WAAW,SAAS,IAAI;IAC3B;GAEL,CAAC,CAAC,QAAQ,KAAK,EAAE,QAAQ,YAAY,CAAC,CAAC;;;;;;;;AAS1C,SAAS,iBAAiB,UAAkB,cAA8C;CACxF,OAAO,SACJ,MAAM,MAAM,CACZ,OAAO,QAAQ,CACf,KAAK,QAAQ;EACZ,KAAK,MAAM,CAAC,MAAM,OAAO,OAAO,QAAQ,aAAa,EACnD,MAAM,IAAI,MAAM,KAAK,CAAC,KAAK,GAAG;EAEhC,OAAO;GACP,CACD,KAAK,IAAI;;;;;;;;;;;;;;;;AAiBd,SAAgB,eAAe,KAAkB,SAAoB,EAAE,EAAe;CACpF,MAAM,SAAS,OAAO,QAAQ;CAE9B,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,eACJ,UAAU,OAAO,WAAW,WACxB;EAAE,GAAG;EAAsB,GAAG;EAAQ,GACtC;CAEN,KAAK,MAAM,SAAS;EAClB,MAAM,KAAK;EAGX,IAAI,GAAG,SAAS,WAAW,GAAG,UAAU,QAAQ;GAC9C,MAAM,OAAO,GAAG,SAAS,MAAM,MAAM,EAAE,SAAS,OAAO;GACvD,IAAI,MAAM,MAAM,MAAM,EACpB,KAAK,OAAO,oBAAoB,KAAK,MAAM,aAAa;;EAK5D,IAAI,aAAa,MAAM,GAAG,SAAS,OACjC,GAAG,QAAQ,QAAQ,iBAAiB,GAAG,QAAQ,OAAO,aAAa;GAErE;CAEF,OAAO"}