@raypx-kit/email-preview 0.0.0-alpha.0 → 0.0.0-alpha.1

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @raypx-kit/email-preview
2
2
 
3
+ ## 0.0.0-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - f4006e4: Recover a template whose repair lands within 50 ms of the broken save. Vite's watcher drops that second change on Linux, which left the failed render cached and the preview stuck on the old compile error; the runtime now re-checks each changed file after the watcher's throttle window and refreshes the preview when it changed.
8
+
3
9
  ## 0.0.0-alpha.0
4
10
 
5
11
  Initial prerelease.
package/dist/index.mjs CHANGED
@@ -2,6 +2,7 @@ import { fileURLToPath } from "node:url";
2
2
  import { createRunnableDevEnvironment, isRunnableDevEnvironment } from "vite";
3
3
  import { readFile, readdir, realpath, stat } from "node:fs/promises";
4
4
  import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
+ import { statSync } from "node:fs";
5
6
  import * as html from "prettier/plugins/html";
6
7
  import { format } from "prettier/standalone";
7
8
  // src/server/errors.ts
@@ -273,6 +274,10 @@ async function readStaticFile(directory, name) {
273
274
  };
274
275
  }
275
276
  // src/server/runtime.ts
277
+ function fileSignature(path) {
278
+ const stats = statSync(path, { throwIfNoEntry: false });
279
+ return stats ? `${stats.mtimeMs}:${stats.size}` : "missing";
280
+ }
276
281
  function isRenderedEmail(value) {
277
282
  return typeof value === "object" && value !== null && "html" in value && typeof value.html === "string" && "text" in value && typeof value.text === "string";
278
283
  }
