{"id":41503819,"url":"https://github.com/maxlapides/dovetailer","last_synced_at":"2026-01-23T19:13:46.589Z","repository":{"id":20989070,"uuid":"24278512","full_name":"maxlapides/dovetailer","owner":"maxlapides","description":"Use Sass and Nunjucks to generate HTML emails with text versions, optimized for all email clients and ready to send.","archived":false,"fork":false,"pushed_at":"2018-08-17T23:03:01.000Z","size":474,"stargazers_count":16,"open_issues_count":5,"forks_count":1,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-10-20T06:57:44.581Z","etag":null,"topics":["emails","html-emails"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maxlapides.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-09-21T01:01:36.000Z","updated_at":"2022-09-19T23:45:05.000Z","dependencies_parsed_at":"2022-07-31T05:07:57.244Z","dependency_job_id":null,"html_url":"https://github.com/maxlapides/dovetailer","commit_stats":null,"previous_names":["maxlapides/html-email-generator"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/maxlapides/dovetailer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxlapides%2Fdovetailer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxlapides%2Fdovetailer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxlapides%2Fdovetailer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxlapides%2Fdovetailer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maxlapides","download_url":"https://codeload.github.com/maxlapides/dovetailer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxlapides%2Fdovetailer/sbom","scorecard":{"id":629643,"data":{"date":"2025-08-11","repo":{"name":"github.com/maxlapides/dovetailer","commit":"179c0627be99a00346e65172f2499d11578b6096"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"80 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-c6rq-rjc2-86v2","Warn: Project is vulnerable to: GHSA-257v-vj4p-3w2h","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-p28h-cc7q-c4fg","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-ff7x-qrg7-qggm","Warn: Project is vulnerable to: GHSA-3gx7-xhv7-5mx3","Warn: Project is vulnerable to: GHSA-qx4v-6gc5-f2vv","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-xf7w-r453-m56c","Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-pfq8-rq6v-vf5m","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-7r28-3m3f-r2pr","Warn: Project is vulnerable to: GHSA-r8j5-h5cx-65gg","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-h726-x36v-rx45","Warn: Project is vulnerable to: GHSA-779f-wgxg-qr8f","Warn: Project is vulnerable to: GHSA-xf5p-87ch-gxw2","Warn: Project is vulnerable to: GHSA-ch52-vgq2-943f","Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj","Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-fhjf-83wg-r2j9","Warn: Project is vulnerable to: GHSA-9v62-24cr-58cx","Warn: Project is vulnerable to: GHSA-r8f7-9pfq-mjmv","Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr","Warn: Project is vulnerable to: GHSA-x77j-w7wf-fjmw","Warn: Project is vulnerable to: GHSA-cwx2-736x-mf6w","Warn: Project is vulnerable to: GHSA-v39p-96qg-c8rf","Warn: Project is vulnerable to: GHSA-8v63-cqqc-6r2c","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-566m-qj78-rww5","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-hwj9-h5mp-3pm3","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-7mwh-4pqv-wmr8","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-j44m-qm6p-hp7m","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-r628-mhmh-qjhw","Warn: Project is vulnerable to: GHSA-9r2w-394v-53qc","Warn: Project is vulnerable to: GHSA-qq89-hq3f-393p","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-38fc-wpqx-33j7","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T07:28:28.425Z","repository_id":20989070,"created_at":"2025-08-21T07:28:28.425Z","updated_at":"2025-08-21T07:28:28.425Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28698448,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T17:25:48.045Z","status":"ssl_error","status_checked_at":"2026-01-23T17:25:47.153Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["emails","html-emails"],"created_at":"2026-01-23T19:13:45.884Z","updated_at":"2026-01-23T19:13:46.568Z","avatar_url":"https://github.com/maxlapides.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm version](https://badge.fury.io/js/dovetailer.svg)](https://badge.fury.io/js/dovetailer)\n\n# Dovetailer: HTML Email Generator\n\n## Features\n\n- HTML and text versions\n- [Nunjucks](https://mozilla.github.io/nunjucks/) support\n- [Sass](http://sass-lang.com) support\n- [Markdown](http://commonmark.org/help/) support\n- [Reset styles](#reset-styles)\n- [Responsive support](#responsive-styles)\n- HTML minification\n- [Development build](#development-and-production-builds) for more efficient development and debugging\n- Automatic special character replacement\n  - In HTML version, converts special characters to HTML entities\n  - In text version, replace non-ASCII characters with ASCII equivalents (ex: smart \"curly\" quotes are replaced by dumb quotes)\n- [CSS Transformations](#css-transformations)\n- [HTML Transformations](#html-transformations)\n\n## Quick Start\n\nThe easiest way to get started is to fork the [Dovetailer starter project](https://github.com/maxlapides/dovetailer-starter):\n\n```\ngit clone git@github.com:maxlapides/dovetailer-starter.git\ncd dovetailer-starter\nnpm install\nnpm start\n```\n\n## Installation\n\nIf you want to set up Dovetailer yourself, you can install it into a new repo:\n\n```\nnpm install dovetailer --save-dev\n```\n\n## Usage\n\nDovetailer comes with two methods out of the box:\n\n1.  `compileDirectory` compiles all of the emails in your templates directory, and saves them to the file system\n2.  `compileEmail` compiles only a single email template and returns the result without saving to the file system\n\n### `compileDirectory`\n\nParameters:\n\n- `templatesPath` (STRING, required): filepath to your email templates\n- `options` (OBJECT, optional):\n  - `doctype` (STRING): a custom doctype if the HTML4 doctype isn't sufficient\n  - `whitelistSelectors` (ARRAY): selectors to not automatically strip from the HTML. For more info, [see here](https://github.com/codsen/email-remove-unused-css#input-optionswhitelist).\n\n```javascript\nconst compileDirectory = require('dovetailer')\nreturn compileDirectory(templatesPath, options)\n```\n\n### `compileEmail`\n\nTo compile a single email template, you can use the `compileEmail` method:\n\nParameters:\n\n- `templatePath` (STRING, required): filepath to email template\n- `extraContext` (OBJECT, optional): data to override context\n\nExample:\n\n```javascript\nconst { compileEmail } = require('dovetailer')\ncompileEmail(pathToTemplate, { extra: 'context' })\n```\n\n## Writing Your Own Emails\n\n1.  In the `templates` folder, add another folder for the new template you want to build. Name this folder whatever you want to call your email template.\n\n2.  In that folder, add the following files:\n\n- `html.njk`: your Nunjucks template for the HTML version\n- `style.scss`: your main Sass file (these styles will be automatically inlined)\n- `text.njk`: your Nunjucks template for the text version\n- `context.json`: the data file used by Nunjucks to compile your template\n\nOptional files:\n\n- `reset.scss`: your Sass file for custom reset styles (see [Reset Styles](#reset-styles) below)\n\nYou can also add additional files and folders in your template directory such as Sass partials. See the `example` template for, well, an example.\n\n## Development and Production Builds\n\nThe development and production versions of your email should always render exactly the same (see below) in the browser. There is no development build of the text version, only the HTML version.\n\nThe main difference between the development build and the production build is the development build references external stylesheets. The external stylesheets have sourcemaps that point back to the original Sass files. This makes it much easier to develop and debug your emails.\n\nYou should use the development build when you're working on coding an email and you're viewing it in a web browser. You should never try to actually send a development build, even just as a test to yourself. It definitely won't work at all.\n\n### Great, but...the development and production builds aren't rendering the same for me!\n\nThe production build moves the media queries into the head and groups the styles together by media query. Since CSS is order dependent, in some cases this can produce unexpected results. However, if you follow best practices and keep your Sass organized, you can avoid these issues.\n\n## Reset Styles\n\nIncluding \"reset\" styles helps ensure that your emails render the same across all email clients. There are two different types of reset styles: reset styles that need to be included in the `\u003chead\u003e` of the email and reset styles that need to be inlined. Generally, `\u003chead\u003e` reset styles are client-specific hacks.\n\nAccordingly, in the included `common` folder, there are two reset files named `reset-head.scss` and `reset-inline.scss`. These are the default sets of reset styles that are included automatically in every email. These files are the result of our research and have been thoroughly tested.\n\nThis requires no configuration, but if you want to specify your own set of reset styles for an individual email, you can do so by adding files named `reset-head.scss` and/or `reset-inline.scss` to your email's template folder.\n\n## Responsive Styles\n\nIf you want to build a responsive email, you're going to need to use media queries. Since it's impossible to inline media queries, your responsive styles will be injected into the `\u003chead\u003e` of your HTML.\n\nNo extra configuration required! Your media queries will automatically be extracted from your CSS and injected. Also, if you have multiple of the same media query in your stylesheet, the selectors will all be grouped together into a single media query.\n\n## CSS Transformations\n\n### `!important` directive\n\nFor styles in media queries to take any effect in HTML emails, they need to override the internal styles. So, the compiler automatically adds the `!important` directive to all styles in media queries.\n\n### Autoprefixer\n\nAll compiled Sass files are also run through [Autoprefixer](https://github.com/postcss/autoprefixer), which in most cases will actually act as a minifier by removing extraneous vendor-prefixed styles.\n\n### MQ Packer\n\nMedia query declarations in the same media query rule are packed into one media query rule using [CSS MQPacker](https://github.com/hail2u/node-css-mqpacker). This enables you to nest media queries inside of any style rule in Sass without having redundant media query rules in your compiled CSS.\n\n## HTML Transformations\n\n### tables\n\nTables (`\u003ctable\u003e`) always get the following HTML attributes:\n\n- `cellpadding=\"0\"`\n- `cellspacing=\"0\"`\n- `border=\"0\"`\n\nEmpty table cells (`\u003ctd\u003e`) are automatically filled with a non-breaking space (`\u0026nbsp;`). An \"empty\" table cell is defined to be any table cell that contains either no characters or whitespace only.\n\n### links\n\nAnchor tags (`\u003ca\u003e`) always get the following HTML attributes:\n\n- `target=\"_blank\"`\n\n### imgs\n\nImages (`\u003cimg\u003e`) always get the following HTML attributes:\n\n- `border=\"0\"`\n\nAny `width` and `height` styles are always applied to `\u003cimg\u003e`s as width/height HTML attributes.\n\nDovetailer does its best to look up the dimensions of any `\u003cimg\u003e` image. It will automatically inject those dimensions as `width`/`height` HTML attributes as well as `width`/`height` inline CSS styles. If the image name ends in `@2x`, it will assume the image is retina quality, and divide the dimensions in half. Similarly, `@3x` images will have dimensions divided by 3. If you specify width/height values for an `\u003cimg\u003e` using CSS, the natural dimensions are overridden. Image dimensions are cached; if you want to invalidate the cache you can delete/modify `cache/images.json`.\n\n## Known Issues\n\n- If you rename a directory in the `templates` folder while Gulp is running, it will crash Gulp.\n- Adding a directory in the `templates` folder while Gulp is running causes an infinite loop?\n\n## Roadmap\n\n- Improve error handling of email compiles that result in `undefined` output\n- Improve caching mechanism for image dimensions\n- Support `@import` in CSS\n- Easy mechanism for dates in copyright statements\n- Strip `font-family` style from empty table cells\n- Replace attributes like `\"\"blah\"\"` with `'\"blah\"'`\n- [Outlook margin support](https://www.emailonacid.com/blog/article/email-development/outlook.com-does-support-margins/)\n- Add command line flags:\n  - Beautifying production HTML\n  - Disabling development version\n- Automatically ensure that empty table cells have `line-height: 1px` and `font-size: 1px`\n- Resolve adding/renaming templates issues\n- table attributes ordered: width, height, cellpadding, cellspacing, border\n- Automatically convert responsive styles to use the `[class=\"...\"]` syntax\n- Move common build folder elsewhere\n- BrowserSync - CSS injection on dev build\n- Automatic Gmail Promotions tab code generation\n- em/rem to px converter\n- Warnings:\n  - relative img references\n  - `\u003clink\u003e` tags\n  - `\u003cscript\u003e` tags\n  - W3C validation\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxlapides%2Fdovetailer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxlapides%2Fdovetailer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxlapides%2Fdovetailer/lists"}