@maizzle/framework 6.0.0-rc.17 → 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.
- package/bin/maizzle.mjs +1 -1
- package/dist/build.d.ts +6 -5
- package/dist/build.d.ts.map +1 -1
- package/dist/{build.mjs → build.js} +23 -15
- package/dist/build.js.map +1 -0
- package/dist/components/Body.vue +10 -0
- package/dist/components/Button.vue +86 -49
- package/dist/components/Column.vue +10 -0
- package/dist/components/Container.vue +35 -7
- package/dist/components/Hr.vue +33 -0
- package/dist/components/Html.vue +29 -1
- package/dist/components/Layout.vue +27 -12
- package/dist/components/MarkdownLayout.vue +39 -0
- package/dist/components/NotPlaintext.vue +14 -0
- package/dist/components/{Vml.vue → OutlookBg.vue} +1 -1
- package/dist/components/Overlap.vue +10 -0
- package/dist/components/Plaintext.vue +14 -0
- package/dist/components/Preheader.vue +3 -8
- package/dist/components/QrCode.vue +157 -0
- package/dist/components/Row.vue +10 -0
- package/dist/components/Section.vue +10 -0
- package/dist/components/Spacer.vue +28 -27
- package/dist/components/{utils.mjs → utils.js} +2 -2
- package/dist/components/utils.js.map +1 -0
- package/dist/composables/{defineConfig.mjs → defineConfig.js} +4 -5
- package/dist/composables/defineConfig.js.map +1 -0
- package/dist/composables/{renderContext.mjs → renderContext.js} +2 -2
- package/dist/composables/renderContext.js.map +1 -0
- package/dist/composables/useBaseUrl.d.ts +19 -0
- package/dist/composables/useBaseUrl.d.ts.map +1 -0
- package/dist/composables/useBaseUrl.js +26 -0
- package/dist/composables/useBaseUrl.js.map +1 -0
- package/dist/composables/{useConfig.mjs → useConfig.js} +2 -3
- package/dist/composables/useConfig.js.map +1 -0
- package/dist/composables/{useDoctype.mjs → useDoctype.js} +3 -4
- package/dist/composables/useDoctype.js.map +1 -0
- package/dist/composables/{useEvent.mjs → useEvent.js} +3 -4
- package/dist/composables/useEvent.js.map +1 -0
- package/dist/composables/{useFont.mjs → useFont.js} +3 -4
- package/dist/composables/useFont.js.map +1 -0
- package/dist/composables/{useOutlookFallback.mjs → useOutlookFallback.js} +2 -3
- package/dist/composables/useOutlookFallback.js.map +1 -0
- package/dist/composables/usePlaintext.d.ts +2 -0
- package/dist/composables/usePlaintext.d.ts.map +1 -1
- package/dist/composables/{usePlaintext.mjs → usePlaintext.js} +4 -4
- package/dist/composables/usePlaintext.js.map +1 -0
- package/dist/composables/{usePreheader.mjs → usePreheader.js} +3 -4
- package/dist/composables/usePreheader.js.map +1 -0
- package/dist/composables/useTransformers.d.ts +34 -0
- package/dist/composables/useTransformers.d.ts.map +1 -0
- package/dist/composables/useTransformers.js +48 -0
- package/dist/composables/useTransformers.js.map +1 -0
- package/dist/composables/useUrlQuery.d.ts +19 -0
- package/dist/composables/useUrlQuery.d.ts.map +1 -0
- package/dist/composables/useUrlQuery.js +26 -0
- package/dist/composables/useUrlQuery.js.map +1 -0
- package/dist/config/{defaults.mjs → defaults.js} +9 -3
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/{index.mjs → index.js} +4 -5
- package/dist/config/index.js.map +1 -0
- package/dist/events/index.d.ts +8 -2
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/{index.mjs → index.js} +21 -5
- package/dist/events/index.js.map +1 -0
- package/dist/index.d.ts +12 -9
- package/dist/index.js +36 -0
- package/dist/{plaintext.mjs → plaintext.js} +4 -5
- package/dist/plaintext.js.map +1 -0
- package/dist/{plugin.mjs → plugin.js} +6 -7
- package/dist/plugin.js.map +1 -0
- package/dist/plugins/postcss/{mergeMediaQueries.mjs → mergeMediaQueries.js} +2 -3
- package/dist/plugins/postcss/mergeMediaQueries.js.map +1 -0
- package/dist/plugins/postcss/{pruneVars.mjs → pruneVars.js} +2 -2
- package/dist/plugins/postcss/pruneVars.js.map +1 -0
- package/dist/plugins/postcss/{quoteFontFamilies.mjs → quoteFontFamilies.js} +2 -2
- package/dist/plugins/postcss/quoteFontFamilies.js.map +1 -0
- package/dist/plugins/postcss/{removeDeclarations.mjs → removeDeclarations.js} +2 -2
- package/dist/plugins/postcss/removeDeclarations.js.map +1 -0
- package/dist/plugins/postcss/{resolveMaizzleImports.mjs → resolveMaizzleImports.js} +2 -3
- package/dist/plugins/postcss/resolveMaizzleImports.js.map +1 -0
- package/dist/plugins/postcss/{resolveProps.mjs → resolveProps.js} +2 -2
- package/dist/plugins/postcss/resolveProps.js.map +1 -0
- package/dist/plugins/postcss/{tailwindCleanup.mjs → tailwindCleanup.js} +2 -2
- package/dist/plugins/postcss/tailwindCleanup.js.map +1 -0
- package/dist/{prepare.mjs → prepare.js} +5 -6
- package/dist/prepare.js.map +1 -0
- package/dist/render/active.d.ts +8 -0
- package/dist/render/active.d.ts.map +1 -0
- package/dist/render/active.js +12 -0
- package/dist/render/active.js.map +1 -0
- package/dist/render/createRenderer.d.ts.map +1 -1
- package/dist/render/{createRenderer.mjs → createRenderer.js} +24 -19
- package/dist/render/createRenderer.js.map +1 -0
- package/dist/render/index.d.ts.map +1 -1
- package/dist/render/{index.mjs → index.js} +18 -11
- package/dist/render/index.js.map +1 -0
- package/dist/render/{injectFonts.mjs → injectFonts.js} +3 -4
- package/dist/render/injectFonts.js.map +1 -0
- package/dist/render/plugins/{codeBlockExtract.mjs → codeBlockExtract.js} +2 -2
- package/dist/render/plugins/codeBlockExtract.js.map +1 -0
- package/dist/render/plugins/{markdownExtract.mjs → markdownExtract.js} +2 -3
- package/dist/render/plugins/markdownExtract.js.map +1 -0
- package/dist/render/plugins/{rawExtract.mjs → rawExtract.js} +2 -2
- package/dist/render/plugins/rawExtract.js.map +1 -0
- package/dist/render/plugins/{rowSourceLocation.mjs → rowSourceLocation.js} +2 -2
- package/dist/render/plugins/rowSourceLocation.js.map +1 -0
- package/dist/serve.d.ts +2 -0
- package/dist/serve.d.ts.map +1 -1
- package/dist/{serve.mjs → serve.js} +20 -15
- package/dist/serve.js.map +1 -0
- package/dist/server/{compatibility.mjs → compatibility.js} +5 -6
- package/dist/server/compatibility.js.map +1 -0
- package/dist/server/{email.mjs → email.js} +2 -3
- package/dist/server/email.js.map +1 -0
- package/dist/server/{linter.mjs → linter.js} +3 -4
- package/dist/server/linter.js.map +1 -0
- package/dist/server/{sfc-utils.mjs → sfc-utils.js} +2 -3
- package/dist/server/sfc-utils.js.map +1 -0
- package/dist/server/ui/App.vue +18 -0
- package/dist/server/ui/components/ui/command/Command.vue +4 -1
- package/dist/tests/render/_helpers.d.ts +6 -0
- package/dist/tests/render/_helpers.d.ts.map +1 -0
- package/dist/tests/render/_helpers.js +16 -0
- package/dist/tests/render/_helpers.js.map +1 -0
- package/dist/transformers/{addAttributes.mjs → addAttributes.js} +6 -7
- package/dist/transformers/addAttributes.js.map +1 -0
- package/dist/transformers/attributeToStyle.d.ts +27 -14
- package/dist/transformers/attributeToStyle.d.ts.map +1 -1
- package/dist/transformers/attributeToStyle.js +94 -0
- package/dist/transformers/attributeToStyle.js.map +1 -0
- package/dist/transformers/base.d.ts +66 -3
- package/dist/transformers/base.d.ts.map +1 -1
- package/dist/transformers/{base.mjs → base.js} +56 -30
- package/dist/transformers/base.js.map +1 -0
- package/dist/transformers/{columnWidth.mjs → columnWidth.js} +4 -5
- package/dist/transformers/columnWidth.js.map +1 -0
- package/dist/transformers/entities.d.ts +31 -2
- package/dist/transformers/entities.d.ts.map +1 -1
- package/dist/transformers/entities.js +73 -0
- package/dist/transformers/entities.js.map +1 -0
- package/dist/transformers/filters/{defaults.mjs → defaults.js} +2 -2
- package/dist/transformers/filters/defaults.js.map +1 -0
- package/dist/transformers/filters/index.d.ts +31 -10
- package/dist/transformers/filters/index.d.ts.map +1 -1
- package/dist/transformers/filters/index.js +89 -0
- package/dist/transformers/filters/index.js.map +1 -0
- package/dist/transformers/format.d.ts +14 -7
- package/dist/transformers/format.d.ts.map +1 -1
- package/dist/transformers/format.js +30 -0
- package/dist/transformers/format.js.map +1 -0
- package/dist/transformers/index.js +133 -0
- package/dist/transformers/index.js.map +1 -0
- package/dist/transformers/inlineCss.d.ts +84 -0
- package/dist/transformers/inlineCss.d.ts.map +1 -0
- package/dist/transformers/{inlineCSS.mjs → inlineCss.js} +28 -18
- package/dist/transformers/inlineCss.js.map +1 -0
- package/dist/transformers/inlineLink.d.ts +26 -5
- package/dist/transformers/inlineLink.d.ts.map +1 -1
- package/dist/transformers/{inlineLink.mjs → inlineLink.js} +34 -10
- package/dist/transformers/inlineLink.js.map +1 -0
- package/dist/transformers/minify.d.ts +13 -9
- package/dist/transformers/minify.d.ts.map +1 -1
- package/dist/transformers/minify.js +25 -0
- package/dist/transformers/minify.js.map +1 -0
- package/dist/transformers/msoPlaceholders.d.ts +28 -0
- package/dist/transformers/msoPlaceholders.d.ts.map +1 -0
- package/dist/transformers/msoPlaceholders.js +88 -0
- package/dist/transformers/msoPlaceholders.js.map +1 -0
- package/dist/transformers/purgeCss.d.ts +43 -0
- package/dist/transformers/purgeCss.d.ts.map +1 -0
- package/dist/transformers/{purgeCSS.mjs → purgeCss.js} +36 -29
- package/dist/transformers/purgeCss.js.map +1 -0
- package/dist/transformers/removeAttributes.d.ts +43 -20
- package/dist/transformers/removeAttributes.d.ts.map +1 -1
- package/dist/transformers/removeAttributes.js +70 -0
- package/dist/transformers/removeAttributes.js.map +1 -0
- package/dist/transformers/{replaceStrings.mjs → replaceStrings.js} +2 -2
- package/dist/transformers/replaceStrings.js.map +1 -0
- package/dist/transformers/{safeClassNames.mjs → safeClassNames.js} +4 -5
- package/dist/transformers/safeClassNames.js.map +1 -0
- package/dist/transformers/shorthandCss.d.ts +47 -0
- package/dist/transformers/shorthandCss.d.ts.map +1 -0
- package/dist/transformers/shorthandCss.js +61 -0
- package/dist/transformers/shorthandCss.js.map +1 -0
- package/dist/transformers/sixHex.d.ts +16 -7
- package/dist/transformers/sixHex.d.ts.map +1 -1
- package/dist/transformers/sixHex.js +42 -0
- package/dist/transformers/sixHex.js.map +1 -0
- package/dist/transformers/{tailwindComponent.mjs → tailwindComponent.js} +5 -6
- package/dist/transformers/tailwindComponent.js.map +1 -0
- package/dist/transformers/{tailwindcss.mjs → tailwindcss.js} +6 -7
- package/dist/transformers/tailwindcss.js.map +1 -0
- package/dist/transformers/urlQuery.d.ts +26 -14
- package/dist/transformers/urlQuery.d.ts.map +1 -1
- package/dist/transformers/urlQuery.js +77 -0
- package/dist/transformers/urlQuery.js.map +1 -0
- package/dist/types/config.d.ts +108 -15
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +1 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +1 -0
- package/dist/utils/ast/index.js +4 -0
- package/dist/utils/ast/{parser.mjs → parser.js} +2 -3
- package/dist/utils/ast/parser.js.map +1 -0
- package/dist/utils/ast/{serializer.mjs → serializer.js} +3 -4
- package/dist/utils/ast/serializer.js.map +1 -0
- package/dist/utils/ast/{walker.mjs → walker.js} +2 -2
- package/dist/utils/ast/walker.js.map +1 -0
- package/dist/utils/{compileTailwindCss.mjs → compileTailwindCss.js} +8 -9
- package/dist/utils/compileTailwindCss.js.map +1 -0
- package/dist/utils/{decodeStyleEntities.mjs → decodeStyleEntities.js} +2 -2
- package/dist/utils/decodeStyleEntities.js.map +1 -0
- package/dist/utils/{detect.mjs → detect.js} +2 -3
- package/dist/utils/detect.js.map +1 -0
- package/dist/utils/output-markers.d.ts +29 -0
- package/dist/utils/output-markers.d.ts.map +1 -0
- package/dist/utils/output-markers.js +68 -0
- package/dist/utils/output-markers.js.map +1 -0
- package/dist/utils/{url.mjs → url.js} +2 -3
- package/dist/utils/url.js.map +1 -0
- package/node_modules/@clack/core/CHANGELOG.md +8 -0
- package/node_modules/@clack/core/dist/index.d.mts +18 -4
- package/node_modules/@clack/core/dist/index.mjs +16 -10
- package/node_modules/@clack/core/dist/index.mjs.map +1 -1
- package/node_modules/@clack/core/package.json +5 -2
- package/node_modules/@clack/prompts/CHANGELOG.md +15 -0
- package/node_modules/@clack/prompts/README.md +107 -2
- package/node_modules/@clack/prompts/dist/index.d.mts +16 -11
- package/node_modules/@clack/prompts/dist/index.mjs +114 -107
- package/node_modules/@clack/prompts/dist/index.mjs.map +1 -1
- package/node_modules/@clack/prompts/package.json +7 -4
- package/node_modules/fast-string-truncated-width/dist/index.js +36 -96
- package/node_modules/fast-string-truncated-width/dist/types.d.ts +0 -3
- package/node_modules/fast-string-truncated-width/dist/utils.d.ts +3 -3
- package/node_modules/fast-string-truncated-width/dist/utils.js +14 -9
- package/node_modules/fast-string-truncated-width/package.json +1 -1
- package/node_modules/fast-string-truncated-width/readme.md +2 -3
- package/node_modules/fast-string-width/package.json +2 -2
- package/node_modules/fast-string-width/readme.md +0 -3
- package/node_modules/fast-wrap-ansi/lib/main.js +4 -1
- package/node_modules/fast-wrap-ansi/lib/main.js.map +1 -1
- package/node_modules/fast-wrap-ansi/package.json +2 -2
- package/node_modules/maizzle/README.md +24 -0
- package/node_modules/maizzle/dist/commands/make/component.mjs +1 -1
- package/node_modules/maizzle/dist/commands/make/config.mjs +1 -1
- package/node_modules/maizzle/dist/commands/make/layout.mjs +3 -3
- package/node_modules/maizzle/dist/commands/make/scaffold.mjs +1 -1
- package/node_modules/maizzle/dist/commands/make/stubs/Layout.vue +146 -0
- package/node_modules/maizzle/dist/commands/make/stubs/component.vue +2 -4
- package/node_modules/maizzle/dist/commands/make/stubs/config.ts +1 -5
- package/node_modules/maizzle/dist/commands/make/template.mjs +1 -1
- package/node_modules/maizzle/dist/commands/new.mjs +29 -24
- package/node_modules/maizzle/dist/index.mjs +28 -8
- package/node_modules/maizzle/package.json +1 -1
- package/node_modules/tinyexec/README.md +1 -1
- package/node_modules/tinyexec/dist/main.d.mts +6 -6
- package/node_modules/tinyexec/dist/main.mjs +126 -134
- package/node_modules/tinyexec/package.json +9 -9
- package/package.json +4 -4
- package/dist/build.mjs.map +0 -1
- package/dist/components/Divider.vue +0 -133
- package/dist/components/utils.mjs.map +0 -1
- package/dist/composables/defineConfig.mjs.map +0 -1
- package/dist/composables/renderContext.mjs.map +0 -1
- package/dist/composables/useConfig.mjs.map +0 -1
- package/dist/composables/useDoctype.mjs.map +0 -1
- package/dist/composables/useEvent.mjs.map +0 -1
- package/dist/composables/useFont.mjs.map +0 -1
- package/dist/composables/useOutlookFallback.mjs.map +0 -1
- package/dist/composables/usePlaintext.mjs.map +0 -1
- package/dist/composables/usePreheader.mjs.map +0 -1
- package/dist/config/defaults.mjs.map +0 -1
- package/dist/config/index.mjs.map +0 -1
- package/dist/events/index.mjs.map +0 -1
- package/dist/index.mjs +0 -34
- package/dist/plaintext.mjs.map +0 -1
- package/dist/plugin.mjs.map +0 -1
- package/dist/plugins/postcss/mergeMediaQueries.mjs.map +0 -1
- package/dist/plugins/postcss/pruneVars.mjs.map +0 -1
- package/dist/plugins/postcss/quoteFontFamilies.mjs.map +0 -1
- package/dist/plugins/postcss/removeDeclarations.mjs.map +0 -1
- package/dist/plugins/postcss/resolveMaizzleImports.mjs.map +0 -1
- package/dist/plugins/postcss/resolveProps.mjs.map +0 -1
- package/dist/plugins/postcss/tailwindCleanup.mjs.map +0 -1
- package/dist/prepare.mjs.map +0 -1
- package/dist/render/createRenderer.mjs.map +0 -1
- package/dist/render/index.mjs.map +0 -1
- package/dist/render/injectFonts.mjs.map +0 -1
- package/dist/render/plugins/codeBlockExtract.mjs.map +0 -1
- package/dist/render/plugins/markdownExtract.mjs.map +0 -1
- package/dist/render/plugins/rawExtract.mjs.map +0 -1
- package/dist/render/plugins/rowSourceLocation.mjs.map +0 -1
- package/dist/serve.mjs.map +0 -1
- package/dist/server/compatibility.mjs.map +0 -1
- package/dist/server/email.mjs.map +0 -1
- package/dist/server/linter.mjs.map +0 -1
- package/dist/server/sfc-utils.mjs.map +0 -1
- package/dist/transformers/addAttributes.mjs.map +0 -1
- package/dist/transformers/attributeToStyle.mjs +0 -80
- package/dist/transformers/attributeToStyle.mjs.map +0 -1
- package/dist/transformers/base.mjs.map +0 -1
- package/dist/transformers/columnWidth.mjs.map +0 -1
- package/dist/transformers/entities.mjs +0 -41
- package/dist/transformers/entities.mjs.map +0 -1
- package/dist/transformers/filters/defaults.mjs.map +0 -1
- package/dist/transformers/filters/index.mjs +0 -67
- package/dist/transformers/filters/index.mjs.map +0 -1
- package/dist/transformers/format.mjs +0 -26
- package/dist/transformers/format.mjs.map +0 -1
- package/dist/transformers/index.mjs +0 -87
- package/dist/transformers/index.mjs.map +0 -1
- package/dist/transformers/inlineCSS.d.ts +0 -17
- package/dist/transformers/inlineCSS.d.ts.map +0 -1
- package/dist/transformers/inlineCSS.mjs.map +0 -1
- package/dist/transformers/inlineLink.mjs.map +0 -1
- package/dist/transformers/minify.mjs +0 -24
- package/dist/transformers/minify.mjs.map +0 -1
- package/dist/transformers/msoWidthFromClass.d.ts +0 -19
- package/dist/transformers/msoWidthFromClass.d.ts.map +0 -1
- package/dist/transformers/msoWidthFromClass.mjs +0 -61
- package/dist/transformers/msoWidthFromClass.mjs.map +0 -1
- package/dist/transformers/purgeCSS.d.ts +0 -23
- package/dist/transformers/purgeCSS.d.ts.map +0 -1
- package/dist/transformers/purgeCSS.mjs.map +0 -1
- package/dist/transformers/removeAttributes.mjs +0 -63
- package/dist/transformers/removeAttributes.mjs.map +0 -1
- package/dist/transformers/replaceStrings.mjs.map +0 -1
- package/dist/transformers/safeClassNames.mjs.map +0 -1
- package/dist/transformers/shorthandCSS.d.ts +0 -24
- package/dist/transformers/shorthandCSS.d.ts.map +0 -1
- package/dist/transformers/shorthandCSS.mjs +0 -48
- package/dist/transformers/shorthandCSS.mjs.map +0 -1
- package/dist/transformers/sixHex.mjs +0 -30
- package/dist/transformers/sixHex.mjs.map +0 -1
- package/dist/transformers/tailwindComponent.mjs.map +0 -1
- package/dist/transformers/tailwindcss.mjs.map +0 -1
- package/dist/transformers/urlQuery.mjs +0 -65
- package/dist/transformers/urlQuery.mjs.map +0 -1
- package/dist/types/config.mjs +0 -1
- package/dist/types/index.mjs +0 -1
- package/dist/utils/ast/index.mjs +0 -5
- package/dist/utils/ast/parser.mjs.map +0 -1
- package/dist/utils/ast/serializer.mjs.map +0 -1
- package/dist/utils/ast/walker.mjs.map +0 -1
- package/dist/utils/compileTailwindCss.mjs.map +0 -1
- package/dist/utils/decodeStyleEntities.mjs.map +0 -1
- package/dist/utils/detect.mjs.map +0 -1
- package/dist/utils/url.mjs.map +0 -1
- package/node_modules/maizzle/dist/commands/make/stubs/layout.vue +0 -39
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"addAttributes.js","names":["merge"],"sources":["../../src/transformers/addAttributes.ts"],"sourcesContent":["import { defu as merge } from 'defu'\nimport type { ChildNode, Element } from 'domhandler'\nimport { walk } from '../utils/ast/index.ts'\nimport type { AttributesConfig } from '../types/config.ts'\n\n/**\n * Default attributes to add to elements.\n */\nconst DEFAULT_ATTRIBUTES: Record<string, Record<string, string | boolean | number>> = {\n table: {\n cellpadding: 0,\n cellspacing: 0,\n role: 'none',\n },\n img: {\n alt: '',\n },\n}\n\n/**\n * Add attributes transformer.\n *\n * Automatically adds attributes to HTML elements based on CSS selectors.\n *\n * Default attributes (can be disabled by setting `attributes.add` to false):\n * - table: cellpadding=\"0\", cellspacing=\"0\", role=\"none\"\n * - img: alt=\"\"\n *\n * Supports tag, class, id, and attribute selectors.\n * Multiple selectors can be specified by comma-separating them.\n *\n * Examples:\n * ```js\n * attributes: {\n * add: {\n * div: { role: 'article' },\n * '.test': { editable: true },\n * '#header': { 'data-id': 'main' },\n * 'div, p': { class: 'content' },\n * }\n * }\n * ```\n */\nexport function addAttributes(dom: ChildNode[], config: AttributesConfig = {}): ChildNode[] {\n const addConfig = config.add\n\n // Disabled when explicitly set to false\n if (addConfig === false) {\n return dom\n }\n\n // Deep merge user attributes on top of defaults using defu\n const userAttributes = typeof addConfig === 'object' ? addConfig : {}\n const attributesToAdd = merge(userAttributes, DEFAULT_ATTRIBUTES) as Record<string, false | Record<string, false | string | boolean | number>>\n\n if (Object.keys(attributesToAdd).length === 0) {\n return dom\n }\n\n // Process each selector pattern\n for (const [selectorPattern, attributes] of Object.entries(attributesToAdd)) {\n // User opted out of this selector entirely (e.g. `table: false`)\n if (attributes === false) continue\n // Split by comma for multiple selectors\n const selectors = selectorPattern.split(',').map(s => s.trim())\n\n walk(dom, (node) => {\n const el = node as Element\n if (!el.name) return\n\n // Check if element matches any selector in the pattern\n const matches = selectors.some(selector => elementMatches(el, selector))\n\n if (matches) {\n // Initialize attribs if needed\n if (!el.attribs) {\n el.attribs = {}\n }\n\n for (const [attrName, attrValue] of Object.entries(attributes)) {\n // User opted out of this specific attribute (e.g. `role: false`)\n if (attrValue === false) continue\n // Special handling for class - merge instead of replace\n if (attrName === 'class' && el.attribs.class) {\n const existingClasses = el.attribs.class.split(/\\s+/).filter(Boolean)\n const newClasses = String(attrValue).split(/\\s+/).filter(Boolean)\n const mergedClasses = [...new Set([...existingClasses, ...newClasses])]\n if (mergedClasses.join(' ') !== el.attribs.class) {\n el.attribs.class = mergedClasses.join(' ')\n }\n } else {\n // Only add attribute if not already present\n if (!(attrName in el.attribs)) {\n el.attribs[attrName] = String(attrValue)\n }\n }\n }\n }\n })\n }\n\n return dom\n}\n\n/**\n * Check if an element matches a CSS selector.\n * Supports: tag, .class, #id, [attribute], [attribute=value]\n */\nfunction elementMatches(el: Element, selector: string): boolean {\n // Remove whitespace\n selector = selector.trim()\n\n // Check for attribute selector [attr] or [attr=value]\n const attrMatch = selector.match(/^\\[([^\\]=]+)(?:=([^\\]]*))?\\]$/)\n if (attrMatch) {\n const [, attrName, attrValue] = attrMatch\n if (attrValue === undefined) {\n // Just checking if attribute exists\n return attrName in (el.attribs || {})\n } else {\n // Check if attribute has specific value\n return el.attribs?.[attrName] === attrValue\n }\n }\n\n // Check for class selector .class\n if (selector.startsWith('.')) {\n const className = selector.slice(1)\n const classes = el.attribs?.class?.split(/\\s+/) || []\n return classes.includes(className)\n }\n\n // Check for id selector #id\n if (selector.startsWith('#')) {\n const id = selector.slice(1)\n return el.attribs?.id === id\n }\n\n // Check for tag selector (possibly with attribute)\n // Split tag from attribute if present, e.g., \"div[role=alert]\"\n const tagAttrMatch = selector.match(/^([a-z][a-z0-9]*)\\[([^\\]]+)\\]$/i)\n if (tagAttrMatch) {\n const [, tagName, attrPart] = tagAttrMatch\n if (el.name !== tagName) return false\n\n // Parse attribute part: could be \"attr\" or \"attr=value\"\n const attrEqMatch = attrPart.match(/^([^=]+)(?:=(.*))?$/)\n if (attrEqMatch) {\n const [, attrName, attrValue] = attrEqMatch\n if (attrValue === undefined) {\n return attrName in (el.attribs || {})\n } else {\n return el.attribs?.[attrName] === attrValue\n }\n }\n return false\n }\n\n // Simple tag selector\n return el.name === selector\n}\n"],"mappings":";;;;;;;AAQA,MAAM,qBAAgF;CACpF,OAAO;EACL,aAAa;EACb,aAAa;EACb,MAAM;EACP;CACD,KAAK,EACH,KAAK,IACN;CACF;;;;;;;;;;;;;;;;;;;;;;;;;AA0BD,SAAgB,cAAc,KAAkB,SAA2B,EAAE,EAAe;CAC1F,MAAM,YAAY,OAAO;CAGzB,IAAI,cAAc,OAChB,OAAO;CAKT,MAAM,kBAAkBA,OADD,OAAO,cAAc,WAAW,YAAY,EAAE,EACvB,mBAAmB;CAEjE,IAAI,OAAO,KAAK,gBAAgB,CAAC,WAAW,GAC1C,OAAO;CAIT,KAAK,MAAM,CAAC,iBAAiB,eAAe,OAAO,QAAQ,gBAAgB,EAAE;EAE3E,IAAI,eAAe,OAAO;EAE1B,MAAM,YAAY,gBAAgB,MAAM,IAAI,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC;EAE/D,KAAK,MAAM,SAAS;GAClB,MAAM,KAAK;GACX,IAAI,CAAC,GAAG,MAAM;GAKd,IAFgB,UAAU,MAAK,aAAY,eAAe,IAAI,SAAS,CAE5D,EAAE;IAEX,IAAI,CAAC,GAAG,SACN,GAAG,UAAU,EAAE;IAGjB,KAAK,MAAM,CAAC,UAAU,cAAc,OAAO,QAAQ,WAAW,EAAE;KAE9D,IAAI,cAAc,OAAO;KAEzB,IAAI,aAAa,WAAW,GAAG,QAAQ,OAAO;MAC5C,MAAM,kBAAkB,GAAG,QAAQ,MAAM,MAAM,MAAM,CAAC,OAAO,QAAQ;MACrE,MAAM,aAAa,OAAO,UAAU,CAAC,MAAM,MAAM,CAAC,OAAO,QAAQ;MACjE,MAAM,gBAAgB,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,iBAAiB,GAAG,WAAW,CAAC,CAAC;MACvE,IAAI,cAAc,KAAK,IAAI,KAAK,GAAG,QAAQ,OACzC,GAAG,QAAQ,QAAQ,cAAc,KAAK,IAAI;YAI5C,IAAI,EAAE,YAAY,GAAG,UACnB,GAAG,QAAQ,YAAY,OAAO,UAAU;;;IAKhD;;CAGJ,OAAO;;;;;;AAOT,SAAS,eAAe,IAAa,UAA2B;CAE9D,WAAW,SAAS,MAAM;CAG1B,MAAM,YAAY,SAAS,MAAM,gCAAgC;CACjE,IAAI,WAAW;EACb,MAAM,GAAG,UAAU,aAAa;EAChC,IAAI,cAAc,KAAA,GAEhB,OAAO,aAAa,GAAG,WAAW,EAAE;OAGpC,OAAO,GAAG,UAAU,cAAc;;CAKtC,IAAI,SAAS,WAAW,IAAI,EAAE;EAC5B,MAAM,YAAY,SAAS,MAAM,EAAE;EAEnC,QADgB,GAAG,SAAS,OAAO,MAAM,MAAM,IAAI,EAAE,EACtC,SAAS,UAAU;;CAIpC,IAAI,SAAS,WAAW,IAAI,EAAE;EAC5B,MAAM,KAAK,SAAS,MAAM,EAAE;EAC5B,OAAO,GAAG,SAAS,OAAO;;CAK5B,MAAM,eAAe,SAAS,MAAM,kCAAkC;CACtE,IAAI,cAAc;EAChB,MAAM,GAAG,SAAS,YAAY;EAC9B,IAAI,GAAG,SAAS,SAAS,OAAO;EAGhC,MAAM,cAAc,SAAS,MAAM,sBAAsB;EACzD,IAAI,aAAa;GACf,MAAM,GAAG,UAAU,aAAa;GAChC,IAAI,cAAc,KAAA,GAChB,OAAO,aAAa,GAAG,WAAW,EAAE;QAEpC,OAAO,GAAG,UAAU,cAAc;;EAGtC,OAAO;;CAIT,OAAO,GAAG,SAAS"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { CssConfig } from "../types/config.js";
|
|
2
1
|
import { ChildNode } from "domhandler";
|
|
3
2
|
|
|
4
3
|
//#region src/transformers/attributeToStyle.d.ts
|
|
@@ -6,20 +5,34 @@ import { ChildNode } from "domhandler";
|
|
|
6
5
|
* Convert HTML attributes to inline CSS styles.
|
|
7
6
|
*
|
|
8
7
|
* Supported attributes:
|
|
9
|
-
* - width
|
|
10
|
-
* - height
|
|
11
|
-
* - bgcolor
|
|
12
|
-
* - background
|
|
13
|
-
* - align
|
|
14
|
-
*
|
|
15
|
-
* - valign
|
|
8
|
+
* - `width`: `width: ${value}${unit}` (px and %, defaults to px)
|
|
9
|
+
* - `height`: `height: ${value}${unit}` (px and %, defaults to px)
|
|
10
|
+
* - `bgcolor`: `background-color: ${value}`
|
|
11
|
+
* - `background`: `background-image: url('${value}')`
|
|
12
|
+
* - `align`: on `<table>`, `left`/`right` become `float`, `center` becomes
|
|
13
|
+
* `margin-left/right: auto`; on other elements, becomes `text-align`
|
|
14
|
+
* - `valign`: `vertical-align: ${value}`
|
|
16
15
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* @param html HTML string to transform.
|
|
17
|
+
* @param attributes `true` to process the default set, an array to restrict
|
|
18
|
+
* to specific attribute names, `false` to disable.
|
|
19
|
+
* @returns The transformed HTML string.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* import { attributeToStyle } from '@maizzle/framework'
|
|
23
|
+
*
|
|
24
|
+
* const out = attributeToStyle('<table align="center"><tr><td bgcolor="#f00">x</td></tr></table>')
|
|
25
|
+
*
|
|
26
|
+
* // Restrict to specific attributes:
|
|
27
|
+
* const limited = attributeToStyle(html, ['width', 'height'])
|
|
28
|
+
*/
|
|
29
|
+
declare function attributeToStyle(html: string, attributes?: boolean | string[]): string;
|
|
30
|
+
/**
|
|
31
|
+
* DOM-form of {@link attributeToStyle} used by the internal transformer
|
|
32
|
+
* pipeline. Takes a parsed DOM, returns a parsed DOM — avoids redundant
|
|
33
|
+
* serialize/parse round-trips when chained with other transformers.
|
|
21
34
|
*/
|
|
22
|
-
declare function
|
|
35
|
+
declare function attributeToStyleDom(dom: ChildNode[], attributes?: boolean | string[]): ChildNode[];
|
|
23
36
|
//#endregion
|
|
24
|
-
export { attributeToStyle };
|
|
37
|
+
export { attributeToStyle, attributeToStyleDom };
|
|
25
38
|
//# sourceMappingURL=attributeToStyle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributeToStyle.d.ts","names":[],"sources":["../../src/transformers/attributeToStyle.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"attributeToStyle.d.ts","names":[],"sources":["../../src/transformers/attributeToStyle.ts"],"mappings":";;;;;AAiCA;;;;;AASA;;;;;;;;;;;;;;;;;;iBATgB,gBAAA,CAAiB,IAAA,UAAc,UAAA;;;;;;iBAS/B,mBAAA,CAAoB,GAAA,EAAK,SAAA,IAAa,UAAA,wBAAwC,SAAA"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { parse } from "../utils/ast/parser.js";
|
|
2
|
+
import { walk } from "../utils/ast/walker.js";
|
|
3
|
+
import { serialize } from "../utils/ast/serializer.js";
|
|
4
|
+
import "../utils/ast/index.js";
|
|
5
|
+
//#region src/transformers/attributeToStyle.ts
|
|
6
|
+
/**
|
|
7
|
+
* Default list of attributes that can be converted to inline styles.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_ATTRIBUTES = [
|
|
10
|
+
"width",
|
|
11
|
+
"height",
|
|
12
|
+
"bgcolor",
|
|
13
|
+
"background",
|
|
14
|
+
"align",
|
|
15
|
+
"valign"
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* Convert HTML attributes to inline CSS styles.
|
|
19
|
+
*
|
|
20
|
+
* Supported attributes:
|
|
21
|
+
* - `width`: `width: ${value}${unit}` (px and %, defaults to px)
|
|
22
|
+
* - `height`: `height: ${value}${unit}` (px and %, defaults to px)
|
|
23
|
+
* - `bgcolor`: `background-color: ${value}`
|
|
24
|
+
* - `background`: `background-image: url('${value}')`
|
|
25
|
+
* - `align`: on `<table>`, `left`/`right` become `float`, `center` becomes
|
|
26
|
+
* `margin-left/right: auto`; on other elements, becomes `text-align`
|
|
27
|
+
* - `valign`: `vertical-align: ${value}`
|
|
28
|
+
*
|
|
29
|
+
* @param html HTML string to transform.
|
|
30
|
+
* @param attributes `true` to process the default set, an array to restrict
|
|
31
|
+
* to specific attribute names, `false` to disable.
|
|
32
|
+
* @returns The transformed HTML string.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* import { attributeToStyle } from '@maizzle/framework'
|
|
36
|
+
*
|
|
37
|
+
* const out = attributeToStyle('<table align="center"><tr><td bgcolor="#f00">x</td></tr></table>')
|
|
38
|
+
*
|
|
39
|
+
* // Restrict to specific attributes:
|
|
40
|
+
* const limited = attributeToStyle(html, ['width', 'height'])
|
|
41
|
+
*/
|
|
42
|
+
function attributeToStyle(html, attributes = true) {
|
|
43
|
+
return serialize(attributeToStyleDom(parse(html), attributes));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* DOM-form of {@link attributeToStyle} used by the internal transformer
|
|
47
|
+
* pipeline. Takes a parsed DOM, returns a parsed DOM — avoids redundant
|
|
48
|
+
* serialize/parse round-trips when chained with other transformers.
|
|
49
|
+
*/
|
|
50
|
+
function attributeToStyleDom(dom, attributes = true) {
|
|
51
|
+
if (!attributes) return dom;
|
|
52
|
+
const attributesToProcess = attributes === true ? DEFAULT_ATTRIBUTES : Array.isArray(attributes) ? attributes : [];
|
|
53
|
+
if (attributesToProcess.length === 0) return dom;
|
|
54
|
+
walk(dom, (node) => {
|
|
55
|
+
const el = node;
|
|
56
|
+
if (!("attribs" in el) || !el.attribs) return;
|
|
57
|
+
const styles = [];
|
|
58
|
+
for (const attr of attributesToProcess) {
|
|
59
|
+
const value = el.attribs[attr];
|
|
60
|
+
if (!value) continue;
|
|
61
|
+
const styleValue = convertAttributeToStyle(el.name, attr, value);
|
|
62
|
+
if (styleValue) styles.push(styleValue);
|
|
63
|
+
}
|
|
64
|
+
if (styles.length > 0) {
|
|
65
|
+
const existingStyle = el.attribs.style || "";
|
|
66
|
+
const separator = existingStyle ? "; " : "";
|
|
67
|
+
el.attribs.style = existingStyle + separator + styles.join("; ");
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return dom;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Convert a single HTML attribute value to a CSS style declaration.
|
|
74
|
+
*/
|
|
75
|
+
function convertAttributeToStyle(tagName, attr, value) {
|
|
76
|
+
switch (attr) {
|
|
77
|
+
case "width":
|
|
78
|
+
case "height": return `${attr}: ${/^\d+$/.test(value) ? `${value}px` : value}`;
|
|
79
|
+
case "bgcolor": return `background-color: ${value}`;
|
|
80
|
+
case "background": return `background-image: url('${value}')`;
|
|
81
|
+
case "align":
|
|
82
|
+
if (tagName === "table") {
|
|
83
|
+
if (value === "left" || value === "right") return `float: ${value}`;
|
|
84
|
+
if (value === "center") return "margin-left: auto; margin-right: auto";
|
|
85
|
+
}
|
|
86
|
+
return `text-align: ${value}`;
|
|
87
|
+
case "valign": return `vertical-align: ${value}`;
|
|
88
|
+
default: return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { attributeToStyle, attributeToStyleDom };
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=attributeToStyle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attributeToStyle.js","names":[],"sources":["../../src/transformers/attributeToStyle.ts"],"sourcesContent":["import type { ChildNode, Element } from 'domhandler'\nimport { parse, serialize, walk } from '../utils/ast/index.ts'\n\n/**\n * Default list of attributes that can be converted to inline styles.\n */\nconst DEFAULT_ATTRIBUTES = ['width', 'height', 'bgcolor', 'background', 'align', 'valign']\n\n/**\n * Convert HTML attributes to inline CSS styles.\n *\n * Supported attributes:\n * - `width`: `width: ${value}${unit}` (px and %, defaults to px)\n * - `height`: `height: ${value}${unit}` (px and %, defaults to px)\n * - `bgcolor`: `background-color: ${value}`\n * - `background`: `background-image: url('${value}')`\n * - `align`: on `<table>`, `left`/`right` become `float`, `center` becomes\n * `margin-left/right: auto`; on other elements, becomes `text-align`\n * - `valign`: `vertical-align: ${value}`\n *\n * @param html HTML string to transform.\n * @param attributes `true` to process the default set, an array to restrict\n * to specific attribute names, `false` to disable.\n * @returns The transformed HTML string.\n *\n * @example\n * import { attributeToStyle } from '@maizzle/framework'\n *\n * const out = attributeToStyle('<table align=\"center\"><tr><td bgcolor=\"#f00\">x</td></tr></table>')\n *\n * // Restrict to specific attributes:\n * const limited = attributeToStyle(html, ['width', 'height'])\n */\nexport function attributeToStyle(html: string, attributes: boolean | string[] = true): string {\n return serialize(attributeToStyleDom(parse(html), attributes))\n}\n\n/**\n * DOM-form of {@link attributeToStyle} 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 attributeToStyleDom(dom: ChildNode[], attributes: boolean | string[] = true): ChildNode[] {\n if (!attributes) return dom\n\n const attributesToProcess: string[] = attributes === true\n ? DEFAULT_ATTRIBUTES\n : Array.isArray(attributes)\n ? attributes\n : []\n\n if (attributesToProcess.length === 0) return dom\n\n walk(dom, (node) => {\n const el = node as Element\n\n if (!('attribs' in el) || !el.attribs) {\n return\n }\n\n const styles: string[] = []\n\n for (const attr of attributesToProcess) {\n const value = el.attribs[attr]\n if (!value) continue\n\n const styleValue = convertAttributeToStyle(el.name, attr, value)\n if (styleValue) {\n styles.push(styleValue)\n }\n }\n\n // Append new styles to existing style attribute\n if (styles.length > 0) {\n const existingStyle = el.attribs.style || ''\n const separator = existingStyle ? '; ' : ''\n el.attribs.style = existingStyle + separator + styles.join('; ')\n }\n })\n\n return dom\n}\n\n/**\n * Convert a single HTML attribute value to a CSS style declaration.\n */\nfunction convertAttributeToStyle(\n tagName: string,\n attr: string,\n value: string,\n): string | null {\n switch (attr) {\n case 'width':\n case 'height': {\n // Support px and % values, default to px if no unit\n const normalizedValue = /^\\d+$/.test(value) ? `${value}px` : value\n return `${attr}: ${normalizedValue}`\n }\n\n case 'bgcolor':\n return `background-color: ${value}`\n\n case 'background':\n return `background-image: url('${value}')`\n\n case 'align': {\n // On table elements: left/right -> float, center -> margin auto\n if (tagName === 'table') {\n if (value === 'left' || value === 'right') {\n return `float: ${value}`\n }\n if (value === 'center') {\n return 'margin-left: auto; margin-right: auto'\n }\n }\n // On other elements: text-align\n return `text-align: ${value}`\n }\n\n case 'valign':\n return `vertical-align: ${value}`\n\n default:\n return null\n }\n}\n"],"mappings":";;;;;;;;AAMA,MAAM,qBAAqB;CAAC;CAAS;CAAU;CAAW;CAAc;CAAS;CAAS;;;;;;;;;;;;;;;;;;;;;;;;;;AA2B1F,SAAgB,iBAAiB,MAAc,aAAiC,MAAc;CAC5F,OAAO,UAAU,oBAAoB,MAAM,KAAK,EAAE,WAAW,CAAC;;;;;;;AAQhE,SAAgB,oBAAoB,KAAkB,aAAiC,MAAmB;CACxG,IAAI,CAAC,YAAY,OAAO;CAExB,MAAM,sBAAgC,eAAe,OACjD,qBACA,MAAM,QAAQ,WAAW,GACvB,aACA,EAAE;CAER,IAAI,oBAAoB,WAAW,GAAG,OAAO;CAE7C,KAAK,MAAM,SAAS;EAClB,MAAM,KAAK;EAEX,IAAI,EAAE,aAAa,OAAO,CAAC,GAAG,SAC5B;EAGF,MAAM,SAAmB,EAAE;EAE3B,KAAK,MAAM,QAAQ,qBAAqB;GACtC,MAAM,QAAQ,GAAG,QAAQ;GACzB,IAAI,CAAC,OAAO;GAEZ,MAAM,aAAa,wBAAwB,GAAG,MAAM,MAAM,MAAM;GAChE,IAAI,YACF,OAAO,KAAK,WAAW;;EAK3B,IAAI,OAAO,SAAS,GAAG;GACrB,MAAM,gBAAgB,GAAG,QAAQ,SAAS;GAC1C,MAAM,YAAY,gBAAgB,OAAO;GACzC,GAAG,QAAQ,QAAQ,gBAAgB,YAAY,OAAO,KAAK,KAAK;;GAElE;CAEF,OAAO;;;;;AAMT,SAAS,wBACP,SACA,MACA,OACe;CACf,QAAQ,MAAR;EACE,KAAK;EACL,KAAK,UAGH,OAAO,GAAG,KAAK,IADS,QAAQ,KAAK,MAAM,GAAG,GAAG,MAAM,MAAM;EAI/D,KAAK,WACH,OAAO,qBAAqB;EAE9B,KAAK,cACH,OAAO,0BAA0B,MAAM;EAEzC,KAAK;GAEH,IAAI,YAAY,SAAS;IACvB,IAAI,UAAU,UAAU,UAAU,SAChC,OAAO,UAAU;IAEnB,IAAI,UAAU,UACZ,OAAO;;GAIX,OAAO,eAAe;EAGxB,KAAK,UACH,OAAO,mBAAmB;EAE5B,SACE,OAAO"}
|
|
@@ -1,8 +1,71 @@
|
|
|
1
|
-
import { UrlConfig } from "../types/config.js";
|
|
2
1
|
import { ChildNode } from "domhandler";
|
|
3
2
|
|
|
4
3
|
//#region src/transformers/base.d.ts
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Options for the `base` transformer.
|
|
6
|
+
*/
|
|
7
|
+
interface BaseUrlOptions {
|
|
8
|
+
/** Base URL to prepend to relative links. */
|
|
9
|
+
url: string;
|
|
10
|
+
/**
|
|
11
|
+
* Tag/attribute scope for prepending. Omit to use the built-in defaults
|
|
12
|
+
* (`a[href]`, `img[src]`, `link[href]`, etc.).
|
|
13
|
+
*
|
|
14
|
+
* - Array of tag names — restrict the built-in defaults to these tags.
|
|
15
|
+
* - Object — explicit per-tag attribute map. Each attribute value is
|
|
16
|
+
* `true` (use the base url) or a string (use that string as the url
|
|
17
|
+
* for this attribute only).
|
|
18
|
+
*/
|
|
19
|
+
tags?: string[] | Record<string, Record<string, string | boolean>>;
|
|
20
|
+
/**
|
|
21
|
+
* Custom attributes to rewrite globally, regardless of tag. Each key
|
|
22
|
+
* is the attribute name; the value is the URL to prepend.
|
|
23
|
+
*/
|
|
24
|
+
attributes?: Record<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* Rewrite `url()` references inside `<style>` tag contents.
|
|
27
|
+
*
|
|
28
|
+
* @default true
|
|
29
|
+
*/
|
|
30
|
+
styleTag?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Rewrite `url()` references inside inline `style` attributes.
|
|
33
|
+
*
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
inlineCss?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Prepend a base URL to relative `src`/`href`/etc. references throughout
|
|
40
|
+
* the document, including inside `<style>` blocks, inline `style`
|
|
41
|
+
* attributes, MSO conditional comments, and VML tags.
|
|
42
|
+
*
|
|
43
|
+
* @param html HTML string to transform.
|
|
44
|
+
* @param options Either a base URL string, or a {@link BaseUrlOptions} object
|
|
45
|
+
* for finer control.
|
|
46
|
+
* @returns The transformed HTML string.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* import { base } from '@maizzle/framework'
|
|
50
|
+
*
|
|
51
|
+
* // Just a URL — applied with the built-in tag/attribute defaults.
|
|
52
|
+
* const out = base('<img src="/a.png">', 'https://cdn.example.com/')
|
|
53
|
+
*
|
|
54
|
+
* // Restrict to specific tags, opt out of style rewriting:
|
|
55
|
+
* const limited = base(html, {
|
|
56
|
+
* url: 'https://cdn.example.com/',
|
|
57
|
+
* tags: ['img'],
|
|
58
|
+
* styleTag: false,
|
|
59
|
+
* inlineCss: false,
|
|
60
|
+
* })
|
|
61
|
+
*/
|
|
62
|
+
declare function base(html: string, options: string | BaseUrlOptions): string;
|
|
63
|
+
/**
|
|
64
|
+
* DOM-form of {@link base} used by the internal transformer pipeline.
|
|
65
|
+
* Takes a parsed DOM, returns a parsed DOM — avoids redundant
|
|
66
|
+
* serialize/parse round-trips when chained with other transformers.
|
|
67
|
+
*/
|
|
68
|
+
declare function baseDom(dom: ChildNode[], options: string | BaseUrlOptions | undefined | null | false): ChildNode[];
|
|
6
69
|
//#endregion
|
|
7
|
-
export { base };
|
|
70
|
+
export { BaseUrlOptions, base, baseDom };
|
|
8
71
|
//# sourceMappingURL=base.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","names":[],"sources":["../../src/transformers/base.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"base.d.ts","names":[],"sources":["../../src/transformers/base.ts"],"mappings":";;;;;AAUA;UAAiB,cAAA;;EAEf,GAAA;EAUkB;;;;;;;;;EAAlB,IAAA,cAAkB,MAAA,SAAe,MAAA;EAWjC;;;;EANA,UAAA,GAAa,MAAA;EAuIK;;;;;EAjIlB,QAAA;EAiIiE;;AASnE;;;EApIE,SAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;iBA2Hc,IAAA,CAAK,IAAA,UAAc,OAAA,WAAkB,cAAA;;;;;;iBASrC,OAAA,CAAQ,GAAA,EAAK,SAAA,IAAa,OAAA,WAAkB,cAAA,8BAA4C,SAAA"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { parse } from "../utils/ast/parser.
|
|
2
|
-
import { walk } from "../utils/ast/walker.
|
|
3
|
-
import { serialize } from "../utils/ast/serializer.
|
|
4
|
-
import "../utils/ast/index.
|
|
5
|
-
import { defaultTags, isAbsoluteUrl, processSrcset } from "../utils/url.
|
|
1
|
+
import { parse } from "../utils/ast/parser.js";
|
|
2
|
+
import { walk } from "../utils/ast/walker.js";
|
|
3
|
+
import { serialize } from "../utils/ast/serializer.js";
|
|
4
|
+
import "../utils/ast/index.js";
|
|
5
|
+
import { defaultTags, isAbsoluteUrl, processSrcset } from "../utils/url.js";
|
|
6
6
|
import postcss from "postcss";
|
|
7
7
|
import safeParser from "postcss-safe-parser";
|
|
8
8
|
import valueParser from "postcss-value-parser";
|
|
9
|
-
|
|
10
9
|
//#region src/transformers/base.ts
|
|
11
10
|
const sourceAttributes = [
|
|
12
11
|
"src",
|
|
@@ -59,24 +58,19 @@ function processInlineStyle(style, url) {
|
|
|
59
58
|
return style;
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
function resolveOptions(baseUrlConfig) {
|
|
68
|
-
if (!baseUrlConfig) return void 0;
|
|
69
|
-
if (typeof baseUrlConfig === "string") return {
|
|
70
|
-
url: baseUrlConfig,
|
|
61
|
+
function resolveOptions(input) {
|
|
62
|
+
if (!input) return void 0;
|
|
63
|
+
if (typeof input === "string") return {
|
|
64
|
+
url: input,
|
|
71
65
|
styleTag: true,
|
|
72
66
|
inlineCss: true
|
|
73
67
|
};
|
|
74
|
-
if (typeof
|
|
75
|
-
url:
|
|
76
|
-
tags:
|
|
77
|
-
attributes:
|
|
78
|
-
styleTag:
|
|
79
|
-
inlineCss:
|
|
68
|
+
if (typeof input === "object" && "url" in input) return {
|
|
69
|
+
url: input.url ?? "",
|
|
70
|
+
tags: input.tags,
|
|
71
|
+
attributes: input.attributes,
|
|
72
|
+
styleTag: input.styleTag ?? true,
|
|
73
|
+
inlineCss: input.inlineCss ?? true
|
|
80
74
|
};
|
|
81
75
|
}
|
|
82
76
|
function getTagConfig(tagName, options) {
|
|
@@ -88,10 +82,42 @@ function getTagConfig(tagName, options) {
|
|
|
88
82
|
}
|
|
89
83
|
if (typeof tags === "object") return tags[tagName];
|
|
90
84
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Prepend a base URL to relative `src`/`href`/etc. references throughout
|
|
87
|
+
* the document, including inside `<style>` blocks, inline `style`
|
|
88
|
+
* attributes, MSO conditional comments, and VML tags.
|
|
89
|
+
*
|
|
90
|
+
* @param html HTML string to transform.
|
|
91
|
+
* @param options Either a base URL string, or a {@link BaseUrlOptions} object
|
|
92
|
+
* for finer control.
|
|
93
|
+
* @returns The transformed HTML string.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* import { base } from '@maizzle/framework'
|
|
97
|
+
*
|
|
98
|
+
* // Just a URL — applied with the built-in tag/attribute defaults.
|
|
99
|
+
* const out = base('<img src="/a.png">', 'https://cdn.example.com/')
|
|
100
|
+
*
|
|
101
|
+
* // Restrict to specific tags, opt out of style rewriting:
|
|
102
|
+
* const limited = base(html, {
|
|
103
|
+
* url: 'https://cdn.example.com/',
|
|
104
|
+
* tags: ['img'],
|
|
105
|
+
* styleTag: false,
|
|
106
|
+
* inlineCss: false,
|
|
107
|
+
* })
|
|
108
|
+
*/
|
|
109
|
+
function base(html, options) {
|
|
110
|
+
return serialize(baseDom(parse(html), options));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* DOM-form of {@link base} used by the internal transformer pipeline.
|
|
114
|
+
* Takes a parsed DOM, returns a parsed DOM — avoids redundant
|
|
115
|
+
* serialize/parse round-trips when chained with other transformers.
|
|
116
|
+
*/
|
|
117
|
+
function baseDom(dom, options) {
|
|
118
|
+
const resolved = resolveOptions(options);
|
|
119
|
+
if (!resolved || !resolved.url) return dom;
|
|
120
|
+
const { url: baseUrl, styleTag = true, inlineCss = true, attributes = {} } = resolved;
|
|
95
121
|
walk(dom, (node) => {
|
|
96
122
|
const el = node;
|
|
97
123
|
if (!el.name) return;
|
|
@@ -104,8 +130,8 @@ function base(dom, config = {}) {
|
|
|
104
130
|
return;
|
|
105
131
|
}
|
|
106
132
|
if (!el.attribs) return;
|
|
107
|
-
const tagConfig = getTagConfig(el.name,
|
|
108
|
-
if (tagConfig ||
|
|
133
|
+
const tagConfig = getTagConfig(el.name, resolved);
|
|
134
|
+
if (tagConfig || resolved.tags === void 0) for (const [attr, value] of Object.entries(el.attribs)) {
|
|
109
135
|
if (!value) continue;
|
|
110
136
|
const attrConfig = tagConfig?.[attr];
|
|
111
137
|
if (!attrConfig && attr !== "style") continue;
|
|
@@ -154,7 +180,7 @@ function rewriteMsoComments(html, url) {
|
|
|
154
180
|
return result;
|
|
155
181
|
});
|
|
156
182
|
}
|
|
157
|
-
|
|
158
183
|
//#endregion
|
|
159
|
-
export { base };
|
|
160
|
-
|
|
184
|
+
export { base, baseDom };
|
|
185
|
+
|
|
186
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../src/transformers/base.ts"],"sourcesContent":["import postcss from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport valueParser from 'postcss-value-parser'\nimport { walk, serialize, parse } from '../utils/ast/index.ts'\nimport { isAbsoluteUrl, defaultTags, processSrcset } from '../utils/url.ts'\nimport type { ChildNode, Element } from 'domhandler'\n\n/**\n * Options for the `base` transformer.\n */\nexport interface BaseUrlOptions {\n /** Base URL to prepend to relative links. */\n url: string\n /**\n * Tag/attribute scope for prepending. Omit to use the built-in defaults\n * (`a[href]`, `img[src]`, `link[href]`, etc.).\n *\n * - Array of tag names — restrict the built-in defaults to these tags.\n * - Object — explicit per-tag attribute map. Each attribute value is\n * `true` (use the base url) or a string (use that string as the url\n * for this attribute only).\n */\n tags?: string[] | Record<string, Record<string, string | boolean>>\n /**\n * Custom attributes to rewrite globally, regardless of tag. Each key\n * is the attribute name; the value is the URL to prepend.\n */\n attributes?: Record<string, string>\n /**\n * Rewrite `url()` references inside `<style>` tag contents.\n *\n * @default true\n */\n styleTag?: boolean\n /**\n * Rewrite `url()` references inside inline `style` attributes.\n *\n * @default true\n */\n inlineCss?: boolean\n}\n\nconst sourceAttributes = ['src', 'href', 'srcset', 'poster', 'background', 'data']\n\n/**\n * Convert the shared `defaultTags` (tag → string[]) into the richer format\n * the transformer needs (tag → Record<attr, true>).\n */\nconst defaultTagConfig: Record<string, Record<string, string | boolean>> = Object.fromEntries(\n Object.entries(defaultTags).map(([tag, attrs]) => [\n tag,\n Object.fromEntries(attrs.map(attr => [attr, true])),\n ]),\n)\n\nconst postcssBaseUrl: postcss.PluginCreator<{ url: string }> = (opts) => {\n return {\n postcssPlugin: 'postcss-base-url',\n Declaration(decl) {\n if (!decl.value.includes('url(')) return\n\n const parsed = valueParser(decl.value)\n let changed = false\n\n parsed.walk(node => {\n if (node.type !== 'function' || node.value !== 'url') return\n\n const urlNode = node.nodes[0]\n if (!urlNode) return\n\n if (isAbsoluteUrl(urlNode.value)) return\n\n urlNode.value = opts!.url + urlNode.value\n changed = true\n })\n\n if (changed) {\n decl.value = parsed.toString()\n }\n }\n }\n}\npostcssBaseUrl.postcss = true\n\nfunction processCss(css: string, url: string): string {\n const { css: result } = postcss([postcssBaseUrl({ url })]).process(css, { parser: safeParser, from: undefined })\n return result\n}\n\nfunction processInlineStyle(style: string, url: string): string {\n try {\n const { css } = postcss([postcssBaseUrl({ url })]).process(`a{${style}}`, { parser: safeParser, from: undefined })\n const match = css.match(/a\\s*\\{\\s*([\\s\\S]*?)\\s*\\}/)\n return match?.[1]?.trim() ?? style\n } catch {\n return style\n }\n}\n\nfunction resolveOptions(input: string | BaseUrlOptions | undefined | null | false): BaseUrlOptions | undefined {\n if (!input) return undefined\n if (typeof input === 'string') {\n return { url: input, styleTag: true, inlineCss: true }\n }\n if (typeof input === 'object' && 'url' in input) {\n return {\n url: input.url ?? '',\n tags: input.tags,\n attributes: input.attributes,\n styleTag: input.styleTag ?? true,\n inlineCss: input.inlineCss ?? true,\n }\n }\n return undefined\n}\n\nfunction getTagConfig(\n tagName: string,\n options: BaseUrlOptions\n): Record<string, string | boolean> | undefined {\n const { tags } = options\n\n if (tags === undefined) {\n return defaultTagConfig[tagName]\n }\n\n if (Array.isArray(tags)) {\n if (!tags.includes(tagName)) return undefined\n return defaultTagConfig[tagName]\n }\n\n if (typeof tags === 'object') {\n return tags[tagName]\n }\n\n return undefined\n}\n\n/**\n * Prepend a base URL to relative `src`/`href`/etc. references throughout\n * the document, including inside `<style>` blocks, inline `style`\n * attributes, MSO conditional comments, and VML tags.\n *\n * @param html HTML string to transform.\n * @param options Either a base URL string, or a {@link BaseUrlOptions} object\n * for finer control.\n * @returns The transformed HTML string.\n *\n * @example\n * import { base } from '@maizzle/framework'\n *\n * // Just a URL — applied with the built-in tag/attribute defaults.\n * const out = base('<img src=\"/a.png\">', 'https://cdn.example.com/')\n *\n * // Restrict to specific tags, opt out of style rewriting:\n * const limited = base(html, {\n * url: 'https://cdn.example.com/',\n * tags: ['img'],\n * styleTag: false,\n * inlineCss: false,\n * })\n */\nexport function base(html: string, options: string | BaseUrlOptions): string {\n return serialize(baseDom(parse(html), options))\n}\n\n/**\n * DOM-form of {@link base} 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 baseDom(dom: ChildNode[], options: string | BaseUrlOptions | undefined | null | false): ChildNode[] {\n const resolved = resolveOptions(options)\n if (!resolved || !resolved.url) return dom\n\n const { url: baseUrl, styleTag = true, inlineCss = true, attributes = {} } = resolved\n\n walk(dom, (node) => {\n const el = node as Element\n if (!el.name) return\n\n // Process <style> tag content with PostCSS\n if (el.name === 'style' && styleTag && el.children) {\n for (const child of el.children) {\n if (child.type === 'text') {\n const textNode = child as unknown as { data: string }\n const processed = processCss(textNode.data, baseUrl)\n if (processed !== textNode.data) {\n textNode.data = processed\n }\n }\n }\n return\n }\n\n if (!el.attribs) return\n\n // Process tag-specific attributes (respects tags filter)\n const tagConfig = getTagConfig(el.name, resolved)\n\n if (tagConfig || resolved.tags === undefined) {\n for (const [attr, value] of Object.entries(el.attribs)) {\n if (!value) continue\n\n const attrConfig = tagConfig?.[attr]\n if (!attrConfig && attr !== 'style') continue\n\n if (attr === 'srcset' && (attrConfig === true || typeof attrConfig === 'string')) {\n const newSrcset = processSrcset(value, typeof attrConfig === 'string' ? attrConfig : baseUrl)\n if (newSrcset !== value) {\n el.attribs.srcset = newSrcset\n }\n } else if (attr === 'style' && inlineCss && value.includes('url(')) {\n const newStyle = processInlineStyle(value, baseUrl)\n if (newStyle !== value) {\n el.attribs.style = newStyle\n }\n } else if (attrConfig === true && !isAbsoluteUrl(value)) {\n el.attribs[attr] = baseUrl + value\n } else if (typeof attrConfig === 'string' && !isAbsoluteUrl(value)) {\n el.attribs[attr] = attrConfig + value\n }\n }\n }\n\n // Process custom attributes (not affected by tags filter)\n for (const [attr, url] of Object.entries(attributes)) {\n if (el.attribs[attr] && !isAbsoluteUrl(el.attribs[attr])) {\n el.attribs[attr] = url + el.attribs[attr]\n }\n }\n })\n\n // VML and MSO comment rewrites require operating on serialized HTML\n // (HTML comments are not represented as traversable DOM nodes)\n const serialized = serialize(dom)\n const rewritten = rewriteMsoComments(rewriteVMLs(serialized, baseUrl), baseUrl)\n\n // Only re-parse if the regex passes actually changed anything\n if (rewritten !== serialized) {\n return parse(rewritten)\n }\n\n return dom\n}\n\nfunction rewriteVMLs(html: string, url: string): string {\n html = html.replace(/<v:image[^>]+src=\"?([^\"\\s]+)\"/gi, (match, src) => {\n if (isAbsoluteUrl(src)) return match\n return match.replace(src, url + src)\n })\n\n html = html.replace(/<v:fill[^>]+src=\"?([^\"\\s]+)\"/gi, (match, src) => {\n if (isAbsoluteUrl(src)) return match\n return match.replace(src, url + src)\n })\n\n return html\n}\n\nfunction rewriteMsoComments(html: string, url: string): string {\n return html.replace(/<!--\\[if [^\\]]+\\]>[\\s\\S]*?<!\\[endif\\]-->/g, (msoBlock) => {\n let result = msoBlock\n\n for (const attr of sourceAttributes) {\n const attrRegex = new RegExp(`\\\\b${attr}=\"([^\"]+)\"`, 'gi')\n result = result.replace(attrRegex, (match, value) => {\n if (isAbsoluteUrl(value)) return match\n\n if (attr === 'srcset') {\n return `srcset=\"${processSrcset(value, url)}\"`\n }\n\n return `${attr}=\"${url}${value}\"`\n })\n }\n\n // Use PostCSS for style attribute url() rewriting inside MSO comments\n result = result.replace(/style=\"([^\"]+)\"/gi, (match, style) => {\n if (!style.includes('url(')) return match\n const processed = processInlineStyle(style, url)\n return `style=\"${processed}\"`\n })\n\n return result\n })\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { walk } from "../utils/ast/walker.
|
|
2
|
-
import "../utils/ast/index.
|
|
1
|
+
import { walk } from "../utils/ast/walker.js";
|
|
2
|
+
import "../utils/ast/index.js";
|
|
3
3
|
import postcss from "postcss";
|
|
4
4
|
import safeParser from "postcss-safe-parser";
|
|
5
|
-
|
|
6
5
|
//#region src/transformers/columnWidth.ts
|
|
7
6
|
const RE_PERCENT = /^[\d.]+%$/;
|
|
8
7
|
const NO_BORDER_STYLES = new Set(["none", "hidden"]);
|
|
@@ -541,7 +540,7 @@ function columnWidth(dom) {
|
|
|
541
540
|
});
|
|
542
541
|
return dom;
|
|
543
542
|
}
|
|
544
|
-
|
|
545
543
|
//#endregion
|
|
546
544
|
export { columnWidth };
|
|
547
|
-
|
|
545
|
+
|
|
546
|
+
//# sourceMappingURL=columnWidth.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -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
|
-
|
|
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 world'
|
|
21
|
+
*
|
|
22
|
+
* // Add a custom mapping
|
|
23
|
+
* entities('© Maizzle', { '©': '©' })
|
|
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":"
|
|
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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { parse } from "../utils/ast/parser.js";
|
|
2
|
+
import { walk } from "../utils/ast/walker.js";
|
|
3
|
+
import { serialize } from "../utils/ast/serializer.js";
|
|
4
|
+
import "../utils/ast/index.js";
|
|
5
|
+
import { defu as defu$1 } from "defu";
|
|
6
|
+
//#region src/transformers/entities.ts
|
|
7
|
+
const DEFAULT_ENTITIES = {
|
|
8
|
+
"": "‍",
|
|
9
|
+
"": "‌",
|
|
10
|
+
"\xA0": " ",
|
|
11
|
+
"": "­",
|
|
12
|
+
"": "​",
|
|
13
|
+
" ": " ",
|
|
14
|
+
"͏": "͏",
|
|
15
|
+
" ": " ",
|
|
16
|
+
"\u2028": "&LineSeparator;",
|
|
17
|
+
"\u2029": "&ParagraphSeparator;",
|
|
18
|
+
"·": "·",
|
|
19
|
+
"–": "–",
|
|
20
|
+
"—": "—",
|
|
21
|
+
"‘": "‘",
|
|
22
|
+
"’": "’",
|
|
23
|
+
"“": "“",
|
|
24
|
+
"”": "”",
|
|
25
|
+
"«": "«",
|
|
26
|
+
"»": "»",
|
|
27
|
+
"•": "•",
|
|
28
|
+
"‹": "‹",
|
|
29
|
+
"›": "›"
|
|
30
|
+
};
|
|
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 world'
|
|
47
|
+
*
|
|
48
|
+
* // Add a custom mapping
|
|
49
|
+
* entities('© Maizzle', { '©': '©' })
|
|
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;
|
|
65
|
+
walk(dom, (node) => {
|
|
66
|
+
if (node.type === "text") for (const [char, entity] of Object.entries(map)) node.data = node.data.split(char).join(entity);
|
|
67
|
+
});
|
|
68
|
+
return dom;
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { entities, entitiesDom };
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=entities.js.map
|