Skip to content

Commit 22f9a4e

Browse files
authored
feat(create-jsx-email): choose JS or TS (#160)
1 parent 1867e83 commit 22f9a4e

File tree

4 files changed

+70
-35
lines changed

4 files changed

+70
-35
lines changed

packages/create-jsx-email/generators/package.json.mustache

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@
99
"dev": "email preview ./templates"
1010
},
1111
"dependencies": {
12-
"jsx-email": "^1.0.1",
13-
"superstruct": "^1.0.3"
12+
"jsx-email": "^1.0.1"
1413
},
1514
"devDependencies": {
16-
"@types/react": "^18.2.0",
17-
"react": "^18.2.0",
18-
"typescript": "^5.2.2"
15+
"react": "^18.2.0"{{ typeDep }}
1916
}
2017
}

packages/create-jsx-email/generators/templates/email.tsx.mustache renamed to packages/create-jsx-email/generators/templates/email.mustache

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,7 @@ import {
1111
Text
1212
} from 'jsx-email';
1313

14-
/**
15-
* Note: Superstruct is a fantastic validation package. It's smaller and faster than alternatives
16-
* and uses a delightful API without chaining. docs.superstructjs.org
17-
*
18-
* To install run `pnpm add superstruct`.
19-
*/
20-
import { defaulted, object, string, type Infer } from 'superstruct';
21-
import React from 'react';
22-
23-
export const TemplateName = '{{ name }}';
24-
25-
export const TemplateStruct = object({
26-
email: defaulted(string(), '[email protected]'),
27-
name: defaulted(string(), 'Bruce Wayne')
28-
});
29-
30-
export type TemplateProps = Infer<typeof TemplateStruct>;
14+
{{{ typeProps }}}
3115

