Skip to content

Commit 6ac7ecf

Browse files
juandavclaude
andcommitted
feat: auto-changeset on push and update documentation
- Workflow auto-creates patch changeset when none exists - Add OAuth2 authentication docs - Add attachments docs (file, buffer, inline images) - Add custom headers and reply-to docs - Add Docker deployment guide - Add webpack/bundler troubleshooting - Add third-party transports docs - Clarify preview behavior and template paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0059da8 commit 6ac7ecf

File tree

4 files changed

+229
-10
lines changed

4 files changed

+229
-10
lines changed

.github/workflows/release.yml

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,29 @@ jobs:
3838
- name: Run tests
3939
run: pnpm run test --filter=@nestjs-modules/mailer
4040

41-
- name: Create Release Pull Request
41+
- name: Auto-create changeset if none exists
42+
run: |
43+
CHANGESETS=$(ls .changeset/*.md 2>/dev/null | grep -v README.md || true)
44+
if [ -z "$CHANGESETS" ]; then
45+
echo "No changesets found, creating auto patch changeset..."
46+
cat > .changeset/auto-release.md << 'CHANGESET'
47+
---
48+
'@nestjs-modules/mailer': patch
49+
---
50+
51+
Auto-release patch version with latest changes.
52+
CHANGESET
53+
fi
54+
55+
- name: Create Release Pull Request or Publish
4256
id: changesets
4357
uses: changesets/action@v1
4458
with:
59+
publish: pnpm run release
4560
version: pnpm run version-packages
4661
title: 'chore(release): version packages'
4762
commit: 'chore(release): version packages'
4863
env:
4964
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50-
51-
- name: Publish to npm
52-
if: steps.changesets.outputs.hasChangesets == 'false'
53-
working-directory: packages/mailer
54-
run: npm publish --provenance --access public
55-
env:
65+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
5666
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

apps/website/docs/advanced.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,64 @@ MailerModule.forRootAsync({
3838
})
3939
```
4040

41+
## Third-Party Transports
42+
43+
You can use any Nodemailer-compatible transport plugin:
44+
45+
```typescript
46+
import * as sendgridTransport from 'nodemailer-sendgrid';
47+
48+
MailerModule.forRoot({
49+
transport: sendgridTransport({ apiKey: 'SG.xxxx' }),
50+
defaults: {
51+
from: '"App" <noreply@example.com>',
52+
},
53+
})
54+
```
55+
56+
Other popular transports:
57+
- `nodemailer-sendgrid` — SendGrid API
58+
- `nodemailer-mailgun-transport` — Mailgun API
59+
- `nodemailer-ses-transport` — AWS SES (also built-in via Nodemailer)
60+
61+
## Docker Deployment
62+
63+
When deploying with Docker, ensure templates are included in the image:
64+
65+
```dockerfile
66+
FROM node:20-alpine
67+
WORKDIR /app
68+
69+
COPY package.json pnpm-lock.yaml ./
70+
RUN corepack enable && pnpm install --frozen-lockfile --prod
71+
72+
COPY dist/ ./dist/
73+
COPY templates/ ./templates/
74+
75+
CMD ["node", "dist/main.js"]
76+
```
77+
78+
:::tip
79+
- Use **absolute paths** for template directories in production
80+
- Set SMTP credentials via environment variables, not hardcoded values
81+
- If using `preview: true`, disable it in production environments
82+
:::
83+
84+
## Webpack / Custom Bundlers
85+
86+
If you bundle your NestJS app with webpack, the `@css-inline/css-inline` WASM binary may not be included. This is handled automatically in v2.1.x+ via lazy-loading — the binary is loaded at runtime from `node_modules` instead of at build time.
87+
88+
If you still encounter issues, exclude it from your webpack config:
89+
90+
```javascript
91+
// webpack.config.js
92+
module.exports = {
93+
externals: {
94+
'@css-inline/css-inline': 'commonjs @css-inline/css-inline',
95+
},
96+
};
97+
```
98+
4199
## Monorepo Structure
42100

43101
This project is organized as a monorepo using [pnpm workspaces](https://pnpm.io/workspaces) and [Turborepo](https://turbo.build/):

apps/website/docs/configuration.md

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,30 @@ MailerModule.forRoot({
5151
})
5252
```
5353

54+
## OAuth2 Authentication
55+
56+
Use Gmail or other providers with OAuth2:
57+
58+
```typescript
59+
MailerModule.forRoot({
60+
transport: {
61+
service: 'gmail',
62+
auth: {
63+
type: 'OAuth2',
64+
user: 'your-email@gmail.com',
65+
clientId: 'CLIENT_ID',
66+
clientSecret: 'CLIENT_SECRET',
67+
refreshToken: 'REFRESH_TOKEN',
68+
},
69+
},
70+
defaults: {
71+
from: '"App Name" <your-email@gmail.com>',
72+
},
73+
})
74+
```
75+
76+
See [Nodemailer OAuth2 docs](https://nodemailer.com/smtp/oauth2/) for details on setting up OAuth2 credentials.
77+
5478
## Async Configuration
5579

5680
Use `forRootAsync()` to load config from environment or external services:
@@ -92,16 +116,23 @@ export class AppModule {}
92116

93117
## Multiple Transporters
94118

95-
Configure multiple SMTP servers and switch between them per message:
119+
Configure multiple SMTP servers with different credentials and switch between them per message:
96120

97121
```typescript
98122
MailerModule.forRoot({
99123
transports: {
100-
primary: 'smtps://user@domain.com:pass@smtp.domain.com',
124+
primary: {
125+
host: 'smtp.example.com',
126+
port: 587,
127+
auth: { user: 'primary@example.com', pass: 'pass1' },
128+
},
101129
secondary: {
102130
host: 'smtp.other.com',
103131
port: 587,
104-
auth: { user: 'user', pass: 'pass' },
132+
auth: { user: 'secondary@other.com', pass: 'pass2' },
133+
},
134+
ses: {
135+
SES: { /* AWS SES config */ },
105136
},
106137
},
107138
defaults: {
@@ -125,6 +156,8 @@ await this.mailerService.sendMail({
125156
});
126157
```
127158

159+
All transport types are supported for additional transporters: SMTP, SES, Sendmail, Stream, JSON, and custom transports.
160+
128161
## Dynamic Transporters
129162

130163
Add transporters at runtime using `addTransporter()`:
@@ -135,6 +168,14 @@ this.mailerService.addTransporter('custom', {
135168
port: 587,
136169
auth: { user: 'user', pass: 'pass' },
137170
});
171+
172+
// Use immediately
173+
await this.mailerService.sendMail({
174+
transporterName: 'custom',
175+
to: 'user@example.com',
176+
subject: 'Hello',
177+
html: '<p>Hello!</p>',
178+
});
138179
```
139180

140181
## Verify Transporters
@@ -148,6 +189,8 @@ MailerModule.forRoot({
148189
})
149190
```
150191

192+
This checks SMTP connectivity when the module initializes. If verification fails, a warning is logged but the application continues to start.
193+
151194
Or verify programmatically:
152195

153196
```typescript
@@ -158,6 +201,10 @@ const allReady = await this.mailerService.verifyAllTransporters();
158201

159202
Use `preview-email` to open a preview in the browser during development:
160203

204+
```bash
205+
pnpm add preview-email
206+
```
207+
161208
```typescript
162209
MailerModule.forRoot({
163210
transport: {
@@ -177,6 +224,13 @@ MailerModule.forRoot({
177224
})
178225
```
179226

227+
:::note
228+
- `preview-email` is an **optional** dependency. Install it only if you use this feature.
229+
- Preview does **not** prevent the email from being sent — it runs alongside sending.
230+
- Preview may not work in headless environments (Docker, CI servers).
231+
- You can pass options: `preview: { open: { wait: false } }`.
232+
:::
233+
180234
## Copy Templates to `dist`
181235

182236
If your templates are inside `src/`, add them as assets in `nest-cli.json`:
@@ -193,3 +247,7 @@ If your templates are inside `src/`, add them as assets in `nest-cli.json`:
193247
```
194248

195249
Use the appropriate extension (`.hbs`, `.pug`, `.ejs`) for your template engine.
250+
251+
:::warning
252+
Templates are **not** compiled by TypeScript. If you reference templates with `__dirname`, make sure they exist in the `dist` folder at runtime.
253+
:::

apps/website/docs/sending-emails.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,97 @@ await this.mailerService.sendMail({
6464
});
6565
```
6666

67+
:::tip
68+
When using absolute paths, make sure template files are copied to the `dist` folder at build time. See [Copy Templates to dist](/docs/configuration#copy-templates-to-dist).
69+
:::
70+
71+
## Attachments
72+
73+
Send emails with file attachments:
74+
75+
```typescript
76+
await this.mailerService.sendMail({
77+
to: 'user@example.com',
78+
subject: 'Invoice',
79+
html: '<p>Please find your invoice attached.</p>',
80+
attachments: [
81+
{
82+
filename: 'invoice.pdf',
83+
path: '/absolute/path/to/invoice.pdf',
84+
},
85+
],
86+
});
87+
```
88+
89+
### Multiple Attachments
90+
91+
```typescript
92+
await this.mailerService.sendMail({
93+
to: 'user@example.com',
94+
subject: 'Report',
95+
template: 'report',
96+
context: { title: 'Monthly Report' },
97+
attachments: [
98+
// File from disk
99+
{ filename: 'report.pdf', path: '/path/to/report.pdf' },
100+
// Buffer
101+
{ filename: 'data.csv', content: Buffer.from('id,name\n1,John') },
102+
// String content
103+
{ filename: 'notes.txt', content: 'Some notes here' },
104+
// URL (nodemailer will fetch it)
105+
{ filename: 'logo.png', path: 'https://example.com/logo.png' },
106+
],
107+
});
108+
```
109+
110+
### Inline Images (Embedded)
111+
112+
Embed images directly in the HTML using `cid`:
113+
114+
```typescript
115+
await this.mailerService.sendMail({
116+
to: 'user@example.com',
117+
subject: 'Newsletter',
118+
html: '<img src="cid:logo" alt="Logo" />',
119+
attachments: [
120+
{
121+
filename: 'logo.png',
122+
path: '/path/to/logo.png',
123+
cid: 'logo',
124+
},
125+
],
126+
});
127+
```
128+
129+
See [Nodemailer attachments docs](https://nodemailer.com/message/attachments/) for all options.
130+
131+
## Custom Headers
132+
133+
```typescript
134+
await this.mailerService.sendMail({
135+
to: 'user@example.com',
136+
subject: 'Hello',
137+
html: '<p>Hello!</p>',
138+
headers: {
139+
'X-Custom-Header': 'custom-value',
140+
'X-Priority': '1',
141+
},
142+
});
143+
```
144+
145+
## Reply-To and Threading
146+
147+
```typescript
148+
await this.mailerService.sendMail({
149+
to: 'user@example.com',
150+
replyTo: 'support@example.com',
151+
subject: 'Re: Your ticket',
152+
html: '<p>We received your request.</p>',
153+
inReplyTo: '<original-message-id@example.com>',
154+
references: ['<original-message-id@example.com>'],
155+
});
156+
```
157+
67158
## Using a Specific Transporter
68159

69160
When you have multiple transporters configured, specify which one to use:
@@ -76,3 +167,5 @@ await this.mailerService.sendMail({
76167
html: '<p>Hello!</p>',
77168
});
78169
```
170+
171+
See [Multiple Transporters](/docs/configuration#multiple-transporters) for setup instructions.

0 commit comments

Comments
 (0)