@razmatinyan/nuxt-email 0.1.0
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/LICENSE +21 -0
- package/README.md +272 -0
- package/dist/module.d.mts +7 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +190 -0
- package/dist/runtime/server/api/config.get.d.ts +5 -0
- package/dist/runtime/server/api/config.get.js +10 -0
- package/dist/runtime/server/api/devtools.d.ts +2 -0
- package/dist/runtime/server/api/devtools.js +265 -0
- package/dist/runtime/server/api/log.get.d.ts +2 -0
- package/dist/runtime/server/api/log.get.js +5 -0
- package/dist/runtime/server/api/preview.d.ts +2 -0
- package/dist/runtime/server/api/preview.js +12 -0
- package/dist/runtime/server/api/send-test.post.d.ts +2 -0
- package/dist/runtime/server/api/send-test.post.js +33 -0
- package/dist/runtime/server/api/templates.get.d.ts +5 -0
- package/dist/runtime/server/api/templates.get.js +8 -0
- package/dist/runtime/server/composables/useEmail.d.ts +9 -0
- package/dist/runtime/server/composables/useEmail.js +96 -0
- package/dist/runtime/server/utils/dev-log.d.ts +14 -0
- package/dist/runtime/server/utils/dev-log.js +8 -0
- package/dist/runtime/server/utils/email-utils.d.ts +9 -0
- package/dist/runtime/server/utils/email-utils.js +134 -0
- package/dist/runtime/server/utils/providers/console.d.ts +6 -0
- package/dist/runtime/server/utils/providers/console.js +44 -0
- package/dist/runtime/server/utils/providers/fetch.d.ts +1 -0
- package/dist/runtime/server/utils/providers/index.d.ts +9 -0
- package/dist/runtime/server/utils/providers/index.js +33 -0
- package/dist/runtime/server/utils/providers/postmark.d.ts +8 -0
- package/dist/runtime/server/utils/providers/postmark.js +94 -0
- package/dist/runtime/server/utils/providers/resend.d.ts +8 -0
- package/dist/runtime/server/utils/providers/resend.js +73 -0
- package/dist/runtime/server/utils/providers/sendgrid.d.ts +8 -0
- package/dist/runtime/server/utils/providers/sendgrid.js +99 -0
- package/dist/runtime/server/utils/providers/shared.d.ts +7 -0
- package/dist/runtime/server/utils/providers/shared.js +29 -0
- package/dist/runtime/server/utils/providers/smtp.d.ts +8 -0
- package/dist/runtime/server/utils/providers/smtp.js +85 -0
- package/dist/runtime/server/utils/template-renderer.d.ts +5 -0
- package/dist/runtime/server/utils/template-renderer.js +10 -0
- package/dist/runtime/server/utils/templates.d.ts +4 -0
- package/dist/runtime/server/utils/templates.js +17 -0
- package/dist/runtime/templates.d.ts +6 -0
- package/dist/runtime/types/index.d.ts +110 -0
- package/dist/runtime/types/index.js +0 -0
- package/dist/types.d.mts +9 -0
- package/package.json +84 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Razmik Matinyan <razmatinyan28@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# nuxt-email
|
|
2
|
+
|
|
3
|
+
Transactional email for Nuxt 4: a provider-agnostic API, Vue-powered templates, and type-safe server composables.
|
|
4
|
+
|
|
5
|
+
- 📨 **One API, many providers** — Resend, SendGrid, Postmark, SMTP, and a console provider for local dev
|
|
6
|
+
- 🧩 **Vue email templates** — author emails as `.vue` files, rendered on the server and CSS-inlined
|
|
7
|
+
- 🔒 **Server-only & type-safe** — credentials stay private; `useEmail()` is auto-imported in your server routes
|
|
8
|
+
- 📎 **Attachments, CC/BCC, reply-to, headers, tags, and scheduling**
|
|
9
|
+
- 🔁 **Automatic retries** on transient failures, plus batch sending with a concurrency limit
|
|
10
|
+
- 🛠 **Nuxt DevTools tab** — browse templates, live-preview them, send a test, and watch a send log
|
|
11
|
+
|
|
12
|
+
## Quick Setup
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @razmatinyan/nuxt-email
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// nuxt.config.ts
|
|
20
|
+
export default defineNuxtConfig({
|
|
21
|
+
modules: ['@razmatinyan/nuxt-email'],
|
|
22
|
+
email: {
|
|
23
|
+
provider: 'console', // logs to the terminal during development
|
|
24
|
+
from: 'App <noreply@myapp.com>',
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Send your first email from any server route:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
// server/api/register.post.ts
|
|
33
|
+
export default defineEventHandler(async (event) => {
|
|
34
|
+
const { sendEmail } = useEmail() // auto-imported on the server
|
|
35
|
+
|
|
36
|
+
const result = await sendEmail({
|
|
37
|
+
to: 'user@example.com',
|
|
38
|
+
subject: 'Welcome!',
|
|
39
|
+
html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
if (!result.success) {
|
|
43
|
+
console.error('Email failed:', result.error)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return result
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Providers
|
|
51
|
+
|
|
52
|
+
All providers share the same API — switch by changing `provider` in `nuxt.config.ts`, no code changes.
|
|
53
|
+
|
|
54
|
+
| Provider | Transport | Extra dependency |
|
|
55
|
+
|------------|--------------------|-------------------------|
|
|
56
|
+
| `console` | Logs to stdout | — |
|
|
57
|
+
| `resend` | REST (`$fetch`) | — |
|
|
58
|
+
| `sendgrid` | REST (`$fetch`) | — |
|
|
59
|
+
| `postmark` | REST (`$fetch`) | — |
|
|
60
|
+
| `smtp` | `nodemailer` | `npm install nodemailer` (optional peer dependency) |
|
|
61
|
+
|
|
62
|
+
The REST providers use Nitro's built-in `$fetch`, so no SDKs are added to your dependency tree. SMTP needs `nodemailer`, which is an optional peer dependency you install only if you use it.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
export default defineNuxtConfig({
|
|
66
|
+
email: {
|
|
67
|
+
provider: 'resend',
|
|
68
|
+
from: 'Acme <hello@acme.com>',
|
|
69
|
+
apiKey: process.env.NUXT_EMAIL_API_KEY,
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```ini
|
|
75
|
+
# .env
|
|
76
|
+
NUXT_EMAIL_API_KEY=re_your_key_here
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For SMTP:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
email: {
|
|
83
|
+
provider: 'smtp',
|
|
84
|
+
from: 'Acme <hello@acme.com>',
|
|
85
|
+
smtp: {
|
|
86
|
+
host: 'smtp.example.com',
|
|
87
|
+
port: 587,
|
|
88
|
+
user: process.env.SMTP_USER,
|
|
89
|
+
pass: process.env.SMTP_PASS,
|
|
90
|
+
secure: false, // true for port 465
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Multiple providers
|
|
96
|
+
|
|
97
|
+
Give each provider its own credentials with the optional `providers` map. Each entry overrides the top-level `apiKey` / `smtp` / `from` for that provider, so you can switch providers per call without reconfiguring:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
email: {
|
|
101
|
+
provider: 'resend', // the default
|
|
102
|
+
from: 'Acme <hello@acme.com>',
|
|
103
|
+
providers: {
|
|
104
|
+
resend: { apiKey: process.env.RESEND_API_KEY },
|
|
105
|
+
postmark: { apiKey: process.env.POSTMARK_API_KEY, from: 'Acme <tx@acme.com>' },
|
|
106
|
+
smtp: { smtp: { host: 'smtp.example.com', user: process.env.SMTP_USER, pass: process.env.SMTP_PASS } },
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
// uses the matching credentials from `providers.postmark`
|
|
113
|
+
await sendEmail({ to, subject, template, props }, { provider: 'postmark' })
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
If you only use one provider, the top-level `apiKey`/`smtp` is all you need — `providers` is optional.
|
|
117
|
+
|
|
118
|
+
## Vue email templates
|
|
119
|
+
|
|
120
|
+
Drop a `.vue` file into `server/emails/` (configurable via `templateDir`) and reference it by filename:
|
|
121
|
+
|
|
122
|
+
```vue
|
|
123
|
+
<!-- server/emails/welcome.vue -->
|
|
124
|
+
<script setup lang="ts">
|
|
125
|
+
defineProps<{ name: string, verifyUrl: string }>()
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<template>
|
|
129
|
+
<div style="font-family: sans-serif; padding: 24px;">
|
|
130
|
+
<h1>Welcome, {{ name }}!</h1>
|
|
131
|
+
<p>Please confirm your email address.</p>
|
|
132
|
+
<a :href="verifyUrl">Verify</a>
|
|
133
|
+
</div>
|
|
134
|
+
</template>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
await sendEmail({
|
|
139
|
+
to: user.email,
|
|
140
|
+
subject: 'Welcome to Acme!',
|
|
141
|
+
template: 'welcome',
|
|
142
|
+
props: { name: user.name, verifyUrl: 'https://acme.com/verify?token=abc' },
|
|
143
|
+
})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The template is rendered to HTML on the server, CSS is inlined for email-client compatibility, and a plain-text fallback is generated automatically. Style with inline `style` attributes or `<style>` blocks inside the `<template>` — SFC `<style>` blocks are stripped during compilation and won't be inlined.
|
|
147
|
+
|
|
148
|
+
### Preview data
|
|
149
|
+
|
|
150
|
+
Export `previewProps` from a plain `<script>` block to give the dev preview and DevTools sample data:
|
|
151
|
+
|
|
152
|
+
```vue
|
|
153
|
+
<script lang="ts">
|
|
154
|
+
export const previewProps = {
|
|
155
|
+
name: 'Jane Doe',
|
|
156
|
+
verifyUrl: 'https://example.com/verify',
|
|
157
|
+
}
|
|
158
|
+
</script>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Sending options
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
await sendEmail({
|
|
165
|
+
to: ['a@example.com', 'b@example.com'],
|
|
166
|
+
cc: 'manager@example.com',
|
|
167
|
+
bcc: 'audit@example.com',
|
|
168
|
+
replyTo: 'support@example.com',
|
|
169
|
+
from: 'Billing <billing@acme.com>', // overrides the default `from`
|
|
170
|
+
subject: 'Your invoice',
|
|
171
|
+
template: 'invoice',
|
|
172
|
+
props: { total: '$99.00' },
|
|
173
|
+
headers: { 'X-Entity-Ref-Id': 'inv_123' },
|
|
174
|
+
tags: { category: 'billing' }, // resend, sendgrid, postmark
|
|
175
|
+
scheduledAt: new Date(Date.now() + 3600_000), // resend, sendgrid
|
|
176
|
+
attachments: [
|
|
177
|
+
{
|
|
178
|
+
filename: 'invoice.pdf',
|
|
179
|
+
content: pdfBuffer, // string or Buffer
|
|
180
|
+
contentType: 'application/pdf',
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Every send returns a structured result (never throws from a provider):
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
interface EmailResponse {
|
|
190
|
+
success: boolean
|
|
191
|
+
messageId?: string
|
|
192
|
+
error?: string
|
|
193
|
+
provider: string
|
|
194
|
+
duration: number // ms
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`tags` and `scheduledAt` are applied by the providers that support them and ignored by the rest.
|
|
199
|
+
|
|
200
|
+
### Batch sending
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
const { sendBatch } = useEmail()
|
|
204
|
+
|
|
205
|
+
const results = await sendBatch(
|
|
206
|
+
users.map(u => ({ to: u.email, subject: 'Newsletter', template: 'newsletter', props: { name: u.name } })),
|
|
207
|
+
{ concurrency: 10 }, // default 5
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
const failed = results.filter(r => !r.success)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Per-call provider override
|
|
214
|
+
|
|
215
|
+
Override the configured provider for a single send (handy for testing). The override reuses the same credentials, so only providers whose credentials are configured will succeed — `console` always works:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
await sendEmail({ to: 'me@example.com', subject: 'Test', template: 'welcome', props }, { provider: 'console' })
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Reliability & validation
|
|
222
|
+
|
|
223
|
+
- **Retries** — failures that look transient (HTTP 429/5xx, timeouts, connection resets) are retried up to `retries` times with a growing delay. Client errors (4xx) are not retried.
|
|
224
|
+
- **Validation** — recipient addresses (`to`/`cc`/`bcc`/`replyTo`) and `from` are validated, `"Name <email>"` form is accepted, and newlines are rejected to prevent header injection. Attachments must have a `filename` and `content`.
|
|
225
|
+
- **Prerendering** — sends are skipped during `nuxi generate` / prerendering and return a no-op success.
|
|
226
|
+
|
|
227
|
+
## DevTools
|
|
228
|
+
|
|
229
|
+
In dev, an **Email** tab appears in Nuxt DevTools. From it you can browse discovered templates, live-preview them (using their `previewProps`), pick a provider and send a test email, and watch an in-session send log. The panel follows the DevTools light/dark theme.
|
|
230
|
+
|
|
231
|
+
These dev-only routes back the tab (and are handy on their own):
|
|
232
|
+
|
|
233
|
+
| Route | Purpose |
|
|
234
|
+
|--------------------------------|------------------------------------------|
|
|
235
|
+
| `GET /_email/preview/:template`| Render a template to HTML |
|
|
236
|
+
| `GET /_email/templates` | List templates and their preview props |
|
|
237
|
+
| `POST /_email/send-test/:template` | Send a test email |
|
|
238
|
+
| `GET /_email/log` | Recent sends from this dev session |
|
|
239
|
+
| `GET /_email/config` | Configured provider and available list |
|
|
240
|
+
|
|
241
|
+
Set `preview: false` to disable them.
|
|
242
|
+
|
|
243
|
+
## Configuration
|
|
244
|
+
|
|
245
|
+
| Option | Type | Default | Description |
|
|
246
|
+
|---------------|-----------|-------------------|--------------------------------------|
|
|
247
|
+
| `provider` | string | `'console'` | `console` · `resend` · `sendgrid` · `postmark` · `smtp` |
|
|
248
|
+
| `from` | string | — | Default sender (`"Name <email>"` or `email`) |
|
|
249
|
+
| `apiKey` | string | — | API key for the REST providers |
|
|
250
|
+
| `smtp` | object | — | `{ host, port?, user?, pass?, secure? }` |
|
|
251
|
+
| `providers` | object | — | Per-provider credentials, e.g. `{ resend: { apiKey }, smtp: { smtp: {...} } }` |
|
|
252
|
+
| `templateDir` | string | `'server/emails'` | Directory scanned for `.vue` templates |
|
|
253
|
+
| `preview` | boolean | `true` | Enable dev preview routes + DevTools tab |
|
|
254
|
+
| `retries` | number | `2` | Retry attempts on transient failures |
|
|
255
|
+
| `retryDelay` | number | `1000` | Base delay between retries (ms) |
|
|
256
|
+
|
|
257
|
+
Keep secrets in `.env` and read them via `process.env` in `nuxt.config.ts`, as shown above. Provider credentials live in private runtime config and are never exposed to the client.
|
|
258
|
+
|
|
259
|
+
## Development
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
npm install # install dependencies
|
|
263
|
+
npm run dev:prepare # build module stubs (run once after cloning)
|
|
264
|
+
npm run dev # playground at http://localhost:3000
|
|
265
|
+
npm run test:unit # fast unit tests
|
|
266
|
+
npm run test:integration # boots Nuxt, slower
|
|
267
|
+
npx tsc --noEmit # type-check
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
[MIT](./LICENSE)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { EmailModuleOptions } from '../dist/runtime/types/index.js';
|
|
3
|
+
export { EmailAttachment, EmailModuleOptions, EmailPayload, EmailProvider, EmailResponse, NormalizedPayload } from '../dist/runtime/types/index.js';
|
|
4
|
+
|
|
5
|
+
declare const _default: _nuxt_schema.NuxtModule<EmailModuleOptions, EmailModuleOptions, false>;
|
|
6
|
+
|
|
7
|
+
export { _default as default };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { resolve, join, relative } from 'node:path';
|
|
3
|
+
import { defineNuxtModule, useLogger, createResolver, addTemplate, addServerImports, addServerHandler, addTypeTemplate } from '@nuxt/kit';
|
|
4
|
+
import { addCustomTab } from '@nuxt/devtools-kit';
|
|
5
|
+
import vuePlugin from 'unplugin-vue/rollup';
|
|
6
|
+
|
|
7
|
+
const MODULE_NAME = "nuxt-email";
|
|
8
|
+
const CONFIG_KEY = "email";
|
|
9
|
+
const VALID_PROVIDERS = ["console", "resend", "sendgrid", "postmark", "smtp"];
|
|
10
|
+
function flattenProviders(providers) {
|
|
11
|
+
if (!providers) return void 0;
|
|
12
|
+
const result = {};
|
|
13
|
+
for (const [name, opts] of Object.entries(providers)) {
|
|
14
|
+
if (!opts) continue;
|
|
15
|
+
result[name] = {
|
|
16
|
+
apiKey: opts.apiKey,
|
|
17
|
+
from: opts.from,
|
|
18
|
+
smtpHost: opts.smtp?.host,
|
|
19
|
+
smtpPort: opts.smtp?.port,
|
|
20
|
+
smtpUser: opts.smtp?.user,
|
|
21
|
+
smtpPass: opts.smtp?.pass,
|
|
22
|
+
smtpSecure: opts.smtp?.secure
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
function scanTemplates(dir) {
|
|
28
|
+
if (!existsSync(dir)) return [];
|
|
29
|
+
const found = [];
|
|
30
|
+
const walk = (current) => {
|
|
31
|
+
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
32
|
+
const full = join(current, entry.name);
|
|
33
|
+
if (entry.isDirectory()) {
|
|
34
|
+
walk(full);
|
|
35
|
+
} else if (entry.isFile() && entry.name.endsWith(".vue")) {
|
|
36
|
+
const name = relative(dir, full).replace(/\\/g, "/").replace(/\.vue$/, "");
|
|
37
|
+
found.push({ name, absPath: full });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
walk(dir);
|
|
42
|
+
return found;
|
|
43
|
+
}
|
|
44
|
+
const module$1 = defineNuxtModule({
|
|
45
|
+
meta: {
|
|
46
|
+
name: MODULE_NAME,
|
|
47
|
+
configKey: CONFIG_KEY,
|
|
48
|
+
compatibility: {
|
|
49
|
+
nuxt: ">=4.0.0"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
defaults: {
|
|
53
|
+
provider: "console",
|
|
54
|
+
from: void 0,
|
|
55
|
+
templateDir: "server/emails",
|
|
56
|
+
preview: true,
|
|
57
|
+
retries: 2,
|
|
58
|
+
retryDelay: 1e3
|
|
59
|
+
},
|
|
60
|
+
setup(options, nuxt) {
|
|
61
|
+
const logger = useLogger(MODULE_NAME);
|
|
62
|
+
const { resolve: resolve$1 } = createResolver(import.meta.url);
|
|
63
|
+
if (!VALID_PROVIDERS.includes(options.provider)) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`[nuxt-email] Unknown provider "${options.provider}". Valid options: ${VALID_PROVIDERS.join(", ")}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (options.provider !== "console" && !options.from) {
|
|
69
|
+
logger.warn(
|
|
70
|
+
"`email.from` is not set. Emails will fail unless `from` is passed per-call. Set `email.from` in nuxt.config.ts."
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const runtimeConfig = nuxt.options.runtimeConfig;
|
|
74
|
+
runtimeConfig._email = {
|
|
75
|
+
provider: options.provider,
|
|
76
|
+
apiKey: options.apiKey ?? "",
|
|
77
|
+
from: options.from ?? "",
|
|
78
|
+
smtpHost: options.smtp?.host ?? "",
|
|
79
|
+
smtpPort: options.smtp?.port ?? 587,
|
|
80
|
+
smtpUser: options.smtp?.user ?? "",
|
|
81
|
+
smtpPass: options.smtp?.pass ?? "",
|
|
82
|
+
smtpSecure: options.smtp?.secure ?? false,
|
|
83
|
+
retries: options.retries,
|
|
84
|
+
retryDelay: options.retryDelay,
|
|
85
|
+
providers: flattenProviders(options.providers)
|
|
86
|
+
};
|
|
87
|
+
const templatesDir = resolve(
|
|
88
|
+
nuxt.options.rootDir,
|
|
89
|
+
options.templateDir
|
|
90
|
+
);
|
|
91
|
+
const templatesFile = addTemplate({
|
|
92
|
+
filename: "nuxt-email/templates.mjs",
|
|
93
|
+
write: true,
|
|
94
|
+
getContents: () => {
|
|
95
|
+
const templates = scanTemplates(templatesDir);
|
|
96
|
+
const imports = templates.map(
|
|
97
|
+
(t, i) => `import * as t${i} from ${JSON.stringify(t.absPath.replace(/\\/g, "/"))}`
|
|
98
|
+
).join("\n");
|
|
99
|
+
const entries = templates.map(
|
|
100
|
+
(t, i) => ` ${JSON.stringify(t.name)}: t${i}.default,`
|
|
101
|
+
).join("\n");
|
|
102
|
+
const previewEntries = templates.map((t, i) => ` ${JSON.stringify(t.name)}: __pp(t${i}),`).join("\n");
|
|
103
|
+
return `${imports}
|
|
104
|
+
|
|
105
|
+
const __pp = m => (m && m.previewProps) ? m.previewProps : {}
|
|
106
|
+
|
|
107
|
+
export const templates = {
|
|
108
|
+
${entries}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const previewProps = {
|
|
112
|
+
${previewEntries}
|
|
113
|
+
}
|
|
114
|
+
`;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
const templatesFilePath = templatesFile.dst.replace(/\\/g, "/");
|
|
118
|
+
const templatesDirPath = templatesDir.replace(/\\/g, "/");
|
|
119
|
+
const onNitroConfig = nuxt.hook;
|
|
120
|
+
onNitroConfig("nitro:config", (config) => {
|
|
121
|
+
config.alias = config.alias || {};
|
|
122
|
+
config.alias["#nuxt-email/templates"] = templatesFilePath;
|
|
123
|
+
config.externals = config.externals || {};
|
|
124
|
+
config.externals.inline = config.externals.inline || [];
|
|
125
|
+
config.externals.inline.push(templatesFilePath, templatesDirPath);
|
|
126
|
+
config.rollupConfig = config.rollupConfig || {};
|
|
127
|
+
config.rollupConfig.plugins = config.rollupConfig.plugins || [];
|
|
128
|
+
config.rollupConfig.plugins.push(vuePlugin());
|
|
129
|
+
});
|
|
130
|
+
addServerImports([
|
|
131
|
+
{
|
|
132
|
+
name: "useEmail",
|
|
133
|
+
from: resolve$1("./runtime/server/composables/useEmail")
|
|
134
|
+
}
|
|
135
|
+
]);
|
|
136
|
+
if (nuxt.options.dev && options.preview) {
|
|
137
|
+
addServerHandler({
|
|
138
|
+
route: "/_email/preview/:template",
|
|
139
|
+
handler: resolve$1("./runtime/server/api/preview")
|
|
140
|
+
});
|
|
141
|
+
addServerHandler({
|
|
142
|
+
route: "/_email/send-test/:template",
|
|
143
|
+
method: "post",
|
|
144
|
+
handler: resolve$1("./runtime/server/api/send-test.post")
|
|
145
|
+
});
|
|
146
|
+
addServerHandler({
|
|
147
|
+
route: "/_email/templates",
|
|
148
|
+
method: "get",
|
|
149
|
+
handler: resolve$1("./runtime/server/api/templates.get")
|
|
150
|
+
});
|
|
151
|
+
addServerHandler({
|
|
152
|
+
route: "/_email/log",
|
|
153
|
+
method: "get",
|
|
154
|
+
handler: resolve$1("./runtime/server/api/log.get")
|
|
155
|
+
});
|
|
156
|
+
addServerHandler({
|
|
157
|
+
route: "/_email/config",
|
|
158
|
+
method: "get",
|
|
159
|
+
handler: resolve$1("./runtime/server/api/config.get")
|
|
160
|
+
});
|
|
161
|
+
addServerHandler({
|
|
162
|
+
route: "/_email/devtools",
|
|
163
|
+
handler: resolve$1("./runtime/server/api/devtools")
|
|
164
|
+
});
|
|
165
|
+
addCustomTab({
|
|
166
|
+
name: "nuxt-email",
|
|
167
|
+
title: "Email",
|
|
168
|
+
icon: "carbon:email",
|
|
169
|
+
view: { type: "iframe", src: "/_email/devtools" }
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
addTypeTemplate({
|
|
173
|
+
filename: "types/nuxt-email.d.ts",
|
|
174
|
+
getContents: () => `
|
|
175
|
+
import type { EmailRuntimeConfig } from '${resolve$1("./runtime/types/index.js")}'
|
|
176
|
+
|
|
177
|
+
declare module 'nitropack' {
|
|
178
|
+
interface NitroRuntimeConfig {
|
|
179
|
+
_email: EmailRuntimeConfig
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export {}
|
|
184
|
+
`
|
|
185
|
+
});
|
|
186
|
+
logger.success(`nuxt-email ready (provider: ${options.provider})`);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineEventHandler } from "h3";
|
|
2
|
+
import { useRuntimeConfig } from "nitropack/runtime";
|
|
3
|
+
import { VALID_PROVIDERS } from "../utils/providers/index.js";
|
|
4
|
+
export default defineEventHandler(() => {
|
|
5
|
+
const config = useRuntimeConfig()._email;
|
|
6
|
+
return {
|
|
7
|
+
provider: config.provider,
|
|
8
|
+
providers: VALID_PROVIDERS
|
|
9
|
+
};
|
|
10
|
+
});
|