Skip to content

Commit fcf1220

Browse files
feat(charlie): align next/v3 CLI --use-preview-props behavior with main (#358)
Co-authored-by: CharlieHelps <[email protected]>
1 parent 161c837 commit fcf1220

File tree

10 files changed

+150
-6
lines changed

10 files changed

+150
-6
lines changed

apps/web/src/content/docs/core/cli.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ email build ./emails/Welcome.tsx
6363
To view info, flags, and options for the `build` command, run `email help build`.
6464
:::
6565

66+
To render using the sample props you export from a template file, pass the `--use-preview-props` flag. When set, `email build` will use the `previewProps` export (if present) instead of `--props`.
67+
6668
### Check Command
6769

6870
Verify email client compatibility using [caniuse.com](https://caniemail.com/) data:
@@ -88,6 +90,8 @@ Check Complete: 14 error(s), 20 warning(s)
8890
To view info, flags, and options for the `check` command, run `email help check`.
8991
:::
9092

93+
You can also pass `--use-preview-props` so `email check` builds templates using their exported `previewProps`, keeping compatibility checks aligned with the data used for previews and builds.
94+
9195
### Preview Command
9296

9397
Launch a development server to preview your email templates with these features:

docs/components/graph.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This component is wrapper around [QuickChart API](https://quickchart.io/) for ge
1818
Add the graph component to your email template.
1919

2020
```jsx
21-
import { Html, Body, Section, Graph } from 'jsx-email';
21+
import { Body, Graph, Html, Section } from 'jsx-email';
2222

2323
const Email = () => {
2424
return (

docs/core/cli.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ $ cd ~/code/email-app
6767
$ email build ./emails/Batman.tsx
6868
```
6969

70+
To render using the sample props you export from a template file, pass the `--use-preview-props` flag. When set, `email build` will use the `previewProps` export (if present) instead of `--props`.
71+
7072
## Check
7173

7274
::: tip
@@ -79,6 +81,8 @@ To view info, flags, and options for the `check` command, run `email help check`
7981
$ email check ./emails/Batman.tsx
8082
```
8183

84+
To check templates using their exported `previewProps`, pass the `--use-preview-props` flag so the check runs against the same sample data as your previews and builds.
85+
8286
Example output:
8387

8488
```console

docs/core/compile.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ const compiledFiles = await compile({ files: [templatePath], hashFiles: false, o
2828
Once compiled into a bundle, the file can be imported and passed to render such like:
2929

3030
```jsx
31-
import { Template } from './.compiled/batman.js';
32-
3331
import { render } from 'jsx-email';
3432

33+
import { Template } from './.compiled/batman.js';
34+
3535
const html = render(<Template />);
3636
```
3737

docs/core/plugins.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ To instruct a render to use plugins, utilize a [Configuration File](/docs/core/c
3333

3434
```js
3535
import { defineConfig } from 'jsx-email/config';
36+
3637
import { somePlugin } from './plugins/some-plugin';
3738

3839
export const config = defineConfig({

packages/jsx-email/src/cli/commands/build.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ Builds a template and saves the result
8383
--props A JSON string containing props to be passed to the email template
8484
This is usually only useful when building a single template, unless all of your
8585
templates share the same props.
86+
--use-preview-props
87+
When set, use the \`previewProps\` exported by the template file (if present).
8688
8789
{underline Examples}
8890
$ email build ./src/emails

packages/jsx-email/src/cli/commands/check.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { type IssueGroup, caniemail, groupIssues, sortIssues } from 'caniemail';
44
import chalk from 'chalk';
55
import chalkTmpl from 'chalk-template';
66
import stripAnsi from 'strip-ansi';
7-
import { type InferOutput as Infer, parse as assert, object } from 'valibot';
7+
import { type InferOutput as Infer, parse as assert, boolean, object, optional } from 'valibot';
88

99
import { formatBytes, gmailByteLimit, gmailBytesSafe } from '../helpers.js';
1010

@@ -13,7 +13,9 @@ import { type CommandFn } from './types.js';
1313

1414
const { error, log } = console;
1515

16-
const CheckOptionsStruct = object({});
16+
const CheckOptionsStruct = object({
17+
usePreviewProps: optional(boolean())
18+
});
1719

1820
type CheckOptions = Infer<typeof CheckOptionsStruct>;
1921

@@ -34,6 +36,10 @@ Check jsx-email templates for client compatibility
3436
{underline Usage}
3537
$ email check <template file name>
3638
39+
{underline Options}
40+
--use-preview-props
41+
When set, use the \`previewProps\` exported by the template file (if present).
42+
3743
{underline Examples}
3844
$ email check ./emails/Batman.tsx
3945
`;
@@ -129,7 +135,11 @@ export const command: CommandFn = async (argv: CheckOptions, input) => {
129135
log(chalkTmpl`{blue Checking email template for Client Compatibility...}\n`);
130136

131137
const [file] = await buildTemplates({
132-
buildOptions: { showStats: false, writeToFile: false },
138+
buildOptions: {
139+
showStats: false,
140+
writeToFile: false,
141+
usePreviewProps: argv.usePreviewProps
142+
},
133143
targetPath: input[0]
134144
});
135145

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { rm } from 'node:fs/promises';
2+
import { resolve } from 'node:path';
3+
4+
import { describe, expect, it } from 'vitest';
5+
6+
import { buildTemplates } from '../../src/cli/commands/build.js';
7+
8+
import { previewProps } from './fixtures/cli-preview-props-template.js';
9+
10+
const templatePath = resolve(__dirname, 'fixtures/cli-preview-props-template.tsx');
11+
const outDir = resolve(__dirname, '.test-build-preview-props');
12+
13+
describe('cli build --use-preview-props', () => {
14+
it('uses template.previewProps when flag is set', async () => {
15+
(globalThis as any).__jsxEmailCliPreviewProps = undefined;
16+
await rm(outDir, { recursive: true, force: true });
17+
18+
const [result] = await buildTemplates({
19+
buildOptions: {
20+
html: true,
21+
out: outDir,
22+
plain: false,
23+
props: JSON.stringify({ source: 'cli', test: 'robin' }),
24+
showStats: false,
25+
silent: true,
26+
usePreviewProps: true,
27+
writeToFile: false
28+
},
29+
targetPath: templatePath
30+
});
31+
32+
const usedProps = (globalThis as any).__jsxEmailCliPreviewProps;
33+
34+
expect(usedProps).toEqual(previewProps);
35+
expect(result.html).toContain('batman');
36+
expect(result.html).not.toContain('robin');
37+
});
38+
39+
it('uses --props JSON when usePreviewProps is not set', async () => {
40+
(globalThis as any).__jsxEmailCliPreviewProps = undefined;
41+
await rm(outDir, { recursive: true, force: true });
42+
43+
const props = { source: 'cli', test: 'robin' } as const;
44+
45+
const [result] = await buildTemplates({
46+
buildOptions: {
47+
html: true,
48+
out: outDir,
49+
plain: false,
50+
props: JSON.stringify(props),
51+
showStats: false,
52+
silent: true,
53+
// `usePreviewProps` intentionally omitted
54+
writeToFile: false
55+
},
56+
targetPath: templatePath
57+
});
58+
59+
const usedProps = (globalThis as any).__jsxEmailCliPreviewProps;
60+
61+
expect(usedProps).toEqual(props);
62+
expect(result.html).toContain('robin');
63+
expect(result.html).not.toContain('batman');
64+
});
65+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { resolve } from 'node:path';
2+
3+
import { describe, expect, it, vi } from 'vitest';
4+
5+
import { previewProps } from './fixtures/cli-preview-props-template.js';
6+
7+
const caniemailMock = vi.fn(() => ({
8+
issues: { errors: new Map(), warnings: undefined },
9+
success: true
10+
}));
11+
12+
vi.mock('caniemail', () => ({
13+
caniemail: caniemailMock,
14+
groupIssues: () => [],
15+
sortIssues: (groups: unknown[]) => groups
16+
}));
17+
18+
const templatePath = resolve(__dirname, 'fixtures/cli-preview-props-template.tsx');
19+
20+
describe('cli check --use-preview-props', () => {
21+
it('builds using template.previewProps when flag is set', async () => {
22+
(globalThis as any).__jsxEmailCliPreviewProps = undefined;
23+
caniemailMock.mockClear();
24+
25+
const { command } = await import('../../src/cli/commands/check.js');
26+
27+
const result = await command({ usePreviewProps: true } as any, [templatePath]);
28+
29+
expect(result).toBe(true);
30+
31+
const usedProps = (globalThis as any).__jsxEmailCliPreviewProps;
32+
expect(usedProps).toEqual(previewProps);
33+
34+
expect(caniemailMock).toHaveBeenCalledTimes(1);
35+
const [{ html }] = caniemailMock.mock.calls[0] as [{ html: string }];
36+
expect(html).toContain('batman');
37+
});
38+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Body, Html } from '../../../src/index.js';
2+
3+
export const previewProps = { source: 'preview', test: 'batman' } as const;
4+
5+
interface TemplateProps {
6+
source?: string;
7+
test?: string;
8+
}
9+
10+
export const Template = (props: TemplateProps) => {
11+
(globalThis as any).__jsxEmailCliPreviewProps = props;
12+
13+
return (
14+
<Html>
15+
<Body>{props.test}</Body>
16+
</Html>
17+
);
18+
};
19+
20+
export const templateName = 'cli-preview-props';

0 commit comments

Comments
 (0)