├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── examples
├── nestjs-example
│ ├── .env.example
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── src
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── config
│ │ │ └── mailer.config.ts
│ │ ├── main.ts
│ │ └── templates
│ │ │ └── welcome.tsx
│ ├── test
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
└── nestjs-with-swc-example
│ ├── .env.example
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── gulpfile.js
│ ├── nest-cli.json
│ ├── package.json
│ ├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ ├── config
│ │ └── mailer.config.ts
│ ├── main.ts
│ └── templates
│ │ └── welcome.tsx
│ ├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── package.json
├── src
├── index.ts
└── react.adapter.tsx
├── tsconfig.json
└── tsup.config.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | package-lock.json
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # TypeScript v1 declaration files
46 | typings/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Microbundle cache
58 | .rpt2_cache/
59 | .rts2_cache_cjs/
60 | .rts2_cache_es/
61 | .rts2_cache_umd/
62 |
63 | # Optional REPL history
64 | .node_repl_history
65 |
66 | # Output of 'npm pack'
67 | *.tgz
68 |
69 | # Yarn Integrity file
70 | .yarn-integrity
71 |
72 | # dotenv environment variables file
73 | .env
74 | .env.test
75 |
76 | # parcel-bundler cache (https://parceljs.org/)
77 | .cache
78 |
79 | # Next.js build output
80 | .next
81 |
82 | # Nuxt.js build / generate output
83 | .nuxt
84 | dist
85 |
86 | # Gatsby files
87 | .cache/
88 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
89 | # https://nextjs.org/blog/next-9-1#public-directory-support
90 | # public
91 |
92 | # vuepress build output
93 | .vuepress/dist
94 |
95 | # Serverless directories
96 | .serverless/
97 |
98 | # FuseBox cache
99 | .fusebox/
100 |
101 | # DynamoDB Local files
102 | .dynamodb/
103 |
104 | # TernJS port file
105 | .tern-port
106 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # source
2 | src
3 | examples
4 | package-lock.json
5 | tsconfig.json
6 | tsup.config.ts
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Webtre Technologies
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 📨 Build and send emails in Nest framework using React.js
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ## Features
18 |
19 | - 🦾 Write your email templates in [React](https://github.com/facebook/react/) and [TypeScript](https://www.typescriptlang.org/)
20 |
21 | - 📬 No more template not found / sending blank emails.
22 |
23 | - 🔰 No more missing context / variables from template.
24 |
25 | - 🧪 Write testable templates intended for email clients.
26 |
27 | - 💌 Built on top of [`react-email`](https://github.com/resendlabs/react-email) — the next generation of writing emails.
28 |
29 | ## Installation
30 |
31 | > This library is an adapter for the [`@nestjs-modules/mailer`](https://github.com/nest-modules/mailer) module, so we'll install the dependencies alongside by running the command below.
32 |
33 | ```sh
34 | npm i @webtre/nestjs-mailer-react-adapter @nestjs-modules/mailer nodemailer
35 | ```
36 |
37 | ### Getting Started
38 |
39 | To add support for `React` to your project, modify `tsconfig.json`
40 |
41 | ```javascript
42 | {
43 | "compilerOptions": {
44 | // add this line
45 | "jsx": "react-jsx"
46 | }
47 | }
48 | ```
49 |
50 | ### Configuration
51 |
52 | ```javascript
53 | // src/app.module.ts
54 | import { Module } from "@nestjs/common";
55 | import { MailerModule } from "@nestjs-modules/mailer";
56 | import { ReactAdapter } from "@webtre/nestjs-mailer-react-adapter";
57 |
58 | @Module({
59 | imports: [
60 | MailerModule.forRoot({
61 | transport: {
62 | host: "smtp.domain.com",
63 | port: 2525,
64 | secure: false,
65 | auth: {
66 | user: "user@domain.com",
67 | pass: "password",
68 | },
69 | },
70 | defaults: {
71 | from: '"Webtre Technologies" ',
72 | },
73 | template: {
74 | dir: __dirname + "/templates",
75 | // Use the adapter
76 | adapter: new ReactAdapter(),
77 |
78 | // Or with optional config
79 | adapter: new ReactAdapter({
80 | pretty: false,
81 | plainText: true,
82 | htmlToTextOptions: {
83 | wordwrap: 130,
84 | limits: {
85 | ellipsis: "...",
86 | },
87 | },
88 | }),
89 | },
90 | }),
91 | ],
92 | })
93 | export class AppModule {}
94 | ```
95 |
96 | To see more options that can be passed to the `htmlToTextOptions` object, [click here](https://github.com/html-to-text/node-html-to-text/tree/master/packages/html-to-text#options).
97 |
98 | ### Service Provider
99 |
100 | ```javascript
101 | import { Injectable } from '@nestjs/common';
102 | import { MailerService } from '@nestjs-modules/mailer';
103 |
104 | @Injectable()
105 | export class ExampleService {
106 | constructor(private readonly mailerService: MailerService) {}
107 |
108 | async public example(): Promise {
109 | await this.mailerService
110 | .sendMail({
111 | to: 'john@domain.com',
112 | subject: 'Testing react template',
113 | template: 'welcome', // The compiled extension is appended automatically.
114 | context: { // Data to be passed as props to your template.
115 | code: '123456',
116 | name: 'John Doe',
117 | },
118 | });
119 | }
120 | }
121 | ```
122 |
123 | ### React Template
124 |
125 | ```javascript
126 | // src/templates/welcome.tsx
127 | interface Props {
128 | code: string;
129 | name: string;
130 | }
131 |
132 | export default function Welcome({ name, code }: Props) {
133 | return (
134 |
135 | Hi {name}, thanks for signing up. Your code is {code}
136 |
137 | );
138 | }
139 | ```
140 |
141 | ## Examples
142 |
143 | You could also check the [examples folder](./examples) in this repo for a working usage example and how to set it up with [SWC](https://docs.nestjs.com/recipes/swc) using [gulp](https://gulpjs.com/).
144 |
145 | ## Credits
146 |
147 | - [`react-email`](https://github.com/resendlabs/react-email) — build and send emails using React
148 | - [`@nestjs-modules/mailer`](https://github.com/nest-modules/mailer) — a mailer module for Nest framework (node.js)
149 |
150 | ## License
151 |
152 | [MIT](./LICENSE) License © 2022 [Webtre Technologies](https://github.com/webtretech)
153 |
--------------------------------------------------------------------------------
/examples/nestjs-example/.env.example:
--------------------------------------------------------------------------------
1 | MAIL_SERVICE=smtp
2 | MAIL_HOST=
3 | MAIL_PORT=2525
4 | MAIL_USERNAME=
5 | MAIL_PASSWORD=
6 | MAIL_FROM_ADDRESS=hello@domain.com
7 | MAIL_FROM_NAME="Webtre Technologies"
8 |
--------------------------------------------------------------------------------
/examples/nestjs-example/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | tsconfigRootDir: __dirname,
6 | sourceType: 'module',
7 | },
8 | plugins: ['@typescript-eslint/eslint-plugin'],
9 | extends: [
10 | 'plugin:@typescript-eslint/recommended',
11 | 'plugin:prettier/recommended',
12 | ],
13 | root: true,
14 | env: {
15 | node: true,
16 | jest: true,
17 | },
18 | ignorePatterns: ['.eslintrc.js'],
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/explicit-module-boundary-types': 'off',
23 | '@typescript-eslint/no-explicit-any': 'off',
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/examples/nestjs-example/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### JetBrains template
3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
5 |
6 | # User-specific stuff:
7 | .idea/**/workspace.xml
8 | .idea/**/tasks.xml
9 | .idea/dictionaries
10 |
11 | # Sensitive or high-churn files:
12 | .idea/**/dataSources/
13 | .idea/**/dataSources.ids
14 | .idea/**/dataSources.xml
15 | .idea/**/dataSources.local.xml
16 | .idea/**/sqlDataSources.xml
17 | .idea/**/dynamic.xml
18 | .idea/**/uiDesigner.xml
19 |
20 | # Gradle:
21 | .idea/**/gradle.xml
22 | .idea/**/libraries
23 |
24 | # CMake
25 | cmake-build-debug/
26 |
27 | # Mongo Explorer plugin:
28 | .idea/**/mongoSettings.xml
29 |
30 | ## File-based project format:
31 | *.iws
32 |
33 | ## Plugin-specific files:
34 |
35 | # IntelliJ
36 | out/
37 |
38 | # mpeltonen/sbt-idea plugin
39 | .idea_modules/
40 |
41 | # JIRA plugin
42 | atlassian-ide-plugin.xml
43 |
44 | # Cursive Clojure plugin
45 | .idea/replstate.xml
46 |
47 | # Crashlytics plugin (for Android Studio and IntelliJ)
48 | com_crashlytics_export_strings.xml
49 | crashlytics.properties
50 | crashlytics-build.properties
51 | fabric.properties
52 | ### VisualStudio template
53 | ## Ignore Visual Studio temporary files, build results, and
54 | ## files generated by popular Visual Studio add-ons.
55 | ##
56 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
57 |
58 | # User-specific files
59 | *.suo
60 | *.user
61 | *.userosscache
62 | *.sln.docstates
63 |
64 | # User-specific files (MonoDevelop/Xamarin Studio)
65 | *.userprefs
66 |
67 | # Build results
68 | [Dd]ebug/
69 | [Dd]ebugPublic/
70 | [Rr]elease/
71 | [Rr]eleases/
72 | x64/
73 | x86/
74 | bld/
75 | [Bb]in/
76 | [Oo]bj/
77 | [Ll]og/
78 |
79 | # Visual Studio 2015 cache/options directory
80 | .vs/
81 | # Uncomment if you have tasks that create the project's static files in wwwroot
82 | #wwwroot/
83 |
84 | # MSTest test Results
85 | [Tt]est[Rr]esult*/
86 | [Bb]uild[Ll]og.*
87 |
88 | # NUNIT
89 | *.VisualState.xml
90 | TestResult.xml
91 |
92 | # Build Results of an ATL Project
93 | [Dd]ebugPS/
94 | [Rr]eleasePS/
95 | dlldata.c
96 |
97 | # Benchmark Results
98 | BenchmarkDotNet.Artifacts/
99 |
100 | # .NET Core
101 | project.lock.json
102 | project.fragment.lock.json
103 | artifacts/
104 | **/Properties/launchSettings.json
105 |
106 | *_i.c
107 | *_p.c
108 | *_i.h
109 | *.ilk
110 | *.meta
111 | *.obj
112 | *.pch
113 | *.pdb
114 | *.pgc
115 | *.pgd
116 | *.rsp
117 | *.sbr
118 | *.tlb
119 | *.tli
120 | *.tlh
121 | *.tmp
122 | *.tmp_proj
123 | *.log
124 | *.vspscc
125 | *.vssscc
126 | .builds
127 | *.pidb
128 | *.svclog
129 | *.scc
130 |
131 | # Chutzpah Test files
132 | _Chutzpah*
133 |
134 | # Visual C++ cache files
135 | ipch/
136 | *.aps
137 | *.ncb
138 | *.opendb
139 | *.opensdf
140 | *.sdf
141 | *.cachefile
142 | *.VC.db
143 | *.VC.VC.opendb
144 |
145 | # Visual Studio profiler
146 | *.psess
147 | *.vsp
148 | *.vspx
149 | *.sap
150 |
151 | # Visual Studio Trace Files
152 | *.e2e
153 |
154 | # TFS 2012 Local Workspace
155 | $tf/
156 |
157 | # Guidance Automation Toolkit
158 | *.gpState
159 |
160 | # ReSharper is a .NET coding add-in
161 | _ReSharper*/
162 | *.[Rr]e[Ss]harper
163 | *.DotSettings.user
164 |
165 | # JustCode is a .NET coding add-in
166 | .JustCode
167 |
168 | # TeamCity is a build add-in
169 | _TeamCity*
170 |
171 | # DotCover is a Code Coverage Tool
172 | *.dotCover
173 |
174 | # AxoCover is a Code Coverage Tool
175 | .axoCover/*
176 | !.axoCover/settings.json
177 |
178 | # Visual Studio code coverage results
179 | *.coverage
180 | *.coveragexml
181 |
182 | # NCrunch
183 | _NCrunch_*
184 | .*crunch*.local.xml
185 | nCrunchTemp_*
186 |
187 | # MightyMoose
188 | *.mm.*
189 | AutoTest.Net/
190 |
191 | # Web workbench (sass)
192 | .sass-cache/
193 |
194 | # Installshield output folder
195 | [Ee]xpress/
196 |
197 | # DocProject is a documentation generator add-in
198 | DocProject/buildhelp/
199 | DocProject/Help/*.HxT
200 | DocProject/Help/*.HxC
201 | DocProject/Help/*.hhc
202 | DocProject/Help/*.hhk
203 | DocProject/Help/*.hhp
204 | DocProject/Help/Html2
205 | DocProject/Help/html
206 |
207 | # Click-Once directory
208 | publish/
209 |
210 | # Publish Web Output
211 | *.[Pp]ublish.xml
212 | *.azurePubxml
213 | # Note: Comment the next line if you want to checkin your web deploy settings,
214 | # but database connection strings (with potential passwords) will be unencrypted
215 | *.pubxml
216 | *.publishproj
217 |
218 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
219 | # checkin your Azure Web App publish settings, but sensitive information contained
220 | # in these scripts will be unencrypted
221 | PublishScripts/
222 |
223 | # NuGet Packages
224 | *.nupkg
225 | # The packages folder can be ignored because of Package Restore
226 | **/[Pp]ackages/*
227 | # except build/, which is used as an MSBuild target.
228 | !**/[Pp]ackages/build/
229 | # Uncomment if necessary however generally it will be regenerated when needed
230 | #!**/[Pp]ackages/repositories.config
231 | # NuGet v3's project.json files produces more ignorable files
232 | *.nuget.props
233 | *.nuget.targets
234 |
235 | # Microsoft Azure Build Output
236 | csx/
237 | *.build.csdef
238 |
239 | # Microsoft Azure Emulator
240 | ecf/
241 | rcf/
242 |
243 | # Windows Store app package directories and files
244 | AppPackages/
245 | BundleArtifacts/
246 | Package.StoreAssociation.xml
247 | _pkginfo.txt
248 | *.appx
249 |
250 | # Visual Studio cache files
251 | # files ending in .cache can be ignored
252 | *.[Cc]ache
253 | # but keep track of directories ending in .cache
254 | !*.[Cc]ache/
255 |
256 | # Others
257 | ClientBin/
258 | ~$*
259 | *~
260 | *.dbmdl
261 | *.dbproj.schemaview
262 | *.jfm
263 | *.pfx
264 | *.publishsettings
265 | orleans.codegen.cs
266 |
267 | # Since there are multiple workflows, uncomment next line to ignore bower_components
268 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
269 | #bower_components/
270 |
271 | # RIA/Silverlight projects
272 | Generated_Code/
273 |
274 | # Backup & report files from converting an old project file
275 | # to a newer Visual Studio version. Backup files are not needed,
276 | # because we have git ;-)
277 | _UpgradeReport_Files/
278 | Backup*/
279 | UpgradeLog*.XML
280 | UpgradeLog*.htm
281 |
282 | # SQL Server files
283 | *.mdf
284 | *.ldf
285 | *.ndf
286 |
287 | # Business Intelligence projects
288 | *.rdl.data
289 | *.bim.layout
290 | *.bim_*.settings
291 |
292 | # Microsoft Fakes
293 | FakesAssemblies/
294 |
295 | # GhostDoc plugin setting file
296 | *.GhostDoc.xml
297 |
298 | # Node.js Tools for Visual Studio
299 | .ntvs_analysis.dat
300 | node_modules/
301 |
302 | # Typescript v1 declaration files
303 | typings/
304 |
305 | # Visual Studio 6 build log
306 | *.plg
307 |
308 | # Visual Studio 6 workspace options file
309 | *.opt
310 |
311 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
312 | *.vbw
313 |
314 | # Visual Studio LightSwitch build output
315 | **/*.HTMLClient/GeneratedArtifacts
316 | **/*.DesktopClient/GeneratedArtifacts
317 | **/*.DesktopClient/ModelManifest.xml
318 | **/*.Server/GeneratedArtifacts
319 | **/*.Server/ModelManifest.xml
320 | _Pvt_Extensions
321 |
322 | # Paket dependency manager
323 | .paket/paket.exe
324 | paket-files/
325 |
326 | # FAKE - F# Make
327 | .fake/
328 |
329 | # JetBrains Rider
330 | .idea/
331 | *.sln.iml
332 |
333 | # IDE - VSCode
334 | .vscode/*
335 | !.vscode/settings.json
336 | !.vscode/tasks.json
337 | !.vscode/launch.json
338 | !.vscode/extensions.json
339 |
340 | # CodeRush
341 | .cr/
342 |
343 | # Python Tools for Visual Studio (PTVS)
344 | __pycache__/
345 | *.pyc
346 |
347 | # Cake - Uncomment if you are using it
348 | # tools/**
349 | # !tools/packages.config
350 |
351 | # Tabs Studio
352 | *.tss
353 |
354 | # Telerik's JustMock configuration file
355 | *.jmconfig
356 |
357 | # BizTalk build output
358 | *.btp.cs
359 | *.btm.cs
360 | *.odx.cs
361 | *.xsd.cs
362 |
363 | # OpenCover UI analysis results
364 | OpenCover/
365 | coverage/
366 |
367 | ### macOS template
368 | # General
369 | .DS_Store
370 | .AppleDouble
371 | .LSOverride
372 |
373 | # Icon must end with two \r
374 | Icon
375 |
376 | # Thumbnails
377 | ._*
378 |
379 | # Files that might appear in the root of a volume
380 | .DocumentRevisions-V100
381 | .fseventsd
382 | .Spotlight-V100
383 | .TemporaryItems
384 | .Trashes
385 | .VolumeIcon.icns
386 | .com.apple.timemachine.donotpresent
387 |
388 | # Directories potentially created on remote AFP share
389 | .AppleDB
390 | .AppleDesktop
391 | Network Trash Folder
392 | Temporary Items
393 | .apdisk
394 |
395 | =======
396 | # Local
397 | .env
398 | dist
399 | .webpack
400 | .serverless/**/*.zip
401 |
--------------------------------------------------------------------------------
/examples/nestjs-example/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
5 |
--------------------------------------------------------------------------------
/examples/nestjs-example/README.md:
--------------------------------------------------------------------------------
1 | ## Project setup
2 |
3 | ```bash
4 | $ npm install
5 | ```
6 |
7 | ## Compile and run the project
8 |
9 | ```bash
10 | # development
11 | $ npm run start
12 |
13 | # watch mode
14 | $ npm run start:dev
15 |
16 | # production mode
17 | $ npm run start:prod
18 | ```
19 |
20 | ## Run tests
21 |
22 | ```bash
23 | # unit tests
24 | $ npm run test
25 |
26 | # e2e tests
27 | $ npm run test:e2e
28 |
29 | # test coverage
30 | $ npm run test:cov
31 | ```
32 |
--------------------------------------------------------------------------------
/examples/nestjs-example/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/nest-cli",
3 | "collection": "@nestjs/schematics",
4 | "sourceRoot": "src",
5 | "compilerOptions": {
6 | "deleteOutDir": true
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/nestjs-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@webtre/nestjs-mailer-react-adapter-example",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "This is a demo using @webtre/nestjs-mailer-react-adapter",
6 | "license": "MIT",
7 | "scripts": {
8 | "build": "nest build",
9 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
10 | "start": "nest start",
11 | "start:dev": "nest start --watch",
12 | "start:debug": "nest start --debug --watch",
13 | "start:prod": "node dist/main",
14 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
15 | "test": "jest",
16 | "test:watch": "jest --watch",
17 | "test:cov": "jest --coverage",
18 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/jest/bin/jest --runInBand",
19 | "test:e2e": "jest --config ./test/jest-e2e.json"
20 | },
21 | "dependencies": {
22 | "@nestjs-modules/mailer": "^2.0.2",
23 | "@nestjs/common": "^11.0.11",
24 | "@nestjs/config": "^4.0.1",
25 | "@nestjs/core": "^11.0.11",
26 | "@nestjs/platform-express": "^11.0.11",
27 | "@webtre/nestjs-mailer-react-adapter": "^0.2.4",
28 | "nodemailer": "^6.10.0",
29 | "reflect-metadata": "^0.2.2",
30 | "rxjs": "^7.8.2"
31 | },
32 | "devDependencies": {
33 | "@nestjs/cli": "^11.0.5",
34 | "@nestjs/schematics": "^11.0.2",
35 | "@nestjs/testing": "^11.0.11",
36 | "@swc/cli": "^0.6.0",
37 | "@swc/core": "^1.11.8",
38 | "@types/express": "^5.0.0",
39 | "@types/jest": "^29.5.14",
40 | "@types/node": "^22.13.10",
41 | "@types/supertest": "^6.0.2",
42 | "@typescript-eslint/eslint-plugin": "^6.21.0",
43 | "@typescript-eslint/parser": "^6.21.0",
44 | "eslint": "^8.56.0",
45 | "eslint-config-prettier": "^9.1.0",
46 | "eslint-plugin-prettier": "^5.1.3",
47 | "jest": "^29.7.0",
48 | "prettier": "^3.5.3",
49 | "source-map-support": "^0.5.21",
50 | "supertest": "^7.0.0",
51 | "ts-jest": "^29.2.6",
52 | "ts-loader": "^9.5.2",
53 | "ts-node": "^10.9.2",
54 | "tsconfig-paths": "^4.2.0",
55 | "typescript": "^5.3.3"
56 | },
57 | "jest": {
58 | "moduleFileExtensions": [
59 | "js",
60 | "json",
61 | "ts"
62 | ],
63 | "rootDir": "src",
64 | "testRegex": ".*\\.spec\\.ts$",
65 | "transform": {
66 | "^.+\\.(t|j)s$": "ts-jest"
67 | },
68 | "collectCoverageFrom": [
69 | "**/*.(t|j)s"
70 | ],
71 | "coverageDirectory": "../coverage",
72 | "testEnvironment": "node"
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/examples/nestjs-example/src/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AppController } from './app.controller';
3 | import { AppService } from './app.service';
4 |
5 | describe('AppController', () => {
6 | let app: TestingModule;
7 |
8 | beforeAll(async () => {
9 | app = await Test.createTestingModule({
10 | controllers: [AppController],
11 | providers: [AppService],
12 | }).compile();
13 | });
14 |
15 | describe('getHello', () => {
16 | it('should return "Hello World!"', () => {
17 | const appController = app.get(AppController);
18 | expect(appController.getHello()).toBe('Hello World!');
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/examples/nestjs-example/src/app.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get } from '@nestjs/common';
2 | import { AppService } from './app.service';
3 |
4 | @Controller()
5 | export class AppController {
6 | constructor(private readonly appService: AppService) {}
7 |
8 | @Get()
9 | welcome(): Promise {
10 | return this.appService.sendWelcomeMail();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/nestjs-example/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { MailerModule } from '@nestjs-modules/mailer';
2 | import { Module } from '@nestjs/common';
3 | import { ConfigModule, ConfigType } from '@nestjs/config';
4 | import { ReactAdapter } from '@webtre/nestjs-mailer-react-adapter';
5 | import { AppController } from './app.controller';
6 | import { AppService } from './app.service';
7 | import mailerConfig from './config/mailer.config';
8 |
9 | @Module({
10 | imports: [
11 | ConfigModule.forRoot({
12 | isGlobal: true,
13 | load: [mailerConfig],
14 | envFilePath: __dirname + '/../.env',
15 | }),
16 | MailerModule.forRootAsync({
17 | inject: [mailerConfig.KEY],
18 | useFactory: (mailerConf: ConfigType) => ({
19 | ...mailerConf,
20 | template: {
21 | adapter: new ReactAdapter(),
22 | dir: __dirname + '/templates',
23 | },
24 | }),
25 | }),
26 | ],
27 | controllers: [AppController],
28 | providers: [AppService],
29 | })
30 | export class AppModule {}
31 |
--------------------------------------------------------------------------------
/examples/nestjs-example/src/app.service.ts:
--------------------------------------------------------------------------------
1 | import { MailerService } from '@nestjs-modules/mailer';
2 | import { Injectable } from '@nestjs/common';
3 |
4 | @Injectable()
5 | export class AppService {
6 | constructor(private readonly mailerService: MailerService) {}
7 |
8 | public async sendWelcomeMail(): Promise {
9 | await this.mailerService.sendMail({
10 | to: 'john@domain.com',
11 | subject: 'Testing react template',
12 | template: 'welcome', // The compiled extension is appended automatically.
13 | context: {
14 | // Data to be passed as props to your template.
15 | code: '123456',
16 | name: 'John Doe',
17 | },
18 | });
19 |
20 | return 'Mail sent!';
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/nestjs-example/src/config/mailer.config.ts:
--------------------------------------------------------------------------------
1 | import { MailerOptions } from '@nestjs-modules/mailer';
2 | import { registerAs } from '@nestjs/config';
3 |
4 | export default registerAs('mailer', (): MailerOptions => {
5 | return {
6 | transport: {
7 | service: process.env.MAIL_SERVICE,
8 | host: process.env.MAIL_HOST,
9 | port: parseInt(process.env.MAIL_PORT, 10),
10 | auth: {
11 | user: process.env.MAIL_USERNAME,
12 | pass: process.env.MAIL_PASSWORD,
13 | },
14 | },
15 | defaults: {
16 | from: {
17 | name: process.env.MAIL_FROM_NAME,
18 | address: process.env.MAIL_FROM_ADDRESS,
19 | },
20 | },
21 | };
22 | });
23 |
--------------------------------------------------------------------------------
/examples/nestjs-example/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { AppModule } from './app.module';
3 |
4 | async function bootstrap() {
5 | const app = await NestFactory.create(AppModule);
6 | await app.listen(3000);
7 | }
8 | bootstrap();
9 |
--------------------------------------------------------------------------------
/examples/nestjs-example/src/templates/welcome.tsx:
--------------------------------------------------------------------------------
1 | interface Props {
2 | code: string;
3 | name: string;
4 | }
5 |
6 | export default function Welcome({ name, code }: Props) {
7 | return (
8 |
9 | Hi {name}, thanks for signing up. Your code is {code}
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/examples/nestjs-example/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { INestApplication } from '@nestjs/common';
2 | import { Test } from '@nestjs/testing';
3 | import * as request from 'supertest';
4 | import { AppModule } from '../src/app.module';
5 |
6 | describe('AppController (e2e)', () => {
7 | let app: INestApplication;
8 |
9 | beforeAll(async () => {
10 | const moduleFixture = await Test.createTestingModule({
11 | imports: [AppModule],
12 | }).compile();
13 |
14 | app = moduleFixture.createNestApplication();
15 | await app.init();
16 | });
17 |
18 | afterAll(async () => {
19 | await app.close();
20 | });
21 |
22 | it('/ (GET)', () => {
23 | return request(app.getHttpServer())
24 | .get('/')
25 | .expect(200)
26 | .expect('Hello World!');
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/examples/nestjs-example/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": "\\.e2e-spec\\.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/nestjs-example/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/examples/nestjs-example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "ES2021",
10 | "sourceMap": true,
11 | "outDir": "./dist",
12 | "baseUrl": "./",
13 | "incremental": true,
14 | "skipLibCheck": true,
15 | "jsx": "react-jsx"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/.env.example:
--------------------------------------------------------------------------------
1 | MAIL_SERVICE=smtp
2 | MAIL_HOST=
3 | MAIL_PORT=2525
4 | MAIL_USERNAME=
5 | MAIL_PASSWORD=
6 | MAIL_FROM_ADDRESS=hello@domain.com
7 | MAIL_FROM_NAME="Webtre Technologies"
8 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | tsconfigRootDir: __dirname,
6 | sourceType: 'module',
7 | },
8 | plugins: ['@typescript-eslint/eslint-plugin'],
9 | extends: [
10 | 'plugin:@typescript-eslint/recommended',
11 | 'plugin:prettier/recommended',
12 | ],
13 | root: true,
14 | env: {
15 | node: true,
16 | jest: true,
17 | },
18 | ignorePatterns: ['.eslintrc.js'],
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/explicit-module-boundary-types': 'off',
23 | '@typescript-eslint/no-explicit-any': 'off',
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### JetBrains template
3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
5 |
6 | # User-specific stuff:
7 | .idea/**/workspace.xml
8 | .idea/**/tasks.xml
9 | .idea/dictionaries
10 |
11 | # Sensitive or high-churn files:
12 | .idea/**/dataSources/
13 | .idea/**/dataSources.ids
14 | .idea/**/dataSources.xml
15 | .idea/**/dataSources.local.xml
16 | .idea/**/sqlDataSources.xml
17 | .idea/**/dynamic.xml
18 | .idea/**/uiDesigner.xml
19 |
20 | # Gradle:
21 | .idea/**/gradle.xml
22 | .idea/**/libraries
23 |
24 | # CMake
25 | cmake-build-debug/
26 |
27 | # Mongo Explorer plugin:
28 | .idea/**/mongoSettings.xml
29 |
30 | ## File-based project format:
31 | *.iws
32 |
33 | ## Plugin-specific files:
34 |
35 | # IntelliJ
36 | out/
37 |
38 | # mpeltonen/sbt-idea plugin
39 | .idea_modules/
40 |
41 | # JIRA plugin
42 | atlassian-ide-plugin.xml
43 |
44 | # Cursive Clojure plugin
45 | .idea/replstate.xml
46 |
47 | # Crashlytics plugin (for Android Studio and IntelliJ)
48 | com_crashlytics_export_strings.xml
49 | crashlytics.properties
50 | crashlytics-build.properties
51 | fabric.properties
52 | ### VisualStudio template
53 | ## Ignore Visual Studio temporary files, build results, and
54 | ## files generated by popular Visual Studio add-ons.
55 | ##
56 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
57 |
58 | # User-specific files
59 | *.suo
60 | *.user
61 | *.userosscache
62 | *.sln.docstates
63 |
64 | # User-specific files (MonoDevelop/Xamarin Studio)
65 | *.userprefs
66 |
67 | # Build results
68 | [Dd]ebug/
69 | [Dd]ebugPublic/
70 | [Rr]elease/
71 | [Rr]eleases/
72 | x64/
73 | x86/
74 | bld/
75 | [Bb]in/
76 | [Oo]bj/
77 | [Ll]og/
78 |
79 | # Visual Studio 2015 cache/options directory
80 | .vs/
81 | # Uncomment if you have tasks that create the project's static files in wwwroot
82 | #wwwroot/
83 |
84 | # MSTest test Results
85 | [Tt]est[Rr]esult*/
86 | [Bb]uild[Ll]og.*
87 |
88 | # NUNIT
89 | *.VisualState.xml
90 | TestResult.xml
91 |
92 | # Build Results of an ATL Project
93 | [Dd]ebugPS/
94 | [Rr]eleasePS/
95 | dlldata.c
96 |
97 | # Benchmark Results
98 | BenchmarkDotNet.Artifacts/
99 |
100 | # .NET Core
101 | project.lock.json
102 | project.fragment.lock.json
103 | artifacts/
104 | **/Properties/launchSettings.json
105 |
106 | *_i.c
107 | *_p.c
108 | *_i.h
109 | *.ilk
110 | *.meta
111 | *.obj
112 | *.pch
113 | *.pdb
114 | *.pgc
115 | *.pgd
116 | *.rsp
117 | *.sbr
118 | *.tlb
119 | *.tli
120 | *.tlh
121 | *.tmp
122 | *.tmp_proj
123 | *.log
124 | *.vspscc
125 | *.vssscc
126 | .builds
127 | *.pidb
128 | *.svclog
129 | *.scc
130 |
131 | # Chutzpah Test files
132 | _Chutzpah*
133 |
134 | # Visual C++ cache files
135 | ipch/
136 | *.aps
137 | *.ncb
138 | *.opendb
139 | *.opensdf
140 | *.sdf
141 | *.cachefile
142 | *.VC.db
143 | *.VC.VC.opendb
144 |
145 | # Visual Studio profiler
146 | *.psess
147 | *.vsp
148 | *.vspx
149 | *.sap
150 |
151 | # Visual Studio Trace Files
152 | *.e2e
153 |
154 | # TFS 2012 Local Workspace
155 | $tf/
156 |
157 | # Guidance Automation Toolkit
158 | *.gpState
159 |
160 | # ReSharper is a .NET coding add-in
161 | _ReSharper*/
162 | *.[Rr]e[Ss]harper
163 | *.DotSettings.user
164 |
165 | # JustCode is a .NET coding add-in
166 | .JustCode
167 |
168 | # TeamCity is a build add-in
169 | _TeamCity*
170 |
171 | # DotCover is a Code Coverage Tool
172 | *.dotCover
173 |
174 | # AxoCover is a Code Coverage Tool
175 | .axoCover/*
176 | !.axoCover/settings.json
177 |
178 | # Visual Studio code coverage results
179 | *.coverage
180 | *.coveragexml
181 |
182 | # NCrunch
183 | _NCrunch_*
184 | .*crunch*.local.xml
185 | nCrunchTemp_*
186 |
187 | # MightyMoose
188 | *.mm.*
189 | AutoTest.Net/
190 |
191 | # Web workbench (sass)
192 | .sass-cache/
193 |
194 | # Installshield output folder
195 | [Ee]xpress/
196 |
197 | # DocProject is a documentation generator add-in
198 | DocProject/buildhelp/
199 | DocProject/Help/*.HxT
200 | DocProject/Help/*.HxC
201 | DocProject/Help/*.hhc
202 | DocProject/Help/*.hhk
203 | DocProject/Help/*.hhp
204 | DocProject/Help/Html2
205 | DocProject/Help/html
206 |
207 | # Click-Once directory
208 | publish/
209 |
210 | # Publish Web Output
211 | *.[Pp]ublish.xml
212 | *.azurePubxml
213 | # Note: Comment the next line if you want to checkin your web deploy settings,
214 | # but database connection strings (with potential passwords) will be unencrypted
215 | *.pubxml
216 | *.publishproj
217 |
218 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
219 | # checkin your Azure Web App publish settings, but sensitive information contained
220 | # in these scripts will be unencrypted
221 | PublishScripts/
222 |
223 | # NuGet Packages
224 | *.nupkg
225 | # The packages folder can be ignored because of Package Restore
226 | **/[Pp]ackages/*
227 | # except build/, which is used as an MSBuild target.
228 | !**/[Pp]ackages/build/
229 | # Uncomment if necessary however generally it will be regenerated when needed
230 | #!**/[Pp]ackages/repositories.config
231 | # NuGet v3's project.json files produces more ignorable files
232 | *.nuget.props
233 | *.nuget.targets
234 |
235 | # Microsoft Azure Build Output
236 | csx/
237 | *.build.csdef
238 |
239 | # Microsoft Azure Emulator
240 | ecf/
241 | rcf/
242 |
243 | # Windows Store app package directories and files
244 | AppPackages/
245 | BundleArtifacts/
246 | Package.StoreAssociation.xml
247 | _pkginfo.txt
248 | *.appx
249 |
250 | # Visual Studio cache files
251 | # files ending in .cache can be ignored
252 | *.[Cc]ache
253 | # but keep track of directories ending in .cache
254 | !*.[Cc]ache/
255 |
256 | # Others
257 | ClientBin/
258 | ~$*
259 | *~
260 | *.dbmdl
261 | *.dbproj.schemaview
262 | *.jfm
263 | *.pfx
264 | *.publishsettings
265 | orleans.codegen.cs
266 |
267 | # Since there are multiple workflows, uncomment next line to ignore bower_components
268 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
269 | #bower_components/
270 |
271 | # RIA/Silverlight projects
272 | Generated_Code/
273 |
274 | # Backup & report files from converting an old project file
275 | # to a newer Visual Studio version. Backup files are not needed,
276 | # because we have git ;-)
277 | _UpgradeReport_Files/
278 | Backup*/
279 | UpgradeLog*.XML
280 | UpgradeLog*.htm
281 |
282 | # SQL Server files
283 | *.mdf
284 | *.ldf
285 | *.ndf
286 |
287 | # Business Intelligence projects
288 | *.rdl.data
289 | *.bim.layout
290 | *.bim_*.settings
291 |
292 | # Microsoft Fakes
293 | FakesAssemblies/
294 |
295 | # GhostDoc plugin setting file
296 | *.GhostDoc.xml
297 |
298 | # Node.js Tools for Visual Studio
299 | .ntvs_analysis.dat
300 | node_modules/
301 |
302 | # Typescript v1 declaration files
303 | typings/
304 |
305 | # Visual Studio 6 build log
306 | *.plg
307 |
308 | # Visual Studio 6 workspace options file
309 | *.opt
310 |
311 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
312 | *.vbw
313 |
314 | # Visual Studio LightSwitch build output
315 | **/*.HTMLClient/GeneratedArtifacts
316 | **/*.DesktopClient/GeneratedArtifacts
317 | **/*.DesktopClient/ModelManifest.xml
318 | **/*.Server/GeneratedArtifacts
319 | **/*.Server/ModelManifest.xml
320 | _Pvt_Extensions
321 |
322 | # Paket dependency manager
323 | .paket/paket.exe
324 | paket-files/
325 |
326 | # FAKE - F# Make
327 | .fake/
328 |
329 | # JetBrains Rider
330 | .idea/
331 | *.sln.iml
332 |
333 | # IDE - VSCode
334 | .vscode/*
335 | !.vscode/settings.json
336 | !.vscode/tasks.json
337 | !.vscode/launch.json
338 | !.vscode/extensions.json
339 |
340 | # CodeRush
341 | .cr/
342 |
343 | # Python Tools for Visual Studio (PTVS)
344 | __pycache__/
345 | *.pyc
346 |
347 | # Cake - Uncomment if you are using it
348 | # tools/**
349 | # !tools/packages.config
350 |
351 | # Tabs Studio
352 | *.tss
353 |
354 | # Telerik's JustMock configuration file
355 | *.jmconfig
356 |
357 | # BizTalk build output
358 | *.btp.cs
359 | *.btm.cs
360 | *.odx.cs
361 | *.xsd.cs
362 |
363 | # OpenCover UI analysis results
364 | OpenCover/
365 | coverage/
366 |
367 | ### macOS template
368 | # General
369 | .DS_Store
370 | .AppleDouble
371 | .LSOverride
372 |
373 | # Icon must end with two \r
374 | Icon
375 |
376 | # Thumbnails
377 | ._*
378 |
379 | # Files that might appear in the root of a volume
380 | .DocumentRevisions-V100
381 | .fseventsd
382 | .Spotlight-V100
383 | .TemporaryItems
384 | .Trashes
385 | .VolumeIcon.icns
386 | .com.apple.timemachine.donotpresent
387 |
388 | # Directories potentially created on remote AFP share
389 | .AppleDB
390 | .AppleDesktop
391 | Network Trash Folder
392 | Temporary Items
393 | .apdisk
394 |
395 | =======
396 | # Local
397 | .env
398 | dist
399 | .webpack
400 | .serverless/**/*.zip
401 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
5 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/README.md:
--------------------------------------------------------------------------------
1 | ## SWC Project setup
2 |
3 | ```bash
4 | $ npm install
5 | ```
6 |
7 | ## Compile and run the project
8 |
9 | ```bash
10 | # build mail templates
11 | $ npm run build:mail
12 |
13 | # development
14 | $ npm run start
15 |
16 | # watch mode
17 | $ npm run start:dev
18 |
19 | # production mode
20 | $ npm run start:prod
21 | ```
22 |
23 | ## Run tests
24 |
25 | ```bash
26 | # unit tests
27 | $ npm run test
28 |
29 | # e2e tests
30 | $ npm run test:e2e
31 |
32 | # test coverage
33 | $ npm run test:cov
34 | ```
35 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/gulpfile.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 |
3 | const gulp = require('gulp');
4 | const ts = require('gulp-typescript');
5 |
6 | const paths = {
7 | src: 'src/templates/**/*.tsx',
8 | dest: 'dist/templates/',
9 | };
10 |
11 | exports.default = function (cb) {
12 | gulp
13 | .src(paths.src, { sourcemaps: true })
14 | .pipe(ts({ noImplicitAny: true, jsx: 'react-jsx' }))
15 | .pipe(gulp.dest(paths.dest));
16 |
17 | cb();
18 | };
19 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/nest-cli",
3 | "collection": "@nestjs/schematics",
4 | "sourceRoot": "src",
5 | "compilerOptions": {
6 | "builder": "swc",
7 | "typeCheck": true,
8 | "deleteOutDir": false
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@webtre/nestjs-mailer-react-adapter-swc-example",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "This is a demo using @webtre/nestjs-mailer-react-adapter",
6 | "license": "MIT",
7 | "scripts": {
8 | "build:mail": "gulp",
9 | "build": "nest build",
10 | "postbuild": "npm run build:mail",
11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12 | "start": "nest start",
13 | "start:dev": "nest start --watch",
14 | "start:debug": "nest start --debug --watch",
15 | "start:prod": "node dist/main",
16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
17 | "test": "jest",
18 | "test:watch": "jest --watch",
19 | "test:cov": "jest --coverage",
20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/jest/bin/jest --runInBand",
21 | "test:e2e": "jest --config ./test/jest-e2e.json"
22 | },
23 | "dependencies": {
24 | "@nestjs-modules/mailer": "^2.0.2",
25 | "@nestjs/common": "^11.0.11",
26 | "@nestjs/config": "^4.0.1",
27 | "@nestjs/core": "^11.0.11",
28 | "@nestjs/platform-express": "^11.0.11",
29 | "@webtre/nestjs-mailer-react-adapter": "^0.2.4",
30 | "nodemailer": "^6.10.0",
31 | "reflect-metadata": "^0.2.2",
32 | "rxjs": "^7.8.2"
33 | },
34 | "devDependencies": {
35 | "@nestjs/cli": "^11.0.5",
36 | "@nestjs/schematics": "^11.0.2",
37 | "@nestjs/testing": "^11.0.11",
38 | "@swc/cli": "^0.6.0",
39 | "@swc/core": "^1.11.8",
40 | "@types/express": "^5.0.0",
41 | "@types/jest": "^29.5.14",
42 | "@types/node": "^22.13.10",
43 | "@types/supertest": "^6.0.2",
44 | "@typescript-eslint/eslint-plugin": "^6.21.0",
45 | "@typescript-eslint/parser": "^6.21.0",
46 | "eslint": "^8.56.0",
47 | "eslint-config-prettier": "^9.1.0",
48 | "eslint-plugin-prettier": "^5.1.3",
49 | "gulp": "^5.0.0",
50 | "gulp-typescript": "^6.0.0-alpha.1",
51 | "jest": "^29.7.0",
52 | "prettier": "^3.5.3",
53 | "source-map-support": "^0.5.21",
54 | "supertest": "^7.0.0",
55 | "ts-jest": "^29.2.6",
56 | "ts-loader": "^9.5.2",
57 | "ts-node": "^10.9.2",
58 | "tsconfig-paths": "^4.2.0",
59 | "typescript": "^5.3.3"
60 | },
61 | "jest": {
62 | "moduleFileExtensions": [
63 | "js",
64 | "json",
65 | "ts"
66 | ],
67 | "rootDir": "src",
68 | "testRegex": ".*\\.spec\\.ts$",
69 | "transform": {
70 | "^.+\\.(t|j)s$": "ts-jest"
71 | },
72 | "collectCoverageFrom": [
73 | "**/*.(t|j)s"
74 | ],
75 | "coverageDirectory": "../coverage",
76 | "testEnvironment": "node"
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/src/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AppController } from './app.controller';
3 | import { AppService } from './app.service';
4 |
5 | describe('AppController', () => {
6 | let app: TestingModule;
7 |
8 | beforeAll(async () => {
9 | app = await Test.createTestingModule({
10 | controllers: [AppController],
11 | providers: [AppService],
12 | }).compile();
13 | });
14 |
15 | describe('getHello', () => {
16 | it('should return "Hello World!"', () => {
17 | const appController = app.get(AppController);
18 | expect(appController.getHello()).toBe('Hello World!');
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/src/app.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get } from '@nestjs/common';
2 | import { AppService } from './app.service';
3 |
4 | @Controller()
5 | export class AppController {
6 | constructor(private readonly appService: AppService) {}
7 |
8 | @Get()
9 | welcome(): Promise {
10 | return this.appService.sendWelcomeMail();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { MailerModule } from '@nestjs-modules/mailer';
2 | import { Module } from '@nestjs/common';
3 | import { ConfigModule, ConfigType } from '@nestjs/config';
4 | import { ReactAdapter } from '@webtre/nestjs-mailer-react-adapter';
5 | import { AppController } from './app.controller';
6 | import { AppService } from './app.service';
7 | import mailerConfig from './config/mailer.config';
8 |
9 | @Module({
10 | imports: [
11 | ConfigModule.forRoot({
12 | isGlobal: true,
13 | load: [mailerConfig],
14 | envFilePath: __dirname + '/../.env',
15 | }),
16 | MailerModule.forRootAsync({
17 | inject: [mailerConfig.KEY],
18 | useFactory: (mailerConf: ConfigType) => ({
19 | ...mailerConf,
20 | template: {
21 | adapter: new ReactAdapter(),
22 | dir: __dirname + '/templates',
23 | },
24 | }),
25 | }),
26 | ],
27 | controllers: [AppController],
28 | providers: [AppService],
29 | })
30 | export class AppModule {}
31 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/src/app.service.ts:
--------------------------------------------------------------------------------
1 | import { MailerService } from '@nestjs-modules/mailer';
2 | import { Injectable } from '@nestjs/common';
3 |
4 | @Injectable()
5 | export class AppService {
6 | constructor(private readonly mailerService: MailerService) {}
7 |
8 | public async sendWelcomeMail(): Promise {
9 | await this.mailerService.sendMail({
10 | to: 'john@domain.com',
11 | subject: 'Testing react template',
12 | template: 'welcome', // The compiled extension is appended automatically.
13 | context: {
14 | // Data to be passed as props to your template.
15 | code: '123456',
16 | name: 'John Doe',
17 | },
18 | });
19 |
20 | return 'Mail sent!';
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/src/config/mailer.config.ts:
--------------------------------------------------------------------------------
1 | import { MailerOptions } from '@nestjs-modules/mailer';
2 | import { registerAs } from '@nestjs/config';
3 |
4 | export default registerAs('mailer', (): MailerOptions => {
5 | return {
6 | transport: {
7 | service: process.env.MAIL_SERVICE,
8 | host: process.env.MAIL_HOST,
9 | port: parseInt(process.env.MAIL_PORT, 10),
10 | auth: {
11 | user: process.env.MAIL_USERNAME,
12 | pass: process.env.MAIL_PASSWORD,
13 | },
14 | },
15 | defaults: {
16 | from: {
17 | name: process.env.MAIL_FROM_NAME,
18 | address: process.env.MAIL_FROM_ADDRESS,
19 | },
20 | },
21 | };
22 | });
23 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { AppModule } from './app.module';
3 |
4 | async function bootstrap() {
5 | const app = await NestFactory.create(AppModule);
6 | await app.listen(3000);
7 | }
8 | bootstrap();
9 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/src/templates/welcome.tsx:
--------------------------------------------------------------------------------
1 | interface Props {
2 | code: string;
3 | name: string;
4 | }
5 |
6 | export default function Welcome({ name, code }: Props) {
7 | return (
8 |
9 | Hi {name}, thanks for signing up. Your code is {code}
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { INestApplication } from '@nestjs/common';
2 | import { Test } from '@nestjs/testing';
3 | import * as request from 'supertest';
4 | import { AppModule } from '../src/app.module';
5 |
6 | describe('AppController (e2e)', () => {
7 | let app: INestApplication;
8 |
9 | beforeAll(async () => {
10 | const moduleFixture = await Test.createTestingModule({
11 | imports: [AppModule],
12 | }).compile();
13 |
14 | app = moduleFixture.createNestApplication();
15 | await app.init();
16 | });
17 |
18 | afterAll(async () => {
19 | await app.close();
20 | });
21 |
22 | it('/ (GET)', () => {
23 | return request(app.getHttpServer())
24 | .get('/')
25 | .expect(200)
26 | .expect('Hello World!');
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": "\\.e2e-spec\\.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/examples/nestjs-with-swc-example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "ES2021",
10 | "sourceMap": true,
11 | "outDir": "./dist",
12 | "baseUrl": "./",
13 | "incremental": true,
14 | "skipLibCheck": true,
15 | "jsx": "react-jsx"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@webtre/nestjs-mailer-react-adapter",
3 | "version": "0.2.5",
4 | "description": "Build and send emails in Nest framework using React.js",
5 | "main": "./dist/index.js",
6 | "module": "./dist/index.mjs",
7 | "types": "./dist/index.d.ts",
8 | "files": [
9 | "dist/**"
10 | ],
11 | "exports": {
12 | ".": {
13 | "import": {
14 | "types": "./dist/index.d.mts",
15 | "default": "./dist/index.mjs"
16 | },
17 | "require": {
18 | "types": "./dist/index.d.ts",
19 | "default": "./dist/index.js"
20 | }
21 | }
22 | },
23 | "engines": {
24 | "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
25 | },
26 | "scripts": {
27 | "build": "tsup",
28 | "dev": "tsup --watch"
29 | },
30 | "publishConfig": {
31 | "access": "public"
32 | },
33 | "repository": {
34 | "type": "git",
35 | "url": "git+https://github.com/webtretech/nestjs-mailer-react-adapter.git"
36 | },
37 | "author": "Lawrence Onah ",
38 | "license": "MIT",
39 | "bugs": {
40 | "url": "https://github.com/webtretech/nestjs-mailer-react-adapter/issues"
41 | },
42 | "homepage": "https://github.com/webtretech/nestjs-mailer-react-adapter#readme",
43 | "dependencies": {
44 | "@react-email/render": "1.0.2",
45 | "locter": "^2.1.6"
46 | },
47 | "peerDependencies": {
48 | "@nestjs-modules/mailer": "^2.0.0"
49 | },
50 | "devDependencies": {
51 | "@nestjs-modules/mailer": "^2.0.2",
52 | "@types/node": "^22.13.10",
53 | "@types/react": "^19.0.10",
54 | "tsup": "^8.4.0",
55 | "typescript": "^5.8.2"
56 | },
57 | "keywords": [
58 | "nestjs",
59 | "mailer",
60 | "module",
61 | "email",
62 | "nodemailer",
63 | "nodejs",
64 | "mail",
65 | "nest",
66 | "reactjs",
67 | "nestjs-mailer",
68 | "node",
69 | "react",
70 | "mailer-module",
71 | "adapter"
72 | ]
73 | }
74 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./react.adapter";
2 |
--------------------------------------------------------------------------------
/src/react.adapter.tsx:
--------------------------------------------------------------------------------
1 | import path from "node:path";
2 | import { pathToFileURL } from "node:url";
3 |
4 | import { MailerOptions, TemplateAdapter } from "@nestjs-modules/mailer";
5 | import { Options, render } from "@react-email/render";
6 | import { getModuleExport, load } from "locter";
7 |
8 | export class ReactAdapter implements TemplateAdapter {
9 | private config: Options = {
10 | pretty: false,
11 | plainText: false,
12 | };
13 |
14 | constructor(config?: Options) {
15 | Object.assign(this.config, config);
16 | }
17 |
18 | public compile(mail: any, callback: any, options: MailerOptions): void {
19 | const { context, template } = mail.data;
20 | const templateExt = path.extname(template) || ".js";
21 | const templateName = path.basename(template, templateExt);
22 | const templateDir = path.isAbsolute(template)
23 | ? path.dirname(template)
24 | : path.join(options.template.dir, path.dirname(template));
25 | const templatePath = path.join(templateDir, templateName + templateExt);
26 | const templatePathFileURL = pathToFileURL(templatePath).href;
27 |
28 | load(templatePathFileURL)
29 | .then((tmplModule) => {
30 | const moduleDefault = getModuleExport(
31 | tmplModule,
32 | (key) => key === "default"
33 | );
34 | const Comp = moduleDefault.value;
35 |
36 | return render(, this.config);
37 | })
38 | .then((html) => {
39 | mail.data.html = html;
40 | return callback();
41 | })
42 | .catch(callback);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "esModuleInterop": true,
5 | "declaration": true,
6 | "removeComments": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "es2017",
10 | "noImplicitAny": true,
11 | "moduleResolution": "node",
12 | "sourceMap": true,
13 | "outDir": "dist",
14 | "rootDir": "./src",
15 | "skipLibCheck": true,
16 | "jsx": "react-jsx"
17 | },
18 | "include": ["src/**/*"]
19 | }
20 |
--------------------------------------------------------------------------------
/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "tsup";
2 |
3 | export default defineConfig({
4 | entry: ["src/index.ts"],
5 | format: ["cjs", "esm"],
6 | external: ["react"],
7 | sourcemap: false,
8 | clean: true,
9 | dts: true,
10 | });
11 |
--------------------------------------------------------------------------------