@maizzle/framework 6.1.2 → 6.1.4
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.
- package/dist/components/Outlook.vue +8 -2
- package/dist/render/buildTemplate.d.ts.map +1 -1
- package/dist/render/buildTemplate.js +1 -1
- package/dist/render/buildTemplate.js.map +1 -1
- package/dist/render/createRenderer.js +16 -0
- package/dist/render/createRenderer.js.map +1 -1
- package/dist/render/index.js +1 -1
- package/dist/render/index.js.map +1 -1
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +55 -6
- package/dist/serve.js.map +1 -1
- package/dist/server/ui/.vite/deps/_metadata.json +14 -14
- package/dist/server/ui/.vite/deps/reka-ui.js +273 -75
- package/dist/server/ui/.vite/deps/reka-ui.js.map +1 -1
- package/dist/transformers/index.d.ts +1 -0
- package/dist/transformers/index.d.ts.map +1 -1
- package/dist/transformers/index.js +8 -2
- package/dist/transformers/index.js.map +1 -1
- package/dist/transformers/msoConditionals.d.ts +5 -0
- package/dist/transformers/msoConditionals.d.ts.map +1 -0
- package/dist/transformers/msoConditionals.js +25 -0
- package/dist/transformers/msoConditionals.js.map +1 -0
- package/dist/transformers/shorthandCss.d.ts +0 -22
- package/dist/transformers/shorthandCss.d.ts.map +1 -1
- package/dist/transformers/shorthandCss.js +36 -2
- package/dist/transformers/shorthandCss.js.map +1 -1
- package/dist/utils/watchPaths.d.ts +28 -1
- package/dist/utils/watchPaths.d.ts.map +1 -1
- package/dist/utils/watchPaths.js +38 -3
- package/dist/utils/watchPaths.js.map +1 -1
- package/package.json +3 -3
|
@@ -28,6 +28,7 @@ import { TailwindBlock } from "../composables/renderContext.js";
|
|
|
28
28
|
* 12. Purge CSS (serializes/parses internally around email-comb)
|
|
29
29
|
* 13. Entities
|
|
30
30
|
* + Vue-generated comments stripped here (on serialized string)
|
|
31
|
+
* 13.5 Outlook placeholders → MSO conditional comments
|
|
31
32
|
* 14. Replace strings
|
|
32
33
|
* 15. Prettify
|
|
33
34
|
* 16. Minify
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/transformers/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/transformers/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2DsB,gBACpB,cACA,QAAQ,eACR,mBACA,kBACA,iBAAiB,iBACjB,yBACC"}
|
|
@@ -19,6 +19,7 @@ import { baseDom } from "./base.js";
|
|
|
19
19
|
import { entitiesDom } from "./entities.js";
|
|
20
20
|
import { urlQueryDom } from "./urlQuery.js";
|
|
21
21
|
import { purgeCssDom } from "./purgeCss.js";
|
|
22
|
+
import { msoConditionals } from "./msoConditionals.js";
|
|
22
23
|
import { replaceStrings } from "./replaceStrings.js";
|
|
23
24
|
import { format } from "./format.js";
|
|
24
25
|
import { minifyCodeInline } from "./minifyCodeInline.js";
|
|
@@ -51,15 +52,19 @@ import { minify } from "./minify.js";
|
|
|
51
52
|
* 12. Purge CSS (serializes/parses internally around email-comb)
|
|
52
53
|
* 13. Entities
|
|
53
54
|
* + Vue-generated comments stripped here (on serialized string)
|
|
55
|
+
* 13.5 Outlook placeholders → MSO conditional comments
|
|
54
56
|
* 14. Replace strings
|
|
55
57
|
* 15. Prettify
|
|
56
58
|
* 16. Minify
|
|
57
59
|
*/
|
|
58
60
|
async function runTransformers(html, config, filePath, doctype, tailwindBlocks, sourceFiles) {
|
|
61
|
+
/**
|
|
62
|
+
* Whole-pipeline opt-out. `<Outlook>` placeholders still have to
|
|
63
|
+
* become real conditional comments, so that one step always runs.
|
|
64
|
+
*/
|
|
65
|
+
if (config.useTransformers === false) return msoConditionals(html);
|
|
59
66
|
/**
|
|
60
67
|
* Per-transformer skip map — only honored when useTransformers is an object.
|
|
61
|
-
* Whole-pipeline opt-out (`useTransformers === false`) is handled upstream
|
|
62
|
-
* in build.ts / render so we never reach this function in that case.
|
|
63
68
|
*
|
|
64
69
|
* A toggle set to `true` *force-enables* its transformer for this run
|
|
65
70
|
* by layering on the matching config slice (e.g. `prettify: true`
|
|
@@ -139,6 +144,7 @@ async function runTransformers(html, config, filePath, doctype, tailwindBlocks,
|
|
|
139
144
|
if (enabled("entities")) dom = entitiesDom(dom, effective.html?.decodeEntities);
|
|
140
145
|
const isXhtml = doctype ? /xhtml/i.test(doctype) : false;
|
|
141
146
|
let result = serialize(dom, { selfClosingTags: isXhtml });
|
|
147
|
+
result = msoConditionals(result);
|
|
142
148
|
if (enabled("replaceStrings")) result = replaceStrings(result, effective);
|
|
143
149
|
const minifyWillRun = enabled("minify") && !!effective.html?.minify;
|
|
144
150
|
if (enabled("prettify") && !minifyWillRun && effective.html?.format) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/transformers/index.ts"],"sourcesContent":["import { parse, serialize } from '../utils/ast/index.ts'\nimport { inlineLinkDom } from './inlineLink.ts'\nimport { tailwindComponent } from './tailwindComponent.ts'\nimport { tailwindcss } from './tailwindcss.ts'\nimport { safeSelectorsDom } from './safeSelectors.ts'\nimport { attributeToStyleDom } from './attributeToStyle.ts'\nimport { inlineCssDom } from './inlineCss.ts'\nimport { msoPlaceholders } from './msoPlaceholders.ts'\nimport { columnWidth } from './columnWidth.ts'\nimport { imgWidthDom } from './imgWidth.ts'\nimport { removeAttributesDom } from './removeAttributes.ts'\nimport { shorthandCssDom } from './shorthandCss.ts'\nimport { sixHexDom } from './sixHex.ts'\nimport { addAttributesDom } from './addAttributes.ts'\nimport { filtersDom } from './filters/index.ts'\nimport { baseDom } from './base.ts'\nimport { entitiesDom } from './entities.ts'\nimport { urlQueryDom } from './urlQuery.ts'\nimport { purgeCssDom } from './purgeCss.ts'\nimport { replaceStrings } from './replaceStrings.ts'\nimport { format } from './format.ts'\nimport { minifyCodeInline } from './minifyCodeInline.ts'\nimport { minify } from './minify.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\nimport type { TailwindBlock } from '../composables/renderContext.ts'\n\n/**\n * Run all Maizzle transformers on the rendered HTML.\n *\n * The HTML is parsed into a DOM once at the start and passed through all\n * DOM-based transformers as a shared `ChildNode[]`. After all DOM transformers\n * complete, the DOM is serialized back to a string exactly once.\n *\n * String-only transformers (those that rely on external tools that require a\n * raw HTML string) then run on the serialized output.\n *\n * Transformers run in a specific order:\n * 0. Inline link stylesheets — replace `<link rel=\"stylesheet\">` with `<style>` tags\n * 1. Tailwind CSS — compile CSS, lower syntax, optimize (cleanup + merge media queries)\n * 2. Safe class names\n * 3. Attribute to style\n * 4. CSS inliner\n * 5. Remove attributes\n * 6. Shorthand CSS\n * 7. Six-digit HEX\n * 8. Add attributes\n * 9. Filters\n * 10. Base URL\n * 11. URL query\n * 11.5 Entities in comment nodes (before purge — protects MSO conditionals)\n * 12. Purge CSS (serializes/parses internally around email-comb)\n * 13. Entities\n * + Vue-generated comments stripped here (on serialized string)\n * 14. Replace strings\n * 15. Prettify\n * 16. Minify\n */\nexport async function runTransformers(\n html: string,\n config: MaizzleConfig,\n filePath?: string,\n doctype?: string,\n tailwindBlocks?: TailwindBlock[],\n sourceFiles?: string[],\n): Promise<string> {\n /**\n * Per-transformer skip map — only honored when useTransformers is an object.\n * Whole-pipeline opt-out (`useTransformers === false`) is handled upstream\n * in build.ts / render so we never reach this function in that case.\n *\n * A toggle set to `true` *force-enables* its transformer for this run\n * by layering on the matching config slice (e.g. `prettify: true`\n * sets `html.format = true`). This only applies to transformers\n * whose enable flag is a plain boolean — data-driven ones\n * (filters, baseURL, urlQuery, etc.) need actual config\n * values, so a bare `true` for those is a no-op.\n */\n const toggles = typeof config.useTransformers === 'object' ? config.useTransformers : null\n const enabled = (key: keyof NonNullable<typeof toggles>) => toggles?.[key] !== false\n\n let effective = config\n if (toggles) {\n const cssOver: Record<string, unknown> = {}\n const htmlOver: Record<string, unknown> = {}\n if (toggles.inlineCss === true) cssOver.inline = true\n if (toggles.purgeCss === true) cssOver.purge = true\n if (toggles.safeSelectors === true) cssOver.safe = true\n if (toggles.shorthandCss === true) cssOver.shorthand = true\n if (toggles.sixHex === true) cssOver.sixHex = true\n if (toggles.prettify === true) htmlOver.format = true\n if (toggles.minify === true) htmlOver.minify = true\n if (toggles.entities === true) htmlOver.decodeEntities = true\n\n if (Object.keys(cssOver).length || Object.keys(htmlOver).length) {\n effective = {\n ...config,\n css: { ...config.css, ...cssOver },\n html: { ...config.html, ...htmlOver },\n }\n }\n }\n\n // Parse once — all DOM transformers share this array\n let dom = parse(html)\n\n // 0. Inline <link> stylesheets\n dom = await inlineLinkDom(dom, filePath)\n\n // 0.5. <Tailwind> component — compile per-block scoped CSS, inject into <head>\n if (tailwindBlocks?.length) {\n dom = await tailwindComponent(dom, tailwindBlocks, effective, filePath, sourceFiles)\n }\n\n // 1. Tailwind CSS — always runs first\n dom = await tailwindcss(dom, effective, filePath, sourceFiles)\n\n // 2. Safe class names\n if (enabled('safeSelectors')) dom = safeSelectorsDom(dom, effective.css)\n\n // 3. Attribute to style\n if (enabled('attributeToStyle') && typeof effective.css?.inline === 'object' && effective.css.inline.attributeToStyle) {\n dom = attributeToStyleDom(dom, effective.css.inline.attributeToStyle)\n }\n\n // 4. CSS inliner (serializes/parses internally around juice)\n if (enabled('inlineCss') && effective.css?.inline) {\n const inlineOptions = typeof effective.css.inline === 'object' ? effective.css.inline : {}\n dom = inlineCssDom(dom, inlineOptions)\n }\n\n // 4.5. Resolve MSO placeholders (table width + td style) from inlined CSS\n dom = msoPlaceholders(dom)\n\n // 4.6. Resolve Column min-width placeholders from nearest sized ancestor\n dom = columnWidth(dom)\n\n // 4.7. Backfill width on <Img> images that inherit their parent's width\n dom = imgWidthDom(dom)\n\n // 5. Remove attributes\n if (enabled('removeAttributes')) {\n const removeRules = effective.html?.attributes?.remove\n dom = removeAttributesDom(dom, Array.isArray(removeRules) ? removeRules : [])\n }\n\n // 6. Shorthand CSS\n if (enabled('shorthandCss') && effective.css?.shorthand) {\n const shorthandOptions = typeof effective.css.shorthand === 'object' ? effective.css.shorthand : {}\n dom = shorthandCssDom(dom, shorthandOptions)\n }\n\n // 7. Six-digit HEX\n if (enabled('sixHex') && effective.css?.sixHex !== false) dom = sixHexDom(dom)\n\n // 8. Add attributes\n if (enabled('addAttributes')) dom = addAttributesDom(dom, effective.html?.attributes)\n\n // 9. Filters\n if (enabled('filters')) dom = filtersDom(dom, effective.filters)\n\n // 10. Base URL (serializes/parses internally for VML/MSO regex passes)\n if (enabled('baseURL') && effective.url?.base) dom = baseDom(dom, effective.url.base)\n\n // 11. URL query\n if (enabled('urlQuery') && effective.url?.query && Object.keys(effective.url.query).length > 0) {\n const { _options: queryOptions, ...queryParams } = effective.url.query as Record<string, unknown>\n dom = urlQueryDom(dom, queryParams, (queryOptions ?? {}) as import('../types/config.ts').UrlQueryOptions)\n }\n\n /**\n * 11.5. Encode entities in comment nodes before purge/minify. Raw\n * invisible chars (e.g. U+00A0 from Vue decoding at compile\n * time) inside MSO conditionals make email-comb remove the whole\n * \"whitespace-only\" conditional and html-crush collapse the chars.\n * Text nodes are skipped here — purge's internal parse round-trip\n * would decode them again — comment data survives un-decoded.\n */\n if (enabled('entities')) dom = entitiesDom(dom, effective.html?.decodeEntities, { text: false })\n\n // 12. Remove unused CSS (serializes/parses internally around email-comb)\n if (enabled('purgeCss') && effective.css?.purge) {\n const purgeOptions = typeof effective.css.purge === 'object' ? effective.css.purge : {}\n dom = purgeCssDom(dom, purgeOptions)\n }\n\n // 13. Entities\n if (enabled('entities')) dom = entitiesDom(dom, effective.html?.decodeEntities)\n\n // Serialize once — remaining transformers operate on the HTML string\n const isXhtml = doctype ? /xhtml/i.test(doctype) : false\n let result = serialize(dom, { selfClosingTags: isXhtml })\n\n // 14. Replace strings\n if (enabled('replaceStrings')) result = replaceStrings(result, effective)\n\n // 15. Format — skipped when `minify` is enabled\n const minifyWillRun = enabled('minify') && !!effective.html?.minify\n if (enabled('prettify') && !minifyWillRun && effective.html?.format) {\n const formatOptions = typeof effective.html.format === 'object' ? effective.html.format : {}\n result = await format(result, formatOptions)\n }\n\n // 16. Minify\n if (enabled('minify') && effective.html?.minify) {\n const minifyOptions = typeof effective.html.minify === 'object' ? effective.html.minify : {}\n result = minify(result, minifyOptions)\n }\n\n /**\n * Strip self-closing slashes for HTML5 doctypes, but preserve content\n * inside MSO conditional comments (XML-ish, case/syntax sensitive).\n * MUST run BEFORE minifyCodeInline: at this point, CodeInline's\n * shiki output is still marker-encoded (§MZLT§/§MZGT§), so any\n * ` />` in the highlighted source code (e.g. a Vue self-close\n * tag) hasn't materialized yet and can't be mistakenly\n * stripped from inside a `<code>` element.\n */\n if (!isXhtml) {\n result = result.replace(\n /<!--\\[if [^\\]]*\\]>[\\s\\S]*?<!\\[endif\\]-->|( \\/>)/g,\n (match, selfClose) => selfClose ? '>' : match,\n )\n }\n\n /**\n * 16.5. Strip whitespace inside `data-minify-inline` markers (CodeInline's\n * Shiki output, etc.). Runs after format/minify so it cleans up the\n * pretty-printer's indentation between sibling tags.\n */\n result = minifyCodeInline(result)\n\n return result\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/transformers/index.ts"],"sourcesContent":["import { parse, serialize } from '../utils/ast/index.ts'\nimport { inlineLinkDom } from './inlineLink.ts'\nimport { tailwindComponent } from './tailwindComponent.ts'\nimport { tailwindcss } from './tailwindcss.ts'\nimport { safeSelectorsDom } from './safeSelectors.ts'\nimport { attributeToStyleDom } from './attributeToStyle.ts'\nimport { inlineCssDom } from './inlineCss.ts'\nimport { msoPlaceholders } from './msoPlaceholders.ts'\nimport { columnWidth } from './columnWidth.ts'\nimport { imgWidthDom } from './imgWidth.ts'\nimport { removeAttributesDom } from './removeAttributes.ts'\nimport { shorthandCssDom } from './shorthandCss.ts'\nimport { sixHexDom } from './sixHex.ts'\nimport { addAttributesDom } from './addAttributes.ts'\nimport { filtersDom } from './filters/index.ts'\nimport { baseDom } from './base.ts'\nimport { entitiesDom } from './entities.ts'\nimport { urlQueryDom } from './urlQuery.ts'\nimport { purgeCssDom } from './purgeCss.ts'\nimport { msoConditionals } from './msoConditionals.ts'\nimport { replaceStrings } from './replaceStrings.ts'\nimport { format } from './format.ts'\nimport { minifyCodeInline } from './minifyCodeInline.ts'\nimport { minify } from './minify.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\nimport type { TailwindBlock } from '../composables/renderContext.ts'\n\n/**\n * Run all Maizzle transformers on the rendered HTML.\n *\n * The HTML is parsed into a DOM once at the start and passed through all\n * DOM-based transformers as a shared `ChildNode[]`. After all DOM transformers\n * complete, the DOM is serialized back to a string exactly once.\n *\n * String-only transformers (those that rely on external tools that require a\n * raw HTML string) then run on the serialized output.\n *\n * Transformers run in a specific order:\n * 0. Inline link stylesheets — replace `<link rel=\"stylesheet\">` with `<style>` tags\n * 1. Tailwind CSS — compile CSS, lower syntax, optimize (cleanup + merge media queries)\n * 2. Safe class names\n * 3. Attribute to style\n * 4. CSS inliner\n * 5. Remove attributes\n * 6. Shorthand CSS\n * 7. Six-digit HEX\n * 8. Add attributes\n * 9. Filters\n * 10. Base URL\n * 11. URL query\n * 11.5 Entities in comment nodes (before purge — protects MSO conditionals)\n * 12. Purge CSS (serializes/parses internally around email-comb)\n * 13. Entities\n * + Vue-generated comments stripped here (on serialized string)\n * 13.5 Outlook placeholders → MSO conditional comments\n * 14. Replace strings\n * 15. Prettify\n * 16. Minify\n */\nexport async function runTransformers(\n html: string,\n config: MaizzleConfig,\n filePath?: string,\n doctype?: string,\n tailwindBlocks?: TailwindBlock[],\n sourceFiles?: string[],\n): Promise<string> {\n /**\n * Whole-pipeline opt-out. `<Outlook>` placeholders still have to\n * become real conditional comments, so that one step always runs.\n */\n if (config.useTransformers === false) return msoConditionals(html)\n\n /**\n * Per-transformer skip map — only honored when useTransformers is an object.\n *\n * A toggle set to `true` *force-enables* its transformer for this run\n * by layering on the matching config slice (e.g. `prettify: true`\n * sets `html.format = true`). This only applies to transformers\n * whose enable flag is a plain boolean — data-driven ones\n * (filters, baseURL, urlQuery, etc.) need actual config\n * values, so a bare `true` for those is a no-op.\n */\n const toggles = typeof config.useTransformers === 'object' ? config.useTransformers : null\n const enabled = (key: keyof NonNullable<typeof toggles>) => toggles?.[key] !== false\n\n let effective = config\n if (toggles) {\n const cssOver: Record<string, unknown> = {}\n const htmlOver: Record<string, unknown> = {}\n if (toggles.inlineCss === true) cssOver.inline = true\n if (toggles.purgeCss === true) cssOver.purge = true\n if (toggles.safeSelectors === true) cssOver.safe = true\n if (toggles.shorthandCss === true) cssOver.shorthand = true\n if (toggles.sixHex === true) cssOver.sixHex = true\n if (toggles.prettify === true) htmlOver.format = true\n if (toggles.minify === true) htmlOver.minify = true\n if (toggles.entities === true) htmlOver.decodeEntities = true\n\n if (Object.keys(cssOver).length || Object.keys(htmlOver).length) {\n effective = {\n ...config,\n css: { ...config.css, ...cssOver },\n html: { ...config.html, ...htmlOver },\n }\n }\n }\n\n // Parse once — all DOM transformers share this array\n let dom = parse(html)\n\n // 0. Inline <link> stylesheets\n dom = await inlineLinkDom(dom, filePath)\n\n // 0.5. <Tailwind> component — compile per-block scoped CSS, inject into <head>\n if (tailwindBlocks?.length) {\n dom = await tailwindComponent(dom, tailwindBlocks, effective, filePath, sourceFiles)\n }\n\n // 1. Tailwind CSS — always runs first\n dom = await tailwindcss(dom, effective, filePath, sourceFiles)\n\n // 2. Safe class names\n if (enabled('safeSelectors')) dom = safeSelectorsDom(dom, effective.css)\n\n // 3. Attribute to style\n if (enabled('attributeToStyle') && typeof effective.css?.inline === 'object' && effective.css.inline.attributeToStyle) {\n dom = attributeToStyleDom(dom, effective.css.inline.attributeToStyle)\n }\n\n // 4. CSS inliner (serializes/parses internally around juice)\n if (enabled('inlineCss') && effective.css?.inline) {\n const inlineOptions = typeof effective.css.inline === 'object' ? effective.css.inline : {}\n dom = inlineCssDom(dom, inlineOptions)\n }\n\n // 4.5. Resolve MSO placeholders (table width + td style) from inlined CSS\n dom = msoPlaceholders(dom)\n\n // 4.6. Resolve Column min-width placeholders from nearest sized ancestor\n dom = columnWidth(dom)\n\n // 4.7. Backfill width on <Img> images that inherit their parent's width\n dom = imgWidthDom(dom)\n\n // 5. Remove attributes\n if (enabled('removeAttributes')) {\n const removeRules = effective.html?.attributes?.remove\n dom = removeAttributesDom(dom, Array.isArray(removeRules) ? removeRules : [])\n }\n\n // 6. Shorthand CSS\n if (enabled('shorthandCss') && effective.css?.shorthand) {\n const shorthandOptions = typeof effective.css.shorthand === 'object' ? effective.css.shorthand : {}\n dom = shorthandCssDom(dom, shorthandOptions)\n }\n\n // 7. Six-digit HEX\n if (enabled('sixHex') && effective.css?.sixHex !== false) dom = sixHexDom(dom)\n\n // 8. Add attributes\n if (enabled('addAttributes')) dom = addAttributesDom(dom, effective.html?.attributes)\n\n // 9. Filters\n if (enabled('filters')) dom = filtersDom(dom, effective.filters)\n\n // 10. Base URL (serializes/parses internally for VML/MSO regex passes)\n if (enabled('baseURL') && effective.url?.base) dom = baseDom(dom, effective.url.base)\n\n // 11. URL query\n if (enabled('urlQuery') && effective.url?.query && Object.keys(effective.url.query).length > 0) {\n const { _options: queryOptions, ...queryParams } = effective.url.query as Record<string, unknown>\n dom = urlQueryDom(dom, queryParams, (queryOptions ?? {}) as import('../types/config.ts').UrlQueryOptions)\n }\n\n /**\n * 11.5. Encode entities in comment nodes before purge/minify. Raw\n * invisible chars (e.g. U+00A0 from Vue decoding at compile\n * time) inside MSO conditionals make email-comb remove the whole\n * \"whitespace-only\" conditional and html-crush collapse the chars.\n * Text nodes are skipped here — purge's internal parse round-trip\n * would decode them again — comment data survives un-decoded.\n */\n if (enabled('entities')) dom = entitiesDom(dom, effective.html?.decodeEntities, { text: false })\n\n // 12. Remove unused CSS (serializes/parses internally around email-comb)\n if (enabled('purgeCss') && effective.css?.purge) {\n const purgeOptions = typeof effective.css.purge === 'object' ? effective.css.purge : {}\n dom = purgeCssDom(dom, purgeOptions)\n }\n\n // 13. Entities\n if (enabled('entities')) dom = entitiesDom(dom, effective.html?.decodeEntities)\n\n // Serialize once — remaining transformers operate on the HTML string\n const isXhtml = doctype ? /xhtml/i.test(doctype) : false\n let result = serialize(dom, { selfClosingTags: isXhtml })\n\n // 13.5. Collapse <Outlook> placeholders into MSO conditional comments\n result = msoConditionals(result)\n\n // 14. Replace strings\n if (enabled('replaceStrings')) result = replaceStrings(result, effective)\n\n // 15. Format — skipped when `minify` is enabled\n const minifyWillRun = enabled('minify') && !!effective.html?.minify\n if (enabled('prettify') && !minifyWillRun && effective.html?.format) {\n const formatOptions = typeof effective.html.format === 'object' ? effective.html.format : {}\n result = await format(result, formatOptions)\n }\n\n // 16. Minify\n if (enabled('minify') && effective.html?.minify) {\n const minifyOptions = typeof effective.html.minify === 'object' ? effective.html.minify : {}\n result = minify(result, minifyOptions)\n }\n\n /**\n * Strip self-closing slashes for HTML5 doctypes, but preserve content\n * inside MSO conditional comments (XML-ish, case/syntax sensitive).\n * MUST run BEFORE minifyCodeInline: at this point, CodeInline's\n * shiki output is still marker-encoded (§MZLT§/§MZGT§), so any\n * ` />` in the highlighted source code (e.g. a Vue self-close\n * tag) hasn't materialized yet and can't be mistakenly\n * stripped from inside a `<code>` element.\n */\n if (!isXhtml) {\n result = result.replace(\n /<!--\\[if [^\\]]*\\]>[\\s\\S]*?<!\\[endif\\]-->|( \\/>)/g,\n (match, selfClose) => selfClose ? '>' : match,\n )\n }\n\n /**\n * 16.5. Strip whitespace inside `data-minify-inline` markers (CodeInline's\n * Shiki output, etc.). Runs after format/minify so it cleans up the\n * pretty-printer's indentation between sibling tags.\n */\n result = minifyCodeInline(result)\n\n return result\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msoConditionals.d.ts","names":[],"sources":["../../src/transformers/msoConditionals.ts"],"mappings":";iBAkBgB,gBAAgB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/transformers/msoConditionals.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the placeholder comments emitted by `<Outlook>` into real MSO
|
|
4
|
+
* conditional comments.
|
|
5
|
+
*
|
|
6
|
+
* `<Outlook>` can't render `<!--[if mso]>…<![endif]-->` directly: the
|
|
7
|
+
* whole block would parse as a single comment node and every DOM
|
|
8
|
+
* transformer (inliner, safe selectors, purge, attributes…) would
|
|
9
|
+
* be blind to the markup inside it. Instead it wraps its slot in
|
|
10
|
+
* two self-contained conditional comments carrying a marker, so
|
|
11
|
+
* the content stays real DOM through the pipeline. Shaping the
|
|
12
|
+
* markers as conditionals means juice and email-comb leave
|
|
13
|
+
* them alone. This runs on the serialized string, after
|
|
14
|
+
* every DOM transformer, and turns the pair back into a
|
|
15
|
+
* single hidden conditional block.
|
|
16
|
+
*/
|
|
17
|
+
const RE_OPEN = /<!--\[if ([^\]]+)\]>__MAIZZLE_MSO_OPEN__<!\[endif\]-->/g;
|
|
18
|
+
const RE_CLOSE = /<!--\[if mso\]>__MAIZZLE_MSO_CLOSE__<!\[endif\]-->/g;
|
|
19
|
+
function msoConditionals(html) {
|
|
20
|
+
return html.replace(RE_OPEN, "<!--[if $1]>").replace(RE_CLOSE, "<![endif]-->");
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { msoConditionals };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=msoConditionals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msoConditionals.js","names":[],"sources":["../../src/transformers/msoConditionals.ts"],"sourcesContent":["/**\n * Resolve the placeholder comments emitted by `<Outlook>` into real MSO\n * conditional comments.\n *\n * `<Outlook>` can't render `<!--[if mso]>…<![endif]-->` directly: the\n * whole block would parse as a single comment node and every DOM\n * transformer (inliner, safe selectors, purge, attributes…) would\n * be blind to the markup inside it. Instead it wraps its slot in\n * two self-contained conditional comments carrying a marker, so\n * the content stays real DOM through the pipeline. Shaping the\n * markers as conditionals means juice and email-comb leave\n * them alone. This runs on the serialized string, after\n * every DOM transformer, and turns the pair back into a\n * single hidden conditional block.\n */\nconst RE_OPEN = /<!--\\[if ([^\\]]+)\\]>__MAIZZLE_MSO_OPEN__<!\\[endif\\]-->/g\nconst RE_CLOSE = /<!--\\[if mso\\]>__MAIZZLE_MSO_CLOSE__<!\\[endif\\]-->/g\n\nexport function msoConditionals(html: string): string {\n return html\n .replace(RE_OPEN, '<!--[if $1]>')\n .replace(RE_CLOSE, '<![endif]-->')\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,MAAM,UAAU;AAChB,MAAM,WAAW;AAEjB,SAAgB,gBAAgB,MAAsB;CACpD,OAAO,KACJ,QAAQ,SAAS,cAAc,CAAC,CAChC,QAAQ,UAAU,cAAc;AACrC"}
|
|
@@ -12,28 +12,6 @@ interface ShorthandCssOptions {
|
|
|
12
12
|
*/
|
|
13
13
|
tags?: string[];
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Rewrite longhand CSS inside inline `style` attributes with shorthand
|
|
17
|
-
* syntax. Works with margin, padding, and border when all sides are
|
|
18
|
-
* specified.
|
|
19
|
-
*
|
|
20
|
-
* For example:
|
|
21
|
-
* `margin-left: 2px; margin-right: 2px; margin-top: 4px; margin-bottom: 4px`
|
|
22
|
-
* becomes:
|
|
23
|
-
* `margin: 4px 2px`
|
|
24
|
-
*
|
|
25
|
-
* @param html HTML string to transform.
|
|
26
|
-
* @param options Optional Maizzle options (`tags`).
|
|
27
|
-
* @returns The transformed HTML string.
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* import { shorthandCss } from '@maizzle/framework'
|
|
31
|
-
*
|
|
32
|
-
* const out = shorthandCss(
|
|
33
|
-
* '<p style="margin-top: 4px; margin-right: 2px; margin-bottom: 4px; margin-left: 2px;">x</p>',
|
|
34
|
-
* { tags: ['p'] },
|
|
35
|
-
* )
|
|
36
|
-
*/
|
|
37
15
|
declare function shorthandCss(html: string, options?: ShorthandCssOptions): string;
|
|
38
16
|
/**
|
|
39
17
|
* DOM-form of {@link shorthandCss} used by the internal transformer
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shorthandCss.d.ts","names":[],"sources":["../../src/transformers/shorthandCss.ts"],"mappings":";;;;;UASiB;;;;;;;EAOf
|
|
1
|
+
{"version":3,"file":"shorthandCss.d.ts","names":[],"sources":["../../src/transformers/shorthandCss.ts"],"mappings":";;;;;UASiB;;;;;;;EAOf;;iBAwEc,aAAa,cAAc,UAAS;;;;;;iBASpC,gBAAgB,KAAK,aAAa,UAAS,sBAA2B"}
|
|
@@ -2,7 +2,7 @@ import { parse } from "../utils/ast/parser.js";
|
|
|
2
2
|
import { walk } from "../utils/ast/walker.js";
|
|
3
3
|
import { serialize } from "../utils/ast/serializer.js";
|
|
4
4
|
import "../utils/ast/index.js";
|
|
5
|
-
import postcss from "postcss";
|
|
5
|
+
import postcss, { list } from "postcss";
|
|
6
6
|
import safeParser from "postcss-safe-parser";
|
|
7
7
|
import mergeLonghand from "postcss-merge-longhand";
|
|
8
8
|
//#region src/transformers/shorthandCss.ts
|
|
@@ -28,6 +28,40 @@ import mergeLonghand from "postcss-merge-longhand";
|
|
|
28
28
|
* { tags: ['p'] },
|
|
29
29
|
* )
|
|
30
30
|
*/
|
|
31
|
+
/**
|
|
32
|
+
* Fold `border-width`, `border-style` and `border-color` into `border`.
|
|
33
|
+
*
|
|
34
|
+
* postcss-merge-longhand stopped doing this in 8.0.3 because the
|
|
35
|
+
* shorthand also resets `border-image`. Email clients don't support
|
|
36
|
+
* `border-image`, so the merge is safe for inline styles.
|
|
37
|
+
*/
|
|
38
|
+
const mergeBorder = {
|
|
39
|
+
postcssPlugin: "maizzle-merge-border",
|
|
40
|
+
OnceExit(root) {
|
|
41
|
+
root.walkRules((rule) => {
|
|
42
|
+
const decls = [];
|
|
43
|
+
for (const prop of [
|
|
44
|
+
"border-width",
|
|
45
|
+
"border-style",
|
|
46
|
+
"border-color"
|
|
47
|
+
]) {
|
|
48
|
+
const found = rule.nodes.filter((node) => node.type === "decl" && node.prop.toLowerCase() === prop);
|
|
49
|
+
if (found.length !== 1) return;
|
|
50
|
+
decls.push(found[0]);
|
|
51
|
+
}
|
|
52
|
+
const [width, style, color] = decls;
|
|
53
|
+
const { important } = width;
|
|
54
|
+
for (const decl of decls) if (decl.important !== important || list.space(decl.value).length !== 1 || /var\s*\(/i.test(decl.value)) return;
|
|
55
|
+
const last = decls.reduce((a, b) => rule.index(b) > rule.index(a) ? b : a);
|
|
56
|
+
last.replaceWith({
|
|
57
|
+
prop: "border",
|
|
58
|
+
value: `${width.value} ${style.value} ${color.value}`,
|
|
59
|
+
important
|
|
60
|
+
});
|
|
61
|
+
for (const decl of decls) if (decl !== last) decl.remove();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
};
|
|
31
65
|
function shorthandCss(html, options = {}) {
|
|
32
66
|
return serialize(shorthandCssDom(parse(html), options));
|
|
33
67
|
}
|
|
@@ -46,7 +80,7 @@ function shorthandCssDom(dom, options = {}) {
|
|
|
46
80
|
*/
|
|
47
81
|
const mergeStyleValue = (styleValue) => {
|
|
48
82
|
try {
|
|
49
|
-
const { css } = postcss().use(mergeLonghand).process(`div { ${styleValue} }`, { parser: safeParser });
|
|
83
|
+
const { css } = postcss().use(mergeLonghand).use(mergeBorder).process(`div { ${styleValue} }`, { parser: safeParser });
|
|
50
84
|
const match = css.match(/div\s*\{\s*([^}]+)\s*\}/);
|
|
51
85
|
if (match && match[1]) {
|
|
52
86
|
const merged = match[1].trim();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shorthandCss.js","names":[],"sources":["../../src/transformers/shorthandCss.ts"],"sourcesContent":["import postcss from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport mergeLonghand from 'postcss-merge-longhand'\nimport type { ChildNode, Element } from 'domhandler'\nimport { parse, serialize, walk } from '../utils/ast/index.ts'\n\n/**\n * Options for the `shorthandCss` transformer.\n */\nexport interface ShorthandCssOptions {\n /**\n * Restrict the transform to a list of HTML tag names. Omit to apply to\n * every element with a `style` attribute.\n *\n * @example ['td', 'div']\n */\n tags?: string[]\n}\n\n/**\n * Rewrite longhand CSS inside inline `style` attributes with shorthand\n * syntax. Works with margin, padding, and border when all sides are\n * specified.\n *\n * For example:\n * `margin-left: 2px; margin-right: 2px; margin-top: 4px; margin-bottom: 4px`\n * becomes:\n * `margin: 4px 2px`\n *\n * @param html HTML string to transform.\n * @param options Optional Maizzle options (`tags`).\n * @returns The transformed HTML string.\n *\n * @example\n * import { shorthandCss } from '@maizzle/framework'\n *\n * const out = shorthandCss(\n * '<p style=\"margin-top: 4px; margin-right: 2px; margin-bottom: 4px; margin-left: 2px;\">x</p>',\n * { tags: ['p'] },\n * )\n */\nexport function shorthandCss(html: string, options: ShorthandCssOptions = {}): string {\n return serialize(shorthandCssDom(parse(html), options))\n}\n\n/**\n * DOM-form of {@link shorthandCss} used by the internal transformer\n * pipeline. Takes a parsed DOM, returns a parsed DOM — avoids redundant\n * serialize/parse round-trips when chained with other transformers.\n */\nexport function shorthandCssDom(dom: ChildNode[], options: ShorthandCssOptions = {}): ChildNode[] {\n const allowedTags = options.tags ?? []\n const hasTagFilter = allowedTags.length > 0\n\n /**\n * Merge longhand within a single inline-style value. Returns the merged\n * string when shorter, otherwise the original. Wraps the value in a\n * dummy selector since postcss-merge-longhand operates on rules.\n */\n const mergeStyleValue = (styleValue: string): string => {\n try {\n const { css } = postcss()\n .use(mergeLonghand)\n .process(`div { ${styleValue} }`, { parser: safeParser })\n const match = css.match(/div\\s*\\{\\s*([^}]+)\\s*\\}/)\n if (match && match[1]) {\n const merged = match[1].trim()\n if (merged !== styleValue) return merged\n }\n }\n catch {}\n return styleValue\n }\n\n walk(dom, (node) => {\n /**\n * MSO conditional comments carry their own inline-style attributes\n * (e.g. `<!--[if mso]><td style=\"…\"><![endif]-->`) as opaque text.\n * The element walker can't see them, so without this branch the td/\n * v:rect styles inside comments stay longhand even when the visible\n * div has already been merged. Match each `style=\"…\"` substring,\n * run it through mergeLonghand, splice back.\n *\n * Tag filter intentionally bypassed: the user can't address MSO td\n * elements (they don't parse as elements), and these comments\n * always wrap email-layout primitives anyway.\n */\n if (node.type === 'comment') {\n const data = (node as any).data as string\n if (!data || !data.includes('style=\"')) return\n const newData = data.replace(/style=\"([^\"]*)\"/g, (full, value) => {\n const merged = mergeStyleValue(value)\n return merged === value ? full : `style=\"${merged}\"`\n })\n if (newData !== data) (node as any).data = newData\n return\n }\n\n const el = node as Element\n\n if (!el.attribs?.style) return\n if (hasTagFilter && !allowedTags.includes(el.name)) return\n\n const merged = mergeStyleValue(el.attribs.style)\n if (merged !== el.attribs.style) el.attribs.style = merged\n })\n\n return dom\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"shorthandCss.js","names":[],"sources":["../../src/transformers/shorthandCss.ts"],"sourcesContent":["import postcss, { list, type Declaration, type Plugin } from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport mergeLonghand from 'postcss-merge-longhand'\nimport type { ChildNode, Element } from 'domhandler'\nimport { parse, serialize, walk } from '../utils/ast/index.ts'\n\n/**\n * Options for the `shorthandCss` transformer.\n */\nexport interface ShorthandCssOptions {\n /**\n * Restrict the transform to a list of HTML tag names. Omit to apply to\n * every element with a `style` attribute.\n *\n * @example ['td', 'div']\n */\n tags?: string[]\n}\n\n/**\n * Rewrite longhand CSS inside inline `style` attributes with shorthand\n * syntax. Works with margin, padding, and border when all sides are\n * specified.\n *\n * For example:\n * `margin-left: 2px; margin-right: 2px; margin-top: 4px; margin-bottom: 4px`\n * becomes:\n * `margin: 4px 2px`\n *\n * @param html HTML string to transform.\n * @param options Optional Maizzle options (`tags`).\n * @returns The transformed HTML string.\n *\n * @example\n * import { shorthandCss } from '@maizzle/framework'\n *\n * const out = shorthandCss(\n * '<p style=\"margin-top: 4px; margin-right: 2px; margin-bottom: 4px; margin-left: 2px;\">x</p>',\n * { tags: ['p'] },\n * )\n */\n/**\n * Fold `border-width`, `border-style` and `border-color` into `border`.\n *\n * postcss-merge-longhand stopped doing this in 8.0.3 because the\n * shorthand also resets `border-image`. Email clients don't support\n * `border-image`, so the merge is safe for inline styles.\n */\nconst mergeBorder: Plugin = {\n postcssPlugin: 'maizzle-merge-border',\n OnceExit(root) {\n root.walkRules((rule) => {\n const decls: Declaration[] = []\n\n for (const prop of ['border-width', 'border-style', 'border-color']) {\n const found = rule.nodes.filter(\n (node): node is Declaration => node.type === 'decl' && node.prop.toLowerCase() === prop,\n )\n if (found.length !== 1) return\n decls.push(found[0])\n }\n\n const [width, style, color] = decls\n const { important } = width\n\n for (const decl of decls) {\n if (\n decl.important !== important\n || list.space(decl.value).length !== 1\n || /var\\s*\\(/i.test(decl.value)\n ) return\n }\n\n const last = decls.reduce((a, b) => (rule.index(b) > rule.index(a) ? b : a))\n\n last.replaceWith({\n prop: 'border',\n value: `${width.value} ${style.value} ${color.value}`,\n important,\n })\n\n for (const decl of decls) {\n if (decl !== last) decl.remove()\n }\n })\n },\n}\n\nexport function shorthandCss(html: string, options: ShorthandCssOptions = {}): string {\n return serialize(shorthandCssDom(parse(html), options))\n}\n\n/**\n * DOM-form of {@link shorthandCss} used by the internal transformer\n * pipeline. Takes a parsed DOM, returns a parsed DOM — avoids redundant\n * serialize/parse round-trips when chained with other transformers.\n */\nexport function shorthandCssDom(dom: ChildNode[], options: ShorthandCssOptions = {}): ChildNode[] {\n const allowedTags = options.tags ?? []\n const hasTagFilter = allowedTags.length > 0\n\n /**\n * Merge longhand within a single inline-style value. Returns the merged\n * string when shorter, otherwise the original. Wraps the value in a\n * dummy selector since postcss-merge-longhand operates on rules.\n */\n const mergeStyleValue = (styleValue: string): string => {\n try {\n const { css } = postcss()\n .use(mergeLonghand)\n .use(mergeBorder)\n .process(`div { ${styleValue} }`, { parser: safeParser })\n const match = css.match(/div\\s*\\{\\s*([^}]+)\\s*\\}/)\n if (match && match[1]) {\n const merged = match[1].trim()\n if (merged !== styleValue) return merged\n }\n }\n catch {}\n return styleValue\n }\n\n walk(dom, (node) => {\n /**\n * MSO conditional comments carry their own inline-style attributes\n * (e.g. `<!--[if mso]><td style=\"…\"><![endif]-->`) as opaque text.\n * The element walker can't see them, so without this branch the td/\n * v:rect styles inside comments stay longhand even when the visible\n * div has already been merged. Match each `style=\"…\"` substring,\n * run it through mergeLonghand, splice back.\n *\n * Tag filter intentionally bypassed: the user can't address MSO td\n * elements (they don't parse as elements), and these comments\n * always wrap email-layout primitives anyway.\n */\n if (node.type === 'comment') {\n const data = (node as any).data as string\n if (!data || !data.includes('style=\"')) return\n const newData = data.replace(/style=\"([^\"]*)\"/g, (full, value) => {\n const merged = mergeStyleValue(value)\n return merged === value ? full : `style=\"${merged}\"`\n })\n if (newData !== data) (node as any).data = newData\n return\n }\n\n const el = node as Element\n\n if (!el.attribs?.style) return\n if (hasTagFilter && !allowedTags.includes(el.name)) return\n\n const merged = mergeStyleValue(el.attribs.style)\n if (merged !== el.attribs.style) el.attribs.style = merged\n })\n\n return dom\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,MAAM,cAAsB;CAC1B,eAAe;CACf,SAAS,MAAM;EACb,KAAK,WAAW,SAAS;GACvB,MAAM,QAAuB,CAAC;GAE9B,KAAK,MAAM,QAAQ;IAAC;IAAgB;IAAgB;GAAc,GAAG;IACnE,MAAM,QAAQ,KAAK,MAAM,QACtB,SAA8B,KAAK,SAAS,UAAU,KAAK,KAAK,YAAY,MAAM,IACrF;IACA,IAAI,MAAM,WAAW,GAAG;IACxB,MAAM,KAAK,MAAM,EAAE;GACrB;GAEA,MAAM,CAAC,OAAO,OAAO,SAAS;GAC9B,MAAM,EAAE,cAAc;GAEtB,KAAK,MAAM,QAAQ,OACjB,IACE,KAAK,cAAc,aAChB,KAAK,MAAM,KAAK,KAAK,CAAC,CAAC,WAAW,KAClC,YAAY,KAAK,KAAK,KAAK,GAC9B;GAGJ,MAAM,OAAO,MAAM,QAAQ,GAAG,MAAO,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,IAAI,CAAE;GAE3E,KAAK,YAAY;IACf,MAAM;IACN,OAAO,GAAG,MAAM,MAAM,GAAG,MAAM,MAAM,GAAG,MAAM;IAC9C;GACF,CAAC;GAED,KAAK,MAAM,QAAQ,OACjB,IAAI,SAAS,MAAM,KAAK,OAAO;EAEnC,CAAC;CACH;AACF;AAEA,SAAgB,aAAa,MAAc,UAA+B,CAAC,GAAW;CACpF,OAAO,UAAU,gBAAgB,MAAM,IAAI,GAAG,OAAO,CAAC;AACxD;;;;;;AAOA,SAAgB,gBAAgB,KAAkB,UAA+B,CAAC,GAAgB;CAChG,MAAM,cAAc,QAAQ,QAAQ,CAAC;CACrC,MAAM,eAAe,YAAY,SAAS;;;;;;CAO1C,MAAM,mBAAmB,eAA+B;EACtD,IAAI;GACF,MAAM,EAAE,QAAQ,QAAQ,CAAC,CACtB,IAAI,aAAa,CAAC,CAClB,IAAI,WAAW,CAAC,CAChB,QAAQ,SAAS,WAAW,KAAK,EAAE,QAAQ,WAAW,CAAC;GAC1D,MAAM,QAAQ,IAAI,MAAM,yBAAyB;GACjD,IAAI,SAAS,MAAM,IAAI;IACrB,MAAM,SAAS,MAAM,EAAE,CAAC,KAAK;IAC7B,IAAI,WAAW,YAAY,OAAO;GACpC;EACF,QACM,CAAC;EACP,OAAO;CACT;CAEA,KAAK,MAAM,SAAS;;;;;;;;;;;;;EAalB,IAAI,KAAK,SAAS,WAAW;GAC3B,MAAM,OAAQ,KAAa;GAC3B,IAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,UAAS,GAAG;GACxC,MAAM,UAAU,KAAK,QAAQ,qBAAqB,MAAM,UAAU;IAChE,MAAM,SAAS,gBAAgB,KAAK;IACpC,OAAO,WAAW,QAAQ,OAAO,UAAU,OAAO;GACpD,CAAC;GACD,IAAI,YAAY,MAAM,KAAc,OAAO;GAC3C;EACF;EAEA,MAAM,KAAK;EAEX,IAAI,CAAC,GAAG,SAAS,OAAO;EACxB,IAAI,gBAAgB,CAAC,YAAY,SAAS,GAAG,IAAI,GAAG;EAEpD,MAAM,SAAS,gBAAgB,GAAG,QAAQ,KAAK;EAC/C,IAAI,WAAW,GAAG,QAAQ,OAAO,GAAG,QAAQ,QAAQ;CACtD,CAAC;CAED,OAAO;AACT"}
|
|
@@ -4,8 +4,35 @@
|
|
|
4
4
|
* chokidar matches any of the given globs. Patterns are interpreted as
|
|
5
5
|
* project-relative; a leading `./` is stripped so user-supplied globs like
|
|
6
6
|
* `./locales/**` behave identically to `locales/**`.
|
|
7
|
+
*
|
|
8
|
+
* Negated patterns (`!…`) are excludes: a file matches when it matches an
|
|
9
|
+
* include pattern and no exclude pattern. They must not reach `matchesGlob`
|
|
10
|
+
* as-is — it treats a lone negated pattern as "everything except", which
|
|
11
|
+
* under `some()` would make the predicate true for almost every file.
|
|
7
12
|
*/
|
|
8
13
|
declare function createWatchedFileMatcher(patterns: string[], cwd: string): (file: string) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Derive the directories the dev server watcher must cover explicitly now
|
|
16
|
+
* that its Vite root is the dev UI directory, not the project cwd: the
|
|
17
|
+
* static prefixes of the content globs, component source dirs, the Maizzle
|
|
18
|
+
* root, and the static prefixes of the watch globs. Directories, not globs —
|
|
19
|
+
* the dev server runs with Vite's default `disableGlobbing`, so globs passed
|
|
20
|
+
* to `watcher.add` are treated literally.
|
|
21
|
+
*
|
|
22
|
+
* Negated patterns are excludes and produce no watch root. A watch pattern
|
|
23
|
+
* with no static prefix (a bare `*.json`, or a glob starting with `**`)
|
|
24
|
+
* deliberately falls back to `cwd`:
|
|
25
|
+
* it asks for project-wide matching, and only a project-wide watch can honor
|
|
26
|
+
* it. Content patterns cannot hit that fallback — they arrive from
|
|
27
|
+
* `resolveConfig` already resolved against the (absolute) root.
|
|
28
|
+
*/
|
|
29
|
+
declare function deriveWatchRoots(options: {
|
|
30
|
+
content: string[];
|
|
31
|
+
componentDirs: string[];
|
|
32
|
+
root?: string;
|
|
33
|
+
watchPaths: string[];
|
|
34
|
+
cwd: string;
|
|
35
|
+
}): string[];
|
|
9
36
|
//#endregion
|
|
10
|
-
export { createWatchedFileMatcher };
|
|
37
|
+
export { createWatchedFileMatcher, deriveWatchRoots };
|
|
11
38
|
//# sourceMappingURL=watchPaths.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watchPaths.d.ts","names":[],"sources":["../../src/utils/watchPaths.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"watchPaths.d.ts","names":[],"sources":["../../src/utils/watchPaths.ts"],"mappings":";;;;;;;;;;;;iBAagB,yBAAyB,oBAAoB,eAInD;;;;;;;;;;;;;;;;iBAqBM,iBAAiB;EAC/B;EACA;EACA;EACA;EACA"}
|
package/dist/utils/watchPaths.js
CHANGED
|
@@ -5,15 +5,50 @@ import { matchesGlob, relative } from "pathe";
|
|
|
5
5
|
* chokidar matches any of the given globs. Patterns are interpreted as
|
|
6
6
|
* project-relative; a leading `./` is stripped so user-supplied globs like
|
|
7
7
|
* `./locales/**` behave identically to `locales/**`.
|
|
8
|
+
*
|
|
9
|
+
* Negated patterns (`!…`) are excludes: a file matches when it matches an
|
|
10
|
+
* include pattern and no exclude pattern. They must not reach `matchesGlob`
|
|
11
|
+
* as-is — it treats a lone negated pattern as "everything except", which
|
|
12
|
+
* under `some()` would make the predicate true for almost every file.
|
|
8
13
|
*/
|
|
9
14
|
function createWatchedFileMatcher(patterns, cwd) {
|
|
10
|
-
const
|
|
15
|
+
const stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
16
|
+
const includes = patterns.filter((p) => !p.startsWith("!")).map(stripDotSlash);
|
|
17
|
+
const excludes = patterns.filter((p) => p.startsWith("!")).map((p) => stripDotSlash(p.slice(1)));
|
|
11
18
|
return (file) => {
|
|
12
19
|
const rel = relative(cwd, file);
|
|
13
|
-
return
|
|
20
|
+
return includes.some((p) => matchesGlob(rel, p)) && !excludes.some((p) => matchesGlob(rel, p));
|
|
14
21
|
};
|
|
15
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Derive the directories the dev server watcher must cover explicitly now
|
|
25
|
+
* that its Vite root is the dev UI directory, not the project cwd: the
|
|
26
|
+
* static prefixes of the content globs, component source dirs, the Maizzle
|
|
27
|
+
* root, and the static prefixes of the watch globs. Directories, not globs —
|
|
28
|
+
* the dev server runs with Vite's default `disableGlobbing`, so globs passed
|
|
29
|
+
* to `watcher.add` are treated literally.
|
|
30
|
+
*
|
|
31
|
+
* Negated patterns are excludes and produce no watch root. A watch pattern
|
|
32
|
+
* with no static prefix (a bare `*.json`, or a glob starting with `**`)
|
|
33
|
+
* deliberately falls back to `cwd`:
|
|
34
|
+
* it asks for project-wide matching, and only a project-wide watch can honor
|
|
35
|
+
* it. Content patterns cannot hit that fallback — they arrive from
|
|
36
|
+
* `resolveConfig` already resolved against the (absolute) root.
|
|
37
|
+
*/
|
|
38
|
+
function deriveWatchRoots(options) {
|
|
39
|
+
const { content, componentDirs, root, watchPaths, cwd } = options;
|
|
40
|
+
const globFreePrefix = (pattern) => {
|
|
41
|
+
const prefix = pattern.split(/[*?{[]|[+@!]\(/)[0];
|
|
42
|
+
return prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
43
|
+
};
|
|
44
|
+
return [...new Set([
|
|
45
|
+
...content.filter((p) => !p.startsWith("!")).map(globFreePrefix),
|
|
46
|
+
...componentDirs,
|
|
47
|
+
...root ? [root] : [],
|
|
48
|
+
...watchPaths.filter((p) => !p.startsWith("!")).map((p) => globFreePrefix(p) || cwd)
|
|
49
|
+
].filter(Boolean))];
|
|
50
|
+
}
|
|
16
51
|
//#endregion
|
|
17
|
-
export { createWatchedFileMatcher };
|
|
52
|
+
export { createWatchedFileMatcher, deriveWatchRoots };
|
|
18
53
|
|
|
19
54
|
//# sourceMappingURL=watchPaths.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watchPaths.js","names":[],"sources":["../../src/utils/watchPaths.ts"],"sourcesContent":["import { matchesGlob, relative } from 'pathe'\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
|
|
1
|
+
{"version":3,"file":"watchPaths.js","names":[],"sources":["../../src/utils/watchPaths.ts"],"sourcesContent":["import { matchesGlob, relative } from 'pathe'\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 *\n * Negated patterns (`!…`) are excludes: a file matches when it matches an\n * include pattern and no exclude pattern. They must not reach `matchesGlob`\n * as-is — it treats a lone negated pattern as \"everything except\", which\n * under `some()` would make the predicate true for almost every file.\n */\nexport function createWatchedFileMatcher(patterns: string[], cwd: string) {\n const stripDotSlash = (p: string) => p.replace(/^\\.\\//, '')\n const includes = patterns.filter(p => !p.startsWith('!')).map(stripDotSlash)\n const excludes = patterns.filter(p => p.startsWith('!')).map(p => stripDotSlash(p.slice(1)))\n return (file: string) => {\n const rel = relative(cwd, file)\n return includes.some(p => matchesGlob(rel, p)) && !excludes.some(p => matchesGlob(rel, p))\n }\n}\n\n/**\n * Derive the directories the dev server watcher must cover explicitly now\n * that its Vite root is the dev UI directory, not the project cwd: the\n * static prefixes of the content globs, component source dirs, the Maizzle\n * root, and the static prefixes of the watch globs. Directories, not globs —\n * the dev server runs with Vite's default `disableGlobbing`, so globs passed\n * to `watcher.add` are treated literally.\n *\n * Negated patterns are excludes and produce no watch root. A watch pattern\n * with no static prefix (a bare `*.json`, or a glob starting with `**`)\n * deliberately falls back to `cwd`:\n * it asks for project-wide matching, and only a project-wide watch can honor\n * it. Content patterns cannot hit that fallback — they arrive from\n * `resolveConfig` already resolved against the (absolute) root.\n */\nexport function deriveWatchRoots(options: {\n content: string[]\n componentDirs: string[]\n root?: string\n watchPaths: string[]\n cwd: string\n}): string[] {\n const { content, componentDirs, root, watchPaths, cwd } = options\n\n // Stop at any glob syntax across both glob backends in use — pathe's\n // matchesGlob for the watched-file matcher (wildcards, ?, braces, bracket\n // classes) and tinyglobby/picomatch for template listing (additionally\n // extglobs: +(…), @(…), !(…), matched as two-char openers so bare\n // parentheses in directory names stay literal). Splitting too late would\n // leave a literal never-existing path as the root and silently lose\n // coverage; splitting too early merely watches the parent directory,\n // which still covers the target — so err on the side of splitting.\n const globFreePrefix = (pattern: string) => {\n const prefix = pattern.split(/[*?{[]|[+@!]\\(/)[0]\n return prefix.endsWith('/') ? prefix.slice(0, -1) : prefix\n }\n\n return [...new Set([\n ...content.filter(p => !p.startsWith('!')).map(globFreePrefix),\n ...componentDirs,\n ...(root ? [root] : []),\n ...watchPaths.filter(p => !p.startsWith('!')).map(p => globFreePrefix(p) || cwd),\n ].filter(Boolean))]\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,yBAAyB,UAAoB,KAAa;CACxE,MAAM,iBAAiB,MAAc,EAAE,QAAQ,SAAS,EAAE;CAC1D,MAAM,WAAW,SAAS,QAAO,MAAK,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,IAAI,aAAa;CAC3E,MAAM,WAAW,SAAS,QAAO,MAAK,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,KAAI,MAAK,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;CAC3F,QAAQ,SAAiB;EACvB,MAAM,MAAM,SAAS,KAAK,IAAI;EAC9B,OAAO,SAAS,MAAK,MAAK,YAAY,KAAK,CAAC,CAAC,KAAK,CAAC,SAAS,MAAK,MAAK,YAAY,KAAK,CAAC,CAAC;CAC3F;AACF;;;;;;;;;;;;;;;;AAiBA,SAAgB,iBAAiB,SAMpB;CACX,MAAM,EAAE,SAAS,eAAe,MAAM,YAAY,QAAQ;CAU1D,MAAM,kBAAkB,YAAoB;EAC1C,MAAM,SAAS,QAAQ,MAAM,gBAAgB,CAAC,CAAC;EAC/C,OAAO,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI;CACtD;CAEA,OAAO,CAAC,GAAG,IAAI,IAAI;EACjB,GAAG,QAAQ,QAAO,MAAK,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,IAAI,cAAc;EAC7D,GAAG;EACH,GAAI,OAAO,CAAC,IAAI,IAAI,CAAC;EACrB,GAAG,WAAW,QAAO,MAAK,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,KAAI,MAAK,eAAe,CAAC,KAAK,GAAG;CACjF,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC;AACpB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maizzle/framework",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.4",
|
|
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",
|
|
@@ -58,11 +58,11 @@
|
|
|
58
58
|
"markdown-exit": "^1.1.0-beta.2",
|
|
59
59
|
"nodemailer": "^9.0.0",
|
|
60
60
|
"ora": "^9.3.0",
|
|
61
|
-
"oxfmt": "^0.
|
|
61
|
+
"oxfmt": "^0.65.0",
|
|
62
62
|
"pathe": "^2.0.3",
|
|
63
63
|
"postcss": "^8.5.6",
|
|
64
64
|
"postcss-calc": "^10.1.1",
|
|
65
|
-
"postcss-merge-longhand": "^
|
|
65
|
+
"postcss-merge-longhand": "^9.0.1",
|
|
66
66
|
"postcss-safe-parser": "^7.0.1",
|
|
67
67
|
"postcss-sort-media-queries": "^6.5.0",
|
|
68
68
|
"postcss-value-parser": "^4.2.0",
|