@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":"columnWidth.js","names":[],"sources":["../../src/transformers/columnWidth.ts"],"sourcesContent":["import postcss, { type Root, type Declaration } from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport { walk } from '../utils/ast/index.ts'\nimport type { ChildNode, Element, ParentNode } from 'domhandler'\n\nconst RE_PERCENT = /^[\\d.]+%$/\nconst NO_BORDER_STYLES = new Set(['none', 'hidden'])\n\n/**\n * Stringify decls into a `; `-joined inline-style attribute. PostCSS raws\n * preserve the original source spacing, which mixes poorly with\n * the fresh decls we inject — plain join keeps output uniform.\n */\nfunction serializeStyle(root: Root): string {\n const parts: string[] = []\n root.walkDecls((d) => {\n parts.push(`${d.prop}: ${d.value}${d.important ? ' !important' : ''}`)\n })\n return parts.join('; ')\n}\n\nfunction firstDeclValue(root: Root, prop: string): string | undefined {\n let found: string | undefined\n root.walkDecls(prop, (d) => {\n found = d.value\n return false\n })\n return found\n}\n\n/**\n * Find the user-set `min-width:` value on a column. Juice keeps both ours\n * and the one inlined from a class like `min-w-1/3` — we skip any\n * min-width whose value still contains our placeholder token,\n * returning the first remaining user value, or null.\n */\nfunction findUserMinWidth(root: Root): string | null {\n let userVal: string | null = null\n root.walkDecls('min-width', (d) => {\n if (!d.value.includes('__MAIZZLE_COLW_')) {\n userVal = d.value\n return false\n }\n })\n return userVal\n}\n\nfunction resolveLength(value: string): string | null {\n const trimmed = value.trim()\n if (RE_PERCENT.test(trimmed)) return trimmed\n const m = trimmed.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 `${Math.round(n)}px`\n case 'rem':\n case 'em': return `${Math.round(n * 16)}px`\n case 'pt': return `${Math.round(n * 1.333)}px`\n default: return null\n }\n}\n\nfunction 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\nfunction divideLength(value: string, divisor: number): string | null {\n const m = value.match(/^([\\d.]+)(px|%)$/)\n if (!m || divisor < 1) return null\n const n = parseFloat(m[1])\n return `${parseFloat((n / divisor).toFixed(2))}${m[2]}`\n}\n\nfunction subtractInsetPx(width: string, insetPx: number): string {\n if (insetPx <= 0) return width\n const m = width.match(/^([\\d.]+)(px|%)$/)\n if (!m) return width\n // Don't subtract px from percentage widths — units don't match.\n if (m[2] === '%') return width\n const n = parseFloat(m[1]) - insetPx\n return `${Math.max(0, Math.round(n))}px`\n}\n\n/**\n * Return the smaller of two px lengths. Clamps our count-based min-width\n * down to the user's `max-width:` so the cap is never silently\n * violated when our computed min would exceed the user's max.\n */\nfunction minPxLength(a: string, b: string): string {\n const am = a.match(/^([\\d.]+)px$/)\n const bm = b.match(/^([\\d.]+)px$/)\n if (!am || !bm) return a\n return parseFloat(am[1]) < parseFloat(bm[1]) ? a : b\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 */\nfunction 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 — they'd need a known container width.\n */\nfunction horizontalPaddingPx(root: Root): number {\n let left: number | null = null\n let right: number | null = null\n\n // Shorthand applies first; longhand overrides per side.\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\n * no explicit width token is present in the shorthand value.\n */\nfunction 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 // Visible style, no explicit width → CSS default `medium` = 3px.\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 */\nfunction 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\nfunction depth(node: ChildNode): number {\n let d = 0\n let cur: ParentNode | null = node.parent\n while (cur) {\n d++\n cur = (cur as any).parent ?? null\n }\n return d\n}\n\nfunction readWidthFromRoot(root: Root): string | null {\n const raw = firstDeclValue(root, 'max-width')\n ?? firstDeclValue(root, 'width')\n ?? firstDeclValue(root, 'min-width')\n return raw ? resolveLength(raw) : null\n}\n\nfunction readHeightFromRoot(root: Root): string | null {\n const raw = firstDeclValue(root, 'max-height')\n ?? firstDeclValue(root, 'height')\n ?? firstDeclValue(root, 'min-height')\n return raw ? resolveLength(raw) : null\n}\n\nfunction readWidthSource(el: Element, root: Root | null): string | null {\n const explicit = el.attribs?.['data-maizzle-cw']\n if (explicit) {\n const r = resolveLength(explicit)\n if (r) return r\n }\n return root ? readWidthFromRoot(root) : null\n}\n\n/**\n * Convert a user-supplied length to absolute px against the column's source\n * width (post-inset). Percentages multiply against the source while\n * absolute units pass through `resolveLength`. Returns null when\n * the value or source can't be expressed in px.\n */\nfunction userValueToPx(rawValue: string, sourcePx: string | null): string | null {\n const trimmed = rawValue.trim()\n\n const absMatch = trimmed.match(/^([\\d.]+)(px|rem|em|pt)$/i)\n if (absMatch) return resolveLength(trimmed)\n\n const pctMatch = trimmed.match(/^([\\d.]+)%$/)\n if (!pctMatch || !sourcePx) return null\n const sourceMatch = sourcePx.match(/^([\\d.]+)px$/)\n if (!sourceMatch) return null\n const pct = parseFloat(pctMatch[1])\n const src = parseFloat(sourceMatch[1])\n return `${Math.round((pct / 100) * src)}px`\n}\n\n/**\n * Resolve `__MAIZZLE_COLW_{id}__` and `__MAIZZLE_OH_{id}__` placeholders.\n *\n * COLW (column width) — emitted by `<Column>` and `<Overlap>`. Walks up to\n * the nearest ancestor marked `data-maizzle-cw` (Container, Section,\n * Row, or another Column already resolved) and divides the source\n * width by `data-maizzle-cw-count`. With `data-maizzle-cw-self`,\n * reads from the element's own inlined max/width/min-width\n * instead — used by `<Overlap>` with its own width class.\n *\n * OH (overlap height) — emitted by `<Overlap>`. Reads max-height, height,\n * or min-height from the element's own inlined style.\n *\n * Resolution rules:\n * - Style placeholders for `min-width`: replaced when resolvable, otherwise\n * the entire `min-width` declaration is stripped.\n * - Other style placeholders (Overlap td `width`, etc.): replaced when\n * resolvable, otherwise replaced with the count-based fallback or `100%`.\n * - Comment placeholders: same fallback chain.\n *\n * Resolved column widths are written back to `data-maizzle-cw` so nested\n * rows cascade. All `data-maizzle-cw*` and `data-maizzle-oh-*` are\n * stripped at the end of the second walk pass.\n */\nexport function columnWidth(dom: ChildNode[]): ChildNode[] {\n /**\n * Cache parsed style ASTs for this columnWidth invocation. The walk-up\n * loop visits the same Section/Container once per column of a Row,\n * so without caching each column re-parses every ancestor's style.\n * Cache is function-local — no cross-build leak via the WeakMap.\n */\n const styleCache = new WeakMap<Element, Root>()\n const parseElStyle = (el: Element): Root => {\n const cached = styleCache.get(el)\n if (cached) return cached\n const style = el.attribs?.style ?? ''\n const root = style ? safeParser(style) : postcss.root()\n styleCache.set(el, root)\n return root\n }\n\n const columns: { el: Element; id: string; count: number; d: number; self: boolean }[] = []\n const heightTargets: { el: Element; id: string }[] = []\n\n walk(dom, (node) => {\n const el = node as Element\n if (!el.attribs) return\n\n const id = el.attribs['data-maizzle-cw-id']\n if (id) {\n const count = parseInt(el.attribs['data-maizzle-cw-count'] || '1', 10)\n const self = 'data-maizzle-cw-self' in el.attribs\n columns.push({ el, id, count, d: depth(node), self })\n }\n\n const ohId = el.attribs['data-maizzle-oh-id']\n if (ohId) heightTargets.push({ el, id: ohId })\n })\n\n columns.sort((a, b) => a.d - b.d)\n\n const widthResolutions = new Map<string, string>()\n const widthFallbacks = new Map<string, string>()\n /**\n * Column ids whose absolute user `width:` was promoted to `min-width:`\n * — the original `width:` declaration must be stripped from the\n * column's style (otherwise it'd compete with the min-width).\n */\n const stripWidth = new Set<string>()\n /**\n * Column ids where the user wrote a percentage `width:` (e.g. `w-1/2`) —\n * explicit opt-out of px-based stacking. Keep the user's `width: X%`\n * and drop our `min-width:` placeholder so the column stays at\n * that percentage of its parent forever and never stacks.\n */\n const dropMinWidth = new Set<string>()\n /**\n * Column ids where the user wrote their own `min-width:` (via `min-w-1/3`).\n * Juice inlines theirs after ours, so two `min-width:` decls land in\n * the style — we strip the user's after using its value as the\n * column's resolution, leaving our placeholder as last word.\n */\n const stripUserMinWidth = new Set<string>()\n /**\n * Column ids where the user already supplied a `max-width:` of their own.\n * Our default `max-width: 100%` would just be shadowed by it via\n * last-wins and bloat the style — so we skip emitting it.\n */\n const userHasMaxWidth = new Set<string>()\n\n for (const { id, count } of columns) {\n widthFallbacks.set(id, `${Math.round(100 / Math.max(count, 1))}%`)\n }\n\n for (const { el, id, count, self } of columns) {\n const ownRoot = parseElStyle(el)\n\n let sourceWidth: string | null = null\n let accumulatedInsetPx = 0\n\n if (self) {\n sourceWidth = readWidthFromRoot(ownRoot)\n accumulatedInsetPx = horizontalPaddingPx(ownRoot) + horizontalBorderPx(ownRoot)\n }\n else {\n /**\n * Walk up through every ancestor with attribs, accumulating horizontal\n * padding+border along the way (including the source). Stop at the\n * first `data-maizzle-cw` ancestor whose width is resolvable.\n * Markers without a resolvable width (Row emitted empty after\n * Tailwind dropped a bogus class) shouldn't shadow a real\n * width on a higher ancestor like `<Container>`.\n *\n * With CSS content-box this is technically generous toward the\n * source's own padding/border, but matches user expectations\n * when they put `px-9` or `border-2` on a wrapper.\n */\n let cur: ParentNode | null = el.parent\n while (cur) {\n const parentEl = cur as Element\n if (parentEl.attribs) {\n let pRoot: Root | null = null\n if (parentEl.attribs.style) {\n pRoot = parseElStyle(parentEl)\n accumulatedInsetPx += horizontalPaddingPx(pRoot) + horizontalBorderPx(pRoot)\n }\n if ('data-maizzle-cw' in parentEl.attribs) {\n const w = readWidthSource(parentEl, pRoot)\n if (w) {\n sourceWidth = w\n break\n }\n }\n }\n cur = (cur as any).parent ?? null\n }\n }\n\n const adjusted = sourceWidth ? subtractInsetPx(sourceWidth, accumulatedInsetPx) : null\n const countBased = adjusted ? divideLength(adjusted, count) : null\n\n /**\n * Four user-override paths, decided by which CSS property the user\n * actually wrote:\n *\n * - `min-width: X` → user's value wins. Convert to px against\n * the source (if %), use as the column's\n * resolution, and strip the user's min-width\n * declaration so our placeholder substitution\n * remains the last `min-width:` in style.\n * - `width: X%` → opt-out of px stacking. Keep `width:` in\n * style, drop our `min-width:` placeholder.\n * Cols stay at X% of parent forever, never stack.\n * - `width: Xpx` (or rem/em/pt) → fixed pixel column. Promote to\n * `min-width:`, strip the original `width:` so\n * it doesn't compete.\n * - `max-width: X` → CSS cap. Keep the `max-width:` declaration;\n * clamp our count-based min-width *down* to\n * the user's max-width when our min would\n * otherwise violate it.\n */\n const userMinRaw = findUserMinWidth(ownRoot)\n const widthRaw = firstDeclValue(ownRoot, 'width')\n const maxRaw = firstDeclValue(ownRoot, 'max-width')\n\n if (userMinRaw) {\n const minPx = userValueToPx(userMinRaw, adjusted) ?? resolveLength(userMinRaw)\n if (minPx) {\n widthResolutions.set(id, minPx)\n el.attribs['data-maizzle-cw'] = minPx\n stripUserMinWidth.add(id)\n continue\n }\n }\n\n if (widthRaw) {\n const widthVal = resolveLength(widthRaw)\n if (widthVal?.endsWith('%')) {\n widthResolutions.set(id, widthVal)\n el.attribs['data-maizzle-cw'] = widthVal\n dropMinWidth.add(id)\n continue\n }\n if (widthVal) {\n widthResolutions.set(id, widthVal)\n el.attribs['data-maizzle-cw'] = widthVal\n stripWidth.add(id)\n continue\n }\n }\n\n if (maxRaw && countBased) {\n const maxPx = userValueToPx(maxRaw, adjusted)\n if (maxPx) {\n const cappedMin = countBased.endsWith('px')\n ? minPxLength(countBased, maxPx)\n : maxPx\n widthResolutions.set(id, cappedMin)\n el.attribs['data-maizzle-cw'] = cappedMin\n userHasMaxWidth.add(id)\n continue\n }\n }\n\n if (countBased) {\n widthResolutions.set(id, countBased)\n el.attribs['data-maizzle-cw'] = countBased\n }\n }\n\n const heightResolutions = new Map<string, string>()\n for (const { el, id } of heightTargets) {\n if (!el.attribs?.style) continue\n const h = readHeightFromRoot(parseElStyle(el))\n if (h) heightResolutions.set(id, h)\n }\n\n walk(dom, (node) => {\n if (node.type === 'comment') {\n const data = (node as any).data as string\n if (!data || (!data.includes('__MAIZZLE_COLW_') && !data.includes('__MAIZZLE_OH_'))) return\n ;(node as any).data = data\n .replace(/__MAIZZLE_COLW_([^_]+)__/g,\n (_m, mid) => widthResolutions.get(mid) ?? widthFallbacks.get(mid) ?? '100%')\n .replace(/__MAIZZLE_OH_([^_]+)__/g,\n (_m, hid) => heightResolutions.get(hid) ?? '100%')\n return\n }\n\n const el = node as Element\n if (!el.attribs) return\n\n const style = el.attribs.style\n if (style && (style.includes('__MAIZZLE_COLW_') || style.includes('__MAIZZLE_OH_'))) {\n const root = parseElStyle(el)\n const cwId = el.attribs['data-maizzle-cw-id']\n\n /**\n * Strip user dups BEFORE substitution — last-wins CSS would\n * otherwise shadow our resolved values in the output.\n */\n if (cwId && stripUserMinWidth.has(cwId)) {\n root.walkDecls('min-width', (d) => {\n if (!d.value.includes('__MAIZZLE_COLW_')) d.remove()\n })\n }\n if (cwId && stripWidth.has(cwId)) {\n root.walkDecls('width', (d) => { d.remove() })\n }\n\n /**\n * Substitute the column's `min-width:` placeholder with `width: <res>;\n * max-width: 100%`. Width gives the same stacking trigger as\n * min-width — inline-block wraps when children sum > parent\n * — and the `max-width: 100%` clamp keeps the column from\n * overflowing the viewport once it drops to its own row on\n * mobile. Skip the clamp when the user supplied their own.\n *\n * Other placeholders (Overlap td `width`, comment markers,\n * OH height) get a plain value substitution.\n */\n root.walkDecls((d) => {\n if (d.prop === 'min-width') {\n const m = d.value.match(/^__MAIZZLE_COLW_([^_]+)__$/)\n if (m) {\n const mid = m[1]\n if (dropMinWidth.has(mid) || !widthResolutions.has(mid)) {\n d.remove()\n return\n }\n const resolved = widthResolutions.get(mid)!\n const repl: Declaration[] = [postcss.decl({ prop: 'width', value: resolved })]\n if (!userHasMaxWidth.has(mid)) {\n repl.push(postcss.decl({ prop: 'max-width', value: '100%' }))\n }\n d.replaceWith(...repl)\n return\n }\n }\n if (d.value.includes('__MAIZZLE_COLW_') || d.value.includes('__MAIZZLE_OH_')) {\n d.value = d.value\n .replace(/__MAIZZLE_COLW_([^_]+)__/g,\n (_m, mid) => widthResolutions.get(mid) ?? widthFallbacks.get(mid) ?? '100%')\n .replace(/__MAIZZLE_OH_([^_]+)__/g,\n (_m, hid) => heightResolutions.get(hid) ?? '100%')\n }\n })\n\n const out = serializeStyle(root)\n if (out) el.attribs.style = out\n else delete el.attribs.style\n }\n\n delete el.attribs['data-maizzle-cw']\n delete el.attribs['data-maizzle-cw-id']\n delete el.attribs['data-maizzle-cw-count']\n delete el.attribs['data-maizzle-cw-self']\n delete el.attribs['data-maizzle-oh-id']\n })\n\n return dom\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"columnWidth.js","names":[],"sources":["../../src/transformers/columnWidth.ts"],"sourcesContent":["import postcss, { type Root, type Declaration } from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport { walk } from '../utils/ast/index.ts'\nimport type { ChildNode, Element, ParentNode } from 'domhandler'\n\nconst RE_PERCENT = /^[\\d.]+%$/\nconst NO_BORDER_STYLES = new Set(['none', 'hidden'])\n\n/**\n * Stringify decls into a `; `-joined inline-style attribute. PostCSS raws\n * preserve the original source spacing, which mixes poorly with\n * the fresh decls we inject — plain join keeps output uniform.\n */\nfunction serializeStyle(root: Root): string {\n const parts: string[] = []\n root.walkDecls((d) => {\n parts.push(`${d.prop}: ${d.value}${d.important ? ' !important' : ''}`)\n })\n return parts.join('; ')\n}\n\nfunction firstDeclValue(root: Root, prop: string): string | undefined {\n let found: string | undefined\n root.walkDecls(prop, (d) => {\n found = d.value\n return false\n })\n return found\n}\n\n/**\n * Find the user-set `min-width:` value on a column. Juice keeps both ours\n * and the one inlined from a class like `min-w-1/3` — we skip any\n * min-width whose value still contains our placeholder token,\n * returning the first remaining user value, or null.\n */\nfunction findUserMinWidth(root: Root): string | null {\n let userVal: string | null = null\n root.walkDecls('min-width', (d) => {\n if (!d.value.includes('__MAIZZLE_COLW_')) {\n userVal = d.value\n return false\n }\n })\n return userVal\n}\n\nfunction resolveLength(value: string): string | null {\n const trimmed = value.trim()\n if (RE_PERCENT.test(trimmed)) return trimmed\n const m = trimmed.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 `${Math.round(n)}px`\n case 'rem':\n case 'em': return `${Math.round(n * 16)}px`\n case 'pt': return `${Math.round(n * 1.333)}px`\n default: return null\n }\n}\n\nfunction 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\nfunction divideLength(value: string, divisor: number): string | null {\n const m = value.match(/^([\\d.]+)(px|%)$/)\n if (!m || divisor < 1) return null\n const n = parseFloat(m[1])\n return `${parseFloat((n / divisor).toFixed(2))}${m[2]}`\n}\n\nfunction subtractInsetPx(width: string, insetPx: number): string {\n if (insetPx <= 0) return width\n const m = width.match(/^([\\d.]+)(px|%)$/)\n if (!m) return width\n // Don't subtract px from percentage widths — units don't match.\n if (m[2] === '%') return width\n const n = parseFloat(m[1]) - insetPx\n return `${Math.max(0, Math.round(n))}px`\n}\n\n/**\n * Return the smaller of two px lengths. Clamps our count-based min-width\n * down to the user's `max-width:` so the cap is never silently\n * violated when our computed min would exceed the user's max.\n */\nfunction minPxLength(a: string, b: string): string {\n const am = a.match(/^([\\d.]+)px$/)\n const bm = b.match(/^([\\d.]+)px$/)\n if (!am || !bm) return a\n return parseFloat(am[1]) < parseFloat(bm[1]) ? a : b\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 */\nfunction 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 — they'd need a known container width.\n */\nfunction horizontalPaddingPx(root: Root): number {\n let left: number | null = null\n let right: number | null = null\n\n // Shorthand applies first; longhand overrides per side.\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\n * no explicit width token is present in the shorthand value.\n */\nfunction 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 // Visible style, no explicit width → CSS default `medium` = 3px.\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 */\nfunction 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\nfunction depth(node: ChildNode): number {\n let d = 0\n let cur: ParentNode | null = node.parent\n while (cur) {\n d++\n cur = (cur as any).parent ?? null\n }\n return d\n}\n\nfunction readWidthFromRoot(root: Root): string | null {\n const raw = firstDeclValue(root, 'max-width')\n ?? firstDeclValue(root, 'width')\n ?? firstDeclValue(root, 'min-width')\n return raw ? resolveLength(raw) : null\n}\n\nfunction readHeightFromRoot(root: Root): string | null {\n const raw = firstDeclValue(root, 'max-height')\n ?? firstDeclValue(root, 'height')\n ?? firstDeclValue(root, 'min-height')\n return raw ? resolveLength(raw) : null\n}\n\nfunction readWidthSource(el: Element, root: Root | null): string | null {\n const explicit = el.attribs?.['data-maizzle-cw']\n if (explicit) {\n const r = resolveLength(explicit)\n if (r) return r\n }\n return root ? readWidthFromRoot(root) : null\n}\n\n/**\n * Convert a user-supplied length to absolute px against the column's source\n * width (post-inset). Percentages multiply against the source while\n * absolute units pass through `resolveLength`. Returns null when\n * the value or source can't be expressed in px.\n */\nfunction userValueToPx(rawValue: string, sourcePx: string | null): string | null {\n const trimmed = rawValue.trim()\n\n const absMatch = trimmed.match(/^([\\d.]+)(px|rem|em|pt)$/i)\n if (absMatch) return resolveLength(trimmed)\n\n const pctMatch = trimmed.match(/^([\\d.]+)%$/)\n if (!pctMatch || !sourcePx) return null\n const sourceMatch = sourcePx.match(/^([\\d.]+)px$/)\n if (!sourceMatch) return null\n const pct = parseFloat(pctMatch[1])\n const src = parseFloat(sourceMatch[1])\n return `${Math.round((pct / 100) * src)}px`\n}\n\n/**\n * Resolve `__MAIZZLE_COLW_{id}__` and `__MAIZZLE_OH_{id}__` placeholders.\n *\n * COLW (column width) — emitted by `<Column>` and `<Overlap>`. Walks up to\n * the nearest ancestor marked `data-maizzle-cw` (Container, Section,\n * Row, or another Column already resolved) and divides the source\n * width by `data-maizzle-cw-count`. With `data-maizzle-cw-self`,\n * reads from the element's own inlined max/width/min-width\n * instead — used by `<Overlap>` with its own width class.\n *\n * OH (overlap height) — emitted by `<Overlap>`. Reads max-height, height,\n * or min-height from the element's own inlined style.\n *\n * Resolution rules:\n * - Style placeholders for `min-width`: replaced when resolvable, otherwise\n * the entire `min-width` declaration is stripped.\n * - Other style placeholders (Overlap td `width`, etc.): replaced when\n * resolvable, otherwise replaced with the count-based fallback or `100%`.\n * - Comment placeholders: same fallback chain.\n *\n * Resolved column widths are written back to `data-maizzle-cw` so nested\n * rows cascade. All `data-maizzle-cw*` and `data-maizzle-oh-*` are\n * stripped at the end of the second walk pass.\n */\nexport function columnWidth(dom: ChildNode[]): ChildNode[] {\n /**\n * Cache parsed style ASTs for this columnWidth invocation. The walk-up\n * loop visits the same Section/Container once per column of a Row,\n * so without caching each column re-parses every ancestor's style.\n * Cache is function-local — no cross-build leak via the WeakMap.\n */\n const styleCache = new WeakMap<Element, Root>()\n const parseElStyle = (el: Element): Root => {\n const cached = styleCache.get(el)\n if (cached) return cached\n const style = el.attribs?.style ?? ''\n const root = style ? safeParser(style) : postcss.root()\n styleCache.set(el, root)\n return root\n }\n\n const columns: { el: Element; id: string; count: number; d: number; self: boolean }[] = []\n const heightTargets: { el: Element; id: string }[] = []\n\n walk(dom, (node) => {\n const el = node as Element\n if (!el.attribs) return\n\n const id = el.attribs['data-maizzle-cw-id']\n if (id) {\n const count = parseInt(el.attribs['data-maizzle-cw-count'] || '1', 10)\n const self = 'data-maizzle-cw-self' in el.attribs\n columns.push({ el, id, count, d: depth(node), self })\n }\n\n const ohId = el.attribs['data-maizzle-oh-id']\n if (ohId) heightTargets.push({ el, id: ohId })\n })\n\n columns.sort((a, b) => a.d - b.d)\n\n const widthResolutions = new Map<string, string>()\n const widthFallbacks = new Map<string, string>()\n /**\n * Column ids whose absolute user `width:` was promoted to `min-width:`\n * — the original `width:` declaration must be stripped from the\n * column's style (otherwise it'd compete with the min-width).\n */\n const stripWidth = new Set<string>()\n /**\n * Column ids where the user wrote a percentage `width:` (e.g. `w-1/2`) —\n * explicit opt-out of px-based stacking. Keep the user's `width: X%`\n * and drop our `min-width:` placeholder so the column stays at\n * that percentage of its parent forever and never stacks.\n */\n const dropMinWidth = new Set<string>()\n /**\n * Column ids where the user wrote their own `min-width:` (via `min-w-1/3`).\n * Juice inlines theirs after ours, so two `min-width:` decls land in\n * the style — we strip the user's after using its value as the\n * column's resolution, leaving our placeholder as last word.\n */\n const stripUserMinWidth = new Set<string>()\n /**\n * Column ids where the user already supplied a `max-width:` of their own.\n * Our default `max-width: 100%` would just be shadowed by it via\n * last-wins and bloat the style — so we skip emitting it.\n */\n const userHasMaxWidth = new Set<string>()\n\n for (const { id, count } of columns) {\n widthFallbacks.set(id, `${Math.round(100 / Math.max(count, 1))}%`)\n }\n\n for (const { el, id, count, self } of columns) {\n const ownRoot = parseElStyle(el)\n\n let sourceWidth: string | null = null\n let accumulatedInsetPx = 0\n\n if (self) {\n sourceWidth = readWidthFromRoot(ownRoot)\n accumulatedInsetPx = horizontalPaddingPx(ownRoot) + horizontalBorderPx(ownRoot)\n }\n else {\n /**\n * Walk up through every ancestor with attribs, accumulating horizontal\n * padding+border along the way (including the source). Stop at the\n * first `data-maizzle-cw` ancestor whose width is resolvable.\n * Markers without a resolvable width (Row emitted empty after\n * Tailwind dropped a bogus class) shouldn't shadow a real\n * width on a higher ancestor like `<Container>`.\n *\n * With CSS content-box this is technically generous toward the\n * source's own padding/border, but matches user expectations\n * when they put `px-9` or `border-2` on a wrapper.\n */\n let cur: ParentNode | null = el.parent\n while (cur) {\n const parentEl = cur as Element\n if (parentEl.attribs) {\n let pRoot: Root | null = null\n if (parentEl.attribs.style) {\n pRoot = parseElStyle(parentEl)\n accumulatedInsetPx += horizontalPaddingPx(pRoot) + horizontalBorderPx(pRoot)\n }\n if ('data-maizzle-cw' in parentEl.attribs) {\n const w = readWidthSource(parentEl, pRoot)\n if (w) {\n sourceWidth = w\n break\n }\n }\n }\n cur = (cur as any).parent ?? null\n }\n }\n\n const adjusted = sourceWidth ? subtractInsetPx(sourceWidth, accumulatedInsetPx) : null\n const countBased = adjusted ? divideLength(adjusted, count) : null\n\n /**\n * Four user-override paths, decided by which CSS property the user\n * actually wrote:\n *\n * - `min-width: X` → user's value wins. Convert to px against\n * the source (if %), use as the column's\n * resolution, and strip the user's min-width\n * declaration so our placeholder substitution\n * remains the last `min-width:` in style.\n * - `width: X%` → opt-out of px stacking. Keep `width:` in\n * style, drop our `min-width:` placeholder.\n * Cols stay at X% of parent forever, never stack.\n * - `width: Xpx` (or rem/em/pt) → fixed pixel column. Promote to\n * `min-width:`, strip the original `width:` so\n * it doesn't compete.\n * - `max-width: X` → CSS cap. Keep the `max-width:` declaration;\n * clamp our count-based min-width *down* to\n * the user's max-width when our min would\n * otherwise violate it.\n */\n const userMinRaw = findUserMinWidth(ownRoot)\n const widthRaw = firstDeclValue(ownRoot, 'width')\n const maxRaw = firstDeclValue(ownRoot, 'max-width')\n\n if (userMinRaw) {\n const minPx = userValueToPx(userMinRaw, adjusted) ?? resolveLength(userMinRaw)\n if (minPx) {\n widthResolutions.set(id, minPx)\n el.attribs['data-maizzle-cw'] = minPx\n stripUserMinWidth.add(id)\n continue\n }\n }\n\n if (widthRaw) {\n const widthVal = resolveLength(widthRaw)\n if (widthVal?.endsWith('%')) {\n widthResolutions.set(id, widthVal)\n el.attribs['data-maizzle-cw'] = widthVal\n dropMinWidth.add(id)\n continue\n }\n if (widthVal) {\n widthResolutions.set(id, widthVal)\n el.attribs['data-maizzle-cw'] = widthVal\n stripWidth.add(id)\n continue\n }\n }\n\n if (maxRaw && countBased) {\n const maxPx = userValueToPx(maxRaw, adjusted)\n if (maxPx) {\n const cappedMin = countBased.endsWith('px')\n ? minPxLength(countBased, maxPx)\n : maxPx\n widthResolutions.set(id, cappedMin)\n el.attribs['data-maizzle-cw'] = cappedMin\n userHasMaxWidth.add(id)\n continue\n }\n }\n\n if (countBased) {\n widthResolutions.set(id, countBased)\n el.attribs['data-maizzle-cw'] = countBased\n }\n }\n\n const heightResolutions = new Map<string, string>()\n for (const { el, id } of heightTargets) {\n if (!el.attribs?.style) continue\n const h = readHeightFromRoot(parseElStyle(el))\n if (h) heightResolutions.set(id, h)\n }\n\n walk(dom, (node) => {\n if (node.type === 'comment') {\n const data = (node as any).data as string\n if (!data || (!data.includes('__MAIZZLE_COLW_') && !data.includes('__MAIZZLE_OH_'))) return\n ;(node as any).data = data\n .replace(/__MAIZZLE_COLW_([^_]+)__/g,\n (_m, mid) => widthResolutions.get(mid) ?? widthFallbacks.get(mid) ?? '100%')\n .replace(/__MAIZZLE_OH_([^_]+)__/g,\n (_m, hid) => heightResolutions.get(hid) ?? '100%')\n return\n }\n\n const el = node as Element\n if (!el.attribs) return\n\n const style = el.attribs.style\n if (style && (style.includes('__MAIZZLE_COLW_') || style.includes('__MAIZZLE_OH_'))) {\n const root = parseElStyle(el)\n const cwId = el.attribs['data-maizzle-cw-id']\n\n /**\n * Strip user dups BEFORE substitution — last-wins CSS would\n * otherwise shadow our resolved values in the output.\n */\n if (cwId && stripUserMinWidth.has(cwId)) {\n root.walkDecls('min-width', (d) => {\n if (!d.value.includes('__MAIZZLE_COLW_')) d.remove()\n })\n }\n if (cwId && stripWidth.has(cwId)) {\n root.walkDecls('width', (d) => { d.remove() })\n }\n\n /**\n * Substitute the column's `min-width:` placeholder with `width: <res>;\n * max-width: 100%`. Width gives the same stacking trigger as\n * min-width — inline-block wraps when children sum > parent\n * — and the `max-width: 100%` clamp keeps the column from\n * overflowing the viewport once it drops to its own row on\n * mobile. Skip the clamp when the user supplied their own.\n *\n * Other placeholders (Overlap td `width`, comment markers,\n * OH height) get a plain value substitution.\n */\n root.walkDecls((d) => {\n if (d.prop === 'min-width') {\n const m = d.value.match(/^__MAIZZLE_COLW_([^_]+)__$/)\n if (m) {\n const mid = m[1]\n if (dropMinWidth.has(mid) || !widthResolutions.has(mid)) {\n d.remove()\n return\n }\n const resolved = widthResolutions.get(mid)!\n const repl: Declaration[] = [postcss.decl({ prop: 'width', value: resolved })]\n if (!userHasMaxWidth.has(mid)) {\n repl.push(postcss.decl({ prop: 'max-width', value: '100%' }))\n }\n d.replaceWith(...repl)\n return\n }\n }\n if (d.value.includes('__MAIZZLE_COLW_') || d.value.includes('__MAIZZLE_OH_')) {\n d.value = d.value\n .replace(/__MAIZZLE_COLW_([^_]+)__/g,\n (_m, mid) => widthResolutions.get(mid) ?? widthFallbacks.get(mid) ?? '100%')\n .replace(/__MAIZZLE_OH_([^_]+)__/g,\n (_m, hid) => heightResolutions.get(hid) ?? '100%')\n }\n })\n\n const out = serializeStyle(root)\n if (out) el.attribs.style = out\n else delete el.attribs.style\n }\n\n delete el.attribs['data-maizzle-cw']\n delete el.attribs['data-maizzle-cw-id']\n delete el.attribs['data-maizzle-cw-count']\n delete el.attribs['data-maizzle-cw-self']\n delete el.attribs['data-maizzle-oh-id']\n })\n\n return dom\n}\n"],"mappings":"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"}
@@ -2,7 +2,36 @@ import { EntitiesConfig } from "../types/config.js";
2
2
  import { ChildNode } from "domhandler";
3
3
 
4
4
  //#region src/transformers/entities.d.ts
5
- declare function entities(dom: ChildNode[], config?: EntitiesConfig): ChildNode[];
5
+ /**
6
+ * Replace literal Unicode characters in text nodes with their HTML entity
7
+ * equivalents (zero-width joiners, non-breaking spaces, smart quotes,
8
+ * dashes, etc.) for better email-client rendering.
9
+ *
10
+ * @param html HTML string to transform.
11
+ * @param custom Extra entries merged on top of the built-in entity map, or
12
+ * `false` to disable the transform. Defaults to `true`
13
+ * (built-ins only).
14
+ * @returns The transformed HTML string.
15
+ *
16
+ * @example
17
+ * import { entities } from '@maizzle/framework'
18
+ *
19
+ * // Defaults only
20
+ * entities('hello world') // → 'hello&nbsp;world'
21
+ *
22
+ * // Add a custom mapping
23
+ * entities('© Maizzle', { '©': '&copy;' })
24
+ *
25
+ * // Disable the transform
26
+ * entities('hello world', false)
27
+ */
28
+ declare function entities(html: string, custom?: EntitiesConfig): string;
29
+ /**
30
+ * DOM-form of {@link entities} used by the internal transformer pipeline.
31
+ * Takes a parsed DOM, returns a parsed DOM — avoids redundant
32
+ * serialize/parse round-trips when chained with other transformers.
33
+ */
34
+ declare function entitiesDom(dom: ChildNode[], custom?: EntitiesConfig): ChildNode[];
6
35
  //#endregion
7
- export { entities };
36
+ export { entities, entitiesDom };
8
37
  //# sourceMappingURL=entities.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entities.d.ts","names":[],"sources":["../../src/transformers/entities.ts"],"mappings":";;;;iBA8BgB,QAAA,CAAS,GAAA,EAAK,SAAA,IAAa,MAAA,GAAQ,cAAA,GAAwB,SAAA"}
1
+ {"version":3,"file":"entities.d.ts","names":[],"sources":["../../src/transformers/entities.ts"],"mappings":";;;;;;;AAqDA;;;;;;;;;AASA;;;;;;;;;;;iBATgB,QAAA,CAAS,IAAA,UAAc,MAAA,GAAQ,cAAA;;;;;;iBAS/B,WAAA,CAAY,GAAA,EAAK,SAAA,IAAa,MAAA,GAAQ,cAAA,GAAwB,SAAA"}
@@ -1,7 +1,8 @@
1
+ import { parse } from "../utils/ast/parser.js";
1
2
  import { walk } from "../utils/ast/walker.js";
3
+ import { serialize } from "../utils/ast/serializer.js";
2
4
  import "../utils/ast/index.js";
3
- import { defu } from "defu";
4
-
5
+ import { defu as defu$1 } from "defu";
5
6
  //#region src/transformers/entities.ts
6
7
  const DEFAULT_ENTITIES = {
7
8
  "‍": "&zwj;",
@@ -27,15 +28,46 @@ const DEFAULT_ENTITIES = {
27
28
  "‹": "&lsaquo;",
28
29
  "›": "&rsaquo;"
29
30
  };
30
- function entities(dom, config = true) {
31
- if (!config) return dom;
32
- const map = typeof config === "object" ? defu(config, DEFAULT_ENTITIES) : DEFAULT_ENTITIES;
31
+ /**
32
+ * Replace literal Unicode characters in text nodes with their HTML entity
33
+ * equivalents (zero-width joiners, non-breaking spaces, smart quotes,
34
+ * dashes, etc.) for better email-client rendering.
35
+ *
36
+ * @param html HTML string to transform.
37
+ * @param custom Extra entries merged on top of the built-in entity map, or
38
+ * `false` to disable the transform. Defaults to `true`
39
+ * (built-ins only).
40
+ * @returns The transformed HTML string.
41
+ *
42
+ * @example
43
+ * import { entities } from '@maizzle/framework'
44
+ *
45
+ * // Defaults only
46
+ * entities('hello world') // → 'hello&nbsp;world'
47
+ *
48
+ * // Add a custom mapping
49
+ * entities('© Maizzle', { '©': '&copy;' })
50
+ *
51
+ * // Disable the transform
52
+ * entities('hello world', false)
53
+ */
54
+ function entities(html, custom = true) {
55
+ return serialize(entitiesDom(parse(html), custom));
56
+ }
57
+ /**
58
+ * DOM-form of {@link entities} used by the internal transformer pipeline.
59
+ * Takes a parsed DOM, returns a parsed DOM — avoids redundant
60
+ * serialize/parse round-trips when chained with other transformers.
61
+ */
62
+ function entitiesDom(dom, custom = true) {
63
+ if (!custom) return dom;
64
+ const map = typeof custom === "object" ? defu$1(custom, DEFAULT_ENTITIES) : DEFAULT_ENTITIES;
33
65
  walk(dom, (node) => {
34
66
  if (node.type === "text") for (const [char, entity] of Object.entries(map)) node.data = node.data.split(char).join(entity);
35
67
  });
36
68
  return dom;
37
69
  }
38
-
39
70
  //#endregion
40
- export { entities };
71
+ export { entities, entitiesDom };
72
+
41
73
  //# sourceMappingURL=entities.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"entities.js","names":["merge"],"sources":["../../src/transformers/entities.ts"],"sourcesContent":["import { defu as merge } from 'defu'\nimport { walk } from '../utils/ast/index.ts'\nimport type { ChildNode } from 'domhandler'\nimport type { EntitiesConfig } from '../types/index.ts'\n\nconst DEFAULT_ENTITIES: Record<string, string> = {\n '\\u200D': '&zwj;',\n '\\u200C': '&zwnj;',\n '\\u00A0': '&nbsp;',\n '\\u00AD': '&shy;',\n '\\u200B': '&#8203;',\n '\\u2007': '&#8199;',\n '\\u034F': '&#847;',\n '\\u2003': '&emsp;',\n '\\u2028': '&LineSeparator;',\n '\\u2029': '&ParagraphSeparator;',\n '\\u00B7': '&middot;',\n '\\u2013': '&ndash;',\n '\\u2014': '&mdash;',\n '\\u2018': '&lsquo;',\n '\\u2019': '&rsquo;',\n '\\u201C': '&ldquo;',\n '\\u201D': '&rdquo;',\n '\\u00AB': '&laquo;',\n '\\u00BB': '&raquo;',\n '\\u2022': '&bull;',\n '\\u2039': '&lsaquo;',\n '\\u203A': '&rsaquo;'\n}\n\nexport function entities(dom: ChildNode[], config: EntitiesConfig = true): ChildNode[] {\n if (!config) return dom\n\n const map = typeof config === 'object'\n ? merge(config as Record<string, string>, DEFAULT_ENTITIES)\n : DEFAULT_ENTITIES\n\n walk(dom, (node) => {\n if (node.type === 'text') {\n for (const [char, entity] of Object.entries(map)) {\n node.data = node.data.split(char).join(entity)\n }\n }\n })\n\n return dom\n}\n"],"mappings":";;;;;AAKA,MAAM,mBAA2C;CAC/C,KAAU;CACV,KAAU;CACV,QAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,UAAU;CACV,UAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACX;AAED,SAAgB,SAAS,KAAkB,SAAyB,MAAmB;AACrF,KAAI,CAAC,OAAQ,QAAO;CAEpB,MAAM,MAAM,OAAO,WAAW,WAC1BA,KAAM,QAAkC,iBAAiB,GACzD;AAEJ,MAAK,MAAM,SAAS;AAClB,MAAI,KAAK,SAAS,OAChB,MAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,IAAI,CAC9C,MAAK,OAAO,KAAK,KAAK,MAAM,KAAK,CAAC,KAAK,OAAO;GAGlD;AAEF,QAAO"}
1
+ {"version":3,"file":"entities.js","names":["merge"],"sources":["../../src/transformers/entities.ts"],"sourcesContent":["import { defu as merge } from 'defu'\nimport type { ChildNode } from 'domhandler'\nimport { parse, serialize, walk } from '../utils/ast/index.ts'\nimport type { EntitiesConfig } from '../types/index.ts'\n\nconst DEFAULT_ENTITIES: Record<string, string> = {\n '\\u200D': '&zwj;',\n '\\u200C': '&zwnj;',\n '\\u00A0': '&nbsp;',\n '\\u00AD': '&shy;',\n '\\u200B': '&#8203;',\n '\\u2007': '&#8199;',\n '\\u034F': '&#847;',\n '\\u2003': '&emsp;',\n '\\u2028': '&LineSeparator;',\n '\\u2029': '&ParagraphSeparator;',\n '\\u00B7': '&middot;',\n '\\u2013': '&ndash;',\n '\\u2014': '&mdash;',\n '\\u2018': '&lsquo;',\n '\\u2019': '&rsquo;',\n '\\u201C': '&ldquo;',\n '\\u201D': '&rdquo;',\n '\\u00AB': '&laquo;',\n '\\u00BB': '&raquo;',\n '\\u2022': '&bull;',\n '\\u2039': '&lsaquo;',\n '\\u203A': '&rsaquo;'\n}\n\n/**\n * Replace literal Unicode characters in text nodes with their HTML entity\n * equivalents (zero-width joiners, non-breaking spaces, smart quotes,\n * dashes, etc.) for better email-client rendering.\n *\n * @param html HTML string to transform.\n * @param custom Extra entries merged on top of the built-in entity map, or\n * `false` to disable the transform. Defaults to `true`\n * (built-ins only).\n * @returns The transformed HTML string.\n *\n * @example\n * import { entities } from '@maizzle/framework'\n *\n * // Defaults only\n * entities('hello world') // → 'hello&nbsp;world'\n *\n * // Add a custom mapping\n * entities('© Maizzle', { '©': '&copy;' })\n *\n * // Disable the transform\n * entities('hello world', false)\n */\nexport function entities(html: string, custom: EntitiesConfig = true): string {\n return serialize(entitiesDom(parse(html), custom))\n}\n\n/**\n * DOM-form of {@link entities} used by the internal transformer pipeline.\n * Takes a parsed DOM, returns a parsed DOM — avoids redundant\n * serialize/parse round-trips when chained with other transformers.\n */\nexport function entitiesDom(dom: ChildNode[], custom: EntitiesConfig = true): ChildNode[] {\n if (!custom) return dom\n\n const map = typeof custom === 'object'\n ? merge(custom as Record<string, string>, DEFAULT_ENTITIES)\n : DEFAULT_ENTITIES\n\n walk(dom, (node) => {\n if (node.type === 'text') {\n for (const [char, entity] of Object.entries(map)) {\n node.data = node.data.split(char).join(entity)\n }\n }\n })\n\n return dom\n}\n"],"mappings":";;;;;;AAKA,MAAM,mBAA2C;CAC/C,KAAU;CACV,KAAU;CACV,QAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,UAAU;CACV,UAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACX;;;;;;;;;;;;;;;;;;;;;;;;AAyBD,SAAgB,SAAS,MAAc,SAAyB,MAAc;CAC5E,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,OAAO,CAAC;;;;;;;AAQpD,SAAgB,YAAY,KAAkB,SAAyB,MAAmB;CACxF,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,MAAM,OAAO,WAAW,WAC1BA,OAAM,QAAkC,iBAAiB,GACzD;CAEJ,KAAK,MAAM,SAAS;EAClB,IAAI,KAAK,SAAS,QAChB,KAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,IAAI,EAC9C,KAAK,OAAO,KAAK,KAAK,MAAM,KAAK,CAAC,KAAK,OAAO;GAGlD;CAEF,OAAO"}
@@ -72,7 +72,7 @@ const defaults = {
72
72
  "url-decode": (str) => decodeURIComponent(str.replace(/\+/g, " ")),
73
73
  "url-encode": (str) => encodeURIComponent(str)
74
74
  };
75
-
76
75
  //#endregion
77
76
  export { defaults };
77
+
78
78
  //# sourceMappingURL=defaults.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.js","names":[],"sources":["../../../src/transformers/filters/defaults.ts"],"sourcesContent":["export type FilterFunction = (str: string, value: string) => string\n\nconst escapeMap: Record<string, string> = {\n '\"': '&#34;',\n '&': '&amp;',\n \"'\": '&#39;',\n '<': '&lt;',\n '>': '&gt;',\n}\n\nconst escapeRegex = /[\"&'<>]/g\n\nfunction escapeHtml(str: string): string {\n return str.replace(escapeRegex, ch => escapeMap[ch])\n}\n\nexport const defaults: Record<string, FilterFunction> = {\n append: (str, value) => str + value,\n prepend: (str, value) => value + str,\n uppercase: str => str.toUpperCase(),\n lowercase: str => str.toLowerCase(),\n capitalize: str => str.charAt(0).toUpperCase() + str.slice(1),\n ceil: str => String(Math.ceil(Number.parseFloat(str))),\n floor: str => String(Math.floor(Number.parseFloat(str))),\n round: str => String(Math.round(Number.parseFloat(str))),\n escape: str => escapeHtml(str),\n 'escape-once': str => {\n const decoded = str\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&#34;/g, '\"')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&apos;/g, \"'\")\n\n return escapeHtml(decoded)\n },\n lstrip: str => str.trimStart(),\n rstrip: str => str.trimEnd(),\n trim: str => str.trim(),\n minus: (str, value) => String(Number.parseFloat(str) - Number.parseFloat(value)),\n plus: (str, value) => String(Number.parseFloat(str) + Number.parseFloat(value)),\n multiply: (str, value) => String(Number.parseFloat(str) * Number.parseFloat(value)),\n times: (str, value) => String(Number.parseFloat(str) * Number.parseFloat(value)),\n 'divide-by': (str, value) => String(Number.parseFloat(str) / Number.parseFloat(value)),\n divide: (str, value) => String(Number.parseFloat(str) / Number.parseFloat(value)),\n modulo: (str, value) => String(Number.parseFloat(str) % Number.parseFloat(value)),\n 'newline-to-br': str => str.replace(/\\n/g, '<br>'),\n 'strip-newlines': str => str.replace(/\\n/g, ''),\n remove: (str, value) => str.split(value).join(''),\n 'remove-first': (str, value) => {\n const i = str.indexOf(value)\n return i === -1 ? str : str.slice(0, i) + str.slice(i + value.length)\n },\n replace: (str, value) => {\n const [search = '', replacement = ''] = value.split('|')\n return str.split(search).join(replacement)\n },\n 'replace-first': (str, value) => {\n const [search = '', replacement = ''] = value.split('|')\n const i = str.indexOf(search)\n return i === -1 ? str : str.slice(0, i) + replacement + str.slice(i + search.length)\n },\n size: str => String(str.length),\n slice: (str, value) => {\n const args = value.split(',').map(s => Number.parseInt(s.trim(), 10))\n return str.slice(args[0], args[1])\n },\n truncate: (str, value) => {\n const commaIndex = value.indexOf(',')\n const length = Number.parseInt(commaIndex === -1 ? value : value.slice(0, commaIndex), 10)\n const ellipsis = commaIndex === -1 ? '...' : value.slice(commaIndex + 1)\n\n if (str.length <= length) return str\n\n return str.slice(0, length) + ellipsis\n },\n 'truncate-words': (str, value) => {\n const commaIndex = value.indexOf(',')\n const count = Number.parseInt(commaIndex === -1 ? value : value.slice(0, commaIndex), 10)\n const ellipsis = commaIndex === -1 ? '...' : value.slice(commaIndex + 1)\n const words = str.split(/\\s+/).filter(Boolean)\n\n if (words.length <= count) return str\n\n return words.slice(0, count).join(' ') + ellipsis\n },\n 'url-decode': str => decodeURIComponent(str.replace(/\\+/g, ' ')),\n 'url-encode': str => encodeURIComponent(str),\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"defaults.js","names":[],"sources":["../../../src/transformers/filters/defaults.ts"],"sourcesContent":["export type FilterFunction = (str: string, value: string) => string\n\nconst escapeMap: Record<string, string> = {\n '\"': '&#34;',\n '&': '&amp;',\n \"'\": '&#39;',\n '<': '&lt;',\n '>': '&gt;',\n}\n\nconst escapeRegex = /[\"&'<>]/g\n\nfunction escapeHtml(str: string): string {\n return str.replace(escapeRegex, ch => escapeMap[ch])\n}\n\nexport const defaults: Record<string, FilterFunction> = {\n append: (str, value) => str + value,\n prepend: (str, value) => value + str,\n uppercase: str => str.toUpperCase(),\n lowercase: str => str.toLowerCase(),\n capitalize: str => str.charAt(0).toUpperCase() + str.slice(1),\n ceil: str => String(Math.ceil(Number.parseFloat(str))),\n floor: str => String(Math.floor(Number.parseFloat(str))),\n round: str => String(Math.round(Number.parseFloat(str))),\n escape: str => escapeHtml(str),\n 'escape-once': str => {\n const decoded = str\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&#34;/g, '\"')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&apos;/g, \"'\")\n\n return escapeHtml(decoded)\n },\n lstrip: str => str.trimStart(),\n rstrip: str => str.trimEnd(),\n trim: str => str.trim(),\n minus: (str, value) => String(Number.parseFloat(str) - Number.parseFloat(value)),\n plus: (str, value) => String(Number.parseFloat(str) + Number.parseFloat(value)),\n multiply: (str, value) => String(Number.parseFloat(str) * Number.parseFloat(value)),\n times: (str, value) => String(Number.parseFloat(str) * Number.parseFloat(value)),\n 'divide-by': (str, value) => String(Number.parseFloat(str) / Number.parseFloat(value)),\n divide: (str, value) => String(Number.parseFloat(str) / Number.parseFloat(value)),\n modulo: (str, value) => String(Number.parseFloat(str) % Number.parseFloat(value)),\n 'newline-to-br': str => str.replace(/\\n/g, '<br>'),\n 'strip-newlines': str => str.replace(/\\n/g, ''),\n remove: (str, value) => str.split(value).join(''),\n 'remove-first': (str, value) => {\n const i = str.indexOf(value)\n return i === -1 ? str : str.slice(0, i) + str.slice(i + value.length)\n },\n replace: (str, value) => {\n const [search = '', replacement = ''] = value.split('|')\n return str.split(search).join(replacement)\n },\n 'replace-first': (str, value) => {\n const [search = '', replacement = ''] = value.split('|')\n const i = str.indexOf(search)\n return i === -1 ? str : str.slice(0, i) + replacement + str.slice(i + search.length)\n },\n size: str => String(str.length),\n slice: (str, value) => {\n const args = value.split(',').map(s => Number.parseInt(s.trim(), 10))\n return str.slice(args[0], args[1])\n },\n truncate: (str, value) => {\n const commaIndex = value.indexOf(',')\n const length = Number.parseInt(commaIndex === -1 ? value : value.slice(0, commaIndex), 10)\n const ellipsis = commaIndex === -1 ? '...' : value.slice(commaIndex + 1)\n\n if (str.length <= length) return str\n\n return str.slice(0, length) + ellipsis\n },\n 'truncate-words': (str, value) => {\n const commaIndex = value.indexOf(',')\n const count = Number.parseInt(commaIndex === -1 ? value : value.slice(0, commaIndex), 10)\n const ellipsis = commaIndex === -1 ? '...' : value.slice(commaIndex + 1)\n const words = str.split(/\\s+/).filter(Boolean)\n\n if (words.length <= count) return str\n\n return words.slice(0, count).join(' ') + ellipsis\n },\n 'url-decode': str => decodeURIComponent(str.replace(/\\+/g, ' ')),\n 'url-encode': str => encodeURIComponent(str),\n}\n"],"mappings":"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"}
@@ -4,19 +4,40 @@ import { ChildNode } from "domhandler";
4
4
  //#region src/transformers/filters/index.d.ts
5
5
  type FiltersConfig = false | Record<string, (str: string, value: string) => string>;
6
6
  /**
7
- * Filters transformer.
7
+ * Apply transformation functions to the content of elements that have
8
+ * matching filter attributes. Multiple filters on the same element are
9
+ * executed in the order the attributes are defined.
8
10
  *
9
- * Applies transformation functions to the content of elements that
10
- * have matching filter attributes. Multiple filters on the same element
11
- * are executed in the order the attributes are defined.
11
+ * Default filters include string manipulation (`uppercase`, `lowercase`,
12
+ * `trim`, etc.), math operations (`plus`, `minus`, `multiply`, etc.),
13
+ * and more.
12
14
  *
13
- * Default filters include string manipulation (uppercase, lowercase, trim, etc.),
14
- * math operations (plus, minus, multiply, etc.), and more.
15
+ * @param html HTML string to transform.
16
+ * @param custom Custom filters to merge with the defaults. Pass `false`
17
+ * to disable all filters (including the defaults).
18
+ * @returns The transformed HTML string.
15
19
  *
16
- * Custom filters can be added via config, and will be merged with defaults.
17
- * Set config to `false` to disable all filters.
20
+ * @example
21
+ * import { filters } from '@maizzle/framework'
22
+ *
23
+ * // Defaults only
24
+ * const out = filters('<p uppercase>foo</p>') // → '<p>FOO</p>'
25
+ *
26
+ * // Add a custom filter
27
+ * filters('<p suffix=" world">hello</p>', {
28
+ * suffix: (s, v) => s + v,
29
+ * })
30
+ *
31
+ * // Disable all filters
32
+ * filters('<p uppercase>foo</p>', false)
33
+ */
34
+ declare function filters(html: string, custom?: FiltersConfig): string;
35
+ /**
36
+ * DOM-form of {@link filters} used by the internal transformer pipeline.
37
+ * Takes a parsed DOM, returns a parsed DOM — avoids redundant
38
+ * serialize/parse round-trips when chained with other transformers.
18
39
  */
19
- declare function filters(dom: ChildNode[], config?: FiltersConfig): ChildNode[];
40
+ declare function filtersDom(dom: ChildNode[], custom?: FiltersConfig): ChildNode[];
20
41
  //#endregion
21
- export { type FilterFunction, FiltersConfig, filters };
42
+ export { type FilterFunction, FiltersConfig, filters, filtersDom };
22
43
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/transformers/filters/index.ts"],"mappings":";;;;KAMY,aAAA,WAAwB,MAAA,UAAgB,GAAA,UAAa,KAAA;;AAAjE;;;;;;;;;AA4BA;;;iBAAgB,OAAA,CAAQ,GAAA,EAAK,SAAA,IAAa,MAAA,GAAQ,aAAA,GAAqB,SAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/transformers/filters/index.ts"],"mappings":";;;;KAMY,aAAA,WAAwB,MAAA,UAAgB,GAAA,UAAa,KAAA;;AAAjE;;;;;;;;;AA2CA;;;;;;;;;AASA;;;;;;;;;iBATgB,OAAA,CAAQ,IAAA,UAAc,MAAA,GAAQ,aAAA;;;;;;iBAS9B,UAAA,CAAW,GAAA,EAAK,SAAA,IAAa,MAAA,GAAQ,aAAA,GAAqB,SAAA"}
@@ -3,7 +3,6 @@ import { serialize } from "../../utils/ast/serializer.js";
3
3
  import "../../utils/ast/index.js";
4
4
  import { defaults } from "./defaults.js";
5
5
  import { Text } from "domhandler";
6
-
7
6
  //#region src/transformers/filters/index.ts
8
7
  /**
9
8
  * Process children before parents so nested filter elements work correctly.
@@ -15,23 +14,46 @@ function walkBottomUp(nodes, callback) {
15
14
  }
16
15
  }
17
16
  /**
18
- * Filters transformer.
17
+ * Apply transformation functions to the content of elements that have
18
+ * matching filter attributes. Multiple filters on the same element are
19
+ * executed in the order the attributes are defined.
20
+ *
21
+ * Default filters include string manipulation (`uppercase`, `lowercase`,
22
+ * `trim`, etc.), math operations (`plus`, `minus`, `multiply`, etc.),
23
+ * and more.
24
+ *
25
+ * @param html HTML string to transform.
26
+ * @param custom Custom filters to merge with the defaults. Pass `false`
27
+ * to disable all filters (including the defaults).
28
+ * @returns The transformed HTML string.
19
29
  *
20
- * Applies transformation functions to the content of elements that
21
- * have matching filter attributes. Multiple filters on the same element
22
- * are executed in the order the attributes are defined.
30
+ * @example
31
+ * import { filters } from '@maizzle/framework'
23
32
  *
24
- * Default filters include string manipulation (uppercase, lowercase, trim, etc.),
25
- * math operations (plus, minus, multiply, etc.), and more.
33
+ * // Defaults only
34
+ * const out = filters('<p uppercase>foo</p>') // '<p>FOO</p>'
26
35
  *
27
- * Custom filters can be added via config, and will be merged with defaults.
28
- * Set config to `false` to disable all filters.
36
+ * // Add a custom filter
37
+ * filters('<p suffix=" world">hello</p>', {
38
+ * suffix: (s, v) => s + v,
39
+ * })
40
+ *
41
+ * // Disable all filters
42
+ * filters('<p uppercase>foo</p>', false)
29
43
  */
30
- function filters(dom, config = {}) {
31
- if (config === false) return dom;
44
+ function filters(html, custom = {}) {
45
+ return serialize(filtersDom(parse(html), custom));
46
+ }
47
+ /**
48
+ * DOM-form of {@link filters} used by the internal transformer pipeline.
49
+ * Takes a parsed DOM, returns a parsed DOM — avoids redundant
50
+ * serialize/parse round-trips when chained with other transformers.
51
+ */
52
+ function filtersDom(dom, custom = {}) {
53
+ if (custom === false) return dom;
32
54
  const allFilters = {
33
55
  ...defaults,
34
- ...config
56
+ ...custom
35
57
  };
36
58
  const filterNames = new Set(Object.keys(allFilters));
37
59
  walkBottomUp(dom, (node) => {
@@ -61,7 +83,7 @@ function filters(dom, config = {}) {
61
83
  });
62
84
  return dom;
63
85
  }
64
-
65
86
  //#endregion
66
- export { filters };
87
+ export { filters, filtersDom };
88
+
67
89
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/transformers/filters/index.ts"],"sourcesContent":["import { Text } from 'domhandler'\nimport type { ChildNode, Element } from 'domhandler'\nimport { parse, serialize } from '../../utils/ast/index.ts'\nimport { defaults } from './defaults.ts'\n\nexport type { FilterFunction } from './defaults.ts'\nexport type FiltersConfig = false | Record<string, (str: string, value: string) => string>\n\n/**\n * Process children before parents so nested filter elements work correctly.\n */\nfunction walkBottomUp(nodes: ChildNode[], callback: (node: ChildNode) => void): void {\n for (const node of nodes.slice()) {\n if ('children' in node && node.children?.length) {\n walkBottomUp(node.children as ChildNode[], callback)\n }\n\n callback(node)\n }\n}\n\n/**\n * Filters transformer.\n *\n * Applies transformation functions to the content of elements that\n * have matching filter attributes. Multiple filters on the same element\n * are executed in the order the attributes are defined.\n *\n * Default filters include string manipulation (uppercase, lowercase, trim, etc.),\n * math operations (plus, minus, multiply, etc.), and more.\n *\n * Custom filters can be added via config, and will be merged with defaults.\n * Set config to `false` to disable all filters.\n */\nexport function filters(dom: ChildNode[], config: FiltersConfig = {}): ChildNode[] {\n if (config === false) return dom\n\n const allFilters = { ...defaults, ...config }\n const filterNames = new Set(Object.keys(allFilters))\n\n walkBottomUp(dom, (node) => {\n const el = node as Element\n\n if (!el.attribs) return\n\n // Collect matching filter attributes in source order\n const matched: Array<{ name: string; value: string }> = []\n\n for (const attr of Object.keys(el.attribs)) {\n if (filterNames.has(attr)) {\n matched.push({ name: attr, value: el.attribs[attr] })\n }\n }\n\n if (matched.length === 0) return\n\n // Serialize children to get innerHTML\n let content = serialize(el.children as ChildNode[])\n\n // Apply each filter in attribute order\n for (const { name, value } of matched) {\n content = allFilters[name](content, value)\n delete el.attribs[name]\n }\n\n // Replace children with the filtered content\n if (content === '') {\n el.children = []\n } else if (/<[a-z/!]/i.test(content)) {\n // Result contains HTML elements — parse back to DOM\n const newChildren = parse(content)\n\n for (const child of newChildren) {\n child.parent = el as any\n }\n\n el.children = newChildren as ChildNode[]\n } else {\n // Text-only result — create a text node directly to preserve entity strings\n const textNode = new Text(content)\n textNode.parent = el as any\n el.children = [textNode]\n }\n })\n\n return dom\n}\n"],"mappings":";;;;;;;;;;AAWA,SAAS,aAAa,OAAoB,UAA2C;AACnF,MAAK,MAAM,QAAQ,MAAM,OAAO,EAAE;AAChC,MAAI,cAAc,QAAQ,KAAK,UAAU,OACvC,cAAa,KAAK,UAAyB,SAAS;AAGtD,WAAS,KAAK;;;;;;;;;;;;;;;;AAiBlB,SAAgB,QAAQ,KAAkB,SAAwB,EAAE,EAAe;AACjF,KAAI,WAAW,MAAO,QAAO;CAE7B,MAAM,aAAa;EAAE,GAAG;EAAU,GAAG;EAAQ;CAC7C,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,WAAW,CAAC;AAEpD,cAAa,MAAM,SAAS;EAC1B,MAAM,KAAK;AAEX,MAAI,CAAC,GAAG,QAAS;EAGjB,MAAM,UAAkD,EAAE;AAE1D,OAAK,MAAM,QAAQ,OAAO,KAAK,GAAG,QAAQ,CACxC,KAAI,YAAY,IAAI,KAAK,CACvB,SAAQ,KAAK;GAAE,MAAM;GAAM,OAAO,GAAG,QAAQ;GAAO,CAAC;AAIzD,MAAI,QAAQ,WAAW,EAAG;EAG1B,IAAI,UAAU,UAAU,GAAG,SAAwB;AAGnD,OAAK,MAAM,EAAE,MAAM,WAAW,SAAS;AACrC,aAAU,WAAW,MAAM,SAAS,MAAM;AAC1C,UAAO,GAAG,QAAQ;;AAIpB,MAAI,YAAY,GACd,IAAG,WAAW,EAAE;WACP,YAAY,KAAK,QAAQ,EAAE;GAEpC,MAAM,cAAc,MAAM,QAAQ;AAElC,QAAK,MAAM,SAAS,YAClB,OAAM,SAAS;AAGjB,MAAG,WAAW;SACT;GAEL,MAAM,WAAW,IAAI,KAAK,QAAQ;AAClC,YAAS,SAAS;AAClB,MAAG,WAAW,CAAC,SAAS;;GAE1B;AAEF,QAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/transformers/filters/index.ts"],"sourcesContent":["import { Text } from 'domhandler'\nimport type { ChildNode, Element } from 'domhandler'\nimport { parse, serialize } from '../../utils/ast/index.ts'\nimport { defaults } from './defaults.ts'\n\nexport type { FilterFunction } from './defaults.ts'\nexport type FiltersConfig = false | Record<string, (str: string, value: string) => string>\n\n/**\n * Process children before parents so nested filter elements work correctly.\n */\nfunction walkBottomUp(nodes: ChildNode[], callback: (node: ChildNode) => void): void {\n for (const node of nodes.slice()) {\n if ('children' in node && node.children?.length) {\n walkBottomUp(node.children as ChildNode[], callback)\n }\n\n callback(node)\n }\n}\n\n/**\n * Apply transformation functions to the content of elements that have\n * matching filter attributes. Multiple filters on the same element are\n * executed in the order the attributes are defined.\n *\n * Default filters include string manipulation (`uppercase`, `lowercase`,\n * `trim`, etc.), math operations (`plus`, `minus`, `multiply`, etc.),\n * and more.\n *\n * @param html HTML string to transform.\n * @param custom Custom filters to merge with the defaults. Pass `false`\n * to disable all filters (including the defaults).\n * @returns The transformed HTML string.\n *\n * @example\n * import { filters } from '@maizzle/framework'\n *\n * // Defaults only\n * const out = filters('<p uppercase>foo</p>') // → '<p>FOO</p>'\n *\n * // Add a custom filter\n * filters('<p suffix=\" world\">hello</p>', {\n * suffix: (s, v) => s + v,\n * })\n *\n * // Disable all filters\n * filters('<p uppercase>foo</p>', false)\n */\nexport function filters(html: string, custom: FiltersConfig = {}): string {\n return serialize(filtersDom(parse(html), custom))\n}\n\n/**\n * DOM-form of {@link filters} used by the internal transformer pipeline.\n * Takes a parsed DOM, returns a parsed DOM — avoids redundant\n * serialize/parse round-trips when chained with other transformers.\n */\nexport function filtersDom(dom: ChildNode[], custom: FiltersConfig = {}): ChildNode[] {\n if (custom === false) return dom\n\n const allFilters = { ...defaults, ...custom }\n const filterNames = new Set(Object.keys(allFilters))\n\n walkBottomUp(dom, (node) => {\n const el = node as Element\n\n if (!el.attribs) return\n\n // Collect matching filter attributes in source order\n const matched: Array<{ name: string; value: string }> = []\n\n for (const attr of Object.keys(el.attribs)) {\n if (filterNames.has(attr)) {\n matched.push({ name: attr, value: el.attribs[attr] })\n }\n }\n\n if (matched.length === 0) return\n\n // Serialize children to get innerHTML\n let content = serialize(el.children as ChildNode[])\n\n // Apply each filter in attribute order\n for (const { name, value } of matched) {\n content = allFilters[name](content, value)\n delete el.attribs[name]\n }\n\n // Replace children with the filtered content\n if (content === '') {\n el.children = []\n } else if (/<[a-z/!]/i.test(content)) {\n // Result contains HTML elements — parse back to DOM\n const newChildren = parse(content)\n\n for (const child of newChildren) {\n child.parent = el as any\n }\n\n el.children = newChildren as ChildNode[]\n } else {\n // Text-only result — create a text node directly to preserve entity strings\n const textNode = new Text(content)\n textNode.parent = el as any\n el.children = [textNode]\n }\n })\n\n return dom\n}\n"],"mappings":";;;;;;;;;AAWA,SAAS,aAAa,OAAoB,UAA2C;CACnF,KAAK,MAAM,QAAQ,MAAM,OAAO,EAAE;EAChC,IAAI,cAAc,QAAQ,KAAK,UAAU,QACvC,aAAa,KAAK,UAAyB,SAAS;EAGtD,SAAS,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgClB,SAAgB,QAAQ,MAAc,SAAwB,EAAE,EAAU;CACxE,OAAO,UAAU,WAAW,MAAM,KAAK,EAAE,OAAO,CAAC;;;;;;;AAQnD,SAAgB,WAAW,KAAkB,SAAwB,EAAE,EAAe;CACpF,IAAI,WAAW,OAAO,OAAO;CAE7B,MAAM,aAAa;EAAE,GAAG;EAAU,GAAG;EAAQ;CAC7C,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,WAAW,CAAC;CAEpD,aAAa,MAAM,SAAS;EAC1B,MAAM,KAAK;EAEX,IAAI,CAAC,GAAG,SAAS;EAGjB,MAAM,UAAkD,EAAE;EAE1D,KAAK,MAAM,QAAQ,OAAO,KAAK,GAAG,QAAQ,EACxC,IAAI,YAAY,IAAI,KAAK,EACvB,QAAQ,KAAK;GAAE,MAAM;GAAM,OAAO,GAAG,QAAQ;GAAO,CAAC;EAIzD,IAAI,QAAQ,WAAW,GAAG;EAG1B,IAAI,UAAU,UAAU,GAAG,SAAwB;EAGnD,KAAK,MAAM,EAAE,MAAM,WAAW,SAAS;GACrC,UAAU,WAAW,MAAM,SAAS,MAAM;GAC1C,OAAO,GAAG,QAAQ;;EAIpB,IAAI,YAAY,IACd,GAAG,WAAW,EAAE;OACX,IAAI,YAAY,KAAK,QAAQ,EAAE;GAEpC,MAAM,cAAc,MAAM,QAAQ;GAElC,KAAK,MAAM,SAAS,aAClB,MAAM,SAAS;GAGjB,GAAG,WAAW;SACT;GAEL,MAAM,WAAW,IAAI,KAAK,QAAQ;GAClC,SAAS,SAAS;GAClB,GAAG,WAAW,CAAC,SAAS;;GAE1B;CAEF,OAAO"}
@@ -1,15 +1,22 @@
1
- import { MaizzleConfig } from "../types/config.js";
1
+ import { FormatOptions, FormatOptions as FormatOptions$1 } from "oxfmt";
2
2
 
3
3
  //#region src/transformers/format.d.ts
4
4
  /**
5
- * Format transformer.
5
+ * Pretty-print an HTML string with `oxfmt`. Maizzle defaults
6
+ * (`printWidth: 320`, `htmlWhitespaceSensitivity: 'ignore'`,
7
+ * `embeddedLanguageFormatting: 'off'`) are merged underneath any options
8
+ * you pass.
6
9
  *
7
- * Formats the HTML string using `oxfmt`. Accepts all oxfmt `FormatOptions`.
10
+ * @param html HTML string to format.
11
+ * @param options [oxfmt `FormatOptions`](https://github.com/oxc-project/oxfmt).
12
+ * @returns The formatted HTML string.
8
13
  *
9
- * Enable by setting `html.format: true` (or passing options).
10
- * User options are merged on top of the defaults.
14
+ * @example
15
+ * import { format } from '@maizzle/framework'
16
+ *
17
+ * const pretty = await format(html, { useTabs: true, tabWidth: 4 })
11
18
  */
12
- declare function format(html: string, config?: MaizzleConfig): Promise<string>;
19
+ declare function format(html: string, options?: FormatOptions$1): Promise<string>;
13
20
  //#endregion
14
- export { format };
21
+ export { type FormatOptions, format };
15
22
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","names":[],"sources":["../../src/transformers/format.ts"],"mappings":";;;;;AAmBA;;;;;;iBAAsB,MAAA,CAAO,IAAA,UAAc,MAAA,GAAQ,aAAA,GAAqB,OAAA"}
1
+ {"version":3,"file":"format.d.ts","names":[],"sources":["../../src/transformers/format.ts"],"mappings":";;;;AA2BA;;;;;;;;;;;;;;iBAAsB,MAAA,CAAO,IAAA,UAAc,OAAA,GAAS,eAAA,GAAqB,OAAA"}
@@ -1,6 +1,5 @@
1
- import { defu } from "defu";
1
+ import { defu as defu$1 } from "defu";
2
2
  import { format as format$1 } from "oxfmt";
3
-
4
3
  //#region src/transformers/format.ts
5
4
  const DEFAULT_OPTIONS = {
6
5
  printWidth: 320,
@@ -8,19 +7,24 @@ const DEFAULT_OPTIONS = {
8
7
  embeddedLanguageFormatting: "off"
9
8
  };
10
9
  /**
11
- * Format transformer.
10
+ * Pretty-print an HTML string with `oxfmt`. Maizzle defaults
11
+ * (`printWidth: 320`, `htmlWhitespaceSensitivity: 'ignore'`,
12
+ * `embeddedLanguageFormatting: 'off'`) are merged underneath any options
13
+ * you pass.
14
+ *
15
+ * @param html HTML string to format.
16
+ * @param options [oxfmt `FormatOptions`](https://github.com/oxc-project/oxfmt).
17
+ * @returns The formatted HTML string.
12
18
  *
13
- * Formats the HTML string using `oxfmt`. Accepts all oxfmt `FormatOptions`.
19
+ * @example
20
+ * import { format } from '@maizzle/framework'
14
21
  *
15
- * Enable by setting `html.format: true` (or passing options).
16
- * User options are merged on top of the defaults.
22
+ * const pretty = await format(html, { useTabs: true, tabWidth: 4 })
17
23
  */
18
- async function format(html, config = {}) {
19
- const option = config.html?.format;
20
- if (!option) return html;
21
- return (await format$1("input.html", html, defu(typeof option === "object" ? option : {}, DEFAULT_OPTIONS))).code;
24
+ async function format(html, options = {}) {
25
+ return (await format$1("input.html", html, defu$1(options, DEFAULT_OPTIONS))).code;
22
26
  }
23
-
24
27
  //#endregion
25
28
  export { format };
29
+
26
30
  //# sourceMappingURL=format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","names":["oxfmt","merge"],"sources":["../../src/transformers/format.ts"],"sourcesContent":["import { format as oxfmt } from 'oxfmt'\nimport { defu as merge } from 'defu'\nimport type { FormatOptions } from 'oxfmt'\nimport type { MaizzleConfig } from '../types/config.ts'\n\nconst DEFAULT_OPTIONS: FormatOptions = {\n printWidth: 320,\n htmlWhitespaceSensitivity: 'ignore',\n embeddedLanguageFormatting: 'off',\n}\n\n/**\n * Format transformer.\n *\n * Formats the HTML string using `oxfmt`. Accepts all oxfmt `FormatOptions`.\n *\n * Enable by setting `html.format: true` (or passing options).\n * User options are merged on top of the defaults.\n */\nexport async function format(html: string, config: MaizzleConfig = {}): Promise<string> {\n const option = config.html?.format\n\n if (!option) return html\n\n const userOptions: FormatOptions = typeof option === 'object' ? option : {}\n const options = merge(userOptions, DEFAULT_OPTIONS)\n\n const result = await oxfmt('input.html', html, options)\n\n return result.code\n}\n"],"mappings":";;;;AAKA,MAAM,kBAAiC;CACrC,YAAY;CACZ,2BAA2B;CAC3B,4BAA4B;CAC7B;;;;;;;;;AAUD,eAAsB,OAAO,MAAc,SAAwB,EAAE,EAAmB;CACtF,MAAM,SAAS,OAAO,MAAM;AAE5B,KAAI,CAAC,OAAQ,QAAO;AAOpB,SAFe,MAAMA,SAAM,cAAc,MAFzBC,KADmB,OAAO,WAAW,WAAW,SAAS,EAAE,EACxC,gBAAgB,CAEI,EAEzC"}
1
+ {"version":3,"file":"format.js","names":["oxfmt","merge"],"sources":["../../src/transformers/format.ts"],"sourcesContent":["import { format as oxfmt } from 'oxfmt'\nimport { defu as merge } from 'defu'\nimport type { FormatOptions } from 'oxfmt'\n\nexport type { FormatOptions } from 'oxfmt'\n\nconst DEFAULT_OPTIONS: FormatOptions = {\n printWidth: 320,\n htmlWhitespaceSensitivity: 'ignore',\n embeddedLanguageFormatting: 'off',\n}\n\n/**\n * Pretty-print an HTML string with `oxfmt`. Maizzle defaults\n * (`printWidth: 320`, `htmlWhitespaceSensitivity: 'ignore'`,\n * `embeddedLanguageFormatting: 'off'`) are merged underneath any options\n * you pass.\n *\n * @param html HTML string to format.\n * @param options [oxfmt `FormatOptions`](https://github.com/oxc-project/oxfmt).\n * @returns The formatted HTML string.\n *\n * @example\n * import { format } from '@maizzle/framework'\n *\n * const pretty = await format(html, { useTabs: true, tabWidth: 4 })\n */\nexport async function format(html: string, options: FormatOptions = {}): Promise<string> {\n const merged = merge(options, DEFAULT_OPTIONS)\n const result = await oxfmt('input.html', html, merged)\n return result.code\n}\n"],"mappings":";;;AAMA,MAAM,kBAAiC;CACrC,YAAY;CACZ,2BAA2B;CAC3B,4BAA4B;CAC7B;;;;;;;;;;;;;;;;AAiBD,eAAsB,OAAO,MAAc,UAAyB,EAAE,EAAmB;CAGvF,QAAO,MADcA,SAAM,cAAc,MAD1BC,OAAM,SAAS,gBACuB,CAAC,EACxC"}