react-email 6.10.0 → 6.11.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 +13 -0
- package/dist/cli/index.mjs +99 -25
- package/dist/components/markdown/markdown.cjs +2 -1
- package/dist/components/markdown/markdown.mjs +2 -1
- package/package.json +1 -1
- package/readme.md +8 -0
- package/src/components/markdown/markdown.spec.tsx +29 -0
- package/src/components/markdown/markdown.tsx +4 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# react-email
|
|
2
2
|
|
|
3
|
+
## 6.11.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 54769fc: Prevent `email export` from deleting the project, the home directory, the email templates or the static assets when `--outDir` points at them.
|
|
8
|
+
- 6926bac: `<Markdown>` now applies `markdownCustomStyles.th` to table header cells. Header cells were previously styled with `td`, so any `th` styles were ignored.
|
|
9
|
+
|
|
10
|
+
## 6.11.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- 0432f96: Use custom esbuild plugins by passing the new `--esbuild-plugins` flag to `email dev`, `email build` and `email export`, so templates that need a custom compile-time transform can be previewed and exported.
|
|
15
|
+
|
|
3
16
|
## 6.10.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/dist/cli/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import * as fs$1 from "node:fs";
|
|
|
6
6
|
import fs, { existsSync, promises, statSync } from "node:fs";
|
|
7
7
|
import * as path$2 from "node:path";
|
|
8
8
|
import path from "node:path";
|
|
9
|
-
import url, { fileURLToPath } from "node:url";
|
|
9
|
+
import url, { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
10
|
import logSymbols from "log-symbols";
|
|
11
11
|
import { addDevDependency, installDependencies, runScript } from "nypm";
|
|
12
12
|
import * as fsp$1 from "node:fs/promises";
|
|
@@ -6533,7 +6533,7 @@ const getTracingRootDir = async (usersProjectLocation) => {
|
|
|
6533
6533
|
//#region package.json
|
|
6534
6534
|
var package_default = {
|
|
6535
6535
|
name: "react-email",
|
|
6536
|
-
version: "6.
|
|
6536
|
+
version: "6.11.1",
|
|
6537
6537
|
description: "A live preview of your emails right in your browser.",
|
|
6538
6538
|
bin: { "email": "./dist/cli/index.mjs" },
|
|
6539
6539
|
type: "module",
|
|
@@ -6739,8 +6739,9 @@ const stopSpinnerAndPersist = (spinner, display) => {
|
|
|
6739
6739
|
//#endregion
|
|
6740
6740
|
//#region src/cli/commands/build.ts
|
|
6741
6741
|
const isInReactEmailMonorepo = !path.dirname(fileURLToPath(import.meta.url)).includes("node_modules");
|
|
6742
|
-
const setNextEnvironmentVariablesForBuild = async (emailsDirRelativePath, builtPreviewAppPath, usersProjectLocation) => {
|
|
6742
|
+
const setNextEnvironmentVariablesForBuild = async (emailsDirRelativePath, builtPreviewAppPath, usersProjectLocation, esbuildPluginsPath) => {
|
|
6743
6743
|
const rootDir = await getTracingRootDir(usersProjectLocation);
|
|
6744
|
+
const esbuildPluginsAbsolutePath = esbuildPluginsPath ? path.resolve(usersProjectLocation, esbuildPluginsPath).replaceAll("\\", "/") : void 0;
|
|
6744
6745
|
const nextConfigContents = `
|
|
6745
6746
|
import path from 'path';
|
|
6746
6747
|
const emailsDirRelativePath = path.normalize('${emailsDirRelativePath}');
|
|
@@ -6754,13 +6755,14 @@ const nextConfig = {
|
|
|
6754
6755
|
REACT_EMAIL_INTERNAL_EMAILS_DIR_RELATIVE_PATH: emailsDirRelativePath,
|
|
6755
6756
|
REACT_EMAIL_INTERNAL_EMAILS_DIR_ABSOLUTE_PATH: path.resolve(userProjectLocation, emailsDirRelativePath),
|
|
6756
6757
|
REACT_EMAIL_INTERNAL_PREVIEW_SERVER_LOCATION: previewServerLocation,
|
|
6757
|
-
REACT_EMAIL_INTERNAL_USER_PROJECT_LOCATION: userProjectLocation
|
|
6758
|
+
REACT_EMAIL_INTERNAL_USER_PROJECT_LOCATION: userProjectLocation,
|
|
6759
|
+
${esbuildPluginsAbsolutePath ? `REACT_EMAIL_INTERNAL_ESBUILD_PLUGINS_PATH: '${esbuildPluginsAbsolutePath}',` : ""}
|
|
6758
6760
|
},
|
|
6759
6761
|
turbopack: {
|
|
6760
6762
|
root: rootDir,
|
|
6761
6763
|
},
|
|
6762
6764
|
outputFileTracingRoot: rootDir,
|
|
6763
|
-
serverExternalPackages: ['esbuild'],
|
|
6765
|
+
serverExternalPackages: ['esbuild', 'jiti'],
|
|
6764
6766
|
typescript: {
|
|
6765
6767
|
ignoreBuildErrors: true
|
|
6766
6768
|
},
|
|
@@ -6803,7 +6805,7 @@ const updatePackageJson = async (builtUiPath) => {
|
|
|
6803
6805
|
for (const [dependency, version] of Object.entries(packageJson.devDependencies)) packageJson.devDependencies[dependency] = version.replace("workspace:", "");
|
|
6804
6806
|
await fs.promises.writeFile(packageJsonPath, JSON.stringify(packageJson), "utf8");
|
|
6805
6807
|
};
|
|
6806
|
-
const build$1 = async ({ dir: emailsDirRelativePath, packageManager }) => {
|
|
6808
|
+
const build$1 = async ({ dir: emailsDirRelativePath, packageManager, esbuildPlugins }) => {
|
|
6807
6809
|
if (packageManager) console.warn("The --packageManager option is deprecated and ignored. The build command now just uses npm.");
|
|
6808
6810
|
try {
|
|
6809
6811
|
const usersProjectLocation = process.cwd();
|
|
@@ -6837,7 +6839,7 @@ const build$1 = async ({ dir: emailsDirRelativePath, packageManager }) => {
|
|
|
6837
6839
|
await fs.promises.cp(staticPath, builtStaticDirectory, { recursive: true });
|
|
6838
6840
|
}
|
|
6839
6841
|
spinner.setText("Setting Next environment variables for preview app to work properly");
|
|
6840
|
-
await setNextEnvironmentVariablesForBuild(emailsDirRelativePath, builtPreviewAppPath, usersProjectLocation);
|
|
6842
|
+
await setNextEnvironmentVariablesForBuild(emailsDirRelativePath, builtPreviewAppPath, usersProjectLocation, esbuildPlugins);
|
|
6841
6843
|
spinner.setText("Setting server side generation for the email preview pages");
|
|
6842
6844
|
await forceSSGForEmailPreviews(emailsDirPath, builtPreviewAppPath);
|
|
6843
6845
|
spinner.setText("Updating package.json's build and start scripts");
|
|
@@ -7232,14 +7234,15 @@ const conf = new Conf({
|
|
|
7232
7234
|
const styleText = nodeUtil.styleText ? nodeUtil.styleText : (_, text) => text;
|
|
7233
7235
|
//#endregion
|
|
7234
7236
|
//#region src/cli/utils/preview/get-env-variables-for-preview-app.ts
|
|
7235
|
-
const getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, previewServerLocation, cwd, resendApiKey, compatibilityClients) => {
|
|
7237
|
+
const getEnvVariablesForPreviewApp = (relativePathToEmailsDirectory, previewServerLocation, cwd, resendApiKey, compatibilityClients, esbuildPluginsPath) => {
|
|
7236
7238
|
return {
|
|
7237
7239
|
REACT_EMAIL_INTERNAL_EMAILS_DIR_RELATIVE_PATH: relativePathToEmailsDirectory,
|
|
7238
7240
|
REACT_EMAIL_INTERNAL_EMAILS_DIR_ABSOLUTE_PATH: path.resolve(cwd, relativePathToEmailsDirectory),
|
|
7239
7241
|
REACT_EMAIL_INTERNAL_PREVIEW_SERVER_LOCATION: previewServerLocation,
|
|
7240
7242
|
REACT_EMAIL_INTERNAL_USER_PROJECT_LOCATION: cwd,
|
|
7241
7243
|
REACT_EMAIL_INTERNAL_RESEND_API_KEY: resendApiKey,
|
|
7242
|
-
...compatibilityClients !== void 0 && { COMPATIBILITY_EMAIL_CLIENTS: compatibilityClients }
|
|
7244
|
+
...compatibilityClients !== void 0 && { COMPATIBILITY_EMAIL_CLIENTS: compatibilityClients },
|
|
7245
|
+
...esbuildPluginsPath !== void 0 && { REACT_EMAIL_INTERNAL_ESBUILD_PLUGINS_PATH: path.resolve(cwd, esbuildPluginsPath) }
|
|
7243
7246
|
};
|
|
7244
7247
|
};
|
|
7245
7248
|
//#endregion
|
|
@@ -7294,7 +7297,7 @@ const safeAsyncServerListen = (server, port) => {
|
|
|
7294
7297
|
});
|
|
7295
7298
|
});
|
|
7296
7299
|
};
|
|
7297
|
-
const startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, port, compatibilityClients) => {
|
|
7300
|
+
const startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath, port, compatibilityClients, esbuildPluginsPath) => {
|
|
7298
7301
|
const [major = 0, minor = 0] = process.versions.node.split(".").map(Number);
|
|
7299
7302
|
if (major < 20 || major === 20 && minor < 19) {
|
|
7300
7303
|
console.error(` ${logSymbols.error} Node ${process.versions.node} is not supported. Please upgrade to Node 20.19 or higher.`);
|
|
@@ -7328,7 +7331,7 @@ const startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath,
|
|
|
7328
7331
|
} else {
|
|
7329
7332
|
const nextPortToTry = port + 1;
|
|
7330
7333
|
console.warn(` ${logSymbols.warning} Port ${port} is already in use, trying ${nextPortToTry}`);
|
|
7331
|
-
return startDevServer(emailsDirRelativePath, staticBaseDirRelativePath, nextPortToTry, compatibilityClients);
|
|
7334
|
+
return startDevServer(emailsDirRelativePath, staticBaseDirRelativePath, nextPortToTry, compatibilityClients, esbuildPluginsPath);
|
|
7332
7335
|
}
|
|
7333
7336
|
devServer.on("close", async () => {
|
|
7334
7337
|
await app.close();
|
|
@@ -7350,7 +7353,7 @@ const startDevServer = async (emailsDirRelativePath, staticBaseDirRelativePath,
|
|
|
7350
7353
|
process.env = {
|
|
7351
7354
|
NODE_ENV: "development",
|
|
7352
7355
|
...process.env,
|
|
7353
|
-
...getEnvVariablesForPreviewApp(path.normalize(emailsDirRelativePath), previewServerLocation, process.cwd(), conf.get("resendApiKey"), compatibilityClients)
|
|
7356
|
+
...getEnvVariablesForPreviewApp(path.normalize(emailsDirRelativePath), previewServerLocation, process.cwd(), conf.get("resendApiKey"), compatibilityClients, esbuildPluginsPath)
|
|
7354
7357
|
};
|
|
7355
7358
|
if (!process.env.ESBUILD_BINARY_PATH) try {
|
|
7356
7359
|
const esbuild = createJiti(previewServer.esmResolve("esbuild"));
|
|
@@ -7449,13 +7452,13 @@ const tree = async (dirPath, depth) => {
|
|
|
7449
7452
|
};
|
|
7450
7453
|
//#endregion
|
|
7451
7454
|
//#region src/cli/commands/dev.ts
|
|
7452
|
-
const dev = async ({ dir: emailsDirRelativePath, port, clients }) => {
|
|
7455
|
+
const dev = async ({ dir: emailsDirRelativePath, port, clients, esbuildPlugins }) => {
|
|
7453
7456
|
try {
|
|
7454
7457
|
if (!fs.existsSync(emailsDirRelativePath)) {
|
|
7455
7458
|
console.error(`Missing ${emailsDirRelativePath} folder`);
|
|
7456
7459
|
process.exit(1);
|
|
7457
7460
|
}
|
|
7458
|
-
await setupHotreloading(await startDevServer(emailsDirRelativePath, emailsDirRelativePath, Number.parseInt(port, 10), clients), emailsDirRelativePath);
|
|
7461
|
+
await setupHotreloading(await startDevServer(emailsDirRelativePath, emailsDirRelativePath, Number.parseInt(port, 10), clients, esbuildPlugins), emailsDirRelativePath);
|
|
7459
7462
|
} catch (error) {
|
|
7460
7463
|
console.log(error);
|
|
7461
7464
|
process.exit(1);
|
|
@@ -7489,12 +7492,26 @@ const inlineCssLoader = () => ({
|
|
|
7489
7492
|
}
|
|
7490
7493
|
});
|
|
7491
7494
|
//#endregion
|
|
7495
|
+
//#region src/cli/utils/esbuild/load-esbuild-plugins.ts
|
|
7496
|
+
/**
|
|
7497
|
+
* Loads the esbuild plugins a user wants applied when their templates are
|
|
7498
|
+
* bundled. The module's default export is either an array of plugins or a
|
|
7499
|
+
* function returning one, so plugins that need setup can do it lazily.
|
|
7500
|
+
*/
|
|
7501
|
+
const loadEsbuildPlugins = async (modulePath) => {
|
|
7502
|
+
const exported = await createJiti(pathToFileURL(modulePath).href).import(modulePath, { default: true });
|
|
7503
|
+
const plugins = typeof exported === "function" ? await exported() : exported;
|
|
7504
|
+
if (!Array.isArray(plugins)) throw new Error(`Expected the default export of ${modulePath} to be an array of esbuild plugins or a function returning one`);
|
|
7505
|
+
return plugins;
|
|
7506
|
+
};
|
|
7507
|
+
//#endregion
|
|
7492
7508
|
//#region src/cli/utils/esbuild/escape-string-for-regex.ts
|
|
7493
7509
|
function escapeStringForRegex(string) {
|
|
7494
7510
|
return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
|
|
7495
7511
|
}
|
|
7496
7512
|
//#endregion
|
|
7497
7513
|
//#region src/cli/utils/esbuild/renderring-utilities-exporter.ts
|
|
7514
|
+
const ENTRY_SUFFIX = "?react-email-entry";
|
|
7498
7515
|
/**
|
|
7499
7516
|
* Made to export the `render` function out of the user's email template
|
|
7500
7517
|
* so that issues like https://github.com/resend/react-email/issues/649 don't
|
|
@@ -7505,17 +7522,39 @@ function escapeStringForRegex(string) {
|
|
|
7505
7522
|
*
|
|
7506
7523
|
* This avoids multiple versions of React being involved, i.e., the version
|
|
7507
7524
|
* in the CLI vs. the version the user has on their emails.
|
|
7525
|
+
*
|
|
7526
|
+
* Each template entry point is replaced by a wrapper module that re-exports
|
|
7527
|
+
* it along with the utilities, instead of loading the template's source here.
|
|
7528
|
+
* The template itself then goes through the regular load pipeline, so other
|
|
7529
|
+
* plugins (for example the user's `--esbuild-plugins`) can still transform it.
|
|
7508
7530
|
*/
|
|
7509
7531
|
const renderingUtilitiesExporter = (emailTemplates) => ({
|
|
7510
7532
|
name: "rendering-utilities-exporter",
|
|
7511
|
-
setup: (b) => {
|
|
7512
|
-
|
|
7533
|
+
setup: async (b) => {
|
|
7534
|
+
const templatePaths = await Promise.all(emailTemplates.map((emailPath) => promises.realpath(emailPath)));
|
|
7535
|
+
const templateFilter = new RegExp(templatePaths.map((templatePath) => escapeStringForRegex(templatePath)).join("|"));
|
|
7536
|
+
b.onResolve({ filter: /.*/ }, async (args) => {
|
|
7537
|
+
if (args.kind !== "entry-point") return null;
|
|
7538
|
+
const resolvedPath = path.isAbsolute(args.path) ? args.path : path.resolve(args.resolveDir, args.path);
|
|
7539
|
+
const realPath = await promises.realpath(resolvedPath);
|
|
7540
|
+
if (!templatePaths.includes(realPath)) return null;
|
|
7513
7541
|
return {
|
|
7514
|
-
|
|
7515
|
-
|
|
7542
|
+
path: realPath,
|
|
7543
|
+
suffix: ENTRY_SUFFIX
|
|
7544
|
+
};
|
|
7545
|
+
});
|
|
7546
|
+
b.onLoad({ filter: templateFilter }, ({ path: pathToFile, suffix }) => {
|
|
7547
|
+
if (suffix !== ENTRY_SUFFIX) return null;
|
|
7548
|
+
const templateSpecifier = JSON.stringify(pathToFile);
|
|
7549
|
+
return {
|
|
7550
|
+
contents: `export * from ${templateSpecifier};
|
|
7551
|
+
import * as emailModule from ${templateSpecifier};
|
|
7552
|
+
export default emailModule.default;
|
|
7553
|
+
export { render } from 'react-email-module-that-will-export-render';
|
|
7516
7554
|
export { createElement as reactEmailCreateReactElement } from 'react';
|
|
7517
7555
|
`,
|
|
7518
|
-
loader:
|
|
7556
|
+
loader: "js",
|
|
7557
|
+
resolveDir: path.dirname(pathToFile)
|
|
7519
7558
|
};
|
|
7520
7559
|
});
|
|
7521
7560
|
b.onResolve({ filter: /^react-email-module-that-will-export-render$/ }, async (args) => {
|
|
@@ -7541,6 +7580,19 @@ const getEmailTemplatesFromDirectory = (emailDirectory) => {
|
|
|
7541
7580
|
for (const directory of emailDirectory.subDirectories) templatePaths.push(...getEmailTemplatesFromDirectory(directory));
|
|
7542
7581
|
return templatePaths;
|
|
7543
7582
|
};
|
|
7583
|
+
const isInside = (parent, child) => child.startsWith(`${parent}${path.sep}`);
|
|
7584
|
+
const getUnsafeOutDirMessage = (outDir, emailsDirectoryPath, { cwd, homeDirectory }) => {
|
|
7585
|
+
const resolvedOutDir = path.resolve(cwd, outDir);
|
|
7586
|
+
const resolvedEmailsDirectory = path.resolve(cwd, emailsDirectoryPath);
|
|
7587
|
+
const staticDirectory = path.join(resolvedEmailsDirectory, "static");
|
|
7588
|
+
if (resolvedOutDir === path.parse(resolvedOutDir).root) return "--outDir can't be the filesystem root.";
|
|
7589
|
+
if (resolvedOutDir === homeDirectory) return "--outDir can't be your home directory.";
|
|
7590
|
+
if (isInside(resolvedOutDir, homeDirectory)) return "--outDir can't contain your home directory.";
|
|
7591
|
+
if (resolvedOutDir === cwd) return "--outDir can't be the project directory.";
|
|
7592
|
+
if (isInside(resolvedOutDir, cwd)) return "--outDir can't contain the project directory.";
|
|
7593
|
+
if (resolvedOutDir === resolvedEmailsDirectory || isInside(resolvedOutDir, resolvedEmailsDirectory)) return "--outDir can't contain your email templates.";
|
|
7594
|
+
if (resolvedOutDir === staticDirectory || isInside(staticDirectory, resolvedOutDir)) return "--outDir can't be your static assets directory.";
|
|
7595
|
+
};
|
|
7544
7596
|
const BUILD_BATCH_SIZE = 10;
|
|
7545
7597
|
const RENDER_BATCH_SIZE = 25;
|
|
7546
7598
|
const renderWorkerSource = `
|
|
@@ -7588,9 +7640,26 @@ const exportTemplates = async (pathToWhereEmailMarkupShouldBeDumped, emailsDirec
|
|
|
7588
7640
|
else console.error(`Could not find the directory at ${emailsDirectoryPath}`);
|
|
7589
7641
|
process.exit(1);
|
|
7590
7642
|
}
|
|
7591
|
-
|
|
7643
|
+
const resolvedPathToWhereEmailMarkupShouldBeDumped = path.resolve(process.cwd(), pathToWhereEmailMarkupShouldBeDumped);
|
|
7644
|
+
if (fs.existsSync(resolvedPathToWhereEmailMarkupShouldBeDumped)) {
|
|
7645
|
+
const unsafeOutDirMessage = getUnsafeOutDirMessage(resolvedPathToWhereEmailMarkupShouldBeDumped, emailsDirectoryPath, {
|
|
7646
|
+
cwd: process.cwd(),
|
|
7647
|
+
homeDirectory: os.homedir()
|
|
7648
|
+
});
|
|
7649
|
+
if (unsafeOutDirMessage) {
|
|
7650
|
+
const message = `${unsafeOutDirMessage} Choose a different --outDir, such as "out".`;
|
|
7651
|
+
if (spinner) stopSpinnerAndPersist(spinner, {
|
|
7652
|
+
symbol: logSymbols.error,
|
|
7653
|
+
text: message
|
|
7654
|
+
});
|
|
7655
|
+
else console.error(message);
|
|
7656
|
+
process.exit(1);
|
|
7657
|
+
}
|
|
7658
|
+
fs.rmSync(resolvedPathToWhereEmailMarkupShouldBeDumped, { recursive: true });
|
|
7659
|
+
}
|
|
7592
7660
|
const allTemplates = getEmailTemplatesFromDirectory(emailsDirectoryMetadata);
|
|
7593
7661
|
try {
|
|
7662
|
+
const userPlugins = options.esbuildPlugins ? await loadEsbuildPlugins(path.resolve(process.cwd(), options.esbuildPlugins)) : [];
|
|
7594
7663
|
for (let i = 0; i < allTemplates.length; i += BUILD_BATCH_SIZE) {
|
|
7595
7664
|
const batch = allTemplates.slice(i, i + BUILD_BATCH_SIZE);
|
|
7596
7665
|
await build({
|
|
@@ -7604,7 +7673,11 @@ const exportTemplates = async (pathToWhereEmailMarkupShouldBeDumped, emailsDirec
|
|
|
7604
7673
|
outExtension: { ".js": ".cjs" },
|
|
7605
7674
|
outdir: pathToWhereEmailMarkupShouldBeDumped,
|
|
7606
7675
|
platform: "node",
|
|
7607
|
-
plugins: [
|
|
7676
|
+
plugins: [
|
|
7677
|
+
inlineCssLoader(),
|
|
7678
|
+
renderingUtilitiesExporter(batch),
|
|
7679
|
+
...userPlugins
|
|
7680
|
+
],
|
|
7608
7681
|
write: true
|
|
7609
7682
|
});
|
|
7610
7683
|
await stop();
|
|
@@ -7788,14 +7861,15 @@ if (!requiredFlags.every((flag) => process.execArgv.includes(flag))) spawn(proce
|
|
|
7788
7861
|
});
|
|
7789
7862
|
else {
|
|
7790
7863
|
program.name("react-email").description("A live preview of your emails right in your browser").version(package_default.version);
|
|
7791
|
-
program.command("dev").description("Starts the preview email development app").option("-d, --dir <path>", "Directory with your email templates", "./emails").option("-p --port <port>", "Port to run dev server on", "3000").option("-c, --clients <clients>", "Comma-separated list of email clients to show compatibility warnings for (overrides COMPATIBILITY_EMAIL_CLIENTS)", parseClientsOption).action(dev);
|
|
7792
|
-
program.command("build").description("Copies the preview app for onto .react-email and builds it").option("-d, --dir <path>", "Directory with your email templates", "./emails").addOption(new Option("-p, --packageManager <name>").hideHelp()).action(build$1);
|
|
7864
|
+
program.command("dev").description("Starts the preview email development app").option("-d, --dir <path>", "Directory with your email templates", "./emails").option("-p --port <port>", "Port to run dev server on", "3000").option("-c, --clients <clients>", "Comma-separated list of email clients to show compatibility warnings for (overrides COMPATIBILITY_EMAIL_CLIENTS)", parseClientsOption).option("--esbuild-plugins <path>", "Path to a module whose default export is an array of esbuild plugins (or a function returning one) applied when bundling email templates").action(dev);
|
|
7865
|
+
program.command("build").description("Copies the preview app for onto .react-email and builds it").option("-d, --dir <path>", "Directory with your email templates", "./emails").addOption(new Option("-p, --packageManager <name>").hideHelp()).option("--esbuild-plugins <path>", "Path to a module whose default export is an array of esbuild plugins (or a function returning one) applied when bundling email templates").action(build$1);
|
|
7793
7866
|
program.command("start").description("Runs the built preview app that is inside of \".react-email\"").action(start);
|
|
7794
|
-
program.command("export").description("Build the templates to the `out` directory").option("--outDir <path>", "Output directory", "out").option("-p, --pretty", "Pretty print the output", false).option("-t, --plainText", "Set output format as plain text", false).option("-d, --dir <path>", "Directory with your email templates", "./emails").option("-e, --extension <extension>", "Set a custom file extension for rendered emails (e.g. blade.php)").option("-s, --silent", "To, or not to show a spinner with process information", false).action(({ outDir, pretty, plainText, silent, dir: srcDir, extension }) => exportTemplates(outDir, srcDir, {
|
|
7867
|
+
program.command("export").description("Build the templates to the `out` directory").option("--outDir <path>", "Output directory", "out").option("-p, --pretty", "Pretty print the output", false).option("-t, --plainText", "Set output format as plain text", false).option("-d, --dir <path>", "Directory with your email templates", "./emails").option("-e, --extension <extension>", "Set a custom file extension for rendered emails (e.g. blade.php)").option("-s, --silent", "To, or not to show a spinner with process information", false).option("--esbuild-plugins <path>", "Path to a module whose default export is an array of esbuild plugins (or a function returning one) applied when bundling email templates").action(({ outDir, pretty, plainText, silent, dir: srcDir, extension, esbuildPlugins }) => exportTemplates(outDir, srcDir, {
|
|
7795
7868
|
silent,
|
|
7796
7869
|
plainText,
|
|
7797
7870
|
pretty,
|
|
7798
|
-
extension
|
|
7871
|
+
extension,
|
|
7872
|
+
esbuildPlugins
|
|
7799
7873
|
}));
|
|
7800
7874
|
const resend = program.command("resend");
|
|
7801
7875
|
resend.command("setup").description("Sets up the integration between the React Email CLI, and your Resend account through an API Key").action(resendSetup);
|
|
@@ -80,7 +80,8 @@ const Markdown = react.forwardRef(({ children, markdownContainerStyles, markdown
|
|
|
80
80
|
renderer.tablecell = ({ tokens, align, header }) => {
|
|
81
81
|
const text = renderer.parser.parseInline(tokens);
|
|
82
82
|
const type = header ? "th" : "td";
|
|
83
|
-
|
|
83
|
+
const style = require_parse_css_in_js_to_inline_css.parseCssInJsToInlineCss(finalStyles[type]);
|
|
84
|
+
return `${`<${type}${align ? ` align="${align}"` : ""}${style !== "" ? ` style="${style}"` : ""}>`}${text}</${type}>\n`;
|
|
84
85
|
};
|
|
85
86
|
renderer.tablerow = ({ text }) => {
|
|
86
87
|
return `<tr${require_parse_css_in_js_to_inline_css.parseCssInJsToInlineCss(finalStyles.tr) !== "" ? ` style="${require_parse_css_in_js_to_inline_css.parseCssInJsToInlineCss(finalStyles.tr)}"` : ""}>\n${text}</tr>\n`;
|
|
@@ -78,7 +78,8 @@ const Markdown = React$1.forwardRef(({ children, markdownContainerStyles, markdo
|
|
|
78
78
|
renderer.tablecell = ({ tokens, align, header }) => {
|
|
79
79
|
const text = renderer.parser.parseInline(tokens);
|
|
80
80
|
const type = header ? "th" : "td";
|
|
81
|
-
|
|
81
|
+
const style = parseCssInJsToInlineCss(finalStyles[type]);
|
|
82
|
+
return `${`<${type}${align ? ` align="${align}"` : ""}${style !== "" ? ` style="${style}"` : ""}>`}${text}</${type}>\n`;
|
|
82
83
|
};
|
|
83
84
|
renderer.tablerow = ({ text }) => {
|
|
84
85
|
return `<tr${parseCssInJsToInlineCss(finalStyles.tr) !== "" ? ` style="${parseCssInJsToInlineCss(finalStyles.tr)}"` : ""}>\n${text}</tr>\n`;
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -30,6 +30,14 @@ Starts a local development server that will watch your files and automatically r
|
|
|
30
30
|
npx react-email dev
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
Templates are bundled with [esbuild](https://esbuild.github.io/). To run additional esbuild plugins, for example a compile-time transform your templates depend on, pass `--esbuild-plugins` with the path to a module whose default export is an array of plugins (or a function returning one):
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npx react-email dev --esbuild-plugins ./esbuild-plugins.mjs
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The same option is accepted by `email build` and `email export`.
|
|
40
|
+
|
|
33
41
|
### `email export`
|
|
34
42
|
|
|
35
43
|
Generates the plain HTML files of your emails into a `out` directory.
|
|
@@ -218,4 +218,33 @@ console.log(\`Hello, $\{name}!\`);
|
|
|
218
218
|
</div><!--/$-->"
|
|
219
219
|
`);
|
|
220
220
|
});
|
|
221
|
+
it('applies th styles to table header cells and td styles to body cells', async () => {
|
|
222
|
+
const actualOutput = await render(
|
|
223
|
+
<Markdown
|
|
224
|
+
markdownCustomStyles={{
|
|
225
|
+
th: { fontWeight: 'bold' },
|
|
226
|
+
td: { color: 'gray' },
|
|
227
|
+
}}
|
|
228
|
+
>
|
|
229
|
+
{`| Name | Qty |
|
|
230
|
+
| ---- | --: |
|
|
231
|
+
| Apples | 3 |`}
|
|
232
|
+
</Markdown>,
|
|
233
|
+
);
|
|
234
|
+
expect(actualOutput).toMatchInlineSnapshot(`
|
|
235
|
+
"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><div data-id="react-email-markdown"><table role="presentation">
|
|
236
|
+
<thead>
|
|
237
|
+
<tr>
|
|
238
|
+
<th style="font-weight:bold">Name</th>
|
|
239
|
+
<th align="right" style="font-weight:bold">Qty</th>
|
|
240
|
+
</tr>
|
|
241
|
+
</thead>
|
|
242
|
+
<tbody><tr>
|
|
243
|
+
<td style="color:gray">Apples</td>
|
|
244
|
+
<td align="right" style="color:gray">3</td>
|
|
245
|
+
</tr>
|
|
246
|
+
</tbody></table>
|
|
247
|
+
</div><!--/$-->"
|
|
248
|
+
`);
|
|
249
|
+
});
|
|
221
250
|
});
|
|
@@ -197,17 +197,10 @@ export const Markdown = React.forwardRef<HTMLDivElement, MarkdownProps>(
|
|
|
197
197
|
renderer.tablecell = ({ tokens, align, header }) => {
|
|
198
198
|
const text = renderer.parser.parseInline(tokens);
|
|
199
199
|
const type = header ? 'th' : 'td';
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
: ''
|
|
205
|
-
}>`
|
|
206
|
-
: `<${type}${
|
|
207
|
-
parseCssInJsToInlineCss(finalStyles.td) !== ''
|
|
208
|
-
? ` style="${parseCssInJsToInlineCss(finalStyles.td)}"`
|
|
209
|
-
: ''
|
|
210
|
-
}>`;
|
|
200
|
+
const style = parseCssInJsToInlineCss(finalStyles[type]);
|
|
201
|
+
const tag = `<${type}${align ? ` align="${align}"` : ''}${
|
|
202
|
+
style !== '' ? ` style="${style}"` : ''
|
|
203
|
+
}>`;
|
|
211
204
|
return `${tag}${text}</${type}>\n`;
|
|
212
205
|
};
|
|
213
206
|
|