@@ -280,20 +285,23 @@ var PreviewRuntime = class PreviewRuntime {
280
285
  server;
281
286
  directory;
282
287
  timeoutMs;
288
+ recheckMs;
283
289
  listeners = /* @__PURE__ */ new Set();
284
290
  streams = /* @__PURE__ */ new Set();
291
+ rechecks = /* @__PURE__ */ new Map();
285
292
  notification;
286
293
  closed = false;
287
294
  currentRevision = 0;
288
- constructor(server, directory, timeoutMs) {
295
+ constructor(server, directory, timeoutMs, recheckMs) {
289
296
  this.server = server;
290
297
  this.directory = directory;
291
298
  this.timeoutMs = timeoutMs;
299
+ this.recheckMs = recheckMs;
292
300
  }
293
301
  static async create(server, options) {
294
302
  if (!Number.isFinite(options.renderTimeoutMs) || options.renderTimeoutMs < 1) throw new PreviewRuntimeError("INVALID_OPTIONS", "renderTimeoutMs must be a positive number.", 400);
295
303
  const directory = await resolveDirectory(server.config.root, options.directory);
296
- const runtime = new PreviewRuntime(server, directory, options.renderTimeoutMs);
304
+ const runtime = new PreviewRuntime(server, directory, options.renderTimeoutMs, options.missedChangeWindowMs ?? 75);
297
305
  server.watcher.add(directory);
298
306
  server.watcher.on("all", runtime.onFileEvent);
299
307
  return runtime;
@@ -307,12 +315,22 @@ var PreviewRuntime = class PreviewRuntime {
307
315
  const path = resolve(file);
308
316
  const environment = this.server.environments[environmentName];
309
317
  if (!isContained(this.directory, path) && basename(path) !== "tsconfig.json" && !environment?.moduleGraph.getModulesByFile(path)?.size) return;
318
+ this.refresh();
319
+ const seen = fileSignature(path);
320
+ clearTimeout(this.rechecks.get(path));
321
+ this.rechecks.set(path, setTimeout(() => {
322
+ this.rechecks.delete(path);
323
+ if (!this.closed && fileSignature(path) !== seen) this.refresh();
324
+ }, this.recheckMs));
325
+ };
326
+ refresh() {
327
+ const environment = this.server.environments[environmentName];
310
328
  if (environment && isRunnableDevEnvironment(environment)) {
311
329
  environment.moduleGraph.invalidateAll();
312
330
  environment.runner.clearCache();
313
331
  }
314
332
  this.invalidate();
315
- };
333
+ }
316
334
  get revision() {
317
335
  return this.currentRevision;
318
336
  }
@@ -410,6 +428,8 @@ var PreviewRuntime = class PreviewRuntime {
410
428
  if (this.closed) return;
411
429
  this.closed = true;
412
430
  if (this.notification) clearTimeout(this.notification);
431
+ for (const timer of this.rechecks.values()) clearTimeout(timer);
432
+ this.rechecks.clear();
413
433
  this.server.watcher.off("all", this.onFileEvent);
414
434
  for (const close of this.streams) close();
415
435
  this.streams.clear();
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/server/errors.ts","../src/server/discovery.ts","../src/server/middleware.ts","../src/server/render.ts","../src/server/format.ts","../src/server/static.ts","../src/server/runtime.ts","../src/server/plugin.ts"],"sourcesContent":["export class PreviewRuntimeError extends Error {\n readonly code: string;\n readonly status: number;\n\n constructor(code: string, message: string, status = 500) {\n super(message);\n this.name = \"PreviewRuntimeError\";\n this.code = code;\n this.status = status;\n }\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { readdir, realpath, stat } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport type { Template } from \"../types.js\";\nimport { PreviewRuntimeError } from \"./errors.js\";\n\nexport function isContained(root: string, path: string): boolean {\n const name = relative(root, path);\n return name === \"\" || (!isAbsolute(name) && name !== \"..\" && !name.startsWith(`..${sep}`));\n}\n\nexport async function resolveDirectory(cwd: string, directory: string): Promise<string> {\n const requested = resolve(cwd, directory);\n try {\n const path = await realpath(requested);\n if (!(await stat(path)).isDirectory()) throw new Error(\"Not a directory\");\n return path;\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n // A new project can start the preview before its emails folder exists.\n // Resolve existing ancestors so future path checks use one canonical root.\n const parent = dirname(requested);\n if (parent !== requested)\n return resolve(await resolveDirectory(parent, \".\"), basename(requested));\n }\n throw new PreviewRuntimeError(\n \"DIRECTORY_NOT_FOUND\",\n `Email directory not found: ${directory}`,\n 400,\n );\n }\n}\n\n/** Never use an HTTP-supplied path as a filesystem path without this boundary. */\nexport async function resolveContainedFile(root: string, name: string): Promise<string> {\n if (!name || name.includes(\"\\\\\") || name.includes(\"\\0\") || isAbsolute(name)) {\n throw new PreviewRuntimeError(\"NOT_FOUND\", \"File not found\", 404);\n }\n const path = resolve(root, name);\n if (!isContained(root, path)) throw new PreviewRuntimeError(\"NOT_FOUND\", \"File not found\", 404);\n try {\n const canonical = await realpath(path);\n if (!isContained(root, canonical) || !(await stat(canonical)).isFile()) {\n throw new Error(\"Outside directory or not a file\");\n }\n return canonical;\n } catch {\n throw new PreviewRuntimeError(\"NOT_FOUND\", \"File not found\", 404);\n }\n}\n\nexport async function discoverTemplates(root: string): Promise<Template[]> {\n const templates: Template[] = [];\n async function visit(directory: string): Promise<void> {\n const entries = await readdir(directory, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name.startsWith(\".\") || entry.name.startsWith(\"_\") || entry.name === \"static\")\n continue;\n const path = resolve(directory, entry.name);\n if (entry.isDirectory()) {\n await visit(path);\n } else if (entry.isFile() && /\\.(tsx|jsx)$/.test(entry.name)) {\n const id = relative(root, path).split(sep).join(\"/\");\n const slash = id.lastIndexOf(\"/\");\n templates.push({\n id,\n name: entry.name.replace(/\\.(tsx|jsx)$/, \"\"),\n directory: slash < 0 ? \"\" : id.slice(0, slash),\n });\n }\n }\n }\n try {\n await visit(root);\n } catch (error) {\n if (!(error instanceof Error) || !(\"code\" in error) || error.code !== \"ENOENT\") throw error;\n }\n return templates.sort((left, right) => left.id.localeCompare(right.id));\n}\n","import { readFile } from \"node:fs/promises\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { extname } from \"node:path\";\nimport { resolveContainedFile } from \"./discovery.js\";\nimport { PreviewRuntimeError } from \"./errors.js\";\nimport type { PreviewRuntime } from \"./runtime.js\";\n\ntype Next = (error?: unknown) => void;\n\nconst noCache = { \"Cache-Control\": \"no-store\", \"X-Content-Type-Options\": \"nosniff\" };\n\nconst interfaceTypes: Readonly<Record<string, string>> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".woff2\": \"font/woff2\",\n};\n\nfunction send(\n response: ServerResponse,\n status: number,\n headers: Record<string, string>,\n body?: string | Uint8Array,\n) {\n response.writeHead(status, headers);\n response.end(body);\n}\n\nfunction sendJson(response: ServerResponse, status: number, value: unknown) {\n send(\n response,\n status,\n { ...noCache, \"Content-Type\": \"application/json; charset=utf-8\" },\n JSON.stringify(value),\n );\n}\n\nfunction sendError(response: ServerResponse, error: unknown) {\n const expected = error instanceof PreviewRuntimeError;\n sendJson(response, expected ? error.status : 500, {\n error: {\n code: expected ? error.code : \"INTERNAL_ERROR\",\n message: expected\n ? error.message\n : \"The preview request failed. Try again after checking the template.\",\n },\n });\n}\n\nfunction openEvents(runtime: PreviewRuntime, request: IncomingMessage, response: ServerResponse) {\n response.writeHead(200, {\n ...noCache,\n \"Content-Type\": \"text/event-stream\",\n Connection: \"keep-alive\",\n \"X-Accel-Buffering\": \"no\",\n });\n const send = (event: string, revision: number) =>\n response.write(`event: ${event}\\ndata: ${JSON.stringify({ revision })}\\n\\n`);\n const unsubscribe = runtime.subscribe((revision) => send(\"change\", revision));\n const heartbeat = setInterval(() => response.write(\": heartbeat\\n\\n\"), 15000);\n heartbeat.unref();\n let untrack = () => {};\n const close = () => {\n clearInterval(heartbeat);\n unsubscribe();\n untrack();\n if (!response.writableEnded) response.end();\n };\n untrack = runtime.trackStream(close);\n request.once(\"close\", close);\n send(\"ready\", runtime.revision);\n}\n\n/**\n * Serves the prebuilt interface and its API below `base`, which ends with a slash.\n * Requests outside `base` continue to the host server's own middleware.\n */\nexport function previewMiddleware(\n base: string,\n interfaceDirectory: string,\n getRuntime: () => Promise<PreviewRuntime>,\n) {\n let indexHtml: Promise<string> | undefined;\n const readIndex = () =>\n (indexHtml ??= readFile(`${interfaceDirectory}/index.html`, \"utf8\").then(\n // Relative asset and API URLs resolve against the mount point.\n (html) => html.replace(\"<head>\", `<head><base href=\"${base}\" />`),\n () => {\n indexHtml = undefined;\n throw new PreviewRuntimeError(\n \"NOT_FOUND\",\n \"The preview interface has not been built.\",\n 404,\n );\n },\n ));\n\n return async (request: IncomingMessage, response: ServerResponse, next: Next) => {\n const url = new URL(request.url ?? \"/\", \"http://localhost\");\n const mount = base.slice(0, -1);\n if (mount && url.pathname === mount) {\n send(response, 302, { Location: `${base}${url.search}` });\n return;\n }\n if (!url.pathname.startsWith(base)) return next();\n if (request.method !== \"GET\" && request.method !== \"HEAD\") return next();\n const path = url.pathname.slice(base.length);\n try {\n if (path === \"api/templates\") {\n sendJson(response, 200, await (await getRuntime()).listTemplates());\n } else if (path === \"api/preview\") {\n const template = url.searchParams.get(\"template\");\n if (!template) {\n throw new PreviewRuntimeError(\"INVALID_TEMPLATE\", \"Select an email template.\", 400);\n }\n sendJson(response, 200, await (await getRuntime()).renderTemplate(template));\n } else if (path === \"api/events\") {\n openEvents(await getRuntime(), request, response);\n } else if (path.startsWith(\"static/\")) {\n const file = await (await getRuntime()).getStaticFile(decodeURIComponent(path.slice(7)));\n send(\n response,\n 200,\n { ...noCache, \"Content-Type\": file.contentType, \"Content-Security-Policy\": \"sandbox\" },\n file.data,\n );\n } else if (path === \"\" || path === \"index.html\") {\n send(\n response,\n 200,\n { ...noCache, \"Content-Type\": interfaceTypes[\".html\"]! },\n await readIndex(),\n );\n } else if (path.startsWith(\"assets/\")) {\n const file = await resolveContainedFile(interfaceDirectory, path).catch(() => undefined);\n // Unknown paths belong to the host server, which matters when `base` is `/`.\n if (!file) return next();\n send(\n response,\n 200,\n {\n \"Cache-Control\": \"public, max-age=31536000, immutable\",\n \"Content-Type\": interfaceTypes[extname(file)] ?? \"application/octet-stream\",\n \"X-Content-Type-Options\": \"nosniff\",\n },\n await readFile(file),\n );\n } else {\n return next();\n }\n } catch (error) {\n if (response.headersSent) response.destroy();\n else sendError(response, error);\n }\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** The Vite environment that evaluates templates, separate from the application's `ssr`. */\nexport const environmentName = \"email_preview\";\n\nconst entryPrefix = \"virtual:email-preview-render:\";\nconst resolvedEntryPrefix = `\\0${entryPrefix}`;\n// Resolved from this package, which depends on the renderer; the project may not.\n// The generated entry imports it by path inside the project's Vite environment, so\n// its `react` import resolves to the same React as the template.\nconst rendererPath = fileURLToPath(import.meta.resolve(\"@react-email/render\"));\n\nexport function renderEntryId(template: string): string {\n return `${entryPrefix}${template}`;\n}\n\nexport function resolveRenderEntry(id: string): string | undefined {\n return id.startsWith(entryPrefix) ? `\\0${id}` : undefined;\n}\n\n/** Renders one template with its `PreviewProps`, importing React through the project. */\nexport function loadRenderEntry(id: string): string | undefined {\n if (!id.startsWith(resolvedEntryPrefix)) return;\n const template = id.slice(resolvedEntryPrefix.length);\n return `import { createElement } from \"react\";\nimport { render, toPlainText } from ${JSON.stringify(rendererPath)};\nimport * as email from ${JSON.stringify(template)};\nexport async function renderEmail() {\n const Component = email.default;\n if (!Component || (typeof Component !== \"function\" && typeof Component !== \"object\")) {\n throw new Error(\"The email template must default-export a React component.\");\n }\n const html = await render(createElement(Component, Component.PreviewProps ?? {}));\n return { html, text: toPlainText(html) };\n}`;\n}\n\n/**\n * Reads the project's manifest straight from disk. Bun caches failed module\n * lookups in-process, so `require.resolve` would miss a later install.\n */\nasync function readProjectManifest(name: string, from: string): Promise<{ version?: string }> {\n for (let directory = dirname(from); ; directory = dirname(directory)) {\n try {\n return JSON.parse(\n await readFile(join(directory, \"node_modules\", name, \"package.json\"), \"utf8\"),\n );\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n }\n if (dirname(directory) === directory) return {};\n }\n}\n\nexport async function assertReact(template: string): Promise<void> {\n for (const name of [\"react\", \"react-dom\"]) {\n const manifest = await readProjectManifest(name, template);\n if (!manifest.version?.startsWith(\"19.\")) {\n throw new Error(`Install ${name} 19 in the project containing your email templates.`);\n }\n }\n}\n","import * as html from \"prettier/plugins/html\";\nimport { format } from \"prettier/standalone\";\n\n/**\n * Indents rendered HTML for the source view. The preview, clipboard and downloads\n * keep the original output, so markup Prettier cannot parse is shown unchanged.\n */\nexport async function formatHtml(source: string): Promise<string> {\n try {\n return await format(source, {\n parser: \"html\",\n plugins: [html],\n endOfLine: \"lf\",\n tabWidth: 2,\n bracketSameLine: true,\n });\n } catch {\n return source;\n }\n}\n","import { readFile, realpath } from \"node:fs/promises\";\nimport { extname, resolve } from \"node:path\";\nimport { isContained, resolveContainedFile } from \"./discovery.js\";\nimport { PreviewRuntimeError } from \"./errors.js\";\n\nconst contentTypes: Readonly<Record<string, string>> = {\n \".avif\": \"image/avif\",\n \".css\": \"text/css; charset=utf-8\",\n \".gif\": \"image/gif\",\n \".ico\": \"image/x-icon\",\n \".jpeg\": \"image/jpeg\",\n \".jpg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".svg\": \"image/svg+xml\",\n \".ttf\": \"font/ttf\",\n \".webp\": \"image/webp\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n};\n\nexport async function readStaticFile(\n directory: string,\n name: string,\n): Promise<{ data: Uint8Array<ArrayBuffer>; contentType: string }> {\n let root: string;\n try {\n root = await realpath(resolve(directory, \"static\"));\n } catch {\n throw new PreviewRuntimeError(\"NOT_FOUND\", \"Static file not found\", 404);\n }\n if (!isContained(directory, root)) {\n throw new PreviewRuntimeError(\"NOT_FOUND\", \"Static file not found\", 404);\n }\n const path = await resolveContainedFile(root, name);\n return {\n data: await readFile(path),\n contentType: contentTypes[extname(path).toLowerCase()] ?? \"application/octet-stream\",\n };\n}\n","import { basename, resolve } from \"node:path\";\nimport { isRunnableDevEnvironment } from \"vite\";\nimport type { RunnableDevEnvironment, ViteDevServer } from \"vite\";\nimport type { PreviewResult, TemplateList } from \"../types.js\";\nimport {\n discoverTemplates,\n isContained,\n resolveContainedFile,\n resolveDirectory,\n} from \"./discovery.js\";\nimport { errorMessage, PreviewRuntimeError } from \"./errors.js\";\nimport { formatHtml } from \"./format.js\";\nimport { assertReact, environmentName, renderEntryId } from \"./render.js\";\nimport { readStaticFile } from \"./static.js\";\n\nexport interface RuntimeOptions {\n /** Template directory, relative to the Vite root unless absolute. */\n directory: string;\n renderTimeoutMs: number;\n}\n\ntype RenderedEmail = Pick<PreviewResult, \"html\" | \"text\">;\n\nfunction isRenderedEmail(value: unknown): value is RenderedEmail {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"html\" in value &&\n typeof value.html === \"string\" &&\n \"text\" in value &&\n typeof value.text === \"string\"\n );\n}\n\nexport class PreviewRuntime {\n private readonly listeners = new Set<(revision: number) => void>();\n private readonly streams = new Set<() => void>();\n private notification: ReturnType<typeof setTimeout> | undefined;\n private closed = false;\n private currentRevision = 0;\n\n private constructor(\n private readonly server: ViteDevServer,\n readonly directory: string,\n private readonly timeoutMs: number,\n ) {}\n\n static async create(server: ViteDevServer, options: RuntimeOptions): Promise<PreviewRuntime> {\n if (!Number.isFinite(options.renderTimeoutMs) || options.renderTimeoutMs < 1) {\n throw new PreviewRuntimeError(\n \"INVALID_OPTIONS\",\n \"renderTimeoutMs must be a positive number.\",\n 400,\n );\n }\n const directory = await resolveDirectory(server.config.root, options.directory);\n const runtime = new PreviewRuntime(server, directory, options.renderTimeoutMs);\n // The template directory may sit outside the Vite root, for example in a sibling package.\n server.watcher.add(directory);\n server.watcher.on(\"all\", runtime.onFileEvent);\n return runtime;\n }\n\n private get environment(): RunnableDevEnvironment {\n const environment = this.server.environments[environmentName];\n if (!environment || !isRunnableDevEnvironment(environment)) {\n throw new PreviewRuntimeError(\n \"NOT_INITIALIZED\",\n \"The email preview environment is not available.\",\n 503,\n );\n }\n return environment;\n }\n\n private readonly onFileEvent = (_event: string, file: string): void => {\n const path = resolve(file);\n const environment = this.server.environments[environmentName];\n if (\n !isContained(this.directory, path) &&\n basename(path) !== \"tsconfig.json\" &&\n !environment?.moduleGraph.getModulesByFile(path)?.size\n ) {\n return;\n }\n // Vite invalidates changed modules asynchronously. Template graphs are small,\n // so drop them now and the next render can never observe stale code.\n if (environment && isRunnableDevEnvironment(environment)) {\n environment.moduleGraph.invalidateAll();\n environment.runner.clearCache();\n }\n this.invalidate();\n };\n\n get revision(): number {\n return this.currentRevision;\n }\n\n private invalidate(): void {\n if (this.closed) return;\n this.currentRevision += 1;\n if (this.notification) clearTimeout(this.notification);\n this.notification = setTimeout(() => {\n this.notification = undefined;\n for (const listener of this.listeners) {\n try {\n listener(this.currentRevision);\n } catch {\n this.listeners.delete(listener);\n }\n }\n }, 80);\n }\n\n private assertOpen(): void {\n if (this.closed) throw new PreviewRuntimeError(\"CLOSED\", \"The preview server is closing.\", 503);\n }\n\n async listTemplates(): Promise<TemplateList> {\n this.assertOpen();\n return {\n directory: this.directory,\n templates: await discoverTemplates(this.directory),\n revision: this.revision,\n };\n }\n\n async renderTemplate(id: string): Promise<PreviewResult> {\n this.assertOpen();\n const { templates } = await this.listTemplates();\n if (!templates.some((template) => template.id === id)) {\n throw new PreviewRuntimeError(\"TEMPLATE_NOT_FOUND\", \"Email template not found.\", 404);\n }\n const path = await resolveContainedFile(this.directory, id);\n try {\n await assertReact(path);\n } catch (error) {\n throw new PreviewRuntimeError(\"DEPENDENCY_ERROR\", errorMessage(error), 422);\n }\n for (let attempt = 0; attempt < 3; attempt += 1) {\n this.assertOpen();\n const revision = this.revision;\n const result = await this.render(path);\n if (this.revision === revision) {\n return { id, revision, ...result, formattedHtml: await formatHtml(result.html) };\n }\n }\n throw new PreviewRuntimeError(\n \"TEMPLATE_CHANGED\",\n \"The template changed while rendering. Please try again.\",\n 409,\n );\n }\n\n private async render(path: string): Promise<RenderedEmail> {\n let entry: { renderEmail?: unknown };\n try {\n entry = await this.withTimeout(this.environment.runner.import(renderEntryId(path)));\n } catch (error) {\n if (error instanceof PreviewRuntimeError) throw error;\n throw new PreviewRuntimeError(\"COMPILE_ERROR\", errorMessage(error), 422);\n }\n try {\n if (typeof entry.renderEmail !== \"function\") throw new Error(\"Rendering failed to load.\");\n const value: unknown = await this.withTimeout(Promise.resolve(entry.renderEmail()));\n if (!isRenderedEmail(value)) throw new Error(\"The template returned an invalid result.\");\n return value;\n } catch (error) {\n if (error instanceof PreviewRuntimeError) throw error;\n throw new PreviewRuntimeError(\"RENDER_ERROR\", errorMessage(error), 422);\n }\n }\n\n /**\n * Templates run in the dev server process. A timeout reports work that never\n * settles; it cannot interrupt a synchronous loop.\n */\n private withTimeout<T>(work: Promise<T>): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () =>\n reject(\n new PreviewRuntimeError(\n \"RENDER_TIMEOUT\",\n `Template rendering exceeded ${this.timeoutMs} ms.`,\n 422,\n ),\n ),\n this.timeoutMs,\n );\n });\n return Promise.race([work, timeout]).finally(() => clearTimeout(timer));\n }\n\n subscribe(listener: (revision: number) => void): () => void {\n this.assertOpen();\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /** Registers an open event stream so shutdown can end it. */\n trackStream(close: () => void): () => void {\n this.streams.add(close);\n return () => this.streams.delete(close);\n }\n\n async getStaticFile(\n path: string,\n ): Promise<{ data: Uint8Array<ArrayBuffer>; contentType: string }> {\n this.assertOpen();\n return readStaticFile(this.directory, path);\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n if (this.notification) clearTimeout(this.notification);\n this.server.watcher.off(\"all\", this.onFileEvent);\n for (const close of this.streams) close();\n this.streams.clear();\n this.listeners.clear();\n }\n}\n","import { fileURLToPath } from \"node:url\";\nimport { createRunnableDevEnvironment } from \"vite\";\nimport type { Plugin } from \"vite\";\nimport { previewMiddleware } from \"./middleware.js\";\nimport { environmentName, loadRenderEntry, resolveRenderEntry } from \"./render.js\";\nimport { PreviewRuntime } from \"./runtime.js\";\n\nexport interface EmailPreviewOptions {\n /** Template directory relative to the Vite root. Defaults to `emails`. */\n dir?: string;\n /** URL prefix for the interface and its API. Defaults to `/__email`. */\n base?: string;\n /** Rendering timeout in milliseconds. Defaults to 10000. */\n renderTimeoutMs?: number;\n}\n\n/** @internal Lets source tests and the package's own development serve another build. */\nexport interface InternalOptions extends EmailPreviewOptions {\n interfaceDirectory?: string;\n}\n\nfunction normalizeBase(base: string): string {\n const value = `/${base.replace(/^\\/+|\\/+$/g, \"\")}/`.replace(/^\\/\\/$/, \"/\");\n if (!/^\\/(?:[\\w.-]+\\/)*$/.test(value)) {\n throw new Error(`Invalid email preview base: ${base}`);\n }\n return value;\n}\n\n/** Adds the React Email preview to a Vite dev server. Builds are unaffected. */\nexport function emailPreview(options: EmailPreviewOptions = {}): Plugin {\n const {\n dir = \"emails\",\n renderTimeoutMs = 10_000,\n interfaceDirectory = fileURLToPath(new URL(\"./ui\", import.meta.url)),\n } = options as InternalOptions;\n const base = normalizeBase(options.base ?? \"/__email\");\n let runtime: Promise<PreviewRuntime> | undefined;\n\n return {\n name: \"raypx-email-preview\",\n apply: \"serve\",\n // Lets tests and cooperating plugins reach the running preview.\n api: { runtime: () => runtime },\n config() {\n return {\n environments: {\n // Templates get their own module graph, so rendering never loads into or\n // invalidates the application's SSR environment.\n [environmentName]: {\n consumer: \"server\",\n resolve: {\n dedupe: [\"react\", \"react-dom\"],\n external: true,\n // Bundled through Vite so the renderer resolves the project's React.\n noExternal: [\"@react-email/render\"],\n },\n dev: {\n createEnvironment: (name, config) => createRunnableDevEnvironment(name, config),\n },\n },\n },\n };\n },\n resolveId(id) {\n return resolveRenderEntry(id);\n },\n load(id) {\n return loadRenderEntry(id);\n },\n configureServer(server) {\n runtime = PreviewRuntime.create(server, { directory: dir, renderTimeoutMs });\n // Keep a failed start observable through each request instead of crashing.\n runtime.catch(() => {});\n server.middlewares.use(previewMiddleware(base, interfaceDirectory, () => runtime!));\n server.httpServer?.once(\"close\", () => void runtime?.then((value) => value.close()));\n },\n async buildEnd() {\n (await runtime?.catch(() => undefined))?.close();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/server/errors.ts","../src/server/discovery.ts","../src/server/middleware.ts","../src/server/render.ts","../src/server/format.ts","../src/server/static.ts","../src/server/runtime.ts","../src/server/plugin.ts"],"sourcesContent":["export class PreviewRuntimeError extends Error {\n readonly code: string;\n readonly status: number;\n\n constructor(code: string, message: string, status = 500) {\n super(message);\n this.name = \"PreviewRuntimeError\";\n this.code = code;\n this.status = status;\n }\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","import { readdir, realpath, stat } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport type { Template } from \"../types.js\";\nimport { PreviewRuntimeError } from \"./errors.js\";\n\nexport function isContained(root: string, path: string): boolean {\n const name = relative(root, path);\n return name === \"\" || (!isAbsolute(name) && name !== \"..\" && !name.startsWith(`..${sep}`));\n}\n\nexport async function resolveDirectory(cwd: string, directory: string): Promise<string> {\n const requested = resolve(cwd, directory);\n try {\n const path = await realpath(requested);\n if (!(await stat(path)).isDirectory()) throw new Error(\"Not a directory\");\n return path;\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n // A new project can start the preview before its emails folder exists.\n // Resolve existing ancestors so future path checks use one canonical root.\n const parent = dirname(requested);\n if (parent !== requested)\n return resolve(await resolveDirectory(parent, \".\"), basename(requested));\n }\n throw new PreviewRuntimeError(\n \"DIRECTORY_NOT_FOUND\",\n `Email directory not found: ${directory}`,\n 400,\n );\n }\n}\n\n/** Never use an HTTP-supplied path as a filesystem path without this boundary. */\nexport async function resolveContainedFile(root: string, name: string): Promise<string> {\n if (!name || name.includes(\"\\\\\") || name.includes(\"\\0\") || isAbsolute(name)) {\n throw new PreviewRuntimeError(\"NOT_FOUND\", \"File not found\", 404);\n }\n const path = resolve(root, name);\n if (!isContained(root, path)) throw new PreviewRuntimeError(\"NOT_FOUND\", \"File not found\", 404);\n try {\n const canonical = await realpath(path);\n if (!isContained(root, canonical) || !(await stat(canonical)).isFile()) {\n throw new Error(\"Outside directory or not a file\");\n }\n return canonical;\n } catch {\n throw new PreviewRuntimeError(\"NOT_FOUND\", \"File not found\", 404);\n }\n}\n\nexport async function discoverTemplates(root: string): Promise<Template[]> {\n const templates: Template[] = [];\n async function visit(directory: string): Promise<void> {\n const entries = await readdir(directory, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name.startsWith(\".\") || entry.name.startsWith(\"_\") || entry.name === \"static\")\n continue;\n const path = resolve(directory, entry.name);\n if (entry.isDirectory()) {\n await visit(path);\n } else if (entry.isFile() && /\\.(tsx|jsx)$/.test(entry.name)) {\n const id = relative(root, path).split(sep).join(\"/\");\n const slash = id.lastIndexOf(\"/\");\n templates.push({\n id,\n name: entry.name.replace(/\\.(tsx|jsx)$/, \"\"),\n directory: slash < 0 ? \"\" : id.slice(0, slash),\n });\n }\n }\n }\n try {\n await visit(root);\n } catch (error) {\n if (!(error instanceof Error) || !(\"code\" in error) || error.code !== \"ENOENT\") throw error;\n }\n return templates.sort((left, right) => left.id.localeCompare(right.id));\n}\n","import { readFile } from \"node:fs/promises\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { extname } from \"node:path\";\nimport { resolveContainedFile } from \"./discovery.js\";\nimport { PreviewRuntimeError } from \"./errors.js\";\nimport type { PreviewRuntime } from \"./runtime.js\";\n\ntype Next = (error?: unknown) => void;\n\nconst noCache = { \"Cache-Control\": \"no-store\", \"X-Content-Type-Options\": \"nosniff\" };\n\nconst interfaceTypes: Readonly<Record<string, string>> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".woff2\": \"font/woff2\",\n};\n\nfunction send(\n response: ServerResponse,\n status: number,\n headers: Record<string, string>,\n body?: string | Uint8Array,\n) {\n response.writeHead(status, headers);\n response.end(body);\n}\n\nfunction sendJson(response: ServerResponse, status: number, value: unknown) {\n send(\n response,\n status,\n { ...noCache, \"Content-Type\": \"application/json; charset=utf-8\" },\n JSON.stringify(value),\n );\n}\n\nfunction sendError(response: ServerResponse, error: unknown) {\n const expected = error instanceof PreviewRuntimeError;\n sendJson(response, expected ? error.status : 500, {\n error: {\n code: expected ? error.code : \"INTERNAL_ERROR\",\n message: expected\n ? error.message\n : \"The preview request failed. Try again after checking the template.\",\n },\n });\n}\n\nfunction openEvents(runtime: PreviewRuntime, request: IncomingMessage, response: ServerResponse) {\n response.writeHead(200, {\n ...noCache,\n \"Content-Type\": \"text/event-stream\",\n Connection: \"keep-alive\",\n \"X-Accel-Buffering\": \"no\",\n });\n const send = (event: string, revision: number) =>\n response.write(`event: ${event}\\ndata: ${JSON.stringify({ revision })}\\n\\n`);\n const unsubscribe = runtime.subscribe((revision) => send(\"change\", revision));\n const heartbeat = setInterval(() => response.write(\": heartbeat\\n\\n\"), 15000);\n heartbeat.unref();\n let untrack = () => {};\n const close = () => {\n clearInterval(heartbeat);\n unsubscribe();\n untrack();\n if (!response.writableEnded) response.end();\n };\n untrack = runtime.trackStream(close);\n request.once(\"close\", close);\n send(\"ready\", runtime.revision);\n}\n\n/**\n * Serves the prebuilt interface and its API below `base`, which ends with a slash.\n * Requests outside `base` continue to the host server's own middleware.\n */\nexport function previewMiddleware(\n base: string,\n interfaceDirectory: string,\n getRuntime: () => Promise<PreviewRuntime>,\n) {\n let indexHtml: Promise<string> | undefined;\n const readIndex = () =>\n (indexHtml ??= readFile(`${interfaceDirectory}/index.html`, \"utf8\").then(\n // Relative asset and API URLs resolve against the mount point.\n (html) => html.replace(\"<head>\", `<head><base href=\"${base}\" />`),\n () => {\n indexHtml = undefined;\n throw new PreviewRuntimeError(\n \"NOT_FOUND\",\n \"The preview interface has not been built.\",\n 404,\n );\n },\n ));\n\n return async (request: IncomingMessage, response: ServerResponse, next: Next) => {\n const url = new URL(request.url ?? \"/\", \"http://localhost\");\n const mount = base.slice(0, -1);\n if (mount && url.pathname === mount) {\n send(response, 302, { Location: `${base}${url.search}` });\n return;\n }\n if (!url.pathname.startsWith(base)) return next();\n if (request.method !== \"GET\" && request.method !== \"HEAD\") return next();\n const path = url.pathname.slice(base.length);\n try {\n if (path === \"api/templates\") {\n sendJson(response, 200, await (await getRuntime()).listTemplates());\n } else if (path === \"api/preview\") {\n const template = url.searchParams.get(\"template\");\n if (!template) {\n throw new PreviewRuntimeError(\"INVALID_TEMPLATE\", \"Select an email template.\", 400);\n }\n sendJson(response, 200, await (await getRuntime()).renderTemplate(template));\n } else if (path === \"api/events\") {\n openEvents(await getRuntime(), request, response);\n } else if (path.startsWith(\"static/\")) {\n const file = await (await getRuntime()).getStaticFile(decodeURIComponent(path.slice(7)));\n send(\n response,\n 200,\n { ...noCache, \"Content-Type\": file.contentType, \"Content-Security-Policy\": \"sandbox\" },\n file.data,\n );\n } else if (path === \"\" || path === \"index.html\") {\n send(\n response,\n 200,\n { ...noCache, \"Content-Type\": interfaceTypes[\".html\"]! },\n await readIndex(),\n );\n } else if (path.startsWith(\"assets/\")) {\n const file = await resolveContainedFile(interfaceDirectory, path).catch(() => undefined);\n // Unknown paths belong to the host server, which matters when `base` is `/`.\n if (!file) return next();\n send(\n response,\n 200,\n {\n \"Cache-Control\": \"public, max-age=31536000, immutable\",\n \"Content-Type\": interfaceTypes[extname(file)] ?? \"application/octet-stream\",\n \"X-Content-Type-Options\": \"nosniff\",\n },\n await readFile(file),\n );\n } else {\n return next();\n }\n } catch (error) {\n if (response.headersSent) response.destroy();\n else sendError(response, error);\n }\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** The Vite environment that evaluates templates, separate from the application's `ssr`. */\nexport const environmentName = \"email_preview\";\n\nconst entryPrefix = \"virtual:email-preview-render:\";\nconst resolvedEntryPrefix = `\\0${entryPrefix}`;\n// Resolved from this package, which depends on the renderer; the project may not.\n// The generated entry imports it by path inside the project's Vite environment, so\n// its `react` import resolves to the same React as the template.\nconst rendererPath = fileURLToPath(import.meta.resolve(\"@react-email/render\"));\n\nexport function renderEntryId(template: string): string {\n return `${entryPrefix}${template}`;\n}\n\nexport function resolveRenderEntry(id: string): string | undefined {\n return id.startsWith(entryPrefix) ? `\\0${id}` : undefined;\n}\n\n/** Renders one template with its `PreviewProps`, importing React through the project. */\nexport function loadRenderEntry(id: string): string | undefined {\n if (!id.startsWith(resolvedEntryPrefix)) return;\n const template = id.slice(resolvedEntryPrefix.length);\n return `import { createElement } from \"react\";\nimport { render, toPlainText } from ${JSON.stringify(rendererPath)};\nimport * as email from ${JSON.stringify(template)};\nexport async function renderEmail() {\n const Component = email.default;\n if (!Component || (typeof Component !== \"function\" && typeof Component !== \"object\")) {\n throw new Error(\"The email template must default-export a React component.\");\n }\n const html = await render(createElement(Component, Component.PreviewProps ?? {}));\n return { html, text: toPlainText(html) };\n}`;\n}\n\n/**\n * Reads the project's manifest straight from disk. Bun caches failed module\n * lookups in-process, so `require.resolve` would miss a later install.\n */\nasync function readProjectManifest(name: string, from: string): Promise<{ version?: string }> {\n for (let directory = dirname(from); ; directory = dirname(directory)) {\n try {\n return JSON.parse(\n await readFile(join(directory, \"node_modules\", name, \"package.json\"), \"utf8\"),\n );\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n }\n if (dirname(directory) === directory) return {};\n }\n}\n\nexport async function assertReact(template: string): Promise<void> {\n for (const name of [\"react\", \"react-dom\"]) {\n const manifest = await readProjectManifest(name, template);\n if (!manifest.version?.startsWith(\"19.\")) {\n throw new Error(`Install ${name} 19 in the project containing your email templates.`);\n }\n }\n}\n","import * as html from \"prettier/plugins/html\";\nimport { format } from \"prettier/standalone\";\n\n/**\n * Indents rendered HTML for the source view. The preview, clipboard and downloads\n * keep the original output, so markup Prettier cannot parse is shown unchanged.\n */\nexport async function formatHtml(source: string): Promise<string> {\n try {\n return await format(source, {\n parser: \"html\",\n plugins: [html],\n endOfLine: \"lf\",\n tabWidth: 2,\n bracketSameLine: true,\n });\n } catch {\n return source;\n }\n}\n","import { readFile, realpath } from \"node:fs/promises\";\nimport { extname, resolve } from \"node:path\";\nimport { isContained, resolveContainedFile } from \"./discovery.js\";\nimport { PreviewRuntimeError } from \"./errors.js\";\n\nconst contentTypes: Readonly<Record<string, string>> = {\n \".avif\": \"image/avif\",\n \".css\": \"text/css; charset=utf-8\",\n \".gif\": \"image/gif\",\n \".ico\": \"image/x-icon\",\n \".jpeg\": \"image/jpeg\",\n \".jpg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".svg\": \"image/svg+xml\",\n \".ttf\": \"font/ttf\",\n \".webp\": \"image/webp\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n};\n\nexport async function readStaticFile(\n directory: string,\n name: string,\n): Promise<{ data: Uint8Array<ArrayBuffer>; contentType: string }> {\n let root: string;\n try {\n root = await realpath(resolve(directory, \"static\"));\n } catch {\n throw new PreviewRuntimeError(\"NOT_FOUND\", \"Static file not found\", 404);\n }\n if (!isContained(directory, root)) {\n throw new PreviewRuntimeError(\"NOT_FOUND\", \"Static file not found\", 404);\n }\n const path = await resolveContainedFile(root, name);\n return {\n data: await readFile(path),\n contentType: contentTypes[extname(path).toLowerCase()] ?? \"application/octet-stream\",\n };\n}\n","import { statSync } from \"node:fs\";\nimport { basename, resolve } from \"node:path\";\nimport { isRunnableDevEnvironment } from \"vite\";\nimport type { RunnableDevEnvironment, ViteDevServer } from \"vite\";\nimport type { PreviewResult, TemplateList } from \"../types.js\";\nimport {\n discoverTemplates,\n isContained,\n resolveContainedFile,\n resolveDirectory,\n} from \"./discovery.js\";\nimport { errorMessage, PreviewRuntimeError } from \"./errors.js\";\nimport { formatHtml } from \"./format.js\";\nimport { assertReact, environmentName, renderEntryId } from \"./render.js\";\nimport { readStaticFile } from \"./static.js\";\n\nexport interface RuntimeOptions {\n /** Template directory, relative to the Vite root unless absolute. */\n directory: string;\n renderTimeoutMs: number;\n /**\n * Vite's watcher drops a second change to the same file within 50 ms, which would\n * leave a failed render cached until an unrelated edit. Each changed file is compared\n * again after this window. Defaults to 75 ms; tests widen it.\n */\n missedChangeWindowMs?: number;\n}\n\ntype RenderedEmail = Pick<PreviewResult, \"html\" | \"text\">;\n\nfunction fileSignature(path: string): string {\n const stats = statSync(path, { throwIfNoEntry: false });\n return stats ? `${stats.mtimeMs}:${stats.size}` : \"missing\";\n}\n\nfunction isRenderedEmail(value: unknown): value is RenderedEmail {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"html\" in value &&\n typeof value.html === \"string\" &&\n \"text\" in value &&\n typeof value.text === \"string\"\n );\n}\n\nexport class PreviewRuntime {\n private readonly listeners = new Set<(revision: number) => void>();\n private readonly streams = new Set<() => void>();\n private readonly rechecks = new Map<string, ReturnType<typeof setTimeout>>();\n private notification: ReturnType<typeof setTimeout> | undefined;\n private closed = false;\n private currentRevision = 0;\n\n private constructor(\n private readonly server: ViteDevServer,\n readonly directory: string,\n private readonly timeoutMs: number,\n private readonly recheckMs: number,\n ) {}\n\n static async create(server: ViteDevServer, options: RuntimeOptions): Promise<PreviewRuntime> {\n if (!Number.isFinite(options.renderTimeoutMs) || options.renderTimeoutMs < 1) {\n throw new PreviewRuntimeError(\n \"INVALID_OPTIONS\",\n \"renderTimeoutMs must be a positive number.\",\n 400,\n );\n }\n const directory = await resolveDirectory(server.config.root, options.directory);\n const runtime = new PreviewRuntime(\n server,\n directory,\n options.renderTimeoutMs,\n options.missedChangeWindowMs ?? 75,\n );\n // The template directory may sit outside the Vite root, for example in a sibling package.\n server.watcher.add(directory);\n server.watcher.on(\"all\", runtime.onFileEvent);\n return runtime;\n }\n\n private get environment(): RunnableDevEnvironment {\n const environment = this.server.environments[environmentName];\n if (!environment || !isRunnableDevEnvironment(environment)) {\n throw new PreviewRuntimeError(\n \"NOT_INITIALIZED\",\n \"The email preview environment is not available.\",\n 503,\n );\n }\n return environment;\n }\n\n private readonly onFileEvent = (_event: string, file: string): void => {\n const path = resolve(file);\n const environment = this.server.environments[environmentName];\n if (\n !isContained(this.directory, path) &&\n basename(path) !== \"tsconfig.json\" &&\n !environment?.moduleGraph.getModulesByFile(path)?.size\n ) {\n return;\n }\n this.refresh();\n const seen = fileSignature(path);\n clearTimeout(this.rechecks.get(path));\n this.rechecks.set(\n path,\n setTimeout(() => {\n this.rechecks.delete(path);\n if (!this.closed && fileSignature(path) !== seen) this.refresh();\n }, this.recheckMs),\n );\n };\n\n private refresh(): void {\n // Vite invalidates changed modules asynchronously. Template graphs are small,\n // so drop them now and the next render can never observe stale code.\n const environment = this.server.environments[environmentName];\n if (environment && isRunnableDevEnvironment(environment)) {\n environment.moduleGraph.invalidateAll();\n environment.runner.clearCache();\n }\n this.invalidate();\n }\n\n get revision(): number {\n return this.currentRevision;\n }\n\n private invalidate(): void {\n if (this.closed) return;\n this.currentRevision += 1;\n if (this.notification) clearTimeout(this.notification);\n this.notification = setTimeout(() => {\n this.notification = undefined;\n for (const listener of this.listeners) {\n try {\n listener(this.currentRevision);\n } catch {\n this.listeners.delete(listener);\n }\n }\n }, 80);\n }\n\n private assertOpen(): void {\n if (this.closed) throw new PreviewRuntimeError(\"CLOSED\", \"The preview server is closing.\", 503);\n }\n\n async listTemplates(): Promise<TemplateList> {\n this.assertOpen();\n return {\n directory: this.directory,\n templates: await discoverTemplates(this.directory),\n revision: this.revision,\n };\n }\n\n async renderTemplate(id: string): Promise<PreviewResult> {\n this.assertOpen();\n const { templates } = await this.listTemplates();\n if (!templates.some((template) => template.id === id)) {\n throw new PreviewRuntimeError(\"TEMPLATE_NOT_FOUND\", \"Email template not found.\", 404);\n }\n const path = await resolveContainedFile(this.directory, id);\n try {\n await assertReact(path);\n } catch (error) {\n throw new PreviewRuntimeError(\"DEPENDENCY_ERROR\", errorMessage(error), 422);\n }\n for (let attempt = 0; attempt < 3; attempt += 1) {\n this.assertOpen();\n const revision = this.revision;\n const result = await this.render(path);\n if (this.revision === revision) {\n return { id, revision, ...result, formattedHtml: await formatHtml(result.html) };\n }\n }\n throw new PreviewRuntimeError(\n \"TEMPLATE_CHANGED\",\n \"The template changed while rendering. Please try again.\",\n 409,\n );\n }\n\n private async render(path: string): Promise<RenderedEmail> {\n let entry: { renderEmail?: unknown };\n try {\n entry = await this.withTimeout(this.environment.runner.import(renderEntryId(path)));\n } catch (error) {\n if (error instanceof PreviewRuntimeError) throw error;\n throw new PreviewRuntimeError(\"COMPILE_ERROR\", errorMessage(error), 422);\n }\n try {\n if (typeof entry.renderEmail !== \"function\") throw new Error(\"Rendering failed to load.\");\n const value: unknown = await this.withTimeout(Promise.resolve(entry.renderEmail()));\n if (!isRenderedEmail(value)) throw new Error(\"The template returned an invalid result.\");\n return value;\n } catch (error) {\n if (error instanceof PreviewRuntimeError) throw error;\n throw new PreviewRuntimeError(\"RENDER_ERROR\", errorMessage(error), 422);\n }\n }\n\n /**\n * Templates run in the dev server process. A timeout reports work that never\n * settles; it cannot interrupt a synchronous loop.\n */\n private withTimeout<T>(work: Promise<T>): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () =>\n reject(\n new PreviewRuntimeError(\n \"RENDER_TIMEOUT\",\n `Template rendering exceeded ${this.timeoutMs} ms.`,\n 422,\n ),\n ),\n this.timeoutMs,\n );\n });\n return Promise.race([work, timeout]).finally(() => clearTimeout(timer));\n }\n\n subscribe(listener: (revision: number) => void): () => void {\n this.assertOpen();\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /** Registers an open event stream so shutdown can end it. */\n trackStream(close: () => void): () => void {\n this.streams.add(close);\n return () => this.streams.delete(close);\n }\n\n async getStaticFile(\n path: string,\n ): Promise<{ data: Uint8Array<ArrayBuffer>; contentType: string }> {\n this.assertOpen();\n return readStaticFile(this.directory, path);\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n if (this.notification) clearTimeout(this.notification);\n for (const timer of this.rechecks.values()) clearTimeout(timer);\n this.rechecks.clear();\n this.server.watcher.off(\"all\", this.onFileEvent);\n for (const close of this.streams) close();\n this.streams.clear();\n this.listeners.clear();\n }\n}\n","import { fileURLToPath } from \"node:url\";\nimport { createRunnableDevEnvironment } from \"vite\";\nimport type { Plugin } from \"vite\";\nimport { previewMiddleware } from \"./middleware.js\";\nimport { environmentName, loadRenderEntry, resolveRenderEntry } from \"./render.js\";\nimport { PreviewRuntime } from \"./runtime.js\";\n\nexport interface EmailPreviewOptions {\n /** Template directory relative to the Vite root. Defaults to `emails`. */\n dir?: string;\n /** URL prefix for the interface and its API. Defaults to `/__email`. */\n base?: string;\n /** Rendering timeout in milliseconds. Defaults to 10000. */\n renderTimeoutMs?: number;\n}\n\n/** @internal Lets source tests and the package's own development serve another build. */\nexport interface InternalOptions extends EmailPreviewOptions {\n interfaceDirectory?: string;\n}\n\nfunction normalizeBase(base: string): string {\n const value = `/${base.replace(/^\\/+|\\/+$/g, \"\")}/`.replace(/^\\/\\/$/, \"/\");\n if (!/^\\/(?:[\\w.-]+\\/)*$/.test(value)) {\n throw new Error(`Invalid email preview base: ${base}`);\n }\n return value;\n}\n\n/** Adds the React Email preview to a Vite dev server. Builds are unaffected. */\nexport function emailPreview(options: EmailPreviewOptions = {}): Plugin {\n const {\n dir = \"emails\",\n renderTimeoutMs = 10_000,\n interfaceDirectory = fileURLToPath(new URL(\"./ui\", import.meta.url)),\n } = options as InternalOptions;\n const base = normalizeBase(options.base ?? \"/__email\");\n let runtime: Promise<PreviewRuntime> | undefined;\n\n return {\n name: \"raypx-email-preview\",\n apply: \"serve\",\n // Lets tests and cooperating plugins reach the running preview.\n api: { runtime: () => runtime },\n config() {\n return {\n environments: {\n // Templates get their own module graph, so rendering never loads into or\n // invalidates the application's SSR environment.\n [environmentName]: {\n consumer: \"server\",\n resolve: {\n dedupe: [\"react\", \"react-dom\"],\n external: true,\n // Bundled through Vite so the renderer resolves the project's React.\n noExternal: [\"@react-email/render\"],\n },\n dev: {\n createEnvironment: (name, config) => createRunnableDevEnvironment(name, config),\n },\n },\n },\n };\n },\n resolveId(id) {\n return resolveRenderEntry(id);\n },\n load(id) {\n return loadRenderEntry(id);\n },\n configureServer(server) {\n runtime = PreviewRuntime.create(server, { directory: dir, renderTimeoutMs });\n // Keep a failed start observable through each request instead of crashing.\n runtime.catch(() => {});\n server.middlewares.use(previewMiddleware(base, interfaceDirectory, () => runtime!));\n server.httpServer?.once(\"close\", () => void runtime?.then((value) => value.close()));\n },\n async buildEnd() {\n (await runtime?.catch(() => undefined))?.close();\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raypx-kit/email-preview",
3
- "version": "0.0.0-alpha.0",
3
+ "version": "0.0.0-alpha.1",
4
4
  "description": "Preview React Email templates in a Vite dev server or a standalone CLI.",
5
5
  "homepage": "https://github.com/raypx/raypx-kit/tree/canary/packages/email-preview",
6
6
  "license": "Apache-2.0",
@@ -51,9 +51,9 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "@playwright/test": "^1.64.1",
54
- "@raypx-kit/themes": "workspace:*",
55
- "@raypx-kit/tsconfig": "workspace:*",
56
- "@raypx-kit/ui": "workspace:*",
54
+ "@raypx-kit/themes": "0.0.0-alpha.0",
55
+ "@raypx-kit/tsconfig": "0.0.0-alpha.1",
56
+ "@raypx-kit/ui": "0.0.0-alpha.0",
57
57
  "@react-email/components": "^1.0.12",
58
58
  "@tailwindcss/vite": "^4.3.3",
59
59
  "@tanstack/react-router": "^1.170.45",