better-svelte-email 0.0.3 → 0.2.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.
Files changed (40) hide show
  1. package/dist/components/Body.svelte +14 -5
  2. package/dist/components/Body.svelte.d.ts +6 -12
  3. package/dist/components/Button.svelte +11 -4
  4. package/dist/components/Button.svelte.d.ts +5 -15
  5. package/dist/components/Column.svelte +19 -0
  6. package/dist/components/Column.svelte.d.ts +10 -0
  7. package/dist/components/Container.svelte +10 -3
  8. package/dist/components/Container.svelte.d.ts +5 -11
  9. package/dist/components/Hr.svelte +14 -0
  10. package/dist/components/Hr.svelte.d.ts +4 -0
  11. package/dist/components/Html.svelte +3 -3
  12. package/dist/components/Html.svelte.d.ts +1 -1
  13. package/dist/components/Link.svelte +26 -0
  14. package/dist/components/Link.svelte.d.ts +9 -0
  15. package/dist/components/Row.svelte +30 -0
  16. package/dist/components/Row.svelte.d.ts +8 -0
  17. package/dist/components/Section.svelte +14 -5
  18. package/dist/components/Section.svelte.d.ts +5 -11
  19. package/dist/components/Text.svelte +13 -3
  20. package/dist/components/Text.svelte.d.ts +6 -12
  21. package/dist/components/index.d.ts +7 -3
  22. package/dist/components/index.js +7 -3
  23. package/dist/emails/apple-receipt.svelte +260 -0
  24. package/dist/emails/apple-receipt.svelte.d.ts +18 -0
  25. package/dist/emails/demo-email.svelte +31 -31
  26. package/dist/emails/test-email.svelte +7 -3
  27. package/dist/emails/vercel-invite-user.svelte +133 -0
  28. package/dist/emails/vercel-invite-user.svelte.d.ts +14 -0
  29. package/dist/index.d.ts +3 -2
  30. package/dist/index.js +2 -2
  31. package/dist/preprocessor/index.js +27 -15
  32. package/dist/preprocessor/parser.d.ts +5 -2
  33. package/dist/preprocessor/parser.js +87 -21
  34. package/dist/preprocessor/transformer.js +3 -3
  35. package/dist/preprocessor/types.d.ts +21 -0
  36. package/dist/preview/Preview.svelte +231 -0
  37. package/dist/preview/Preview.svelte.d.ts +7 -0
  38. package/dist/preview/index.d.ts +85 -0
  39. package/dist/preview/index.js +183 -0
  40. package/package.json +3 -1
