-
Notifications
You must be signed in to change notification settings - Fork 65
Expand file tree
/
Copy pathindex.ts
More file actions
88 lines (75 loc) · 3.32 KB
/
Copy pathindex.ts
File metadata and controls
88 lines (75 loc) · 3.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import { resolve, extname } from 'pathe'
import { resolveConfigObject } from '../config/index.ts'
import { runTransformers } from '../transformers/index.ts'
import { createPlaintext } from '../plaintext.ts'
import { stripForHtml, stripForPlaintext } from '../utils/output-markers.ts'
import defu from 'defu'
import type { Component } from 'vue'
import type { MaizzleConfig } from '../types/index.ts'
import { createRenderer } from './createRenderer.ts'
import { getActiveRenderer } from './active.ts'
import { normalizeComponentSources } from '../utils/componentSources.ts'
export type { Renderer, RenderedTemplate, CreateRendererOptions } from './createRenderer.ts'
export { createRenderer } from './createRenderer.ts'
export interface RenderResult {
html: string
config: MaizzleConfig
plaintext?: string
}
/**
* Render a Vue SFC email template to a fully-transformed HTML string.
* Accepts a file path or a raw SFC source string.
*/
export async function render(
template: string | Component,
config?: Partial<MaizzleConfig>,
): Promise<RenderResult> {
if (template == null) {
throw new Error(
`render() received ${template}. If you used \`import X from './x.vue'\`, Node cannot load .vue files natively — pass the path string instead: render('./x.vue').`,
)
}
if (typeof template !== 'string' && typeof template !== 'object' && typeof template !== 'function') {
throw new TypeError(
`render() expected a file path or SFC source string, got ${typeof template}.`,
)
}
const resolvedConfig = resolveConfigObject(config)
const { props, ...templateConfig } = resolvedConfig
/**
* Reuse a renderer started by the Vite plugin when one is active.
* Spinning up a fresh Vite SSR server inside a host Vite dev process
* (e.g. TanStack Start) collides on env wiring and throws
* "outsideEmitter undefined".
*/
const active = getActiveRenderer()
const renderer = active ?? await createRenderer({
markdown: resolvedConfig.markdown,
root: resolvedConfig.root,
componentDirs: normalizeComponentSources(resolvedConfig.components?.source, process.cwd()),
vite: resolvedConfig.vite,
})
try {
const isFile = typeof template === 'string'
&& ['.vue', '.md'].includes(extname(template))
&& !template.includes('\n')
const rendered = await renderer.render(isFile ? resolve(template) : template, templateConfig, { props })
let html = rendered.html
const doctype = rendered.doctype ?? rendered.templateConfig.doctype ?? '<!DOCTYPE html>'
if (rendered.templateConfig.useTransformers !== false) {
html = await runTransformers(html, rendered.templateConfig, isFile ? resolve(template) : undefined, doctype, rendered.tailwindBlocks)
}
if (doctype) html = `${doctype}\n${html}`
const globalPlaintext = rendered.templateConfig.plaintext
const sfcPlaintext = rendered.plaintext
let plaintextResult: string | undefined
if (globalPlaintext || sfcPlaintext) {
const globalCfg = typeof globalPlaintext === 'object' ? globalPlaintext : {}
const stripOptions = defu(sfcPlaintext?.options, globalCfg.options)
plaintextResult = createPlaintext(stripForPlaintext(html), stripOptions)
}
return { html: stripForHtml(html), config: rendered.templateConfig, plaintext: plaintextResult }
} finally {
if (!active) await renderer.close()
}
}