@maizzle/framework 6.0.0-rc.8 → 6.0.0-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/_rolldown/runtime.mjs +32 -0
- package/dist/build.mjs +2 -1
- package/dist/build.mjs.map +1 -1
- package/dist/components/Markdown.vue +7 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/node_modules/picomatch/index.mjs +13 -0
- package/dist/node_modules/picomatch/index.mjs.map +1 -0
- package/dist/node_modules/picomatch/lib/constants.mjs +174 -0
- package/dist/node_modules/picomatch/lib/constants.mjs.map +1 -0
- package/dist/node_modules/picomatch/lib/parse.mjs +1067 -0
- package/dist/node_modules/picomatch/lib/parse.mjs.map +1 -0
- package/dist/node_modules/picomatch/lib/picomatch.mjs +304 -0
- package/dist/node_modules/picomatch/lib/picomatch.mjs.map +1 -0
- package/dist/node_modules/picomatch/lib/scan.mjs +296 -0
- package/dist/node_modules/picomatch/lib/scan.mjs.map +1 -0
- package/dist/node_modules/picomatch/lib/utils.mjs +53 -0
- package/dist/node_modules/picomatch/lib/utils.mjs.map +1 -0
- package/dist/plugin.mjs +11 -7
- package/dist/plugin.mjs.map +1 -1
- package/dist/plugins/postcss/tailwindCleanup.d.mts.map +1 -1
- package/dist/plugins/postcss/tailwindCleanup.mjs +24 -2
- package/dist/plugins/postcss/tailwindCleanup.mjs.map +1 -1
- package/dist/render/createRenderer.d.mts +3 -0
- package/dist/render/createRenderer.d.mts.map +1 -1
- package/dist/render/createRenderer.mjs +13 -6
- package/dist/render/createRenderer.mjs.map +1 -1
- package/dist/render/index.mjs +2 -1
- package/dist/render/index.mjs.map +1 -1
- package/dist/serve.d.mts.map +1 -1
- package/dist/serve.mjs +12 -5
- package/dist/serve.mjs.map +1 -1
- package/dist/transformers/index.d.mts +10 -9
- package/dist/transformers/index.d.mts.map +1 -1
- package/dist/transformers/index.mjs +12 -9
- package/dist/transformers/index.mjs.map +1 -1
- package/dist/transformers/sixHex.d.mts +16 -0
- package/dist/transformers/sixHex.d.mts.map +1 -0
- package/dist/transformers/sixHex.mjs +30 -0
- package/dist/transformers/sixHex.mjs.map +1 -0
- package/dist/types/config.d.mts +46 -1
- package/dist/types/config.d.mts.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createRenderer.mjs","names":["merge"],"sources":["../../src/render/createRenderer.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path'\nimport { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { isLaravel } from '../utils/detect.ts'\nimport { createServer } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport Markdown from 'unplugin-vue-markdown/vite'\nimport AutoImport from 'unplugin-auto-import/vite'\nimport Components from 'unplugin-vue-components/vite'\nimport { unheadVueComposablesImports } from '@unhead/vue'\nimport { defu as merge } from 'defu'\nimport { createSSRApp } from 'vue'\nimport { renderToString } from 'vue/server-renderer'\nimport { createHead, renderSSRHead } from '@unhead/vue/server'\nimport { MaizzleConfigKey } from '../composables/useConfig.ts'\nimport { RenderContextKey } from '../composables/renderContext.ts'\nimport type { Component, InjectionKey } from 'vue'\nimport type { MaizzleConfig, MarkdownConfig } from '../types/index.ts'\nimport type { MarkdownExit } from 'markdown-exit'\nimport type { RenderContext } from '../composables/renderContext.ts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/**\n * Vite plugin that extracts raw slot content from <CodeBlock> tags\n * and passes it as a :code prop before Vue compiles the template.\n *\n * This lets users write HTML naturally inside CodeBlock slots without\n * Vue attempting to compile it as template syntax.\n */\nfunction codeBlockExtract() {\n // Matches <CodeBlock ...>content</CodeBlock> (and kebab-case <code-block>)\n const re = /<(CodeBlock|code-block)((?:\\s[^>]*?)?)>([\\s\\S]*?)<\\/\\1>/g\n\n return {\n name: 'maizzle:code-block-extract',\n enforce: 'pre' as const,\n transform(code: string, id: string) {\n if (!id.endsWith('.vue') && !id.endsWith('.md')) return\n if (!code.includes('CodeBlock') && !code.includes('code-block')) return\n\n const transformed = code.replace(re, (_match, tag, attrs, content) => {\n // Skip if already has a :code or v-bind:code prop\n if (/(?:^|\\s):code\\b/.test(attrs) || /v-bind:code\\b/.test(attrs)) return _match\n\n // Strip leading/trailing blank lines, then dedent based on\n // the minimum indent of non-empty lines (à la min-indent)\n const stripped = content.replace(/^\\n+/, '').replace(/\\s+$/, '')\n if (!stripped) return _match\n\n const minIndent = stripped.match(/^[ \\t]*(?=\\S)/gm)\n ?.reduce((min, ws) => Math.min(min, ws.length), Infinity) ?? 0\n\n const dedented = minIndent > 0\n ? stripped.replace(new RegExp(`^[ \\\\t]{${minIndent}}`, 'gm'), '')\n : stripped\n\n // HTML-escape for safe embedding in a static attribute value.\n const escaped = dedented\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n\n return `<${tag}${attrs} code=\"${escaped}\" />`\n })\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n\n/**\n * Vite plugin that pre-processes <Markdown> tags:\n * - Extracts slot content, dedents it, and passes as :content prop\n * - Resolves `src` prop to read file contents at build time\n */\nfunction markdownExtract() {\n const re = /<(Markdown|markdown)((?:\\s[^>]*?)?)>([\\s\\S]*?)<\\/\\1>/g\n const selfClosingRe = /<(Markdown|markdown)((?:\\s[^>]*?\\bsrc\\s*=\\s*\"[^\"]*\"[^>]*?))\\/>/g\n\n return {\n name: 'maizzle:markdown-extract',\n enforce: 'pre' as const,\n transform(code: string, id: string) {\n if (!id.endsWith('.vue') && !id.endsWith('.md')) return\n if (!code.includes('Markdown') && !code.includes('markdown')) return\n\n let transformed = code\n\n // Handle <Markdown>content</Markdown>\n transformed = transformed.replace(re, (_match, tag, attrs, content) => {\n if (/(?:^|\\s):content\\b/.test(attrs) || /v-bind:content\\b/.test(attrs)) return _match\n\n const stripped = content.replace(/^\\n+/, '').replace(/\\s+$/, '')\n if (!stripped) return _match\n\n const minIndent = stripped.match(/^[ \\t]*(?=\\S)/gm)\n ?.reduce((min: number, ws: string) => Math.min(min, ws.length), Infinity) ?? 0\n\n const dedented = minIndent > 0\n ? stripped.replace(new RegExp(`^[ \\\\t]{${minIndent}}`, 'gm'), '')\n : stripped\n\n const escaped = dedented\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n\n return `<${tag}${attrs} content=\"${escaped}\" />`\n })\n\n // Handle <Markdown src=\"./file.md\" /> — resolve and inline file content\n transformed = transformed.replace(selfClosingRe, (_match, tag, attrs) => {\n const srcMatch = attrs.match(/\\bsrc\\s*=\\s*\"([^\"]*)\"/)\n if (!srcMatch) return _match\n\n const srcPath = srcMatch[1]\n const resolvedPath = resolve(dirname(id), srcPath)\n\n let fileContent: string\n try {\n fileContent = readFileSync(resolvedPath, 'utf-8').trim()\n } catch {\n return _match\n }\n\n // Remove src prop, add content prop\n const cleanAttrs = attrs.replace(/\\s*\\bsrc\\s*=\\s*\"[^\"]*\"/, '')\n const escaped = fileContent\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n\n return `<${tag}${cleanAttrs} content=\"${escaped}\" />`\n })\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n\nconst vuePkgDir = dirname(fileURLToPath(import.meta.resolve('vue/package.json')))\nconst vueServerRendererPkgDir = dirname(fileURLToPath(import.meta.resolve('@vue/server-renderer/package.json')))\nconst unheadVuePkgDir = resolve(dirname(fileURLToPath(import.meta.resolve('@unhead/vue'))), '..')\nconst vueRouterPkgDir = dirname(fileURLToPath(import.meta.resolve('vue-router/package.json')))\n\nexport interface RenderedTemplate {\n html: string\n doctype?: string\n templateConfig: MaizzleConfig\n sfcEventHandlers: RenderContext['sfcEventHandlers']\n plaintext?: RenderContext['plaintext']\n}\n\nexport interface Renderer {\n render(input: string | Component, config: MaizzleConfig): Promise<RenderedTemplate>\n invalidate(filePath: string): Promise<void>\n invalidateAll(): Promise<void>\n close(): Promise<void>\n}\n\nexport interface CreateRendererOptions {\n /** Generate .d.ts files for auto-imports and components (default: false) */\n dts?: boolean\n /** Options passed to unplugin-vue-markdown */\n markdown?: MarkdownConfig\n /** Root directory for resolving user component dirs and .d.ts output */\n root?: string\n /** Additional component directories to register for auto-import */\n componentDirs?: string[]\n}\n\n/**\n * Lightweight Vite SSR loader for rendering Vue SFC email templates.\n *\n * Uses only Vue + unplugin for component/auto-import resolution.\n * Tailwind CSS compilation is handled by the transformer pipeline.\n */\nexport async function createRenderer(\n options: CreateRendererOptions = {},\n): Promise<Renderer> {\n const { dts = false, markdown: markdownOptionsRaw, root = process.cwd(), componentDirs = [] } = options\n const { shikiTheme = 'github-light', ...markdownOptions } = markdownOptionsRaw ?? {}\n\n const dtsDir = isLaravel()\n ? resolve(process.cwd(), 'resources/js/types/maizzle')\n : resolve(root, '.maizzle')\n\n const VIRTUAL_SFC_ID = 'virtual:maizzle-sfc.vue'\n let virtualSfcSource = ''\n\n const server = await createServer({\n configFile: false,\n plugins: [\n codeBlockExtract(),\n markdownExtract(),\n {\n name: 'maizzle:virtual-sfc',\n resolveId(id) {\n if (id === VIRTUAL_SFC_ID) return id\n },\n load(id) {\n if (id === VIRTUAL_SFC_ID) return virtualSfcSource\n },\n },\n vue({\n include: [/\\.vue$/, /\\.md$/],\n template: {\n transformAssetUrls: false,\n },\n }),\n Markdown(merge(markdownOptions ?? {}, {\n headEnabled: true,\n wrapperDiv: false,\n wrapperClasses: 'prose',\n markdownOptions: {\n async highlight(code: string, lang: string) {\n const { codeToHtml } = await import('shiki')\n return codeToHtml(code, { lang, theme: shikiTheme })\n },\n },\n markdownSetup(md: MarkdownExit) {\n const wrapPre = (html: string) =>\n `<table class=\"w-full\"><tr><td class=\"max-w-0 mso-padding-alt-4\">${html}</td></tr></table>\\n`\n\n const defaultFence = md.renderer.rules.fence!\n md.renderer.rules.fence = (...args) => {\n const result = defaultFence(...args)\n if (typeof result === 'string') return wrapPre(result)\n return result.then(wrapPre)\n }\n\n const defaultCodeBlock = md.renderer.rules.code_block!\n md.renderer.rules.code_block = (...args) => wrapPre(defaultCodeBlock(...args) as string)\n },\n })),\n AutoImport({\n dirs: [\n resolve(__dirname, '../composables'),\n resolve(__dirname, '../filters'),\n ],\n imports: ['vue', unheadVueComposablesImports],\n dts: dts ? resolve(dtsDir, 'auto-imports.d.ts') : false,\n }),\n Components({\n extensions: ['vue', 'md'],\n include: [/\\.vue$/, /\\.vue\\?vue/, /\\.md$/],\n dirs: [\n resolve(__dirname, '../components'),\n resolve(root, 'components'),\n ...componentDirs,\n ],\n dts: dts ? resolve(dtsDir, 'components.d.ts') : false,\n }),\n ],\n resolve: {\n alias: {\n 'vue/server-renderer': resolve(vueServerRendererPkgDir, 'dist/server-renderer.esm-bundler.js'),\n 'vue': resolve(vuePkgDir, 'dist/vue.runtime.esm-bundler.js'),\n 'vue-router': vueRouterPkgDir,\n '@unhead/vue/server': resolve(unheadVuePkgDir, 'dist/server.mjs'),\n '@unhead/vue': resolve(unheadVuePkgDir, 'dist/index.mjs'),\n },\n },\n server: {\n middlewareMode: true,\n hmr: false,\n watch: null,\n fs: {\n allow: [process.cwd(), root, ...componentDirs, vuePkgDir, vueServerRendererPkgDir, unheadVuePkgDir, vueRouterPkgDir],\n },\n },\n appType: 'custom',\n logLevel: 'silent',\n optimizeDeps: {\n noDiscovery: true,\n },\n })\n\n return {\n async render(input: string | Component, config: MaizzleConfig): Promise<RenderedTemplate> {\n let component: Component\n let configKey: InjectionKey<MaizzleConfig>\n let contextKey: InjectionKey<RenderContext>\n\n if (typeof input === 'string') {\n // String input goes through Vite — must use ssrLoadModule for injection keys\n // so they share the same module instance as the SFC\n const configModule = await server.ssrLoadModule(resolve(__dirname, '../composables/useConfig'))\n const contextModule = await server.ssrLoadModule(resolve(__dirname, '../composables/renderContext'))\n configKey = configModule.MaizzleConfigKey\n contextKey = contextModule.RenderContextKey\n\n if (input.includes('<template') || input.includes('<script')) {\n virtualSfcSource = input\n const mod = server.moduleGraph.getModuleById(VIRTUAL_SFC_ID)\n if (mod) server.moduleGraph.invalidateModule(mod)\n component = (await server.ssrLoadModule(VIRTUAL_SFC_ID)).default\n } else {\n component = (await server.ssrLoadModule(input)).default\n }\n } else {\n // Pre-compiled component — use directly imported keys\n component = input\n configKey = MaizzleConfigKey\n contextKey = RenderContextKey\n }\n\n const renderContext: RenderContext = {\n doctype: undefined,\n sfcConfig: undefined,\n sfcEventHandlers: [],\n }\n\n const head = createHead({ disableDefaults: true })\n const app = createSSRApp(component)\n app.use(head)\n app.provide(configKey, config)\n app.provide(contextKey, renderContext)\n\n const ssrContext: Record<string, any> = {}\n let html: string = await renderToString(app, ssrContext)\n\n const { headTags, bodyTags, bodyTagsOpen, htmlAttrs, bodyAttrs } = await renderSSRHead(head)\n\n // Inject head entries into the rendered HTML\n if (htmlAttrs) {\n html = html.replace(/<html([^>]*)>/, `<html$1 ${htmlAttrs}>`)\n }\n if (headTags) {\n html = html.replace('</head>', `${headTags}\\n</head>`)\n }\n if (bodyAttrs) {\n html = html.replace(/<body([^>]*)>/, `<body$1 ${bodyAttrs}>`)\n }\n if (bodyTagsOpen) {\n html = html.replace(/<body([^>]*)>/, `<body$1>\\n${bodyTagsOpen}`)\n }\n if (bodyTags) {\n html = html.replace('</body>', `${bodyTags}\\n</body>`)\n }\n\n // Inject SSR teleport content into their target elements\n if (ssrContext.teleports) {\n const { parse: parseDom, serialize: serializeDom, walk } = await import('../utils/ast/index.ts')\n let dom = parseDom(html)\n\n for (const [rawTarget, content] of Object.entries(ssrContext.teleports) as [string, string][]) {\n if (!content) continue\n\n const prepend = rawTarget.endsWith(':start')\n const target = prepend ? rawTarget.slice(0, -6) : rawTarget\n const targetChildren = parseDom(content)\n\n walk(dom, (node) => {\n const el = node as import('domhandler').Element\n\n if (!el.name) return\n\n const matched\n = target === el.name\n || (target.startsWith('#') && el.attribs?.id === target.slice(1))\n || (target.startsWith('.') && el.attribs?.class?.split(/\\s+/).includes(target.slice(1)))\n\n if (matched) {\n for (const child of targetChildren) {\n child.parent = el as any\n }\n\n el.children = prepend\n ? [...targetChildren, ...(el.children || [])] as any\n : [...(el.children || []), ...targetChildren] as any\n }\n })\n }\n\n html = serializeDom(dom)\n }\n\n // Inject preview/preheader text from usePreviewText() composable\n if (renderContext.previewText) {\n const { text, fillerCount, shyCount } = renderContext.previewText\n const filler = '\\u2007\\u034F '.repeat(fillerCount)\n const shys = '\\u00AD '.repeat(shyCount)\n const previewHtml = `<div style=\"display:none\">${text}${filler}${shys}\\u00A0</div>`\n html = html.replace(/<body([^>]*)>/, `<body$1>${previewHtml}`)\n }\n\n return {\n html,\n doctype: renderContext.doctype,\n templateConfig: renderContext.sfcConfig ?? config,\n sfcEventHandlers: renderContext.sfcEventHandlers,\n plaintext: renderContext.plaintext,\n }\n },\n\n async invalidate(filePath: string): Promise<void> {\n const mod = await server.moduleGraph.getModuleByUrl(filePath)\n if (mod) {\n server.moduleGraph.invalidateModule(mod)\n }\n },\n\n async invalidateAll(): Promise<void> {\n for (const mod of server.moduleGraph.idToModuleMap.values()) {\n server.moduleGraph.invalidateModule(mod)\n }\n },\n\n async close(): Promise<void> {\n await server.close()\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createRenderer.mjs","names":["merge"],"sources":["../../src/render/createRenderer.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path'\nimport { readFileSync, existsSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { isLaravel } from '../utils/detect.ts'\nimport { createServer, mergeConfig, type InlineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport Markdown from 'unplugin-vue-markdown/vite'\nimport AutoImport from 'unplugin-auto-import/vite'\nimport Components from 'unplugin-vue-components/vite'\nimport { unheadVueComposablesImports } from '@unhead/vue'\nimport { defu as merge } from 'defu'\nimport { createSSRApp } from 'vue'\nimport { renderToString } from 'vue/server-renderer'\nimport { createHead, renderSSRHead } from '@unhead/vue/server'\nimport { MaizzleConfigKey } from '../composables/useConfig.ts'\nimport { RenderContextKey } from '../composables/renderContext.ts'\nimport type { Component, InjectionKey } from 'vue'\nimport type { MaizzleConfig, MarkdownConfig } from '../types/index.ts'\nimport type { MarkdownExit } from 'markdown-exit'\nimport type { RenderContext } from '../composables/renderContext.ts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n/**\n * Vite plugin that extracts raw slot content from <CodeBlock> tags\n * and passes it as a :code prop before Vue compiles the template.\n *\n * This lets users write HTML naturally inside CodeBlock slots without\n * Vue attempting to compile it as template syntax.\n */\nfunction codeBlockExtract() {\n // Matches <CodeBlock ...>content</CodeBlock> (and kebab-case <code-block>)\n const re = /<(CodeBlock|code-block)((?:\\s[^>]*?)?)>([\\s\\S]*?)<\\/\\1>/g\n\n return {\n name: 'maizzle:code-block-extract',\n enforce: 'pre' as const,\n transform(code: string, id: string) {\n if (!id.endsWith('.vue') && !id.endsWith('.md')) return\n if (!code.includes('CodeBlock') && !code.includes('code-block')) return\n\n const transformed = code.replace(re, (_match, tag, attrs, content) => {\n // Skip if already has a :code or v-bind:code prop\n if (/(?:^|\\s):code\\b/.test(attrs) || /v-bind:code\\b/.test(attrs)) return _match\n\n // Strip leading/trailing blank lines, then dedent based on\n // the minimum indent of non-empty lines (à la min-indent)\n const stripped = content.replace(/^\\n+/, '').replace(/\\s+$/, '')\n if (!stripped) return _match\n\n const minIndent = stripped.match(/^[ \\t]*(?=\\S)/gm)\n ?.reduce((min, ws) => Math.min(min, ws.length), Infinity) ?? 0\n\n const dedented = minIndent > 0\n ? stripped.replace(new RegExp(`^[ \\\\t]{${minIndent}}`, 'gm'), '')\n : stripped\n\n // HTML-escape for safe embedding in a static attribute value.\n const escaped = dedented\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n\n return `<${tag}${attrs} code=\"${escaped}\" />`\n })\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n\n/**\n * Vite plugin that pre-processes <Markdown> tags:\n * - Extracts slot content, dedents it, and passes as :content prop\n * - Resolves `src` prop to read file contents at build time\n */\nfunction markdownExtract() {\n const re = /<(Markdown|markdown)((?:\\s[^>]*?)?)>([\\s\\S]*?)<\\/\\1>/g\n const selfClosingRe = /<(Markdown|markdown)((?:\\s[^>]*?\\bsrc\\s*=\\s*\"[^\"]*\"[^>]*?))\\/>/g\n\n return {\n name: 'maizzle:markdown-extract',\n enforce: 'pre' as const,\n transform(code: string, id: string) {\n if (!id.endsWith('.vue') && !id.endsWith('.md')) return\n if (!code.includes('Markdown') && !code.includes('markdown')) return\n\n let transformed = code\n\n // Handle <Markdown>content</Markdown>\n transformed = transformed.replace(re, (_match, tag, attrs, content) => {\n if (/(?:^|\\s):content\\b/.test(attrs) || /v-bind:content\\b/.test(attrs)) return _match\n\n const stripped = content.replace(/^\\n+/, '').replace(/\\s+$/, '')\n if (!stripped) return _match\n\n const minIndent = stripped.match(/^[ \\t]*(?=\\S)/gm)\n ?.reduce((min: number, ws: string) => Math.min(min, ws.length), Infinity) ?? 0\n\n const dedented = minIndent > 0\n ? stripped.replace(new RegExp(`^[ \\\\t]{${minIndent}}`, 'gm'), '')\n : stripped\n\n const escaped = dedented\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n\n return `<${tag}${attrs} content=\"${escaped}\" />`\n })\n\n // Handle <Markdown src=\"./file.md\" /> — resolve and inline file content\n transformed = transformed.replace(selfClosingRe, (_match, tag, attrs) => {\n const srcMatch = attrs.match(/\\bsrc\\s*=\\s*\"([^\"]*)\"/)\n if (!srcMatch) return _match\n\n const srcPath = srcMatch[1]\n const resolvedPath = resolve(dirname(id), srcPath)\n\n let fileContent: string\n try {\n fileContent = readFileSync(resolvedPath, 'utf-8').trim()\n } catch {\n return _match\n }\n\n // Remove src prop, add content prop\n const cleanAttrs = attrs.replace(/\\s*\\bsrc\\s*=\\s*\"[^\"]*\"/, '')\n const escaped = fileContent\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n\n return `<${tag}${cleanAttrs} content=\"${escaped}\" />`\n })\n\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n }\n}\n\nconst vuePkgDir = dirname(fileURLToPath(import.meta.resolve('vue/package.json')))\nconst vueServerRendererPkgDir = dirname(fileURLToPath(import.meta.resolve('@vue/server-renderer/package.json')))\nconst unheadVuePkgDir = resolve(dirname(fileURLToPath(import.meta.resolve('@unhead/vue'))), '..')\nconst vueRouterPkgDir = dirname(fileURLToPath(import.meta.resolve('vue-router/package.json')))\n\nexport interface RenderedTemplate {\n html: string\n doctype?: string\n templateConfig: MaizzleConfig\n sfcEventHandlers: RenderContext['sfcEventHandlers']\n plaintext?: RenderContext['plaintext']\n}\n\nexport interface Renderer {\n render(input: string | Component, config: MaizzleConfig): Promise<RenderedTemplate>\n invalidate(filePath: string): Promise<void>\n invalidateAll(): Promise<void>\n close(): Promise<void>\n}\n\nexport interface CreateRendererOptions {\n /** Generate .d.ts files for auto-imports and components (default: false) */\n dts?: boolean\n /** Options passed to unplugin-vue-markdown */\n markdown?: MarkdownConfig\n /** Root directory for resolving user component dirs and .d.ts output */\n root?: string\n /** Additional component directories to register for auto-import */\n componentDirs?: string[]\n /** User Vite config options to merge into the internal SSR server */\n vite?: InlineConfig\n}\n\n/**\n * Lightweight Vite SSR loader for rendering Vue SFC email templates.\n *\n * Uses only Vue + unplugin for component/auto-import resolution.\n * Tailwind CSS compilation is handled by the transformer pipeline.\n */\nexport async function createRenderer(\n options: CreateRendererOptions = {},\n): Promise<Renderer> {\n const { dts = false, markdown: markdownOptionsRaw, root = process.cwd(), componentDirs = [], vite: userViteConfig } = options\n const { shikiTheme = 'github-light', ...markdownOptions } = markdownOptionsRaw ?? {}\n\n const dtsDir = isLaravel()\n ? resolve(process.cwd(), 'resources/js/types/maizzle')\n : resolve(root, '.maizzle')\n\n const VIRTUAL_SFC_ID = 'virtual:maizzle-sfc.vue'\n let virtualSfcSource = ''\n\n // Check for a user vite.config file in the project root\n const viteConfigFile = ['vite.config.ts', 'vite.config.js']\n .map(f => resolve(root, f))\n .find(f => existsSync(f))\n\n const maizzleConfig: InlineConfig = {\n configFile: viteConfigFile ?? false,\n plugins: [\n codeBlockExtract(),\n markdownExtract(),\n {\n name: 'maizzle:virtual-sfc',\n resolveId(id) {\n if (id === VIRTUAL_SFC_ID) return id\n },\n load(id) {\n if (id === VIRTUAL_SFC_ID) return virtualSfcSource\n },\n },\n vue({\n include: [/\\.vue$/, /\\.md$/],\n template: {\n transformAssetUrls: false,\n },\n }),\n Markdown(merge(markdownOptions ?? {}, {\n headEnabled: true,\n wrapperDiv: false,\n wrapperClasses: 'prose',\n markdownOptions: {\n async highlight(code: string, lang: string) {\n const { codeToHtml } = await import('shiki')\n return codeToHtml(code, { lang, theme: shikiTheme })\n },\n },\n markdownSetup(md: MarkdownExit) {\n const wrapPre = (html: string) =>\n `<table class=\"w-full\"><tr><td class=\"max-w-0 mso-padding-alt-4\">${html}</td></tr></table>\\n`\n\n const defaultFence = md.renderer.rules.fence!\n md.renderer.rules.fence = (...args) => {\n const result = defaultFence(...args)\n if (typeof result === 'string') return wrapPre(result)\n return result.then(wrapPre)\n }\n\n const defaultCodeBlock = md.renderer.rules.code_block!\n md.renderer.rules.code_block = (...args) => wrapPre(defaultCodeBlock(...args) as string)\n },\n })),\n AutoImport({\n dirs: [\n resolve(__dirname, '../composables'),\n resolve(__dirname, '../filters'),\n ],\n imports: ['vue', unheadVueComposablesImports],\n dts: dts ? resolve(dtsDir, 'auto-imports.d.ts') : false,\n }),\n Components({\n extensions: ['vue', 'md'],\n include: [/\\.vue$/, /\\.vue\\?vue/, /\\.md$/],\n dirs: [\n resolve(__dirname, '../components'),\n resolve(root, 'components'),\n ...componentDirs,\n ],\n dts: dts ? resolve(dtsDir, 'components.d.ts') : false,\n }),\n ],\n resolve: {\n alias: {\n 'vue/server-renderer': resolve(vueServerRendererPkgDir, 'dist/server-renderer.esm-bundler.js'),\n 'vue': resolve(vuePkgDir, 'dist/vue.runtime.esm-bundler.js'),\n 'vue-router': vueRouterPkgDir,\n '@unhead/vue/server': resolve(unheadVuePkgDir, 'dist/server.mjs'),\n '@unhead/vue': resolve(unheadVuePkgDir, 'dist/index.mjs'),\n },\n },\n server: {\n middlewareMode: true,\n hmr: false,\n watch: null,\n fs: {\n allow: [process.cwd(), root, ...componentDirs, vuePkgDir, vueServerRendererPkgDir, unheadVuePkgDir, vueRouterPkgDir],\n },\n },\n appType: 'custom',\n logLevel: 'silent',\n optimizeDeps: {\n noDiscovery: true,\n },\n }\n\n // Merge user's vite config (from config.vite) under Maizzle's config.\n // mergeConfig(a, b) → b overrides a for scalars, arrays are concatenated.\n // This ensures Maizzle's critical settings (middlewareMode, appType, etc.) always win,\n // while user plugins and other options are included.\n const finalConfig = userViteConfig && !viteConfigFile\n ? mergeConfig(userViteConfig, maizzleConfig)\n : maizzleConfig\n\n const server = await createServer(finalConfig)\n\n return {\n async render(input: string | Component, config: MaizzleConfig): Promise<RenderedTemplate> {\n let component: Component\n let configKey: InjectionKey<MaizzleConfig>\n let contextKey: InjectionKey<RenderContext>\n\n if (typeof input === 'string') {\n // String input goes through Vite — must use ssrLoadModule for injection keys\n // so they share the same module instance as the SFC\n const configModule = await server.ssrLoadModule(resolve(__dirname, '../composables/useConfig'))\n const contextModule = await server.ssrLoadModule(resolve(__dirname, '../composables/renderContext'))\n configKey = configModule.MaizzleConfigKey\n contextKey = contextModule.RenderContextKey\n\n if (input.includes('<template') || input.includes('<script')) {\n virtualSfcSource = input\n const mod = server.moduleGraph.getModuleById(VIRTUAL_SFC_ID)\n if (mod) server.moduleGraph.invalidateModule(mod)\n component = (await server.ssrLoadModule(VIRTUAL_SFC_ID)).default\n } else {\n component = (await server.ssrLoadModule(input)).default\n }\n } else {\n // Pre-compiled component — use directly imported keys\n component = input\n configKey = MaizzleConfigKey\n contextKey = RenderContextKey\n }\n\n const renderContext: RenderContext = {\n doctype: undefined,\n sfcConfig: undefined,\n sfcEventHandlers: [],\n }\n\n const head = createHead({ disableDefaults: true })\n const app = createSSRApp(component)\n app.use(head)\n\n // Register user Vue plugins, directives, and global properties\n if (config.vue) {\n for (const plugin of config.vue.plugins ?? []) {\n app.use(plugin)\n }\n for (const [name, directive] of Object.entries(config.vue.directives ?? {})) {\n app.directive(name, directive)\n }\n Object.assign(app.config.globalProperties, config.vue.globalProperties)\n }\n\n app.provide(configKey, config)\n app.provide(contextKey, renderContext)\n\n const ssrContext: Record<string, any> = {}\n let html: string = await renderToString(app, ssrContext)\n\n const { headTags, bodyTags, bodyTagsOpen, htmlAttrs, bodyAttrs } = await renderSSRHead(head)\n\n // Inject head entries into the rendered HTML\n if (htmlAttrs) {\n html = html.replace(/<html([^>]*)>/, `<html$1 ${htmlAttrs}>`)\n }\n if (headTags) {\n html = html.replace('</head>', `${headTags}\\n</head>`)\n }\n if (bodyAttrs) {\n html = html.replace(/<body([^>]*)>/, `<body$1 ${bodyAttrs}>`)\n }\n if (bodyTagsOpen) {\n html = html.replace(/<body([^>]*)>/, `<body$1>\\n${bodyTagsOpen}`)\n }\n if (bodyTags) {\n html = html.replace('</body>', `${bodyTags}\\n</body>`)\n }\n\n // Inject SSR teleport content into their target elements\n if (ssrContext.teleports) {\n const { parse: parseDom, serialize: serializeDom, walk } = await import('../utils/ast/index.ts')\n let dom = parseDom(html)\n\n for (const [rawTarget, content] of Object.entries(ssrContext.teleports) as [string, string][]) {\n if (!content) continue\n\n const prepend = rawTarget.endsWith(':start')\n const target = prepend ? rawTarget.slice(0, -6) : rawTarget\n const targetChildren = parseDom(content)\n\n walk(dom, (node) => {\n const el = node as import('domhandler').Element\n\n if (!el.name) return\n\n const matched\n = target === el.name\n || (target.startsWith('#') && el.attribs?.id === target.slice(1))\n || (target.startsWith('.') && el.attribs?.class?.split(/\\s+/).includes(target.slice(1)))\n\n if (matched) {\n for (const child of targetChildren) {\n child.parent = el as any\n }\n\n el.children = prepend\n ? [...targetChildren, ...(el.children || [])] as any\n : [...(el.children || []), ...targetChildren] as any\n }\n })\n }\n\n html = serializeDom(dom)\n }\n\n // Inject preview/preheader text from usePreviewText() composable\n if (renderContext.previewText) {\n const { text, fillerCount, shyCount } = renderContext.previewText\n const filler = '\\u2007\\u034F '.repeat(fillerCount)\n const shys = '\\u00AD '.repeat(shyCount)\n const previewHtml = `<div style=\"display:none\">${text}${filler}${shys}\\u00A0</div>`\n html = html.replace(/<body([^>]*)>/, `<body$1>${previewHtml}`)\n }\n\n return {\n html,\n doctype: renderContext.doctype,\n templateConfig: renderContext.sfcConfig ?? config,\n sfcEventHandlers: renderContext.sfcEventHandlers,\n plaintext: renderContext.plaintext,\n }\n },\n\n async invalidate(filePath: string): Promise<void> {\n const mod = await server.moduleGraph.getModuleByUrl(filePath)\n if (mod) {\n server.moduleGraph.invalidateModule(mod)\n }\n },\n\n async invalidateAll(): Promise<void> {\n for (const mod of server.moduleGraph.idToModuleMap.values()) {\n server.moduleGraph.invalidateModule(mod)\n }\n },\n\n async close(): Promise<void> {\n await server.close()\n },\n }\n}\n"],"mappings":"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"}
|
package/dist/render/index.mjs
CHANGED
|
@@ -16,7 +16,8 @@ async function render(template, options = {}) {
|
|
|
16
16
|
const renderer = options._renderer ?? await createRenderer({
|
|
17
17
|
markdown: config.markdown,
|
|
18
18
|
root: config.root,
|
|
19
|
-
componentDirs: [config.components?.source ?? []].flat()
|
|
19
|
+
componentDirs: [config.components?.source ?? []].flat(),
|
|
20
|
+
vite: config.vite
|
|
20
21
|
});
|
|
21
22
|
const ownsRenderer = !options._renderer;
|
|
22
23
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/render/index.ts"],"sourcesContent":["import { resolve, extname } from 'node:path'\nimport { resolveConfig } from '../config/index.ts'\nimport { runTransformers } from '../transformers/index.ts'\nimport { createPlaintext } from '../plaintext.ts'\nimport type { Component } from 'vue'\nimport type { MaizzleConfig } from '../types/index.ts'\nimport { createRenderer, type Renderer } from './createRenderer.ts'\n\nexport type { Renderer, RenderedTemplate, CreateRendererOptions } from './createRenderer.ts'\nexport { createRenderer } from './createRenderer.ts'\n\nexport interface RenderOptions {\n /** Already-resolved or partial config. If not provided, resolves from disk + defaults. */\n config?: Partial<MaizzleConfig>\n /** Reuse an existing renderer (used internally by build/serve to avoid creating one per template) */\n _renderer?: Renderer\n}\n\nexport interface RenderResult {\n html: string\n config: MaizzleConfig\n plaintext?: string\n}\n\n/**\n * Render a Vue SFC email template to a fully-transformed HTML string.\n *\n * Accepts a file path, a raw SFC source string, or an imported Vue component.\n * Runs the full pipeline: SSR render → transformers → doctype.\n */\nexport async function render(\n template: string | Component,\n options: RenderOptions = {},\n): Promise<RenderResult> {\n const config = await resolveConfig(options.config)\n\n // Reuse provided renderer or create a temporary one\n const renderer = options._renderer ?? await createRenderer({ markdown: config.markdown, root: config.root, componentDirs: [config.components?.source ?? []].flat() })\n const ownsRenderer = !options._renderer\n\n try {\n const isFile = typeof template === 'string'\n && ['.vue', '.md'].includes(extname(template))\n && !template.includes('\\n')\n\n const rendered = await renderer.render(isFile ? resolve(template) : template, config)\n let html = rendered.html\n\n // Prepend doctype\n const doctype = rendered.doctype ?? rendered.templateConfig.doctype ?? '<!DOCTYPE html>'\n\n // Run transformers\n if (rendered.templateConfig.useTransformers !== false) {\n html = await runTransformers(html, rendered.templateConfig, isFile ? resolve(template) : undefined, doctype)\n }\n html = `${doctype}\\n${html}`\n\n const globalPlaintext = rendered.templateConfig.plaintext\n const sfcPlaintext = rendered.plaintext\n\n let plaintextResult: string | undefined\n\n if (globalPlaintext || sfcPlaintext) {\n const stripOptions = typeof globalPlaintext === 'object' ? globalPlaintext : {}\n plaintextResult = createPlaintext(html, stripOptions)\n }\n\n return { html, config: rendered.templateConfig, plaintext: plaintextResult }\n } finally {\n if (ownsRenderer) {\n await renderer.close()\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA8BA,eAAsB,OACpB,UACA,UAAyB,EAAE,EACJ;CACvB,MAAM,SAAS,MAAM,cAAc,QAAQ,OAAO;CAGlD,MAAM,WAAW,QAAQ,aAAa,MAAM,eAAe;EAAE,UAAU,OAAO;EAAU,MAAM,OAAO;EAAM,eAAe,CAAC,OAAO,YAAY,UAAU,EAAE,CAAC,CAAC,MAAM;EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/render/index.ts"],"sourcesContent":["import { resolve, extname } from 'node:path'\nimport { resolveConfig } from '../config/index.ts'\nimport { runTransformers } from '../transformers/index.ts'\nimport { createPlaintext } from '../plaintext.ts'\nimport type { Component } from 'vue'\nimport type { MaizzleConfig } from '../types/index.ts'\nimport { createRenderer, type Renderer } from './createRenderer.ts'\n\nexport type { Renderer, RenderedTemplate, CreateRendererOptions } from './createRenderer.ts'\nexport { createRenderer } from './createRenderer.ts'\n\nexport interface RenderOptions {\n /** Already-resolved or partial config. If not provided, resolves from disk + defaults. */\n config?: Partial<MaizzleConfig>\n /** Reuse an existing renderer (used internally by build/serve to avoid creating one per template) */\n _renderer?: Renderer\n}\n\nexport interface RenderResult {\n html: string\n config: MaizzleConfig\n plaintext?: string\n}\n\n/**\n * Render a Vue SFC email template to a fully-transformed HTML string.\n *\n * Accepts a file path, a raw SFC source string, or an imported Vue component.\n * Runs the full pipeline: SSR render → transformers → doctype.\n */\nexport async function render(\n template: string | Component,\n options: RenderOptions = {},\n): Promise<RenderResult> {\n const config = await resolveConfig(options.config)\n\n // Reuse provided renderer or create a temporary one\n const renderer = options._renderer ?? await createRenderer({ markdown: config.markdown, root: config.root, componentDirs: [config.components?.source ?? []].flat(), vite: config.vite })\n const ownsRenderer = !options._renderer\n\n try {\n const isFile = typeof template === 'string'\n && ['.vue', '.md'].includes(extname(template))\n && !template.includes('\\n')\n\n const rendered = await renderer.render(isFile ? resolve(template) : template, config)\n let html = rendered.html\n\n // Prepend doctype\n const doctype = rendered.doctype ?? rendered.templateConfig.doctype ?? '<!DOCTYPE html>'\n\n // Run transformers\n if (rendered.templateConfig.useTransformers !== false) {\n html = await runTransformers(html, rendered.templateConfig, isFile ? resolve(template) : undefined, doctype)\n }\n html = `${doctype}\\n${html}`\n\n const globalPlaintext = rendered.templateConfig.plaintext\n const sfcPlaintext = rendered.plaintext\n\n let plaintextResult: string | undefined\n\n if (globalPlaintext || sfcPlaintext) {\n const stripOptions = typeof globalPlaintext === 'object' ? globalPlaintext : {}\n plaintextResult = createPlaintext(html, stripOptions)\n }\n\n return { html, config: rendered.templateConfig, plaintext: plaintextResult }\n } finally {\n if (ownsRenderer) {\n await renderer.close()\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA8BA,eAAsB,OACpB,UACA,UAAyB,EAAE,EACJ;CACvB,MAAM,SAAS,MAAM,cAAc,QAAQ,OAAO;CAGlD,MAAM,WAAW,QAAQ,aAAa,MAAM,eAAe;EAAE,UAAU,OAAO;EAAU,MAAM,OAAO;EAAM,eAAe,CAAC,OAAO,YAAY,UAAU,EAAE,CAAC,CAAC,MAAM;EAAE,MAAM,OAAO;EAAM,CAAC;CACxL,MAAM,eAAe,CAAC,QAAQ;AAE9B,KAAI;EACF,MAAM,SAAS,OAAO,aAAa,YAC9B,CAAC,QAAQ,MAAM,CAAC,SAAS,QAAQ,SAAS,CAAC,IAC3C,CAAC,SAAS,SAAS,KAAK;EAE7B,MAAM,WAAW,MAAM,SAAS,OAAO,SAAS,QAAQ,SAAS,GAAG,UAAU,OAAO;EACrF,IAAI,OAAO,SAAS;EAGpB,MAAM,UAAU,SAAS,WAAW,SAAS,eAAe,WAAW;AAGvE,MAAI,SAAS,eAAe,oBAAoB,MAC9C,QAAO,MAAM,gBAAgB,MAAM,SAAS,gBAAgB,SAAS,QAAQ,SAAS,GAAG,QAAW,QAAQ;AAE9G,SAAO,GAAG,QAAQ,IAAI;EAEtB,MAAM,kBAAkB,SAAS,eAAe;EAChD,MAAM,eAAe,SAAS;EAE9B,IAAI;AAEJ,MAAI,mBAAmB,aAErB,mBAAkB,gBAAgB,MADb,OAAO,oBAAoB,WAAW,kBAAkB,EAAE,CAC1B;AAGvD,SAAO;GAAE;GAAM,QAAQ,SAAS;GAAgB,WAAW;GAAiB;WACpE;AACR,MAAI,aACF,OAAM,SAAS,OAAO"}
|
package/dist/serve.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.d.mts","names":[],"sources":["../src/serve.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"serve.d.mts","names":[],"sources":["../src/serve.ts"],"mappings":";;;;UA0BiB,YAAA;EACf,MAAA,GAAS,OAAA,CAAQ,aAAA;;EAEjB,IAAA;EAH2B;EAK3B,MAAA;AAAA;;;;;;;;AAYF;;iBAAsB,KAAA,CAAM,OAAA,GAAS,YAAA,GAAiB,OAAA,CAAA,aAAA;AAAA,iBAyiBtC,WAAA,CAAY,MAAA,EAAQ,aAAA,EAAe,WAAA"}
|
package/dist/serve.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { __toESM } from "./_virtual/_rolldown/runtime.mjs";
|
|
1
2
|
import { resolveConfig } from "./config/index.mjs";
|
|
2
3
|
import { runTransformers } from "./transformers/index.mjs";
|
|
3
4
|
import { createRenderer } from "./render/createRenderer.mjs";
|
|
4
5
|
import { createPlaintext } from "./plaintext.mjs";
|
|
6
|
+
import { require_picomatch } from "./node_modules/picomatch/index.mjs";
|
|
5
7
|
import { serveCompatibility } from "./server/compatibility.mjs";
|
|
6
8
|
import { serveLint } from "./server/linter.mjs";
|
|
7
9
|
import { sendEmail } from "./server/email.mjs";
|
|
@@ -16,6 +18,7 @@ import tailwindcss from "@tailwindcss/vite";
|
|
|
16
18
|
import { createHighlighter } from "shiki";
|
|
17
19
|
|
|
18
20
|
//#region src/serve.ts
|
|
21
|
+
var import_picomatch = /* @__PURE__ */ __toESM(require_picomatch(), 1);
|
|
19
22
|
const devUIDir = resolve(dirname(fileURLToPath(import.meta.url)), "server/ui");
|
|
20
23
|
const require = createRequire(import.meta.url);
|
|
21
24
|
const frameworkNodeModules = resolve(dirname(require.resolve("vue/package.json")), "..");
|
|
@@ -37,7 +40,8 @@ async function serve(options = {}) {
|
|
|
37
40
|
dts: true,
|
|
38
41
|
markdown: config.markdown,
|
|
39
42
|
root: config.root,
|
|
40
|
-
componentDirs: [config.components?.source ?? []].flat()
|
|
43
|
+
componentDirs: [config.components?.source ?? []].flat(),
|
|
44
|
+
vite: config.vite
|
|
41
45
|
});
|
|
42
46
|
const server = await createServer({
|
|
43
47
|
configFile: false,
|
|
@@ -147,10 +151,12 @@ function maizzleDevPlugin(config, renderer, configInput) {
|
|
|
147
151
|
"maizzle.config.js",
|
|
148
152
|
"maizzle.config.ts",
|
|
149
153
|
"tailwind.config.js",
|
|
150
|
-
"tailwind.config.ts"
|
|
154
|
+
"tailwind.config.ts",
|
|
155
|
+
"locales/**"
|
|
151
156
|
];
|
|
152
157
|
const userWatchPaths = config.server?.watch ?? [];
|
|
153
158
|
const watchPaths = [...defaultWatchPaths, ...userWatchPaths];
|
|
159
|
+
const isWatchedFile = (0, import_picomatch.default)(watchPaths);
|
|
154
160
|
for (const watchPath of watchPaths) server.watcher.add(watchPath);
|
|
155
161
|
server.watcher.on("add", async (file) => {
|
|
156
162
|
if (isTemplateFile(file)) {
|
|
@@ -171,18 +177,19 @@ function maizzleDevPlugin(config, renderer, configInput) {
|
|
|
171
177
|
}
|
|
172
178
|
});
|
|
173
179
|
server.watcher.on("change", async (file) => {
|
|
174
|
-
if (
|
|
180
|
+
if (isWatchedFile(file)) {
|
|
175
181
|
config = await resolveConfig(configInput);
|
|
176
182
|
await renderer.close();
|
|
177
183
|
renderer = await createRenderer({
|
|
178
184
|
dts: true,
|
|
179
185
|
markdown: config.markdown,
|
|
180
186
|
root: config.root,
|
|
181
|
-
componentDirs: [config.components?.source ?? []].flat()
|
|
187
|
+
componentDirs: [config.components?.source ?? []].flat(),
|
|
188
|
+
vite: config.vite
|
|
182
189
|
});
|
|
183
190
|
}
|
|
184
191
|
await renderer.invalidateAll();
|
|
185
|
-
if (isTemplateFile(file) ||
|
|
192
|
+
if (isTemplateFile(file) || isWatchedFile(file)) server.ws.send({
|
|
186
193
|
type: "custom",
|
|
187
194
|
event: "maizzle:template-updated",
|
|
188
195
|
data: { file }
|
package/dist/serve.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve.mjs","names":[],"sources":["../src/serve.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { dirname, resolve, basename } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { createRequire } from 'node:module'\nimport { createServer, createLogger, type ViteDevServer } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport tailwindcss from '@tailwindcss/vite'\nimport { glob } from 'tinyglobby'\nimport { createHighlighter, type Highlighter } from 'shiki'\nimport { createPlaintext } from './plaintext.ts'\nimport { resolveConfig } from './config/index.ts'\nimport { runTransformers } from './transformers/index.ts'\nimport { createRenderer, type Renderer } from './render/createRenderer.ts'\nimport { serveCompatibility } from './server/compatibility.ts'\nimport { serveLint } from './server/linter.ts'\nimport { sendEmail } from './server/email.ts'\nimport type { MaizzleConfig } from './types/index.ts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst devUIDir = resolve(__dirname, 'server/ui')\n\nconst require = createRequire(import.meta.url)\nconst frameworkNodeModules = resolve(dirname(require.resolve('vue/package.json')), '..')\nconst vuePkgDir = dirname(require.resolve('vue/package.json'))\n\nexport interface ServeOptions {\n config?: Partial<MaizzleConfig> | string\n /** Expose the server on the network (e.g. --host) */\n host?: boolean | string\n /** When true, suppresses the banner/URL output (used by the Vite plugin, which prints its own) */\n silent?: boolean\n}\n\n/**\n * Start the Maizzle dev server.\n *\n * Creates two things:\n * 1. A Vite dev server for the dev UI (sidebar + preview, with Vue + Tailwind for the UI itself)\n * 2. A Renderer instance for SSR rendering email templates\n *\n * Template rendering goes through the Renderer, not the Vite dev server.\n */\nexport async function serve(options: ServeOptions = {}) {\n const start = performance.now()\n\n let config = await resolveConfig(options.config)\n const port = config.server?.port ?? 3000\n\n // Create a renderer for SSR rendering email templates (with dts for dev)\n let renderer = await createRenderer({ dts: true, markdown: config.markdown, root: config.root, componentDirs: [config.components?.source ?? []].flat() })\n\n const server = await createServer({\n configFile: false,\n plugins: [\n // Vue and Tailwind are only for the dev UI SPA, not for email templates\n vue(),\n tailwindcss(),\n maizzleDevPlugin(config, renderer, options.config),\n ],\n resolve: {\n dedupe: ['vue'],\n alias: [\n { find: '@', replacement: devUIDir },\n { find: 'vue', replacement: resolve(vuePkgDir, 'dist/vue.runtime.esm-bundler.js') },\n { find: 'vue-router', replacement: resolve(frameworkNodeModules, 'vue-router') },\n { find: 'reka-ui', replacement: resolve(frameworkNodeModules, 'reka-ui') },\n { find: '@vueuse/core', replacement: resolve(frameworkNodeModules, '@vueuse/core') },\n { find: '@vueuse/shared', replacement: resolve(frameworkNodeModules, '@vueuse/shared') },\n { find: 'lucide-vue-next', replacement: resolve(frameworkNodeModules, 'lucide-vue-next') },\n { find: 'class-variance-authority', replacement: resolve(frameworkNodeModules, 'class-variance-authority') },\n { find: 'clsx', replacement: resolve(frameworkNodeModules, 'clsx') },\n { find: 'tailwind-merge', replacement: resolve(frameworkNodeModules, 'tailwind-merge') },\n ],\n },\n cacheDir: resolve(devUIDir, '.vite'),\n optimizeDeps: {\n noDiscovery: true,\n include: [\n 'vue',\n 'vue-router',\n 'lucide-vue-next',\n '@vueuse/core',\n '@vueuse/shared',\n 'reka-ui',\n 'class-variance-authority',\n 'clsx',\n 'tailwind-merge',\n ],\n },\n server: {\n port,\n host: options.host,\n fs: {\n allow: [process.cwd(), config.root ?? process.cwd(), devUIDir, frameworkNodeModules],\n },\n },\n customLogger: customLogger(),\n })\n\n // Store renderer ref on server for cleanup\n const originalClose = server.close.bind(server)\n server.close = async () => {\n await renderer.close()\n return originalClose()\n }\n\n await server.listen()\n\n const startupTime = Math.round(performance.now() - start)\n\n if (!options.silent) {\n printBanner(server, startupTime)\n }\n\n // Expose startup time so the plugin can print it later\n ; (server as any)._maizzleStartupTime = startupTime\n\n return server\n}\n\n/**\n * Internal Vite plugin that adds Maizzle middleware and file watching to the dev UI server.\n */\nfunction maizzleDevPlugin(\n config: MaizzleConfig,\n renderer: Renderer,\n configInput: Partial<MaizzleConfig> | string | undefined,\n) {\n return {\n name: 'maizzle:dev',\n enforce: 'pre' as const,\n\n hotUpdate: {\n order: 'pre' as const,\n handler({ file }: { file: string }) {\n // Prevent Tailwind/Vue from triggering a full reload for email template files.\n // Maizzle handles these via custom HMR events in the watcher below.\n if (isTemplateFile(file)) {\n return []\n }\n },\n },\n\n configureServer(server: ViteDevServer) {\n // File watching\n const defaultWatchPaths = [\n 'maizzle.config.js',\n 'maizzle.config.ts',\n 'tailwind.config.js',\n 'tailwind.config.ts',\n ]\n\n const userWatchPaths = config.server?.watch ?? []\n const watchPaths = [...defaultWatchPaths, ...userWatchPaths]\n\n for (const watchPath of watchPaths) {\n server.watcher.add(watchPath)\n }\n\n server.watcher.on('add', async (file) => {\n if (isTemplateFile(file)) {\n await renderer.invalidateAll()\n server.ws.send({ type: 'custom', event: 'maizzle:templates-changed' })\n }\n })\n\n server.watcher.on('unlink', async (file) => {\n if (isTemplateFile(file)) {\n await renderer.invalidateAll()\n server.ws.send({ type: 'custom', event: 'maizzle:templates-changed' })\n }\n })\n\n server.watcher.on('change', async (file) => {\n if (watchPaths.some(p => file.endsWith(p))) {\n config = await resolveConfig(configInput)\n\n // Recreate the renderer so config changes (e.g. markdown.shikiTheme) take effect\n await renderer.close()\n renderer = await createRenderer({ dts: true, markdown: config.markdown, root: config.root, componentDirs: [config.components?.source ?? []].flat() })\n }\n\n // Invalidate all renderer modules so component and config changes\n // are picked up on the next render (Tailwind recompiles with fresh content)\n await renderer.invalidateAll()\n\n if (\n isTemplateFile(file)\n || watchPaths.some(p => file.endsWith(p))\n ) {\n server.ws.send({ type: 'custom', event: 'maizzle:template-updated', data: { file } })\n }\n })\n\n // API middleware (before Vite's middleware)\n server.middlewares.use(async (req: any, res: any, next: any) => {\n const url = req.url || '/'\n\n if (url === '/__maizzle/templates') {\n return serveTemplateList(config, res)\n }\n\n if (url.startsWith('/__maizzle/render/')) {\n return await serveRenderedTemplate(url, config, renderer, res)\n }\n\n if (url.startsWith('/__maizzle/source/')) {\n return await serveHighlightedSource(url, config, renderer, res)\n }\n\n if (url === '/__maizzle/compatibility' && req.method === 'POST') {\n return await serveCompatibility(req, res)\n }\n\n if (url.startsWith('/__maizzle/lint/')) {\n return serveLint(url, res)\n }\n\n if (url.startsWith('/__maizzle/vue-source/')) {\n return await serveVueSource(url, config, res)\n }\n\n if (url.startsWith('/__maizzle/plaintext/')) {\n return await servePlaintext(url, config, renderer, res)\n }\n\n if (url.startsWith('/__maizzle/stats/')) {\n return await serveStats(url, config, renderer, res)\n }\n\n if (url.startsWith('/__maizzle/email/') && req.method === 'POST') {\n return await serveEmailEndpoint(url, req, res, config, renderer)\n }\n\n if (url === '/__maizzle/email-config') {\n return serveEmailConfig(config, res)\n }\n\n next()\n })\n\n // Dev UI fallback (after Vite's middleware)\n return () => {\n server.middlewares.use(async (req: any, res: any, next: any) => {\n if (isNavigationRequest(req)) {\n return await serveDevUI(server, res, req.url || '/')\n }\n\n next()\n })\n }\n },\n }\n}\n\nfunction isTemplateFile(file: string): boolean {\n return (file.endsWith('.vue') || file.endsWith('.md')) && !file.includes('server/ui')\n}\n\nfunction isNavigationRequest(req: any): boolean {\n const accept = req.headers?.accept || ''\n return req.method === 'GET' && accept.includes('text/html')\n}\n\nasync function serveDevUI(server: ViteDevServer, res: any, url: string) {\n let indexHtml = readFileSync(resolve(devUIDir, 'index.html'), 'utf-8')\n\n indexHtml = indexHtml.replace('./main.ts', `/@fs/${resolve(devUIDir, 'main.ts')}`)\n indexHtml = indexHtml.replace('./favicon.svg', `/@fs/${resolve(devUIDir, 'favicon.svg')}`)\n\n const transformed = await server.transformIndexHtml(url, indexHtml)\n\n res.setHeader('Content-Type', 'text/html')\n res.end(transformed)\n}\n\nasync function serveTemplateList(config: MaizzleConfig, res: any) {\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n\n const data = templates.map(t => ({\n name: basename(t).replace(/\\.(vue|md)$/, ''),\n path: t,\n href: '/' + t.replace(/\\.(vue|md)$/, ''),\n }))\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(data))\n}\n\n/**\n * SSR render a .vue template using the Renderer (not the dev UI server).\n */\nasync function serveRenderedTemplate(url: string, config: MaizzleConfig, renderer: Renderer, res: any) {\n const templateSlug = url.replace('/__maizzle/render/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end('Template not found')\n return\n }\n\n try {\n const absolutePath = resolve(match)\n\n // Invalidate all modules so template + component changes are picked up\n await renderer.invalidateAll()\n\n const rendered = await renderer.render(absolutePath, config)\n let html = rendered.html\n\n const templateConfig = rendered.templateConfig\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n html = `${doctype}\\n${html}`\n\n res.setHeader('Content-Type', 'text/html')\n res.end(html)\n } catch (error: any) {\n res.statusCode = 500\n res.end(`<pre>${error.stack || error.message}</pre>`)\n }\n}\n\nlet highlighter: Highlighter | null = null\n\nasync function getHighlighter() {\n if (!highlighter) {\n highlighter = await createHighlighter({\n themes: ['laserwave'],\n langs: ['html', 'vue'],\n })\n }\n return highlighter\n}\n\nasync function serveHighlightedSource(url: string, config: MaizzleConfig, renderer: Renderer, res: any) {\n const templateSlug = url.replace('/__maizzle/source/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end('Template not found')\n return\n }\n\n try {\n const absolutePath = resolve(match)\n\n await renderer.invalidateAll()\n\n const rendered = await renderer.render(absolutePath, config)\n let html = rendered.html\n\n const templateConfig = rendered.templateConfig\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n\n html = `${doctype}\\n${html}`\n\n const hl = await getHighlighter()\n const highlighted = hl.codeToHtml(html, {\n lang: 'html',\n theme: 'laserwave',\n transformers: [{\n line(node, line) {\n node.properties['data-line'] = line\n },\n }],\n })\n\n res.setHeader('Content-Type', 'text/html')\n res.end(highlighted)\n } catch (error: any) {\n res.statusCode = 500\n res.end(`<pre>${error.stack || error.message}</pre>`)\n }\n}\n\nasync function serveVueSource(url: string, config: MaizzleConfig, res: any) {\n const templateSlug = url.replace('/__maizzle/vue-source/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end('Template not found')\n return\n }\n\n try {\n const source = readFileSync(resolve(match), 'utf-8')\n const lang = match.endsWith('.md') ? 'html' : 'vue'\n\n const hl = await getHighlighter()\n const highlighted = hl.codeToHtml(source, {\n lang,\n theme: 'laserwave',\n transformers: [{\n line(node, line) {\n node.properties['data-line'] = line\n },\n }],\n })\n\n res.setHeader('Content-Type', 'text/html')\n res.end(highlighted)\n } catch (error: any) {\n res.statusCode = 500\n res.end(`<pre>${error.stack || error.message}</pre>`)\n }\n}\n\nasync function servePlaintext(url: string, config: MaizzleConfig, renderer: Renderer, res: any) {\n const templateSlug = url.replace('/__maizzle/plaintext/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end('Template not found')\n return\n }\n\n try {\n const absolutePath = resolve(match)\n await renderer.invalidateAll()\n\n const rendered = await renderer.render(absolutePath, config)\n let html = rendered.html\n const templateConfig = rendered.templateConfig\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n\n const plaintext = createPlaintext(html)\n\n res.setHeader('Content-Type', 'text/plain')\n res.end(plaintext)\n } catch (error: any) {\n res.statusCode = 500\n res.end(error.message)\n }\n}\n\nfunction humanFileSize(bytes: number, si = false, dp = 2) {\n const threshold = si ? 1000 : 1024\n\n if (Math.abs(bytes) < threshold) {\n return bytes + ' B'\n }\n\n const units = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']\n let u = -1\n const r = 10 ** dp\n\n do {\n bytes /= threshold\n ++u\n } while (Math.round(Math.abs(bytes) * r) / r >= threshold && u < units.length - 1)\n\n return bytes.toFixed(dp) + ' ' + units[u]\n}\n\nasync function serveStats(url: string, config: MaizzleConfig, renderer: Renderer, res: any) {\n const templateSlug = url.replace('/__maizzle/stats/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end(JSON.stringify({ error: 'Template not found' }))\n return\n }\n\n try {\n const absolutePath = resolve(match)\n await renderer.invalidateAll()\n\n const rendered = await renderer.render(absolutePath, config)\n let html = rendered.html\n const templateConfig = rendered.templateConfig\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n\n const sizeBytes = Buffer.byteLength(html, 'utf-8')\n\n // Count images: <img> tags and CSS background images\n const imgTags = (html.match(/<img\\b[^>]*>/gi) || []).length\n const bgImages = (html.match(/url\\s*\\([^)]+\\)/gi) || []).length\n const totalImages = imgTags + bgImages\n\n // Count links\n const links = (html.match(/<a\\b[^>]*href\\s*=/gi) || []).length\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({\n size: {\n bytes: sizeBytes,\n formatted: humanFileSize(sizeBytes),\n },\n images: totalImages,\n links,\n }))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n\nasync function serveEmailEndpoint(url: string, req: any, res: any, config: MaizzleConfig, renderer: Renderer) {\n const templateSlug = url.replace('/__maizzle/email/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end(JSON.stringify({ success: false, message: 'Template not found' }))\n return\n }\n\n let body = ''\n for await (const chunk of req) body += chunk\n\n let payload: { to: string[]; subject: string }\n\n try {\n payload = JSON.parse(body)\n } catch {\n res.statusCode = 400\n res.end(JSON.stringify({ success: false, message: 'Invalid JSON' }))\n return\n }\n\n if (!payload.to?.length) {\n res.statusCode = 400\n res.end(JSON.stringify({ success: false, message: 'Missing recipients' }))\n return\n }\n\n try {\n const absolutePath = resolve(match)\n await renderer.invalidateAll()\n\n const rendered = await renderer.render(absolutePath, config)\n let html = rendered.html\n const templateConfig = rendered.templateConfig\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n html = `${doctype}\\n${html}`\n\n const text = createPlaintext(html)\n\n const result = await sendEmail(\n { to: payload.to, subject: payload.subject, html, text },\n config,\n templateConfig,\n )\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(result))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ success: false, message: error.message }))\n }\n}\n\nfunction serveEmailConfig(config: MaizzleConfig, res: any) {\n const emailConfig = config.server?.email\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({\n to: emailConfig?.to ? (Array.isArray(emailConfig.to) ? emailConfig.to : [emailConfig.to]) : [],\n from: emailConfig?.from ?? '',\n subject: emailConfig?.subject ?? '',\n hasTransport: !!emailConfig?.transport,\n }))\n}\n\nexport function printBanner(server: ViteDevServer, startupTime?: number) {\n const info = server.config.logger.info\n const time = startupTime ?? (server as any)._maizzleStartupTime\n\n info('')\n info(` \\x1b[32m\\x1b[1mMAIZZLE\\x1b[0m\\x1b[32m v6.0.0\\x1b[0m \\x1b[2mready in\\x1b[0m \\x1b[1m${time}\\x1b[0m ms`)\n info('')\n server.printUrls()\n info('')\n}\n\nfunction customLogger() {\n const logger = createLogger('info')\n const warn = logger.warn\n\n logger.warn = (message, options) => {\n if (typeof message === 'string' && message.includes('<tr> cannot be child of <table>')) {\n return\n }\n\n warn(message, options)\n }\n\n return logger\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmBA,MAAM,WAAW,QADC,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EACrB,YAAY;AAEhD,MAAM,UAAU,cAAc,OAAO,KAAK,IAAI;AAC9C,MAAM,uBAAuB,QAAQ,QAAQ,QAAQ,QAAQ,mBAAmB,CAAC,EAAE,KAAK;AACxF,MAAM,YAAY,QAAQ,QAAQ,QAAQ,mBAAmB,CAAC;;;;;;;;;;AAmB9D,eAAsB,MAAM,UAAwB,EAAE,EAAE;CACtD,MAAM,QAAQ,YAAY,KAAK;CAE/B,IAAI,SAAS,MAAM,cAAc,QAAQ,OAAO;CAChD,MAAM,OAAO,OAAO,QAAQ,QAAQ;CAGpC,IAAI,WAAW,MAAM,eAAe;EAAE,KAAK;EAAM,UAAU,OAAO;EAAU,MAAM,OAAO;EAAM,eAAe,CAAC,OAAO,YAAY,UAAU,EAAE,CAAC,CAAC,MAAM;EAAE,CAAC;CAEzJ,MAAM,SAAS,MAAM,aAAa;EAChC,YAAY;EACZ,SAAS;GAEP,KAAK;GACL,aAAa;GACb,iBAAiB,QAAQ,UAAU,QAAQ,OAAO;GACnD;EACD,SAAS;GACP,QAAQ,CAAC,MAAM;GACf,OAAO;IACL;KAAE,MAAM;KAAK,aAAa;KAAU;IACpC;KAAE,MAAM;KAAO,aAAa,QAAQ,WAAW,kCAAkC;KAAE;IACnF;KAAE,MAAM;KAAc,aAAa,QAAQ,sBAAsB,aAAa;KAAE;IAChF;KAAE,MAAM;KAAW,aAAa,QAAQ,sBAAsB,UAAU;KAAE;IAC1E;KAAE,MAAM;KAAgB,aAAa,QAAQ,sBAAsB,eAAe;KAAE;IACpF;KAAE,MAAM;KAAkB,aAAa,QAAQ,sBAAsB,iBAAiB;KAAE;IACxF;KAAE,MAAM;KAAmB,aAAa,QAAQ,sBAAsB,kBAAkB;KAAE;IAC1F;KAAE,MAAM;KAA4B,aAAa,QAAQ,sBAAsB,2BAA2B;KAAE;IAC5G;KAAE,MAAM;KAAQ,aAAa,QAAQ,sBAAsB,OAAO;KAAE;IACpE;KAAE,MAAM;KAAkB,aAAa,QAAQ,sBAAsB,iBAAiB;KAAE;IACzF;GACF;EACD,UAAU,QAAQ,UAAU,QAAQ;EACpC,cAAc;GACZ,aAAa;GACb,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACF;EACD,QAAQ;GACN;GACA,MAAM,QAAQ;GACd,IAAI,EACF,OAAO;IAAC,QAAQ,KAAK;IAAE,OAAO,QAAQ,QAAQ,KAAK;IAAE;IAAU;IAAqB,EACrF;GACF;EACD,cAAc,cAAc;EAC7B,CAAC;CAGF,MAAM,gBAAgB,OAAO,MAAM,KAAK,OAAO;AAC/C,QAAO,QAAQ,YAAY;AACzB,QAAM,SAAS,OAAO;AACtB,SAAO,eAAe;;AAGxB,OAAM,OAAO,QAAQ;CAErB,MAAM,cAAc,KAAK,MAAM,YAAY,KAAK,GAAG,MAAM;AAEzD,KAAI,CAAC,QAAQ,OACX,aAAY,QAAQ,YAAY;AAIhC,CAAC,OAAe,sBAAsB;AAExC,QAAO;;;;;AAMT,SAAS,iBACP,QACA,UACA,aACA;AACA,QAAO;EACL,MAAM;EACN,SAAS;EAET,WAAW;GACT,OAAO;GACP,QAAQ,EAAE,QAA0B;AAGlC,QAAI,eAAe,KAAK,CACtB,QAAO,EAAE;;GAGd;EAED,gBAAgB,QAAuB;GAErC,MAAM,oBAAoB;IACxB;IACA;IACA;IACA;IACD;GAED,MAAM,iBAAiB,OAAO,QAAQ,SAAS,EAAE;GACjD,MAAM,aAAa,CAAC,GAAG,mBAAmB,GAAG,eAAe;AAE5D,QAAK,MAAM,aAAa,WACtB,QAAO,QAAQ,IAAI,UAAU;AAG/B,UAAO,QAAQ,GAAG,OAAO,OAAO,SAAS;AACvC,QAAI,eAAe,KAAK,EAAE;AACxB,WAAM,SAAS,eAAe;AAC9B,YAAO,GAAG,KAAK;MAAE,MAAM;MAAU,OAAO;MAA6B,CAAC;;KAExE;AAEF,UAAO,QAAQ,GAAG,UAAU,OAAO,SAAS;AAC1C,QAAI,eAAe,KAAK,EAAE;AACxB,WAAM,SAAS,eAAe;AAC9B,YAAO,GAAG,KAAK;MAAE,MAAM;MAAU,OAAO;MAA6B,CAAC;;KAExE;AAEF,UAAO,QAAQ,GAAG,UAAU,OAAO,SAAS;AAC1C,QAAI,WAAW,MAAK,MAAK,KAAK,SAAS,EAAE,CAAC,EAAE;AAC1C,cAAS,MAAM,cAAc,YAAY;AAGzC,WAAM,SAAS,OAAO;AACtB,gBAAW,MAAM,eAAe;MAAE,KAAK;MAAM,UAAU,OAAO;MAAU,MAAM,OAAO;MAAM,eAAe,CAAC,OAAO,YAAY,UAAU,EAAE,CAAC,CAAC,MAAM;MAAE,CAAC;;AAKvJ,UAAM,SAAS,eAAe;AAE9B,QACE,eAAe,KAAK,IACjB,WAAW,MAAK,MAAK,KAAK,SAAS,EAAE,CAAC,CAEzC,QAAO,GAAG,KAAK;KAAE,MAAM;KAAU,OAAO;KAA4B,MAAM,EAAE,MAAM;KAAE,CAAC;KAEvF;AAGF,UAAO,YAAY,IAAI,OAAO,KAAU,KAAU,SAAc;IAC9D,MAAM,MAAM,IAAI,OAAO;AAEvB,QAAI,QAAQ,uBACV,QAAO,kBAAkB,QAAQ,IAAI;AAGvC,QAAI,IAAI,WAAW,qBAAqB,CACtC,QAAO,MAAM,sBAAsB,KAAK,QAAQ,UAAU,IAAI;AAGhE,QAAI,IAAI,WAAW,qBAAqB,CACtC,QAAO,MAAM,uBAAuB,KAAK,QAAQ,UAAU,IAAI;AAGjE,QAAI,QAAQ,8BAA8B,IAAI,WAAW,OACvD,QAAO,MAAM,mBAAmB,KAAK,IAAI;AAG3C,QAAI,IAAI,WAAW,mBAAmB,CACpC,QAAO,UAAU,KAAK,IAAI;AAG5B,QAAI,IAAI,WAAW,yBAAyB,CAC1C,QAAO,MAAM,eAAe,KAAK,QAAQ,IAAI;AAG/C,QAAI,IAAI,WAAW,wBAAwB,CACzC,QAAO,MAAM,eAAe,KAAK,QAAQ,UAAU,IAAI;AAGzD,QAAI,IAAI,WAAW,oBAAoB,CACrC,QAAO,MAAM,WAAW,KAAK,QAAQ,UAAU,IAAI;AAGrD,QAAI,IAAI,WAAW,oBAAoB,IAAI,IAAI,WAAW,OACxD,QAAO,MAAM,mBAAmB,KAAK,KAAK,KAAK,QAAQ,SAAS;AAGlE,QAAI,QAAQ,0BACV,QAAO,iBAAiB,QAAQ,IAAI;AAGtC,UAAM;KACN;AAGF,gBAAa;AACX,WAAO,YAAY,IAAI,OAAO,KAAU,KAAU,SAAc;AAC9D,SAAI,oBAAoB,IAAI,CAC1B,QAAO,MAAM,WAAW,QAAQ,KAAK,IAAI,OAAO,IAAI;AAGtD,WAAM;MACN;;;EAGP;;AAGH,SAAS,eAAe,MAAuB;AAC7C,SAAQ,KAAK,SAAS,OAAO,IAAI,KAAK,SAAS,MAAM,KAAK,CAAC,KAAK,SAAS,YAAY;;AAGvF,SAAS,oBAAoB,KAAmB;CAC9C,MAAM,SAAS,IAAI,SAAS,UAAU;AACtC,QAAO,IAAI,WAAW,SAAS,OAAO,SAAS,YAAY;;AAG7D,eAAe,WAAW,QAAuB,KAAU,KAAa;CACtE,IAAI,YAAY,aAAa,QAAQ,UAAU,aAAa,EAAE,QAAQ;AAEtE,aAAY,UAAU,QAAQ,aAAa,QAAQ,QAAQ,UAAU,UAAU,GAAG;AAClF,aAAY,UAAU,QAAQ,iBAAiB,QAAQ,QAAQ,UAAU,cAAc,GAAG;CAE1F,MAAM,cAAc,MAAM,OAAO,mBAAmB,KAAK,UAAU;AAEnE,KAAI,UAAU,gBAAgB,YAAY;AAC1C,KAAI,IAAI,YAAY;;AAGtB,eAAe,kBAAkB,QAAuB,KAAU;CAIhE,MAAM,QAFY,MAAM,KADA,OAAO,WAAW,CAAC,kBAAkB,CAChB,EAEtB,KAAI,OAAM;EAC/B,MAAM,SAAS,EAAE,CAAC,QAAQ,eAAe,GAAG;EAC5C,MAAM;EACN,MAAM,MAAM,EAAE,QAAQ,eAAe,GAAG;EACzC,EAAE;AAEH,KAAI,UAAU,gBAAgB,mBAAmB;AACjD,KAAI,IAAI,KAAK,UAAU,KAAK,CAAC;;;;;AAM/B,eAAe,sBAAsB,KAAa,QAAuB,UAAoB,KAAU;CACrG,MAAM,eAAe,IAAI,QAAQ,sBAAsB,GAAG,CAAC,QAAQ,SAAS,GAAG;CAI/E,MAAM,SADY,MAAM,KADA,OAAO,WAAW,CAAC,kBAAkB,CAChB,EACrB,MAAK,MAAK,EAAE,QAAQ,eAAe,GAAG,KAAK,aAAa;AAEhF,KAAI,CAAC,OAAO;AACV,MAAI,aAAa;AACjB,MAAI,IAAI,qBAAqB;AAC7B;;AAGF,KAAI;EACF,MAAM,eAAe,QAAQ,MAAM;AAGnC,QAAM,SAAS,eAAe;EAE9B,MAAM,WAAW,MAAM,SAAS,OAAO,cAAc,OAAO;EAC5D,IAAI,OAAO,SAAS;EAEpB,MAAM,iBAAiB,SAAS;EAChC,MAAM,UAAU,SAAS,WAAW,eAAe,WAAW;AAE9D,SAAO,MAAM,gBAAgB,MAAM,gBAAgB,cAAc,QAAQ;AACzE,SAAO,GAAG,QAAQ,IAAI;AAEtB,MAAI,UAAU,gBAAgB,YAAY;AAC1C,MAAI,IAAI,KAAK;UACN,OAAY;AACnB,MAAI,aAAa;AACjB,MAAI,IAAI,QAAQ,MAAM,SAAS,MAAM,QAAQ,QAAQ;;;AAIzD,IAAI,cAAkC;AAEtC,eAAe,iBAAiB;AAC9B,KAAI,CAAC,YACH,eAAc,MAAM,kBAAkB;EACpC,QAAQ,CAAC,YAAY;EACrB,OAAO,CAAC,QAAQ,MAAM;EACvB,CAAC;AAEJ,QAAO;;AAGT,eAAe,uBAAuB,KAAa,QAAuB,UAAoB,KAAU;CACtG,MAAM,eAAe,IAAI,QAAQ,sBAAsB,GAAG,CAAC,QAAQ,SAAS,GAAG;CAI/E,MAAM,SADY,MAAM,KADA,OAAO,WAAW,CAAC,kBAAkB,CAChB,EACrB,MAAK,MAAK,EAAE,QAAQ,eAAe,GAAG,KAAK,aAAa;AAEhF,KAAI,CAAC,OAAO;AACV,MAAI,aAAa;AACjB,MAAI,IAAI,qBAAqB;AAC7B;;AAGF,KAAI;EACF,MAAM,eAAe,QAAQ,MAAM;AAEnC,QAAM,SAAS,eAAe;EAE9B,MAAM,WAAW,MAAM,SAAS,OAAO,cAAc,OAAO;EAC5D,IAAI,OAAO,SAAS;EAEpB,MAAM,iBAAiB,SAAS;EAChC,MAAM,UAAU,SAAS,WAAW,eAAe,WAAW;AAC9D,SAAO,MAAM,gBAAgB,MAAM,gBAAgB,cAAc,QAAQ;AAEzE,SAAO,GAAG,QAAQ,IAAI;EAGtB,MAAM,eADK,MAAM,gBAAgB,EACV,WAAW,MAAM;GACtC,MAAM;GACN,OAAO;GACP,cAAc,CAAC,EACb,KAAK,MAAM,MAAM;AACf,SAAK,WAAW,eAAe;MAElC,CAAC;GACH,CAAC;AAEF,MAAI,UAAU,gBAAgB,YAAY;AAC1C,MAAI,IAAI,YAAY;UACb,OAAY;AACnB,MAAI,aAAa;AACjB,MAAI,IAAI,QAAQ,MAAM,SAAS,MAAM,QAAQ,QAAQ;;;AAIzD,eAAe,eAAe,KAAa,QAAuB,KAAU;CAC1E,MAAM,eAAe,IAAI,QAAQ,0BAA0B,GAAG,CAAC,QAAQ,SAAS,GAAG;CAInF,MAAM,SADY,MAAM,KADA,OAAO,WAAW,CAAC,kBAAkB,CAChB,EACrB,MAAK,MAAK,EAAE,QAAQ,eAAe,GAAG,KAAK,aAAa;AAEhF,KAAI,CAAC,OAAO;AACV,MAAI,aAAa;AACjB,MAAI,IAAI,qBAAqB;AAC7B;;AAGF,KAAI;EACF,MAAM,SAAS,aAAa,QAAQ,MAAM,EAAE,QAAQ;EACpD,MAAM,OAAO,MAAM,SAAS,MAAM,GAAG,SAAS;EAG9C,MAAM,eADK,MAAM,gBAAgB,EACV,WAAW,QAAQ;GACxC;GACA,OAAO;GACP,cAAc,CAAC,EACb,KAAK,MAAM,MAAM;AACf,SAAK,WAAW,eAAe;MAElC,CAAC;GACH,CAAC;AAEF,MAAI,UAAU,gBAAgB,YAAY;AAC1C,MAAI,IAAI,YAAY;UACb,OAAY;AACnB,MAAI,aAAa;AACjB,MAAI,IAAI,QAAQ,MAAM,SAAS,MAAM,QAAQ,QAAQ;;;AAIzD,eAAe,eAAe,KAAa,QAAuB,UAAoB,KAAU;CAC9F,MAAM,eAAe,IAAI,QAAQ,yBAAyB,GAAG,CAAC,QAAQ,SAAS,GAAG;CAIlF,MAAM,SADY,MAAM,KADA,OAAO,WAAW,CAAC,kBAAkB,CAChB,EACrB,MAAK,MAAK,EAAE,QAAQ,eAAe,GAAG,KAAK,aAAa;AAEhF,KAAI,CAAC,OAAO;AACV,MAAI,aAAa;AACjB,MAAI,IAAI,qBAAqB;AAC7B;;AAGF,KAAI;EACF,MAAM,eAAe,QAAQ,MAAM;AACnC,QAAM,SAAS,eAAe;EAE9B,MAAM,WAAW,MAAM,SAAS,OAAO,cAAc,OAAO;EAC5D,IAAI,OAAO,SAAS;EACpB,MAAM,iBAAiB,SAAS;EAChC,MAAM,UAAU,SAAS,WAAW,eAAe,WAAW;AAC9D,SAAO,MAAM,gBAAgB,MAAM,gBAAgB,cAAc,QAAQ;EAEzE,MAAM,YAAY,gBAAgB,KAAK;AAEvC,MAAI,UAAU,gBAAgB,aAAa;AAC3C,MAAI,IAAI,UAAU;UACX,OAAY;AACnB,MAAI,aAAa;AACjB,MAAI,IAAI,MAAM,QAAQ;;;AAI1B,SAAS,cAAc,OAAe,KAAK,OAAO,KAAK,GAAG;CACxD,MAAM,YAAY,KAAK,MAAO;AAE9B,KAAI,KAAK,IAAI,MAAM,GAAG,UACpB,QAAO,QAAQ;CAGjB,MAAM,QAAQ;EAAC;EAAM;EAAM;EAAM;EAAM;EAAM;EAAM;EAAM;EAAK;CAC9D,IAAI,IAAI;CACR,MAAM,IAAI,MAAM;AAEhB,IAAG;AACD,WAAS;AACT,IAAE;UACK,KAAK,MAAM,KAAK,IAAI,MAAM,GAAG,EAAE,GAAG,KAAK,aAAa,IAAI,MAAM,SAAS;AAEhF,QAAO,MAAM,QAAQ,GAAG,GAAG,MAAM,MAAM;;AAGzC,eAAe,WAAW,KAAa,QAAuB,UAAoB,KAAU;CAC1F,MAAM,eAAe,IAAI,QAAQ,qBAAqB,GAAG,CAAC,QAAQ,SAAS,GAAG;CAI9E,MAAM,SADY,MAAM,KADA,OAAO,WAAW,CAAC,kBAAkB,CAChB,EACrB,MAAK,MAAK,EAAE,QAAQ,eAAe,GAAG,KAAK,aAAa;AAEhF,KAAI,CAAC,OAAO;AACV,MAAI,aAAa;AACjB,MAAI,IAAI,KAAK,UAAU,EAAE,OAAO,sBAAsB,CAAC,CAAC;AACxD;;AAGF,KAAI;EACF,MAAM,eAAe,QAAQ,MAAM;AACnC,QAAM,SAAS,eAAe;EAE9B,MAAM,WAAW,MAAM,SAAS,OAAO,cAAc,OAAO;EAC5D,IAAI,OAAO,SAAS;EACpB,MAAM,iBAAiB,SAAS;EAChC,MAAM,UAAU,SAAS,WAAW,eAAe,WAAW;AAC9D,SAAO,MAAM,gBAAgB,MAAM,gBAAgB,cAAc,QAAQ;EAEzE,MAAM,YAAY,OAAO,WAAW,MAAM,QAAQ;EAKlD,MAAM,eAFW,KAAK,MAAM,iBAAiB,IAAI,EAAE,EAAE,UACnC,KAAK,MAAM,oBAAoB,IAAI,EAAE,EAAE;EAIzD,MAAM,SAAS,KAAK,MAAM,sBAAsB,IAAI,EAAE,EAAE;AAExD,MAAI,UAAU,gBAAgB,mBAAmB;AACjD,MAAI,IAAI,KAAK,UAAU;GACrB,MAAM;IACJ,OAAO;IACP,WAAW,cAAc,UAAU;IACpC;GACD,QAAQ;GACR;GACD,CAAC,CAAC;UACI,OAAY;AACnB,MAAI,aAAa;AACjB,MAAI,IAAI,KAAK,UAAU,EAAE,OAAO,MAAM,SAAS,CAAC,CAAC;;;AAIrD,eAAe,mBAAmB,KAAa,KAAU,KAAU,QAAuB,UAAoB;CAC5G,MAAM,eAAe,IAAI,QAAQ,qBAAqB,GAAG,CAAC,QAAQ,SAAS,GAAG;CAI9E,MAAM,SADY,MAAM,KADA,OAAO,WAAW,CAAC,kBAAkB,CAChB,EACrB,MAAK,MAAK,EAAE,QAAQ,eAAe,GAAG,KAAK,aAAa;AAEhF,KAAI,CAAC,OAAO;AACV,MAAI,aAAa;AACjB,MAAI,IAAI,KAAK,UAAU;GAAE,SAAS;GAAO,SAAS;GAAsB,CAAC,CAAC;AAC1E;;CAGF,IAAI,OAAO;AACX,YAAW,MAAM,SAAS,IAAK,SAAQ;CAEvC,IAAI;AAEJ,KAAI;AACF,YAAU,KAAK,MAAM,KAAK;SACpB;AACN,MAAI,aAAa;AACjB,MAAI,IAAI,KAAK,UAAU;GAAE,SAAS;GAAO,SAAS;GAAgB,CAAC,CAAC;AACpE;;AAGF,KAAI,CAAC,QAAQ,IAAI,QAAQ;AACvB,MAAI,aAAa;AACjB,MAAI,IAAI,KAAK,UAAU;GAAE,SAAS;GAAO,SAAS;GAAsB,CAAC,CAAC;AAC1E;;AAGF,KAAI;EACF,MAAM,eAAe,QAAQ,MAAM;AACnC,QAAM,SAAS,eAAe;EAE9B,MAAM,WAAW,MAAM,SAAS,OAAO,cAAc,OAAO;EAC5D,IAAI,OAAO,SAAS;EACpB,MAAM,iBAAiB,SAAS;EAChC,MAAM,UAAU,SAAS,WAAW,eAAe,WAAW;AAC9D,SAAO,MAAM,gBAAgB,MAAM,gBAAgB,cAAc,QAAQ;AACzE,SAAO,GAAG,QAAQ,IAAI;EAEtB,MAAM,OAAO,gBAAgB,KAAK;EAElC,MAAM,SAAS,MAAM,UACnB;GAAE,IAAI,QAAQ;GAAI,SAAS,QAAQ;GAAS;GAAM;GAAM,EACxD,QACA,eACD;AAED,MAAI,UAAU,gBAAgB,mBAAmB;AACjD,MAAI,IAAI,KAAK,UAAU,OAAO,CAAC;UACxB,OAAY;AACnB,MAAI,aAAa;AACjB,MAAI,IAAI,KAAK,UAAU;GAAE,SAAS;GAAO,SAAS,MAAM;GAAS,CAAC,CAAC;;;AAIvE,SAAS,iBAAiB,QAAuB,KAAU;CACzD,MAAM,cAAc,OAAO,QAAQ;AACnC,KAAI,UAAU,gBAAgB,mBAAmB;AACjD,KAAI,IAAI,KAAK,UAAU;EACrB,IAAI,aAAa,KAAM,MAAM,QAAQ,YAAY,GAAG,GAAG,YAAY,KAAK,CAAC,YAAY,GAAG,GAAI,EAAE;EAC9F,MAAM,aAAa,QAAQ;EAC3B,SAAS,aAAa,WAAW;EACjC,cAAc,CAAC,CAAC,aAAa;EAC9B,CAAC,CAAC;;AAGL,SAAgB,YAAY,QAAuB,aAAsB;CACvE,MAAM,OAAO,OAAO,OAAO,OAAO;CAClC,MAAM,OAAO,eAAgB,OAAe;AAE5C,MAAK,GAAG;AACR,MAAK,wFAAwF,KAAK,YAAY;AAC9G,MAAK,GAAG;AACR,QAAO,WAAW;AAClB,MAAK,GAAG;;AAGV,SAAS,eAAe;CACtB,MAAM,SAAS,aAAa,OAAO;CACnC,MAAM,OAAO,OAAO;AAEpB,QAAO,QAAQ,SAAS,YAAY;AAClC,MAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,kCAAkC,CACpF;AAGF,OAAK,SAAS,QAAQ;;AAGxB,QAAO"}
|
|
1
|
+
{"version":3,"file":"serve.mjs","names":[],"sources":["../src/serve.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { dirname, resolve, basename } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { createRequire } from 'node:module'\nimport { createServer, createLogger, type ViteDevServer } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport tailwindcss from '@tailwindcss/vite'\nimport { glob } from 'tinyglobby'\nimport picomatch from 'picomatch'\nimport { createHighlighter, type Highlighter } from 'shiki'\nimport { createPlaintext } from './plaintext.ts'\nimport { resolveConfig } from './config/index.ts'\nimport { runTransformers } from './transformers/index.ts'\nimport { createRenderer, type Renderer } from './render/createRenderer.ts'\nimport { serveCompatibility } from './server/compatibility.ts'\nimport { serveLint } from './server/linter.ts'\nimport { sendEmail } from './server/email.ts'\nimport type { MaizzleConfig } from './types/index.ts'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst devUIDir = resolve(__dirname, 'server/ui')\n\nconst require = createRequire(import.meta.url)\nconst frameworkNodeModules = resolve(dirname(require.resolve('vue/package.json')), '..')\nconst vuePkgDir = dirname(require.resolve('vue/package.json'))\n\nexport interface ServeOptions {\n config?: Partial<MaizzleConfig> | string\n /** Expose the server on the network (e.g. --host) */\n host?: boolean | string\n /** When true, suppresses the banner/URL output (used by the Vite plugin, which prints its own) */\n silent?: boolean\n}\n\n/**\n * Start the Maizzle dev server.\n *\n * Creates two things:\n * 1. A Vite dev server for the dev UI (sidebar + preview, with Vue + Tailwind for the UI itself)\n * 2. A Renderer instance for SSR rendering email templates\n *\n * Template rendering goes through the Renderer, not the Vite dev server.\n */\nexport async function serve(options: ServeOptions = {}) {\n const start = performance.now()\n\n let config = await resolveConfig(options.config)\n const port = config.server?.port ?? 3000\n\n // Create a renderer for SSR rendering email templates (with dts for dev)\n let renderer = await createRenderer({ dts: true, markdown: config.markdown, root: config.root, componentDirs: [config.components?.source ?? []].flat(), vite: config.vite })\n\n const server = await createServer({\n configFile: false,\n plugins: [\n // Vue and Tailwind are only for the dev UI SPA, not for email templates\n vue(),\n tailwindcss(),\n maizzleDevPlugin(config, renderer, options.config),\n ],\n resolve: {\n dedupe: ['vue'],\n alias: [\n { find: '@', replacement: devUIDir },\n { find: 'vue', replacement: resolve(vuePkgDir, 'dist/vue.runtime.esm-bundler.js') },\n { find: 'vue-router', replacement: resolve(frameworkNodeModules, 'vue-router') },\n { find: 'reka-ui', replacement: resolve(frameworkNodeModules, 'reka-ui') },\n { find: '@vueuse/core', replacement: resolve(frameworkNodeModules, '@vueuse/core') },\n { find: '@vueuse/shared', replacement: resolve(frameworkNodeModules, '@vueuse/shared') },\n { find: 'lucide-vue-next', replacement: resolve(frameworkNodeModules, 'lucide-vue-next') },\n { find: 'class-variance-authority', replacement: resolve(frameworkNodeModules, 'class-variance-authority') },\n { find: 'clsx', replacement: resolve(frameworkNodeModules, 'clsx') },\n { find: 'tailwind-merge', replacement: resolve(frameworkNodeModules, 'tailwind-merge') },\n ],\n },\n cacheDir: resolve(devUIDir, '.vite'),\n optimizeDeps: {\n noDiscovery: true,\n include: [\n 'vue',\n 'vue-router',\n 'lucide-vue-next',\n '@vueuse/core',\n '@vueuse/shared',\n 'reka-ui',\n 'class-variance-authority',\n 'clsx',\n 'tailwind-merge',\n ],\n },\n server: {\n port,\n host: options.host,\n fs: {\n allow: [process.cwd(), config.root ?? process.cwd(), devUIDir, frameworkNodeModules],\n },\n },\n customLogger: customLogger(),\n })\n\n // Store renderer ref on server for cleanup\n const originalClose = server.close.bind(server)\n server.close = async () => {\n await renderer.close()\n return originalClose()\n }\n\n await server.listen()\n\n const startupTime = Math.round(performance.now() - start)\n\n if (!options.silent) {\n printBanner(server, startupTime)\n }\n\n // Expose startup time so the plugin can print it later\n ; (server as any)._maizzleStartupTime = startupTime\n\n return server\n}\n\n/**\n * Internal Vite plugin that adds Maizzle middleware and file watching to the dev UI server.\n */\nfunction maizzleDevPlugin(\n config: MaizzleConfig,\n renderer: Renderer,\n configInput: Partial<MaizzleConfig> | string | undefined,\n) {\n return {\n name: 'maizzle:dev',\n enforce: 'pre' as const,\n\n hotUpdate: {\n order: 'pre' as const,\n handler({ file }: { file: string }) {\n // Prevent Tailwind/Vue from triggering a full reload for email template files.\n // Maizzle handles these via custom HMR events in the watcher below.\n if (isTemplateFile(file)) {\n return []\n }\n },\n },\n\n configureServer(server: ViteDevServer) {\n // File watching\n const defaultWatchPaths = [\n 'maizzle.config.js',\n 'maizzle.config.ts',\n 'tailwind.config.js',\n 'tailwind.config.ts',\n 'locales/**',\n ]\n\n const userWatchPaths = config.server?.watch ?? []\n const watchPaths = [...defaultWatchPaths, ...userWatchPaths]\n const isWatchedFile = picomatch(watchPaths)\n\n for (const watchPath of watchPaths) {\n server.watcher.add(watchPath)\n }\n\n server.watcher.on('add', async (file) => {\n if (isTemplateFile(file)) {\n await renderer.invalidateAll()\n server.ws.send({ type: 'custom', event: 'maizzle:templates-changed' })\n }\n })\n\n server.watcher.on('unlink', async (file) => {\n if (isTemplateFile(file)) {\n await renderer.invalidateAll()\n server.ws.send({ type: 'custom', event: 'maizzle:templates-changed' })\n }\n })\n\n server.watcher.on('change', async (file) => {\n if (isWatchedFile(file)) {\n config = await resolveConfig(configInput)\n\n // Recreate the renderer so config changes (e.g. markdown.shikiTheme) take effect\n await renderer.close()\n renderer = await createRenderer({ dts: true, markdown: config.markdown, root: config.root, componentDirs: [config.components?.source ?? []].flat(), vite: config.vite })\n }\n\n // Invalidate all renderer modules so component and config changes\n // are picked up on the next render (Tailwind recompiles with fresh content)\n await renderer.invalidateAll()\n\n if (\n isTemplateFile(file)\n || isWatchedFile(file)\n ) {\n server.ws.send({ type: 'custom', event: 'maizzle:template-updated', data: { file } })\n }\n })\n\n // API middleware (before Vite's middleware)\n server.middlewares.use(async (req: any, res: any, next: any) => {\n const url = req.url || '/'\n\n if (url === '/__maizzle/templates') {\n return serveTemplateList(config, res)\n }\n\n if (url.startsWith('/__maizzle/render/')) {\n return await serveRenderedTemplate(url, config, renderer, res)\n }\n\n if (url.startsWith('/__maizzle/source/')) {\n return await serveHighlightedSource(url, config, renderer, res)\n }\n\n if (url === '/__maizzle/compatibility' && req.method === 'POST') {\n return await serveCompatibility(req, res)\n }\n\n if (url.startsWith('/__maizzle/lint/')) {\n return serveLint(url, res)\n }\n\n if (url.startsWith('/__maizzle/vue-source/')) {\n return await serveVueSource(url, config, res)\n }\n\n if (url.startsWith('/__maizzle/plaintext/')) {\n return await servePlaintext(url, config, renderer, res)\n }\n\n if (url.startsWith('/__maizzle/stats/')) {\n return await serveStats(url, config, renderer, res)\n }\n\n if (url.startsWith('/__maizzle/email/') && req.method === 'POST') {\n return await serveEmailEndpoint(url, req, res, config, renderer)\n }\n\n if (url === '/__maizzle/email-config') {\n return serveEmailConfig(config, res)\n }\n\n next()\n })\n\n // Dev UI fallback (after Vite's middleware)\n return () => {\n server.middlewares.use(async (req: any, res: any, next: any) => {\n if (isNavigationRequest(req)) {\n return await serveDevUI(server, res, req.url || '/')\n }\n\n next()\n })\n }\n },\n }\n}\n\nfunction isTemplateFile(file: string): boolean {\n return (file.endsWith('.vue') || file.endsWith('.md')) && !file.includes('server/ui')\n}\n\nfunction isNavigationRequest(req: any): boolean {\n const accept = req.headers?.accept || ''\n return req.method === 'GET' && accept.includes('text/html')\n}\n\nasync function serveDevUI(server: ViteDevServer, res: any, url: string) {\n let indexHtml = readFileSync(resolve(devUIDir, 'index.html'), 'utf-8')\n\n indexHtml = indexHtml.replace('./main.ts', `/@fs/${resolve(devUIDir, 'main.ts')}`)\n indexHtml = indexHtml.replace('./favicon.svg', `/@fs/${resolve(devUIDir, 'favicon.svg')}`)\n\n const transformed = await server.transformIndexHtml(url, indexHtml)\n\n res.setHeader('Content-Type', 'text/html')\n res.end(transformed)\n}\n\nasync function serveTemplateList(config: MaizzleConfig, res: any) {\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n\n const data = templates.map(t => ({\n name: basename(t).replace(/\\.(vue|md)$/, ''),\n path: t,\n href: '/' + t.replace(/\\.(vue|md)$/, ''),\n }))\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(data))\n}\n\n/**\n * SSR render a .vue template using the Renderer (not the dev UI server).\n */\nasync function serveRenderedTemplate(url: string, config: MaizzleConfig, renderer: Renderer, res: any) {\n const templateSlug = url.replace('/__maizzle/render/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end('Template not found')\n return\n }\n\n try {\n const absolutePath = resolve(match)\n\n // Invalidate all modules so template + component changes are picked up\n await renderer.invalidateAll()\n\n const rendered = await renderer.render(absolutePath, config)\n let html = rendered.html\n\n const templateConfig = rendered.templateConfig\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n html = `${doctype}\\n${html}`\n\n res.setHeader('Content-Type', 'text/html')\n res.end(html)\n } catch (error: any) {\n res.statusCode = 500\n res.end(`<pre>${error.stack || error.message}</pre>`)\n }\n}\n\nlet highlighter: Highlighter | null = null\n\nasync function getHighlighter() {\n if (!highlighter) {\n highlighter = await createHighlighter({\n themes: ['laserwave'],\n langs: ['html', 'vue'],\n })\n }\n return highlighter\n}\n\nasync function serveHighlightedSource(url: string, config: MaizzleConfig, renderer: Renderer, res: any) {\n const templateSlug = url.replace('/__maizzle/source/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end('Template not found')\n return\n }\n\n try {\n const absolutePath = resolve(match)\n\n await renderer.invalidateAll()\n\n const rendered = await renderer.render(absolutePath, config)\n let html = rendered.html\n\n const templateConfig = rendered.templateConfig\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n\n html = `${doctype}\\n${html}`\n\n const hl = await getHighlighter()\n const highlighted = hl.codeToHtml(html, {\n lang: 'html',\n theme: 'laserwave',\n transformers: [{\n line(node, line) {\n node.properties['data-line'] = line\n },\n }],\n })\n\n res.setHeader('Content-Type', 'text/html')\n res.end(highlighted)\n } catch (error: any) {\n res.statusCode = 500\n res.end(`<pre>${error.stack || error.message}</pre>`)\n }\n}\n\nasync function serveVueSource(url: string, config: MaizzleConfig, res: any) {\n const templateSlug = url.replace('/__maizzle/vue-source/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end('Template not found')\n return\n }\n\n try {\n const source = readFileSync(resolve(match), 'utf-8')\n const lang = match.endsWith('.md') ? 'html' : 'vue'\n\n const hl = await getHighlighter()\n const highlighted = hl.codeToHtml(source, {\n lang,\n theme: 'laserwave',\n transformers: [{\n line(node, line) {\n node.properties['data-line'] = line\n },\n }],\n })\n\n res.setHeader('Content-Type', 'text/html')\n res.end(highlighted)\n } catch (error: any) {\n res.statusCode = 500\n res.end(`<pre>${error.stack || error.message}</pre>`)\n }\n}\n\nasync function servePlaintext(url: string, config: MaizzleConfig, renderer: Renderer, res: any) {\n const templateSlug = url.replace('/__maizzle/plaintext/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end('Template not found')\n return\n }\n\n try {\n const absolutePath = resolve(match)\n await renderer.invalidateAll()\n\n const rendered = await renderer.render(absolutePath, config)\n let html = rendered.html\n const templateConfig = rendered.templateConfig\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n\n const plaintext = createPlaintext(html)\n\n res.setHeader('Content-Type', 'text/plain')\n res.end(plaintext)\n } catch (error: any) {\n res.statusCode = 500\n res.end(error.message)\n }\n}\n\nfunction humanFileSize(bytes: number, si = false, dp = 2) {\n const threshold = si ? 1000 : 1024\n\n if (Math.abs(bytes) < threshold) {\n return bytes + ' B'\n }\n\n const units = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']\n let u = -1\n const r = 10 ** dp\n\n do {\n bytes /= threshold\n ++u\n } while (Math.round(Math.abs(bytes) * r) / r >= threshold && u < units.length - 1)\n\n return bytes.toFixed(dp) + ' ' + units[u]\n}\n\nasync function serveStats(url: string, config: MaizzleConfig, renderer: Renderer, res: any) {\n const templateSlug = url.replace('/__maizzle/stats/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end(JSON.stringify({ error: 'Template not found' }))\n return\n }\n\n try {\n const absolutePath = resolve(match)\n await renderer.invalidateAll()\n\n const rendered = await renderer.render(absolutePath, config)\n let html = rendered.html\n const templateConfig = rendered.templateConfig\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n\n const sizeBytes = Buffer.byteLength(html, 'utf-8')\n\n // Count images: <img> tags and CSS background images\n const imgTags = (html.match(/<img\\b[^>]*>/gi) || []).length\n const bgImages = (html.match(/url\\s*\\([^)]+\\)/gi) || []).length\n const totalImages = imgTags + bgImages\n\n // Count links\n const links = (html.match(/<a\\b[^>]*href\\s*=/gi) || []).length\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({\n size: {\n bytes: sizeBytes,\n formatted: humanFileSize(sizeBytes),\n },\n images: totalImages,\n links,\n }))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ error: error.message }))\n }\n}\n\nasync function serveEmailEndpoint(url: string, req: any, res: any, config: MaizzleConfig, renderer: Renderer) {\n const templateSlug = url.replace('/__maizzle/email/', '').replace(/\\?.*$/, '')\n\n const contentPatterns = config.content ?? ['emails/**/*.vue']\n const templates = await glob(contentPatterns)\n const match = templates.find(t => t.replace(/\\.(vue|md)$/, '') === templateSlug)\n\n if (!match) {\n res.statusCode = 404\n res.end(JSON.stringify({ success: false, message: 'Template not found' }))\n return\n }\n\n let body = ''\n for await (const chunk of req) body += chunk\n\n let payload: { to: string[]; subject: string }\n\n try {\n payload = JSON.parse(body)\n } catch {\n res.statusCode = 400\n res.end(JSON.stringify({ success: false, message: 'Invalid JSON' }))\n return\n }\n\n if (!payload.to?.length) {\n res.statusCode = 400\n res.end(JSON.stringify({ success: false, message: 'Missing recipients' }))\n return\n }\n\n try {\n const absolutePath = resolve(match)\n await renderer.invalidateAll()\n\n const rendered = await renderer.render(absolutePath, config)\n let html = rendered.html\n const templateConfig = rendered.templateConfig\n const doctype = rendered.doctype ?? templateConfig.doctype ?? '<!DOCTYPE html>'\n html = await runTransformers(html, templateConfig, absolutePath, doctype)\n html = `${doctype}\\n${html}`\n\n const text = createPlaintext(html)\n\n const result = await sendEmail(\n { to: payload.to, subject: payload.subject, html, text },\n config,\n templateConfig,\n )\n\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(result))\n } catch (error: any) {\n res.statusCode = 500\n res.end(JSON.stringify({ success: false, message: error.message }))\n }\n}\n\nfunction serveEmailConfig(config: MaizzleConfig, res: any) {\n const emailConfig = config.server?.email\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({\n to: emailConfig?.to ? (Array.isArray(emailConfig.to) ? emailConfig.to : [emailConfig.to]) : [],\n from: emailConfig?.from ?? '',\n subject: emailConfig?.subject ?? '',\n hasTransport: !!emailConfig?.transport,\n }))\n}\n\nexport function printBanner(server: ViteDevServer, startupTime?: number) {\n const info = server.config.logger.info\n const time = startupTime ?? (server as any)._maizzleStartupTime\n\n info('')\n info(` \\x1b[32m\\x1b[1mMAIZZLE\\x1b[0m\\x1b[32m v6.0.0\\x1b[0m \\x1b[2mready in\\x1b[0m \\x1b[1m${time}\\x1b[0m ms`)\n info('')\n server.printUrls()\n info('')\n}\n\nfunction customLogger() {\n const logger = createLogger('info')\n const warn = logger.warn\n\n logger.warn = (message, options) => {\n if (typeof message === 'string' && message.includes('<tr> cannot be child of <table>')) {\n return\n }\n\n warn(message, options)\n }\n\n return logger\n}\n"],"mappings":"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"}
|
|
@@ -19,16 +19,17 @@ import { MaizzleConfig } from "../types/config.mjs";
|
|
|
19
19
|
* 4. CSS inliner
|
|
20
20
|
* 5. Remove attributes
|
|
21
21
|
* 6. Shorthand CSS
|
|
22
|
-
* 7.
|
|
23
|
-
* 8.
|
|
24
|
-
* 9.
|
|
25
|
-
* 10. URL
|
|
26
|
-
* 11.
|
|
27
|
-
* 12.
|
|
22
|
+
* 7. Six-digit HEX
|
|
23
|
+
* 8. Add attributes
|
|
24
|
+
* 9. Filters
|
|
25
|
+
* 10. Base URL
|
|
26
|
+
* 11. URL query
|
|
27
|
+
* 12. Purge CSS (serializes/parses internally around email-comb)
|
|
28
|
+
* 13. Entities
|
|
28
29
|
* + Vue-generated comments stripped here (on serialized string)
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
30
|
+
* 14. Replace strings
|
|
31
|
+
* 15. Prettify
|
|
32
|
+
* 16. Minify
|
|
32
33
|
*/
|
|
33
34
|
declare function runTransformers(html: string, config: MaizzleConfig, filePath?: string, doctype?: string): Promise<string>;
|
|
34
35
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/transformers/index.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/transformers/index.ts"],"mappings":";;;;;AAkDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAsB,eAAA,CACpB,IAAA,UACA,MAAA,EAAQ,aAAA,EACR,QAAA,WACA,OAAA,YACC,OAAA"}
|
|
@@ -8,6 +8,7 @@ import { attributeToStyle } from "./attributeToStyle.mjs";
|
|
|
8
8
|
import { inlineCSS } from "./inlineCSS.mjs";
|
|
9
9
|
import { removeAttributes } from "./removeAttributes.mjs";
|
|
10
10
|
import { shorthandCSS } from "./shorthandCSS.mjs";
|
|
11
|
+
import { sixHex } from "./sixHex.mjs";
|
|
11
12
|
import { addAttributes } from "./addAttributes.mjs";
|
|
12
13
|
import { filters } from "./filters/index.mjs";
|
|
13
14
|
import { base } from "./base.mjs";
|
|
@@ -37,16 +38,17 @@ import { minify } from "./minify.mjs";
|
|
|
37
38
|
* 4. CSS inliner
|
|
38
39
|
* 5. Remove attributes
|
|
39
40
|
* 6. Shorthand CSS
|
|
40
|
-
* 7.
|
|
41
|
-
* 8.
|
|
42
|
-
* 9.
|
|
43
|
-
* 10. URL
|
|
44
|
-
* 11.
|
|
45
|
-
* 12.
|
|
41
|
+
* 7. Six-digit HEX
|
|
42
|
+
* 8. Add attributes
|
|
43
|
+
* 9. Filters
|
|
44
|
+
* 10. Base URL
|
|
45
|
+
* 11. URL query
|
|
46
|
+
* 12. Purge CSS (serializes/parses internally around email-comb)
|
|
47
|
+
* 13. Entities
|
|
46
48
|
* + Vue-generated comments stripped here (on serialized string)
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
49
|
+
* 14. Replace strings
|
|
50
|
+
* 15. Prettify
|
|
51
|
+
* 16. Minify
|
|
50
52
|
*/
|
|
51
53
|
async function runTransformers(html, config, filePath, doctype) {
|
|
52
54
|
let dom = parse(html);
|
|
@@ -57,6 +59,7 @@ async function runTransformers(html, config, filePath, doctype) {
|
|
|
57
59
|
dom = inlineCSS(dom, config.css);
|
|
58
60
|
dom = removeAttributes(dom, config.html?.attributes);
|
|
59
61
|
dom = shorthandCSS(dom, config.css);
|
|
62
|
+
dom = sixHex(dom, config.css);
|
|
60
63
|
dom = addAttributes(dom, config.html?.attributes);
|
|
61
64
|
dom = filters(dom, config.filters);
|
|
62
65
|
dom = base(dom, config.url);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/transformers/index.ts"],"sourcesContent":["import { parse, serialize } from '../utils/ast/index.ts'\nimport { inlineLink } from './inlineLink.ts'\nimport { tailwindcss } from './tailwindcss.ts'\nimport { safeClassNames } from './safeClassNames.ts'\nimport { attributeToStyle } from './attributeToStyle.ts'\nimport { inlineCSS } from './inlineCSS.ts'\nimport { removeAttributes } from './removeAttributes.ts'\nimport { shorthandCSS } from './shorthandCSS.ts'\nimport { addAttributes } from './addAttributes.ts'\nimport { filters } from './filters/index.ts'\nimport { base } from './base.ts'\nimport { entities } from './entities.ts'\nimport { urlQuery } from './urlQuery.ts'\nimport { purgeCSS } from './purgeCSS.ts'\nimport { replaceStrings } from './replaceStrings.ts'\nimport { format } from './format.ts'\nimport { minify } from './minify.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\n\n/**\n * Run all Maizzle transformers on the rendered HTML.\n *\n * The HTML is parsed into a DOM once at the start and passed through all\n * DOM-based transformers as a shared `ChildNode[]`. After all DOM transformers\n * complete, the DOM is serialized back to a string exactly once.\n *\n * String-only transformers (those that rely on external tools that require a\n * raw HTML string) then run on the serialized output.\n *\n * Transformers run in a specific order:\n * 0. Inline link stylesheets — replace `<link rel=\"stylesheet\">` with `<style>` tags\n * 1. Tailwind CSS — compile CSS, lower syntax, optimize (cleanup + merge media queries)\n * 2. Safe class names\n * 3. Attribute to style\n * 4. CSS inliner\n * 5. Remove attributes\n * 6. Shorthand CSS\n * 7. Add attributes\n *
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/transformers/index.ts"],"sourcesContent":["import { parse, serialize } from '../utils/ast/index.ts'\nimport { inlineLink } from './inlineLink.ts'\nimport { tailwindcss } from './tailwindcss.ts'\nimport { safeClassNames } from './safeClassNames.ts'\nimport { attributeToStyle } from './attributeToStyle.ts'\nimport { inlineCSS } from './inlineCSS.ts'\nimport { removeAttributes } from './removeAttributes.ts'\nimport { shorthandCSS } from './shorthandCSS.ts'\nimport { sixHex } from './sixHex.ts'\nimport { addAttributes } from './addAttributes.ts'\nimport { filters } from './filters/index.ts'\nimport { base } from './base.ts'\nimport { entities } from './entities.ts'\nimport { urlQuery } from './urlQuery.ts'\nimport { purgeCSS } from './purgeCSS.ts'\nimport { replaceStrings } from './replaceStrings.ts'\nimport { format } from './format.ts'\nimport { minify } from './minify.ts'\nimport type { MaizzleConfig } from '../types/config.ts'\n\n/**\n * Run all Maizzle transformers on the rendered HTML.\n *\n * The HTML is parsed into a DOM once at the start and passed through all\n * DOM-based transformers as a shared `ChildNode[]`. After all DOM transformers\n * complete, the DOM is serialized back to a string exactly once.\n *\n * String-only transformers (those that rely on external tools that require a\n * raw HTML string) then run on the serialized output.\n *\n * Transformers run in a specific order:\n * 0. Inline link stylesheets — replace `<link rel=\"stylesheet\">` with `<style>` tags\n * 1. Tailwind CSS — compile CSS, lower syntax, optimize (cleanup + merge media queries)\n * 2. Safe class names\n * 3. Attribute to style\n * 4. CSS inliner\n * 5. Remove attributes\n * 6. Shorthand CSS\n * 7. Six-digit HEX\n * 8. Add attributes\n * 9. Filters\n * 10. Base URL\n * 11. URL query\n * 12. Purge CSS (serializes/parses internally around email-comb)\n * 13. Entities\n * + Vue-generated comments stripped here (on serialized string)\n * 14. Replace strings\n * 15. Prettify\n * 16. Minify\n */\nexport async function runTransformers(\n html: string,\n config: MaizzleConfig,\n filePath?: string,\n doctype?: string,\n): Promise<string> {\n // Parse once — all DOM transformers share this array\n let dom = parse(html)\n\n // 0. Inline <link> stylesheets\n dom = await inlineLink(dom, filePath)\n\n // 1. Tailwind CSS — always runs first\n dom = await tailwindcss(dom, config, filePath)\n\n // 2. Safe class names\n dom = safeClassNames(dom, config.css)\n\n // 3. Attribute to style\n dom = attributeToStyle(dom, config.css)\n\n // 4. CSS inliner (serializes/parses internally around juice)\n dom = inlineCSS(dom, config.css)\n\n // 5. Remove attributes\n dom = removeAttributes(dom, config.html?.attributes)\n\n // 6. Shorthand CSS\n dom = shorthandCSS(dom, config.css)\n\n // 7. Six-digit HEX\n dom = sixHex(dom, config.css)\n\n // 8. Add attributes\n dom = addAttributes(dom, config.html?.attributes)\n\n // 9. Filters\n dom = filters(dom, config.filters)\n\n // 10. Base URL (serializes/parses internally for VML/MSO regex passes)\n dom = base(dom, config.url)\n\n // 11. URL query\n dom = urlQuery(dom, config.url)\n\n // 12. Purge CSS (serializes/parses internally around email-comb)\n dom = purgeCSS(dom, config.css)\n\n // 13. Entities\n dom = entities(dom, config.html?.decodeEntities)\n\n // Serialize once — remaining transformers operate on the HTML string\n const isXhtml = doctype ? /xhtml/i.test(doctype) : false\n let result = serialize(dom, { selfClosingTags: isXhtml })\n\n // Remove Vue-generated comments after serializing\n result = result\n .replaceAll('<!--[-->', '')\n .replaceAll('<!--]-->', '')\n .replaceAll('<!--teleport start anchor-->', '')\n .replaceAll('<!--teleport anchor-->', '')\n .replaceAll('<!--teleport start-->', '')\n .replaceAll('<!--teleport end-->', '')\n\n // 14. Replace strings\n result = replaceStrings(result, config)\n\n // 15. Format\n result = await format(result, config)\n\n // 16. Minify\n result = minify(result, config)\n\n // Strip self-closing slashes for HTML5 doctypes\n if (!isXhtml) {\n result = result.replace(/ \\/>/g, '>')\n }\n\n return result\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,eAAsB,gBACpB,MACA,QACA,UACA,SACiB;CAEjB,IAAI,MAAM,MAAM,KAAK;AAGrB,OAAM,MAAM,WAAW,KAAK,SAAS;AAGrC,OAAM,MAAM,YAAY,KAAK,QAAQ,SAAS;AAG9C,OAAM,eAAe,KAAK,OAAO,IAAI;AAGrC,OAAM,iBAAiB,KAAK,OAAO,IAAI;AAGvC,OAAM,UAAU,KAAK,OAAO,IAAI;AAGhC,OAAM,iBAAiB,KAAK,OAAO,MAAM,WAAW;AAGpD,OAAM,aAAa,KAAK,OAAO,IAAI;AAGnC,OAAM,OAAO,KAAK,OAAO,IAAI;AAG7B,OAAM,cAAc,KAAK,OAAO,MAAM,WAAW;AAGjD,OAAM,QAAQ,KAAK,OAAO,QAAQ;AAGlC,OAAM,KAAK,KAAK,OAAO,IAAI;AAG3B,OAAM,SAAS,KAAK,OAAO,IAAI;AAG/B,OAAM,SAAS,KAAK,OAAO,IAAI;AAG/B,OAAM,SAAS,KAAK,OAAO,MAAM,eAAe;CAGhD,MAAM,UAAU,UAAU,SAAS,KAAK,QAAQ,GAAG;CACnD,IAAI,SAAS,UAAU,KAAK,EAAE,iBAAiB,SAAS,CAAC;AAGzD,UAAS,OACN,WAAW,YAAY,GAAG,CAC1B,WAAW,YAAY,GAAG,CAC1B,WAAW,gCAAgC,GAAG,CAC9C,WAAW,0BAA0B,GAAG,CACxC,WAAW,yBAAyB,GAAG,CACvC,WAAW,uBAAuB,GAAG;AAGxC,UAAS,eAAe,QAAQ,OAAO;AAGvC,UAAS,MAAM,OAAO,QAAQ,OAAO;AAGrC,UAAS,OAAO,QAAQ,OAAO;AAG/B,KAAI,CAAC,QACH,UAAS,OAAO,QAAQ,SAAS,IAAI;AAGvC,QAAO"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CssConfig } from "../types/config.mjs";
|
|
2
|
+
import { ChildNode } from "domhandler";
|
|
3
|
+
|
|
4
|
+
//#region src/transformers/sixHex.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Six-digit HEX transformer.
|
|
7
|
+
*
|
|
8
|
+
* Converts 3-digit HEX color codes to 6-digit in `bgcolor` and `color`
|
|
9
|
+
* attributes, for better email client compatibility.
|
|
10
|
+
*
|
|
11
|
+
* Enabled by default via `css.sixHex`.
|
|
12
|
+
*/
|
|
13
|
+
declare function sixHex(dom: ChildNode[], config?: CssConfig): ChildNode[];
|
|
14
|
+
//#endregion
|
|
15
|
+
export { sixHex };
|
|
16
|
+
//# sourceMappingURL=sixHex.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sixHex.d.mts","names":[],"sources":["../../src/transformers/sixHex.ts"],"mappings":";;;;;;AAeA;;;;;;iBAAgB,MAAA,CAAO,GAAA,EAAK,SAAA,IAAa,MAAA,GAAQ,SAAA,GAAiB,SAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { walk } from "../utils/ast/walker.mjs";
|
|
2
|
+
import "../utils/ast/index.mjs";
|
|
3
|
+
import { conv } from "color-shorthand-hex-to-six-digit";
|
|
4
|
+
|
|
5
|
+
//#region src/transformers/sixHex.ts
|
|
6
|
+
const targets = new Set(["bgcolor", "color"]);
|
|
7
|
+
/**
|
|
8
|
+
* Six-digit HEX transformer.
|
|
9
|
+
*
|
|
10
|
+
* Converts 3-digit HEX color codes to 6-digit in `bgcolor` and `color`
|
|
11
|
+
* attributes, for better email client compatibility.
|
|
12
|
+
*
|
|
13
|
+
* Enabled by default via `css.sixHex`.
|
|
14
|
+
*/
|
|
15
|
+
function sixHex(dom, config = {}) {
|
|
16
|
+
if (config.sixHex === false) return dom;
|
|
17
|
+
walk(dom, (node) => {
|
|
18
|
+
const el = node;
|
|
19
|
+
if (!el.attribs) return;
|
|
20
|
+
for (const attr of targets) {
|
|
21
|
+
const value = el.attribs[attr];
|
|
22
|
+
if (value) el.attribs[attr] = conv(value);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return dom;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { sixHex };
|
|
30
|
+
//# sourceMappingURL=sixHex.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sixHex.mjs","names":[],"sources":["../../src/transformers/sixHex.ts"],"sourcesContent":["import { conv } from 'color-shorthand-hex-to-six-digit'\nimport type { ChildNode, Element } from 'domhandler'\nimport { walk } from '../utils/ast/index.ts'\nimport type { CssConfig } from '../types/config.ts'\n\nconst targets = new Set(['bgcolor', 'color'])\n\n/**\n * Six-digit HEX transformer.\n *\n * Converts 3-digit HEX color codes to 6-digit in `bgcolor` and `color`\n * attributes, for better email client compatibility.\n *\n * Enabled by default via `css.sixHex`.\n */\nexport function sixHex(dom: ChildNode[], config: CssConfig = {}): ChildNode[] {\n if (config.sixHex === false) {\n return dom\n }\n\n walk(dom, (node) => {\n const el = node as Element\n\n if (!el.attribs) {\n return\n }\n\n for (const attr of targets) {\n const value = el.attribs[attr]\n\n if (value) {\n el.attribs[attr] = conv(value)\n }\n }\n })\n\n return dom\n}\n"],"mappings":";;;;;AAKA,MAAM,UAAU,IAAI,IAAI,CAAC,WAAW,QAAQ,CAAC;;;;;;;;;AAU7C,SAAgB,OAAO,KAAkB,SAAoB,EAAE,EAAe;AAC5E,KAAI,OAAO,WAAW,MACpB,QAAO;AAGT,MAAK,MAAM,SAAS;EAClB,MAAM,KAAK;AAEX,MAAI,CAAC,GAAG,QACN;AAGF,OAAK,MAAM,QAAQ,SAAS;GAC1B,MAAM,QAAQ,GAAG,QAAQ;AAEzB,OAAI,MACF,IAAG,QAAQ,QAAQ,KAAK,MAAM;;GAGlC;AAEF,QAAO"}
|