@maizzle/framework 6.0.0-rc.15 → 6.0.0-rc.17
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/README.md +3 -3
- package/dist/{build.d.mts → build.d.ts} +2 -2
- package/dist/build.d.ts.map +1 -0
- package/dist/build.mjs +1 -1
- package/dist/build.mjs.map +1 -1
- package/dist/components/Body.vue +9 -2
- package/dist/components/Button.vue +13 -8
- package/dist/components/Column.vue +22 -8
- package/dist/components/Container.vue +9 -5
- package/dist/components/Html.vue +7 -2
- package/dist/components/Layout.vue +30 -15
- package/dist/components/Overlap.vue +8 -3
- package/dist/components/Raw.vue +28 -0
- package/dist/components/Row.vue +23 -11
- package/dist/components/Section.vue +9 -5
- package/dist/components/Spacer.vue +9 -4
- package/dist/components/Tailwind.vue +43 -0
- package/dist/components/{utils.d.mts → utils.d.ts} +12 -2
- package/dist/components/utils.d.ts.map +1 -0
- package/dist/components/utils.mjs +11 -1
- package/dist/components/utils.mjs.map +1 -1
- package/dist/components/utils.ts +12 -0
- package/dist/composables/{defineConfig.d.mts → defineConfig.d.ts} +2 -2
- package/dist/composables/defineConfig.d.ts.map +1 -0
- package/dist/composables/{renderContext.d.mts → renderContext.d.ts} +11 -5
- package/dist/composables/renderContext.d.ts.map +1 -0
- package/dist/composables/renderContext.mjs.map +1 -1
- package/dist/composables/{useConfig.d.mts → useConfig.d.ts} +2 -2
- package/dist/composables/useConfig.d.ts.map +1 -0
- package/dist/composables/{useDoctype.d.mts → useDoctype.d.ts} +1 -1
- package/dist/composables/useDoctype.d.ts.map +1 -0
- package/dist/composables/{useEvent.d.mts → useEvent.d.ts} +2 -2
- package/dist/composables/useEvent.d.ts.map +1 -0
- package/dist/composables/{useFont.d.mts → useFont.d.ts} +1 -1
- package/dist/composables/useFont.d.ts.map +1 -0
- package/dist/composables/useOutlookFallback.d.ts +21 -0
- package/dist/composables/useOutlookFallback.d.ts.map +1 -0
- package/dist/composables/useOutlookFallback.mjs +30 -0
- package/dist/composables/useOutlookFallback.mjs.map +1 -0
- package/dist/composables/{usePlaintext.d.mts → usePlaintext.d.ts} +1 -1
- package/dist/composables/usePlaintext.d.ts.map +1 -0
- package/dist/composables/{usePreheader.d.mts → usePreheader.d.ts} +1 -1
- package/dist/composables/usePreheader.d.ts.map +1 -0
- package/dist/config/{defaults.d.mts → defaults.d.ts} +2 -2
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/{index.d.mts → index.d.ts} +4 -4
- package/dist/config/index.d.ts.map +1 -0
- package/dist/events/{index.d.mts → index.d.ts} +2 -2
- package/dist/events/index.d.ts.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.mjs +2 -1
- package/dist/{plaintext.d.mts → plaintext.d.ts} +1 -1
- package/dist/plaintext.d.ts.map +1 -0
- package/dist/{plugin.d.mts → plugin.d.ts} +2 -2
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugins/postcss/{mergeMediaQueries.d.mts → mergeMediaQueries.d.ts} +2 -2
- package/dist/plugins/postcss/mergeMediaQueries.d.ts.map +1 -0
- package/dist/plugins/postcss/{pruneVars.d.mts → pruneVars.d.ts} +1 -1
- package/dist/plugins/postcss/pruneVars.d.ts.map +1 -0
- package/dist/plugins/postcss/{quoteFontFamilies.d.mts → quoteFontFamilies.d.ts} +1 -1
- package/dist/plugins/postcss/quoteFontFamilies.d.ts.map +1 -0
- package/dist/plugins/postcss/{removeDeclarations.d.mts → removeDeclarations.d.ts} +1 -1
- package/dist/plugins/postcss/removeDeclarations.d.ts.map +1 -0
- package/dist/plugins/postcss/resolveMaizzleImports.d.ts +16 -0
- package/dist/plugins/postcss/resolveMaizzleImports.d.ts.map +1 -0
- package/dist/plugins/postcss/resolveMaizzleImports.mjs +40 -0
- package/dist/plugins/postcss/resolveMaizzleImports.mjs.map +1 -0
- package/dist/plugins/postcss/{resolveProps.d.mts → resolveProps.d.ts} +1 -1
- package/dist/plugins/postcss/resolveProps.d.ts.map +1 -0
- package/dist/plugins/postcss/{tailwindCleanup.d.mts → tailwindCleanup.d.ts} +2 -2
- package/dist/plugins/postcss/tailwindCleanup.d.ts.map +1 -0
- package/dist/{prepare.d.mts → prepare.d.ts} +1 -1
- package/dist/prepare.d.ts.map +1 -0
- package/dist/render/{createRenderer.d.mts → createRenderer.d.ts} +4 -3
- package/dist/render/createRenderer.d.ts.map +1 -0
- package/dist/render/createRenderer.mjs +9 -76
- package/dist/render/createRenderer.mjs.map +1 -1
- package/dist/render/index.d.ts +18 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.mjs +13 -14
- package/dist/render/index.mjs.map +1 -1
- package/dist/render/{injectFonts.d.mts → injectFonts.d.ts} +2 -2
- package/dist/render/injectFonts.d.ts.map +1 -0
- package/dist/render/plugins/codeBlockExtract.d.ts +14 -0
- package/dist/render/plugins/codeBlockExtract.d.ts.map +1 -0
- package/dist/render/plugins/codeBlockExtract.mjs +34 -0
- package/dist/render/plugins/codeBlockExtract.mjs.map +1 -0
- package/dist/render/plugins/markdownExtract.d.ts +12 -0
- package/dist/render/plugins/markdownExtract.d.ts.map +1 -0
- package/dist/render/plugins/markdownExtract.mjs +50 -0
- package/dist/render/plugins/markdownExtract.mjs.map +1 -0
- package/dist/render/plugins/rawExtract.d.ts +14 -0
- package/dist/render/plugins/rawExtract.d.ts.map +1 -0
- package/dist/render/plugins/rawExtract.mjs +34 -0
- package/dist/render/plugins/rawExtract.mjs.map +1 -0
- package/dist/render/plugins/{rowSourceLocation.d.mts → rowSourceLocation.d.ts} +1 -1
- package/dist/render/plugins/rowSourceLocation.d.ts.map +1 -0
- package/dist/{serve.d.mts → serve.d.ts} +2 -2
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.mjs +15 -6
- package/dist/serve.mjs.map +1 -1
- package/dist/server/{compatibility.d.mts → compatibility.d.ts} +2 -2
- package/dist/server/compatibility.d.ts.map +1 -0
- package/dist/server/{email.d.mts → email.d.ts} +2 -2
- package/dist/server/email.d.ts.map +1 -0
- package/dist/server/{linter.d.mts → linter.d.ts} +2 -2
- package/dist/server/linter.d.ts.map +1 -0
- package/dist/server/{sfc-utils.d.mts → sfc-utils.d.ts} +1 -1
- package/dist/server/sfc-utils.d.ts.map +1 -0
- package/dist/server/ui/App.vue +47 -2
- package/dist/server/ui/pages/Preview.vue +110 -22
- package/dist/transformers/{addAttributes.d.mts → addAttributes.d.ts} +2 -2
- package/dist/transformers/addAttributes.d.ts.map +1 -0
- package/dist/transformers/{attributeToStyle.d.mts → attributeToStyle.d.ts} +2 -2
- package/dist/transformers/attributeToStyle.d.ts.map +1 -0
- package/dist/transformers/{base.d.mts → base.d.ts} +2 -2
- package/dist/transformers/base.d.ts.map +1 -0
- package/dist/transformers/{columnWidth.d.mts → columnWidth.d.ts} +10 -10
- package/dist/transformers/columnWidth.d.ts.map +1 -0
- package/dist/transformers/columnWidth.mjs +422 -41
- package/dist/transformers/columnWidth.mjs.map +1 -1
- package/dist/transformers/{entities.d.mts → entities.d.ts} +2 -2
- package/dist/transformers/entities.d.ts.map +1 -0
- package/dist/transformers/filters/{defaults.d.mts → defaults.d.ts} +1 -1
- package/dist/transformers/filters/defaults.d.ts.map +1 -0
- package/dist/transformers/filters/{index.d.mts → index.d.ts} +2 -2
- package/dist/transformers/filters/index.d.ts.map +1 -0
- package/dist/transformers/{format.d.mts → format.d.ts} +2 -2
- package/dist/transformers/format.d.ts.map +1 -0
- package/dist/transformers/{index.d.mts → index.d.ts} +4 -3
- package/dist/transformers/index.d.ts.map +1 -0
- package/dist/transformers/index.mjs +3 -1
- package/dist/transformers/index.mjs.map +1 -1
- package/dist/transformers/{inlineCSS.d.mts → inlineCSS.d.ts} +2 -2
- package/dist/transformers/inlineCSS.d.ts.map +1 -0
- package/dist/transformers/inlineCSS.mjs +7 -1
- package/dist/transformers/inlineCSS.mjs.map +1 -1
- package/dist/transformers/{inlineLink.d.mts → inlineLink.d.ts} +1 -1
- package/dist/transformers/inlineLink.d.ts.map +1 -0
- package/dist/transformers/{minify.d.mts → minify.d.ts} +2 -2
- package/dist/transformers/minify.d.ts.map +1 -0
- package/dist/transformers/{msoWidthFromClass.d.mts → msoWidthFromClass.d.ts} +1 -1
- package/dist/transformers/msoWidthFromClass.d.ts.map +1 -0
- package/dist/transformers/{purgeCSS.d.mts → purgeCSS.d.ts} +2 -2
- package/dist/transformers/purgeCSS.d.ts.map +1 -0
- package/dist/transformers/purgeCSS.mjs +44 -2
- package/dist/transformers/purgeCSS.mjs.map +1 -1
- package/dist/transformers/{removeAttributes.d.mts → removeAttributes.d.ts} +2 -2
- package/dist/transformers/removeAttributes.d.ts.map +1 -0
- package/dist/transformers/{replaceStrings.d.mts → replaceStrings.d.ts} +2 -2
- package/dist/transformers/replaceStrings.d.ts.map +1 -0
- package/dist/transformers/{safeClassNames.d.mts → safeClassNames.d.ts} +2 -2
- package/dist/transformers/safeClassNames.d.ts.map +1 -0
- package/dist/transformers/{shorthandCSS.d.mts → shorthandCSS.d.ts} +2 -2
- package/dist/transformers/shorthandCSS.d.ts.map +1 -0
- package/dist/transformers/{sixHex.d.mts → sixHex.d.ts} +2 -2
- package/dist/transformers/sixHex.d.ts.map +1 -0
- package/dist/transformers/tailwindComponent.d.ts +16 -0
- package/dist/transformers/tailwindComponent.d.ts.map +1 -0
- package/dist/transformers/tailwindComponent.mjs +93 -0
- package/dist/transformers/tailwindComponent.mjs.map +1 -0
- package/dist/transformers/{tailwindcss.d.mts → tailwindcss.d.ts} +2 -2
- package/dist/transformers/tailwindcss.d.ts.map +1 -0
- package/dist/transformers/tailwindcss.mjs +2 -54
- package/dist/transformers/tailwindcss.mjs.map +1 -1
- package/dist/transformers/{urlQuery.d.mts → urlQuery.d.ts} +2 -2
- package/dist/transformers/urlQuery.d.ts.map +1 -0
- package/dist/types/{config.d.mts → config.d.ts} +2 -2
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/{index.d.mts → index.d.ts} +1 -1
- package/dist/utils/ast/index.d.ts +4 -0
- package/dist/utils/ast/{parser.d.mts → parser.d.ts} +1 -1
- package/dist/utils/ast/parser.d.ts.map +1 -0
- package/dist/utils/ast/{serializer.d.mts → serializer.d.ts} +1 -1
- package/dist/utils/ast/serializer.d.ts.map +1 -0
- package/dist/utils/ast/{walker.d.mts → walker.d.ts} +1 -1
- package/dist/utils/ast/walker.d.ts.map +1 -0
- package/dist/utils/compileTailwindCss.d.ts +16 -0
- package/dist/utils/compileTailwindCss.d.ts.map +1 -0
- package/dist/utils/compileTailwindCss.mjs +55 -0
- package/dist/utils/compileTailwindCss.mjs.map +1 -0
- package/dist/utils/{decodeStyleEntities.d.mts → decodeStyleEntities.d.ts} +1 -1
- package/dist/utils/decodeStyleEntities.d.ts.map +1 -0
- package/dist/utils/{detect.d.mts → detect.d.ts} +1 -1
- package/dist/utils/detect.d.ts.map +1 -0
- package/dist/utils/{url.d.mts → url.d.ts} +1 -1
- package/dist/utils/url.d.ts.map +1 -0
- package/package.json +13 -6
- package/dist/build.d.mts.map +0 -1
- package/dist/components/utils.d.mts.map +0 -1
- package/dist/composables/defineConfig.d.mts.map +0 -1
- package/dist/composables/renderContext.d.mts.map +0 -1
- package/dist/composables/useConfig.d.mts.map +0 -1
- package/dist/composables/useDoctype.d.mts.map +0 -1
- package/dist/composables/useEvent.d.mts.map +0 -1
- package/dist/composables/useFont.d.mts.map +0 -1
- package/dist/composables/usePlaintext.d.mts.map +0 -1
- package/dist/composables/usePreheader.d.mts.map +0 -1
- package/dist/config/defaults.d.mts.map +0 -1
- package/dist/config/index.d.mts.map +0 -1
- package/dist/events/index.d.mts.map +0 -1
- package/dist/index.d.mts +0 -33
- package/dist/plaintext.d.mts.map +0 -1
- package/dist/plugin.d.mts.map +0 -1
- package/dist/plugins/postcss/mergeMediaQueries.d.mts.map +0 -1
- package/dist/plugins/postcss/pruneVars.d.mts.map +0 -1
- package/dist/plugins/postcss/quoteFontFamilies.d.mts.map +0 -1
- package/dist/plugins/postcss/removeDeclarations.d.mts.map +0 -1
- package/dist/plugins/postcss/resolveProps.d.mts.map +0 -1
- package/dist/plugins/postcss/tailwindCleanup.d.mts.map +0 -1
- package/dist/prepare.d.mts.map +0 -1
- package/dist/render/createRenderer.d.mts.map +0 -1
- package/dist/render/index.d.mts +0 -26
- package/dist/render/index.d.mts.map +0 -1
- package/dist/render/injectFonts.d.mts.map +0 -1
- package/dist/render/plugins/rowSourceLocation.d.mts.map +0 -1
- package/dist/serve.d.mts.map +0 -1
- package/dist/server/compatibility.d.mts.map +0 -1
- package/dist/server/email.d.mts.map +0 -1
- package/dist/server/linter.d.mts.map +0 -1
- package/dist/server/sfc-utils.d.mts.map +0 -1
- package/dist/transformers/addAttributes.d.mts.map +0 -1
- package/dist/transformers/attributeToStyle.d.mts.map +0 -1
- package/dist/transformers/base.d.mts.map +0 -1
- package/dist/transformers/columnWidth.d.mts.map +0 -1
- package/dist/transformers/entities.d.mts.map +0 -1
- package/dist/transformers/filters/defaults.d.mts.map +0 -1
- package/dist/transformers/filters/index.d.mts.map +0 -1
- package/dist/transformers/format.d.mts.map +0 -1
- package/dist/transformers/index.d.mts.map +0 -1
- package/dist/transformers/inlineCSS.d.mts.map +0 -1
- package/dist/transformers/inlineLink.d.mts.map +0 -1
- package/dist/transformers/minify.d.mts.map +0 -1
- package/dist/transformers/msoWidthFromClass.d.mts.map +0 -1
- package/dist/transformers/purgeCSS.d.mts.map +0 -1
- package/dist/transformers/removeAttributes.d.mts.map +0 -1
- package/dist/transformers/replaceStrings.d.mts.map +0 -1
- package/dist/transformers/safeClassNames.d.mts.map +0 -1
- package/dist/transformers/shorthandCSS.d.mts.map +0 -1
- package/dist/transformers/sixHex.d.mts.map +0 -1
- package/dist/transformers/tailwindcss.d.mts.map +0 -1
- package/dist/transformers/urlQuery.d.mts.map +0 -1
- package/dist/types/config.d.mts.map +0 -1
- package/dist/utils/ast/index.d.mts +0 -4
- package/dist/utils/ast/parser.d.mts.map +0 -1
- package/dist/utils/ast/serializer.d.mts.map +0 -1
- package/dist/utils/ast/walker.d.mts.map +0 -1
- package/dist/utils/decodeStyleEntities.d.mts.map +0 -1
- package/dist/utils/detect.d.mts.map +0 -1
- package/dist/utils/url.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</picture>
|
|
8
8
|
</a>
|
|
9
9
|
</p>
|
|
10
|
-
<p>
|
|
10
|
+
<p>The modern email development framework</p>
|
|
11
11
|
<div>
|
|
12
12
|
|
|
13
13
|
[![Version][npm-version-shield]][npm]
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
|
|
21
21
|
## About
|
|
22
22
|
|
|
23
|
-
> **Note:** This repository contains the core code of the Maizzle framework. If you want to
|
|
23
|
+
> **Note:** This repository contains the core code of the Maizzle framework. If you want to start building HTML emails using Maizzle, visit the [Starter repository](https://github.com/maizzle/maizzle).
|
|
24
24
|
|
|
25
|
-
Maizzle is a framework
|
|
25
|
+
Maizzle is a Vite-powered framework for building HTML emails with Vue and Tailwind CSS.
|
|
26
26
|
|
|
27
27
|
## Documentation
|
|
28
28
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MaizzleConfig } from "./types/config.
|
|
1
|
+
import { MaizzleConfig } from "./types/config.js";
|
|
2
2
|
//#region src/build.d.ts
|
|
3
3
|
interface BuildOptions {
|
|
4
4
|
config?: Partial<MaizzleConfig> | string;
|
|
@@ -16,4 +16,4 @@ interface BuildResult {
|
|
|
16
16
|
declare function build(options?: BuildOptions): Promise<BuildResult>;
|
|
17
17
|
//#endregion
|
|
18
18
|
export { BuildOptions, BuildResult, build };
|
|
19
|
-
//# sourceMappingURL=build.d.
|
|
19
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","names":[],"sources":["../src/build.ts"],"mappings":";;UAWiB,YAAA;EACf,MAAA,GAAS,OAAA,CAAQ,aAAA;AAAA;AAAA,UAGF,WAAA;EACf,KAAA;EACA,MAAA,EAAQ,aAAA;AAAA;;;;;;AAFV;iBAWsB,KAAA,CAAM,OAAA,GAAS,YAAA,GAAoB,OAAA,CAAQ,WAAA"}
|
package/dist/build.mjs
CHANGED
|
@@ -64,7 +64,7 @@ async function build(options = {}) {
|
|
|
64
64
|
});
|
|
65
65
|
const templateConfig = rendered.templateConfig;
|
|
66
66
|
const doctype = rendered.doctype ?? templateConfig.doctype ?? "<!DOCTYPE html>";
|
|
67
|
-
if (templateConfig.useTransformers !== false) html = await runTransformers(html, templateConfig, absolutePath, doctype);
|
|
67
|
+
if (templateConfig.useTransformers !== false) html = await runTransformers(html, templateConfig, absolutePath, doctype, rendered.tailwindBlocks);
|
|
68
68
|
html = await events.fireAfterTransform({
|
|
69
69
|
config,
|
|
70
70
|
template,
|
package/dist/build.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.mjs","names":[],"sources":["../src/build.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, cpSync, existsSync, rmSync } from 'node:fs'\nimport { resolve, dirname, basename, relative, join } from 'node:path'\nimport { glob } from 'tinyglobby'\nimport ora from 'ora'\nimport { resolveConfig } from './config/index.ts'\nimport { EventManager } from './events/index.ts'\nimport { runTransformers } from './transformers/index.ts'\nimport { createRenderer } from './render/createRenderer.ts'\nimport { createPlaintext } from './plaintext.ts'\nimport type { MaizzleConfig } from './types/index.ts'\n\nexport interface BuildOptions {\n config?: Partial<MaizzleConfig> | string\n}\n\nexport interface BuildResult {\n files: string[]\n config: MaizzleConfig\n}\n\n/**\n * Build all SFC email templates to HTML files.\n *\n * Creates a single Renderer instance, then loops through each template\n * calling render → transformers → write to disk.\n */\nexport async function build(options: BuildOptions = {}): Promise<BuildResult> {\n const start = Date.now()\n const spinner = ora({ text: 'Building templates...', spinner: 'circleHalves' }).start()\n\n const config = await resolveConfig(options.config)\n\n const events = new EventManager()\n events.registerConfig(config)\n await events.fireBeforeCreate({ config })\n\n const outputPath = resolve(config.output?.path ?? 'dist')\n const outputExtension = config.output?.extension ?? 'html'\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const contentBase = computeContentBase(contentPatterns)\n const templateFiles = await glob(contentPatterns)\n\n if (templateFiles.length === 0) {\n spinner.succeed('No templates found')\n return { files: [], config }\n }\n\n // Clear the output directory before writing fresh output\n if (existsSync(outputPath)) {\n rmSync(outputPath, { recursive: true, force: true })\n }\n\n const renderer = await createRenderer({ markdown: config.markdown, root: config.root, componentDirs: [config.components?.source ?? []].flat(), vite: config.vite })\n const outputFiles: string[] = []\n\n try {\n for (const templatePath of templateFiles) {\n const absolutePath = resolve(templatePath)\n let template = readFileSync(absolutePath, 'utf-8')\n\n template = await events.fireBeforeRender({ config, template })\n\n const rendered = await renderer.render(absolutePath, config)\n\n let html = await events.fireAfterRender({ config, template, html: rendered.html })\n\n // Use the per-template merged config (from defineConfig() in the SFC) so that\n // template-level overrides like css.safe: false are respected by transformers.\n const templateConfig = rendered.templateConfig\n\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n\n if (templateConfig.useTransformers !== false) {\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n }\n\n html = await events.fireAfterTransform({ config, template, html })\n html = `${doctype}\\n${html}`\n\n const outputFilePath = resolveOutputPath(templatePath, outputPath, outputExtension, contentBase)\n mkdirSync(dirname(outputFilePath), { recursive: true })\n writeFileSync(outputFilePath, html)\n outputFiles.push(outputFilePath)\n\n // Generate plaintext version if configured\n const globalPlaintext = templateConfig.plaintext\n const sfcPlaintext = rendered.plaintext\n\n if (globalPlaintext || sfcPlaintext) {\n const stripOptions = typeof globalPlaintext === 'object' ? globalPlaintext : {}\n const plaintext = createPlaintext(html, stripOptions)\n const ptExtension = sfcPlaintext?.extension ?? 'txt'\n\n let ptOutputPath: string\n\n if (sfcPlaintext?.destination) {\n const name = basename(templatePath).replace(/\\.(vue|md)$/, '')\n ptOutputPath = join(resolve(sfcPlaintext.destination), `${name}.${ptExtension}`)\n } else if (typeof globalPlaintext === 'string') {\n ptOutputPath = resolveOutputPath(templatePath, resolve(globalPlaintext), ptExtension, contentBase)\n } else {\n ptOutputPath = resolveOutputPath(templatePath, outputPath, ptExtension, contentBase)\n }\n\n mkdirSync(dirname(ptOutputPath), { recursive: true })\n writeFileSync(ptOutputPath, plaintext)\n }\n\n // Register SFC event handlers that were collected during render\n for (const { name, handler } of rendered.sfcEventHandlers) {\n events.on(name, handler)\n }\n\n events.clearSfcHandlers()\n }\n\n await copyStatic(config, outputPath)\n await events.fireAfterBuild({ files: outputFiles, config })\n } finally {\n await renderer.close()\n }\n\n const duration = ((Date.now() - start) / 1000).toFixed(2)\n const count = outputFiles.length\n spinner.stopAndPersist({\n symbol: '✅',\n text: `Built ${count} template${count !== 1 ? 's' : ''} in ${duration}s`,\n })\n\n return { files: outputFiles, config }\n}\n\n/**\n * Extract the static (non-glob) prefix from content patterns.\n *\n * For example, `['/abs/path/emails/**\\/*.vue']` → `'/abs/path/emails'`\n *\n * This is used to strip the content base from template paths\n * so the output preserves only the subdirectory structure.\n */\nfunction computeContentBase(patterns: string[]): string {\n // Use the first non-negated pattern\n const pattern = patterns.find(p => !p.startsWith('!')) ?? patterns[0]\n\n // Split on first glob character (* { ? [) and take the directory part\n const staticPart = pattern.split(/[*{?[]/)[0]\n\n // Ensure we have a clean directory path (not a partial segment)\n return resolve(staticPart.endsWith('/') ? staticPart : dirname(staticPart))\n}\n\nfunction resolveOutputPath(templatePath: string, outputDir: string, extension: string, contentBase: string): string {\n const name = basename(templatePath).replace(/\\.(vue|md)$/, '')\n const absTemplate = resolve(templatePath)\n const rel = relative(contentBase, dirname(absTemplate))\n\n return join(outputDir, rel, `${name}.${extension}`)\n}\n\nasync function copyStatic(config: MaizzleConfig, outputPath: string): Promise<void> {\n const sources = config.static?.source ?? ['public/**/*.*']\n const destination = config.static?.destination ?? 'public'\n\n const files = await glob(sources)\n\n for (const file of files) {\n const destPath = join(outputPath, destination, relative(dirname(sources[0]).replace(/\\*.*$/, ''), file))\n const destDir = dirname(destPath)\n\n if (!existsSync(destDir)) {\n mkdirSync(destDir, { recursive: true })\n }\n\n cpSync(file, destPath)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0BA,eAAsB,MAAM,UAAwB,EAAE,EAAwB;CAC5E,MAAM,QAAQ,KAAK,KAAK;CACxB,MAAM,UAAU,IAAI;EAAE,MAAM;EAAyB,SAAS;EAAgB,CAAC,CAAC,OAAO;CAEvF,MAAM,SAAS,MAAM,cAAc,QAAQ,OAAO;CAElD,MAAM,SAAS,IAAI,cAAc;AACjC,QAAO,eAAe,OAAO;AAC7B,OAAM,OAAO,iBAAiB,EAAE,QAAQ,CAAC;CAEzC,MAAM,aAAa,QAAQ,OAAO,QAAQ,QAAQ,OAAO;CACzD,MAAM,kBAAkB,OAAO,QAAQ,aAAa;CAEpD,MAAM,kBAAkB,OAAO,WAAW,CAAC,kBAAkB;CAC7D,MAAM,cAAc,mBAAmB,gBAAgB;CACvD,MAAM,gBAAgB,MAAM,KAAK,gBAAgB;AAEjD,KAAI,cAAc,WAAW,GAAG;AAC9B,UAAQ,QAAQ,qBAAqB;AACrC,SAAO;GAAE,OAAO,EAAE;GAAE;GAAQ;;AAI9B,KAAI,WAAW,WAAW,CACxB,QAAO,YAAY;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;CAGtD,MAAM,WAAW,MAAM,eAAe;EAAE,UAAU,OAAO;EAAU,MAAM,OAAO;EAAM,eAAe,CAAC,OAAO,YAAY,UAAU,EAAE,CAAC,CAAC,MAAM;EAAE,MAAM,OAAO;EAAM,CAAC;CACnK,MAAM,cAAwB,EAAE;AAEhC,KAAI;AACF,OAAK,MAAM,gBAAgB,eAAe;GACxC,MAAM,eAAe,QAAQ,aAAa;GAC1C,IAAI,WAAW,aAAa,cAAc,QAAQ;AAElD,cAAW,MAAM,OAAO,iBAAiB;IAAE;IAAQ;IAAU,CAAC;GAE9D,MAAM,WAAW,MAAM,SAAS,OAAO,cAAc,OAAO;GAE5D,IAAI,OAAO,MAAM,OAAO,gBAAgB;IAAE;IAAQ;IAAU,MAAM,SAAS;IAAM,CAAC;GAIlF,MAAM,iBAAiB,SAAS;GAEhC,MAAM,UAAU,SAAS,WAAW,eAAe,WAAW;AAE9D,OAAI,eAAe,oBAAoB,MACrC,QAAO,MAAM,gBAAgB,MAAM,gBAAgB,cAAc,
|
|
1
|
+
{"version":3,"file":"build.mjs","names":[],"sources":["../src/build.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, cpSync, existsSync, rmSync } from 'node:fs'\nimport { resolve, dirname, basename, relative, join } from 'node:path'\nimport { glob } from 'tinyglobby'\nimport ora from 'ora'\nimport { resolveConfig } from './config/index.ts'\nimport { EventManager } from './events/index.ts'\nimport { runTransformers } from './transformers/index.ts'\nimport { createRenderer } from './render/createRenderer.ts'\nimport { createPlaintext } from './plaintext.ts'\nimport type { MaizzleConfig } from './types/index.ts'\n\nexport interface BuildOptions {\n config?: Partial<MaizzleConfig> | string\n}\n\nexport interface BuildResult {\n files: string[]\n config: MaizzleConfig\n}\n\n/**\n * Build all SFC email templates to HTML files.\n *\n * Creates a single Renderer instance, then loops through each template\n * calling render → transformers → write to disk.\n */\nexport async function build(options: BuildOptions = {}): Promise<BuildResult> {\n const start = Date.now()\n const spinner = ora({ text: 'Building templates...', spinner: 'circleHalves' }).start()\n\n const config = await resolveConfig(options.config)\n\n const events = new EventManager()\n events.registerConfig(config)\n await events.fireBeforeCreate({ config })\n\n const outputPath = resolve(config.output?.path ?? 'dist')\n const outputExtension = config.output?.extension ?? 'html'\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const contentBase = computeContentBase(contentPatterns)\n const templateFiles = await glob(contentPatterns)\n\n if (templateFiles.length === 0) {\n spinner.succeed('No templates found')\n return { files: [], config }\n }\n\n // Clear the output directory before writing fresh output\n if (existsSync(outputPath)) {\n rmSync(outputPath, { recursive: true, force: true })\n }\n\n const renderer = await createRenderer({ markdown: config.markdown, root: config.root, componentDirs: [config.components?.source ?? []].flat(), vite: config.vite })\n const outputFiles: string[] = []\n\n try {\n for (const templatePath of templateFiles) {\n const absolutePath = resolve(templatePath)\n let template = readFileSync(absolutePath, 'utf-8')\n\n template = await events.fireBeforeRender({ config, template })\n\n const rendered = await renderer.render(absolutePath, config)\n\n let html = await events.fireAfterRender({ config, template, html: rendered.html })\n\n // Use the per-template merged config (from defineConfig() in the SFC) so that\n // template-level overrides like css.safe: false are respected by transformers.\n const templateConfig = rendered.templateConfig\n\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n\n if (templateConfig.useTransformers !== false) {\n html = await runTransformers(html, templateConfig, absolutePath, doctype, rendered.tailwindBlocks)\n }\n\n html = await events.fireAfterTransform({ config, template, html })\n html = `${doctype}\\n${html}`\n\n const outputFilePath = resolveOutputPath(templatePath, outputPath, outputExtension, contentBase)\n mkdirSync(dirname(outputFilePath), { recursive: true })\n writeFileSync(outputFilePath, html)\n outputFiles.push(outputFilePath)\n\n // Generate plaintext version if configured\n const globalPlaintext = templateConfig.plaintext\n const sfcPlaintext = rendered.plaintext\n\n if (globalPlaintext || sfcPlaintext) {\n const stripOptions = typeof globalPlaintext === 'object' ? globalPlaintext : {}\n const plaintext = createPlaintext(html, stripOptions)\n const ptExtension = sfcPlaintext?.extension ?? 'txt'\n\n let ptOutputPath: string\n\n if (sfcPlaintext?.destination) {\n const name = basename(templatePath).replace(/\\.(vue|md)$/, '')\n ptOutputPath = join(resolve(sfcPlaintext.destination), `${name}.${ptExtension}`)\n } else if (typeof globalPlaintext === 'string') {\n ptOutputPath = resolveOutputPath(templatePath, resolve(globalPlaintext), ptExtension, contentBase)\n } else {\n ptOutputPath = resolveOutputPath(templatePath, outputPath, ptExtension, contentBase)\n }\n\n mkdirSync(dirname(ptOutputPath), { recursive: true })\n writeFileSync(ptOutputPath, plaintext)\n }\n\n // Register SFC event handlers that were collected during render\n for (const { name, handler } of rendered.sfcEventHandlers) {\n events.on(name, handler)\n }\n\n events.clearSfcHandlers()\n }\n\n await copyStatic(config, outputPath)\n await events.fireAfterBuild({ files: outputFiles, config })\n } finally {\n await renderer.close()\n }\n\n const duration = ((Date.now() - start) / 1000).toFixed(2)\n const count = outputFiles.length\n spinner.stopAndPersist({\n symbol: '✅',\n text: `Built ${count} template${count !== 1 ? 's' : ''} in ${duration}s`,\n })\n\n return { files: outputFiles, config }\n}\n\n/**\n * Extract the static (non-glob) prefix from content patterns.\n *\n * For example, `['/abs/path/emails/**\\/*.vue']` → `'/abs/path/emails'`\n *\n * This is used to strip the content base from template paths\n * so the output preserves only the subdirectory structure.\n */\nfunction computeContentBase(patterns: string[]): string {\n // Use the first non-negated pattern\n const pattern = patterns.find(p => !p.startsWith('!')) ?? patterns[0]\n\n // Split on first glob character (* { ? [) and take the directory part\n const staticPart = pattern.split(/[*{?[]/)[0]\n\n // Ensure we have a clean directory path (not a partial segment)\n return resolve(staticPart.endsWith('/') ? staticPart : dirname(staticPart))\n}\n\nfunction resolveOutputPath(templatePath: string, outputDir: string, extension: string, contentBase: string): string {\n const name = basename(templatePath).replace(/\\.(vue|md)$/, '')\n const absTemplate = resolve(templatePath)\n const rel = relative(contentBase, dirname(absTemplate))\n\n return join(outputDir, rel, `${name}.${extension}`)\n}\n\nasync function copyStatic(config: MaizzleConfig, outputPath: string): Promise<void> {\n const sources = config.static?.source ?? ['public/**/*.*']\n const destination = config.static?.destination ?? 'public'\n\n const files = await glob(sources)\n\n for (const file of files) {\n const destPath = join(outputPath, destination, relative(dirname(sources[0]).replace(/\\*.*$/, ''), file))\n const destDir = dirname(destPath)\n\n if (!existsSync(destDir)) {\n mkdirSync(destDir, { recursive: true })\n }\n\n cpSync(file, destPath)\n }\n}\n"],"mappings":"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"}
|
package/dist/components/Body.vue
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { createStaticVNode, inject, useAttrs, useSlots } from 'vue'
|
|
3
3
|
import type { PropType } from 'vue'
|
|
4
|
+
import { outlookFallbackProp } from './utils.ts'
|
|
5
|
+
import { useOutlookFallback } from '../composables/useOutlookFallback'
|
|
4
6
|
|
|
5
7
|
defineOptions({ inheritAttrs: false })
|
|
6
8
|
|
|
@@ -65,9 +67,12 @@ const props = defineProps({
|
|
|
65
67
|
ariaLabel: {
|
|
66
68
|
type: String,
|
|
67
69
|
default: undefined
|
|
68
|
-
}
|
|
70
|
+
},
|
|
71
|
+
outlookFallback: outlookFallbackProp,
|
|
69
72
|
})
|
|
70
73
|
|
|
74
|
+
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
75
|
+
|
|
71
76
|
const htmlLang = inject<string>('htmlLang', 'en')
|
|
72
77
|
|
|
73
78
|
const render = () => {
|
|
@@ -78,10 +83,12 @@ const render = () => {
|
|
|
78
83
|
const lang = props.xmlLang ?? htmlLang
|
|
79
84
|
|
|
80
85
|
const parts = [
|
|
81
|
-
`xml:lang="${lang}"`,
|
|
82
86
|
`dir="${props.dir}"`,
|
|
83
87
|
'style="margin: 0; padding: 0; width: 100%; word-break: break-word;"',
|
|
84
88
|
]
|
|
89
|
+
if (outlookFallback) {
|
|
90
|
+
parts.unshift(`xml:lang="${lang}"`)
|
|
91
|
+
}
|
|
85
92
|
|
|
86
93
|
if (extraAttrs) {
|
|
87
94
|
parts.push(extraAttrs)
|
|
@@ -3,6 +3,8 @@ import { computed, useAttrs } from 'vue'
|
|
|
3
3
|
import type { PropType } from 'vue'
|
|
4
4
|
import { twMerge } from 'tailwind-merge'
|
|
5
5
|
import Outlook from './Outlook.vue'
|
|
6
|
+
import { outlookFallbackProp } from './utils.ts'
|
|
7
|
+
import { useOutlookFallback } from '../composables/useOutlookFallback'
|
|
6
8
|
|
|
7
9
|
defineOptions({ inheritAttrs: false })
|
|
8
10
|
|
|
@@ -103,9 +105,12 @@ const props = defineProps({
|
|
|
103
105
|
iconClass: {
|
|
104
106
|
type: String,
|
|
105
107
|
default: ''
|
|
106
|
-
}
|
|
108
|
+
},
|
|
109
|
+
outlookFallback: outlookFallbackProp,
|
|
107
110
|
})
|
|
108
111
|
|
|
112
|
+
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
113
|
+
|
|
109
114
|
const parsedIconWidth = computed(() => parseInt(String(props.iconWidth), 10))
|
|
110
115
|
|
|
111
116
|
const alignClass = computed(() => props.align ? ({
|
|
@@ -171,21 +176,21 @@ const mergedClass = computed(() => twMerge(defaultClasses.value, attrs.class as
|
|
|
171
176
|
:class="mergedClass"
|
|
172
177
|
>
|
|
173
178
|
<template v-if="!isLink">
|
|
174
|
-
<Outlook><i class="mso-font-width-[150%]" :style="`mso-text-raise: ${msoPb};`" hidden> </i></Outlook>
|
|
179
|
+
<Outlook v-if="outlookFallback"><i class="mso-font-width-[150%]" :style="`mso-text-raise: ${msoPb};`" hidden> </i></Outlook>
|
|
175
180
|
<template v-if="icon && iconPosition === 'left'">
|
|
176
|
-
<span :style="`mso-text-raise: ${msoPt}`">
|
|
181
|
+
<span :style="outlookFallback ? `mso-text-raise: ${msoPt}` : undefined">
|
|
177
182
|
<img :src="icon" :width="parsedIconWidth" :class="`align-baseline max-w-full ${iconClass}`" alt="">
|
|
178
183
|
</span>
|
|
179
|
-
<Outlook><i class="mso-font-width-[30%]" hidden> ​</i></Outlook>
|
|
184
|
+
<Outlook v-if="outlookFallback"><i class="mso-font-width-[30%]" hidden> ​</i></Outlook>
|
|
180
185
|
</template>
|
|
181
|
-
<span :class="icon ? (iconPosition === 'right' ? 'mr-2' : 'ml-2') : ''" :style="`mso-text-raise: ${msoPt}`"><slot /></span>
|
|
186
|
+
<span :class="icon ? (iconPosition === 'right' ? 'mr-2' : 'ml-2') : ''" :style="outlookFallback ? `mso-text-raise: ${msoPt}` : undefined"><slot /></span>
|
|
182
187
|
<template v-if="icon && iconPosition === 'right'">
|
|
183
|
-
<Outlook><i class="mso-font-width-[30%]" hidden> ​</i></Outlook>
|
|
184
|
-
<span :style="`mso-text-raise: ${msoPt}`">
|
|
188
|
+
<Outlook v-if="outlookFallback"><i class="mso-font-width-[30%]" hidden> ​</i></Outlook>
|
|
189
|
+
<span :style="outlookFallback ? `mso-text-raise: ${msoPt}` : undefined">
|
|
185
190
|
<img :src="icon" :width="parsedIconWidth" :class="`align-baseline max-w-full ${iconClass}`" alt="">
|
|
186
191
|
</span>
|
|
187
192
|
</template>
|
|
188
|
-
<Outlook><i class="mso-font-width-[150%]" hidden> ​</i></Outlook>
|
|
193
|
+
<Outlook v-if="outlookFallback"><i class="mso-font-width-[150%]" hidden> ​</i></Outlook>
|
|
189
194
|
</template>
|
|
190
195
|
<template v-else>
|
|
191
196
|
<slot />
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, createStaticVNode, inject, useAttrs } from 'vue'
|
|
3
3
|
import type { ComputedRef } from 'vue'
|
|
4
|
-
import {
|
|
4
|
+
import { twMerge } from 'tailwind-merge'
|
|
5
|
+
import { nextId, normalizeToPixels, outlookFallbackProp } from './utils.ts'
|
|
6
|
+
import { useOutlookFallback } from '../composables/useOutlookFallback'
|
|
5
7
|
|
|
6
8
|
defineOptions({ inheritAttrs: false })
|
|
7
9
|
|
|
@@ -29,9 +31,12 @@ const props = defineProps({
|
|
|
29
31
|
msoStyle: {
|
|
30
32
|
type: String,
|
|
31
33
|
default: undefined
|
|
32
|
-
}
|
|
34
|
+
},
|
|
35
|
+
outlookFallback: outlookFallbackProp,
|
|
33
36
|
})
|
|
34
37
|
|
|
38
|
+
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
39
|
+
|
|
35
40
|
const columnCount = inject<ComputedRef<number> | null>('columnCount', null)
|
|
36
41
|
|
|
37
42
|
const count = computed(() => columnCount?.value ?? 2)
|
|
@@ -49,9 +54,17 @@ const msoWidth = computed(() => {
|
|
|
49
54
|
return `__MAIZZLE_COLW_${colId}__`
|
|
50
55
|
})
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Baseline display/typography lives in classes — not inline `:style` —
|
|
59
|
+
* so the user can override any of them via tailwind utilities. Inline
|
|
60
|
+
* `display: inline-block` would silently shadow a class like
|
|
61
|
+
* `inline-table` during CSS inlining; routing both through twMerge lets
|
|
62
|
+
* the user's utility cleanly replace ours instead of being dropped.
|
|
63
|
+
*/
|
|
64
|
+
const baseClass = 'inline-block align-top text-base'
|
|
65
|
+
const mergedClass = computed(() => twMerge(baseClass, (attrs.class as string) ?? ''))
|
|
66
|
+
|
|
67
|
+
const styles = computed(() => `min-width: ${minWidth.value};`)
|
|
55
68
|
|
|
56
69
|
const tdStyle = computed(() => {
|
|
57
70
|
const parts = [`width: ${msoWidth.value}`, 'vertical-align: top']
|
|
@@ -71,14 +84,15 @@ const MsoAfter = () => createStaticVNode(
|
|
|
71
84
|
</script>
|
|
72
85
|
|
|
73
86
|
<template>
|
|
74
|
-
<MsoBefore />
|
|
87
|
+
<MsoBefore v-if="outlookFallback" />
|
|
75
88
|
<div
|
|
76
|
-
v-bind="attrs"
|
|
89
|
+
v-bind="{ ...attrs, class: undefined }"
|
|
90
|
+
:class="mergedClass"
|
|
77
91
|
:style="styles"
|
|
78
92
|
:data-maizzle-cw-id="colId"
|
|
79
93
|
:data-maizzle-cw-count="useMarker ? count : null"
|
|
80
94
|
>
|
|
81
95
|
<slot />
|
|
82
96
|
</div>
|
|
83
|
-
<MsoAfter />
|
|
97
|
+
<MsoAfter v-if="outlookFallback" />
|
|
84
98
|
</template>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, provide, createStaticVNode, useAttrs } from 'vue'
|
|
3
3
|
import { twMerge } from 'tailwind-merge'
|
|
4
|
-
import { hasWidthUtility, nextId, normalizeToPixels } from './utils.ts'
|
|
4
|
+
import { hasWidthUtility, nextId, normalizeToPixels, outlookFallbackProp } from './utils.ts'
|
|
5
|
+
import { useOutlookFallback } from '../composables/useOutlookFallback'
|
|
5
6
|
|
|
6
7
|
defineOptions({ inheritAttrs: false })
|
|
7
8
|
|
|
@@ -33,12 +34,15 @@ const props = defineProps({
|
|
|
33
34
|
msoWidth: {
|
|
34
35
|
type: [String, Number],
|
|
35
36
|
default: null
|
|
36
|
-
}
|
|
37
|
+
},
|
|
38
|
+
outlookFallback: outlookFallbackProp,
|
|
37
39
|
})
|
|
38
40
|
|
|
41
|
+
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
42
|
+
|
|
39
43
|
provide('containerWidth', computed(() => props.width))
|
|
40
44
|
|
|
41
|
-
const useMarker = props.width == null && props.msoWidth == null
|
|
45
|
+
const useMarker = outlookFallback && props.width == null && props.msoWidth == null
|
|
42
46
|
const msoId = useMarker ? nextId('c') : null
|
|
43
47
|
|
|
44
48
|
const styles = computed(() => {
|
|
@@ -77,7 +81,7 @@ const MsoAfter = () => createStaticVNode(
|
|
|
77
81
|
</script>
|
|
78
82
|
|
|
79
83
|
<template>
|
|
80
|
-
<MsoBefore />
|
|
84
|
+
<MsoBefore v-if="outlookFallback" />
|
|
81
85
|
<div
|
|
82
86
|
v-bind="{ ...attrs, class: undefined }"
|
|
83
87
|
:class="mergedClass"
|
|
@@ -87,5 +91,5 @@ const MsoAfter = () => createStaticVNode(
|
|
|
87
91
|
>
|
|
88
92
|
<slot />
|
|
89
93
|
</div>
|
|
90
|
-
<MsoAfter />
|
|
94
|
+
<MsoAfter v-if="outlookFallback" />
|
|
91
95
|
</template>
|
package/dist/components/Html.vue
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { createStaticVNode, provide, useAttrs, useSlots } from 'vue'
|
|
3
3
|
import type { PropType } from 'vue'
|
|
4
|
+
import { outlookFallbackProp } from './utils.ts'
|
|
5
|
+
import { useOutlookFallback } from '../composables/useOutlookFallback'
|
|
4
6
|
|
|
5
7
|
defineOptions({ inheritAttrs: false })
|
|
6
8
|
|
|
@@ -65,9 +67,12 @@ const props = defineProps({
|
|
|
65
67
|
xmlns: {
|
|
66
68
|
type: [Boolean, String],
|
|
67
69
|
default: true
|
|
68
|
-
}
|
|
70
|
+
},
|
|
71
|
+
outlookFallback: outlookFallbackProp,
|
|
69
72
|
})
|
|
70
73
|
|
|
74
|
+
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
75
|
+
|
|
71
76
|
provide('htmlLang', props.lang)
|
|
72
77
|
|
|
73
78
|
const render = () => {
|
|
@@ -80,7 +85,7 @@ const render = () => {
|
|
|
80
85
|
`dir="${props.dir}"`,
|
|
81
86
|
]
|
|
82
87
|
|
|
83
|
-
if (props.xmlns !== false && props.xmlns !== 'false') {
|
|
88
|
+
if (outlookFallback && props.xmlns !== false && props.xmlns !== 'false') {
|
|
84
89
|
parts.push(
|
|
85
90
|
'xmlns:v="urn:schemas-microsoft-com:vml"',
|
|
86
91
|
'xmlns:o="urn:schemas-microsoft-com:office:office"',
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, useAttrs, createStaticVNode, type PropType } from 'vue'
|
|
3
3
|
import { twMerge } from 'tailwind-merge'
|
|
4
|
+
import { outlookFallbackProp } from './utils.ts'
|
|
5
|
+
import { useOutlookFallback } from '../composables/useOutlookFallback'
|
|
4
6
|
|
|
5
7
|
defineOptions({ inheritAttrs: false })
|
|
6
8
|
|
|
@@ -52,27 +54,20 @@ const props = defineProps({
|
|
|
52
54
|
ariaLabel: {
|
|
53
55
|
type: String,
|
|
54
56
|
default: undefined
|
|
55
|
-
}
|
|
57
|
+
},
|
|
58
|
+
outlookFallback: outlookFallbackProp,
|
|
56
59
|
})
|
|
57
60
|
|
|
61
|
+
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
62
|
+
|
|
58
63
|
const attrs = useAttrs()
|
|
59
64
|
const bodyMergedClass = computed(() => twMerge('m-0 p-0 size-full [word-break:break-word]', props.bodyClass))
|
|
60
65
|
const articleMergedClass = computed(() => twMerge('[font-size:max(16px,1rem)] font-inter', attrs.class as string))
|
|
61
66
|
|
|
62
67
|
const EmptyHead = () => createStaticVNode('<head></head>', 1)
|
|
63
|
-
</script>
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<EmptyHead v-if="props.doubleHead === true || props.doubleHead === 'true'" />
|
|
68
|
-
<head>
|
|
69
|
-
<meta charset="utf-8">
|
|
70
|
-
<meta name="x-apple-disable-message-reformatting">
|
|
71
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
72
|
-
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no, url=no">
|
|
73
|
-
<meta name="color-scheme" content="light dark">
|
|
74
|
-
<meta name="supported-color-schemes" content="light dark">
|
|
75
|
-
<!--[if mso]>
|
|
69
|
+
const MsoHead = () => createStaticVNode(
|
|
70
|
+
`<!--[if mso]>
|
|
76
71
|
<noscript>
|
|
77
72
|
<xml>
|
|
78
73
|
<o:OfficeDocumentSettings xmlns:o="urn:schemas-microsoft-com:office:office">
|
|
@@ -84,7 +79,27 @@ const EmptyHead = () => createStaticVNode('<head></head>', 1)
|
|
|
84
79
|
td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;}
|
|
85
80
|
.mso-break-all {word-break: break-all;}
|
|
86
81
|
</style>
|
|
87
|
-
<![endif]
|
|
82
|
+
<![endif]-->`,
|
|
83
|
+
1
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const htmlXmlns = computed(() => outlookFallback ? {
|
|
87
|
+
'xmlns:v': 'urn:schemas-microsoft-com:vml',
|
|
88
|
+
'xmlns:o': 'urn:schemas-microsoft-com:office:office',
|
|
89
|
+
} : {})
|
|
90
|
+
</script>
|
|
91
|
+
|
|
92
|
+
<template>
|
|
93
|
+
<html :lang="lang" :dir="dir" v-bind="htmlXmlns">
|
|
94
|
+
<EmptyHead v-if="props.doubleHead === true || props.doubleHead === 'true'" />
|
|
95
|
+
<head>
|
|
96
|
+
<meta charset="utf-8">
|
|
97
|
+
<meta name="x-apple-disable-message-reformatting">
|
|
98
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
99
|
+
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no, url=no">
|
|
100
|
+
<meta name="color-scheme" content="light dark">
|
|
101
|
+
<meta name="supported-color-schemes" content="light dark">
|
|
102
|
+
<MsoHead v-if="outlookFallback" />
|
|
88
103
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
89
104
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous">
|
|
90
105
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet" media="screen">
|
|
@@ -96,7 +111,7 @@ const EmptyHead = () => createStaticVNode('<head></head>', 1)
|
|
|
96
111
|
}
|
|
97
112
|
</style>
|
|
98
113
|
</head>
|
|
99
|
-
<body :xml:lang="lang" :class="bodyMergedClass">
|
|
114
|
+
<body :xml:lang="outlookFallback ? lang : null" :class="bodyMergedClass">
|
|
100
115
|
<div
|
|
101
116
|
role="article"
|
|
102
117
|
aria-roledescription="email"
|
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
hasWidthInStyle,
|
|
7
7
|
hasWidthUtility,
|
|
8
8
|
nextId,
|
|
9
|
-
normalizeToPixels
|
|
9
|
+
normalizeToPixels,
|
|
10
|
+
outlookFallbackProp
|
|
10
11
|
} from './utils.ts'
|
|
12
|
+
import { useOutlookFallback } from '../composables/useOutlookFallback'
|
|
11
13
|
|
|
12
14
|
defineOptions({ inheritAttrs: false })
|
|
13
15
|
|
|
@@ -60,8 +62,11 @@ const props = defineProps({
|
|
|
60
62
|
type: String,
|
|
61
63
|
default: '0,-60px,0,0'
|
|
62
64
|
},
|
|
65
|
+
outlookFallback: outlookFallbackProp,
|
|
63
66
|
})
|
|
64
67
|
|
|
68
|
+
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
69
|
+
|
|
65
70
|
const userStyle = computed(() => {
|
|
66
71
|
const s = attrs.style
|
|
67
72
|
if (!s) return ''
|
|
@@ -132,9 +137,9 @@ const VmlAfter = () => createStaticVNode('<!--[if mso]></v:textbox></v:rect><![e
|
|
|
132
137
|
<table style="max-height: 0; position: relative; opacity: 0.999;">
|
|
133
138
|
<tr>
|
|
134
139
|
<td :style="tdStyle">
|
|
135
|
-
<VmlBefore />
|
|
140
|
+
<VmlBefore v-if="outlookFallback" />
|
|
136
141
|
<slot name="overlay" />
|
|
137
|
-
<VmlAfter />
|
|
142
|
+
<VmlAfter v-if="outlookFallback" />
|
|
138
143
|
</td>
|
|
139
144
|
</tr>
|
|
140
145
|
</table>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createStaticVNode } from 'vue'
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
inheritAttrs: false,
|
|
6
|
+
props: {
|
|
7
|
+
/**
|
|
8
|
+
* Raw content to emit verbatim.
|
|
9
|
+
*
|
|
10
|
+
* Auto-populated from slot content by
|
|
11
|
+
* the `maizzle:raw-extract` Vite plugin
|
|
12
|
+
* before Vue compiles the template,
|
|
13
|
+
* so `{{ }}` and other Vue/ESP
|
|
14
|
+
* syntax pass through untouched.
|
|
15
|
+
*/
|
|
16
|
+
content: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: '',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
setup(props) {
|
|
22
|
+
if (!props.content) {
|
|
23
|
+
return () => createStaticVNode('', 0)
|
|
24
|
+
}
|
|
25
|
+
return () => createStaticVNode(props.content, 1)
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
</script>
|
package/dist/components/Row.vue
CHANGED
|
@@ -5,8 +5,10 @@ const warnedLocations = new Set<string>()
|
|
|
5
5
|
<script setup lang="ts">
|
|
6
6
|
import { Comment, Text, computed, createStaticVNode, provide, useAttrs, useSlots, Fragment } from 'vue'
|
|
7
7
|
import type { VNode } from 'vue'
|
|
8
|
+
import { twMerge } from 'tailwind-merge'
|
|
8
9
|
import Column from './Column.vue'
|
|
9
|
-
import { hasWidthInStyle, hasWidthUtility, normalizeToPixels } from './utils.ts'
|
|
10
|
+
import { hasWidthInStyle, hasWidthUtility, normalizeToPixels, outlookFallbackProp } from './utils.ts'
|
|
11
|
+
import { useOutlookFallback } from '../composables/useOutlookFallback'
|
|
10
12
|
|
|
11
13
|
defineOptions({ inheritAttrs: false })
|
|
12
14
|
|
|
@@ -35,9 +37,12 @@ const props = defineProps({
|
|
|
35
37
|
cols: {
|
|
36
38
|
type: Number,
|
|
37
39
|
default: null
|
|
38
|
-
}
|
|
40
|
+
},
|
|
41
|
+
outlookFallback: outlookFallbackProp,
|
|
39
42
|
})
|
|
40
43
|
|
|
44
|
+
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
45
|
+
|
|
41
46
|
const slots = useSlots()
|
|
42
47
|
|
|
43
48
|
function countChildren(vnodes: VNode[]): number {
|
|
@@ -118,15 +123,20 @@ const colWidthSource = computed(() => {
|
|
|
118
123
|
})
|
|
119
124
|
|
|
120
125
|
const restAttrs = computed(() => {
|
|
121
|
-
const { style: _, 'data-maizzle-loc':
|
|
126
|
+
const { style: _, class: __, 'data-maizzle-loc': ___, ...rest } = attrs
|
|
122
127
|
return rest
|
|
123
128
|
})
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
/**
|
|
131
|
+
* `font-size: 0;` removes the whitespace gap between inline-block
|
|
132
|
+
* children. Lives in a class so users can override (e.g. via a custom
|
|
133
|
+
* `text-*`) and twMerge resolves the conflict cleanly instead of the
|
|
134
|
+
* inline declaration silently shadowing the user's class.
|
|
135
|
+
*/
|
|
136
|
+
const baseClass = 'text-0'
|
|
137
|
+
const mergedClass = computed(() => twMerge(baseClass, (attrs.class as string) ?? ''))
|
|
138
|
+
|
|
139
|
+
const divStyle = computed(() => userStyle.value || undefined)
|
|
130
140
|
|
|
131
141
|
const MsoBefore = () => createStaticVNode(
|
|
132
142
|
'<!--[if mso]><table role="none" cellpadding="0" cellspacing="0" style="width: 100%"><tr><![endif]-->',
|
|
@@ -144,19 +154,21 @@ if (hasMeaningfulContent(initialChildren) && !hasColumnChild(initialChildren)) {
|
|
|
144
154
|
if (!warnedLocations.has(loc)) {
|
|
145
155
|
warnedLocations.add(loc)
|
|
146
156
|
const display = loc.split('/').pop() ?? loc
|
|
147
|
-
|
|
157
|
+
const suffix = outlookFallback ? ' Layout will break in Outlook.' : ''
|
|
158
|
+
console.warn(`[maizzle] <Row> in ${display} has no <Column> inside it.${suffix}`)
|
|
148
159
|
}
|
|
149
160
|
}
|
|
150
161
|
</script>
|
|
151
162
|
|
|
152
163
|
<template>
|
|
153
|
-
<MsoBefore />
|
|
164
|
+
<MsoBefore v-if="outlookFallback" />
|
|
154
165
|
<div
|
|
155
166
|
v-bind="restAttrs"
|
|
167
|
+
:class="mergedClass"
|
|
156
168
|
:style="divStyle"
|
|
157
169
|
:data-maizzle-cw="colWidthSource"
|
|
158
170
|
>
|
|
159
171
|
<slot />
|
|
160
172
|
</div>
|
|
161
|
-
<MsoAfter />
|
|
173
|
+
<MsoAfter v-if="outlookFallback" />
|
|
162
174
|
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, createStaticVNode, useAttrs } from 'vue'
|
|
3
|
-
import { hasWidthInStyle, hasWidthUtility, nextId, normalizeToPixels } from './utils.ts'
|
|
3
|
+
import { hasWidthInStyle, hasWidthUtility, nextId, normalizeToPixels, outlookFallbackProp } from './utils.ts'
|
|
4
|
+
import { useOutlookFallback } from '../composables/useOutlookFallback'
|
|
4
5
|
|
|
5
6
|
defineOptions({ inheritAttrs: false })
|
|
6
7
|
|
|
@@ -31,9 +32,12 @@ const props = defineProps({
|
|
|
31
32
|
msoStyle: {
|
|
32
33
|
type: String,
|
|
33
34
|
default: undefined
|
|
34
|
-
}
|
|
35
|
+
},
|
|
36
|
+
outlookFallback: outlookFallbackProp,
|
|
35
37
|
})
|
|
36
38
|
|
|
39
|
+
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
40
|
+
|
|
37
41
|
const userStyle = computed(() => {
|
|
38
42
|
const s = attrs.style
|
|
39
43
|
if (!s) return ''
|
|
@@ -47,7 +51,7 @@ const userHasWidth = computed(() => {
|
|
|
47
51
|
return hasWidthUtility(cls) || hasWidthInStyle(userStyle.value)
|
|
48
52
|
})
|
|
49
53
|
|
|
50
|
-
const useMarker = props.width == null && userHasWidth.value
|
|
54
|
+
const useMarker = outlookFallback && props.width == null && userHasWidth.value
|
|
51
55
|
const msoId = useMarker ? nextId('s') : null
|
|
52
56
|
|
|
53
57
|
const divStyle = computed(() => {
|
|
@@ -96,7 +100,7 @@ const MsoAfter = () => createStaticVNode(
|
|
|
96
100
|
</script>
|
|
97
101
|
|
|
98
102
|
<template>
|
|
99
|
-
<MsoBefore />
|
|
103
|
+
<MsoBefore v-if="outlookFallback" />
|
|
100
104
|
<div
|
|
101
105
|
v-bind="restAttrs"
|
|
102
106
|
:style="divStyle"
|
|
@@ -106,5 +110,5 @@ const MsoAfter = () => createStaticVNode(
|
|
|
106
110
|
>
|
|
107
111
|
<slot />
|
|
108
112
|
</div>
|
|
109
|
-
<MsoAfter />
|
|
113
|
+
<MsoAfter v-if="outlookFallback" />
|
|
110
114
|
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
|
-
import { normalizeToPixels } from './utils.ts'
|
|
3
|
+
import { normalizeToPixels, outlookFallbackProp } from './utils.ts'
|
|
4
|
+
import { useOutlookFallback } from '../composables/useOutlookFallback'
|
|
4
5
|
|
|
5
6
|
const props = defineProps({
|
|
6
7
|
/** The type of spacer. */
|
|
@@ -22,9 +23,12 @@ const props = defineProps({
|
|
|
22
23
|
msoHeight: {
|
|
23
24
|
type: [String, Number],
|
|
24
25
|
default: null
|
|
25
|
-
}
|
|
26
|
+
},
|
|
27
|
+
outlookFallback: outlookFallbackProp,
|
|
26
28
|
})
|
|
27
29
|
|
|
30
|
+
const outlookFallback = useOutlookFallback(props.outlookFallback)
|
|
31
|
+
|
|
28
32
|
function parsePixelValue(value: string | number): number {
|
|
29
33
|
if (typeof value === 'number') return value
|
|
30
34
|
return Number.parseFloat(value) || 0
|
|
@@ -37,7 +41,7 @@ const verticalStyles = computed(() => {
|
|
|
37
41
|
s.push(`line-height: ${normalizeToPixels(props.height)};`)
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
if (props.msoHeight) {
|
|
44
|
+
if (outlookFallback && props.msoHeight) {
|
|
41
45
|
s.push(`mso-line-height-alt: ${normalizeToPixels(props.msoHeight)};`)
|
|
42
46
|
}
|
|
43
47
|
|
|
@@ -45,7 +49,8 @@ const verticalStyles = computed(() => {
|
|
|
45
49
|
})
|
|
46
50
|
|
|
47
51
|
const horizontalStyles = computed(() => {
|
|
48
|
-
|
|
52
|
+
const mso = outlookFallback ? msoFontWidth.value : ''
|
|
53
|
+
return `display:inline-block; width: ${normalizeToPixels(props.width)}; font-size: 16px;${mso}`
|
|
49
54
|
})
|
|
50
55
|
|
|
51
56
|
const msoFontWidth = computed(() => {
|