{"_id":"@email-lint/core","_rev":"3-34d8b4f78cf3877b42763bc48d06fc55","name":"@email-lint/core","dist-tags":{"latest":"0.3.0"},"versions":{"0.2.0":{"name":"@email-lint/core","version":"0.2.0","keywords":["email","lint","linter","email-lint","caniemail","html-email","email-compatibility","react-email","cli","ci"],"author":{"name":"Jarod Stewart"},"license":"MIT","_id":"@email-lint/core@0.2.0","maintainers":[{"name":"stewartjarod","email":"stewartjarod@gmail.com"}],"homepage":"https://github.com/stewartjarod/email-lint/tree/main/packages/email-lint#readme","bugs":{"url":"https://github.com/stewartjarod/email-lint/issues"},"bin":{"email-lint":"dist/cli.mjs"},"dist":{"shasum":"00c36f14a1dc66db958a7baf40f7484a1d5dfff2","tarball":"https://registry.npmjs.org/@email-lint/core/-/core-0.2.0.tgz","fileCount":15,"integrity":"sha512-gI00jSIyPXSyDZgTfxB/oXSQnI1MgCGrv64tCmLXr04xXgUd9j40Obi59W5a6u/dbG9kPgkMir1nKd/qYzwd7Q==","signatures":[{"sig":"MEYCIQC/b3RvegJnKjk4E43v/5U74OwrT8Cy/XxvlrzVg/woKAIhAJhzsCfRmBcp/o6ZnmkW+QRS7CtQAIkRlt1SGqodOxwU","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":235119},"main":"dist/index.js","_from":"file:email-lint-core-0.2.0.tgz","types":"dist/index.d.ts","module":"dist/index.mjs","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"biome check .","test":"vitest run","build":"tsup","clean":"rm -rf dist coverage","format":"biome format --write .","lint:fix":"biome check --write .","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"stewartjarod","email":"stewartjarod@gmail.com"},"_resolved":"/private/var/folders/34/p_rvsmhd3g1dfs5sp910wh840000gn/T/d39d80a888e759a65fa72bed90f7b2e1/email-lint-core-0.2.0.tgz","_integrity":"sha512-gI00jSIyPXSyDZgTfxB/oXSQnI1MgCGrv64tCmLXr04xXgUd9j40Obi59W5a6u/dbG9kPgkMir1nKd/qYzwd7Q==","repository":{"url":"git+https://github.com/stewartjarod/email-lint.git","type":"git","directory":"packages/email-lint"},"_npmVersion":"11.9.0","description":"Lint email HTML for client compatibility using caniemail data. CLI + library.","directories":{},"sideEffects":false,"_nodeVersion":"25.6.1","dependencies":{"jiti":"2.6.1","citty":"^0.1.6","caniemail":"^1.0.4","picocolors":"^1.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^4.0.8","typescript":"^5.9.3","@types/node":"^24.10.0","@biomejs/biome":"^2.3.5","@vitest/coverage-v8":"^4.0.8"},"_npmOperationalInternal":{"tmp":"tmp/core_0.2.0_1776105460651_0.6840943280234995","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@email-lint/core","version":"0.2.1","keywords":["email","lint","linter","email-lint","caniemail","html-email","email-compatibility","react-email","cli","ci"],"author":{"name":"Jarod Stewart"},"license":"MIT","_id":"@email-lint/core@0.2.1","maintainers":[{"name":"stewartjarod","email":"stewartjarod@gmail.com"}],"homepage":"https://github.com/stewartjarod/email-lint/tree/main/packages/email-lint#readme","bugs":{"url":"https://github.com/stewartjarod/email-lint/issues"},"bin":{"email-lint":"dist/cli.mjs"},"dist":{"shasum":"448980c914504a670760e354b05faa39b13f16b2","tarball":"https://registry.npmjs.org/@email-lint/core/-/core-0.2.1.tgz","fileCount":15,"integrity":"sha512-PIrLUKs8fJbKJfyN3XJxCVlau29RxtmF2ZqsWdJcexwyQ+xU0YVgWQEEaoJJfAa7XJJZK3TqmZM9cgWBvXFEPA==","signatures":[{"sig":"MEUCIAw+XvBxCioC+/zojaa6eMThe3oGGA3KNEEzP4U1FGHgAiEAqZnxOofbxJR90u8TvpwGs4dfTsUE3kn466nY93NWX6E=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":248904},"main":"dist/index.js","_from":"file:email-lint-core-0.2.1.tgz","types":"dist/index.d.ts","module":"dist/index.mjs","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","lint":"biome check .","test":"vitest run","build":"tsup","clean":"rm -rf dist coverage","format":"biome format --write .","lint:fix":"biome check --write .","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"stewartjarod","email":"stewartjarod@gmail.com"},"_resolved":"/private/var/folders/34/p_rvsmhd3g1dfs5sp910wh840000gn/T/9bd14c2d6cddae07a28789f346372e4f/email-lint-core-0.2.1.tgz","_integrity":"sha512-PIrLUKs8fJbKJfyN3XJxCVlau29RxtmF2ZqsWdJcexwyQ+xU0YVgWQEEaoJJfAa7XJJZK3TqmZM9cgWBvXFEPA==","repository":{"url":"git+https://github.com/stewartjarod/email-lint.git","type":"git","directory":"packages/email-lint"},"_npmVersion":"11.9.0","description":"Lint email HTML for client compatibility using caniemail data. CLI + library.","directories":{},"sideEffects":false,"_nodeVersion":"25.6.1","dependencies":{"jiti":"2.6.1","citty":"^0.1.6","caniemail":"^1.0.4","picocolors":"^1.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^4.0.8","typescript":"^5.9.3","@types/node":"^24.10.0","@biomejs/biome":"^2.3.5","@vitest/coverage-v8":"^4.0.8"},"_npmOperationalInternal":{"tmp":"tmp/core_0.2.1_1776107419136_0.019409074892040135","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@email-lint/core","version":"0.3.0","description":"Lint email HTML for client compatibility using caniemail data. CLI + library.","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"bin":{"email-lint":"dist/cli.mjs"},"engines":{"node":">=20.0.0"},"sideEffects":false,"dependencies":{"caniemail":"^1.0.4","citty":"^0.1.6","jiti":"2.6.1","picocolors":"^1.1.1"},"devDependencies":{"@biomejs/biome":"^2.3.5","@types/node":"^24.10.0","@vitest/coverage-v8":"^4.1.11","tsup":"^8.5.0","typescript":"^5.9.3","vitest":"^4.1.11"},"keywords":["email","lint","linter","email-lint","caniemail","html-email","email-compatibility","react-email","cli","ci"],"author":{"name":"Jarod Stewart"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/stewartjarod/email-lint.git","directory":"packages/email-lint"},"homepage":"https://github.com/stewartjarod/email-lint/tree/main/packages/email-lint#readme","bugs":{"url":"https://github.com/stewartjarod/email-lint/issues"},"publishConfig":{"access":"public"},"scripts":{"build":"tsup","dev":"tsup --watch","clean":"rm -rf dist coverage","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","lint":"biome check .","lint:fix":"biome check --write .","format":"biome format --write .","typecheck":"tsc --noEmit"},"_id":"@email-lint/core@0.3.0","_integrity":"sha512-i5BXiX1Igv2U9P5EPE9GpxoeyqQlB9cWHrVjTVyojlGt0NvLnGS7gJQzRq4nwOnoSQLvsmQqn/rtat3sPj3izg==","_resolved":"/private/var/folders/34/p_rvsmhd3g1dfs5sp910wh840000gn/T/92e6d7a3a49e0b8f138b18ade42c32ca/email-lint-core-0.3.0.tgz","_from":"file:email-lint-core-0.3.0.tgz","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-i5BXiX1Igv2U9P5EPE9GpxoeyqQlB9cWHrVjTVyojlGt0NvLnGS7gJQzRq4nwOnoSQLvsmQqn/rtat3sPj3izg==","shasum":"acb7e54e38e2c51cc2dc615786fc0d2f4af389d3","tarball":"https://registry.npmjs.org/@email-lint/core/-/core-0.3.0.tgz","fileCount":15,"unpackedSize":270260,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIA4dXqusdKmEHcOBNPqj0THNJ3Rpa+ZGHrsqMbXejlXyAiAzJD/WVPpf8B35JgkRLQr2E9hDNRq7EMVEbeBnNUW0sQ=="}]},"_npmUser":{"name":"stewartjarod","email":"stewartjarod@gmail.com"},"directories":{},"maintainers":[{"name":"stewartjarod","email":"stewartjarod@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/core_0.3.0_1788977167557_0.6712378244177979"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-13T18:37:40.565Z","modified":"2026-09-09T18:06:08.002Z","0.2.0":"2026-04-13T18:37:40.784Z","0.2.1":"2026-04-13T19:10:19.331Z","0.3.0":"2026-09-09T18:06:07.730Z"},"bugs":{"url":"https://github.com/stewartjarod/email-lint/issues"},"author":{"name":"Jarod Stewart"},"license":"MIT","homepage":"https://github.com/stewartjarod/email-lint/tree/main/packages/email-lint#readme","keywords":["email","lint","linter","email-lint","caniemail","html-email","email-compatibility","react-email","cli","ci"],"repository":{"type":"git","url":"git+https://github.com/stewartjarod/email-lint.git","directory":"packages/email-lint"},"description":"Lint email HTML for client compatibility using caniemail data. CLI + library.","maintainers":[{"name":"stewartjarod","email":"stewartjarod@gmail.com"}],"readme":"# email-lint\n\nLint email HTML for client compatibility. Validates CSS properties, HTML elements, and attributes against [caniemail](https://www.caniemail.com/) data for 30+ email clients.\n\n```\n$ email-lint check welcome.html\n\nwelcome.html\n  12:5   error    cursor not supported (4/4 variants)  [gmail]\n  18:3   warning  background-image not supported (2/6 variants: windows, windows-mail)  [outlook]\n  24:10  warning  <picture> not supported (4/4 variants)  [gmail]\n\n✖ 1 error, 2 warnings\n```\n\n## Why not use caniemail directly?\n\nYou 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.\n\nWhat 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:\n\n- **Family collapsing** — \"unsupported in gmail.android, gmail.ios, gmail.desktop-webmail, gmail.mobile-webmail\" becomes one line: `cursor not supported (4/4 variants) [gmail]`\n- **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`\n- **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\n- **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\n- **Output formats** — Pretty terminal output, JSON for tooling, GitHub Actions annotations that show inline on PR diffs\n- **TSX support** — Point it at a `.tsx` component and it renders + lints in one step, no build pipeline needed\n\n## Install\n\n```sh\nnpm install @email-lint/core\n```\n\n## CLI\n\n```sh\n# Lint HTML files\nemail-lint check emails/*.html\n\n# Lint React Email components directly (.tsx/.jsx)\nemail-lint check src/emails/welcome.tsx\n\n# Target specific clients\nemail-lint check welcome.html --preset gmail\nemail-lint check welcome.html --preset outlook\n\n# Several at once, for the union of their clients\nemail-lint check welcome.html --preset gmail,outlook\n\n# Drop clients your audience never uses\nemail-lint check welcome.html --exclude orange,mail-ru\nemail-lint check welcome.html --preset all-clients --exclude '*.android'\n\n# Output formats\nemail-lint check welcome.html --format pretty   # default, colored terminal output\nemail-lint check welcome.html --format json      # structured JSON for tooling\nemail-lint check welcome.html --format github    # GitHub Actions annotations\n\n# Expand collapsed diagnostics to one line per variant\nemail-lint check welcome.html --verbose\n```\n\nExits with code `1` when errors are found — drop it into CI and it just works.\n\n### Presets\n\n| Preset | Clients |\n|---|---|\n| `all-clients` | All 30+ clients (default) |\n| `gmail` | Gmail desktop, iOS, Android, mobile web |\n| `outlook` | Outlook desktop, web, Windows Mail |\n| `apple-mail` | Apple Mail macOS, iOS |\n| `yahoo` | Yahoo Mail desktop, iOS, Android |\n\n### React Email / TSX\n\nWhen you pass `.tsx` or `.jsx` files, the CLI automatically:\n\n1. Loads the component using [jiti](https://github.com/unjs/jiti) (no build step needed)\n2. Renders it to HTML via `@react-email/components`\n3. Enables framework-aware filtering to suppress known false positives\n\nExport your component as default and optionally export `testData` for props:\n\n```tsx\nexport const testData = { name: 'Jane' };\n\nexport default function Welcome({ name }: { name: string }) {\n  return (\n    <Html>\n      <Body>\n        <Text>Hey {name}</Text>\n      </Body>\n    </Html>\n  );\n}\n```\n\n```sh\nemail-lint check src/emails/welcome.tsx\n```\n\nUse `--no-ignore-framework` to disable framework filtering, or `--show-ignored` to see suppressed diagnostics in a separate section.\n\n## Library API\n\n```ts\nimport { lint } from '@email-lint/core';\n\nconst html = '<div style=\"cursor: pointer;\">Click</div>';\nconst result = lint(html, { preset: 'gmail' });\n\nconsole.log(result.success);      // false\nconsole.log(result.errorCount);   // 1\nconsole.log(result.diagnostics);  // LintDiagnostic[]\n```\n\n### `lint(html, config?)`\n\nReturns a `LintResult` with diagnostics grouped by client family.\n\n```ts\ninterface LintResult {\n  diagnostics: LintDiagnostic[];\n  errorCount: number;\n  warningCount: number;\n  success: boolean;\n}\n\ninterface LintDiagnostic {\n  severity: 'error' | 'warning' | 'info';\n  title: string;                    // Feature name from caniemail\n  message: string;                  // Human-readable message with notes\n  family: string;                   // \"gmail\", \"outlook\", etc.\n  variants: string[];               // Affected variants within the family\n  familySize: number;               // Total variants in this family\n  position?: {\n    start: { line: number; column: number };\n    end: { line: number; column: number };\n  };\n}\n\ninterface LintConfig {\n  preset?: string;                  // Client preset (default: \"all-clients\")\n  exclude?: string;                 // Clients to drop, comma-separated globs\n  framework?: 'react-email';       // Enable framework-aware filtering\n  showIgnored?: boolean;            // Keep suppressed diagnostics in output\n}\n```\n\n### Formatters\n\n```ts\nimport { lint, formatPretty, formatJson, formatGitHub } from '@email-lint/core';\n\nconst result = lint(html);\nconsole.log(formatPretty([result]));\nconsole.log(formatJson([result]));\nconsole.log(formatGitHub([result]));\n```\n\n## How it works\n\n1. **Parse** — Feeds your HTML to [caniemail](https://www.npmjs.com/package/caniemail), which checks every CSS property, HTML element, and attribute against its compatibility database\n2. **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\n3. **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)`\n4. **Framework filtering** — When linting React Email components, suppresses diagnostics caused by the framework itself (preview text blocks, preload image tags, forced `target=\"_blank\"`)\n\n## GitHub Actions\n\n```yaml\n- name: Lint emails\n  run: email-lint check 'src/emails/**/*.tsx' --format github\n```\n\nThe `github` formatter emits [workflow commands](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions) that show inline annotations on your PR diffs.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}