From 17c87bb4aaa03301c05a0fc2c6e42370ba105dde Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Thu, 29 Jan 2026 13:16:45 -0300 Subject: [PATCH 1/7] remove unused directories --- .../__snapshots__/check-images.spec.tsx.snap | 84 ------------------- .../create-dependency-graph.spec.ts.snap | 68 --------------- .../inner/data-to-import.json | 1 - .../test/dependency-graph/inner/file-a.ts | 5 -- .../test/dependency-graph/inner/file-b.ts | 5 -- .../inner/general-importing-file.ts | 9 -- .../inner/outer-dependency.ts | 3 - .../test/dependency-graph/outer.ts | 5 -- 8 files changed, 180 deletions(-) delete mode 100644 packages/react-email/src/actions/email-validation/__snapshots__/check-images.spec.tsx.snap delete mode 100644 packages/react-email/src/cli/utils/preview/hot-reloading/__snapshots__/create-dependency-graph.spec.ts.snap delete mode 100644 packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json delete mode 100644 packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts delete mode 100644 packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts delete mode 100644 packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts delete mode 100644 packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts delete mode 100644 packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/outer.ts diff --git a/packages/react-email/src/actions/email-validation/__snapshots__/check-images.spec.tsx.snap b/packages/react-email/src/actions/email-validation/__snapshots__/check-images.spec.tsx.snap deleted file mode 100644 index 0f682691d1..0000000000 --- a/packages/react-email/src/actions/email-validation/__snapshots__/check-images.spec.tsx.snap +++ /dev/null @@ -1,84 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`checkImages() 1`] = ` -[ - { - "checks": [ - { - "metadata": { - "alt": undefined, - }, - "passed": false, - "type": "accessibility", - }, - { - "passed": true, - "type": "syntax", - }, - { - "passed": true, - "type": "security", - }, - { - "metadata": { - "fetchStatusCode": 200, - }, - "passed": true, - "type": "fetch_attempt", - }, - { - "metadata": { - "byteCount": 26808, - }, - "passed": true, - "type": "image_size", - }, - ], - "codeLocation": { - "column": 3, - "line": 2, - }, - "source": "https://resend.com/static/brand/resend-icon-white.png", - "status": "warning", - }, - { - "checks": [ - { - "metadata": { - "alt": "codepen challenges", - }, - "passed": true, - "type": "accessibility", - }, - { - "passed": true, - "type": "syntax", - }, - { - "passed": true, - "type": "security", - }, - { - "metadata": { - "fetchStatusCode": 200, - }, - "passed": true, - "type": "fetch_attempt", - }, - { - "metadata": { - "byteCount": 111922, - }, - "passed": true, - "type": "image_size", - }, - ], - "codeLocation": { - "column": 3, - "line": 3, - }, - "source": "/static/codepen-challengers.png", - "status": "success", - }, -] -`; diff --git a/packages/react-email/src/cli/utils/preview/hot-reloading/__snapshots__/create-dependency-graph.spec.ts.snap b/packages/react-email/src/cli/utils/preview/hot-reloading/__snapshots__/create-dependency-graph.spec.ts.snap deleted file mode 100644 index 024eeac3d7..0000000000 --- a/packages/react-email/src/cli/utils/preview/hot-reloading/__snapshots__/create-dependency-graph.spec.ts.snap +++ /dev/null @@ -1,68 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`createDependencyGraph() > should have the right initial value for the dependency graph 1`] = ` -{ - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json": { - "dependencyPaths": [], - "dependentPaths": [ - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts", - ], - "moduleDependencies": [], - "path": "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json", - }, - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts": { - "dependencyPaths": [ - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts", - ], - "dependentPaths": [ - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts", - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts", - ], - "moduleDependencies": [], - "path": "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts", - }, - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts": { - "dependencyPaths": [ - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts", - ], - "dependentPaths": [ - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts", - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts", - ], - "moduleDependencies": [], - "path": "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts", - }, - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts": { - "dependencyPaths": [ - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/outer.ts", - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json", - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts", - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts", - ], - "dependentPaths": [], - "moduleDependencies": [ - "node:os", - "node:path", - ], - "path": "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts", - }, - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts": { - "dependencyPaths": [], - "dependentPaths": [ - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/outer.ts", - ], - "moduleDependencies": [], - "path": "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts", - }, - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/outer.ts": { - "dependencyPaths": [ - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts", - ], - "dependentPaths": [ - "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts", - ], - "moduleDependencies": [], - "path": "/home/gabriel/Projects/resend/react-email/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/outer.ts", - }, -} -`; diff --git a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts deleted file mode 100644 index f72bde0de8..0000000000 --- a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** biome-ignore-all lint/correctness/noUnusedImports: used in testing */ -/** biome-ignore-all lint/complexity/noUselessEmptyExport: used in testing */ -import * as _ from './file-b'; - -export {}; diff --git a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts deleted file mode 100644 index ac42b442f6..0000000000 --- a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** biome-ignore-all lint/correctness/noUnusedImports: used in testing */ -/** biome-ignore-all lint/complexity/noUselessEmptyExport: used in testing */ -import * as _ from './file-a'; - -export {}; diff --git a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts deleted file mode 100644 index 638e807962..0000000000 --- a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** biome-ignore-all lint/correctness/noUnusedImports: used in testing */ -/** biome-ignore-all lint/complexity/noUselessEmptyExport: used in testing */ - -import _os from 'node:os'; -import _path from 'node:path'; -import * as _outer from '../outer'; -import _json from './data-to-import.json'; -import * as _a from './file-a'; -import * as _b from './file-b'; diff --git a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts deleted file mode 100644 index da0110b9e2..0000000000 --- a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts +++ /dev/null @@ -1,3 +0,0 @@ -/** biome-ignore-all lint/correctness/noUnusedImports: used in testing */ -/** biome-ignore-all lint/complexity/noUselessEmptyExport: used in testing */ -export {}; diff --git a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/outer.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/outer.ts deleted file mode 100644 index b13cf57ae4..0000000000 --- a/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/outer.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** biome-ignore-all lint/correctness/noUnusedImports: used in testing */ -/** biome-ignore-all lint/complexity/noUselessEmptyExport: used in testing */ -import * as _dependency from './inner/outer-dependency'; - -export {}; From b52e4db6371aef9fbfcfff83df96d716dd75a7bb Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Thu, 29 Jan 2026 13:23:36 -0300 Subject: [PATCH 2/7] move CLI code to /src/cli, and export in /dist/cli --- packages/react-email/package.json | 2 +- .../src/{ => cli}/commands/.npmignore | 0 .../src/{ => cli}/commands/build.ts | 0 .../react-email/src/{ => cli}/commands/dev.ts | 0 .../src/{ => cli}/commands/export.ts | 0 .../src/{ => cli}/commands/resend/reset.ts | 0 .../src/{ => cli}/commands/resend/setup.ts | 0 .../src/{ => cli}/commands/start.ts | 0 .../src/{ => cli}/commands/testing/.gitignore | 0 .../testing/emails/vercel-invite-user.tsx | 0 .../{ => cli}/commands/testing/export.spec.ts | 0 packages/react-email/src/cli/index.ts | 71 +++++++++++++++++++ .../src/{ => cli}/utils/.gitignore | 0 .../react-email/src/{ => cli}/utils/conf.ts | 0 .../utils/esbuild/escape-string-for-regex.ts | 0 .../esbuild/renderring-utilities-exporter.ts | 0 .../get-emails-directory-metadata.spec.ts | 0 .../utils/get-emails-directory-metadata.ts | 0 .../utils/get-preview-server-location.spec.ts | 0 .../utils/get-preview-server-location.ts | 0 .../react-email/src/{ => cli}/utils/index.ts | 0 .../src/{ => cli}/utils/packageJson.ts | 2 +- .../get-env-variables-for-preview-app.ts | 0 .../create-dependency-graph.spec.ts | 0 .../hot-reloading/create-dependency-graph.ts | 0 .../get-imported-modules.spec.ts | 0 .../hot-reloading/get-imported-modules.ts | 0 .../resolve-path-aliases.spec.ts | 0 .../hot-reloading/resolve-path-aliases.ts | 0 .../hot-reloading/setup-hot-reloading.ts | 0 .../inner/data-to-import.json | 0 .../test/dependency-graph/inner/file-a.ts | 0 .../test/dependency-graph/inner/file-b.ts | 0 .../inner/general-importing-file.ts | 0 .../inner/outer-dependency.ts | 0 .../dependency-graph/inner/path-aliases.ts | 0 .../test/dependency-graph/outer.ts | 0 .../preview/hot-reloading/test/some-file.ts | 0 .../preview/hot-reloading/test/tsconfig.json | 0 .../src/{ => cli}/utils/preview/index.ts | 0 .../utils/preview/serve-static-file.ts | 0 .../utils/preview/start-dev-server.ts | 0 .../utils/register-spinner-autostopping.ts | 0 .../src/{ => cli}/utils/style-text.ts | 0 .../src/{ => cli}/utils/tree.spec.ts | 0 .../react-email/src/{ => cli}/utils/tree.ts | 0 .../utils/types/hot-reload-change.ts | 0 .../{ => cli}/utils/types/hot-reload-event.ts | 0 packages/react-email/src/index.ts | 71 ------------------- packages/react-email/tsdown.config.ts | 4 +- 50 files changed, 75 insertions(+), 75 deletions(-) rename packages/react-email/src/{ => cli}/commands/.npmignore (100%) rename packages/react-email/src/{ => cli}/commands/build.ts (100%) rename packages/react-email/src/{ => cli}/commands/dev.ts (100%) rename packages/react-email/src/{ => cli}/commands/export.ts (100%) rename packages/react-email/src/{ => cli}/commands/resend/reset.ts (100%) rename packages/react-email/src/{ => cli}/commands/resend/setup.ts (100%) rename packages/react-email/src/{ => cli}/commands/start.ts (100%) rename packages/react-email/src/{ => cli}/commands/testing/.gitignore (100%) rename packages/react-email/src/{ => cli}/commands/testing/emails/vercel-invite-user.tsx (100%) rename packages/react-email/src/{ => cli}/commands/testing/export.spec.ts (100%) create mode 100644 packages/react-email/src/cli/index.ts rename packages/react-email/src/{ => cli}/utils/.gitignore (100%) rename packages/react-email/src/{ => cli}/utils/conf.ts (100%) rename packages/react-email/src/{ => cli}/utils/esbuild/escape-string-for-regex.ts (100%) rename packages/react-email/src/{ => cli}/utils/esbuild/renderring-utilities-exporter.ts (100%) rename packages/react-email/src/{ => cli}/utils/get-emails-directory-metadata.spec.ts (100%) rename packages/react-email/src/{ => cli}/utils/get-emails-directory-metadata.ts (100%) rename packages/react-email/src/{ => cli}/utils/get-preview-server-location.spec.ts (100%) rename packages/react-email/src/{ => cli}/utils/get-preview-server-location.ts (100%) rename packages/react-email/src/{ => cli}/utils/index.ts (100%) rename packages/react-email/src/{ => cli}/utils/packageJson.ts (71%) rename packages/react-email/src/{ => cli}/utils/preview/get-env-variables-for-preview-app.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/create-dependency-graph.spec.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/create-dependency-graph.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/get-imported-modules.spec.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/get-imported-modules.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/resolve-path-aliases.spec.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/resolve-path-aliases.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/setup-hot-reloading.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/test/dependency-graph/inner/path-aliases.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/test/dependency-graph/outer.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/test/some-file.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/hot-reloading/test/tsconfig.json (100%) rename packages/react-email/src/{ => cli}/utils/preview/index.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/serve-static-file.ts (100%) rename packages/react-email/src/{ => cli}/utils/preview/start-dev-server.ts (100%) rename packages/react-email/src/{ => cli}/utils/register-spinner-autostopping.ts (100%) rename packages/react-email/src/{ => cli}/utils/style-text.ts (100%) rename packages/react-email/src/{ => cli}/utils/tree.spec.ts (100%) rename packages/react-email/src/{ => cli}/utils/tree.ts (100%) rename packages/react-email/src/{ => cli}/utils/types/hot-reload-change.ts (100%) rename packages/react-email/src/{ => cli}/utils/types/hot-reload-event.ts (100%) diff --git a/packages/react-email/package.json b/packages/react-email/package.json index 941721b3a8..9451aae931 100644 --- a/packages/react-email/package.json +++ b/packages/react-email/package.json @@ -3,7 +3,7 @@ "version": "5.3.0-canary.2", "description": "A live preview of your emails right in your browser.", "bin": { - "email": "./dist/index.js" + "email": "./dist/cli/index.js" }, "type": "module", "scripts": { diff --git a/packages/react-email/src/commands/.npmignore b/packages/react-email/src/cli/commands/.npmignore similarity index 100% rename from packages/react-email/src/commands/.npmignore rename to packages/react-email/src/cli/commands/.npmignore diff --git a/packages/react-email/src/commands/build.ts b/packages/react-email/src/cli/commands/build.ts similarity index 100% rename from packages/react-email/src/commands/build.ts rename to packages/react-email/src/cli/commands/build.ts diff --git a/packages/react-email/src/commands/dev.ts b/packages/react-email/src/cli/commands/dev.ts similarity index 100% rename from packages/react-email/src/commands/dev.ts rename to packages/react-email/src/cli/commands/dev.ts diff --git a/packages/react-email/src/commands/export.ts b/packages/react-email/src/cli/commands/export.ts similarity index 100% rename from packages/react-email/src/commands/export.ts rename to packages/react-email/src/cli/commands/export.ts diff --git a/packages/react-email/src/commands/resend/reset.ts b/packages/react-email/src/cli/commands/resend/reset.ts similarity index 100% rename from packages/react-email/src/commands/resend/reset.ts rename to packages/react-email/src/cli/commands/resend/reset.ts diff --git a/packages/react-email/src/commands/resend/setup.ts b/packages/react-email/src/cli/commands/resend/setup.ts similarity index 100% rename from packages/react-email/src/commands/resend/setup.ts rename to packages/react-email/src/cli/commands/resend/setup.ts diff --git a/packages/react-email/src/commands/start.ts b/packages/react-email/src/cli/commands/start.ts similarity index 100% rename from packages/react-email/src/commands/start.ts rename to packages/react-email/src/cli/commands/start.ts diff --git a/packages/react-email/src/commands/testing/.gitignore b/packages/react-email/src/cli/commands/testing/.gitignore similarity index 100% rename from packages/react-email/src/commands/testing/.gitignore rename to packages/react-email/src/cli/commands/testing/.gitignore diff --git a/packages/react-email/src/commands/testing/emails/vercel-invite-user.tsx b/packages/react-email/src/cli/commands/testing/emails/vercel-invite-user.tsx similarity index 100% rename from packages/react-email/src/commands/testing/emails/vercel-invite-user.tsx rename to packages/react-email/src/cli/commands/testing/emails/vercel-invite-user.tsx diff --git a/packages/react-email/src/commands/testing/export.spec.ts b/packages/react-email/src/cli/commands/testing/export.spec.ts similarity index 100% rename from packages/react-email/src/commands/testing/export.spec.ts rename to packages/react-email/src/cli/commands/testing/export.spec.ts diff --git a/packages/react-email/src/cli/index.ts b/packages/react-email/src/cli/index.ts new file mode 100644 index 0000000000..f2151d8a4b --- /dev/null +++ b/packages/react-email/src/cli/index.ts @@ -0,0 +1,71 @@ +#!/usr/bin/env -S node --experimental-vm-modules --disable-warning=ExperimentalWarning +import { program } from 'commander'; +import { build } from './commands/build.js'; +import { dev } from './commands/dev.js'; +import { exportTemplates } from './commands/export.js'; +import { resendReset } from './commands/resend/reset.js'; +import { resendSetup } from './commands/resend/setup.js'; +import { start } from './commands/start.js'; +import { packageJson } from './utils/packageJson.js'; + +const PACKAGE_NAME = 'react-email'; + +program + .name(PACKAGE_NAME) + .description('A live preview of your emails right in your browser') + .version(packageJson.version); + +program + .command('dev') + .description('Starts the preview email development app') + .option('-d, --dir ', 'Directory with your email templates', './emails') + .option('-p --port ', 'Port to run dev server on', '3000') + .action(dev); + +program + .command('build') + .description('Copies the preview app for onto .react-email and builds it') + .option('-d, --dir ', 'Directory with your email templates', './emails') + .option( + '-p --packageManager ', + 'Package name to use on installation on `.react-email`', + 'npm', + ) + .action(build); + +program + .command('start') + .description('Runs the built preview app that is inside of ".react-email"') + .action(start); + +program + .command('export') + .description('Build the templates to the `out` directory') + .option('--outDir ', 'Output directory', 'out') + .option('-p, --pretty', 'Pretty print the output', false) + .option('-t, --plainText', 'Set output format as plain text', false) + .option('-d, --dir ', 'Directory with your email templates', './emails') + .option( + '-s, --silent', + 'To, or not to show a spinner with process information', + false, + ) + .action(({ outDir, pretty, plainText, silent, dir: srcDir }) => + exportTemplates(outDir, srcDir, { silent, plainText, pretty }), + ); + +const resend = program.command('resend'); + +resend + .command('setup') + .description( + 'Sets up the integration between the React Email CLI, and your Resend account through an API Key', + ) + .action(resendSetup); + +resend + .command('reset') + .description('Deletes your API Key from the React Email configuration') + .action(resendReset); + +program.parse(); diff --git a/packages/react-email/src/utils/.gitignore b/packages/react-email/src/cli/utils/.gitignore similarity index 100% rename from packages/react-email/src/utils/.gitignore rename to packages/react-email/src/cli/utils/.gitignore diff --git a/packages/react-email/src/utils/conf.ts b/packages/react-email/src/cli/utils/conf.ts similarity index 100% rename from packages/react-email/src/utils/conf.ts rename to packages/react-email/src/cli/utils/conf.ts diff --git a/packages/react-email/src/utils/esbuild/escape-string-for-regex.ts b/packages/react-email/src/cli/utils/esbuild/escape-string-for-regex.ts similarity index 100% rename from packages/react-email/src/utils/esbuild/escape-string-for-regex.ts rename to packages/react-email/src/cli/utils/esbuild/escape-string-for-regex.ts diff --git a/packages/react-email/src/utils/esbuild/renderring-utilities-exporter.ts b/packages/react-email/src/cli/utils/esbuild/renderring-utilities-exporter.ts similarity index 100% rename from packages/react-email/src/utils/esbuild/renderring-utilities-exporter.ts rename to packages/react-email/src/cli/utils/esbuild/renderring-utilities-exporter.ts diff --git a/packages/react-email/src/utils/get-emails-directory-metadata.spec.ts b/packages/react-email/src/cli/utils/get-emails-directory-metadata.spec.ts similarity index 100% rename from packages/react-email/src/utils/get-emails-directory-metadata.spec.ts rename to packages/react-email/src/cli/utils/get-emails-directory-metadata.spec.ts diff --git a/packages/react-email/src/utils/get-emails-directory-metadata.ts b/packages/react-email/src/cli/utils/get-emails-directory-metadata.ts similarity index 100% rename from packages/react-email/src/utils/get-emails-directory-metadata.ts rename to packages/react-email/src/cli/utils/get-emails-directory-metadata.ts diff --git a/packages/react-email/src/utils/get-preview-server-location.spec.ts b/packages/react-email/src/cli/utils/get-preview-server-location.spec.ts similarity index 100% rename from packages/react-email/src/utils/get-preview-server-location.spec.ts rename to packages/react-email/src/cli/utils/get-preview-server-location.spec.ts diff --git a/packages/react-email/src/utils/get-preview-server-location.ts b/packages/react-email/src/cli/utils/get-preview-server-location.ts similarity index 100% rename from packages/react-email/src/utils/get-preview-server-location.ts rename to packages/react-email/src/cli/utils/get-preview-server-location.ts diff --git a/packages/react-email/src/utils/index.ts b/packages/react-email/src/cli/utils/index.ts similarity index 100% rename from packages/react-email/src/utils/index.ts rename to packages/react-email/src/cli/utils/index.ts diff --git a/packages/react-email/src/utils/packageJson.ts b/packages/react-email/src/cli/utils/packageJson.ts similarity index 71% rename from packages/react-email/src/utils/packageJson.ts rename to packages/react-email/src/cli/utils/packageJson.ts index 5c0b2d2a63..df2e093d8e 100644 --- a/packages/react-email/src/utils/packageJson.ts +++ b/packages/react-email/src/cli/utils/packageJson.ts @@ -1,4 +1,4 @@ // @ts-expect-error Typescript doesn't want to allow this, but it's fine since we're using tsup -import packageJson from '../../package.json'; +import packageJson from '../../../package.json'; export { packageJson }; diff --git a/packages/react-email/src/utils/preview/get-env-variables-for-preview-app.ts b/packages/react-email/src/cli/utils/preview/get-env-variables-for-preview-app.ts similarity index 100% rename from packages/react-email/src/utils/preview/get-env-variables-for-preview-app.ts rename to packages/react-email/src/cli/utils/preview/get-env-variables-for-preview-app.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/create-dependency-graph.spec.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/create-dependency-graph.spec.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/create-dependency-graph.spec.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/create-dependency-graph.spec.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/create-dependency-graph.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/create-dependency-graph.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/create-dependency-graph.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/create-dependency-graph.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/get-imported-modules.spec.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/get-imported-modules.spec.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/get-imported-modules.spec.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/get-imported-modules.spec.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/get-imported-modules.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/get-imported-modules.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/get-imported-modules.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/get-imported-modules.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/resolve-path-aliases.spec.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.spec.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/resolve-path-aliases.spec.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.spec.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/resolve-path-aliases.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/resolve-path-aliases.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/resolve-path-aliases.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/setup-hot-reloading.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/setup-hot-reloading.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/setup-hot-reloading.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/setup-hot-reloading.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json rename to packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/data-to-import.json diff --git a/packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-a.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/file-b.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/general-importing-file.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/outer-dependency.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/path-aliases.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/path-aliases.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/inner/path-aliases.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/inner/path-aliases.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/outer.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/outer.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/test/dependency-graph/outer.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/test/dependency-graph/outer.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/test/some-file.ts b/packages/react-email/src/cli/utils/preview/hot-reloading/test/some-file.ts similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/test/some-file.ts rename to packages/react-email/src/cli/utils/preview/hot-reloading/test/some-file.ts diff --git a/packages/react-email/src/utils/preview/hot-reloading/test/tsconfig.json b/packages/react-email/src/cli/utils/preview/hot-reloading/test/tsconfig.json similarity index 100% rename from packages/react-email/src/utils/preview/hot-reloading/test/tsconfig.json rename to packages/react-email/src/cli/utils/preview/hot-reloading/test/tsconfig.json diff --git a/packages/react-email/src/utils/preview/index.ts b/packages/react-email/src/cli/utils/preview/index.ts similarity index 100% rename from packages/react-email/src/utils/preview/index.ts rename to packages/react-email/src/cli/utils/preview/index.ts diff --git a/packages/react-email/src/utils/preview/serve-static-file.ts b/packages/react-email/src/cli/utils/preview/serve-static-file.ts similarity index 100% rename from packages/react-email/src/utils/preview/serve-static-file.ts rename to packages/react-email/src/cli/utils/preview/serve-static-file.ts diff --git a/packages/react-email/src/utils/preview/start-dev-server.ts b/packages/react-email/src/cli/utils/preview/start-dev-server.ts similarity index 100% rename from packages/react-email/src/utils/preview/start-dev-server.ts rename to packages/react-email/src/cli/utils/preview/start-dev-server.ts diff --git a/packages/react-email/src/utils/register-spinner-autostopping.ts b/packages/react-email/src/cli/utils/register-spinner-autostopping.ts similarity index 100% rename from packages/react-email/src/utils/register-spinner-autostopping.ts rename to packages/react-email/src/cli/utils/register-spinner-autostopping.ts diff --git a/packages/react-email/src/utils/style-text.ts b/packages/react-email/src/cli/utils/style-text.ts similarity index 100% rename from packages/react-email/src/utils/style-text.ts rename to packages/react-email/src/cli/utils/style-text.ts diff --git a/packages/react-email/src/utils/tree.spec.ts b/packages/react-email/src/cli/utils/tree.spec.ts similarity index 100% rename from packages/react-email/src/utils/tree.spec.ts rename to packages/react-email/src/cli/utils/tree.spec.ts diff --git a/packages/react-email/src/utils/tree.ts b/packages/react-email/src/cli/utils/tree.ts similarity index 100% rename from packages/react-email/src/utils/tree.ts rename to packages/react-email/src/cli/utils/tree.ts diff --git a/packages/react-email/src/utils/types/hot-reload-change.ts b/packages/react-email/src/cli/utils/types/hot-reload-change.ts similarity index 100% rename from packages/react-email/src/utils/types/hot-reload-change.ts rename to packages/react-email/src/cli/utils/types/hot-reload-change.ts diff --git a/packages/react-email/src/utils/types/hot-reload-event.ts b/packages/react-email/src/cli/utils/types/hot-reload-event.ts similarity index 100% rename from packages/react-email/src/utils/types/hot-reload-event.ts rename to packages/react-email/src/cli/utils/types/hot-reload-event.ts diff --git a/packages/react-email/src/index.ts b/packages/react-email/src/index.ts index f2151d8a4b..e69de29bb2 100644 --- a/packages/react-email/src/index.ts +++ b/packages/react-email/src/index.ts @@ -1,71 +0,0 @@ -#!/usr/bin/env -S node --experimental-vm-modules --disable-warning=ExperimentalWarning -import { program } from 'commander'; -import { build } from './commands/build.js'; -import { dev } from './commands/dev.js'; -import { exportTemplates } from './commands/export.js'; -import { resendReset } from './commands/resend/reset.js'; -import { resendSetup } from './commands/resend/setup.js'; -import { start } from './commands/start.js'; -import { packageJson } from './utils/packageJson.js'; - -const PACKAGE_NAME = 'react-email'; - -program - .name(PACKAGE_NAME) - .description('A live preview of your emails right in your browser') - .version(packageJson.version); - -program - .command('dev') - .description('Starts the preview email development app') - .option('-d, --dir ', 'Directory with your email templates', './emails') - .option('-p --port ', 'Port to run dev server on', '3000') - .action(dev); - -program - .command('build') - .description('Copies the preview app for onto .react-email and builds it') - .option('-d, --dir ', 'Directory with your email templates', './emails') - .option( - '-p --packageManager ', - 'Package name to use on installation on `.react-email`', - 'npm', - ) - .action(build); - -program - .command('start') - .description('Runs the built preview app that is inside of ".react-email"') - .action(start); - -program - .command('export') - .description('Build the templates to the `out` directory') - .option('--outDir ', 'Output directory', 'out') - .option('-p, --pretty', 'Pretty print the output', false) - .option('-t, --plainText', 'Set output format as plain text', false) - .option('-d, --dir ', 'Directory with your email templates', './emails') - .option( - '-s, --silent', - 'To, or not to show a spinner with process information', - false, - ) - .action(({ outDir, pretty, plainText, silent, dir: srcDir }) => - exportTemplates(outDir, srcDir, { silent, plainText, pretty }), - ); - -const resend = program.command('resend'); - -resend - .command('setup') - .description( - 'Sets up the integration between the React Email CLI, and your Resend account through an API Key', - ) - .action(resendSetup); - -resend - .command('reset') - .description('Deletes your API Key from the React Email configuration') - .action(resendReset); - -program.parse(); diff --git a/packages/react-email/tsdown.config.ts b/packages/react-email/tsdown.config.ts index 2102dbfa15..23f830bbc7 100644 --- a/packages/react-email/tsdown.config.ts +++ b/packages/react-email/tsdown.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from 'tsdown'; export default defineConfig({ dts: false, - entry: ['./src/index.ts'], + entry: ['./src/cli/index.ts'], format: ['esm'], - outDir: 'dist', + outDir: 'dist/cli', }); From e3ddb642d511286132bdc563623afd9efc723beb Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Fri, 30 Jan 2026 09:02:43 -0300 Subject: [PATCH 3/7] wip --- packages/preview-server/package.json | 1 - .../email-validation/check-compatibility.ts | 87 +- packages/react-email/package.json | 1 + .../scripts/fill-caniemail-data.ts | 29 + .../src/caniemail/all-css-properties.ts | 358 + packages/react-email/src/caniemail/data.ts | 86441 ++++++++++++++++ .../src/caniemail/support-entry-parsing.ts | 381 + packages/react-email/src/index.ts | 55 + packages/react-email/tsconfig.json | 2 +- 9 files changed, 87267 insertions(+), 88 deletions(-) create mode 100644 packages/react-email/scripts/fill-caniemail-data.ts create mode 100644 packages/react-email/src/caniemail/all-css-properties.ts create mode 100644 packages/react-email/src/caniemail/data.ts create mode 100644 packages/react-email/src/caniemail/support-entry-parsing.ts diff --git a/packages/preview-server/package.json b/packages/preview-server/package.json index 9399396f2a..50c216087e 100644 --- a/packages/preview-server/package.json +++ b/packages/preview-server/package.json @@ -4,7 +4,6 @@ "description": "A live preview of your emails right in your browser.", "scripts": { "build": "cross-env NODE_OPTIONS=\"--experimental-vm-modules --disable-warning=ExperimentalWarning\" tsx ./scripts/build-preview-server.mts", - "caniemail:fetch": "tsx ./scripts/fill-caniemail-data.mts", "clean": "rimraf dist", "dev": "cross-env NODE_OPTIONS=\"--experimental-vm-modules --disable-warning=ExperimentalWarning\" tsx ./scripts/dev.mts", "dev:seed": "tsx ./scripts/seed.mts", diff --git a/packages/preview-server/src/actions/email-validation/check-compatibility.ts b/packages/preview-server/src/actions/email-validation/check-compatibility.ts index 5852040d26..824214658a 100644 --- a/packages/preview-server/src/actions/email-validation/check-compatibility.ts +++ b/packages/preview-server/src/actions/email-validation/check-compatibility.ts @@ -1,4 +1,4 @@ -'use server'; +/eleuse server'; import { parse } from '@babel/parser'; import traverse from '@babel/traverse'; @@ -25,91 +25,6 @@ import { getElementNames } from '../../utils/caniemail/get-element-names'; import { snakeToCamel } from '../../utils/snake-to-camel'; import { supportEntries } from './caniemail-data'; -export interface CompatibilityCheckingResult { - location: SourceLocation; - source: string; - entry: SupportEntry; - status: SupportStatus; - statsPerEmailClient: CompatibilityStats['perEmailClient']; -} - -export type EmailClient = - | 'gmail' - | 'outlook' - | 'yahoo' - | 'apple-mail' - | 'aol' - | 'thunderbird' - | 'microsoft' - | 'samsung-email' - | 'sfr' - | 'orange' - | 'protonmail' - | 'hey' - | 'mail-ru' - | 'fastmail' - | 'laposte' - | 't-online-de' - | 'free-fr' - | 'gmx' - | 'web-de' - | 'ionos-1and1' - | 'rainloop' - | 'wp-pl'; - -export type Platform = - | 'desktop-app' - | 'desktop-webmail' - | 'mobile-webmail' - | 'webmail' - | 'ios' - | 'android' - | 'windows' - | 'macos' - | 'windows-mail' - | 'outlook-com'; - -export type SupportEntryCategory = 'html' | 'css' | 'image' | 'others'; - -export interface SupportEntry { - slug: string; - title: string; - description: string | null; - url: string; - category: SupportEntryCategory; - tags: string[]; - keywords: string | null; - last_test_date: string; - test_url: string; - test_results_url: string | null; - stats: Partial< - Record< - EmailClient, - Partial< - Record< - Platform, - /* - This last Record has only one key, as the - ordered version of caniemail's data is meant to be something like: - - [ - { "1.0": "u" }, - { "2.0": "y" }, - { "3.0": "p #1" }, - ] - - So only one key for each object inside of this array, TypeScript can't really - describe this though AFAIK. - */ - Record[] - > - > - > - >; - notes: string | null; - notes_by_num: Record | null; -} - const relevantEmailClients: EmailClient[] = [ 'gmail', 'apple-mail', diff --git a/packages/react-email/package.json b/packages/react-email/package.json index 9451aae931..87cef2d6e9 100644 --- a/packages/react-email/package.json +++ b/packages/react-email/package.json @@ -9,6 +9,7 @@ "scripts": { "build": "tsdown", "build:watch": "tsdown --watch src", + "caniemail:fetch": "tsx ./scripts/fill-caniemail-data.mts", "clean": "rm -rf dist", "test": "vitest run", "test:watch": "vitest" diff --git a/packages/react-email/scripts/fill-caniemail-data.ts b/packages/react-email/scripts/fill-caniemail-data.ts new file mode 100644 index 0000000000..314b73924b --- /dev/null +++ b/packages/react-email/scripts/fill-caniemail-data.ts @@ -0,0 +1,29 @@ +import { promises as fs } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export const caniemailDataURL = + 'https://www.caniemail.com/api/data-ordered.json'; + +const responseFromCaniemail = await fetch(caniemailDataURL); +if (!responseFromCaniemail.ok) { + throw new Error( + `Could not get the data from Caniemail and there is no cached data under your temporary folder to fallback for. + +This could be happneing for the following reasons: +- You don't have internet connectivity +- Caniemail is down +- Caniemail changed from where to fetch their data from, which means we need to fix this. If this is the case, please open up an issue.`, + ); +} + +const response = await responseFromCaniemail.json(); + +await fs.writeFile( + path.resolve(import.meta.dirname, '../src/caniemail/caniemail-data.ts'), + `import type { SupportEntry } from "./support-entry-parsing.js"; + +export const nicenames = ${JSON.stringify(response.nicenames, null, 2)}; + +export const supportEntries: SupportEntry[] = ${JSON.stringify(response.data, null, 2)}`, +); diff --git a/packages/react-email/src/caniemail/all-css-properties.ts b/packages/react-email/src/caniemail/all-css-properties.ts new file mode 100644 index 0000000000..19e27d7ace --- /dev/null +++ b/packages/react-email/src/caniemail/all-css-properties.ts @@ -0,0 +1,358 @@ +// taken from https://www.w3schools.com/cssref/index.php +export const allCssProperties = [ + 'accent-color', + 'align-content', + 'align-items', + 'align-self', + 'all', + 'animation', + 'animation-delay', + 'animation-direction', + 'animation-duration', + 'animation-fill-mode', + 'animation-iteration-count', + 'animation-name', + 'animation-play-state', + 'animation-timing-function', + 'aspect-ratio', + 'backdrop-filter', + 'backface-visibility', + 'background', + 'background-attachment', + 'background-blend-mode', + 'background-clip', + 'background-color', + 'background-image', + 'background-origin', + 'background-position', + 'background-position-x', + 'background-position-y', + 'background-repeat', + 'background-size', + 'block-size', + 'border', + 'border-block', + 'border-block-color', + 'border-block-end', + 'border-block-end-color', + 'border-block-end-style', + 'border-block-end-width', + 'border-block-start', + 'border-block-start-color', + 'border-block-start-style', + 'border-block-start-width', + 'border-block-style', + 'border-block-width', + 'border-bottom', + 'border-bottom-color', + 'border-bottom-left-radius', + 'border-bottom-right-radius', + 'border-bottom-style', + 'border-bottom-width', + 'border-collapse', + 'border-color', + 'border-end-end-radius', + 'border-end-start-radius', + 'border-image', + 'border-image-outset', + 'border-image-repeat', + 'border-image-slice', + 'border-image-source', + 'border-image-width', + 'border-inline', + 'border-inline-color', + 'border-inline-end', + 'border-inline-end-color', + 'border-inline-end-style', + 'border-inline-end-width', + 'border-inline-start', + 'border-inline-start-color', + 'border-inline-start-style', + 'border-inline-start-width', + 'border-inline-style', + 'border-inline-width', + 'border-left', + 'border-left-color', + 'border-left-style', + 'border-left-width', + 'border-radius', + 'border-right', + 'border-right-color', + 'border-right-style', + 'border-right-width', + 'border-spacing', + 'border-start-end-radius', + 'border-start-start-radius', + 'border-style', + 'border-top', + 'border-top-color', + 'border-top-left-radius', + 'border-top-right-radius', + 'border-top-style', + 'border-top-width', + 'border-width', + 'bottom', + 'box-decoration-break', + 'box-reflect', + 'box-shadow', + 'box-sizing', + 'break-after', + 'break-before', + 'break-inside', + 'caption-side', + 'caret-color', + '@charset', + 'clear', + 'clip', + 'clip-path', + 'color', + 'column-count', + 'column-fill', + 'column-gap', + 'column-rule', + 'column-rule-color', + 'column-rule-style', + 'column-rule-width', + 'column-span', + 'column-width', + 'columns', + 'content', + 'counter-increment', + 'counter-reset', + 'counter-set', + 'cursor', + 'direction', + 'display', + 'empty-cells', + 'filter', + 'flex', + 'flex-basis', + 'flex-direction', + 'flex-flow', + 'flex-grow', + 'flex-shrink', + 'flex-wrap', + 'float', + 'font', + '@font-face', + 'font-family', + 'font-feature-settings', + '@font-feature-values', + 'font-kerning', + 'font-language-override', + 'font-size', + 'font-size-adjust', + 'font-stretch', + 'font-style', + 'font-synthesis', + 'font-variant', + 'font-variant-alternates', + 'font-variant-caps', + 'font-variant-east-asian', + 'font-variant-ligatures', + 'font-variant-numeric', + 'font-variant-position', + 'font-weight', + 'gap', + 'grid', + 'grid-area', + 'grid-auto-columns', + 'grid-auto-flow', + 'grid-auto-rows', + 'grid-column', + 'grid-column-end', + 'grid-column-gap', + 'grid-column-start', + 'grid-gap', + 'grid-row', + 'grid-row-end', + 'grid-row-gap', + 'grid-row-start', + 'grid-template', + 'grid-template-areas', + 'grid-template-columns', + 'grid-template-rows', + 'hanging-punctuation', + 'height', + 'hyphens', + 'hypenate-character', + 'image-rendering', + '@import', + 'inline-size', + 'inset', + 'inset-block', + 'inset-block-end', + 'inset-block-start', + 'inset-inline', + 'inset-inline-end', + 'inset-inline-start', + 'isolation', + 'justify-content', + 'justify-items', + 'justify-self', + '@keyframes', + 'left', + 'letter-spacing', + 'line-break', + 'line-height', + 'list-style', + 'list-style-image', + 'list-style-position', + 'list-style-type', + 'margin', + 'shape-margin', + 'margin-block', + 'margin-block-end', + 'margin-block-start', + 'margin-bottom', + 'margin-inline', + 'margin-inline-end', + 'margin-inline-start', + 'margin-left', + 'margin-right', + 'margin-top', + 'mask', + 'mask-clip', + 'mask-composite', + 'mask-image', + 'mask-mode', + 'mask-origin', + 'mask-position', + 'mask-repeat', + 'mask-size', + 'mask-type', + 'max-height', + 'max-width', + '@media', + 'max-block-size', + 'max-inline-size', + 'min-block-size', + 'min-inline-size', + 'min-height', + 'min-width', + 'mix-blend-mode', + 'object-fit', + 'object-position', + 'offset', + 'offset-anchor', + 'offset-distance', + 'offset-path', + 'offset-rotate', + 'opacity', + 'order', + 'orphans', + 'outline', + 'outline-color', + 'outline-offset', + 'outline-style', + 'outline-width', + 'overflow', + 'overflow-anchor', + 'overflow-wrap', + 'overflow-x', + 'overflow-y', + 'overscroll-behavior', + 'overscroll-behavior-block', + 'overscroll-behavior-inline', + 'overscroll-behavior-x', + 'overscroll-behavior-y', + 'padding', + 'padding-block', + 'padding-block-end', + 'padding-block-start', + 'padding-bottom', + 'padding-inline', + 'padding-inline-end', + 'padding-inline-start', + 'padding-left', + 'padding-right', + 'padding-top', + 'page-break-after', + 'page-break-before', + 'page-break-inside', + 'paint-order', + 'perspective', + 'perspective-origin', + 'place-content', + 'place-items', + 'place-self', + 'pointer-events', + 'position', + 'quotes', + 'resize', + 'right', + 'rotate', + 'row-gap', + 'scale', + 'scroll-behavior', + 'scroll-margin', + 'scroll-margin-block', + 'scroll-margin-block-end', + 'scroll-margin-block-start', + 'scroll-margin-bottom', + 'scroll-margin-inline', + 'scroll-margin-inline-end', + 'scroll-margin-inline-start', + 'scroll-margin-left', + 'scroll-margin-right', + 'scroll-margin-top', + 'scroll-padding', + 'scroll-padding-block', + 'scroll-padding-block-end', + 'scroll-padding-block-start', + 'scroll-padding-bottom', + 'scroll-padding-inline', + 'scroll-padding-inline-end', + 'scroll-padding-inline-start', + 'scroll-padding-left', + 'scroll-padding-right', + 'scroll-padding-top', + 'scroll-snap-align', + 'scroll-snap-stop', + 'scroll-snap-type', + 'scrollbar-color', + 'tab-size', + 'table-layout', + 'text-align', + 'text-align-last', + 'text-combine-upright', + 'text-decoration', + 'text-decoration-color', + 'text-decoration-line', + 'text-decoration-style', + 'text-decoration-thickness', + 'text-emphasis', + 'text-emphasis-color', + 'text-emphasis-position', + 'text-emphasis-style', + 'text-indent', + 'text-justify', + 'text-orientation', + 'text-overflow', + 'text-shadow', + 'text-transform', + 'text-underline-offset', + 'text-underline-position', + 'top', + 'transform', + 'transform-origin', + 'transform-style', + 'transition', + 'transition-delay', + 'transition-duration', + 'transition-property', + 'transition-timing-function', + 'translate', + 'unicode-bidi', + 'user-select', + 'vertical-align', + 'visibility', + 'white-space', + 'widows', + 'width', + 'word-break', + 'word-spacing', + 'word-wrap', + 'writing-mode', + 'z-index', +]; diff --git a/packages/react-email/src/caniemail/data.ts b/packages/react-email/src/caniemail/data.ts new file mode 100644 index 0000000000..4a717e5203 --- /dev/null +++ b/packages/react-email/src/caniemail/data.ts @@ -0,0 +1,86441 @@ +import type { SupportEntry } from "./support-entry-parsing.js"; + +export const nicenames = { + "family": { + "gmail": "Gmail", + "outlook": "Outlook", + "yahoo": "Yahoo! Mail", + "apple-mail": "Apple Mail", + "aol": "AOL", + "thunderbird": "Mozilla Thunderbird", + "microsoft": "Microsoft", + "samsung-email": "Samsung Email", + "sfr": "SFR", + "orange": "Orange", + "protonmail": "ProtonMail", + "hey": "HEY", + "mail-ru": "Mail.ru", + "fastmail": "Fastmail", + "laposte": "LaPoste.net", + "t-online-de": "T-online.de", + "free-fr": "Free.fr", + "gmx": "GMX", + "web-de": "WEB.DE", + "ionos-1and1": "1&1", + "rainloop": "RainLoop", + "wp-pl": "WP.pl" + }, + "platform": { + "desktop-app": "Desktop", + "desktop-webmail": "Desktop Webmail", + "mobile-webmail": "Mobile Webmail", + "webmail": "Webmail", + "ios": "iOS", + "android": "Android", + "windows": "Windows", + "macos": "macOS", + "windows-mail": "Windows Mail", + "outlook-com": "Outlook.com" + }, + "support": { + "supported": "Supported", + "mitigated": "Partially supported", + "unsupported": "Not supported", + "unknown": "Support unknown", + "mixed": "Mixed support" + }, + "category": { + "html": "HTML", + "css": "CSS", + "image": "Image formats", + "others": "Others" + } +}; + +export const supportEntries: SupportEntry[] = [ + { + "slug": "amp", + "title": "AMP for Email", + "description": "Support for rendering emails in the AMP format.", + "url": "https://www.caniemail.com/features/amp/", + "category": "others", + "tags": [], + "keywords": "amp4email", + "last_test_date": "2020-03-31", + "test_url": "https://www.caniemail.com/tests/amp.html", + "test_results_url": null, + "stats": { + "apple-mail": { + "macos": [ + { + "12.4": "n" + } + ], + "ios": [ + { + "13.1": "n" + } + ] + }, + "gmail": { + "desktop-webmail": [ + { + "2020-04": "y" + }, + { + "2022-02": "y #1" + } + ], + "ios": [ + { + "2020-04": "y" + } + ], + "android": [ + { + "2020-04": "y" + } + ], + "mobile-webmail": [ + { + "2020-04": "n" + } + ] + }, + "orange": { + "desktop-webmail": [ + { + "2020-01": "n" + }, + { + "2021-03": "n" + } + ], + "ios": [ + { + "2020-01": "n" + } + ], + "android": [ + { + "2020-01": "n" + } + ] + }, + "outlook": { + "windows": [ + { + "2007": "n" + }, + { + "2010": "n" + }, + { + "2013": "n" + }, + { + "2016": "n" + }, + { + "2019": "n" + } + ], + "windows-mail": [ + { + "2019-10": "n" + } + ], + "macos": [ + { + "2019-10": "n" + }, + { + "16.80": "n" + } + ], + "outlook-com": [ + { + "2020-01": "n" + } + ], + "ios": [ + { + "2019-10": "n" + } + ], + "android": [ + { + "2019-10": "n" + } + ] + }, + "yahoo": { + "desktop-webmail": [ + { + "2019-10": "y" + }, + { + "2021-01": "y" + }, + { + "2022-02": "y #1" + } + ], + "ios": [ + { + "2019-10": "n" + }, + { + "2022-12": "y" + } + ], + "android": [ + { + "2019-10": "n" + }, + { + "2022-12": "y" + } + ] + }, + "aol": { + "desktop-webmail": [ + { + "2019-10": "n" + } + ], + "ios": [ + { + "2019-10": "n" + } + ], + "android": [ + { + "2019-10": "n" + } + ] + }, + "samsung-email": { + "android": [ + { + "5.0.10.2": "n" + } + ] + }, + "sfr": { + "desktop-webmail": [ + { + "2020-01": "n" + } + ], + "ios": [ + { + "2020-01": "n" + } + ], + "android": [ + { + "2020-01": "n" + } + ] + }, + "thunderbird": { + "macos": [ + { + "68.4": "n" + } + ] + }, + "protonmail": { + "desktop-webmail": [ + { + "2020-03": "n" + } + ], + "ios": [ + { + "2020-03": "n" + } + ], + "android": [ + { + "2020-03": "n" + } + ] + }, + "hey": { + "desktop-webmail": [ + { + "2020-06": "n" + } + ] + }, + "mail-ru": { + "desktop-webmail": [ + { + "2020-10": "y" + } + ] + }, + "fastmail": { + "desktop-webmail": [ + { + "2021-07": "n" + } + ] + }, + "laposte": { + "desktop-webmail": [ + { + "2021-08": "n" + } + ] + }, + "free-fr": { + "desktop-webmail": [ + { + "2022-12": "n" + } + ] + }, + "t-online-de": { + "desktop-webmail": [ + { + "2022-12": "n" + } + ] + }, + "gmx": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "ios": [ + { + "2022-06": "n" + } + ], + "android": [ + { + "2022-06": "n" + } + ] + }, + "web-de": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "ios": [ + { + "2022-06": "n" + } + ], + "android": [ + { + "2022-06": "n" + } + ] + }, + "ionos-1and1": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "android": [ + { + "2022-06": "n" + } + ] + } + }, + "notes": null, + "notes_by_num": { + "1": "Supported on compatible browsers. Refer to ‘supported platforms’ links listed below under resources." + } + }, + { + "slug": "bimi", + "title": "BIMI", + "description": "BIMI (Brand Indicators for Message Identification) is a specification allowing for the display of brand logos next to authenticated e-mails.", + "url": "https://www.caniemail.com/features/bimi/", + "category": "others", + "tags": [], + "keywords": "bimi, logo, brand", + "last_test_date": "2022-12-29", + "test_url": "https://www.caniemail.com", + "test_results_url": null, + "stats": { + "apple-mail": { + "macos": [ + { + "15": "n" + }, + { + "16": "y" + } + ], + "ios": [ + { + "15": "n" + }, + { + "16": "y" + } + ] + }, + "gmail": { + "desktop-webmail": [ + { + "2023-01": "y" + } + ], + "ios": [ + { + "2023-01": "y" + } + ], + "android": [ + { + "2023-01": "y" + } + ], + "mobile-webmail": [ + { + "2023-01": "n" + } + ] + }, + "orange": { + "desktop-webmail": [ + { + "2023-01": "n" + } + ], + "ios": [ + { + "2023-01": "n" + } + ], + "android": [ + { + "2023-01": "n" + } + ] + }, + "outlook": { + "windows": [ + { + "2007": "n" + }, + { + "2010": "n" + }, + { + "2013": "n" + }, + { + "2016": "n" + }, + { + "2019": "n" + } + ], + "windows-mail": [ + { + "2023-01": "n" + } + ], + "macos": [ + { + "16.56": "n" + } + ], + "outlook-com": [ + { + "2023-01": "n" + } + ], + "ios": [ + { + "2023-01": "n" + } + ], + "android": [ + { + "2023-01": "n" + } + ] + }, + "samsung-email": { + "android": [ + { + "6.0": "n" + } + ] + }, + "sfr": { + "desktop-webmail": [ + { + "2023-01": "n" + } + ], + "ios": [ + { + "2023-01": "n" + } + ], + "android": [ + { + "2023-01": "n" + } + ] + }, + "thunderbird": { + "macos": [ + { + "78.14": "n" + } + ] + }, + "aol": { + "desktop-webmail": [ + { + "2023-01": "n" + } + ], + "ios": [ + { + "2023-01": "n" + } + ], + "android": [ + { + "2023-01": "n" + } + ] + }, + "yahoo": { + "desktop-webmail": [ + { + "2023-01": "y" + } + ], + "ios": [ + { + "2023-01": "y" + } + ], + "android": [ + { + "2023-01": "y" + } + ] + }, + "protonmail": { + "desktop-webmail": [ + { + "2023-01": "n" + } + ], + "ios": [ + { + "2023-01": "n" + } + ], + "android": [ + { + "2023-01": "n" + } + ] + }, + "hey": { + "desktop-webmail": [ + { + "2023-01": "n" + } + ] + }, + "mail-ru": { + "desktop-webmail": [ + { + "2023-01": "n" + } + ] + }, + "fastmail": { + "desktop-webmail": [ + { + "2023-01": "y" + } + ] + }, + "laposte": { + "desktop-webmail": [ + { + "2022-08": "y" + } + ] + }, + "free-fr": { + "desktop-webmail": [ + { + "2023-01": "n" + } + ] + }, + "gmx": { + "desktop-webmail": [ + { + "2023-01": "n" + } + ] + }, + "t-online-de": { + "desktop-webmail": [ + { + "2023-01": "n" + } + ] + } + }, + "notes": "Data based on email clients providers own declarations.", + "notes_by_num": null + }, + { + "slug": "css-accent-color", + "title": "accent-color", + "description": "", + "url": "https://www.caniemail.com/features/css-accent-color/", + "category": "css", + "tags": [], + "keywords": "accent,color", + "last_test_date": "2023-12-19", + "test_url": "https://www.caniemail.com/tests/css-accent-color.html", + "test_results_url": "https://testi.at/proj/LAzSmlkimAnFmnrtPjPuPjpT1rO", + "stats": { + "apple-mail": { + "macos": [ + { + "16": "n" + }, + { + "17": "n" + }, + { + "18": "n" + }, + { + "19": "n" + }, + { + "20": "n" + }, + { + "21": "y" + } + ], + "ios": [ + { + "11": "n" + }, + { + "12": "n" + }, + { + "13": "n" + }, + { + "14": "y #1" + }, + { + "15": "y #1" + } + ] + }, + "gmail": { + "desktop-webmail": [ + { + "2022-07": "n" + } + ], + "ios": [ + { + "2022-07": "n" + } + ], + "android": [ + { + "2022-07": "n" + } + ], + "mobile-webmail": [ + { + "2022-07": "n" + } + ] + }, + "orange": { + "desktop-webmail": [ + { + "2022-07": "n" + } + ], + "ios": [ + { + "2022-07": "n" + } + ], + "android": [ + { + "2022-07": "n" + } + ] + }, + "outlook": { + "windows": [ + { + "2007": "n" + }, + { + "2010": "n" + }, + { + "2013": "n" + }, + { + "2016": "n" + }, + { + "2019": "n" + }, + { + "2021": "n" + } + ], + "windows-mail": [ + { + "2022-07": "n" + } + ], + "macos": [ + { + "2022-07": "n" + }, + { + "16.80": "n" + } + ], + "outlook-com": [ + { + "2022-07": "n" + } + ], + "ios": [ + { + "2022-07": "n" + } + ], + "android": [ + { + "2022-07": "n" + } + ] + }, + "yahoo": { + "desktop-webmail": [ + { + "2022-07": "n" + } + ], + "ios": [ + { + "2022-07": "n" + } + ], + "android": [ + { + "2022-07": "n" + } + ] + }, + "aol": { + "desktop-webmail": [ + { + "2022-07": "n" + } + ], + "ios": [ + { + "2022-07": "n" + } + ], + "android": [ + { + "2022-07": "n" + } + ] + }, + "samsung-email": { + "android": [ + { + "2022-07": "y #1" + } + ] + }, + "sfr": { + "desktop-webmail": [ + { + "2022-07": "y" + } + ], + "ios": [ + { + "2022-07": "n" + } + ], + "android": [ + { + "2022-07": "n" + } + ] + }, + "protonmail": { + "desktop-webmail": [ + { + "2022-07": "n" + } + ], + "ios": [ + { + "2022-07": "n" + } + ], + "android": [ + { + "2022-07": "n" + } + ] + }, + "hey": { + "desktop-webmail": [ + { + "2022-07": "y" + } + ] + }, + "mail-ru": { + "desktop-webmail": [ + { + "2022-07": "y #1" + } + ] + }, + "fastmail": { + "desktop-webmail": [ + { + "2022-07": "n" + } + ] + } + }, + "notes": null, + "notes_by_num": { + "1": "Supports `accent-color` but rendering depends on browser support." + } + }, + { + "slug": "css-align-items", + "title": "align-items", + "description": "", + "url": "https://www.caniemail.com/features/css-align-items/", + "category": "css", + "tags": [], + "keywords": "align,items,flexbox,grid", + "last_test_date": "2023-12-19", + "test_url": "https://www.caniemail.com/tests/css-align-items.html", + "test_results_url": "https://app.emailonacid.com/app/acidtest/FvYneb1dhiR4we6rAOf4AC02oFa6ksA0sTWxbEjgmt6Mg/list", + "stats": { + "apple-mail": { + "macos": [ + { + "11": "y" + }, + { + "12": "y" + }, + { + "13": "y" + } + ], + "ios": [ + { + "11": "y" + }, + { + "12": "y" + }, + { + "13": "y" + }, + { + "14": "y" + } + ] + }, + "gmail": { + "desktop-webmail": [ + { + "2020-12": "n" + } + ], + "ios": [ + { + "2020-12": "n" + } + ], + "android": [ + { + "2020-12": "n" + } + ], + "mobile-webmail": [ + { + "2020-12": "n" + } + ] + }, + "orange": { + "desktop-webmail": [ + { + "2021-02": "y" + }, + { + "2021-03": "n" + } + ], + "ios": [ + { + "2021-03": "y" + }, + { + "2024-04": "n" + } + ], + "android": [ + { + "2021-03": "y" + }, + { + "2024-04": "n" + } + ] + }, + "outlook": { + "windows": [ + { + "2007": "n" + }, + { + "2010": "n" + }, + { + "2013": "n" + }, + { + "2016": "n" + }, + { + "2019": "n" + } + ], + "windows-mail": [ + { + "2020-12": "n" + } + ], + "macos": [ + { + "2020-12": "y" + }, + { + "16.80": "y" + } + ], + "outlook-com": [ + { + "2020-12": "y" + } + ], + "ios": [ + { + "2020-12": "y" + } + ], + "android": [ + { + "4.2048.4": "y" + } + ] + }, + "yahoo": { + "desktop-webmail": [ + { + "2020-12": "n" + } + ], + "ios": [ + { + "2021-03": "n" + } + ], + "android": [ + { + "6.16.2.1519779": "n" + } + ] + }, + "aol": { + "desktop-webmail": [ + { + "2020-12": "n" + } + ], + "ios": [ + { + "2021-03": "n" + } + ], + "android": [ + { + "2021-03": "n" + } + ] + }, + "samsung-email": { + "android": [ + { + "6.1.31.2": "y" + } + ] + }, + "sfr": { + "desktop-webmail": [ + { + "2021-03": "y" + } + ], + "ios": [ + { + "2021-03": "y" + } + ], + "android": [ + { + "2021-03": "y" + } + ] + }, + "thunderbird": { + "macos": [ + { + "2020-12": "y" + } + ] + }, + "protonmail": { + "desktop-webmail": [ + { + "2021-03": "y" + } + ], + "ios": [ + { + "2021-03": "y" + } + ], + "android": [ + { + "2021-03": "y" + } + ] + }, + "hey": { + "desktop-webmail": [ + { + "2021-03": "y" + } + ] + }, + "mail-ru": { + "desktop-webmail": [ + { + "2020-12": "y" + } + ] + }, + "fastmail": { + "desktop-webmail": [ + { + "2021-07": "y" + } + ] + }, + "laposte": { + "desktop-webmail": [ + { + "2021-08": "y #1" + } + ] + }, + "gmx": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "ios": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "y" + } + ] + }, + "web-de": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "ios": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "y" + } + ] + }, + "ionos-1and1": { + "desktop-webmail": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "y" + } + ] + } + }, + "notes": null, + "notes_by_num": { + "1": "Supported. But a default style of `margin:auto` is applied on every element and can prevent the expected result." + } + }, + { + "slug": "css-animation", + "title": "animation", + "description": "Tests for the shorthand `animation` property and its longhand equivalents.", + "url": "https://www.caniemail.com/features/css-animation/", + "category": "css", + "tags": [], + "keywords": "keyframes", + "last_test_date": "2023-12-19", + "test_url": "https://www.caniemail.com/tests/css-animation.html", + "test_results_url": "https://app.emailonacid.com/app/acidtest/u4oWccYOFNNyTagHs2NSUZqJYQ3MssrqDMocBnRa35hf7/list", + "stats": { + "apple-mail": { + "macos": [ + { + "13": "y" + } + ], + "ios": [ + { + "13": "y" + } + ] + }, + "gmail": { + "desktop-webmail": [ + { + "2021-05": "n" + } + ], + "ios": [ + { + "2021-05": "n" + } + ], + "android": [ + { + "2021-05": "n" + } + ], + "mobile-webmail": [ + { + "2021-05": "n" + } + ] + }, + "orange": { + "desktop-webmail": [ + { + "2021-05": "n" + } + ], + "ios": [ + { + "2021-05": "n" + } + ], + "android": [ + { + "2021-05": "n" + } + ] + }, + "outlook": { + "windows": [ + { + "2003": "n" + }, + { + "2007": "n" + }, + { + "2010": "n" + }, + { + "2013": "n" + }, + { + "2016": "n" + }, + { + "2019": "n" + } + ], + "windows-mail": [ + { + "2021-05": "n" + } + ], + "macos": [ + { + "2011": "y" + }, + { + "2016": "y" + }, + { + "16.80": "n" + } + ], + "outlook-com": [ + { + "2021-05": "n" + } + ], + "ios": [ + { + "2021-05": "n" + } + ], + "android": [ + { + "2021-05": "n" + } + ] + }, + "samsung-email": { + "android": [ + { + "6.1": "y" + } + ] + }, + "sfr": { + "desktop-webmail": [ + { + "2021-05": "a #1" + } + ], + "ios": [ + { + "2021-05": "n" + } + ], + "android": [ + { + "2021-05": "n" + } + ] + }, + "thunderbird": { + "macos": [ + { + "78.10": "y" + } + ] + }, + "aol": { + "desktop-webmail": [ + { + "2021-05": "n" + } + ], + "ios": [ + { + "2021-05": "n" + } + ], + "android": [ + { + "2021-05": "n" + } + ] + }, + "yahoo": { + "desktop-webmail": [ + { + "2021-05": "n" + } + ], + "ios": [ + { + "2021-05": "n" + } + ], + "android": [ + { + "2021-05": "n" + } + ] + }, + "protonmail": { + "desktop-webmail": [ + { + "2021-05": "n" + } + ], + "ios": [ + { + "2021-05": "n" + } + ], + "android": [ + { + "2021-05": "n" + } + ] + }, + "hey": { + "desktop-webmail": [ + { + "2021-05": "y" + } + ] + }, + "mail-ru": { + "desktop-webmail": [ + { + "2021-05": "n" + } + ] + }, + "fastmail": { + "desktop-webmail": [ + { + "2021-07": "y" + } + ] + }, + "laposte": { + "desktop-webmail": [ + { + "2021-08": "a #1" + } + ] + }, + "gmx": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "ios": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "y" + } + ] + }, + "web-de": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "ios": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "y" + } + ] + }, + "ionos-1and1": { + "desktop-webmail": [ + { + "2022-06": "a #2" + } + ], + "android": [ + { + "2022-06": "y" + } + ] + } + }, + "notes": null, + "notes_by_num": { + "1": "Buggy. Animation properties are supported but `@keyframes` are incorrectly prefixed.", + "2": "Partial. Only supports from and to keyframes. Does not support % keyframes" + } + }, + { + "slug": "css-aspect-ratio", + "title": "aspect-ratio", + "description": "Sets a preferred aspect ratio for the element", + "url": "https://www.caniemail.com/features/css-aspect-ratio/", + "category": "css", + "tags": [], + "keywords": "ratio", + "last_test_date": "2023-12-19", + "test_url": "https://www.caniemail.com/tests/css-aspect-ratio.html", + "test_results_url": "https://testi.at/proj/Mv0IO0vs3vTgRQuJ8IzyBfD6", + "stats": { + "apple-mail": { + "macos": [ + { + "14": "n" + }, + { + "15.0": "y" + } + ], + "ios": [ + { + "11": "n" + }, + { + "12": "n" + }, + { + "13": "n" + }, + { + "14": "n" + }, + { + "15": "y" + } + ] + }, + "gmail": { + "desktop-webmail": [ + { + "2021-10": "n" + } + ], + "ios": [ + { + "2021-10": "n" + } + ], + "android": [ + { + "2021-10": "n" + } + ], + "mobile-webmail": [ + { + "2021-10": "n" + } + ] + }, + "orange": { + "desktop-webmail": [ + { + "2021-10": "n" + } + ], + "ios": [ + { + "2021-10": "n" + } + ], + "android": [ + { + "2021-10": "n" + } + ] + }, + "outlook": { + "windows": [ + { + "2007": "n" + }, + { + "2010": "n" + }, + { + "2013": "n" + }, + { + "2016": "n" + }, + { + "2019": "n" + } + ], + "windows-mail": [ + { + "2021-10": "n" + } + ], + "macos": [ + { + "2021-10": "n" + }, + { + "16.80": "n" + } + ], + "outlook-com": [ + { + "2021-10": "n" + }, + { + "2023-12": "n" + } + ], + "ios": [ + { + "2021-10": "n" + } + ], + "android": [ + { + "2021-10": "n" + } + ] + }, + "yahoo": { + "desktop-webmail": [ + { + "2021-10": "n" + } + ], + "ios": [ + { + "2021-10": "n" + } + ], + "android": [ + { + "6.37": "n" + } + ] + }, + "aol": { + "desktop-webmail": [ + { + "2021-10": "n" + } + ], + "ios": [ + { + "2021-10": "n" + } + ], + "android": [ + { + "2021-10": "n" + } + ] + }, + "samsung-email": { + "android": [ + { + "2021-10": "n" + } + ] + }, + "sfr": { + "desktop-webmail": [ + { + "2021-11": "y" + } + ], + "ios": [ + { + "2021-11": "y #1" + } + ], + "android": [ + { + "2021-11": "y" + } + ] + }, + "thunderbird": { + "macos": [ + { + "78.10.2": "n" + } + ] + }, + "protonmail": { + "desktop-webmail": [ + { + "2021-11": "y" + } + ], + "ios": [ + { + "2021-11": "y #1" + } + ], + "android": [ + { + "2021-11": "y" + } + ] + }, + "hey": { + "desktop-webmail": [ + { + "2021-11": "y" + } + ] + }, + "mail-ru": { + "desktop-webmail": [ + { + "2021-10": "y" + } + ] + }, + "fastmail": { + "desktop-webmail": [ + { + "2021-11": "n" + } + ] + }, + "laposte": { + "desktop-webmail": [ + { + "2021-10": "y" + } + ] + }, + "gmx": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "ios": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "y" + } + ] + }, + "web-de": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "ios": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "y" + } + ] + }, + "ionos-1and1": { + "desktop-webmail": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "y" + } + ] + } + }, + "notes": null, + "notes_by_num": { + "1": "Requires iOS 15." + } + }, + { + "slug": "css-at-font-face", + "title": "@font-face", + "description": "`@font-face` in CSS allows to include your own fonts inside an email.", + "url": "https://www.caniemail.com/features/css-at-font-face/", + "category": "css", + "tags": [], + "keywords": "font face, web fonts, google fonts", + "last_test_date": "2023-12-19", + "test_url": "https://www.caniemail.com/tests/css-font-face.html", + "test_results_url": "https://app.emailonacid.com/app/acidtest/veY9MhuhgFeF1ly5crrhTXawfLJSwxgpYi27OElI7iSoc/list", + "stats": { + "apple-mail": { + "macos": [ + { + "12.2": "y" + } + ], + "ios": [ + { + "10.3": "y" + }, + { + "12.3.1": "y" + } + ] + }, + "gmail": { + "desktop-webmail": [ + { + "2019-07": "n #6" + } + ], + "ios": [ + { + "2019-07": "n" + } + ], + "android": [ + { + "2019-07": "n" + } + ], + "mobile-webmail": [ + { + "2020-02": "n" + } + ] + }, + "orange": { + "desktop-webmail": [ + { + "2019-05": "a #2" + }, + { + "2021-03": "n #7" + }, + { + "2024-03": "n" + } + ], + "ios": [ + { + "2019-07": "y" + }, + { + "2024-03": "n" + } + ], + "android": [ + { + "2019-07": "a #1" + }, + { + "2024-04": "n" + } + ] + }, + "outlook": { + "windows": [ + { + "2003": "a #3" + }, + { + "2007": "a #4 #5" + }, + { + "2010": "a #4 #5" + }, + { + "2013": "a #4 #5" + }, + { + "2016": "a #4 #5" + }, + { + "2019": "a #4" + } + ], + "windows-mail": [ + { + "2020-01": "n" + } + ], + "macos": [ + { + "2011": "y" + }, + { + "2016": "y" + }, + { + "16.80": "n" + } + ], + "outlook-com": [ + { + "2019-07": "n" + }, + { + "2023-12": "n" + } + ], + "ios": [ + { + "2.51.1": "y" + }, + { + "3.29.0": "n" + } + ], + "android": [ + { + "2019-07": "n" + } + ] + }, + "samsung-email": { + "android": [ + { + "6.0": "y #8" + }, + { + "2021-11": "y #8" + } + ] + }, + "sfr": { + "desktop-webmail": [ + { + "2019-07": "a #2" + }, + { + "2025-07": "n" + } + ], + "ios": [ + { + "2019-07": "n" + } + ], + "android": [ + { + "2019-07": "n" + } + ] + }, + "thunderbird": { + "macos": [ + { + "60.7": "y" + }, + { + "78.5": "y" + } + ] + }, + "aol": { + "desktop-webmail": [ + { + "2020-01": "n" + } + ], + "ios": [ + { + "2020-01": "n" + } + ], + "android": [ + { + "2020-01": "n" + } + ] + }, + "yahoo": { + "desktop-webmail": [ + { + "2019-07": "n" + } + ], + "ios": [ + { + "2019-07": "n" + } + ], + "android": [ + { + "2019-07": "n" + } + ] + }, + "protonmail": { + "desktop-webmail": [ + { + "2020-03": "n" + } + ], + "ios": [ + { + "2020-03": "n" + } + ], + "android": [ + { + "2020-03": "y" + } + ] + }, + "hey": { + "desktop-webmail": [ + { + "2020-06": "y" + } + ] + }, + "mail-ru": { + "desktop-webmail": [ + { + "2020-10": "n" + } + ] + }, + "fastmail": { + "desktop-webmail": [ + { + "2021-07": "n" + } + ] + }, + "laposte": { + "desktop-webmail": [ + { + "2021-08": "a #2" + }, + { + "2025-07": "n" + } + ] + }, + "gmx": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "ios": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "n" + } + ] + }, + "web-de": { + "desktop-webmail": [ + { + "2022-06": "n" + } + ], + "ios": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "n" + } + ] + }, + "ionos-1and1": { + "desktop-webmail": [ + { + "2022-06": "y" + } + ], + "android": [ + { + "2022-06": "n" + } + ] + } + }, + "notes": null, + "notes_by_num": { + "1": "Partial. Only supported through a `` tag.", + "2": "Partial. Only supported directly through a `