@lupinum/nuxt-email 1.0.0-beta.2 → 1.0.0-beta.4
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/CHANGELOG.md +35 -1
- package/README.md +31 -3
- package/dist/agent/AGENTS.md +47 -0
- package/dist/agent/manifest.json +208 -0
- package/dist/agent/pages/docs/components/code.md +68 -0
- package/dist/agent/pages/docs/components/content.md +111 -0
- package/dist/agent/pages/docs/components/layout.md +89 -0
- package/dist/agent/pages/docs/components/markdown.md +49 -0
- package/dist/agent/pages/docs/components/structure.md +122 -0
- package/dist/agent/pages/docs/components/tailwind.md +41 -0
- package/dist/agent/pages/docs/components.md +56 -0
- package/dist/agent/pages/docs/getting-started/choose-a-tool.md +38 -0
- package/dist/agent/pages/docs/getting-started/first-email.md +99 -0
- package/dist/agent/pages/docs/getting-started/installation.md +97 -0
- package/dist/agent/pages/docs/getting-started/render-from-nitro.md +59 -0
- package/dist/agent/pages/docs/getting-started.md +62 -0
- package/dist/agent/pages/docs/guides/client-compatibility.md +65 -0
- package/dist/agent/pages/docs/guides/plain-text.md +42 -0
- package/dist/agent/pages/docs/guides/preview-workflow.md +83 -0
- package/dist/agent/pages/docs/guides/sending-email.md +132 -0
- package/dist/agent/pages/docs/guides/standalone-rendering.md +118 -0
- package/dist/agent/pages/docs/guides/subject-lines.md +95 -0
- package/dist/agent/pages/docs/guides/tailwind.md +128 -0
- package/dist/agent/pages/docs/guides/testing-your-emails.md +118 -0
- package/dist/agent/pages/docs/guides.md +32 -0
- package/dist/agent/pages/docs/reference/conformance.md +55 -0
- package/dist/agent/pages/docs/reference/define-email.md +69 -0
- package/dist/agent/pages/docs/reference/errors.md +72 -0
- package/dist/agent/pages/docs/reference/migration.md +144 -0
- package/dist/agent/pages/docs/reference/module.md +90 -0
- package/dist/agent/pages/docs/reference/render-email.md +107 -0
- package/dist/agent/pages/docs/reference.md +30 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +3 -3
- package/package.json +15 -9
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "renderEmail"
|
|
3
|
+
description: "The one public render API — its signature, deterministic result shape, plain-text contract, and security boundary."
|
|
4
|
+
url: "https://nuxt-email.lupinum.com/docs/reference/render-email"
|
|
5
|
+
route: "/docs/reference/render-email"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# renderEmail
|
|
13
|
+
|
|
14
|
+
> The one public render API — its signature, deterministic result shape, plain-text contract, and security boundary.
|
|
15
|
+
|
|
16
|
+
`renderEmail(name, props)` is the one public application rendering path. Nuxt generates template names and prop types from the Vue single-file components (`.vue` files) in `app/emails/`. It resolves the selected component through the same server-only registry used by the development preview.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
const result = await renderEmail('welcome', {
|
|
20
|
+
firstName: 'Ada',
|
|
21
|
+
activationUrl: 'https://example.com/activate',
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
It is a **Nitro auto-import** — available in server handlers and other Nitro server code, not in Vue components, client plugins, or production client bundles. Do not import it from `@lupinum/nuxt-email` or `#imports`.
|
|
26
|
+
|
|
27
|
+
## Result shape
|
|
28
|
+
|
|
29
|
+
The promise resolves to the canonical `RenderedEmail` type, available from the package root as a type-only import:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import type { RenderedEmail } from '@lupinum/nuxt-email'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `html` — the rendered document. Begins with the XHTML 1.0 Transitional doctype.
|
|
36
|
+
- `text` — authored metadata when declared, otherwise the derived plain-text fallback (see below).
|
|
37
|
+
- `subject` — present only when the template declares one via [`defineEmail`](/raw/docs/guides/subject-lines.md); otherwise absent.
|
|
38
|
+
|
|
39
|
+
There is no preview metadata, provider result, diagnostics object, timing, or size field. Sending is not part of this function — your application supplies recipients, sender identity, subject, credentials, and provider-specific options directly to its chosen provider SDK.
|
|
40
|
+
|
|
41
|
+
## HTML contract
|
|
42
|
+
|
|
43
|
+
- A template must render exactly one `<html>` root containing exactly one `<body>`. `EHtml` requires an explicit non-empty language and defaults only `dir="ltr"`; the renderer never repairs or auto-wraps fragments, text roots, body-only templates, or multiple roots.
|
|
44
|
+
- `EHead` is optional but supplies the recommended UTF-8 and Apple reformatting meta tags.
|
|
45
|
+
- Vue SSR interpolation and attribute values are escaped.
|
|
46
|
+
- A fresh isolated Vue SSR application is created for every call. Email templates receive E-components through server-only auto-imports.
|
|
47
|
+
- Framework code never fetches images, stylesheets, fonts, or other remote assets during rendering.
|
|
48
|
+
|
|
49
|
+
**Determinism:** two calls with the same template code and props return byte-identical `html` and `text`. Templates remain responsible for avoiding clocks, random values, mutable external state, and nondeterministic data.
|
|
50
|
+
|
|
51
|
+
## Props and generated types
|
|
52
|
+
|
|
53
|
+
Template names are relative `.vue` paths under `app/emails/`, without the extension and with `/` separators — `app/emails/account/reset-password.vue` becomes `account/reset-password`. Files under `app/emails/components/` are excluded.
|
|
54
|
+
|
|
55
|
+
Props declared with ordinary `defineProps()` or Vue's `props` option drive both the generated call-site type and runtime validation. TypeScript rejects unknown template names, missing required props, wrong values, and extra props. Untyped runtime calls reject unknown or missing props deterministically before SSR.
|
|
56
|
+
|
|
57
|
+
There is no public registry API — the generated registry is an internal server artifact and the sole source for rendering, generated types, and preview listings.
|
|
58
|
+
|
|
59
|
+
## Select a template dynamically
|
|
60
|
+
|
|
61
|
+
Keep each template name paired with its props in a literal dispatch map. Validate external input before choosing an entry. Do not cast an arbitrary string to a template name; that removes the generated type protection.
|
|
62
|
+
|
|
63
|
+
```ts [server/utils/render-transactional-email.ts]
|
|
64
|
+
interface WelcomeInput {
|
|
65
|
+
firstName: string
|
|
66
|
+
activationUrl: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface PasswordResetInput {
|
|
70
|
+
code: string
|
|
71
|
+
expiresInMinutes: number
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const emailDispatch = {
|
|
75
|
+
welcome: (input: WelcomeInput) => renderEmail('welcome', input),
|
|
76
|
+
passwordReset: (input: PasswordResetInput) => renderEmail('account/reset-password', input),
|
|
77
|
+
} as const
|
|
78
|
+
|
|
79
|
+
// Select an entry only after validating the event and its payload.
|
|
80
|
+
await emailDispatch.welcome({
|
|
81
|
+
firstName: 'Ada',
|
|
82
|
+
activationUrl: 'https://example.com/activate',
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Each map entry contains a literal registry name. TypeScript checks its input against that template's generated props. A wrong name or prop shape fails where the entry is declared.
|
|
87
|
+
|
|
88
|
+
## Plain-text contract
|
|
89
|
+
|
|
90
|
+
Unless the template declares authored `text` with `defineEmail`, Nuxt Email derives `text` from the final HTML. It uses a fixed `html-to-text` version with no line wrapping.
|
|
91
|
+
|
|
92
|
+
The conversion excludes head content, images, script and style content, and elements marked `data-skip-in-text="true"`. This removes `EPreview` from the fallback. Links retain destinations when their visible text differs. See the [plain-text guide](/raw/docs/guides/plain-text.md) for details and the `data-text-format="dataTable"` opt-in.
|
|
93
|
+
|
|
94
|
+
## Security boundary
|
|
95
|
+
|
|
96
|
+
Nuxt Email renders trusted application templates with untrusted values supplied only through normal escaped Vue bindings. It is **not** an HTML sanitizer.
|
|
97
|
+
|
|
98
|
+
- All E-prefixed primitives reject `innerHTML`, `textContent`, and attributes beginning with `on`.
|
|
99
|
+
- There is no raw-HTML component. Do not use `v-html` or native raw HTML with untrusted content inside a template.
|
|
100
|
+
- `href` and `src` values are escaped but URL schemes are **not** validated. Validate application-controlled URLs before rendering.
|
|
101
|
+
- When consumed through the canonical generated API, template modules and the renderer are server-only and excluded from the client build. Do not import email templates into client code.
|
|
102
|
+
- Development fixture modules, preview UI, and preview endpoints are absent from production builds. Do not import fixture modules into production code.
|
|
103
|
+
- No production HTTP render route and no send route are generated. A route exists only when you create one.
|
|
104
|
+
|
|
105
|
+
The production server intentionally contains templates referenced by its registry. The guarantee is client exclusion, not removal from the server that renders them.
|
|
106
|
+
|
|
107
|
+
For render and lookup failures, see [error types](/raw/docs/reference/errors.md).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Reference"
|
|
3
|
+
description: "The precise contracts — renderEmail, module behavior, error types, conformance, and migration."
|
|
4
|
+
url: "https://nuxt-email.lupinum.com/docs/reference"
|
|
5
|
+
route: "/docs/reference"
|
|
6
|
+
locale: "en"
|
|
7
|
+
section: "Documentation"
|
|
8
|
+
collection: "docs"
|
|
9
|
+
source: "ginko-content"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Reference
|
|
13
|
+
|
|
14
|
+
> The precise contracts — renderEmail, module behavior, error types, conformance, and migration.
|
|
15
|
+
|
|
16
|
+
The reference section is the precise contract for Nuxt Email's public surface. Where a guide explains a task, the reference states exactly what is guaranteed.
|
|
17
|
+
|
|
18
|
+
<cards cols="2">
|
|
19
|
+
<card title="renderEmail" description="The render API, result shape, plain-text contract, and security boundary." icon="lucide:function-square" to="/docs/reference/render-email" />
|
|
20
|
+
|
|
21
|
+
<card title="defineEmail" description="Template subject and authored plain-text metadata." icon="lucide:tags" to="/docs/reference/define-email" />
|
|
22
|
+
|
|
23
|
+
<card title="Module behavior" description="Discovery, auto-imports, and opt-in syntax highlighting." icon="lucide:package" to="/docs/reference/module" />
|
|
24
|
+
|
|
25
|
+
<card title="Error types" description="Every typed error and the stable information it carries." icon="lucide:triangle-alert" to="/docs/reference/errors" />
|
|
26
|
+
|
|
27
|
+
<card title="Conformance and divergences" description="How compatibility is measured and where output intentionally differs." icon="lucide:git-compare" to="/docs/reference/conformance" />
|
|
28
|
+
|
|
29
|
+
<card title="Migration from React Email" description="Translate JSX templates and rendering calls to Nuxt Email." icon="lucide:arrow-left-right" to="/docs/reference/migration" />
|
|
30
|
+
</cards>
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -191,9 +191,9 @@ const module$1 = defineNuxtModule({
|
|
|
191
191
|
alias["@lupinum/nuxt-email/errors"] = errorsPublicPath;
|
|
192
192
|
const externals = nitro.externals ??= {};
|
|
193
193
|
const inline = externals.inline ??= [];
|
|
194
|
-
for (const
|
|
195
|
-
if (!inline.includes(
|
|
196
|
-
inline.push(
|
|
194
|
+
for (const runtimePath of [defineEmailPublicPath, errorsPublicPath, testingRendererPath]) {
|
|
195
|
+
if (!inline.includes(runtimePath)) {
|
|
196
|
+
inline.push(runtimePath);
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
const typescript = nitro.typescript ??= {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lupinum/nuxt-email",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
4
4
|
"description": "Typed transactional email for Nuxt with email-safe Tailwind v4",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"email",
|
|
@@ -50,7 +50,8 @@
|
|
|
50
50
|
"./errors": {
|
|
51
51
|
"types": "./dist/runtime/errors.d.ts",
|
|
52
52
|
"import": "./dist/runtime/errors.js"
|
|
53
|
-
}
|
|
53
|
+
},
|
|
54
|
+
"./agent-docs": "./dist/agent/AGENTS.md"
|
|
54
55
|
},
|
|
55
56
|
"main": "./dist/module.mjs",
|
|
56
57
|
"types": "./dist/module.d.mts",
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
],
|
|
62
63
|
"peerDependencies": {
|
|
63
64
|
"nuxt": ">=4.5.1 <5",
|
|
64
|
-
"vue": "^3.5.
|
|
65
|
+
"vue": "^3.5.40"
|
|
65
66
|
},
|
|
66
67
|
"dependencies": {
|
|
67
68
|
"@nuxt/kit": ">=4.5.1 <5",
|
|
@@ -100,7 +101,7 @@
|
|
|
100
101
|
"react-dom": "19.2.7",
|
|
101
102
|
"react-email": "6.9.0",
|
|
102
103
|
"tsx": "4.23.1",
|
|
103
|
-
"vitest": "^4.1.
|
|
104
|
+
"vitest": "^4.1.11",
|
|
104
105
|
"vue": "3.5.42",
|
|
105
106
|
"yaml": "2.9.0"
|
|
106
107
|
},
|
|
@@ -115,20 +116,25 @@
|
|
|
115
116
|
"oracle:check": "node --import tsx test/conformance/tooling/generate-react-oracle.ts --check",
|
|
116
117
|
"oracle:write": "node --import tsx test/conformance/tooling/generate-react-oracle.ts --write",
|
|
117
118
|
"performance:measure": "node --expose-gc --import tsx test/performance/measure.ts",
|
|
118
|
-
"docs:build": "pnpm docs:theme && nuxt-module-build prepare && pnpm
|
|
119
|
+
"docs:build": "pnpm docs:theme && nuxt-module-build prepare && pnpm build && pnpm --dir docs build",
|
|
120
|
+
"docs:onboarding": "node scripts/sync-consumer-onboarding.mjs --write",
|
|
121
|
+
"docs:package": "node scripts/build-agent-docs.mjs",
|
|
119
122
|
"docs:theme": "node scripts/check-docs-theme.mjs",
|
|
120
123
|
"proofs:generate": "node --import tsx scripts/proofs/generate.ts",
|
|
121
124
|
"proofs:send": "node --import tsx scripts/proofs/send.ts",
|
|
122
125
|
"repo:check": "node scripts/check-repository-policy.mjs",
|
|
123
|
-
"release:verify": "pnpm verify &&
|
|
126
|
+
"release:verify": "pnpm verify && pnpm release:pack",
|
|
124
127
|
"release:prepare": "changelogen --bump --clean --hideAuthorEmail",
|
|
125
128
|
"release:artifact": "pnpm release:verify --output .release/lupinum-nuxt-email.tgz && node scripts/write-release-manifest.mjs",
|
|
126
|
-
"lint": "eslint .",
|
|
127
|
-
"test": "pnpm
|
|
129
|
+
"lint": "node scripts/sync-consumer-onboarding.mjs && eslint .",
|
|
130
|
+
"test": "pnpm build && vitest run --exclude test/preview.test.ts --reporter=default --reporter=./test/conformance/tooling/reporter.ts && pnpm test:preview",
|
|
128
131
|
"test:preview": "vitest run test/preview.test.ts",
|
|
129
132
|
"test:conformance": "vitest run test/conformance",
|
|
130
133
|
"test:watch": "vitest watch",
|
|
131
134
|
"test:types": "pnpm dev:prepare && nuxt prepare test/fixtures/basic && nuxt prepare test/fixtures/code-block && nuxt prepare test/fixtures/preview && vue-tsc --noEmit && vue-tsc --noEmit -p test/fixtures/basic/.nuxt/tsconfig.server.json && vue-tsc --noEmit -p test/fixtures/code-block/.nuxt/tsconfig.server.json && vue-tsc --noEmit -p test/fixtures/preview/.nuxt/tsconfig.server.json && cd playground && vue-tsc --noEmit",
|
|
132
|
-
"verify": "pnpm audit:all && pnpm repo:check && pnpm lint && pnpm test:types && pnpm test && pnpm
|
|
135
|
+
"verify": "pnpm check:dependencies && pnpm audit:all && pnpm repo:check && pnpm lint && pnpm test:types && pnpm test && pnpm docs:build",
|
|
136
|
+
"check:dependencies": "node scripts/check-dependency-policy.mjs",
|
|
137
|
+
"build": "nuxt-module-build build",
|
|
138
|
+
"release:pack": "node --import tsx scripts/verify-release.ts"
|
|
133
139
|
}
|
|
134
140
|
}
|