Full Code of squareboat/nest-mailman for AI

main c4dcbd453e77 cached
37 files
30.0 KB
8.8k tokens
56 symbols
1 requests
Download .txt
Repository: squareboat/nest-mailman
Branch: main
Commit: c4dcbd453e77
Files: 37
Total size: 30.0 KB

Directory structure:
gitextract_6x4a5o5l/

├── .gitignore
├── .npmignore
├── CONTRIBUTING.MD
├── LICENSE.md
├── README.md
├── copymailman.js
├── jest.json
├── lib/
│   ├── constants.ts
│   ├── index.ts
│   ├── interfaces/
│   │   ├── MailCompiler.ts
│   │   ├── index.ts
│   │   ├── mjml.ts
│   │   └── options.ts
│   ├── mailman.ts
│   ├── message.ts
│   ├── module.ts
│   ├── provider.map.ts
│   └── service.ts
├── package.json
├── tsconfig.json
└── views/
    ├── assets/
    │   └── style.css
    ├── components/
    │   ├── body.tsx
    │   ├── bodyBuilder.tsx
    │   ├── button.tsx
    │   ├── description.tsx
    │   ├── divider.tsx
    │   ├── footer.tsx
    │   ├── greeting.tsx
    │   ├── head.tsx
    │   ├── header.tsx
    │   ├── html.tsx
    │   ├── image.tsx
    │   ├── regards.tsx
    │   ├── table.tsx
    │   └── text.tsx
    └── mail/
        ├── generic.tsx
        └── index.tsx

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# dependencies
/node_modules

# IDE
/.idea
/.awcache
/.vscode

# misc
npm-debug.log
.DS_Store

# tests
/test
/coverage
/.nyc_output

# dist
dist

================================================
FILE: .npmignore
================================================
# source
lib
tests
index.ts
package-lock.json
tslint.json
tsconfig.json
.prettierrc

# github
.github
CONTRIBUTING.MD

# misc
.commitlintrc.json
.release-it.json
.eslintignore
.eslintrc.js

================================================
FILE: CONTRIBUTING.MD
================================================


================================================
FILE: LICENSE.md
================================================
# The MIT License (MIT)