3216
const main = {
3317
backgroundColor: '#f6f9fc',
@@ -69,18 +53,23 @@ const button = {
6953
display: 'block',
7054
fontSize: '16px',
7155
fontWeight: 'bold',
72-
padding: '10px',
7356
textAlign: 'center' as const,
7457
textDecoration: 'none',
75-
width: '100%'
58+
width: '100%',
59+
padding: '10px'
7660
};
7761

78-
export const Template = ({ email, name }: TemplateProps) => (
62+
export const defaultProps = {
63+
64+
name: 'Bruce Wayne'
65+
} as TemplateProps;
66+
67+
export const templateName = '{{name}}';
68+
69+
export const Template = ({ email, name }{{ propsType }}) => (
7970
<Html>
8071
<Head />
81-
<Preview>
82-
This is our email preview text for {name} &lt;{email}&gt;
83-
</Preview>
72+
<Preview>This is our email preview text for {name} &lt;{email}&gt;</Preview>
8473
<Body style={main}>
8574
<Container style={container}>
8675
<Section style={box}>
@@ -94,6 +83,7 @@ export const Template = ({ email, name }: TemplateProps) => (
9483
<Link style={anchor} href="mailto:{email}">
9584
link
9685
</Link>
86+
.
9787
</Text>
9888
</Section>
9989
</Container>

packages/create-jsx-email/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"url": "https://github.com/shellscape/jsx-email.git",
1212
"directory": "packages/create-jsx-email"
1313
},
14+
"main": "dist/src/index.js",
1415
"bin": {
1516
"create-jsx-email": "./dist/src/index.js"
1617
},
@@ -37,6 +38,7 @@
3738
"@types/fs-extra": "^11.0.2",
3839
"@types/prompts": "^2.4.8"
3940
},
41+
"types": "dist/src/index.d.ts",
4042
"funding": {
4143
"type": "github",
4244
"url": "https://github.com/sponsors/shellscape"

packages/create-jsx-email/src/index.ts

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ import prompts from 'prompts';
1212

1313
import pkg from '../package.json';
1414

15+
interface CreateEmailArgs {
16+
jsx: boolean;
17+
name: string;
18+
outputPath: string;
19+
}
20+
1521
const cancelled = () => {
1622
throw new Error(chalk`{red ✖} Operation cancelled`);
1723
};
@@ -35,13 +41,39 @@ const isEmpty = (path: string) => {
3541
};
3642
const { log } = console;
3743
const normalizePath = (filename: string) => filename.split(win32.sep).join(posix.sep);
44+
const typeDep = ',\n"@types/react": "^18.2.0",\n"typescript": "^5.2.2"';
45+
const typeProps = `\nexport type TemplateProps = {
46+
email: string;
47+
name: string;
48+
}`;
49+
50+
export const createEmail = async ({ jsx, name, outputPath }: CreateEmailArgs) => {
51+
const data = {
52+
name,
53+
propsType: jsx ? '' : ': TemplateProps',
54+
typeProps: jsx ? '' : typeProps
55+
};
56+
const templatePath = resolve(__dirname, '../generators/templates/email.mustache');
57+
const template = await readFile(templatePath, 'utf8');
58+
const contents = mustache.render(template, data);
59+
const fileName = basename(templatePath)
60+
.replace('_', '')
61+
.replace('.mustache', jsx ? '.jsx' : '.tsx');
62+
const outPath = join(outputPath, fileName);
63+
64+
log('Creating a new template at', outPath);
65+
66+
await writeFile(outPath, contents, 'utf8');
67+
68+
return outPath;
69+
};
3870

3971
const run = async () => {
4072
log(intro);
4173

4274
const argTargetDir = formatTargetDir(process.argv[2]);
4375
let targetPath = argTargetDir || defaultTargetDir;
44-
let result: prompts.Answers<'projectName' | 'overwrite'>;
76+
let result: prompts.Answers<'projectName' | 'projectType' | 'overwrite'>;
4577

4678
try {
4779
result = await prompts(
@@ -55,6 +87,15 @@ const run = async () => {
5587
},
5688
type: argTargetDir ? null : 'text'
5789
},
90+
{
91+
choices: [
92+
{ title: 'TypeScript', value: 'TypeScript' },
93+
{ title: 'JavaScript', value: 'JavaScript' }
94+
],
95+
message: 'TypeScript or JavaScript:',
96+
name: 'projectType',
97+
type: 'select'
98+
},
5899
{
59100
message: () =>
60101
targetPath === '.'
@@ -78,20 +119,23 @@ const run = async () => {
78119
return;
79120
}
80121

81-
const { overwrite, projectName } = result;
122+
const { overwrite, projectName, projectType } = result;
82123
const root = join(process.cwd(), targetPath);
124+
const generatorsPath = resolve(__dirname, '../generators');
125+
const jsx = projectType === 'JavaScript';
126+
const templates = await globby([normalizePath(join(generatorsPath, '/*.*'))]);
127+
const outputPath = join(root, 'templates');
128+
const templateData = { name: projectName, typeDep: jsx ? '' : typeDep };
83129

84130
log(chalk`\n{blue Creating Project} at: {dim ${root}}`);
85131

86-
const generatorsPath = resolve(__dirname, '../generators');
87-
const templates = await globby([normalizePath(join(generatorsPath, '/**/*.*'))]);
88-
const templateData = { name: projectName };
89-
90132
if (overwrite && existsSync(root)) clearDirectory(root);
91133

92-
await mkdir(join(root, 'templates'), { recursive: true });
134+
await mkdir(outputPath, { recursive: true });
93135

94136
for (const path of templates) {
137+
// eslint-disable-next-line no-continue
138+
if (path.endsWith('_tsconfig.json') && jsx) continue;
95139
const template = await readFile(path, 'utf8');
96140
const contents = mustache.render(template, templateData);
97141
const basePath = dirname(path);
@@ -101,6 +145,8 @@ const run = async () => {
101145
await writeFile(outPath, contents, 'utf8');
102146
}
103147

148+
await createEmail({ jsx, name: projectName, outputPath });
149+
104150
const packageManager = await detect();
105151
const install =
106152
packageManager === 'yarn'

0 commit comments

Comments
 (0)