@email-lint/core

中文文档
0.3.0发布于 2026/09/10MITTypeScriptESM + CJS近 30 天下载 9.9万

Lint email HTML for client compatibility using caniemail data. CLI + library.

原始使用文档
文档目录11 节
  1. Why not use caniemail directly?
  2. Install
  3. CLI
  4. Presets
  5. React Email / TSX
  6. Library API
  7. lint(html, config?)
  8. Formatters
  9. How it works
  10. GitHub Actions
  11. License

email-lint

Lint email HTML for client compatibility. Validates CSS properties, HTML elements, and attributes against caniemail data for 30+ email clients.

$ email-lint check welcome.html

welcome.html
  12:5   error    cursor not supported (4/4 variants)  [gmail]
  18:3   warning  background-image not supported (2/6 variants: windows, windows-mail)  [outlook]
  24:10  warning  <picture> not supported (4/4 variants)  [gmail]

✖ 1 error, 2 warnings

Why not use caniemail directly?

You can, and often you should. caniemail parses your HTML and CSS, returns issues split into errors and warnings, and ships groupIssues() and formatIssue() to help you present them. It is a good library, and email-lint is built on it.

What it does not do is decide what matters in your codebase, or put an answer in front of you in CI. That is what a linter is for:

  • Family collapsing — "unsupported in gmail.android, gmail.ios, gmail.desktop-webmail, gmail.mobile-webmail" becomes one line: cursor not supported (4/4 variants) [gmail]
  • Severity rules — Not all issues are equal. cursor is cosmetic in email (downgraded to info). Gmail forces target="_blank" on all links, so if you already use it, that's info not error
  • Framework filtering — React Email adds preview text blocks, preload image tags, and target="_blank" to its output. caniemail flags all of these. email-lint knows they're framework artifacts and suppresses them
  • CLI with exit codes — email-lint check exits 1 on errors and 0 when the only findings are warnings, so CI blocks on the things that break and stays quiet about the rest
  • Output formats — Pretty terminal output, JSON for tooling, GitHub Actions annotations that show inline on PR diffs
  • TSX support — Point it at a .tsx component and it renders + lints in one step, no build pipeline needed

Install

npm install @email-lint/core

CLI

# Lint HTML files
email-lint check emails/*.html

# Lint React Email components directly (.tsx/.jsx)
email-lint check src/emails/welcome.tsx

# Target specific clients
email-lint check welcome.html --preset gmail
email-lint check welcome.html --preset outlook

# Several at once, for the union of their clients
email-lint check welcome.html --preset gmail,outlook

# Drop clients your audience never uses
email-lint check welcome.html --exclude orange,mail-ru
email-lint check welcome.html --preset all-clients --exclude '*.android'

# Output formats
email-lint check welcome.html --format pretty   # default, colored terminal output
email-lint check welcome.html --format json      # structured JSON for tooling
email-lint check welcome.html --format github    # GitHub Actions annotations

# Expand collapsed diagnostics to one line per variant
email-lint check welcome.html --verbose

Exits with code 1 when errors are found — drop it into CI and it just works.

Presets

PresetClients
all-clientsAll 30+ clients (default)
gmailGmail desktop, iOS, Android, mobile web
outlookOutlook desktop, web, Windows Mail
apple-mailApple Mail macOS, iOS
yahooYahoo Mail desktop, iOS, Android

React Email / TSX

When you pass .tsx or .jsx files, the CLI automatically:

  1. Loads the component using jiti (no build step needed)
  2. Renders it to HTML via @react-email/components
  3. Enables framework-aware filtering to suppress known false positives

Export your component as default and optionally export testData for props:

export const testData = { name: 'Jane' };

export default function Welcome({ name }: { name: string }) {
  return (
    <Html>
      <Body>
        <Text>Hey {name}</Text>
      </Body>
    </Html>
  );
}
email-lint check src/emails/welcome.tsx

Use --no-ignore-framework to disable framework filtering, or --show-ignored to see suppressed diagnostics in a separate section.

Library API

import { lint } from '@email-lint/core';

const html = '<div style="cursor: pointer;">Click</div>';
const result = lint(html, { preset: 'gmail' });

console.log(result.success);      // false
console.log(result.errorCount);   // 1
console.log(result.diagnostics);  // LintDiagnostic[]

lint(html, config?)

Returns a LintResult with diagnostics grouped by client family.

interface LintResult {
  diagnostics: LintDiagnostic[];
  errorCount: number;
  warningCount: number;
  success: boolean;
}

interface LintDiagnostic {
  severity: 'error' | 'warning' | 'info';
  title: string;                    // Feature name from caniemail
  message: string;                  // Human-readable message with notes
  family: string;                   // "gmail", "outlook", etc.
  variants: string[];               // Affected variants within the family
  familySize: number;               // Total variants in this family
  position?: {
    start: { line: number; column: number };
    end: { line: number; column: number };
  };
}

interface LintConfig {
  preset?: string;                  // Client preset (default: "all-clients")
  exclude?: string;                 // Clients to drop, comma-separated globs
  framework?: 'react-email';       // Enable framework-aware filtering
  showIgnored?: boolean;            // Keep suppressed diagnostics in output
}

Formatters

import { lint, formatPretty, formatJson, formatGitHub } from '@email-lint/core';

const result = lint(html);
console.log(formatPretty([result]));
console.log(formatJson([result]));
console.log(formatGitHub([result]));

How it works

  1. Parse — Feeds your HTML to caniemail, which checks every CSS property, HTML element, and attribute against its compatibility database
  2. Severity rules — Applies context-aware rules. cursor is cosmetic in email, so it downgrades to info. Gmail forces target="_blank" on all links, so if you already use it, that's info too
  3. Family collapsing — Groups per-variant diagnostics into families. Instead of 4 separate "Gmail Android / Gmail iOS / Gmail desktop / Gmail mobile" entries, you get one diagnostic showing (4/4 variants)
  4. Framework filtering — When linting React Email components, suppresses diagnostics caused by the framework itself (preview text blocks, preload image tags, forced target="_blank")

GitHub Actions

- name: Lint emails
  run: email-lint check 'src/emails/**/*.tsx' --format github

The github formatter emits workflow commands that show inline annotations on your PR diffs.

License

MIT

版本与发布记录

@email-lint/core 共发布 3 个版本,首次发布于 2026/04/14;过去 12 个月发布了 3 个版本。

版本发布日期距今
0.3.0latest2026/09/102 个月前
0.2.12026/04/146 个月前
0.2.02026/04/146 个月前

关于 @email-lint/core 的常见问题

@email-lint/core 是什么?

Lint email HTML for client compatibility using caniemail data. CLI + library.

如何安装 @email-lint/core?

在项目目录运行 npm install @email-lint/core,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 email-lint,可以用 npx email-lint 直接运行。

@email-lint/core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@email-lint/core 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@email-lint/core 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@email-lint/core 可以商用吗?

@email-lint/core 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@email-lint/core 的下载量有多少?

根据 npm 官方下载统计,@email-lint/core 在 2026/10/14 – 2026/11/12 这 30 天内被下载 98,898 次,日均约 3,297 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@email-lint/core 还在维护吗?

最新版本 0.3.0 发布于 2026/09/10(2 个月前);至今共发布 3 个版本,其中过去 12 个月发布了 3 个;首次发布于 2026/04/14。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。