Copyright © 2020 [SquareBoat](https://squareboat.com)

> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.

================================================
FILE: README.md
================================================
# Nest Mailman

📮 The mailer package for your NestJS Applications.

### Features

- ✅ Build mails programmtically
- ✅ Supports MJML + React templating
- ✅ Use JSX to easily create clean components
- ✅ Comes with built-in template to quickly send mails without creating templates.
- ✅ Uses nodemailer internally

For complete documentation, head over to [our site](https://squareboat.com/open-source/nest-mailman/).

---

## About Us

We are a bunch of dreamers, designers, and futurists. We are high on collaboration, low on ego, and take our happy hours seriously. We'd love to hear more about your product. Let's talk and turn your great ideas into something even greater! We have something in store for everyone. [☎️ 📧 Connect with us!](https://squareboat.com/contact)

---

## License

The MIT License. Please see License File for more information. Copyright © 2020 SquareBoat.

Made with ❤️ by [Squareboat](https://squareboat.com)


================================================
FILE: copymailman.js
================================================
const fs = require("fs-extra");
var appRoot = require("app-root-path");
const picocolors = require("picocolors");

function handle() {
  const path = `${appRoot.path}/resources/views`;
  if (fs.existsSync(path)) {
    console.log(picocolors.blue`➡️ ${path} already exists. Returning...`);
    console.log(
      picocolors.blue`➡️ To copy file, copy from ${picocolors.white(
        process.cwd() + "/cli.js"
      )} to ${picocolors.white(path)}`
    );
    return;
  }

  fs.copySync("./views", `${appRoot.path}/resources/views`, {
    overwrite: true | false,
  });

  //   (err) => {
  //     if (err) throw err;
  //     console.log(
  //       picocolors.green`🚀 Copying cli.js file to ${appRoot.path}/cli`,
  //     );
  //     console.log(
  //       picocolors.yellow`❓ To know more about on how to change default module and path in cli, go to https://github.com/squareboat/nest-console`,
  //     );
}

handle();


================================================
FILE: jest.json
================================================
{
  "moduleFileExtensions": ["ts", "tsx", "js", "json"],
  "transform": {
    "^.+\\.tsx?$": "ts-jest"
  },
  "testRegex": "/lib/.*\\.(test|spec).(ts|tsx|js)$",
  "collectCoverageFrom": [
    "lib/**/*.{js,jsx,tsx,ts}",
    "!**/node_modules/**",
    "!**/vendor/**"
  ],
  "coverageReporters": ["json", "lcov","text-summary"]
}

================================================
FILE: lib/constants.ts
================================================
export const MAILMAN_QUEUE = "MAILMAN_QUEUE";
export const SEND_MAIL = "SEND_MAIL";

// Mail Formats
export const RAW_MAIL = "RAW";
export const VIEW_BASED_MAIL = "VIEW_BASED";
export const GENERIC_MAIL = "GENERIC";

export class MailmanConstant {}


================================================
FILE: lib/index.ts
================================================
export * from "./module";
export * from "./service";
export * from "./mailman";
export * from "./interfaces";
export * from "./message";


================================================
FILE: lib/interfaces/MailCompiler.ts
================================================
// All mail compilers(Handlebars, Markdown, etc.) will implement this interface.
export interface MailCompiler {
  filePath: string;
  compileMail(options: Record<string, any> | undefined ): string;
}

================================================
FILE: lib/interfaces/index.ts
================================================
export * from './options';
export * from './MailCompiler';
export * from './mjml';


================================================
FILE: lib/interfaces/mjml.ts
================================================
export interface MJMLParsingOpts {
  fonts?: { [key: string]: string };
  keepComments?: boolean;
  beautify?: boolean;
  minify?: boolean;
  validationLevel?: 'strict' | 'soft' | 'skip';
  filePath?: string;
  minifyOptions?: MJMLMinifyOptions;
}

interface MJMLMinifyOptions {
  collapseWhitespace?: boolean;
  minifyCSS?: boolean;
  removeEmptyAttributes?: boolean;
}


================================================
FILE: lib/interfaces/options.ts
================================================
import { ModuleMetadata, Type } from "@nestjs/common/interfaces";
import { Attachment } from "nodemailer/lib/mailer";
import { MailMessage } from "../message";
import { MJMLParsingOpts } from "./mjml";

export interface MailmanBaseTemplateOptions {
  appName?: string;
  appLogoSrc?: string;
  socialMedia?: {
    name: string;
    href: string;
  }[];
  contactEmail?: string;
}

export interface MailmanMetaPayload {
  title?: string;
  preview?: string;
}

export interface MailmanPayload {
  _templateConfig?: MailmanBaseTemplateOptions;
  meta?: MailmanMetaPayload;
  genericFields?: Record<string, any>[];
}

export interface MailmanOptions {
  host: string;
  port: number;
  username: string;
  password: string;
  from: string;
  ignoreTLS?: boolean;
  replyTo?: string;
  path?: string;
  mjml?: MJMLParsingOpts;
  templateConfig: {
    baseComponent: (payload: Record<string, any>) => JSX.Element;
    templateOptions?: MailmanBaseTemplateOptions;
  };
}

export type CompilerOptions = {
  configPath: string;
  mjml?: MJMLParsingOpts;
};

export interface MailmanOptionsFactory {
  createMailmanOptions(): Promise<MailmanOptions> | MailmanOptions;
}

export interface MailmanAsyncOptions extends Pick<ModuleMetadata, "imports"> {
  name?: string;
  useExisting?: Type<MailmanOptions>;
  useClass?: Type<MailmanOptionsFactory>;
  useFactory?: (...args: any[]) => Promise<MailmanOptions> | MailmanOptions;
  inject?: any[];
}

export interface MailData {
  subject?: string;
  html: string;
  attachments: Attachment[];
}

export interface SendMailOptions {
  sender: string;
  replyTo?: string;
  inReplyTo?: string;
  mail: MailMessage;
  cc: string | string[];
  bcc: string | string[];
  receipents: string | string[];
}

export type MailType = "RAW" | "VIEW_BASED" | "GENERIC";


================================================
FILE: lib/mailman.ts
================================================
import { MailmanService } from './service';
import { MailMessage } from './message';
import { MailData } from './interfaces';
export class Mailman {
  private receipents: string | string[];
  private ccReceipents: string | string[];
  private bccReceipents: string | string[];
  private sender: string;
  private _replyTo: string;
  private _inReplyTo: string;

  private constructor() {
    this.sender = '';
    this._replyTo = '';
    this._inReplyTo = '';
    this.receipents = '';
    this.ccReceipents = '';
    this.bccReceipents = '';
  }

  /**
   * Returns new instance
   */
  static init() {
    return new Mailman();
  }

  /**
   * `FROM` in email address.
   * Use this method to override the `from` address provided in configuration.
   * @param sender
   */
  from(sender: string): this {
    this.sender = sender;
    return this;
  }

  /**
   * `REPLY_TO` in email address.
   * Use this method to override the `reply_to` address provided in configuration or to add one.
   * @param replyToEmail
   */
  replyTo(replyToEmail: string): this {
    this._replyTo = replyToEmail;
    return this;
  }

  /**
   * `IN_REPLY_TO` in email address.
   * Use this method to provide the `in_reply_to` header.
   * @param replyToEmail
   */
  inReplyTo(messageId: string): this {
    this._inReplyTo = messageId;
    return this;
  }

  /**
   * `TO` in email address
   * @param receipents
   */
  to(receipents: string | string[]): this {
    this.receipents = receipents;
    return this;
  }

  /**
   * `CC` in email addres
   * @param ccreceipents
   */
  cc(ccReceipents: string | string[]): this {
    this.ccReceipents = ccReceipents;
    return this;
  }

  /**
   * `BCC` in email address
   * @param bccReceipents
   */
  bcc(bccReceipents: string | string[]): this {
    this.bccReceipents = bccReceipents;
    return this;
  }

  /**
   * Send mail
   * @param mail
   */
  send(mail: MailMessage) {
    return MailmanService.send({
      mail,
      cc: this.ccReceipents,
      bcc: this.bccReceipents,
      sender: this.sender,
      replyTo: this._replyTo,
      inReplyTo: this._inReplyTo,
      receipents: this.receipents,
    });
  }
}


================================================
FILE: lib/message.ts
================================================
import { Attachment } from "nodemailer/lib/mailer";
import { GENERIC_MAIL, RAW_MAIL, VIEW_BASED_MAIL } from "./constants";
import {
  MailData,
  MailType,
  MailmanMetaPayload,
  MailmanPayload,
} from "./interfaces";
import { MailmanService } from "./service";
import { renderToMjml } from "@faire/mjml-react/utils/renderToMjml";
import mjml2html from "mjml";

export class MailMessage {
  private mailSubject?: string;
  private viewFile?: (payload: Record<string, any>) => JSX.Element;
  private templateString?: string;
  private payload: MailmanPayload = {};
  private mailType: MailType;
  private compiledHtml: string;
  private attachments: Record<string, Attachment>;

  constructor() {
    this.attachments = {};
    this.compiledHtml = "";
    this.mailType = RAW_MAIL;
  }

  /**
   * static method to create new instance of the MailMessage class
   */
  static init(): MailMessage {
    return new MailMessage();
  }

  /**
   * Define subject of the mail
   * @param subject
   */
  subject(subject: string): this {
    this.mailSubject = subject;
    return this;
  }

  /**
   * Define the view to be used for the mail
   * @param viewFile
   * @param payload
   */
  view(
    component: (payload: Record<string, any>) => JSX.Element,
    payload?: Record<string, any>
  ): this {
    this.mailType = VIEW_BASED_MAIL;
    this.viewFile = component;
    this.payload = payload || {};
    return this;
  }

  /**
   * Define the template string to be used for the mail
   * @param template
   * @param payload
   */
  raw(template: string, payload?: Record<string, any>): this {
    this.mailType = RAW_MAIL;
    this.templateString = template;
    this.payload = payload || {};
    return this;
  }

  /**
   * Add attachment to the mail
   * @param greeting
   */
  attach(filename: string, content: Omit<Attachment, "filename">): this {
    this.attachments[filename] = { ...content, filename };
    return this;
  }

  /**
   * ==> Generic Template Method <==
   * Use this method for adding the greeting to the generic mail
   * @param greeting
   */
  greeting(greeting: string): this {
    this._setGenericMailProperties();
    if (this.payload.genericFields) {
      this.payload?.genericFields.push({ greeting });
    }
    return this;
  }

  /**
   * ==> Generic Template Method <==
   * Use this method for adding a text line to the generic mail
   * @param line
   */
  line(line: string): this {
    this._setGenericMailProperties();
    if (this.payload.genericFields) {
      this.payload?.genericFields.push({ line });
    }
    return this;
  }

  html(html: string): this {
    this._setGenericMailProperties();
    if (this.payload.genericFields) {
      this.payload?.genericFields.push({ html });
    }
    return this;
  }

  /**
   * ==> Generic Template Method <==
   * Use this method for adding a url action to the generic mail
   * @param text
   * @param link
   */
  action(text: string, link: string): this {
    this._setGenericMailProperties();
    if (this.payload.genericFields) {
      this.payload.genericFields.push({ action: { text, link } });
    }
    return this;
  }

  /**
   * ==> Generic Template Method <==
   * Use this method for adding a table to the generic mail
   * @param data
   */
  table(data: Record<string, any>[], showHeading = true, vertical = false): this {
    this._setGenericMailProperties();
    if (this.payload.genericFields) {
      this.payload.genericFields.push({ table: data, showHeading, vertical: vertical });
    }

    return this;
  }

  meta(payload: MailmanMetaPayload): this {
    this.payload.meta = payload;
    return this;
  }

  /**
   * ==> Generic Template Method <==
   * @param greeting
   */
  private _setGenericMailProperties() {
    this.mailType = GENERIC_MAIL;
    if (!this.payload || !this.payload.genericFields) {
      this.payload.genericFields = [];
    }
  }

  /**
   * Method to compile templates
   */
  private _compileTemplate(): string {
    if (this.compiledHtml) return this.compiledHtml;
    const config = MailmanService.getConfig();

    const componentData = {
      ...this.payload,
      _templateConfig: config.templateConfig.templateOptions,
    };

    if (this.mailType === GENERIC_MAIL) {
      const component = config.templateConfig?.baseComponent;

      if (!component) {
        throw new Error(
          "BaseComponent not found for generic view, please check if you have set the baseComponent attribute in config correctly."
        );
      }

      const { html } = mjml2html(
        renderToMjml(component(componentData)),
        config.mjml
      );
      this.compiledHtml = html;
      return this.compiledHtml;
    }

    if (this.mailType === VIEW_BASED_MAIL && this.viewFile) {
      const component = this.viewFile;
      const { html } = mjml2html(
        renderToMjml(component(componentData)),
        config.mjml
      );

      this.compiledHtml = html;
      return this.compiledHtml;
    }

    if (this.mailType === RAW_MAIL && this.templateString) {
      return this.compiledHtml;
    }

    return this.compiledHtml;
  }

  /**
   * Returns the maildata payload
   */
  getMailData(): MailData {
    if (typeof (this as any).handle === "function") {
      (this as any)["handle"]();
    }

    return {
      subject: this.mailSubject,
      html: this._compileTemplate(),
      attachments: Object.values(this.attachments),
    };
  }

  /**
   * Render the email template.
   * Returns the complete html of the mail.
   */
  render(): string {
    return this._compileTemplate();
  }
}


================================================
FILE: lib/module.ts
================================================
import { map } from "./provider.map";
import { Module, DynamicModule, Provider, Type } from "@nestjs/common";
import { MailmanService } from "./service";
import {
  MailmanOptions,
  MailmanAsyncOptions,
  MailmanOptionsFactory,
} from "./interfaces";

@Module({})
export class MailmanModule {
  /**
   * Register options
   * @param options
   */
  static register(options: MailmanOptions): DynamicModule {
    return {
      global: true,
      module: MailmanModule,
      providers: [
        MailmanService,
        { provide: map.MAILABLE_OPTIONS, useValue: options },
      ],
    };
  }

  /**
   * Register Async Options
   */
  static registerAsync(options: MailmanAsyncOptions): DynamicModule {
    return {
      global: true,
      module: MailmanModule,
      imports: [],
      providers: [MailmanService, this.createStorageOptionsProvider(options)],
    };
  }

  private static createStorageOptionsProvider(
    options: MailmanAsyncOptions
  ): Provider {
    if (options.useFactory) {
      return {
        provide: map.MAILABLE_OPTIONS,
        useFactory: options.useFactory,
        inject: options.inject || [],
      };
    }

    const inject = [
      (options.useClass || options.useExisting) as Type<MailmanOptions>,
    ];

    return {
      provide: map.MAILABLE_OPTIONS,
      useFactory: async (optionsFactory: MailmanOptionsFactory) =>
        await optionsFactory.createMailmanOptions(),
      inject,
    };
  }
}


================================================
FILE: lib/provider.map.ts
================================================
export const map = {
  MAILABLE_OPTIONS: 'MAILABLE_OPTIONS',
};


================================================
FILE: lib/service.ts
================================================
import { map } from "./provider.map";
import * as nodemailer from "nodemailer";
import { Injectable, Inject } from "@nestjs/common";
import { MailmanOptions, MailData, SendMailOptions } from "./interfaces";

@Injectable()
export class MailmanService {
  private static options: MailmanOptions;
  private static transporter: any;

  constructor(@Inject(map.MAILABLE_OPTIONS) options: MailmanOptions) {
    MailmanService.options = options;
    MailmanService.transporter = nodemailer.createTransport(
      {
        host: options.host,
        port: options.port,
        ignoreTLS: options.ignoreTLS,
        auth: { user: options.username, pass: options.password },
      },
      { from: options.from }
    );
  }

  static getConfig(): MailmanOptions {
    return MailmanService.options;
  }

  static async send(options: SendMailOptions) {
    const config = MailmanService.options;
    const mailData: MailData = options.mail.getMailData();
    const mail: Record<string, any> = {
      to: options.receipents,
      cc: options.cc,
      bcc: options.bcc,
      from: options.sender || config.from,
      html: mailData.html,
      subject: mailData.subject,
      attachments: mailData.attachments,
    };
    if (options.replyTo || config.replyTo) {
      mail.replyTo = options.replyTo || config.replyTo;
    }
    if (options.inReplyTo) {
      mail.inReplyTo = options.inReplyTo;
    }
    await MailmanService.transporter.sendMail(mail);
  }
}


================================================
FILE: package.json
================================================
{
  "name": "@squareboat/nest-mailman",
  "version": "1.0.5",
  "description": "📮 The mailer package for your NestJS Applications",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "keywords": [
    "nestjs",
    "nodemailer",
    "nestjs-mail",
    "nestjs-mailing",
    "nestjs-mailer",
    "nestjs-mailman"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/squareboat/nest-mailman.git"
  },
  "bugs": {
    "url": "https://github.com/squareboat/nest-mailman/issues"
  },
  "homepage": "https://github.com/squareboat/nest-mailman",
  "author": "Vinayak Sarawagi <vinayaksarawagi25@gmail.com>",
  "private": false,
  "license": "MIT",
  "scripts": {
    "postinstall": "node copymailman.js",
    "build": "rm -rf dist && tsc -p tsconfig.json",
    "format": "prettier --write \"**/*.ts\"",
    "lint": "eslint 'lib/**/*.ts' --fix",
    "prepublish:npm": "npm run build",
    "publish:npm": "npm publish --access public",
    "prepublish:next": "npm run build",
    "publish:next": "npm publish --access public --tag next",
    "test": "jest --config=jest.json",
    "test:cov": "jest --config=jest.json --coverage",
    "test:e2e": "jest --config ./tests/jest-e2e.json --runInBand",
    "test:e2e:dev": "jest --config ./tests/jest-e2e.json --runInBand --watch"
  },
  "devDependencies": {
    "@nestjs/common": "^9.3.9",
    "@nestjs/core": "^9.3.9",
    "@types/mjml": "^4.7.0",
    "@types/nodemailer": "^6.4.7",
    "@types/react": "^18.0.28",
    "typescript": "^4.9.5"
  },
  "dependencies": {
    "@faire/mjml-react": "^3.1.2",
    "app-root-path": "^3.1.0",
    "fs-extra": "^11.1.0",
    "mjml": "^4.7.1",
    "nodemailer": "^6.9.1",
    "picocolors": "^1.0.0",
    "reflect-metadata": "^0.1.13"
  },
  "peerDependencies": {
    "@nestjs/common": "^8.0.0 || ^9.0.0",
    "@nestjs/core": "^8.0.0 || ^9.0.0"
  }
}


================================================
FILE: tsconfig.json
================================================
{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "strict": true,
    "removeComments": true,
    "noLib": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es6",
    "sourceMap": false,
    "outDir": "./dist",
    "rootDir": "./lib",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowJs": true,
    "jsx": "react"
  },
  "include": ["lib/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts", "tests", "views"]
}


================================================
FILE: views/assets/style.css
================================================
.default-bg {
  background-color: #d1deec;
}

.header {
  background-color: #f5f6f6;
}

.footer {
  background-color: #f5f6f6;
}

.card {
  background-color: #f5f6f6;
  border-radius: 8px;
  margin-left: 20px;
  margin-right: 20px;
}

.styled-table {
  /* border-collapse: collapse; */
  font-size: 0.9em;
  font-family: sans-serif;
  border-radius: 10em;
  overflow: hidden;
}

.styled-table thead tr {
  background-color: #0275d8;
  color: #ffffff;
  text-align: left;
}

.styled-table th {
  background-color: #0275d8;
  color: #ffffff;
  text-align: left;
}

.styled-table th,
.styled-table td {
  padding: 12px 15px;
}

.styled-table tbody tr {
  border-bottom: 2px solid #dddddd;
}

.styled-table tbody tr:nth-of-type(even) {
  background-color: #f3f3f3;
}

.styled-table tbody tr:last-of-type {
  border-bottom: 2px solid #0275d8;
}

.styled-table tbody tr.active-row {
  font-weight: bold;
  color: #2d94f3;
}


================================================
FILE: views/components/body.tsx
================================================
import { MjmlBody } from "@faire/mjml-react";
import React from "react";

export const MailmanBody = ({ children }: { children: JSX.Element }) => {
  return <MjmlBody className="default-bg">{children}</MjmlBody>;
};


================================================
FILE: views/components/bodyBuilder.tsx
================================================
import { MjmlColumn, MjmlSection } from "@faire/mjml-react";
import { MailmanButton } from "./button";
import { MailmanDivider } from "./divider";
import { Greeting } from "./greeting";
import { TextLine } from "./text";
import React from "react";
import { MailmanTable } from "./table";
import { RawHtml } from "./html";

export const MailmanBodyBuilder = (payload: Record<string, any>) => {
  return (
    <MjmlSection backgroundColor="#ffffff">
      <MjmlSection padding={0}>
        <MjmlColumn>
          {payload.fields.map((obj) => {
            return ComponentView(obj);
          })}
        </MjmlColumn>
      </MjmlSection>
    </MjmlSection>
  );
};

const ComponentView = (payload: Record<string, any>) => {
  if (payload.greeting) {
    return <Greeting value={payload.greeting} />;
  }

  if (payload.line) {
    return <TextLine value={payload.line} />;
  }

  if (payload.divider) {
    return <MailmanDivider />;
  }

  if (payload.html) {
    return <RawHtml value={payload.html} />;
  }

  if (payload.action) {
    return <MailmanButton value={payload.action} />;
  }

  if (payload.table) {
    return <MailmanTable value={payload.table} header={payload.showHeading} vertical={payload.vertical} />;
  }

  return <></>;
};


================================================
FILE: views/components/button.tsx
================================================
import { MjmlButton } from "@faire/mjml-react";
import React from "react";

export const MailmanButton = (payload: Record<string, any>) => {
  return (
    <>
      <MjmlButton
        backgroundColor="#0275d8"
        color="white"
        borderRadius={8}
        href={payload.value.link}
      >
        <strong>{payload.value.text}</strong>
      </MjmlButton>
    </>
  );
};


================================================
FILE: views/components/description.tsx
================================================
import { MjmlColumn, MjmlText } from "@faire/mjml-react";
import React from "react";

export const Description = (payload: Record<string, any>) => {
  return (
    <MjmlColumn padding={0}>
      <MjmlText paddingLeft={20} paddingTop={0}>
        <strong>{payload.value}</strong>
      </MjmlText>
    </MjmlColumn>
  );
};


================================================
FILE: views/components/divider.tsx
================================================
import { MjmlDivider } from "@faire/mjml-react";
import React from "react";

export const MailmanDivider = (payload: Record<string, any>) => {
  return <MjmlDivider borderWidth={1} borderColor="lightgrey" />;
};


================================================
FILE: views/components/footer.tsx
================================================
import {
  MjmlColumn,
  MjmlDivider,
  MjmlImage,
  MjmlSection,
  MjmlSocial,
  MjmlSocialElement,
  MjmlSpacer,
  MjmlText,
  MjmlWrapper,
} from "@faire/mjml-react";
import React from "react";

export const MailmanFooter = (payload: Record<string, any>) => {
  const appLogoSrc = payload.config.appLogoSrc;
  const appName = payload.config.appName;
  const socialMedia = payload.config.socialMedia;
  const contactEmail = payload.config.contactEmail;

  return (
    <>
      <MjmlWrapper className="footer">
        <MjmlSection padding={0}>
          <MjmlColumn>
            <MjmlImage
              src={appLogoSrc}
              alt={appName}
              align="left"
              width="120px"
              padding="0px 20px 20px 20px"
            />
          </MjmlColumn>

          <MjmlColumn>
            <MjmlSocial
              icon-size="28px"
              mode="horizontal"
              align="right"
              padding="20px"
            >
              {socialMedia?.map((a) => (
                <>
                  <MjmlSocialElement
                    name={a.name}
                    href={a.href}
                    backgroundColor="#DCDCDC"
                    color="darkgrey"
                  />
                </>
              ))}
            </MjmlSocial>
          </MjmlColumn>
        </MjmlSection>

        <MjmlSection padding={0}>
          <MjmlColumn padding={0}>
            <MjmlDivider borderWidth={1} borderColor="#d1deec" />
          </MjmlColumn>
        </MjmlSection>

        <MjmlSection padding={0}>
          <MjmlColumn>
            {contactEmail && (
              <MjmlText align="center">Contact Us: {contactEmail}</MjmlText>
            )}

            <MjmlText align="center">
              © {new Date().getFullYear()} {appName}
            </MjmlText>
          </MjmlColumn>
        </MjmlSection>
      </MjmlWrapper>
      <MjmlSpacer height={50} />
    </>
  );
};


================================================
FILE: views/components/greeting.tsx
================================================
import { MjmlText } from "@faire/mjml-react";
import React from "react";

export const Greeting = (payload: Record<string, any>) => {
  return (
    <MjmlText paddingTop={5} paddingBottom={5}>
      <h2>{payload.value}</h2>
    </MjmlText>
  );
};


================================================
FILE: views/components/head.tsx
================================================
import { MjmlHead, MjmlPreview, MjmlStyle, MjmlTitle } from "@faire/mjml-react";
import { readFileSync } from "fs";
import React from "react";

const css = readFileSync("resources/views/assets/style.css").toString();

export const MailmanHead = (payload: Record<string, any>) => {
  return (
    <MjmlHead>
      <MjmlTitle>{payload.title}</MjmlTitle>
      <MjmlPreview>{payload.preview || payload.title}</MjmlPreview>
      <MjmlStyle>{css}</MjmlStyle>
    </MjmlHead>
  );
};


================================================
FILE: views/components/header.tsx
================================================
import {
  MjmlColumn,
  MjmlImage,
  MjmlSection,
  MjmlSpacer,
} from "@faire/mjml-react";
import React from "react";

{
  /* <mj-section>
<mj-column>
    <mj-spacer height="10px"></mj-spacer>
</mj-column>
</mj-section>
<mj-section css-class="header">
<mj-column>
    <mj-image src="https://d1s70uz4swygev.cloudfront.net/img_small/atadel-logo.png" alt="Atadel Logo"
        align="center" width="200px" padding="0px">
    </mj-image>
</mj-column>
</mj-section> */
}

export const MailmanHeader = (payload: Record<string, any>) => {
  const appLogoSrc = payload.config.appLogoSrc;

  const appName = payload.config.appName;
  return (
    <>
      <MjmlSection>
        <MjmlColumn>
          <MjmlSpacer height={10} />
        </MjmlColumn>
      </MjmlSection>

      <MjmlSection className="header">
        <MjmlColumn>
          <MjmlImage
            src={appLogoSrc}
            alt={appName}
            align="center"
            width={200}
            padding={0}
          />
        </MjmlColumn>
      </MjmlSection>
    </>
  );
};


================================================
FILE: views/components/html.tsx
================================================
import { MjmlText } from "@faire/mjml-react";
import React from "react";


export const RawHtml = (payload: Record<string, any>) => {
  return (
    <MjmlText dangerouslySetInnerHTML={{ __html: payload.value }} />
  );
};


================================================
FILE: views/components/image.tsx
================================================
import { MjmlImage } from "@faire/mjml-react";
import React from "react";

export const MailmanImage = (payload: Record<string, any>) => {
  return (
    <MjmlImage width={300} src={payload.value.src} alt={payload.value.alt} />
  );
};


================================================
FILE: views/components/regards.tsx
================================================
import { MjmlColumn, MjmlSection, MjmlText } from "@faire/mjml-react";
import React from "react";

export const MailmanRegards = (payload: Record<string, any>) => {
  return (
    <MjmlSection padding={0}>
      <MjmlColumn>
        <MjmlText>
          For any help or support, please feel free to reach out to us at{" "}
          <strong>support@atadel.ca</strong>
        </MjmlText>

        <MjmlText lineHeight={40} fontSize={15} padding={"5px 20px"}>
          Thank you,
        </MjmlText>

        <MjmlText fontSize={15} padding={"0px 20px"}>
          Team Atadel
        </MjmlText>
      </MjmlColumn>
    </MjmlSection>
  );
};


================================================
FILE: views/components/table.tsx
================================================
import {
  MjmlColumn,
  MjmlSection,
  MjmlTable
} from "@faire/mjml-react";
import React from "react";

function toTitleCase(str) {
  return str
    .toLowerCase()
    .split(" ")
    .map(function (word) {
      return word.charAt(0).toUpperCase() + word.slice(1);
    })
    .join(" ");
}

export const MailmanTable = (payload: Record<string, any>) => {
  const headings = Object.keys(payload.value[0]);

  const tableHeader = () => {
    return (
      <thead>
        <tr>
          {headings.map((h) => (
            <th>{toTitleCase(h)}</th>
          ))}
        </tr>
      </thead>
    );
  };
  if (payload.vertical) {
    return (
      <>
        {payload.value.map((obj) => {
          const values = Object.values(obj) as string[];
          return (
            <MjmlSection padding={0}>
              <MjmlColumn>
                <MjmlTable className="styled-table">
                  <tbody>
                    {values.map((v, index) => (
                      <tr>
                        <th>{toTitleCase(headings[index])}</th>
                        <td>{v}</td>
                      </tr>
                    ))}
                  </tbody>
                </MjmlTable>
              </MjmlColumn>
            </MjmlSection>
          );
        })}
      </>
    );
  } else {
    return (
      <MjmlSection padding={0}>
        <MjmlColumn>
          <MjmlTable className="styled-table">
            {payload.heading && tableHeader()}
            <tbody>
              {payload.value.map((obj) => {
                const values = Object.values(obj) as string[];
                return (
                  <tr>
                    {values.map((v) => (
                      <td>{v}</td>
                    ))}
                  </tr>
                );
              })}
            </tbody>
          </MjmlTable>
        </MjmlColumn>
      </MjmlSection>
    );
  }
};


================================================
FILE: views/components/text.tsx
================================================
import { MjmlText } from "@faire/mjml-react";
import React from "react";

export const TextLine = (payload: Record<string, any>) => {
  return (
    <>
      <MjmlText>{payload.value}</MjmlText>
    </>
  );
};


================================================
FILE: views/mail/generic.tsx
================================================
import { Mjml } from "@faire/mjml-react";
import { MailmanHead } from "../components/head";
import { MailmanBody } from "../components/body";
import { MailmanHeader } from "../components/header";
import { MailmanFooter } from "../components/footer";
import { MailmanBodyBuilder } from "../components/bodyBuilder";
import React from "react";

export const GenericMail = (payload: Record<string, any>) => {
  return (
    <Mjml>
      <MailmanHead
        title={payload.meta?.title}
        preview={payload.meta?.preview}
      />

      <MailmanBody>
        <>
          <MailmanHeader config={payload._templateConfig} />
          <MailmanBodyBuilder
            config={payload._templateConfig}
            fields={payload.genericFields}
          />
          <MailmanFooter config={payload._templateConfig} />
        </>
      </MailmanBody>
    </Mjml>
  );
};


================================================
FILE: views/mail/index.tsx
================================================
export * from "./generic";
Download .txt
gitextract_6x4a5o5l/

├── .gitignore
├── .npmignore
├── CONTRIBUTING.MD
├── LICENSE.md
├── README.md
├── copymailman.js
├── jest.json
├── lib/
│   ├── constants.ts
│   ├── index.ts
│   ├── interfaces/
│   │   ├── MailCompiler.ts
│   │   ├── index.ts
│   │   ├── mjml.ts
│   │   └── options.ts
│   ├── mailman.ts
│   ├── message.ts
│   ├── module.ts
│   ├── provider.map.ts
│   └── service.ts
├── package.json
├── tsconfig.json
└── views/
    ├── assets/
    │   └── style.css
    ├── components/
    │   ├── body.tsx
    │   ├── bodyBuilder.tsx
    │   ├── button.tsx
    │   ├── description.tsx
    │   ├── divider.tsx
    │   ├── footer.tsx
    │   ├── greeting.tsx
    │   ├── head.tsx
    │   ├── header.tsx
    │   ├── html.tsx
    │   ├── image.tsx
    │   ├── regards.tsx
    │   ├── table.tsx
    │   └── text.tsx
    └── mail/
        ├── generic.tsx
        └── index.tsx
Download .txt
SYMBOL INDEX (56 symbols across 10 files)

FILE: copymailman.js
  function handle (line 5) | function handle() {

FILE: lib/constants.ts
  constant MAILMAN_QUEUE (line 1) | const MAILMAN_QUEUE = "MAILMAN_QUEUE";
  constant SEND_MAIL (line 2) | const SEND_MAIL = "SEND_MAIL";
  constant RAW_MAIL (line 5) | const RAW_MAIL = "RAW";
  constant VIEW_BASED_MAIL (line 6) | const VIEW_BASED_MAIL = "VIEW_BASED";
  constant GENERIC_MAIL (line 7) | const GENERIC_MAIL = "GENERIC";
  class MailmanConstant (line 9) | class MailmanConstant {}

FILE: lib/interfaces/MailCompiler.ts
  type MailCompiler (line 2) | interface MailCompiler {

FILE: lib/interfaces/mjml.ts
  type MJMLParsingOpts (line 1) | interface MJMLParsingOpts {
  type MJMLMinifyOptions (line 11) | interface MJMLMinifyOptions {

FILE: lib/interfaces/options.ts
  type MailmanBaseTemplateOptions (line 6) | interface MailmanBaseTemplateOptions {
  type MailmanMetaPayload (line 16) | interface MailmanMetaPayload {
  type MailmanPayload (line 21) | interface MailmanPayload {
  type MailmanOptions (line 27) | interface MailmanOptions {
  type CompilerOptions (line 43) | type CompilerOptions = {
  type MailmanOptionsFactory (line 48) | interface MailmanOptionsFactory {
  type MailmanAsyncOptions (line 52) | interface MailmanAsyncOptions extends Pick<ModuleMetadata, "imports"> {
  type MailData (line 60) | interface MailData {
  type SendMailOptions (line 66) | interface SendMailOptions {
  type MailType (line 76) | type MailType = "RAW" | "VIEW_BASED" | "GENERIC";

FILE: lib/mailman.ts
  class Mailman (line 4) | class Mailman {
    method constructor (line 12) | private constructor() {
    method init (line 24) | static init() {
    method from (line 33) | from(sender: string): this {
    method replyTo (line 43) | replyTo(replyToEmail: string): this {
    method inReplyTo (line 53) | inReplyTo(messageId: string): this {
    method to (line 62) | to(receipents: string | string[]): this {
    method cc (line 71) | cc(ccReceipents: string | string[]): this {
    method bcc (line 80) | bcc(bccReceipents: string | string[]): this {
    method send (line 89) | send(mail: MailMessage) {

FILE: lib/message.ts
  class MailMessage (line 13) | class MailMessage {
    method constructor (line 22) | constructor() {
    method init (line 31) | static init(): MailMessage {
    method subject (line 39) | subject(subject: string): this {
    method view (line 49) | view(
    method raw (line 64) | raw(template: string, payload?: Record<string, any>): this {
    method attach (line 75) | attach(filename: string, content: Omit<Attachment, "filename">): this {
    method greeting (line 85) | greeting(greeting: string): this {
    method line (line 98) | line(line: string): this {
    method html (line 106) | html(html: string): this {
    method action (line 120) | action(text: string, link: string): this {
    method table (line 133) | table(data: Record<string, any>[], showHeading = true, vertical = fals...
    method meta (line 142) | meta(payload: MailmanMetaPayload): this {
    method _setGenericMailProperties (line 151) | private _setGenericMailProperties() {
    method _compileTemplate (line 161) | private _compileTemplate(): string {
    method getMailData (line 208) | getMailData(): MailData {
    method render (line 224) | render(): string {

FILE: lib/module.ts
  class MailmanModule (line 11) | class MailmanModule {
    method register (line 16) | static register(options: MailmanOptions): DynamicModule {
    method registerAsync (line 30) | static registerAsync(options: MailmanAsyncOptions): DynamicModule {
    method createStorageOptionsProvider (line 39) | private static createStorageOptionsProvider(

FILE: lib/service.ts
  class MailmanService (line 7) | class MailmanService {
    method constructor (line 11) | constructor(@Inject(map.MAILABLE_OPTIONS) options: MailmanOptions) {
    method getConfig (line 24) | static getConfig(): MailmanOptions {
    method send (line 28) | static async send(options: SendMailOptions) {

FILE: views/components/table.tsx
  function toTitleCase (line 8) | function toTitleCase(str) {
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (34K chars).
[
  {
    "path": ".gitignore",
    "chars": 144,
    "preview": "# dependencies\n/node_modules\n\n# IDE\n/.idea\n/.awcache\n/.vscode\n\n# misc\nnpm-debug.log\n.DS_Store\n\n# tests\n/test\n/coverage\n/"
  },
  {
    "path": ".npmignore",
    "chars": 188,
    "preview": "# source\nlib\ntests\nindex.ts\npackage-lock.json\ntslint.json\ntsconfig.json\n.prettierrc\n\n# github\n.github\nCONTRIBUTING.MD\n\n#"
  },
  {
    "path": "CONTRIBUTING.MD",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "LICENSE.md",
    "chars": 1134,
    "preview": "# The MIT License (MIT)\n\nCopyright © 2020 [SquareBoat](https://squareboat.com)\n\n> Permission is hereby granted, free of "
  },
  {
    "path": "README.md",
    "chars": 936,
    "preview": "# Nest Mailman\n\n📮 The mailer package for your NestJS Applications.\n\n### Features\n\n- ✅ Build mails programmtically\n- ✅ Su"
  },
  {
    "path": "copymailman.js",
    "chars": 923,
    "preview": "const fs = require(\"fs-extra\");\nvar appRoot = require(\"app-root-path\");\nconst picocolors = require(\"picocolors\");\n\nfunct"
  },
  {
    "path": "jest.json",
    "chars": 328,
    "preview": "{\n  \"moduleFileExtensions\": [\"ts\", \"tsx\", \"js\", \"json\"],\n  \"transform\": {\n    \"^.+\\\\.tsx?$\": \"ts-jest\"\n  },\n  \"testRegex"
  },
  {
    "path": "lib/constants.ts",
    "chars": 249,
    "preview": "export const MAILMAN_QUEUE = \"MAILMAN_QUEUE\";\nexport const SEND_MAIL = \"SEND_MAIL\";\n\n// Mail Formats\nexport const RAW_MA"
  },
  {
    "path": "lib/index.ts",
    "chars": 137,
    "preview": "export * from \"./module\";\nexport * from \"./service\";\nexport * from \"./mailman\";\nexport * from \"./interfaces\";\nexport * f"
  },
  {
    "path": "lib/interfaces/MailCompiler.ts",
    "chars": 200,
    "preview": "// All mail compilers(Handlebars, Markdown, etc.) will implement this interface.\nexport interface MailCompiler {\n  fileP"
  },
  {
    "path": "lib/interfaces/index.ts",
    "chars": 83,
    "preview": "export * from './options';\nexport * from './MailCompiler';\nexport * from './mjml';\n"
  },
  {
    "path": "lib/interfaces/mjml.ts",
    "chars": 371,
    "preview": "export interface MJMLParsingOpts {\n  fonts?: { [key: string]: string };\n  keepComments?: boolean;\n  beautify?: boolean;\n"
  },
  {
    "path": "lib/interfaces/options.ts",
    "chars": 1793,
    "preview": "import { ModuleMetadata, Type } from \"@nestjs/common/interfaces\";\nimport { Attachment } from \"nodemailer/lib/mailer\";\nim"
  },
  {
    "path": "lib/mailman.ts",
    "chars": 2168,
    "preview": "import { MailmanService } from './service';\nimport { MailMessage } from './message';\nimport { MailData } from './interfa"
  },
  {
    "path": "lib/message.ts",
    "chars": 5558,
    "preview": "import { Attachment } from \"nodemailer/lib/mailer\";\nimport { GENERIC_MAIL, RAW_MAIL, VIEW_BASED_MAIL } from \"./constants"
  },
  {
    "path": "lib/module.ts",
    "chars": 1451,
    "preview": "import { map } from \"./provider.map\";\nimport { Module, DynamicModule, Provider, Type } from \"@nestjs/common\";\nimport { M"
  },
  {
    "path": "lib/provider.map.ts",
    "chars": 64,
    "preview": "export const map = {\n  MAILABLE_OPTIONS: 'MAILABLE_OPTIONS',\n};\n"
  },
  {
    "path": "lib/service.ts",
    "chars": 1457,
    "preview": "import { map } from \"./provider.map\";\nimport * as nodemailer from \"nodemailer\";\nimport { Injectable, Inject } from \"@nes"
  },
  {
    "path": "package.json",
    "chars": 1854,
    "preview": "{\n  \"name\": \"@squareboat/nest-mailman\",\n  \"version\": \"1.0.5\",\n  \"description\": \"📮 The mailer package for your NestJS App"
  },
  {
    "path": "tsconfig.json",
    "chars": 501,
    "preview": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"declaration\": true,\n    \"strict\": true,\n    \"removeComments\": tr"
  },
  {
    "path": "views/assets/style.css",
    "chars": 918,
    "preview": ".default-bg {\n  background-color: #d1deec;\n}\n\n.header {\n  background-color: #f5f6f6;\n}\n\n.footer {\n  background-color: #f"
  },
  {
    "path": "views/components/body.tsx",
    "chars": 216,
    "preview": "import { MjmlBody } from \"@faire/mjml-react\";\nimport React from \"react\";\n\nexport const MailmanBody = ({ children }: { ch"
  },
  {
    "path": "views/components/bodyBuilder.tsx",
    "chars": 1248,
    "preview": "import { MjmlColumn, MjmlSection } from \"@faire/mjml-react\";\nimport { MailmanButton } from \"./button\";\nimport { MailmanD"
  },
  {
    "path": "views/components/button.tsx",
    "chars": 382,
    "preview": "import { MjmlButton } from \"@faire/mjml-react\";\nimport React from \"react\";\n\nexport const MailmanButton = (payload: Recor"
  },
  {
    "path": "views/components/description.tsx",
    "chars": 323,
    "preview": "import { MjmlColumn, MjmlText } from \"@faire/mjml-react\";\nimport React from \"react\";\n\nexport const Description = (payloa"
  },
  {
    "path": "views/components/divider.tsx",
    "chars": 212,
    "preview": "import { MjmlDivider } from \"@faire/mjml-react\";\nimport React from \"react\";\n\nexport const MailmanDivider = (payload: Rec"
  },
  {
    "path": "views/components/footer.tsx",
    "chars": 1948,
    "preview": "import {\n  MjmlColumn,\n  MjmlDivider,\n  MjmlImage,\n  MjmlSection,\n  MjmlSocial,\n  MjmlSocialElement,\n  MjmlSpacer,\n  Mjm"
  },
  {
    "path": "views/components/greeting.tsx",
    "chars": 248,
    "preview": "import { MjmlText } from \"@faire/mjml-react\";\nimport React from \"react\";\n\nexport const Greeting = (payload: Record<strin"
  },
  {
    "path": "views/components/head.tsx",
    "chars": 479,
    "preview": "import { MjmlHead, MjmlPreview, MjmlStyle, MjmlTitle } from \"@faire/mjml-react\";\nimport { readFileSync } from \"fs\";\nimpo"
  },
  {
    "path": "views/components/header.tsx",
    "chars": 1048,
    "preview": "import {\n  MjmlColumn,\n  MjmlImage,\n  MjmlSection,\n  MjmlSpacer,\n} from \"@faire/mjml-react\";\nimport React from \"react\";\n"
  },
  {
    "path": "views/components/html.tsx",
    "chars": 222,
    "preview": "import { MjmlText } from \"@faire/mjml-react\";\nimport React from \"react\";\n\n\nexport const RawHtml = (payload: Record<strin"
  },
  {
    "path": "views/components/image.tsx",
    "chars": 236,
    "preview": "import { MjmlImage } from \"@faire/mjml-react\";\nimport React from \"react\";\n\nexport const MailmanImage = (payload: Record<"
  },
  {
    "path": "views/components/regards.tsx",
    "chars": 644,
    "preview": "import { MjmlColumn, MjmlSection, MjmlText } from \"@faire/mjml-react\";\nimport React from \"react\";\n\nexport const MailmanR"
  },
  {
    "path": "views/components/table.tsx",
    "chars": 1900,
    "preview": "import {\n  MjmlColumn,\n  MjmlSection,\n  MjmlTable\n} from \"@faire/mjml-react\";\nimport React from \"react\";\n\nfunction toTit"
  },
  {
    "path": "views/components/text.tsx",
    "chars": 211,
    "preview": "import { MjmlText } from \"@faire/mjml-react\";\nimport React from \"react\";\n\nexport const TextLine = (payload: Record<strin"
  },
  {
    "path": "views/mail/generic.tsx",
    "chars": 869,
    "preview": "import { Mjml } from \"@faire/mjml-react\";\nimport { MailmanHead } from \"../components/head\";\nimport { MailmanBody } from "
  },
  {
    "path": "views/mail/index.tsx",
    "chars": 27,
    "preview": "export * from \"./generic\";\n"
  }
]

About this extraction

This page contains the full source code of the squareboat/nest-mailman GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 37 files (30.0 KB), approximately 8.8k tokens, and a symbol index with 56 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!