@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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"compatibility.mjs","names":["compileWithPipeline"],"sources":["../../src/server/compatibility.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport postcss, { type Declaration, type AtRule, type Rule } from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport valueParser from 'postcss-value-parser'\nimport { Parser } from 'htmlparser2'\nimport { DomHandler, type ChildNode, type Element } from 'domhandler'\nimport { parseSfcBlocks, findComponentTags, buildComponentMap, type SfcBlock } from './sfc-utils.ts'\nimport { scanLint } from './linter.ts'\nimport { tailwindcss as compileWithPipeline } from '../transformers/tailwindcss.ts'\nimport type { MaizzleConfig } from '../types/index.ts'\n\nconst API_URL = 'https://www.caniemail.com/api/data.json'\nconst DEFAULT_CLIENTS = new Set(['gmail', 'apple-mail', 'outlook', 'yahoo'])\n\ntype SupportLevel = 'unsupported' | 'mitigated' | 'unknown'\n\ninterface Feature {\n slug: string\n title: string\n url: string\n category: string\n stats: any\n}\n\ninterface Issue {\n kind: 'compat' | 'lint'\n slug?: string\n title: string\n url?: string\n category: string\n line?: number\n file: string\n // compat-only\n supportLevel?: SupportLevel\n supportLabel?: string\n affectedClients?: string[]\n // lint-only\n severity?: 'error' | 'warning'\n message?: string\n}\n\ninterface Indexes {\n nicenames: { supported: string, mitigated: string, unsupported: string, unknown: string, mixed: string }\n familyNicenames: Record<string, string>\n cssProp: Map<string, Feature[]>\n cssPropValue: Map<string, Array<{ value: string, feature: Feature }>>\n cssAtRule: Map<string, Feature[]>\n cssMediaFeature: Map<string, Feature[]>\n cssPseudoClass: Map<string, Feature[]>\n cssPseudoElement: Map<string, Feature[]>\n cssFunction: Map<string, Feature[]>\n cssUnit: Map<string, Feature[]>\n cssImportant?: Feature\n cssVariables?: Feature\n cssNesting?: Feature\n cssComments?: Feature\n cssModernColor?: Feature\n htmlTag: Map<string, Feature[]>\n htmlAttr: Map<string, Feature[]>\n htmlInputType: Map<string, Feature[]>\n htmlButtonType: Map<string, Feature[]>\n htmlDoctype?: Feature\n htmlComments?: Feature\n htmlAnchorLinks?: Feature\n htmlMailtoLinks?: Feature\n htmlMetaColorScheme?: Feature\n htmlSemantics?: Feature\n htmlStyleInBody?: Feature\n imageExt: Map<string, Feature[]>\n /** All features by slug — unfiltered, used for URL lookups (e.g. by lint). */\n bySlug: Map<string, { title: string, url: string }>\n}\n\nlet indexes: Indexes | null = null\nlet initPromise: Promise<Indexes | null> | null = null\n\nfunction mpush<K, V>(m: Map<K, V[]>, k: K, v: V) {\n const arr = m.get(k)\n if (arr) arr.push(v)\n else m.set(k, [v])\n}\n\nfunction emptyIndexes(nicenames: any, familyNicenames: Record<string, string>): Indexes {\n return {\n nicenames,\n familyNicenames,\n cssProp: new Map(), cssPropValue: new Map(), cssAtRule: new Map(),\n cssMediaFeature: new Map(), cssPseudoClass: new Map(), cssPseudoElement: new Map(),\n cssFunction: new Map(), cssUnit: new Map(),\n htmlTag: new Map(), htmlAttr: new Map(), htmlInputType: new Map(), htmlButtonType: new Map(),\n imageExt: new Map(),\n bySlug: new Map(),\n }\n}\n\nfunction hasAnyNonY(stats: any): boolean {\n if (!stats) return false\n for (const family in stats) {\n for (const plat in stats[family]) {\n for (const ver in stats[family][plat]) {\n const v = stripNotes(String(stats[family][plat][ver]).trim())\n if (v && v !== 'y') return true\n }\n }\n }\n return false\n}\n\n/** Strip `#N` note markers — `\"y #1\"` → `\"y\"`. Notes document edge cases but\n * don't change support semantics, so treat `y #1` as fully supported. */\nfunction stripNotes(v: string): string {\n return v.split(/\\s+/).filter(t => t && !t.startsWith('#')).join(' ')\n}\n\nfunction computeSupport(stats: any, familyNicenames: Record<string, string>, allowedClients: Set<string> | 'all'): { level: SupportLevel, affected: string[] } | null {\n let nY = 0, nN = 0, nU = 0, nPartial = 0, total = 0\n const affectedFamilies = new Set<string>()\n for (const family in stats) {\n if (allowedClients !== 'all' && !allowedClients.has(family)) continue\n let familyHasNonY = false\n for (const plat in stats[family]) {\n // Only score the latest version per (family, platform) — legacy\n // versions (Outlook 2007, etc.) otherwise flag modern-widely-supported\n // features as partial forever.\n const versions = Object.keys(stats[family][plat]).sort()\n const latest = versions[versions.length - 1]\n if (!latest) continue\n total++\n const v = stripNotes(String(stats[family][plat][latest]).trim())\n if (v === 'y') nY++\n else if (v === 'n') { nN++; familyHasNonY = true }\n else if (v === 'u') { nU++; familyHasNonY = true }\n else { nPartial++; familyHasNonY = true }\n }\n if (familyHasNonY) affectedFamilies.add(family)\n }\n if (!total) return null\n if (nY === total) return null\n const affected = [...affectedFamilies].map(f => familyNicenames[f] ?? f).sort()\n if (nN === total) return { level: 'unsupported', affected }\n if (nU === total) return { level: 'unknown', affected }\n return { level: 'mitigated', affected }\n}\n\n/**\n * Slugs we never report. Fundamental HTML (every email uses these) plus\n * CSS noise that's not actionable (comments, !important usage).\n */\nconst IGNORED_SLUGS = new Set([\n // Required/unavoidable tags\n 'html-doctype', 'html-comments',\n 'html-html', 'html-head', 'html-body', 'html-title',\n 'html-meta', 'html-meta-color-scheme',\n 'html-style', 'html-link',\n 'html-div', 'html-span', 'html-br', 'html-p', 'html-a', 'html-img',\n 'html-table', 'html-tr', 'html-td', 'html-th',\n 'html-thead', 'html-tbody', 'html-tfoot',\n 'html-h1-h6', 'html-lists',\n 'html-strong', 'html-em', 'html-b', 'html-i', 'html-u',\n 'html-semantics',\n // Ubiquitous attributes — always present, caveats aren't actionable.\n 'html-role', 'html-hidden', 'html-width', 'html-height',\n // CSS noise\n 'css-comments', 'css-important',\n // CSS fundamentals — universally used with known minor caveats; flagging\n // them as \"partial\" is noise rather than signal.\n 'css-margin', 'css-padding', 'css-border',\n 'css-font-size', 'css-font-weight', 'css-font', 'css-font-family',\n 'css-line-height', 'css-letter-spacing', 'css-text-align',\n 'css-text-decoration', 'css-text-transform', 'css-color',\n 'css-background', 'css-background-color',\n 'css-width', 'css-height',\n 'css-display',\n])\n\nfunction classify(f: Feature, idx: Indexes) {\n const slug = f.slug\n // Retain html-style feature for the body-only detector even though it's\n // blacklisted from the normal html-tag detection path. Title is suffixed\n // so the flag reads as a body-placement warning, not a blanket `<style>`.\n if (slug === 'html-style') {\n idx.htmlStyleInBody = { ...f, title: `${f.title} in <body>` }\n return\n }\n if (IGNORED_SLUGS.has(slug)) return\n\n if (f.category === 'css') return classifyCss(f, slug, idx)\n if (f.category === 'html') return classifyHtml(f, slug, idx)\n if (f.category === 'image') {\n const ext = slug.slice('image-'.length)\n if (ext === 'base64') return\n mpush(idx.imageExt, ext, f)\n }\n // 'others' (amp, bimi) intentionally skipped\n}\n\nfunction classifyCss(f: Feature, slug: string, idx: Indexes) {\n // Specials first\n switch (slug) {\n case 'css-important': idx.cssImportant = f; return\n case 'css-variables': idx.cssVariables = f; return\n case 'css-nesting': idx.cssNesting = f; return\n case 'css-comments': idx.cssComments = f; return\n case 'css-modern-color': idx.cssModernColor = f; return\n case 'css-display-flex': mpushPropValue(idx, 'display', 'flex', f); return\n case 'css-display-grid': mpushPropValue(idx, 'display', 'grid', f); return\n case 'css-display-none': mpushPropValue(idx, 'display', 'none', f); return\n case 'css-rgb': mpush(idx.cssFunction, 'rgb', f); return\n case 'css-rgba': mpush(idx.cssFunction, 'rgba', f); return\n case 'css-linear-gradient': mpush(idx.cssFunction, 'linear-gradient', f); return\n case 'css-radial-gradient': mpush(idx.cssFunction, 'radial-gradient', f); return\n case 'css-conic-gradient': mpush(idx.cssFunction, 'conic-gradient', f); return\n }\n\n if (slug.startsWith('css-at-media-') && slug !== 'css-at-media') {\n mpush(idx.cssMediaFeature, slug.slice('css-at-media-'.length), f)\n return\n }\n if (slug.startsWith('css-at-')) {\n mpush(idx.cssAtRule, slug.slice('css-at-'.length), f)\n return\n }\n if (slug.startsWith('css-pseudo-class-')) {\n mpush(idx.cssPseudoClass, slug.slice('css-pseudo-class-'.length), f)\n return\n }\n if (slug.startsWith('css-pseudo-element-')) {\n mpush(idx.cssPseudoElement, slug.slice('css-pseudo-element-'.length), f)\n return\n }\n if (slug.startsWith('css-unit-')) {\n const u = slug.slice('css-unit-'.length)\n if (u === 'calc') { mpush(idx.cssFunction, 'calc', f); return }\n if (u === 'initial') return // keyword detection is noisy; skip\n const unit = u === 'percent' ? '%' : u\n mpush(idx.cssUnit, unit, f)\n return\n }\n if (slug.startsWith('css-function-')) {\n mpush(idx.cssFunction, slug.slice('css-function-'.length), f)\n return\n }\n // css-selector-* — skip (too broad to detect meaningfully)\n if (slug.startsWith('css-selector-')) return\n\n // Fallback: treat as property name\n mpush(idx.cssProp, slug.slice('css-'.length), f)\n}\n\nfunction mpushPropValue(idx: Indexes, prop: string, value: string, f: Feature) {\n const arr = idx.cssPropValue.get(prop)\n if (arr) arr.push({ value, feature: f })\n else idx.cssPropValue.set(prop, [{ value, feature: f }])\n}\n\nconst HTML_ATTR_SLUGS = new Set([\n 'align', 'background', 'cellpadding', 'cellspacing', 'height', 'width',\n 'valign', 'target', 'srcset', 'lang', 'dir', 'role', 'required', 'hidden',\n])\n\nfunction classifyHtml(f: Feature, slug: string, idx: Indexes) {\n // Specials\n switch (slug) {\n case 'html-doctype': idx.htmlDoctype = f; return\n case 'html-comments': idx.htmlComments = f; return\n case 'html-anchor-links': idx.htmlAnchorLinks = f; return\n case 'html-mailto-links': idx.htmlMailtoLinks = f; return\n case 'html-meta-color-scheme': idx.htmlMetaColorScheme = f; return\n case 'html-semantics': idx.htmlSemantics = f; return\n case 'html-loading-attribute': mpush(idx.htmlAttr, 'loading', f); return\n case 'html-image-maps':\n mpush(idx.htmlTag, 'map', f); mpush(idx.htmlTag, 'area', f); mpush(idx.htmlAttr, 'usemap', f); return\n case 'html-lists':\n for (const t of ['ul', 'ol', 'li', 'dl', 'dt', 'dd']) mpush(idx.htmlTag, t, f)\n return\n case 'html-h1-h6':\n for (const t of ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']) mpush(idx.htmlTag, t, f)\n return\n }\n\n if (slug.startsWith('html-input-')) {\n mpush(idx.htmlInputType, slug.slice('html-input-'.length), f)\n return\n }\n if (slug.startsWith('html-button-')) {\n mpush(idx.htmlButtonType, slug.slice('html-button-'.length), f)\n return\n }\n if (slug.startsWith('html-aria-')) {\n mpush(idx.htmlAttr, slug.slice('html-'.length), f)\n return\n }\n const name = slug.slice('html-'.length)\n if (HTML_ATTR_SLUGS.has(name)) {\n mpush(idx.htmlAttr, name, f)\n return\n }\n mpush(idx.htmlTag, name, f)\n}\n\nexport async function initCompatibility(): Promise<Indexes | null> {\n if (indexes) return indexes\n if (initPromise) return initPromise\n initPromise = (async () => {\n try {\n const res = await fetch(API_URL)\n if (!res.ok) return null\n const data = await res.json()\n const idx = emptyIndexes(data.nicenames?.support ?? {}, data.nicenames?.family ?? {})\n for (const item of data.data ?? []) {\n // Record every slug's title/url so lint can look up caniemail pages\n // for issues that map to a known feature, even ignored ones.\n if (item.slug && item.url) idx.bySlug.set(item.slug, { title: item.title, url: item.url })\n // Index the feature if any cell anywhere in the matrix is non-y.\n // Per-request aggregation (with the active client filter) decides\n // whether to actually surface the issue.\n if (!hasAnyNonY(item.stats)) continue\n const f: Feature = {\n slug: item.slug,\n title: item.title,\n url: item.url,\n category: item.category,\n stats: item.stats,\n }\n classify(f, idx)\n }\n indexes = idx\n return idx\n } catch {\n return null\n }\n })()\n return initPromise\n}\n\n// Note: fetch of the caniemail dataset is lazy — it fires on the first\n// check request, not at module load, so `server.checks: false` pays no\n// network cost.\n\ninterface FileStreams {\n path: string\n source: string\n template: SfcBlock | null\n styles: SfcBlock[]\n classes: Set<string>\n}\n\nfunction collectStreams(\n filePath: string,\n componentMap: Map<string, string>,\n visited: Set<string>,\n out: FileStreams[],\n) {\n if (visited.has(filePath)) return\n visited.add(filePath)\n\n let source: string\n try {\n source = readFileSync(filePath, 'utf-8')\n } catch { return }\n\n const { template, styles } = parseSfcBlocks(source)\n const classes = new Set<string>()\n if (template) extractClasses(template.content, classes)\n\n out.push({ path: filePath, source, template, styles, classes })\n\n if (template) {\n for (const tag of findComponentTags(template.content)) {\n const cp = componentMap.get(tag.toLowerCase())\n if (cp) collectStreams(cp, componentMap, visited, out)\n }\n }\n}\n\nfunction extractClasses(html: string, out: Set<string>) {\n const parser = new Parser({\n onopentag(_tag, attrs) {\n const c = attrs.class\n if (!c) return\n for (const t of c.split(/\\s+/)) if (t) out.add(t)\n },\n }, { decodeEntities: true })\n parser.write(html)\n parser.end()\n}\n\nfunction parseWithIndices(html: string): ChildNode[] {\n const handler = new DomHandler(undefined, { withStartIndices: true })\n const parser = new Parser(handler)\n parser.write(html)\n parser.end()\n return handler.dom\n}\n\nfunction findStyleNodes(nodes: ChildNode[], out: Element[] = []): Element[] {\n for (const n of nodes) {\n const el = n as Element\n if (el.name === 'style') out.push(el)\n if (el.children?.length) findStyleNodes(el.children as ChildNode[], out)\n }\n return out\n}\n\n/**\n * Parse each file's template, collect every `<style>` node with its source\n * line (via htmlparser2 start indices), then pass the combined DOM through\n * the framework's real Tailwind pipeline. The pipeline resolves imports\n * (@maizzle/tailwindcss), compiles utilities from class attrs, lowers modern\n * CSS via lightningcss, and resolves static calc() — so what we walk matches\n * what ships.\n */\nasync function compileViaPipeline(\n streams: FileStreams[],\n config: MaizzleConfig,\n rootFile: string,\n): Promise<Array<{ file: string, css: string, line: number }>> {\n const all: ChildNode[] = []\n const tracked: Array<{ node: Element, file: string, line: number }> = []\n\n for (const s of streams) {\n if (!s.template) continue\n const templateStart = s.source.indexOf(s.template.content)\n const nodes = parseWithIndices(s.template.content)\n for (const styleNode of findStyleNodes(nodes)) {\n const startIdx = (styleNode as any).startIndex ?? 0\n const line = offsetToLine(s.source, templateStart + startIdx)\n tracked.push({ node: styleNode, file: s.path, line })\n }\n for (const n of nodes) all.push(n)\n }\n\n if (!tracked.length) return []\n\n try {\n await compileWithPipeline(all, config, rootFile)\n } catch { return [] }\n\n return tracked\n .map(t => {\n const txt = t.node.children?.find(c => (c as any).type === 'text') as any\n return txt?.data ? { file: t.file, css: txt.data as string, line: t.line } : null\n })\n .filter((x): x is { file: string, css: string, line: number } => x !== null)\n}\n\n/**\n * Walk CSS AST with detectors. Calls onHit per feature hit.\n * `selector` is the containing rule's selector (undefined if no rule ancestor).\n */\nfunction walkCss(\n css: string,\n idx: Indexes,\n onHit: (feature: Feature, node: { line?: number, selector?: string }) => void,\n) {\n let root: postcss.Root\n try { root = safeParser(css) } catch { return }\n\n const containingSelector = (n: postcss.Node | undefined): string | undefined => {\n let p = n?.parent\n while (p && p.type !== 'root') {\n if (p.type === 'rule') return (p as Rule).selector\n p = p.parent\n }\n return undefined\n }\n\n if (idx.cssComments) {\n root.walkComments((c) => { onHit(idx.cssComments!, { line: c.source?.start?.line, selector: containingSelector(c) }) })\n }\n\n root.walkAtRules((atRule: AtRule) => {\n const line = atRule.source?.start?.line\n let sel = containingSelector(atRule)\n if (atRule.name === 'media' && !sel) {\n const innerSelectors: string[] = []\n atRule.walkRules((r) => { innerSelectors.push(r.selector) })\n if (innerSelectors.length) sel = innerSelectors.join(', ')\n }\n\n if (atRule.name === 'media') {\n // Pick the most specific media-feature match (prefers-color-scheme,\n // hover, orientation, …). If one matches, skip the generic `css-at-media`\n // to avoid duplicate rows pointing at the same line.\n const specific: Feature[] = []\n if (idx.cssMediaFeature.size) {\n for (const [feat, fs2] of idx.cssMediaFeature) {\n if (atRule.params.includes(`(${feat}`) || atRule.params.includes(feat)) {\n specific.push(...fs2)\n }\n }\n }\n if (specific.length) {\n for (const f of specific) onHit(f, { line, selector: sel })\n } else {\n const fs = idx.cssAtRule.get('media')\n if (fs) for (const f of fs) onHit(f, { line, selector: sel })\n }\n } else {\n const fs = idx.cssAtRule.get(atRule.name)\n if (fs) for (const f of fs) onHit(f, { line, selector: sel })\n }\n })\n\n root.walkRules((rule: Rule) => {\n const line = rule.source?.start?.line\n const sel = rule.selector\n if (idx.cssPseudoClass.size) {\n for (const [name, fs] of idx.cssPseudoClass) {\n const re = new RegExp(`(^|[^:]):${escapeRe(name)}(\\\\b|\\\\()`)\n if (re.test(sel)) for (const f of fs) onHit(f, { line, selector: sel })\n }\n }\n if (idx.cssPseudoElement.size) {\n for (const [name, fs] of idx.cssPseudoElement) {\n const re = new RegExp(`::${escapeRe(name)}\\\\b`)\n if (re.test(sel)) for (const f of fs) onHit(f, { line, selector: sel })\n }\n }\n })\n\n root.walkDecls((decl: Declaration) => {\n const line = decl.source?.start?.line\n const sel = containingSelector(decl)\n const prop = decl.prop\n\n if (idx.cssImportant && decl.important) onHit(idx.cssImportant, { line, selector: sel })\n if (idx.cssVariables && prop.startsWith('--')) onHit(idx.cssVariables, { line, selector: sel })\n\n const fs = idx.cssProp.get(prop)\n if (fs) for (const f of fs) onHit(f, { line, selector: sel })\n\n const pvs = idx.cssPropValue.get(prop)\n if (pvs) {\n const v = decl.value.trim().toLowerCase()\n for (const pv of pvs) if (v === pv.value) onHit(pv.feature, { line, selector: sel })\n }\n\n if (idx.cssFunction.size || idx.cssUnit.size || idx.cssVariables || idx.cssModernColor) {\n try {\n valueParser(decl.value).walk((n) => {\n if (n.type === 'function') {\n const fname = n.value.toLowerCase()\n const fs2 = idx.cssFunction.get(fname)\n if (fs2) for (const f of fs2) onHit(f, { line, selector: sel })\n if (idx.cssVariables && fname === 'var') onHit(idx.cssVariables, { line, selector: sel })\n if (idx.cssModernColor && MODERN_COLOR_FNS.has(fname)) onHit(idx.cssModernColor, { line, selector: sel })\n } else if (n.type === 'word') {\n const m = /^-?\\d*\\.?\\d+([a-z%]+)$/i.exec(n.value)\n if (m) {\n const unit = m[1].toLowerCase()\n const fs2 = idx.cssUnit.get(unit)\n if (fs2) for (const f of fs2) onHit(f, { line, selector: sel })\n }\n }\n })\n } catch {}\n }\n })\n}\n\nconst MODERN_COLOR_FNS = new Set(['oklch', 'oklab', 'lch', 'lab', 'color', 'color-mix', 'hwb'])\n\nfunction escapeRe(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nfunction offsetToLine(source: string, offset: number): number {\n let line = 1\n for (let i = 0; i < offset && i < source.length; i++) {\n if (source.charCodeAt(i) === 10) line++\n }\n return line\n}\n\nfunction walkTemplate(\n html: string,\n idx: Indexes,\n fileLineOffset: number,\n source: string,\n templateStartOffset: number,\n onHit: (feature: Feature, line: number) => void,\n) {\n const semanticTags = new Set(['article', 'aside', 'details', 'figcaption', 'figure',\n 'footer', 'header', 'main', 'mark', 'nav', 'section', 'time', 'summary'])\n\n // Stack of tags that opened a body-scope: a literal <body> or a\n // <Teleport to=\"body...\"> whose rendered contents land inside body.\n const bodyScopeStack: string[] = []\n const parser = new Parser({\n onopentag(tag, attrs) {\n const startIdx = (parser as any).startIndex as number\n const line = offsetToLine(source, templateStartOffset + startIdx)\n\n const tagFs = idx.htmlTag.get(tag)\n if (tagFs) for (const f of tagFs) onHit(f, line)\n\n if (idx.htmlSemantics && semanticTags.has(tag)) onHit(idx.htmlSemantics, line)\n\n if (tag === 'style' && bodyScopeStack.length > 0 && idx.htmlStyleInBody) {\n onHit(idx.htmlStyleInBody, line)\n }\n if (tag === 'body') bodyScopeStack.push(tag)\n else if (tag === 'teleport' && /body/i.test(attrs.to ?? '')) bodyScopeStack.push(tag)\n\n for (const attr in attrs) {\n const attrFs = idx.htmlAttr.get(attr)\n if (attrFs) for (const f of attrFs) onHit(f, line)\n }\n\n if (tag === 'input' && attrs.type) {\n const fs = idx.htmlInputType.get(attrs.type.toLowerCase())\n if (fs) for (const f of fs) onHit(f, line)\n }\n if (tag === 'button' && attrs.type) {\n const fs = idx.htmlButtonType.get(attrs.type.toLowerCase())\n if (fs) for (const f of fs) onHit(f, line)\n }\n if (tag === 'a' && attrs.href) {\n const h = attrs.href.trim()\n if (idx.htmlMailtoLinks && /^mailto:/i.test(h)) onHit(idx.htmlMailtoLinks, line)\n else if (idx.htmlAnchorLinks && h.startsWith('#')) onHit(idx.htmlAnchorLinks, line)\n }\n if (tag === 'meta' && idx.htmlMetaColorScheme\n && attrs.name?.toLowerCase() === 'color-scheme') onHit(idx.htmlMetaColorScheme, line)\n\n // Image formats via src / srcset\n if (idx.imageExt.size && (attrs.src || attrs.srcset)) {\n const urls: string[] = []\n if (attrs.src) urls.push(attrs.src)\n if (attrs.srcset) for (const part of attrs.srcset.split(',')) urls.push(part.trim().split(/\\s+/)[0])\n for (const url of urls) {\n const m = /\\.([a-z0-9]+)(?:\\?|#|$)/i.exec(url)\n if (!m) continue\n const fs = idx.imageExt.get(m[1].toLowerCase())\n if (fs) for (const f of fs) onHit(f, line)\n }\n }\n\n // inline style attribute → scan as CSS decl list\n if (attrs.style) scanInlineStyle(attrs.style, idx, line, onHit)\n },\n onclosetag(tag) {\n const top = bodyScopeStack[bodyScopeStack.length - 1]\n if (top === tag) bodyScopeStack.pop()\n },\n onprocessinginstruction(name) {\n if (idx.htmlDoctype && name.toLowerCase() === '!doctype') {\n const startIdx = (parser as any).startIndex as number\n onHit(idx.htmlDoctype, offsetToLine(source, templateStartOffset + startIdx))\n }\n },\n oncomment() {\n if (idx.htmlComments) {\n const startIdx = (parser as any).startIndex as number\n onHit(idx.htmlComments, offsetToLine(source, templateStartOffset + startIdx))\n }\n },\n }, { decodeEntities: false, lowerCaseTags: true, lowerCaseAttributeNames: true })\n\n parser.write(html)\n parser.end()\n}\n\nfunction scanInlineStyle(\n style: string,\n idx: Indexes,\n line: number,\n onHit: (feature: Feature, line: number) => void,\n) {\n // Wrap in a rule so safeParser produces a Root with declarations\n const wrapped = `*{${style}}`\n try {\n const root = safeParser(wrapped)\n root.walkDecls((decl) => {\n if (idx.cssImportant && decl.important) onHit(idx.cssImportant, line)\n const fs = idx.cssProp.get(decl.prop)\n if (fs) for (const f of fs) onHit(f, line)\n if (idx.cssVariables && decl.prop.startsWith('--')) onHit(idx.cssVariables, line)\n const pvs = idx.cssPropValue.get(decl.prop)\n if (pvs) {\n const v = decl.value.trim().toLowerCase()\n for (const pv of pvs) if (v === pv.value) onHit(pv.feature, line)\n }\n if (idx.cssFunction.size || idx.cssUnit.size || idx.cssVariables || idx.cssModernColor) {\n try {\n valueParser(decl.value).walk((n) => {\n if (n.type === 'function') {\n const fname = n.value.toLowerCase()\n const fs2 = idx.cssFunction.get(fname)\n if (fs2) for (const f of fs2) onHit(f, line)\n if (idx.cssVariables && fname === 'var') onHit(idx.cssVariables, line)\n if (idx.cssModernColor && MODERN_COLOR_FNS.has(fname)) onHit(idx.cssModernColor, line)\n } else if (n.type === 'word') {\n const m = /^-?\\d*\\.?\\d+([a-z%]+)$/i.exec(n.value)\n if (m) {\n const fs2 = idx.cssUnit.get(m[1].toLowerCase())\n if (fs2) for (const f of fs2) onHit(f, line)\n }\n }\n })\n } catch {}\n }\n })\n } catch {}\n}\n\nfunction labelFor(idx: Indexes, level: SupportLevel): string {\n const n = idx.nicenames\n if (level === 'unsupported') return n.unsupported ?? 'Not supported'\n if (level === 'mitigated') return n.mitigated ?? 'Partially supported'\n return n.unknown ?? 'Support unknown'\n}\n\nasync function scan(\n rootFile: string,\n config: MaizzleConfig,\n componentDirs: string[],\n allowedClients: Set<string> | 'all',\n): Promise<Issue[]> {\n const idx = await initCompatibility()\n if (!idx) return []\n\n const root = config.root ?? process.cwd()\n const componentMap = await buildComponentMap(root, componentDirs)\n const streams: FileStreams[] = []\n collectStreams(rootFile, componentMap, new Set(), streams)\n\n const issues: Issue[] = []\n const seen = new Set<string>()\n const resolvedCache = new Map<string, { level: SupportLevel, affected: string[] } | null>()\n const resolveSupport = (f: Feature) => {\n let cached = resolvedCache.get(f.slug)\n if (cached === undefined) {\n cached = computeSupport(f.stats, idx.familyNicenames, allowedClients)\n resolvedCache.set(f.slug, cached)\n }\n return cached\n }\n const add = (f: Feature, file: string, line?: number) => {\n const key = `${f.slug}|${file}|${line ?? 0}`\n if (seen.has(key)) return\n const support = resolveSupport(f)\n if (!support) return\n seen.add(key)\n issues.push({\n kind: 'compat',\n slug: f.slug, title: f.title, url: f.url, category: f.category,\n supportLevel: support.level, supportLabel: labelFor(idx, support.level),\n affectedClients: support.affected,\n line, file,\n })\n }\n\n // Stream A: compiled CSS from real pipeline — reflects shipped output\n // (Tailwind utilities resolved, @maizzle/tailwindcss imported, calc\n // resolved, modern CSS lowered). Filter hits whose containing selector\n // doesn't reference a user class — drops Tailwind preflight noise.\n // For hits without a class selector (e.g. @media, user-written rules),\n // attribute to the file that owned the style block.\n const compiledBlocks = await compileViaPipeline(streams, config, rootFile)\n for (const block of compiledBlocks) {\n walkCss(block.css, idx, (feature, node) => {\n const locations = classLocations(node.selector, streams)\n if (!locations.length) {\n add(feature, block.file, block.line)\n return\n }\n // @media features collapse to a single source line: the first usage\n // of whatever class/variant triggered the wrapper. Other features\n // show up for every occurrence.\n if (feature.slug.startsWith('css-at-media')) {\n add(feature, locations[0].file, locations[0].line)\n } else {\n for (const { file, line } of locations) add(feature, file, line)\n }\n })\n }\n\n // Stream C: source template per file\n for (const s of streams) {\n if (!s.template) continue\n walkTemplate(s.template.content, idx, s.template.offset, s.source,\n s.source.indexOf(s.template.content),\n (feature, line) => add(feature, s.path, line))\n }\n\n return issues\n}\n\n/**\n * Return every (file, line) where any class from the selector appears in a\n * template. Scans every stream so a shared utility class used in multiple\n * components surfaces once per occurrence.\n */\nfunction classLocations(\n selector: string | undefined,\n streams: FileStreams[],\n): Array<{ file: string, line: number }> {\n if (!selector) return []\n const classNames = extractSelectorClasses(selector)\n if (!classNames.length) return []\n const out: Array<{ file: string, line: number }> = []\n const seen = new Set<string>()\n for (const cn of classNames) {\n for (const s of streams) {\n if (!s.classes.has(cn) || !s.template) continue\n const tpl = s.template.content\n const tplStart = s.source.indexOf(tpl)\n let pos = 0\n while (true) {\n const i = tpl.indexOf(cn, pos)\n if (i < 0) break\n pos = i + cn.length\n // Whole-word boundary: adjacent char must be whitespace or quote\n const before = i > 0 ? tpl[i - 1] : ' '\n const after = i + cn.length < tpl.length ? tpl[i + cn.length] : ' '\n if (!isClassBoundary(before) || !isClassBoundary(after)) continue\n const line = offsetToLine(s.source, tplStart + i)\n const key = `${s.path}|${line}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push({ file: s.path, line })\n }\n }\n }\n return out\n}\n\nfunction isClassBoundary(c: string): boolean {\n return c === ' ' || c === '\\t' || c === '\\n' || c === '\\r' || c === '\"' || c === \"'\"\n}\n\nfunction extractSelectorClasses(selector: string): string[] {\n const out: string[] = []\n const re = /\\.((?:\\\\.|[\\w-])+)/g\n let m\n while ((m = re.exec(selector)) !== null) {\n out.push(m[1].replace(/\\\\(.)/g, '$1'))\n }\n return out\n}\n\nconst CATEGORY_ORDER = ['css', 'html', 'image', 'others']\nconst LEVEL_ORDER: Record<string, number> = { error: 0, unsupported: 1, warning: 2, mitigated: 3, unknown: 4 }\n\nfunction orderKey(i: Issue): number {\n if (i.kind === 'lint') return LEVEL_ORDER[i.severity!] ?? 99\n return LEVEL_ORDER[i.supportLevel!] ?? 99\n}\n\nfunction resolveChecksConfig(config: MaizzleConfig) {\n const raw = (config as any).server?.checks\n if (raw === false) return null\n const clients: Set<string> | 'all' = raw?.clients === 'all'\n ? 'all'\n : Array.isArray(raw?.clients) && raw.clients.length\n ? new Set(raw.clients as string[])\n : DEFAULT_CLIENTS\n const level: 'error' | 'warning' | 'lint' | null = raw?.level ?? null\n return { clients, level }\n}\n\nfunction passesLevelFilter(issue: Issue, level: 'error' | 'warning' | 'lint' | null): boolean {\n if (!level) return true\n if (level === 'lint') return issue.kind === 'lint'\n if (issue.kind === 'lint') {\n return level === 'error' ? issue.severity === 'error' : issue.severity === 'warning'\n }\n // compat\n return level === 'error'\n ? issue.supportLevel === 'unsupported'\n : issue.supportLevel === 'mitigated' || issue.supportLevel === 'unknown'\n}\n\nexport async function serveCompatibility(\n url: string,\n res: any,\n config: MaizzleConfig,\n componentDirs: string[],\n) {\n const filePath = url.replace('/__maizzle/compatibility/', '').replace(/\\?.*$/, '')\n const checksCfg = resolveChecksConfig(config)\n try {\n res.setHeader('Content-Type', 'application/json')\n if (!checksCfg) {\n // Defensive: UI hides the tab using window.__MAIZZLE_CONFIG__ so it\n // shouldn't reach this endpoint when disabled, but if something else\n // does, return an empty list.\n res.end(JSON.stringify([]))\n return\n }\n const absolutePath = resolve(filePath)\n const [compatIssues, lintIssues] = await Promise.all([\n scan(absolutePath, config, componentDirs, checksCfg.clients),\n scanLint(absolutePath, config, componentDirs),\n ])\n\n const idx = await initCompatibility()\n const lintAsIssues: Issue[] = lintIssues.map((li) => {\n const info = li.slug ? idx?.bySlug.get(li.slug) : undefined\n return {\n kind: 'lint',\n slug: li.slug,\n title: li.title,\n url: info?.url,\n category: li.category,\n severity: li.type,\n message: li.message,\n line: li.line,\n file: li.file,\n }\n })\n\n let issues: Issue[] = [...compatIssues, ...lintAsIssues]\n if (checksCfg.level) issues = issues.filter((i) => passesLevelFilter(i, checksCfg.level))\n issues.sort((a, b) => {\n const c = CATEGORY_ORDER.indexOf(a.category) - CATEGORY_ORDER.indexOf(b.category)\n if (c) return c\n const l = orderKey(a) - orderKey(b)\n if (l) return l\n return (a.slug ?? a.title).localeCompare(b.slug ?? b.title)\n })\n res.end(JSON.stringify(issues))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"email.mjs","names":[],"sources":["../../src/server/email.ts"],"sourcesContent":["import nodemailer from 'nodemailer'\nimport type { MaizzleConfig } from '../types/index.ts'\n\nexport interface SendEmailPayload {\n to: string[]\n subject: string\n html: string\n text?: string\n}\n\ninterface SendEmailResponse {\n success: boolean\n message: string\n previewUrl?: string\n}\n\nexport async function sendEmail(\n payload: SendEmailPayload,\n config: MaizzleConfig,\n templateConfig: MaizzleConfig,\n): Promise<SendEmailResponse> {\n // Template-level config takes priority over global config\n const emailConfig = templateConfig.server?.email ?? config.server?.email\n\n let transport: nodemailer.Transporter\n let isEthereal = false\n\n if (emailConfig?.transport) {\n transport = nodemailer.createTransport(emailConfig.transport as any)\n } else {\n // Fallback to Ethereal\n const testAccount = await nodemailer.createTestAccount()\n transport = nodemailer.createTransport({\n host: 'smtp.ethereal.email',\n port: 587,\n secure: false,\n auth: {\n user: testAccount.user,\n pass: testAccount.pass,\n },\n })\n isEthereal = true\n }\n\n const from = emailConfig?.from ?? 'Maizzle <maizzle@ethereal.email>'\n\n const info = await transport.sendMail({\n from,\n to: payload.to.join(', '),\n subject: payload.subject || 'Test email',\n html: payload.html,\n text: payload.text || undefined,\n })\n\n const previewUrl = isEthereal ? nodemailer.getTestMessageUrl(info) || undefined : undefined\n\n const recipient = payload.to.length === 1\n ? payload.to[0]\n : `${payload.to.length} recipients`\n\n return {\n success: true,\n message: isEthereal\n ? 'Sent via Ethereal'\n : `Sent to ${recipient}`,\n previewUrl: typeof previewUrl === 'string' ? previewUrl : undefined,\n }\n}\n"],"mappings":";;;AAgBA,eAAsB,UACpB,SACA,QACA,gBAC4B;CAE5B,MAAM,cAAc,eAAe,QAAQ,SAAS,OAAO,QAAQ;CAEnE,IAAI;CACJ,IAAI,aAAa;AAEjB,KAAI,aAAa,UACf,aAAY,WAAW,gBAAgB,YAAY,UAAiB;MAC/D;EAEL,MAAM,cAAc,MAAM,WAAW,mBAAmB;AACxD,cAAY,WAAW,gBAAgB;GACrC,MAAM;GACN,MAAM;GACN,QAAQ;GACR,MAAM;IACJ,MAAM,YAAY;IAClB,MAAM,YAAY;IACnB;GACF,CAAC;AACF,eAAa;;CAGf,MAAM,OAAO,aAAa,QAAQ;CAElC,MAAM,OAAO,MAAM,UAAU,SAAS;EACpC;EACA,IAAI,QAAQ,GAAG,KAAK,KAAK;EACzB,SAAS,QAAQ,WAAW;EAC5B,MAAM,QAAQ;EACd,MAAM,QAAQ,QAAQ;EACvB,CAAC;CAEF,MAAM,aAAa,aAAa,WAAW,kBAAkB,KAAK,IAAI,SAAY;CAElF,MAAM,YAAY,QAAQ,GAAG,WAAW,IACpC,QAAQ,GAAG,KACX,GAAG,QAAQ,GAAG,OAAO;AAEzB,QAAO;EACL,SAAS;EACT,SAAS,aACL,sBACA,WAAW;EACf,YAAY,OAAO,eAAe,WAAW,aAAa;EAC3D"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"linter.mjs","names":[],"sources":["../../src/server/linter.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { parseSfcBlocks, findComponentTags, buildComponentMap } from './sfc-utils.ts'\nimport type { MaizzleConfig } from '../types/index.ts'\n\nexport interface LintIssue {\n type: 'error' | 'warning'\n title: string\n message: string\n /** Which tab this lands in when merged into the Checks panel. */\n category: 'css' | 'html' | 'image' | 'others'\n /** Optional caniemail slug for URL enrichment (e.g. \"html-html\"). */\n slug?: string\n line?: number\n file: string\n}\n\ninterface Presence {\n html: boolean\n head: boolean\n body: boolean\n}\n\n/**\n * Maizzle auto-adds role=\"none\" to every <table> by default via the\n * addAttributes transformer. Warn about missing role only when that won't\n * happen:\n * - useTransformers: false → whole pipeline off\n * - html.attributes.add: false → auto-add disabled globally\n * - add.table: false → table selector opted out\n * - add.table.role: false → role attribute specifically opted out\n * An empty `add.table: {}` still inherits role via defu merge, so it's fine.\n */\nfunction tableRoleAutoAdded(config: MaizzleConfig): boolean {\n if (config.useTransformers === false) return false\n const add = config.html?.attributes?.add\n if (add === false) return false\n if (!add || typeof add !== 'object') return true\n const table = (add as any).table\n if (table === false) return false\n if (table && typeof table === 'object' && table.role === false) return false\n return true\n}\n\nexport async function scanLint(\n rootFile: string,\n config: MaizzleConfig,\n componentDirs: string[],\n): Promise<LintIssue[]> {\n const root = config.root ?? process.cwd()\n const componentMap = await buildComponentMap(root, componentDirs)\n const visited = new Set<string>()\n const presence: Presence = { html: false, head: false, body: false }\n const checkTableRole = !tableRoleAutoAdded(config)\n const issues = checkFile(rootFile, componentMap, visited, presence, checkTableRole)\n\n if (!presence.html) issues.push({ type: 'warning', category: 'html', title: 'Missing <html>', message: 'Root <html> tag not found in the template or any of its components.', slug: 'html-html', line: 1, file: rootFile })\n if (!presence.head) issues.push({ type: 'warning', category: 'html', title: 'Missing <head>', message: 'Root <head> tag not found in the template or any of its components.', slug: 'html-head', line: 1, file: rootFile })\n if (!presence.body) issues.push({ type: 'warning', category: 'html', title: 'Missing <body>', message: 'Root <body> tag not found in the template or any of its components.', slug: 'html-body', line: 1, file: rootFile })\n\n return issues\n}\n\nexport async function serveLint(url: string, res: any, config: MaizzleConfig, componentDirs: string[]) {\n const filePath = url.replace('/__maizzle/lint/', '').replace(/\\?.*$/, '')\n\n try {\n const absolutePath = resolve(filePath)\n const issues = await scanLint(absolutePath, config, componentDirs)\n\n // Sort: errors first, then warnings, then by line\n issues.sort((a, b) => {\n if (a.type !== b.type) return a.type === 'error' ? -1 : 1\n return (a.line ?? 0) - (b.line ?? 0)\n })\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(issues))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n\nfunction checkFile(\n filePath: string,\n componentMap: Map<string, string>,\n visited: Set<string>,\n presence: Presence,\n checkTableRole: boolean,\n): LintIssue[] {\n if (visited.has(filePath)) return []\n visited.add(filePath)\n\n let source: string\n try {\n source = readFileSync(filePath, 'utf-8')\n } catch {\n return []\n }\n\n const { template } = parseSfcBlocks(source)\n const issues: LintIssue[] = []\n\n if (template) {\n issues.push(...lintHtml(template.content, template.offset, filePath, presence, checkTableRole))\n\n // Recurse into components\n const componentTags = findComponentTags(template.content)\n for (const tag of componentTags) {\n const componentPath = componentMap.get(tag.toLowerCase())\n if (componentPath) {\n issues.push(...checkFile(componentPath, componentMap, visited, presence, checkTableRole))\n }\n }\n }\n\n return issues\n}\n\nfunction lineAt(html: string, offset: number, lineOffset: number): number {\n return html.slice(0, offset).split('\\n').length + lineOffset\n}\n\n/**\n * True if the <img> tag has a width defined via any of:\n * - `width` attribute\n * - inline `style` with a `width` property\n * - class attribute with a Tailwind `w-` utility (any variant prefix like\n * sm:, hover:), or an arbitrary `[width:…]` utility\n */\nfunction hasWidthDefined(imgTag: string): boolean {\n if (/\\bwidth\\s*=/i.test(imgTag)) return true\n\n const styleMatch = imgTag.match(/\\bstyle\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (styleMatch && /(^|[;\\s])width\\s*:/i.test(styleMatch[1])) return true\n\n const classMatch = imgTag.match(/\\bclass\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (classMatch) {\n const classes = classMatch[1]\n if (/(?:^|\\s)(?:[a-z0-9-]+:)*w-\\S+/i.test(classes)) return true\n if (/\\[width:/i.test(classes)) return true\n }\n return false\n}\n\nfunction lintHtml(html: string, lineOffset: number, filePath: string, presence: Presence, checkTableRole: boolean): LintIssue[] {\n const issues: LintIssue[] = []\n\n // Match all tags (multiline) — [^>] doesn't cross > so use [\\s\\S] with lazy quantifier\n const tagRe = /<([a-zA-Z][a-zA-Z0-9]*)\\b([\\s\\S]*?)>/g\n\n for (const m of Array.from(html.matchAll(tagRe))) {\n const tag = m[0]\n const tagName = m[1].toLowerCase()\n const line = lineAt(html, m.index!, lineOffset)\n\n if (tagName === 'html') presence.html = true\n else if (tagName === 'head') presence.head = true\n else if (tagName === 'body') presence.body = true\n\n // Layout tables — accessibility requires role=\"none\" so screen readers\n // skip the table structure. Only surface the warning when the user has\n // disabled Maizzle's auto-role-add; otherwise every build-step output\n // already has role=\"none\" set.\n if (checkTableRole && tagName === 'table') {\n const roleMatch = tag.match(/\\brole\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (!roleMatch) {\n const tableEndIdx = html.indexOf('</table>', m.index!)\n const inner = tableEndIdx >= 0 ? html.slice(m.index!, tableEndIdx) : ''\n const isDataTable = /<th\\b/i.test(inner) || /<caption\\b/i.test(inner)\n if (!isDataTable) {\n issues.push({ type: 'warning', category: 'html', title: 'Layout table missing role', message: 'Add role=\"none\" so screen readers skip this layout table.', slug: 'html-role', line, file: filePath })\n }\n }\n }\n\n // Images\n if (tagName === 'img') {\n if (!/\\balt\\s*=/i.test(tag)) {\n issues.push({ type: 'warning', category: 'image', title: 'Missing alt text', message: 'Image is missing the alt attribute', line, file: filePath })\n }\n\n const srcMatch = tag.match(/\\bsrc\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (!srcMatch) {\n issues.push({ type: 'error', category: 'image', title: 'Missing image src', message: 'Image tag has no src attribute', line, file: filePath })\n } else if (!srcMatch[1].trim()) {\n issues.push({ type: 'error', category: 'image', title: 'Empty image src', message: 'Image src attribute is empty', line, file: filePath })\n } else if (srcMatch[1].trim().startsWith('http:')) {\n issues.push({ type: 'warning', category: 'image', title: 'Insecure image src', message: 'Image loads over HTTP instead of HTTPS', line, file: filePath })\n }\n\n if (!hasWidthDefined(tag)) {\n issues.push({ type: 'warning', category: 'image', title: 'Missing image width', message: 'Use a `width=\"\"` attribute for best results in Outlook', line, file: filePath })\n }\n }\n\n // Any tag with href — skip resource tags handled below\n if (!['link', 'script', 'source'].includes(tagName)) {\n const hrefMatch = tag.match(/\\bhref\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (hrefMatch) {\n const href = hrefMatch[1].trim()\n if (!href) {\n issues.push({ type: 'error', category: 'html', title: 'Empty link href', message: 'Link href attribute is empty', line, file: filePath })\n } else if (href === '#' || href === '/') {\n issues.push({ type: 'error', category: 'html', title: 'Placeholder link', message: `Link href is \"${href}\"`, line, file: filePath })\n } else if (href.startsWith('http:')) {\n issues.push({ type: 'warning', category: 'html', title: 'Insecure link', message: 'Link uses HTTP instead of HTTPS', line, file: filePath })\n } else if (href.startsWith('http') && !/^https?:\\/\\/.+\\..+/i.test(href)) {\n issues.push({ type: 'error', category: 'html', title: 'Invalid link', message: `Link href \"${href}\" looks malformed`, line, file: filePath })\n }\n }\n }\n\n // Insecure resources (<link>, <script>, <source>)\n if (['link', 'script', 'source'].includes(tagName)) {\n const attrMatch = tag.match(/\\b(?:href|src)\\s*=\\s*[\"']([^\"']*)[\"']/i)\n if (attrMatch && attrMatch[1].trim().startsWith('http:')) {\n issues.push({ type: 'warning', category: 'html', title: 'Insecure resource', message: 'Resource loads over HTTP instead of HTTPS', line, file: filePath })\n }\n }\n }\n\n // Insecure CSS url() references\n for (const m of Array.from(html.matchAll(/url\\s*\\(\\s*[\"']?(http:[^\"')]+)[\"']?\\s*\\)/gi))) {\n issues.push({ type: 'warning', category: 'css', title: 'Insecure CSS url()', message: 'CSS url() loads over HTTP instead of HTTPS', line: lineAt(html, m.index!, lineOffset), file: filePath })\n }\n\n // Check for unclosed tags (block-level and common inline elements)\n const voidElements = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n ])\n\n const trackedTags = new Set([\n 'a', 'b', 'body', 'div', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'head', 'html', 'i', 'li', 'ol', 'p', 'span', 'strong', 'style',\n 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'title', 'tr', 'u', 'ul',\n ])\n\n const stack: Array<{ tag: string, line: number }> = []\n\n // Strip comments and content inside <style>/<script> to avoid false matches\n const stripped = html\n .replace(/<!--[\\s\\S]*?-->/g, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n .replace(/<(style|script)\\b[^>]*>[\\s\\S]*?<\\/\\1>/gi, (m) => '\\n'.repeat((m.match(/\\n/g) || []).length))\n\n const strippedLines = stripped.split('\\n')\n\n for (let i = 0; i < strippedLines.length; i++) {\n const line = strippedLines[i]\n const tagRegex = /<\\/?([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*\\/?>/g\n let m\n\n while ((m = tagRegex.exec(line)) !== null) {\n const fullMatch = m[0]\n const tagName = m[1].toLowerCase()\n\n if (!trackedTags.has(tagName) || voidElements.has(tagName)) continue\n if (fullMatch.endsWith('/>')) continue\n\n if (fullMatch.startsWith('</')) {\n // Closing tag\n let lastOpen = -1\n for (let j = stack.length - 1; j >= 0; j--) {\n if (stack[j].tag === tagName) { lastOpen = j; break }\n }\n if (lastOpen !== -1) {\n stack.splice(lastOpen, 1)\n }\n } else {\n // Opening tag\n stack.push({ tag: tagName, line: i + 1 + lineOffset })\n }\n }\n }\n\n for (const unclosed of stack) {\n issues.push({\n type: 'error',\n category: 'html',\n title: 'Unclosed tag',\n message: `<${unclosed.tag}> tag is not closed`,\n line: unclosed.line,\n file: filePath,\n })\n }\n\n return issues\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sfc-utils.mjs","names":[],"sources":["../../src/server/sfc-utils.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { resolve, dirname, basename } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { glob } from 'tinyglobby'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nexport interface SfcBlock {\n content: string\n offset: number\n}\n\nexport function parseSfcBlocks(source: string): { template: SfcBlock | null, styles: SfcBlock[] } {\n let template: SfcBlock | null = null\n const styles: SfcBlock[] = []\n\n const templateMatch = source.match(/<template\\b[^>]*>([\\s\\S]*)<\\/template>/)\n if (templateMatch) {\n const contentStart = source.indexOf(templateMatch[0]) + templateMatch[0].indexOf(templateMatch[1])\n const offset = source.slice(0, contentStart).split('\\n').length - 1\n template = { content: templateMatch[1], offset }\n }\n\n const styleRe = /<style\\b([^>]*)>([\\s\\S]*?)<\\/style>/g\n let m\n while ((m = styleRe.exec(source)) !== null) {\n // Skip preprocessor styles (scss, less, etc.) — caniemail only parses plain CSS\n if (/\\blang\\s*=\\s*[\"'](?!css)/i.test(m[1])) continue\n\n const contentStart = m.index + m[0].indexOf(m[2])\n const offset = source.slice(0, contentStart).split('\\n').length - 1\n styles.push({ content: m[2], offset })\n }\n\n return { template, styles }\n}\n\n/**\n * Standard HTML elements — anything not in this set is treated as a component.\n */\nexport const HTML_ELEMENTS = new Set([\n 'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base',\n 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption',\n 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del',\n 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset',\n 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',\n 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'img',\n 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', 'main', 'map',\n 'mark', 'menu', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol',\n 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q',\n 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'search', 'section', 'select',\n 'slot', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary',\n 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th',\n 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr',\n])\n\nexport function findComponentTags(templateContent: string): string[] {\n const tags = new Set<string>()\n\n // PascalCase tags like <Section>, <Button>\n const pascalRe = /<([A-Z][a-zA-Z0-9]*)\\b/g\n let m\n while ((m = pascalRe.exec(templateContent)) !== null) {\n tags.add(m[1])\n }\n\n // kebab-case tags like <my-component>\n const kebabRe = /<([a-z][a-z0-9]*(?:-[a-z0-9]+)+)\\b/g\n while ((m = kebabRe.exec(templateContent)) !== null) {\n if (!HTML_ELEMENTS.has(m[1])) {\n tags.add(m[1])\n }\n }\n\n return [...tags]\n}\n\nexport async function buildComponentMap(root: string, componentDirs: string[]): Promise<Map<string, string>> {\n const map = new Map<string, string>()\n\n const dirs = [\n resolve(__dirname, '../components'),\n resolve(root, 'components'),\n ...componentDirs,\n ].filter(d => existsSync(d))\n\n for (const dir of dirs) {\n const files = await glob(['**/*.vue'], { cwd: dir, absolute: true })\n for (const file of files) {\n const name = basename(file, '.vue')\n // Store lowercased for case-insensitive matching\n map.set(name.toLowerCase(), file)\n }\n }\n\n return map\n}\n"],"mappings":";;;;;;AAKA,MAAM,YAAY,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAOzD,SAAgB,eAAe,QAAmE;CAChG,IAAI,WAA4B;CAChC,MAAM,SAAqB,EAAE;CAE7B,MAAM,gBAAgB,OAAO,MAAM,yCAAyC;AAC5E,KAAI,eAAe;EACjB,MAAM,eAAe,OAAO,QAAQ,cAAc,GAAG,GAAG,cAAc,GAAG,QAAQ,cAAc,GAAG;EAClG,MAAM,SAAS,OAAO,MAAM,GAAG,aAAa,CAAC,MAAM,KAAK,CAAC,SAAS;AAClE,aAAW;GAAE,SAAS,cAAc;GAAI;GAAQ;;CAGlD,MAAM,UAAU;CAChB,IAAI;AACJ,SAAQ,IAAI,QAAQ,KAAK,OAAO,MAAM,MAAM;AAE1C,MAAI,4BAA4B,KAAK,EAAE,GAAG,CAAE;EAE5C,MAAM,eAAe,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE,GAAG;EACjD,MAAM,SAAS,OAAO,MAAM,GAAG,aAAa,CAAC,MAAM,KAAK,CAAC,SAAS;AAClE,SAAO,KAAK;GAAE,SAAS,EAAE;GAAI;GAAQ,CAAC;;AAGxC,QAAO;EAAE;EAAU;EAAQ;;;;;AAM7B,MAAa,gBAAgB,IAAI,IAAI;CACnC;CAAK;CAAQ;CAAW;CAAQ;CAAW;CAAS;CAAS;CAAK;CAClE;CAAO;CAAO;CAAc;CAAQ;CAAM;CAAU;CAAU;CAC9D;CAAQ;CAAQ;CAAO;CAAY;CAAQ;CAAY;CAAM;CAC7D;CAAW;CAAO;CAAU;CAAO;CAAM;CAAM;CAAM;CAAS;CAC9D;CAAc;CAAU;CAAU;CAAQ;CAAM;CAAM;CAAM;CAAM;CAClE;CAAM;CAAQ;CAAU;CAAU;CAAM;CAAQ;CAAK;CAAU;CAC/D;CAAS;CAAO;CAAO;CAAS;CAAU;CAAM;CAAQ;CAAQ;CAChE;CAAQ;CAAQ;CAAQ;CAAS;CAAO;CAAY;CAAU;CAC9D;CAAY;CAAU;CAAU;CAAK;CAAW;CAAO;CAAY;CACnE;CAAM;CAAM;CAAQ;CAAK;CAAQ;CAAU;CAAU;CAAW;CAChE;CAAQ;CAAS;CAAU;CAAQ;CAAU;CAAS;CAAO;CAC7D;CAAO;CAAS;CAAS;CAAM;CAAY;CAAY;CAAS;CAChE;CAAS;CAAQ;CAAS;CAAM;CAAS;CAAK;CAAM;CAAO;CAAS;CACrE,CAAC;AAEF,SAAgB,kBAAkB,iBAAmC;CACnE,MAAM,uBAAO,IAAI,KAAa;CAG9B,MAAM,WAAW;CACjB,IAAI;AACJ,SAAQ,IAAI,SAAS,KAAK,gBAAgB,MAAM,KAC9C,MAAK,IAAI,EAAE,GAAG;CAIhB,MAAM,UAAU;AAChB,SAAQ,IAAI,QAAQ,KAAK,gBAAgB,MAAM,KAC7C,KAAI,CAAC,cAAc,IAAI,EAAE,GAAG,CAC1B,MAAK,IAAI,EAAE,GAAG;AAIlB,QAAO,CAAC,GAAG,KAAK;;AAGlB,eAAsB,kBAAkB,MAAc,eAAuD;CAC3G,MAAM,sBAAM,IAAI,KAAqB;CAErC,MAAM,OAAO;EACX,QAAQ,WAAW,gBAAgB;EACnC,QAAQ,MAAM,aAAa;EAC3B,GAAG;EACJ,CAAC,QAAO,MAAK,WAAW,EAAE,CAAC;AAE5B,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,MAAM,KAAK,CAAC,WAAW,EAAE;GAAE,KAAK;GAAK,UAAU;GAAM,CAAC;AACpE,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,OAAO,SAAS,MAAM,OAAO;AAEnC,OAAI,IAAI,KAAK,aAAa,EAAE,KAAK;;;AAIrC,QAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"addAttributes.mjs","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;AAGzB,KAAI,cAAc,MAChB,QAAO;CAKT,MAAM,kBAAkBA,KADD,OAAO,cAAc,WAAW,YAAY,EAAE,EACvB,mBAAmB;AAEjE,KAAI,OAAO,KAAK,gBAAgB,CAAC,WAAW,EAC1C,QAAO;AAIT,MAAK,MAAM,CAAC,iBAAiB,eAAe,OAAO,QAAQ,gBAAgB,EAAE;AAE3E,MAAI,eAAe,MAAO;EAE1B,MAAM,YAAY,gBAAgB,MAAM,IAAI,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC;AAE/D,OAAK,MAAM,SAAS;GAClB,MAAM,KAAK;AACX,OAAI,CAAC,GAAG,KAAM;AAKd,OAFgB,UAAU,MAAK,aAAY,eAAe,IAAI,SAAS,CAAC,EAE3D;AAEX,QAAI,CAAC,GAAG,QACN,IAAG,UAAU,EAAE;AAGjB,SAAK,MAAM,CAAC,UAAU,cAAc,OAAO,QAAQ,WAAW,EAAE;AAE9D,SAAI,cAAc,MAAO;AAEzB,SAAI,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;AACvE,UAAI,cAAc,KAAK,IAAI,KAAK,GAAG,QAAQ,MACzC,IAAG,QAAQ,QAAQ,cAAc,KAAK,IAAI;gBAIxC,EAAE,YAAY,GAAG,SACnB,IAAG,QAAQ,YAAY,OAAO,UAAU;;;IAKhD;;AAGJ,QAAO;;;;;;AAOT,SAAS,eAAe,IAAa,UAA2B;AAE9D,YAAW,SAAS,MAAM;CAG1B,MAAM,YAAY,SAAS,MAAM,gCAAgC;AACjE,KAAI,WAAW;EACb,MAAM,GAAG,UAAU,aAAa;AAChC,MAAI,cAAc,OAEhB,QAAO,aAAa,GAAG,WAAW,EAAE;MAGpC,QAAO,GAAG,UAAU,cAAc;;AAKtC,KAAI,SAAS,WAAW,IAAI,EAAE;EAC5B,MAAM,YAAY,SAAS,MAAM,EAAE;AAEnC,UADgB,GAAG,SAAS,OAAO,MAAM,MAAM,IAAI,EAAE,EACtC,SAAS,UAAU;;AAIpC,KAAI,SAAS,WAAW,IAAI,EAAE;EAC5B,MAAM,KAAK,SAAS,MAAM,EAAE;AAC5B,SAAO,GAAG,SAAS,OAAO;;CAK5B,MAAM,eAAe,SAAS,MAAM,kCAAkC;AACtE,KAAI,cAAc;EAChB,MAAM,GAAG,SAAS,YAAY;AAC9B,MAAI,GAAG,SAAS,QAAS,QAAO;EAGhC,MAAM,cAAc,SAAS,MAAM,sBAAsB;AACzD,MAAI,aAAa;GACf,MAAM,GAAG,UAAU,aAAa;AAChC,OAAI,cAAc,OAChB,QAAO,aAAa,GAAG,WAAW,EAAE;OAEpC,QAAO,GAAG,UAAU,cAAc;;AAGtC,SAAO;;AAIT,QAAO,GAAG,SAAS"}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { walk } from "../utils/ast/walker.mjs";
|
|
2
|
-
import "../utils/ast/index.mjs";
|
|
3
|
-
|
|
4
|
-
//#region src/transformers/attributeToStyle.ts
|
|
5
|
-
/**
|
|
6
|
-
* Default list of attributes that can be converted to inline styles.
|
|
7
|
-
*/
|
|
8
|
-
const DEFAULT_ATTRIBUTES = [
|
|
9
|
-
"width",
|
|
10
|
-
"height",
|
|
11
|
-
"bgcolor",
|
|
12
|
-
"background",
|
|
13
|
-
"align",
|
|
14
|
-
"valign"
|
|
15
|
-
];
|
|
16
|
-
/**
|
|
17
|
-
* Convert HTML attributes to inline CSS styles.
|
|
18
|
-
*
|
|
19
|
-
* Supported attributes:
|
|
20
|
-
* - width: converted to `width: ${value}${unit}` (supports px and %, defaults to px)
|
|
21
|
-
* - height: converted to `height: ${value}${unit}` (supports px and %, defaults to px)
|
|
22
|
-
* - bgcolor: converted to `background-color: ${value}`
|
|
23
|
-
* - background: converted to `background-image: url('${value}')`
|
|
24
|
-
* - align: on `<table>` elements, `left`/`right` become `float`, `center` becomes `margin: 0 auto`;
|
|
25
|
-
* on other elements, becomes `text-align: ${value}`
|
|
26
|
-
* - valign: converted to `vertical-align: ${value}`
|
|
27
|
-
*
|
|
28
|
-
* Enabled via `config.css.inline.attributeToStyle`:
|
|
29
|
-
* - `true`: process all default attributes
|
|
30
|
-
* - `false` or `undefined`: disabled (returns html unchanged)
|
|
31
|
-
* - `string[]`: process only the specified attributes
|
|
32
|
-
*/
|
|
33
|
-
function attributeToStyle(dom, config = {}) {
|
|
34
|
-
const inline = config.inline;
|
|
35
|
-
if (typeof inline !== "object" || inline === null) return dom;
|
|
36
|
-
const option = inline.attributeToStyle;
|
|
37
|
-
if (!option) return dom;
|
|
38
|
-
const attributesToProcess = option === true ? DEFAULT_ATTRIBUTES : Array.isArray(option) ? option : [];
|
|
39
|
-
if (attributesToProcess.length === 0) return dom;
|
|
40
|
-
walk(dom, (node) => {
|
|
41
|
-
const el = node;
|
|
42
|
-
if (!("attribs" in el) || !el.attribs) return;
|
|
43
|
-
const styles = [];
|
|
44
|
-
for (const attr of attributesToProcess) {
|
|
45
|
-
const value = el.attribs[attr];
|
|
46
|
-
if (!value) continue;
|
|
47
|
-
const styleValue = convertAttributeToStyle(el.name, attr, value);
|
|
48
|
-
if (styleValue) styles.push(styleValue);
|
|
49
|
-
}
|
|
50
|
-
if (styles.length > 0) {
|
|
51
|
-
const existingStyle = el.attribs.style || "";
|
|
52
|
-
const separator = existingStyle ? "; " : "";
|
|
53
|
-
el.attribs.style = existingStyle + separator + styles.join("; ");
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
return dom;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Convert a single HTML attribute value to a CSS style declaration.
|
|
60
|
-
*/
|
|
61
|
-
function convertAttributeToStyle(tagName, attr, value) {
|
|
62
|
-
switch (attr) {
|
|
63
|
-
case "width":
|
|
64
|
-
case "height": return `${attr}: ${/^\d+$/.test(value) ? `${value}px` : value}`;
|
|
65
|
-
case "bgcolor": return `background-color: ${value}`;
|
|
66
|
-
case "background": return `background-image: url('${value}')`;
|
|
67
|
-
case "align":
|
|
68
|
-
if (tagName === "table") {
|
|
69
|
-
if (value === "left" || value === "right") return `float: ${value}`;
|
|
70
|
-
if (value === "center") return "margin-left: auto; margin-right: auto";
|
|
71
|
-
}
|
|
72
|
-
return `text-align: ${value}`;
|
|
73
|
-
case "valign": return `vertical-align: ${value}`;
|
|
74
|
-
default: return null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
//#endregion
|
|
79
|
-
export { attributeToStyle };
|
|
80
|
-
//# sourceMappingURL=attributeToStyle.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"attributeToStyle.mjs","names":[],"sources":["../../src/transformers/attributeToStyle.ts"],"sourcesContent":["import type { ChildNode, Element } from 'domhandler'\nimport { walk } from '../utils/ast/index.ts'\nimport type { CssConfig } from '../types/config.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: converted to `width: ${value}${unit}` (supports px and %, defaults to px)\n * - height: converted to `height: ${value}${unit}` (supports px and %, defaults to px)\n * - bgcolor: converted to `background-color: ${value}`\n * - background: converted to `background-image: url('${value}')`\n * - align: on `<table>` elements, `left`/`right` become `float`, `center` becomes `margin: 0 auto`;\n * on other elements, becomes `text-align: ${value}`\n * - valign: converted to `vertical-align: ${value}`\n *\n * Enabled via `config.css.inline.attributeToStyle`:\n * - `true`: process all default attributes\n * - `false` or `undefined`: disabled (returns html unchanged)\n * - `string[]`: process only the specified attributes\n */\nexport function attributeToStyle(dom: ChildNode[], config: CssConfig = {}): ChildNode[] {\n const inline = config.inline\n\n // Disabled when inline is a boolean or undefined\n if (typeof inline !== 'object' || inline === null) {\n return dom\n }\n\n const option = inline.attributeToStyle\n\n // Disabled when not set or explicitly false\n if (!option) {\n return dom\n }\n\n // Determine which attributes to process\n const attributesToProcess: string[] =\n option === true\n ? DEFAULT_ATTRIBUTES\n : Array.isArray(option)\n ? option\n : []\n\n if (attributesToProcess.length === 0) {\n return dom\n }\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":";;;;;;;AAOA,MAAM,qBAAqB;CAAC;CAAS;CAAU;CAAW;CAAc;CAAS;CAAS;;;;;;;;;;;;;;;;;;AAmB1F,SAAgB,iBAAiB,KAAkB,SAAoB,EAAE,EAAe;CACtF,MAAM,SAAS,OAAO;AAGtB,KAAI,OAAO,WAAW,YAAY,WAAW,KAC3C,QAAO;CAGT,MAAM,SAAS,OAAO;AAGtB,KAAI,CAAC,OACH,QAAO;CAIT,MAAM,sBACJ,WAAW,OACP,qBACA,MAAM,QAAQ,OAAO,GACnB,SACA,EAAE;AAEV,KAAI,oBAAoB,WAAW,EACjC,QAAO;AAGT,MAAK,MAAM,SAAS;EAClB,MAAM,KAAK;AAEX,MAAI,EAAE,aAAa,OAAO,CAAC,GAAG,QAC5B;EAGF,MAAM,SAAmB,EAAE;AAE3B,OAAK,MAAM,QAAQ,qBAAqB;GACtC,MAAM,QAAQ,GAAG,QAAQ;AACzB,OAAI,CAAC,MAAO;GAEZ,MAAM,aAAa,wBAAwB,GAAG,MAAM,MAAM,MAAM;AAChE,OAAI,WACF,QAAO,KAAK,WAAW;;AAK3B,MAAI,OAAO,SAAS,GAAG;GACrB,MAAM,gBAAgB,GAAG,QAAQ,SAAS;GAC1C,MAAM,YAAY,gBAAgB,OAAO;AACzC,MAAG,QAAQ,QAAQ,gBAAgB,YAAY,OAAO,KAAK,KAAK;;GAElE;AAEF,QAAO;;;;;AAMT,SAAS,wBACP,SACA,MACA,OACe;AACf,SAAQ,MAAR;EACE,KAAK;EACL,KAAK,SAGH,QAAO,GAAG,KAAK,IADS,QAAQ,KAAK,MAAM,GAAG,GAAG,MAAM,MAAM;EAI/D,KAAK,UACH,QAAO,qBAAqB;EAE9B,KAAK,aACH,QAAO,0BAA0B,MAAM;EAEzC,KAAK;AAEH,OAAI,YAAY,SAAS;AACvB,QAAI,UAAU,UAAU,UAAU,QAChC,QAAO,UAAU;AAEnB,QAAI,UAAU,SACZ,QAAO;;AAIX,UAAO,eAAe;EAGxB,KAAK,SACH,QAAO,mBAAmB;EAE5B,QACE,QAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"base.mjs","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'\nimport type { UrlConfig } from '../types/config.ts'\n\ninterface BaseUrlOptions {\n url: string\n tags?: string[] | Record<string, Record<string, string | boolean>>\n attributes?: Record<string, string>\n styleTag?: boolean\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 getBaseUrl(config: UrlConfig): string | BaseUrlOptions | undefined {\n const baseUrlConfig = config.base\n if (!baseUrlConfig || baseUrlConfig === '') {\n return undefined\n }\n return baseUrlConfig as string | BaseUrlOptions | undefined\n}\n\nfunction resolveOptions(baseUrlConfig: string | BaseUrlOptions | undefined): BaseUrlOptions | undefined {\n if (!baseUrlConfig) return undefined\n if (typeof baseUrlConfig === 'string') {\n return { url: baseUrlConfig, styleTag: true, inlineCss: true }\n }\n if (typeof baseUrlConfig === 'object' && 'url' in baseUrlConfig) {\n return {\n url: baseUrlConfig.url ?? '',\n tags: baseUrlConfig.tags,\n attributes: baseUrlConfig.attributes,\n styleTag: baseUrlConfig.styleTag ?? true,\n inlineCss: baseUrlConfig.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\nexport function base(dom: ChildNode[], config: UrlConfig = {}): ChildNode[] {\n const baseUrlConfig = getBaseUrl(config)\n const options = resolveOptions(baseUrlConfig)\n\n if (!options || !options.url) {\n return dom\n }\n\n const { url: baseUrl, styleTag = true, inlineCss = true, attributes = {} } = options\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, options)\n\n if (tagConfig || options.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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"columnWidth.mjs","names":[],"sources":["../../src/transformers/columnWidth.ts"],"sourcesContent":["import postcss, { type Root, type Declaration } from 'postcss'\nimport safeParser from 'postcss-safe-parser'\nimport { walk } from '../utils/ast/index.ts'\nimport type { ChildNode, Element, ParentNode } from 'domhandler'\n\nconst RE_PERCENT = /^[\\d.]+%$/\nconst NO_BORDER_STYLES = new Set(['none', 'hidden'])\n\n/**\n * Stringify decls into a `; `-joined inline-style attribute. PostCSS raws\n * preserve the original source spacing, which mixes poorly with\n * the fresh decls we inject — plain join keeps output uniform.\n */\nfunction serializeStyle(root: Root): string {\n const parts: string[] = []\n root.walkDecls((d) => {\n parts.push(`${d.prop}: ${d.value}${d.important ? ' !important' : ''}`)\n })\n return parts.join('; ')\n}\n\nfunction firstDeclValue(root: Root, prop: string): string | undefined {\n let found: string | undefined\n root.walkDecls(prop, (d) => {\n found = d.value\n return false\n })\n return found\n}\n\n/**\n * Find the user-set `min-width:` value on a column. Juice keeps both ours\n * and the one inlined from a class like `min-w-1/3` — we skip any\n * min-width whose value still contains our placeholder token,\n * returning the first remaining user value, or null.\n */\nfunction findUserMinWidth(root: Root): string | null {\n let userVal: string | null = null\n root.walkDecls('min-width', (d) => {\n if (!d.value.includes('__MAIZZLE_COLW_')) {\n userVal = d.value\n return false\n }\n })\n return userVal\n}\n\nfunction resolveLength(value: string): string | null {\n const trimmed = value.trim()\n if (RE_PERCENT.test(trimmed)) return trimmed\n const m = trimmed.match(/^([\\d.]+)(px|rem|em|pt)?$/i)\n if (!m) return null\n const n = parseFloat(m[1])\n switch ((m[2] || 'px').toLowerCase()) {\n case 'px': return `${Math.round(n)}px`\n case 'rem':\n case 'em': return `${Math.round(n * 16)}px`\n case 'pt': return `${Math.round(n * 1.333)}px`\n default: return null\n }\n}\n\nfunction lengthToPx(value: string): number | null {\n const m = value.trim().match(/^([\\d.]+)(px|rem|em|pt)?$/i)\n if (!m) return null\n const n = parseFloat(m[1])\n switch ((m[2] || 'px').toLowerCase()) {\n case 'px': return n\n case 'rem':\n case 'em': return n * 16\n case 'pt': return n * 1.333\n default: return null\n }\n}\n\nfunction divideLength(value: string, divisor: number): string | null {\n const m = value.match(/^([\\d.]+)(px|%)$/)\n if (!m || divisor < 1) return null\n const n = parseFloat(m[1])\n return `${parseFloat((n / divisor).toFixed(2))}${m[2]}`\n}\n\nfunction subtractInsetPx(width: string, insetPx: number): string {\n if (insetPx <= 0) return width\n const m = width.match(/^([\\d.]+)(px|%)$/)\n if (!m) return width\n // Don't subtract px from percentage widths — units don't match.\n if (m[2] === '%') return width\n const n = parseFloat(m[1]) - insetPx\n return `${Math.max(0, Math.round(n))}px`\n}\n\n/**\n * Return the smaller of two px lengths. Clamps our count-based min-width\n * down to the user's `max-width:` so the cap is never silently\n * violated when our computed min would exceed the user's max.\n */\nfunction minPxLength(a: string, b: string): string {\n const am = a.match(/^([\\d.]+)px$/)\n const bm = b.match(/^([\\d.]+)px$/)\n if (!am || !bm) return a\n return parseFloat(am[1]) < parseFloat(bm[1]) ? a : b\n}\n\n/**\n * Expand a 1-4 token CSS shorthand (T R B L) into a left/right pair:\n * 1: all sides\n * 2: TB RL\n * 3: T RL B\n * 4: T R B L\n */\nfunction shorthandSides(value: string): { left?: string; right?: string } {\n const parts = value.trim().split(/\\s+/)\n switch (parts.length) {\n case 1: return { left: parts[0], right: parts[0] }\n case 2:\n case 3: return { left: parts[1], right: parts[1] }\n case 4: return { left: parts[3], right: parts[1] }\n default: return {}\n }\n}\n\n/**\n * Read horizontal padding (left + right) px from a parsed style root.\n * Percentages are skipped — they'd need a known container width.\n */\nfunction horizontalPaddingPx(root: Root): number {\n let left: number | null = null\n let right: number | null = null\n\n // Shorthand applies first; longhand overrides per side.\n root.walkDecls((d) => {\n switch (d.prop) {\n case 'padding': {\n const { left: l, right: r } = shorthandSides(d.value)\n if (l) left = lengthToPx(l)\n if (r) right = lengthToPx(r)\n break\n }\n case 'padding-left':\n left = lengthToPx(d.value)\n break\n case 'padding-right':\n right = lengthToPx(d.value)\n break\n }\n })\n\n return (left ?? 0) + (right ?? 0)\n}\n\n/**\n * Extract a px length from a CSS border shorthand (e.g. `1px solid red` → 1).\n * Returns null when the value indicates no border — `none` or `hidden`.\n * Defaults to 3px (CSS `medium`) when a visible style is set but\n * no explicit width token is present in the shorthand value.\n */\nfunction shorthandBorderWidthPx(value: string): number | null {\n const tokens = value.trim().split(/\\s+/)\n if (tokens.some((t) => NO_BORDER_STYLES.has(t.toLowerCase()))) return null\n for (const t of tokens) {\n const px = lengthToPx(t)\n if (px != null) return px\n }\n // Visible style, no explicit width → CSS default `medium` = 3px.\n return 3\n}\n\n/**\n * Read horizontal border widths (left + right) px from a parsed style root.\n * Per-side `border-style: none|hidden` overrides count as zero\n * contribution. Returns total px or 0 when nothing resolves.\n */\nfunction horizontalBorderPx(root: Root): number {\n let left: number | null = null\n let right: number | null = null\n let leftNone = false\n let rightNone = false\n\n root.walkDecls((d) => {\n switch (d.prop) {\n case 'border': {\n const w = shorthandBorderWidthPx(d.value)\n if (w == null) {\n leftNone = rightNone = true\n }\n else {\n left = right = w\n leftNone = rightNone = false\n }\n break\n }\n case 'border-width': {\n const { left: l, right: r } = shorthandSides(d.value)\n if (l) left = lengthToPx(l) ?? left\n if (r) right = lengthToPx(r) ?? right\n break\n }\n case 'border-style': {\n const { left: l, right: r } = shorthandSides(d.value)\n if (l && NO_BORDER_STYLES.has(l.toLowerCase())) leftNone = true\n if (r && NO_BORDER_STYLES.has(r.toLowerCase())) rightNone = true\n break\n }\n case 'border-left': {\n const w = shorthandBorderWidthPx(d.value)\n if (w == null) leftNone = true\n else { left = w; leftNone = false }\n break\n }\n case 'border-right': {\n const w = shorthandBorderWidthPx(d.value)\n if (w == null) rightNone = true\n else { right = w; rightNone = false }\n break\n }\n case 'border-left-width':\n left = lengthToPx(d.value) ?? left\n break\n case 'border-right-width':\n right = lengthToPx(d.value) ?? right\n break\n case 'border-left-style':\n if (NO_BORDER_STYLES.has(d.value.trim().toLowerCase())) leftNone = true\n break\n case 'border-right-style':\n if (NO_BORDER_STYLES.has(d.value.trim().toLowerCase())) rightNone = true\n break\n }\n })\n\n return (leftNone ? 0 : (left ?? 0)) + (rightNone ? 0 : (right ?? 0))\n}\n\nfunction depth(node: ChildNode): number {\n let d = 0\n let cur: ParentNode | null = node.parent\n while (cur) {\n d++\n cur = (cur as any).parent ?? null\n }\n return d\n}\n\nfunction readWidthFromRoot(root: Root): string | null {\n const raw = firstDeclValue(root, 'max-width')\n ?? firstDeclValue(root, 'width')\n ?? firstDeclValue(root, 'min-width')\n return raw ? resolveLength(raw) : null\n}\n\nfunction readHeightFromRoot(root: Root): string | null {\n const raw = firstDeclValue(root, 'max-height')\n ?? firstDeclValue(root, 'height')\n ?? firstDeclValue(root, 'min-height')\n return raw ? resolveLength(raw) : null\n}\n\nfunction readWidthSource(el: Element, root: Root | null): string | null {\n const explicit = el.attribs?.['data-maizzle-cw']\n if (explicit) {\n const r = resolveLength(explicit)\n if (r) return r\n }\n return root ? readWidthFromRoot(root) : null\n}\n\n/**\n * Convert a user-supplied length to absolute px against the column's source\n * width (post-inset). Percentages multiply against the source while\n * absolute units pass through `resolveLength`. Returns null when\n * the value or source can't be expressed in px.\n */\nfunction userValueToPx(rawValue: string, sourcePx: string | null): string | null {\n const trimmed = rawValue.trim()\n\n const absMatch = trimmed.match(/^([\\d.]+)(px|rem|em|pt)$/i)\n if (absMatch) return resolveLength(trimmed)\n\n const pctMatch = trimmed.match(/^([\\d.]+)%$/)\n if (!pctMatch || !sourcePx) return null\n const sourceMatch = sourcePx.match(/^([\\d.]+)px$/)\n if (!sourceMatch) return null\n const pct = parseFloat(pctMatch[1])\n const src = parseFloat(sourceMatch[1])\n return `${Math.round((pct / 100) * src)}px`\n}\n\n/**\n * Resolve `__MAIZZLE_COLW_{id}__` and `__MAIZZLE_OH_{id}__` placeholders.\n *\n * COLW (column width) — emitted by `<Column>` and `<Overlap>`. Walks up to\n * the nearest ancestor marked `data-maizzle-cw` (Container, Section,\n * Row, or another Column already resolved) and divides the source\n * width by `data-maizzle-cw-count`. With `data-maizzle-cw-self`,\n * reads from the element's own inlined max/width/min-width\n * instead — used by `<Overlap>` with its own width class.\n *\n * OH (overlap height) — emitted by `<Overlap>`. Reads max-height, height,\n * or min-height from the element's own inlined style.\n *\n * Resolution rules:\n * - Style placeholders for `min-width`: replaced when resolvable, otherwise\n * the entire `min-width` declaration is stripped.\n * - Other style placeholders (Overlap td `width`, etc.): replaced when\n * resolvable, otherwise replaced with the count-based fallback or `100%`.\n * - Comment placeholders: same fallback chain.\n *\n * Resolved column widths are written back to `data-maizzle-cw` so nested\n * rows cascade. All `data-maizzle-cw*` and `data-maizzle-oh-*` are\n * stripped at the end of the second walk pass.\n */\nexport function columnWidth(dom: ChildNode[]): ChildNode[] {\n /**\n * Cache parsed style ASTs for this columnWidth invocation. The walk-up\n * loop visits the same Section/Container once per column of a Row,\n * so without caching each column re-parses every ancestor's style.\n * Cache is function-local — no cross-build leak via the WeakMap.\n */\n const styleCache = new WeakMap<Element, Root>()\n const parseElStyle = (el: Element): Root => {\n const cached = styleCache.get(el)\n if (cached) return cached\n const style = el.attribs?.style ?? ''\n const root = style ? safeParser(style) : postcss.root()\n styleCache.set(el, root)\n return root\n }\n\n const columns: { el: Element; id: string; count: number; d: number; self: boolean }[] = []\n const heightTargets: { el: Element; id: string }[] = []\n\n walk(dom, (node) => {\n const el = node as Element\n if (!el.attribs) return\n\n const id = el.attribs['data-maizzle-cw-id']\n if (id) {\n const count = parseInt(el.attribs['data-maizzle-cw-count'] || '1', 10)\n const self = 'data-maizzle-cw-self' in el.attribs\n columns.push({ el, id, count, d: depth(node), self })\n }\n\n const ohId = el.attribs['data-maizzle-oh-id']\n if (ohId) heightTargets.push({ el, id: ohId })\n })\n\n columns.sort((a, b) => a.d - b.d)\n\n const widthResolutions = new Map<string, string>()\n const widthFallbacks = new Map<string, string>()\n /**\n * Column ids whose absolute user `width:` was promoted to `min-width:`\n * — the original `width:` declaration must be stripped from the\n * column's style (otherwise it'd compete with the min-width).\n */\n const stripWidth = new Set<string>()\n /**\n * Column ids where the user wrote a percentage `width:` (e.g. `w-1/2`) —\n * explicit opt-out of px-based stacking. Keep the user's `width: X%`\n * and drop our `min-width:` placeholder so the column stays at\n * that percentage of its parent forever and never stacks.\n */\n const dropMinWidth = new Set<string>()\n /**\n * Column ids where the user wrote their own `min-width:` (via `min-w-1/3`).\n * Juice inlines theirs after ours, so two `min-width:` decls land in\n * the style — we strip the user's after using its value as the\n * column's resolution, leaving our placeholder as last word.\n */\n const stripUserMinWidth = new Set<string>()\n /**\n * Column ids where the user already supplied a `max-width:` of their own.\n * Our default `max-width: 100%` would just be shadowed by it via\n * last-wins and bloat the style — so we skip emitting it.\n */\n const userHasMaxWidth = new Set<string>()\n\n for (const { id, count } of columns) {\n widthFallbacks.set(id, `${Math.round(100 / Math.max(count, 1))}%`)\n }\n\n for (const { el, id, count, self } of columns) {\n const ownRoot = parseElStyle(el)\n\n let sourceWidth: string | null = null\n let accumulatedInsetPx = 0\n\n if (self) {\n sourceWidth = readWidthFromRoot(ownRoot)\n accumulatedInsetPx = horizontalPaddingPx(ownRoot) + horizontalBorderPx(ownRoot)\n }\n else {\n /**\n * Walk up through every ancestor with attribs, accumulating horizontal\n * padding+border along the way (including the source). Stop at the\n * first `data-maizzle-cw` ancestor whose width is resolvable.\n * Markers without a resolvable width (Row emitted empty after\n * Tailwind dropped a bogus class) shouldn't shadow a real\n * width on a higher ancestor like `<Container>`.\n *\n * With CSS content-box this is technically generous toward the\n * source's own padding/border, but matches user expectations\n * when they put `px-9` or `border-2` on a wrapper.\n */\n let cur: ParentNode | null = el.parent\n while (cur) {\n const parentEl = cur as Element\n if (parentEl.attribs) {\n let pRoot: Root | null = null\n if (parentEl.attribs.style) {\n pRoot = parseElStyle(parentEl)\n accumulatedInsetPx += horizontalPaddingPx(pRoot) + horizontalBorderPx(pRoot)\n }\n if ('data-maizzle-cw' in parentEl.attribs) {\n const w = readWidthSource(parentEl, pRoot)\n if (w) {\n sourceWidth = w\n break\n }\n }\n }\n cur = (cur as any).parent ?? null\n }\n }\n\n const adjusted = sourceWidth ? subtractInsetPx(sourceWidth, accumulatedInsetPx) : null\n const countBased = adjusted ? divideLength(adjusted, count) : null\n\n /**\n * Four user-override paths, decided by which CSS property the user\n * actually wrote:\n *\n * - `min-width: X` → user's value wins. Convert to px against\n * the source (if %), use as the column's\n * resolution, and strip the user's min-width\n * declaration so our placeholder substitution\n * remains the last `min-width:` in style.\n * - `width: X%` → opt-out of px stacking. Keep `width:` in\n * style, drop our `min-width:` placeholder.\n * Cols stay at X% of parent forever, never stack.\n * - `width: Xpx` (or rem/em/pt) → fixed pixel column. Promote to\n * `min-width:`, strip the original `width:` so\n * it doesn't compete.\n * - `max-width: X` → CSS cap. Keep the `max-width:` declaration;\n * clamp our count-based min-width *down* to\n * the user's max-width when our min would\n * otherwise violate it.\n */\n const userMinRaw = findUserMinWidth(ownRoot)\n const widthRaw = firstDeclValue(ownRoot, 'width')\n const maxRaw = firstDeclValue(ownRoot, 'max-width')\n\n if (userMinRaw) {\n const minPx = userValueToPx(userMinRaw, adjusted) ?? resolveLength(userMinRaw)\n if (minPx) {\n widthResolutions.set(id, minPx)\n el.attribs['data-maizzle-cw'] = minPx\n stripUserMinWidth.add(id)\n continue\n }\n }\n\n if (widthRaw) {\n const widthVal = resolveLength(widthRaw)\n if (widthVal?.endsWith('%')) {\n widthResolutions.set(id, widthVal)\n el.attribs['data-maizzle-cw'] = widthVal\n dropMinWidth.add(id)\n continue\n }\n if (widthVal) {\n widthResolutions.set(id, widthVal)\n el.attribs['data-maizzle-cw'] = widthVal\n stripWidth.add(id)\n continue\n }\n }\n\n if (maxRaw && countBased) {\n const maxPx = userValueToPx(maxRaw, adjusted)\n if (maxPx) {\n const cappedMin = countBased.endsWith('px')\n ? minPxLength(countBased, maxPx)\n : maxPx\n widthResolutions.set(id, cappedMin)\n el.attribs['data-maizzle-cw'] = cappedMin\n userHasMaxWidth.add(id)\n continue\n }\n }\n\n if (countBased) {\n widthResolutions.set(id, countBased)\n el.attribs['data-maizzle-cw'] = countBased\n }\n }\n\n const heightResolutions = new Map<string, string>()\n for (const { el, id } of heightTargets) {\n if (!el.attribs?.style) continue\n const h = readHeightFromRoot(parseElStyle(el))\n if (h) heightResolutions.set(id, h)\n }\n\n walk(dom, (node) => {\n if (node.type === 'comment') {\n const data = (node as any).data as string\n if (!data || (!data.includes('__MAIZZLE_COLW_') && !data.includes('__MAIZZLE_OH_'))) return\n ;(node as any).data = data\n .replace(/__MAIZZLE_COLW_([^_]+)__/g,\n (_m, mid) => widthResolutions.get(mid) ?? widthFallbacks.get(mid) ?? '100%')\n .replace(/__MAIZZLE_OH_([^_]+)__/g,\n (_m, hid) => heightResolutions.get(hid) ?? '100%')\n return\n }\n\n const el = node as Element\n if (!el.attribs) return\n\n const style = el.attribs.style\n if (style && (style.includes('__MAIZZLE_COLW_') || style.includes('__MAIZZLE_OH_'))) {\n const root = parseElStyle(el)\n const cwId = el.attribs['data-maizzle-cw-id']\n\n /**\n * Strip user dups BEFORE substitution — last-wins CSS would\n * otherwise shadow our resolved values in the output.\n */\n if (cwId && stripUserMinWidth.has(cwId)) {\n root.walkDecls('min-width', (d) => {\n if (!d.value.includes('__MAIZZLE_COLW_')) d.remove()\n })\n }\n if (cwId && stripWidth.has(cwId)) {\n root.walkDecls('width', (d) => { d.remove() })\n }\n\n /**\n * Substitute the column's `min-width:` placeholder with `width: <res>;\n * max-width: 100%`. Width gives the same stacking trigger as\n * min-width — inline-block wraps when children sum > parent\n * — and the `max-width: 100%` clamp keeps the column from\n * overflowing the viewport once it drops to its own row on\n * mobile. Skip the clamp when the user supplied their own.\n *\n * Other placeholders (Overlap td `width`, comment markers,\n * OH height) get a plain value substitution.\n */\n root.walkDecls((d) => {\n if (d.prop === 'min-width') {\n const m = d.value.match(/^__MAIZZLE_COLW_([^_]+)__$/)\n if (m) {\n const mid = m[1]\n if (dropMinWidth.has(mid) || !widthResolutions.has(mid)) {\n d.remove()\n return\n }\n const resolved = widthResolutions.get(mid)!\n const repl: Declaration[] = [postcss.decl({ prop: 'width', value: resolved })]\n if (!userHasMaxWidth.has(mid)) {\n repl.push(postcss.decl({ prop: 'max-width', value: '100%' }))\n }\n d.replaceWith(...repl)\n return\n }\n }\n if (d.value.includes('__MAIZZLE_COLW_') || d.value.includes('__MAIZZLE_OH_')) {\n d.value = d.value\n .replace(/__MAIZZLE_COLW_([^_]+)__/g,\n (_m, mid) => widthResolutions.get(mid) ?? widthFallbacks.get(mid) ?? '100%')\n .replace(/__MAIZZLE_OH_([^_]+)__/g,\n (_m, hid) => heightResolutions.get(hid) ?? '100%')\n }\n })\n\n const out = serializeStyle(root)\n if (out) el.attribs.style = out\n else delete el.attribs.style\n }\n\n delete el.attribs['data-maizzle-cw']\n delete el.attribs['data-maizzle-cw-id']\n delete el.attribs['data-maizzle-cw-count']\n delete el.attribs['data-maizzle-cw-self']\n delete el.attribs['data-maizzle-oh-id']\n })\n\n return dom\n}\n"],"mappings":"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"}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { walk } from "../utils/ast/walker.mjs";
|
|
2
|
-
import "../utils/ast/index.mjs";
|
|
3
|
-
import { defu } from "defu";
|
|
4
|
-
|
|
5
|
-
//#region src/transformers/entities.ts
|
|
6
|
-
const DEFAULT_ENTITIES = {
|
|
7
|
-
"": "‍",
|
|
8
|
-
"": "‌",
|
|
9
|
-
"\xA0": " ",
|
|
10
|
-
"": "­",
|
|
11
|
-
"": "​",
|
|
12
|
-
" ": " ",
|
|
13
|
-
"͏": "͏",
|
|
14
|
-
" ": " ",
|
|
15
|
-
"\u2028": "&LineSeparator;",
|
|
16
|
-
"\u2029": "&ParagraphSeparator;",
|
|
17
|
-
"·": "·",
|
|
18
|
-
"–": "–",
|
|
19
|
-
"—": "—",
|
|
20
|
-
"‘": "‘",
|
|
21
|
-
"’": "’",
|
|
22
|
-
"“": "“",
|
|
23
|
-
"”": "”",
|
|
24
|
-
"«": "«",
|
|
25
|
-
"»": "»",
|
|
26
|
-
"•": "•",
|
|
27
|
-
"‹": "‹",
|
|
28
|
-
"›": "›"
|
|
29
|
-
};
|
|
30
|
-
function entities(dom, config = true) {
|
|
31
|
-
if (!config) return dom;
|
|
32
|
-
const map = typeof config === "object" ? defu(config, DEFAULT_ENTITIES) : DEFAULT_ENTITIES;
|
|
33
|
-
walk(dom, (node) => {
|
|
34
|
-
if (node.type === "text") for (const [char, entity] of Object.entries(map)) node.data = node.data.split(char).join(entity);
|
|
35
|
-
});
|
|
36
|
-
return dom;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
//#endregion
|
|
40
|
-
export { entities };
|
|
41
|
-
//# sourceMappingURL=entities.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"entities.mjs","names":["merge"],"sources":["../../src/transformers/entities.ts"],"sourcesContent":["import { defu as merge } from 'defu'\nimport { walk } from '../utils/ast/index.ts'\nimport type { ChildNode } from 'domhandler'\nimport type { EntitiesConfig } from '../types/index.ts'\n\nconst DEFAULT_ENTITIES: Record<string, string> = {\n '\\u200D': '‍',\n '\\u200C': '‌',\n '\\u00A0': ' ',\n '\\u00AD': '­',\n '\\u200B': '​',\n '\\u2007': ' ',\n '\\u034F': '͏',\n '\\u2003': ' ',\n '\\u2028': '&LineSeparator;',\n '\\u2029': '&ParagraphSeparator;',\n '\\u00B7': '·',\n '\\u2013': '–',\n '\\u2014': '—',\n '\\u2018': '‘',\n '\\u2019': '’',\n '\\u201C': '“',\n '\\u201D': '”',\n '\\u00AB': '«',\n '\\u00BB': '»',\n '\\u2022': '•',\n '\\u2039': '‹',\n '\\u203A': '›'\n}\n\nexport function entities(dom: ChildNode[], config: EntitiesConfig = true): ChildNode[] {\n if (!config) return dom\n\n const map = typeof config === 'object'\n ? merge(config as Record<string, string>, DEFAULT_ENTITIES)\n : DEFAULT_ENTITIES\n\n walk(dom, (node) => {\n if (node.type === 'text') {\n for (const [char, entity] of Object.entries(map)) {\n node.data = node.data.split(char).join(entity)\n }\n }\n })\n\n return dom\n}\n"],"mappings":";;;;;AAKA,MAAM,mBAA2C;CAC/C,KAAU;CACV,KAAU;CACV,QAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,UAAU;CACV,UAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACV,KAAU;CACX;AAED,SAAgB,SAAS,KAAkB,SAAyB,MAAmB;AACrF,KAAI,CAAC,OAAQ,QAAO;CAEpB,MAAM,MAAM,OAAO,WAAW,WAC1BA,KAAM,QAAkC,iBAAiB,GACzD;AAEJ,MAAK,MAAM,SAAS;AAClB,MAAI,KAAK,SAAS,OAChB,MAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,IAAI,CAC9C,MAAK,OAAO,KAAK,KAAK,MAAM,KAAK,CAAC,KAAK,OAAO;GAGlD;AAEF,QAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.mjs","names":[],"sources":["../../../src/transformers/filters/defaults.ts"],"sourcesContent":["export type FilterFunction = (str: string, value: string) => string\n\nconst escapeMap: Record<string, string> = {\n '\"': '"',\n '&': '&',\n \"'\": ''',\n '<': '<',\n '>': '>',\n}\n\nconst escapeRegex = /[\"&'<>]/g\n\nfunction escapeHtml(str: string): string {\n return str.replace(escapeRegex, ch => escapeMap[ch])\n}\n\nexport const defaults: Record<string, FilterFunction> = {\n append: (str, value) => str + value,\n prepend: (str, value) => value + str,\n uppercase: str => str.toUpperCase(),\n lowercase: str => str.toLowerCase(),\n capitalize: str => str.charAt(0).toUpperCase() + str.slice(1),\n ceil: str => String(Math.ceil(Number.parseFloat(str))),\n floor: str => String(Math.floor(Number.parseFloat(str))),\n round: str => String(Math.round(Number.parseFloat(str))),\n escape: str => escapeHtml(str),\n 'escape-once': str => {\n const decoded = str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/'/g, \"'\")\n\n return escapeHtml(decoded)\n },\n lstrip: str => str.trimStart(),\n rstrip: str => str.trimEnd(),\n trim: str => str.trim(),\n minus: (str, value) => String(Number.parseFloat(str) - Number.parseFloat(value)),\n plus: (str, value) => String(Number.parseFloat(str) + Number.parseFloat(value)),\n multiply: (str, value) => String(Number.parseFloat(str) * Number.parseFloat(value)),\n times: (str, value) => String(Number.parseFloat(str) * Number.parseFloat(value)),\n 'divide-by': (str, value) => String(Number.parseFloat(str) / Number.parseFloat(value)),\n divide: (str, value) => String(Number.parseFloat(str) / Number.parseFloat(value)),\n modulo: (str, value) => String(Number.parseFloat(str) % Number.parseFloat(value)),\n 'newline-to-br': str => str.replace(/\\n/g, '<br>'),\n 'strip-newlines': str => str.replace(/\\n/g, ''),\n remove: (str, value) => str.split(value).join(''),\n 'remove-first': (str, value) => {\n const i = str.indexOf(value)\n return i === -1 ? str : str.slice(0, i) + str.slice(i + value.length)\n },\n replace: (str, value) => {\n const [search = '', replacement = ''] = value.split('|')\n return str.split(search).join(replacement)\n },\n 'replace-first': (str, value) => {\n const [search = '', replacement = ''] = value.split('|')\n const i = str.indexOf(search)\n return i === -1 ? str : str.slice(0, i) + replacement + str.slice(i + search.length)\n },\n size: str => String(str.length),\n slice: (str, value) => {\n const args = value.split(',').map(s => Number.parseInt(s.trim(), 10))\n return str.slice(args[0], args[1])\n },\n truncate: (str, value) => {\n const commaIndex = value.indexOf(',')\n const length = Number.parseInt(commaIndex === -1 ? value : value.slice(0, commaIndex), 10)\n const ellipsis = commaIndex === -1 ? '...' : value.slice(commaIndex + 1)\n\n if (str.length <= length) return str\n\n return str.slice(0, length) + ellipsis\n },\n 'truncate-words': (str, value) => {\n const commaIndex = value.indexOf(',')\n const count = Number.parseInt(commaIndex === -1 ? value : value.slice(0, commaIndex), 10)\n const ellipsis = commaIndex === -1 ? '...' : value.slice(commaIndex + 1)\n const words = str.split(/\\s+/).filter(Boolean)\n\n if (words.length <= count) return str\n\n return words.slice(0, count).join(' ') + ellipsis\n },\n 'url-decode': str => decodeURIComponent(str.replace(/\\+/g, ' ')),\n 'url-encode': str => encodeURIComponent(str),\n}\n"],"mappings":";AAEA,MAAM,YAAoC;CACxC,MAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;AAED,MAAM,cAAc;AAEpB,SAAS,WAAW,KAAqB;AACvC,QAAO,IAAI,QAAQ,cAAa,OAAM,UAAU,IAAI;;AAGtD,MAAa,WAA2C;CACtD,SAAS,KAAK,UAAU,MAAM;CAC9B,UAAU,KAAK,UAAU,QAAQ;CACjC,YAAW,QAAO,IAAI,aAAa;CACnC,YAAW,QAAO,IAAI,aAAa;CACnC,aAAY,QAAO,IAAI,OAAO,EAAE,CAAC,aAAa,GAAG,IAAI,MAAM,EAAE;CAC7D,OAAM,QAAO,OAAO,KAAK,KAAK,OAAO,WAAW,IAAI,CAAC,CAAC;CACtD,QAAO,QAAO,OAAO,KAAK,MAAM,OAAO,WAAW,IAAI,CAAC,CAAC;CACxD,QAAO,QAAO,OAAO,KAAK,MAAM,OAAO,WAAW,IAAI,CAAC,CAAC;CACxD,SAAQ,QAAO,WAAW,IAAI;CAC9B,gBAAe,QAAO;AAUpB,SAAO,WATS,IACb,QAAQ,UAAU,IAAI,CACtB,QAAQ,SAAS,IAAI,CACrB,QAAQ,SAAS,IAAI,CACrB,QAAQ,UAAU,KAAI,CACtB,QAAQ,WAAW,KAAI,CACvB,QAAQ,UAAU,IAAI,CACtB,QAAQ,WAAW,IAAI,CAEA;;CAE5B,SAAQ,QAAO,IAAI,WAAW;CAC9B,SAAQ,QAAO,IAAI,SAAS;CAC5B,OAAM,QAAO,IAAI,MAAM;CACvB,QAAQ,KAAK,UAAU,OAAO,OAAO,WAAW,IAAI,GAAG,OAAO,WAAW,MAAM,CAAC;CAChF,OAAO,KAAK,UAAU,OAAO,OAAO,WAAW,IAAI,GAAG,OAAO,WAAW,MAAM,CAAC;CAC/E,WAAW,KAAK,UAAU,OAAO,OAAO,WAAW,IAAI,GAAG,OAAO,WAAW,MAAM,CAAC;CACnF,QAAQ,KAAK,UAAU,OAAO,OAAO,WAAW,IAAI,GAAG,OAAO,WAAW,MAAM,CAAC;CAChF,cAAc,KAAK,UAAU,OAAO,OAAO,WAAW,IAAI,GAAG,OAAO,WAAW,MAAM,CAAC;CACtF,SAAS,KAAK,UAAU,OAAO,OAAO,WAAW,IAAI,GAAG,OAAO,WAAW,MAAM,CAAC;CACjF,SAAS,KAAK,UAAU,OAAO,OAAO,WAAW,IAAI,GAAG,OAAO,WAAW,MAAM,CAAC;CACjF,kBAAiB,QAAO,IAAI,QAAQ,OAAO,OAAO;CAClD,mBAAkB,QAAO,IAAI,QAAQ,OAAO,GAAG;CAC/C,SAAS,KAAK,UAAU,IAAI,MAAM,MAAM,CAAC,KAAK,GAAG;CACjD,iBAAiB,KAAK,UAAU;EAC9B,MAAM,IAAI,IAAI,QAAQ,MAAM;AAC5B,SAAO,MAAM,KAAK,MAAM,IAAI,MAAM,GAAG,EAAE,GAAG,IAAI,MAAM,IAAI,MAAM,OAAO;;CAEvE,UAAU,KAAK,UAAU;EACvB,MAAM,CAAC,SAAS,IAAI,cAAc,MAAM,MAAM,MAAM,IAAI;AACxD,SAAO,IAAI,MAAM,OAAO,CAAC,KAAK,YAAY;;CAE5C,kBAAkB,KAAK,UAAU;EAC/B,MAAM,CAAC,SAAS,IAAI,cAAc,MAAM,MAAM,MAAM,IAAI;EACxD,MAAM,IAAI,IAAI,QAAQ,OAAO;AAC7B,SAAO,MAAM,KAAK,MAAM,IAAI,MAAM,GAAG,EAAE,GAAG,cAAc,IAAI,MAAM,IAAI,OAAO,OAAO;;CAEtF,OAAM,QAAO,OAAO,IAAI,OAAO;CAC/B,QAAQ,KAAK,UAAU;EACrB,MAAM,OAAO,MAAM,MAAM,IAAI,CAAC,KAAI,MAAK,OAAO,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC;AACrE,SAAO,IAAI,MAAM,KAAK,IAAI,KAAK,GAAG;;CAEpC,WAAW,KAAK,UAAU;EACxB,MAAM,aAAa,MAAM,QAAQ,IAAI;EACrC,MAAM,SAAS,OAAO,SAAS,eAAe,KAAK,QAAQ,MAAM,MAAM,GAAG,WAAW,EAAE,GAAG;EAC1F,MAAM,WAAW,eAAe,KAAK,QAAQ,MAAM,MAAM,aAAa,EAAE;AAExE,MAAI,IAAI,UAAU,OAAQ,QAAO;AAEjC,SAAO,IAAI,MAAM,GAAG,OAAO,GAAG;;CAEhC,mBAAmB,KAAK,UAAU;EAChC,MAAM,aAAa,MAAM,QAAQ,IAAI;EACrC,MAAM,QAAQ,OAAO,SAAS,eAAe,KAAK,QAAQ,MAAM,MAAM,GAAG,WAAW,EAAE,GAAG;EACzF,MAAM,WAAW,eAAe,KAAK,QAAQ,MAAM,MAAM,aAAa,EAAE;EACxE,MAAM,QAAQ,IAAI,MAAM,MAAM,CAAC,OAAO,QAAQ;AAE9C,MAAI,MAAM,UAAU,MAAO,QAAO;AAElC,SAAO,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI,GAAG;;CAE3C,eAAc,QAAO,mBAAmB,IAAI,QAAQ,OAAO,IAAI,CAAC;CAChE,eAAc,QAAO,mBAAmB,IAAI;CAC7C"}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { parse } from "../../utils/ast/parser.mjs";
|
|
2
|
-
import { serialize } from "../../utils/ast/serializer.mjs";
|
|
3
|
-
import "../../utils/ast/index.mjs";
|
|
4
|
-
import { defaults } from "./defaults.mjs";
|
|
5
|
-
import { Text } from "domhandler";
|
|
6
|
-
|
|
7
|
-
//#region src/transformers/filters/index.ts
|
|
8
|
-
/**
|
|
9
|
-
* Process children before parents so nested filter elements work correctly.
|
|
10
|
-
*/
|
|
11
|
-
function walkBottomUp(nodes, callback) {
|
|
12
|
-
for (const node of nodes.slice()) {
|
|
13
|
-
if ("children" in node && node.children?.length) walkBottomUp(node.children, callback);
|
|
14
|
-
callback(node);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Filters transformer.
|
|
19
|
-
*
|
|
20
|
-
* Applies transformation functions to the content of elements that
|
|
21
|
-
* have matching filter attributes. Multiple filters on the same element
|
|
22
|
-
* are executed in the order the attributes are defined.
|
|
23
|
-
*
|
|
24
|
-
* Default filters include string manipulation (uppercase, lowercase, trim, etc.),
|
|
25
|
-
* math operations (plus, minus, multiply, etc.), and more.
|
|
26
|
-
*
|
|
27
|
-
* Custom filters can be added via config, and will be merged with defaults.
|
|
28
|
-
* Set config to `false` to disable all filters.
|
|
29
|
-
*/
|
|
30
|
-
function filters(dom, config = {}) {
|
|
31
|
-
if (config === false) return dom;
|
|
32
|
-
const allFilters = {
|
|
33
|
-
...defaults,
|
|
34
|
-
...config
|
|
35
|
-
};
|
|
36
|
-
const filterNames = new Set(Object.keys(allFilters));
|
|
37
|
-
walkBottomUp(dom, (node) => {
|
|
38
|
-
const el = node;
|
|
39
|
-
if (!el.attribs) return;
|
|
40
|
-
const matched = [];
|
|
41
|
-
for (const attr of Object.keys(el.attribs)) if (filterNames.has(attr)) matched.push({
|
|
42
|
-
name: attr,
|
|
43
|
-
value: el.attribs[attr]
|
|
44
|
-
});
|
|
45
|
-
if (matched.length === 0) return;
|
|
46
|
-
let content = serialize(el.children);
|
|
47
|
-
for (const { name, value } of matched) {
|
|
48
|
-
content = allFilters[name](content, value);
|
|
49
|
-
delete el.attribs[name];
|
|
50
|
-
}
|
|
51
|
-
if (content === "") el.children = [];
|
|
52
|
-
else if (/<[a-z/!]/i.test(content)) {
|
|
53
|
-
const newChildren = parse(content);
|
|
54
|
-
for (const child of newChildren) child.parent = el;
|
|
55
|
-
el.children = newChildren;
|
|
56
|
-
} else {
|
|
57
|
-
const textNode = new Text(content);
|
|
58
|
-
textNode.parent = el;
|
|
59
|
-
el.children = [textNode];
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
return dom;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
//#endregion
|
|
66
|
-
export { filters };
|
|
67
|
-
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/transformers/filters/index.ts"],"sourcesContent":["import { Text } from 'domhandler'\nimport type { ChildNode, Element } from 'domhandler'\nimport { parse, serialize } from '../../utils/ast/index.ts'\nimport { defaults } from './defaults.ts'\n\nexport type { FilterFunction } from './defaults.ts'\nexport type FiltersConfig = false | Record<string, (str: string, value: string) => string>\n\n/**\n * Process children before parents so nested filter elements work correctly.\n */\nfunction walkBottomUp(nodes: ChildNode[], callback: (node: ChildNode) => void): void {\n for (const node of nodes.slice()) {\n if ('children' in node && node.children?.length) {\n walkBottomUp(node.children as ChildNode[], callback)\n }\n\n callback(node)\n }\n}\n\n/**\n * Filters transformer.\n *\n * Applies transformation functions to the content of elements that\n * have matching filter attributes. Multiple filters on the same element\n * are executed in the order the attributes are defined.\n *\n * Default filters include string manipulation (uppercase, lowercase, trim, etc.),\n * math operations (plus, minus, multiply, etc.), and more.\n *\n * Custom filters can be added via config, and will be merged with defaults.\n * Set config to `false` to disable all filters.\n */\nexport function filters(dom: ChildNode[], config: FiltersConfig = {}): ChildNode[] {\n if (config === false) return dom\n\n const allFilters = { ...defaults, ...config }\n const filterNames = new Set(Object.keys(allFilters))\n\n walkBottomUp(dom, (node) => {\n const el = node as Element\n\n if (!el.attribs) return\n\n // Collect matching filter attributes in source order\n const matched: Array<{ name: string; value: string }> = []\n\n for (const attr of Object.keys(el.attribs)) {\n if (filterNames.has(attr)) {\n matched.push({ name: attr, value: el.attribs[attr] })\n }\n }\n\n if (matched.length === 0) return\n\n // Serialize children to get innerHTML\n let content = serialize(el.children as ChildNode[])\n\n // Apply each filter in attribute order\n for (const { name, value } of matched) {\n content = allFilters[name](content, value)\n delete el.attribs[name]\n }\n\n // Replace children with the filtered content\n if (content === '') {\n el.children = []\n } else if (/<[a-z/!]/i.test(content)) {\n // Result contains HTML elements — parse back to DOM\n const newChildren = parse(content)\n\n for (const child of newChildren) {\n child.parent = el as any\n }\n\n el.children = newChildren as ChildNode[]\n } else {\n // Text-only result — create a text node directly to preserve entity strings\n const textNode = new Text(content)\n textNode.parent = el as any\n el.children = [textNode]\n }\n })\n\n return dom\n}\n"],"mappings":";;;;;;;;;;AAWA,SAAS,aAAa,OAAoB,UAA2C;AACnF,MAAK,MAAM,QAAQ,MAAM,OAAO,EAAE;AAChC,MAAI,cAAc,QAAQ,KAAK,UAAU,OACvC,cAAa,KAAK,UAAyB,SAAS;AAGtD,WAAS,KAAK;;;;;;;;;;;;;;;;AAiBlB,SAAgB,QAAQ,KAAkB,SAAwB,EAAE,EAAe;AACjF,KAAI,WAAW,MAAO,QAAO;CAE7B,MAAM,aAAa;EAAE,GAAG;EAAU,GAAG;EAAQ;CAC7C,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,WAAW,CAAC;AAEpD,cAAa,MAAM,SAAS;EAC1B,MAAM,KAAK;AAEX,MAAI,CAAC,GAAG,QAAS;EAGjB,MAAM,UAAkD,EAAE;AAE1D,OAAK,MAAM,QAAQ,OAAO,KAAK,GAAG,QAAQ,CACxC,KAAI,YAAY,IAAI,KAAK,CACvB,SAAQ,KAAK;GAAE,MAAM;GAAM,OAAO,GAAG,QAAQ;GAAO,CAAC;AAIzD,MAAI,QAAQ,WAAW,EAAG;EAG1B,IAAI,UAAU,UAAU,GAAG,SAAwB;AAGnD,OAAK,MAAM,EAAE,MAAM,WAAW,SAAS;AACrC,aAAU,WAAW,MAAM,SAAS,MAAM;AAC1C,UAAO,GAAG,QAAQ;;AAIpB,MAAI,YAAY,GACd,IAAG,WAAW,EAAE;WACP,YAAY,KAAK,QAAQ,EAAE;GAEpC,MAAM,cAAc,MAAM,QAAQ;AAElC,QAAK,MAAM,SAAS,YAClB,OAAM,SAAS;AAGjB,MAAG,WAAW;SACT;GAEL,MAAM,WAAW,IAAI,KAAK,QAAQ;AAClC,YAAS,SAAS;AAClB,MAAG,WAAW,CAAC,SAAS;;GAE1B;AAEF,QAAO"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { defu } from "defu";
|
|
2
|
-
import { format as format$1 } from "oxfmt";
|
|
3
|
-
|
|
4
|
-
//#region src/transformers/format.ts
|
|
5
|
-
const DEFAULT_OPTIONS = {
|
|
6
|
-
printWidth: 320,
|
|
7
|
-
htmlWhitespaceSensitivity: "ignore",
|
|
8
|
-
embeddedLanguageFormatting: "off"
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Format transformer.
|
|
12
|
-
*
|
|
13
|
-
* Formats the HTML string using `oxfmt`. Accepts all oxfmt `FormatOptions`.
|
|
14
|
-
*
|
|
15
|
-
* Enable by setting `html.format: true` (or passing options).
|
|
16
|
-
* User options are merged on top of the defaults.
|
|
17
|
-
*/
|
|
18
|
-
async function format(html, config = {}) {
|
|
19
|
-
const option = config.html?.format;
|
|
20
|
-
if (!option) return html;
|
|
21
|
-
return (await format$1("input.html", html, defu(typeof option === "object" ? option : {}, DEFAULT_OPTIONS))).code;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
//#endregion
|
|
25
|
-
export { format };
|
|
26
|
-
//# sourceMappingURL=format.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"format.mjs","names":["oxfmt","merge"],"sources":["../../src/transformers/format.ts"],"sourcesContent":["import { format as oxfmt } from 'oxfmt'\nimport { defu as merge } from 'defu'\nimport type { FormatOptions } from 'oxfmt'\nimport type { MaizzleConfig } from '../types/config.ts'\n\nconst DEFAULT_OPTIONS: FormatOptions = {\n printWidth: 320,\n htmlWhitespaceSensitivity: 'ignore',\n embeddedLanguageFormatting: 'off',\n}\n\n/**\n * Format transformer.\n *\n * Formats the HTML string using `oxfmt`. Accepts all oxfmt `FormatOptions`.\n *\n * Enable by setting `html.format: true` (or passing options).\n * User options are merged on top of the defaults.\n */\nexport async function format(html: string, config: MaizzleConfig = {}): Promise<string> {\n const option = config.html?.format\n\n if (!option) return html\n\n const userOptions: FormatOptions = typeof option === 'object' ? option : {}\n const options = merge(userOptions, DEFAULT_OPTIONS)\n\n const result = await oxfmt('input.html', html, options)\n\n return result.code\n}\n"],"mappings":";;;;AAKA,MAAM,kBAAiC;CACrC,YAAY;CACZ,2BAA2B;CAC3B,4BAA4B;CAC7B;;;;;;;;;AAUD,eAAsB,OAAO,MAAc,SAAwB,EAAE,EAAmB;CACtF,MAAM,SAAS,OAAO,MAAM;AAE5B,KAAI,CAAC,OAAQ,QAAO;AAOpB,SAFe,MAAMA,SAAM,cAAc,MAFzBC,KADmB,OAAO,WAAW,WAAW,SAAS,EAAE,EACxC,gBAAgB,CAEI,EAEzC"}
|