@@ -0,0 +1,183 @@
1
+ import { Resend } from 'resend';
2
+ import fs from 'fs';
3
+ import { render } from 'svelte/server';
4
+ import path from 'path';
5
+ import prettier from 'prettier/standalone';
6
+ import parserHtml from 'prettier/parser-html';
7
+ /**
8
+ * Get a list of all email component files in the specified directory.
9
+ *
10
+ * @param options.path - Relative path from root to emails folder (default: '/src/lib/emails')
11
+ * @param options.root - Absolute path to project root (auto-detected if not provided)
12
+ * @returns PreviewData object with list of email files and the path
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // In a +page.server.ts file
17
+ * import { emailList } from 'better-svelte-email/preview';
18
+ *
19
+ * export function load() {
20
+ * const emails = emailList({
21
+ * root: process.cwd(),
22
+ * path: '/src/lib/emails'
23
+ * });
24
+ * return { emails };
25
+ * }
26
+ * ```
27
+ */
28
+ export const emailList = ({ path: emailPath = '/src/lib/emails', root } = {}) => {
29
+ // If root is not provided, try to use process.cwd()
30
+ if (!root) {
31
+ try {
32
+ root = process.cwd();
33
+ }
34
+ catch {
35
+ throw new Error('Could not determine the root path of your project. Please pass in the root param manually using process.cwd() or an absolute path');
36
+ }
37
+ }
38
+ const fullPath = path.join(root, emailPath);
39
+ // Check if directory exists
40
+ if (!fs.existsSync(fullPath)) {
41
+ console.warn(`Email directory not found: ${fullPath}`);
42
+ return { files: null, path: emailPath };
43
+ }
44
+ const files = createEmailComponentList(emailPath, getFiles(fullPath));
45
+ if (!files.length) {
46
+ return { files: null, path: emailPath };
47
+ }
48
+ return { files, path: emailPath };
49
+ };
50
+ /**
51
+ * SvelteKit form action to render an email component.
52
+ * Use this with the Preview component to render email templates on demand.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * // +page.server.ts
57
+ * import { createEmail } from 'better-svelte-email/preview';
58
+ *
59
+ * export const actions = createEmail;
60
+ * ```
61
+ */
62
+ export const createEmail = {
63
+ 'create-email': async (event) => {
64
+ try {
65
+ const data = await event.request.formData();
66
+ const file = data.get('file');
67
+ const emailPath = data.get('path');
68
+ if (!file || !emailPath) {
69
+ return {
70
+ status: 400,
71
+ body: { error: 'Missing file or path parameter' }
72
+ };
73
+ }
74
+ const getEmailComponent = async () => {
75
+ try {
76
+ // Import the email component dynamically
77
+ return (await import(/* @vite-ignore */ `${emailPath}/${file}.svelte`)).default;
78
+ }
79
+ catch {
80
+ throw new Error(`Failed to import email component '${file}'. Make sure the file exists and includes the <Head /> component.`);
81
+ }
82
+ };
83
+ const emailComponent = await getEmailComponent();
84
+ // Render the component to HTML
85
+ const { body } = render(emailComponent);
86
+ // Remove all HTML comments from the body before formatting
87
+ const bodyWithoutComments = body.replace(/<!--[\s\S]*?-->/g, '');
88
+ return prettier.format(bodyWithoutComments, { parser: 'html', plugins: [parserHtml] });
89
+ }
90
+ catch (error) {
91
+ console.error('Error rendering email:', error);
92
+ return {
93
+ status: 500,
94
+ error: {
95
+ message: error instanceof Error ? error.message : 'Failed to render email'
96
+ }
97
+ };
98
+ }
99
+ }
100
+ };
101
+ const defaultSendEmailFunction = async ({ from, to, subject, html }, resendApiKey) => {
102
+ // stringify api key to comment out temp
103
+ const resend = new Resend(resendApiKey);
104
+ const email = { from, to, subject, html };
105
+ const resendReq = await resend.emails.send(email);
106
+ if (resendReq.error) {
107
+ return { success: false, error: resendReq.error };
108
+ }
109
+ else {
110
+ return { success: true, error: null };
111
+ }
112
+ };
113
+ /**
114
+ * Sends the email using the submitted form data.
115
+ */
116
+ export const sendEmail = ({ customSendEmailFunction, resendApiKey }) => {
117
+ return {
118
+ 'send-email': async (event) => {
119
+ const data = await event.request.formData();
120
+ const email = {
121
+ from: 'svelte-email-tailwind <onboarding@resend.dev>',
122
+ to: `${data.get('to')}`,
123
+ subject: `${data.get('component')} ${data.get('note') ? '| ' + data.get('note') : ''}`,
124
+ html: `${data.get('html')}`
125
+ };
126
+ let sent = { success: false, error: null };
127
+ if (!customSendEmailFunction && resendApiKey) {
128
+ sent = await defaultSendEmailFunction(email, resendApiKey);
129
+ }
130
+ else if (customSendEmailFunction) {
131
+ sent = await customSendEmailFunction(email);
132
+ }
133
+ else if (!customSendEmailFunction && !resendApiKey) {
134
+ const error = {
135
+ message: 'Please pass your Resend API key into the "sendEmail" form action, or provide a custom function.'
136
+ };
137
+ return { success: false, error };
138
+ }
139
+ if (sent && sent.error) {
140
+ console.log('Error:', sent.error);
141
+ return { success: false, error: sent.error };
142
+ }
143
+ else {
144
+ console.log('Email was sent successfully.');
145
+ return { success: true, error: null };
146
+ }
147
+ }
148
+ };
149
+ };
150
+ // Recursive function to get files
151
+ function getFiles(dir, files = []) {
152
+ // Get an array of all files and directories in the passed directory using fs.readdirSync
153
+ const fileList = fs.readdirSync(dir);
154
+ // Create the full path of the file/directory by concatenating the passed directory and file/directory name
155
+ for (const file of fileList) {
156
+ const name = `${dir}/${file}`;
157
+ // Check if the current file/directory is a directory using fs.statSync
158
+ if (fs.statSync(name).isDirectory()) {
159
+ // If it is a directory, recursively call the getFiles function with the directory path and the files array
160
+ getFiles(name, files);
161
+ }
162
+ else {
163
+ // If it is a file, push the full path to the files array
164
+ files.push(name);
165
+ }
166
+ }
167
+ return files;
168
+ }
169
+ /**
170
+ * Creates an array of names from the record of svelte email component file paths
171
+ */
172
+ function createEmailComponentList(root, paths) {
173
+ const emailComponentList = [];
174
+ paths.forEach((filePath) => {
175
+ if (filePath.includes(`.svelte`)) {
176
+ const fileName = filePath.substring(filePath.indexOf(root) + root.length + 1, filePath.indexOf('.svelte'));
177
+ emailComponentList.push(fileName);
178
+ }
179
+ });
180
+ return emailComponentList;
181
+ }
182
+ // Export the Preview component
183
+ export { default as Preview } from './Preview.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-svelte-email",
3
- "version": "0.0.3",
3
+ "version": "0.2.0",
4
4
  "author": "Anatole",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,6 +11,7 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "magic-string": "^0.30.19",
14
+ "svelte-highlight": "^7.8.4",
14
15
  "tw-to-css": "^0.0.12"
15
16
  },
16
17
  "devDependencies": {
@@ -83,6 +84,7 @@
83
84
  "dev": "vite dev",
84
85
  "build": "vite build && npm run prepack",
85
86
  "preview": "vite preview",
87
+ "package": "svelte-package",
86
88
  "prepare": "svelte-kit sync || echo ''",
87
89
  "prepack": "svelte-kit sync && svelte-package && publint",
88
90
  "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",