nuxt-generation-emails 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -36
- package/dist/module.json +1 -1
- package/dist/module.mjs +185 -117
- package/dist/runtime/pages/__emails.d.vue.ts +1 -1
- package/dist/runtime/pages/__emails.vue.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,42 +56,7 @@ export default defineNuxtConfig({
|
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
59
|
-
##
|
|
60
|
-
|
|
61
|
-
Templates live inside your app's source directory under the configured `emailDir` (default: `emails/`). You can nest them in subdirectories to organize by version, category, or however you like.
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
app/
|
|
65
|
-
emails/
|
|
66
|
-
v1/
|
|
67
|
-
order-confirmation.vue
|
|
68
|
-
welcome.vue
|
|
69
|
-
v2/
|
|
70
|
-
order-confirmation.vue
|
|
71
|
-
marketing/
|
|
72
|
-
promo.vue
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
The directory structure maps directly to routes:
|
|
76
|
-
|
|
77
|
-
| Template file | Preview URL | API endpoint |
|
|
78
|
-
|----------------------------------------|-------------------------------------------|------------------------------------------|
|
|
79
|
-
| `emails/v1/order-confirmation.vue` | `/__emails/v1/order-confirmation` | `POST /api/emails/v1/order-confirmation` |
|
|
80
|
-
| `emails/v1/welcome.vue` | `/__emails/v1/welcome` | `POST /api/emails/v1/welcome` |
|
|
81
|
-
| `emails/v2/order-confirmation.vue` | `/__emails/v2/order-confirmation` | `POST /api/emails/v2/order-confirmation` |
|
|
82
|
-
| `emails/marketing/promo.vue` | `/__emails/marketing/promo` | `POST /api/emails/marketing/promo` |
|
|
83
|
-
|
|
84
|
-
### Generated routes overview
|
|
85
|
-
|
|
86
|
-

|
|
87
|
-
|
|
88
|
-
### Example route detail
|
|
89
|
-
|
|
90
|
-

|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
## 🛠️ 3. Adding Templates with the CLI
|
|
59
|
+
## 🛠️ 2. Adding Templates with the CLI
|
|
95
60
|
|
|
96
61
|
The fastest way to create a new email template:
|
|
97
62
|
|
|
@@ -172,6 +137,41 @@ If the dev server is running, it will automatically detect the new file and rest
|
|
|
172
137
|
|
|
173
138
|
---
|
|
174
139
|
|
|
140
|
+
## 📁 3. Folder Structure
|
|
141
|
+
|
|
142
|
+
Templates live inside your app's source directory under the configured `emailDir` (default: `emails/`). You can nest them in subdirectories to organize by version, category, or however you like.
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
app/
|
|
146
|
+
emails/
|
|
147
|
+
v1/
|
|
148
|
+
order-confirmation.vue
|
|
149
|
+
welcome.vue
|
|
150
|
+
v2/
|
|
151
|
+
order-confirmation.vue
|
|
152
|
+
marketing/
|
|
153
|
+
promo.vue
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The directory structure maps directly to routes:
|
|
157
|
+
|
|
158
|
+
| Template file | Preview URL | API endpoint |
|
|
159
|
+
|----------------------------------------|-------------------------------------------|------------------------------------------|
|
|
160
|
+
| `emails/v1/order-confirmation.vue` | `/__emails/v1/order-confirmation` | `POST /api/emails/v1/order-confirmation` |
|
|
161
|
+
| `emails/v1/welcome.vue` | `/__emails/v1/welcome` | `POST /api/emails/v1/welcome` |
|
|
162
|
+
| `emails/v2/order-confirmation.vue` | `/__emails/v2/order-confirmation` | `POST /api/emails/v2/order-confirmation` |
|
|
163
|
+
| `emails/marketing/promo.vue` | `/__emails/marketing/promo` | `POST /api/emails/marketing/promo` |
|
|
164
|
+
|
|
165
|
+
### Generated routes overview
|
|
166
|
+
|
|
167
|
+

|
|
168
|
+
|
|
169
|
+
### Example route detail
|
|
170
|
+
|
|
171
|
+

|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
175
|
## ✍️ 4. Writing Email Templates
|
|
176
176
|
|
|
177
177
|
Templates are standard Vue SFCs using [`@vue-email/components`](https://vuemail.net/). Define your template's dynamic data using `defineProps` with `withDefaults`:
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -5,30 +5,101 @@ import { join, relative } from 'pathe';
|
|
|
5
5
|
import { consola } from 'consola';
|
|
6
6
|
import { parse, compileScript } from 'vue/compiler-sfc';
|
|
7
7
|
|
|
8
|
-
function generateWrapperComponent(emailsLayoutPath, emailComponentPath
|
|
9
|
-
const hasProps = extractedProps.props.length > 0;
|
|
10
|
-
const defaultsLiteral = JSON.stringify(extractedProps.defaults, null, 2);
|
|
11
|
-
const propDefsLiteral = JSON.stringify(
|
|
12
|
-
extractedProps.props.map((p) => ({ name: p.name, type: p.type })),
|
|
13
|
-
null,
|
|
14
|
-
2
|
|
15
|
-
);
|
|
8
|
+
function generateWrapperComponent(emailsLayoutPath, emailComponentPath) {
|
|
16
9
|
const scriptClose = "<\/script>";
|
|
17
10
|
const templateOpen = "<template>";
|
|
18
11
|
const templateClose = "</template>";
|
|
19
12
|
return `<script setup lang="ts">
|
|
20
|
-
import { reactive, definePageMeta, onMounted } from '#imports'
|
|
13
|
+
import { reactive, computed, watch, shallowRef, triggerRef, definePageMeta, onMounted } from '#imports'
|
|
21
14
|
import EmailsLayout from '${emailsLayoutPath}'
|
|
22
|
-
import
|
|
15
|
+
import EmailComponentRaw from '${emailComponentPath}'
|
|
23
16
|
|
|
24
17
|
definePageMeta({ layout: false })
|
|
25
|
-
${hasProps ? `
|
|
26
|
-
const propDefaults = ${defaultsLiteral}
|
|
27
|
-
const propDefinitions = ${propDefsLiteral}
|
|
28
18
|
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
|
|
19
|
+
// ---- Runtime component introspection ----
|
|
20
|
+
// We read prop definitions from the compiled component's .props object at
|
|
21
|
+
// runtime. Vue's HMR runtime mutates .props in-place (same object ref) via
|
|
22
|
+
// Object.assign, so a shallowRef change won't fire. We use triggerRef() on
|
|
23
|
+
// every HMR update to force the computed to re-evaluate.
|
|
24
|
+
|
|
25
|
+
const emailComponent = shallowRef(EmailComponentRaw)
|
|
26
|
+
|
|
27
|
+
if (import.meta.hot) {
|
|
28
|
+
// vite:afterUpdate fires client-side after ANY HMR update is applied.
|
|
29
|
+
// triggerRef is cheap \u2014 the computed just re-reads .props from the
|
|
30
|
+
// already-mutated component object.
|
|
31
|
+
import.meta.hot.on('vite:afterUpdate', () => {
|
|
32
|
+
triggerRef(emailComponent)
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function inferType(ctor: unknown): 'string' | 'number' | 'boolean' | 'object' | 'unknown' {
|
|
37
|
+
if (ctor === String) return 'string'
|
|
38
|
+
if (ctor === Number) return 'number'
|
|
39
|
+
if (ctor === Boolean) return 'boolean'
|
|
40
|
+
if (ctor === Object || ctor === Array) return 'object'
|
|
41
|
+
return 'unknown'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface PropDefinition {
|
|
45
|
+
name: string
|
|
46
|
+
type: 'string' | 'number' | 'boolean' | 'object' | 'unknown'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const introspected = computed(() => {
|
|
50
|
+
const comp = emailComponent.value as any
|
|
51
|
+
const raw = comp?.props
|
|
52
|
+
const defs: PropDefinition[] = []
|
|
53
|
+
const defaults: Record<string, unknown> = {}
|
|
54
|
+
|
|
55
|
+
if (!raw) return { defs, defaults }
|
|
56
|
+
|
|
57
|
+
if (Array.isArray(raw)) {
|
|
58
|
+
for (const name of raw) {
|
|
59
|
+
defs.push({ name, type: 'unknown' })
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
for (const [name, spec] of Object.entries(raw as Record<string, any>)) {
|
|
63
|
+
const ctor = spec?.type ?? spec
|
|
64
|
+
defs.push({ name, type: inferType(ctor) })
|
|
65
|
+
|
|
66
|
+
if (spec?.default !== undefined) {
|
|
67
|
+
defaults[name] = typeof spec.default === 'function' ? spec.default() : spec.default
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { defs, defaults }
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const propDefinitions = computed(() => introspected.value.defs)
|
|
76
|
+
|
|
77
|
+
// Reactive state that drives both the live preview and the sidebar controls.
|
|
78
|
+
const emailProps = reactive<Record<string, unknown>>({})
|
|
79
|
+
|
|
80
|
+
// Whenever the component's props change (HMR), reconcile the reactive state:
|
|
81
|
+
// add new props with their defaults, remove props that no longer exist.
|
|
82
|
+
watch(
|
|
83
|
+
() => introspected.value,
|
|
84
|
+
({ defs, defaults }) => {
|
|
85
|
+
const validNames = new Set(defs.map(d => d.name))
|
|
86
|
+
|
|
87
|
+
// Add new props
|
|
88
|
+
for (const name of validNames) {
|
|
89
|
+
if (!(name in emailProps)) {
|
|
90
|
+
emailProps[name] = defaults[name] ?? undefined
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Remove stale props
|
|
95
|
+
for (const key of Object.keys(emailProps)) {
|
|
96
|
+
if (!validNames.has(key)) {
|
|
97
|
+
delete emailProps[key]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{ immediate: true },
|
|
102
|
+
)
|
|
32
103
|
|
|
33
104
|
// Hydrate from URL params on mount so shared links restore state.
|
|
34
105
|
onMounted(() => {
|
|
@@ -49,111 +120,16 @@ onMounted(() => {
|
|
|
49
120
|
})
|
|
50
121
|
}
|
|
51
122
|
})
|
|
52
|
-
` : ""}
|
|
53
123
|
${scriptClose}
|
|
54
124
|
|
|
55
125
|
${templateOpen}
|
|
56
|
-
<EmailsLayout
|
|
57
|
-
<
|
|
126
|
+
<EmailsLayout :email-props="emailProps" :prop-definitions="propDefinitions">
|
|
127
|
+
<component :is="emailComponent" v-bind="emailProps" />
|
|
58
128
|
</EmailsLayout>
|
|
59
129
|
${templateClose}
|
|
60
130
|
`;
|
|
61
131
|
}
|
|
62
132
|
|
|
63
|
-
function extractPropsFromSFC(filePath) {
|
|
64
|
-
const source = fs.readFileSync(filePath, "utf-8");
|
|
65
|
-
const { descriptor } = parse(source, { filename: filePath });
|
|
66
|
-
if (!descriptor.scriptSetup) {
|
|
67
|
-
return { props: [], defaults: {} };
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
const compiled = compileScript(descriptor, {
|
|
71
|
-
id: filePath,
|
|
72
|
-
isProd: false
|
|
73
|
-
});
|
|
74
|
-
const props = [];
|
|
75
|
-
const defaults = {};
|
|
76
|
-
const propNames = [];
|
|
77
|
-
if (compiled.bindings) {
|
|
78
|
-
for (const [name, binding] of Object.entries(compiled.bindings)) {
|
|
79
|
-
if (binding === "props") {
|
|
80
|
-
propNames.push(name);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
const propTypes = extractPropTypesFromSource(descriptor.scriptSetup.content);
|
|
85
|
-
for (const name of propNames) {
|
|
86
|
-
const type = propTypes[name] ?? "unknown";
|
|
87
|
-
props.push({ name, type });
|
|
88
|
-
}
|
|
89
|
-
const defaultValues = extractDefaultsFromSource(descriptor.scriptSetup.content);
|
|
90
|
-
for (const [name, value] of Object.entries(defaultValues)) {
|
|
91
|
-
defaults[name] = value;
|
|
92
|
-
const existing = props.find((p) => p.name === name);
|
|
93
|
-
if (existing) {
|
|
94
|
-
existing.default = value;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return { props, defaults };
|
|
98
|
-
} catch {
|
|
99
|
-
return { props: [], defaults: {} };
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function extractPropTypesFromSource(scriptContent) {
|
|
103
|
-
const result = {};
|
|
104
|
-
const match = scriptContent.match(/defineProps\s*<\s*\{([\s\S]*?)\}\s*>\s*\(/);
|
|
105
|
-
if (!match) return result;
|
|
106
|
-
const typeBody = match[1];
|
|
107
|
-
const propPattern = /(\w+)\s*(?:\?\s*)?:\s*(\w+)/g;
|
|
108
|
-
let propMatch;
|
|
109
|
-
while ((propMatch = propPattern.exec(typeBody)) !== null) {
|
|
110
|
-
const name = propMatch[1];
|
|
111
|
-
const tsType = propMatch[2];
|
|
112
|
-
switch (tsType.toLowerCase()) {
|
|
113
|
-
case "string":
|
|
114
|
-
result[name] = "string";
|
|
115
|
-
break;
|
|
116
|
-
case "number":
|
|
117
|
-
result[name] = "number";
|
|
118
|
-
break;
|
|
119
|
-
case "boolean":
|
|
120
|
-
result[name] = "boolean";
|
|
121
|
-
break;
|
|
122
|
-
default:
|
|
123
|
-
result[name] = "object";
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return result;
|
|
128
|
-
}
|
|
129
|
-
function extractDefaultsFromSource(scriptContent) {
|
|
130
|
-
const withDefaultsMatch = scriptContent.match(
|
|
131
|
-
/withDefaults\s*\(\s*defineProps\s*<[^>]*>\s*\(\s*\)\s*,\s*(\{[\s\S]*?\})\s*\)/
|
|
132
|
-
);
|
|
133
|
-
if (!withDefaultsMatch) return {};
|
|
134
|
-
const defaultsText = withDefaultsMatch[1];
|
|
135
|
-
if (!defaultsText) return {};
|
|
136
|
-
try {
|
|
137
|
-
const jsonish = defaultsText.replace(/'/g, '"').replace(/(\w+)\s*:/g, '"$1":').replace(/,\s*([\]}])/g, "$1");
|
|
138
|
-
return JSON.parse(jsonish);
|
|
139
|
-
} catch {
|
|
140
|
-
return extractPrimitivesFromObjectLiteral(defaultsText);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function extractPrimitivesFromObjectLiteral(text) {
|
|
144
|
-
const result = {};
|
|
145
|
-
const linePattern = /(\w+)\s*:\s*(?:'([^']*)'|"([^"]*)"|(\d+(?:\.\d+)?)|(true|false))\s*[,}]?/g;
|
|
146
|
-
let match;
|
|
147
|
-
while ((match = linePattern.exec(text)) !== null) {
|
|
148
|
-
const key = match[1];
|
|
149
|
-
if (match[2] != null) result[key] = match[2];
|
|
150
|
-
else if (match[3] != null) result[key] = match[3];
|
|
151
|
-
else if (match[4] != null) result[key] = Number(match[4]);
|
|
152
|
-
else if (match[5] != null) result[key] = match[5] === "true";
|
|
153
|
-
}
|
|
154
|
-
return result;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
133
|
function addEmailPages(dirPath, pages, options, routePrefix = "") {
|
|
158
134
|
const entries = fs.readdirSync(dirPath);
|
|
159
135
|
for (const entry of entries) {
|
|
@@ -169,11 +145,9 @@ function addEmailPages(dirPath, pages, options, routePrefix = "") {
|
|
|
169
145
|
if (!fs.existsSync(wrapperDir)) {
|
|
170
146
|
fs.mkdirSync(wrapperDir, { recursive: true });
|
|
171
147
|
}
|
|
172
|
-
const extractedProps = extractPropsFromSFC(fullPath);
|
|
173
148
|
const wrapperContent = generateWrapperComponent(
|
|
174
149
|
options.emailTemplateComponentPath,
|
|
175
|
-
fullPath
|
|
176
|
-
extractedProps
|
|
150
|
+
fullPath
|
|
177
151
|
);
|
|
178
152
|
fs.writeFileSync(wrapperPath, wrapperContent, "utf-8");
|
|
179
153
|
const pageName = `email${routePrefix.replace(/\//g, "-")}-${name}`.replace(/^-+/, "");
|
|
@@ -303,6 +277,100 @@ export default defineEventHandler(async (event) => {
|
|
|
303
277
|
`;
|
|
304
278
|
}
|
|
305
279
|
|
|
280
|
+
function extractPropsFromSFC(filePath) {
|
|
281
|
+
const source = fs.readFileSync(filePath, "utf-8");
|
|
282
|
+
const { descriptor } = parse(source, { filename: filePath });
|
|
283
|
+
if (!descriptor.scriptSetup) {
|
|
284
|
+
return { props: [], defaults: {} };
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
const compiled = compileScript(descriptor, {
|
|
288
|
+
id: filePath,
|
|
289
|
+
isProd: false
|
|
290
|
+
});
|
|
291
|
+
const props = [];
|
|
292
|
+
const defaults = {};
|
|
293
|
+
const propNames = [];
|
|
294
|
+
if (compiled.bindings) {
|
|
295
|
+
for (const [name, binding] of Object.entries(compiled.bindings)) {
|
|
296
|
+
if (binding === "props") {
|
|
297
|
+
propNames.push(name);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const propTypes = extractPropTypesFromSource(descriptor.scriptSetup.content);
|
|
302
|
+
for (const name of propNames) {
|
|
303
|
+
const type = propTypes[name] ?? "unknown";
|
|
304
|
+
props.push({ name, type });
|
|
305
|
+
}
|
|
306
|
+
const defaultValues = extractDefaultsFromSource(descriptor.scriptSetup.content);
|
|
307
|
+
for (const [name, value] of Object.entries(defaultValues)) {
|
|
308
|
+
defaults[name] = value;
|
|
309
|
+
const existing = props.find((p) => p.name === name);
|
|
310
|
+
if (existing) {
|
|
311
|
+
existing.default = value;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return { props, defaults };
|
|
315
|
+
} catch {
|
|
316
|
+
return { props: [], defaults: {} };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function extractPropTypesFromSource(scriptContent) {
|
|
320
|
+
const result = {};
|
|
321
|
+
const match = scriptContent.match(/defineProps\s*<\s*\{([\s\S]*?)\}\s*>\s*\(/);
|
|
322
|
+
if (!match) return result;
|
|
323
|
+
const typeBody = match[1];
|
|
324
|
+
const propPattern = /(\w+)\s*(?:\?\s*)?:\s*(\w+)/g;
|
|
325
|
+
let propMatch;
|
|
326
|
+
while ((propMatch = propPattern.exec(typeBody)) !== null) {
|
|
327
|
+
const name = propMatch[1];
|
|
328
|
+
const tsType = propMatch[2];
|
|
329
|
+
switch (tsType.toLowerCase()) {
|
|
330
|
+
case "string":
|
|
331
|
+
result[name] = "string";
|
|
332
|
+
break;
|
|
333
|
+
case "number":
|
|
334
|
+
result[name] = "number";
|
|
335
|
+
break;
|
|
336
|
+
case "boolean":
|
|
337
|
+
result[name] = "boolean";
|
|
338
|
+
break;
|
|
339
|
+
default:
|
|
340
|
+
result[name] = "object";
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return result;
|
|
345
|
+
}
|
|
346
|
+
function extractDefaultsFromSource(scriptContent) {
|
|
347
|
+
const withDefaultsMatch = scriptContent.match(
|
|
348
|
+
/withDefaults\s*\(\s*defineProps\s*<[^>]*>\s*\(\s*\)\s*,\s*(\{[\s\S]*?\})\s*\)/
|
|
349
|
+
);
|
|
350
|
+
if (!withDefaultsMatch) return {};
|
|
351
|
+
const defaultsText = withDefaultsMatch[1];
|
|
352
|
+
if (!defaultsText) return {};
|
|
353
|
+
try {
|
|
354
|
+
const jsonish = defaultsText.replace(/'/g, '"').replace(/(\w+)\s*:/g, '"$1":').replace(/,\s*([\]}])/g, "$1");
|
|
355
|
+
return JSON.parse(jsonish);
|
|
356
|
+
} catch {
|
|
357
|
+
return extractPrimitivesFromObjectLiteral(defaultsText);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function extractPrimitivesFromObjectLiteral(text) {
|
|
361
|
+
const result = {};
|
|
362
|
+
const linePattern = /(\w+)\s*:\s*(?:'([^']*)'|"([^"]*)"|(\d+(?:\.\d+)?)|(true|false))\s*[,}]?/g;
|
|
363
|
+
let match;
|
|
364
|
+
while ((match = linePattern.exec(text)) !== null) {
|
|
365
|
+
const key = match[1];
|
|
366
|
+
if (match[2] != null) result[key] = match[2];
|
|
367
|
+
else if (match[3] != null) result[key] = match[3];
|
|
368
|
+
else if (match[4] != null) result[key] = Number(match[4]);
|
|
369
|
+
else if (match[5] != null) result[key] = match[5] === "true";
|
|
370
|
+
}
|
|
371
|
+
return result;
|
|
372
|
+
}
|
|
373
|
+
|
|
306
374
|
function generateServerRoutes(emailsDir, buildDir) {
|
|
307
375
|
if (!fs.existsSync(emailsDir)) return [];
|
|
308
376
|
const handlersDir = join(buildDir, "email-handlers");
|
|
@@ -432,7 +500,7 @@ declare module 'nitropack/types' {
|
|
|
432
500
|
const rollupConfig = nuxt.options.nitro?.rollupConfig ?? {};
|
|
433
501
|
const existingPlugins = rollupConfig.plugins ?? [];
|
|
434
502
|
const plugins = Array.isArray(existingPlugins) ? existingPlugins : [existingPlugins];
|
|
435
|
-
rollupConfig.plugins = [
|
|
503
|
+
rollupConfig.plugins = [vue(), ...plugins];
|
|
436
504
|
nuxt.options.nitro = { ...nuxt.options.nitro, rollupConfig };
|
|
437
505
|
if (nuxt.options.dev && fs.existsSync(emailsDir)) {
|
|
438
506
|
const relDir = relative(nuxt.options.rootDir, emailsDir);
|
|
@@ -5,7 +5,7 @@ interface PropDefinition {
|
|
|
5
5
|
type __VLS_Props = {
|
|
6
6
|
/** Reactive object containing current prop values (managed by the wrapper) */
|
|
7
7
|
emailProps?: Record<string, unknown>;
|
|
8
|
-
/** Flat list of prop definitions
|
|
8
|
+
/** Flat list of prop definitions derived from the component at runtime */
|
|
9
9
|
propDefinitions?: PropDefinition[];
|
|
10
10
|
};
|
|
11
11
|
declare var __VLS_1: {}, __VLS_19: {};
|
|
@@ -5,7 +5,7 @@ interface PropDefinition {
|
|
|
5
5
|
type __VLS_Props = {
|
|
6
6
|
/** Reactive object containing current prop values (managed by the wrapper) */
|
|
7
7
|
emailProps?: Record<string, unknown>;
|
|
8
|
-
/** Flat list of prop definitions
|
|
8
|
+
/** Flat list of prop definitions derived from the component at runtime */
|
|
9
9
|
propDefinitions?: PropDefinition[];
|
|
10
10
|
};
|
|
11
11
|
declare var __VLS_1: {}, __VLS_19: {};
|
package/package.json
CHANGED