react-email 4.0.0-alpha.5 → 4.0.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/cli/index.js +1179 -2659
- package/dist/cli/index.mjs +17 -11
- package/dist/preview/.next/BUILD_ID +1 -1
- package/dist/preview/.next/app-build-manifest.json +32 -31
- package/dist/preview/.next/app-path-routes-manifest.json +6 -1
- package/dist/preview/.next/build-manifest.json +14 -14
- package/dist/preview/.next/cache/.rscinfo +1 -1
- package/dist/preview/.next/cache/webpack/client-production/0.pack +0 -0
- package/dist/preview/.next/cache/webpack/client-production/index.pack +0 -0
- package/dist/preview/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/dist/preview/.next/cache/webpack/server-production/0.pack +0 -0
- package/dist/preview/.next/cache/webpack/server-production/index.pack +0 -0
- package/dist/preview/.next/diagnostics/framework.json +1 -1
- package/dist/preview/.next/export-marker.json +6 -1
- package/dist/preview/.next/images-manifest.json +57 -1
- package/dist/preview/.next/next-minimal-server.js.nft.json +1 -1
- package/dist/preview/.next/next-server.js.nft.json +1 -1
- package/dist/preview/.next/prerender-manifest.json +41 -1
- package/dist/preview/.next/required-server-files.json +310 -1
- package/dist/preview/.next/routes-manifest.json +64 -1
- package/dist/preview/.next/server/app/_not-found/page.js +1 -1
- package/dist/preview/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/preview/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/preview/.next/server/app/favicon.ico/route.js +1 -1
- package/dist/preview/.next/server/app/favicon.ico/route.js.nft.json +1 -1
- package/dist/preview/.next/server/app/page.js +1 -1
- package/dist/preview/.next/server/app/page.js.nft.json +1 -1
- package/dist/preview/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/preview/.next/server/app/preview/[...slug]/page.js +51 -11
- package/dist/preview/.next/server/app/preview/[...slug]/page.js.nft.json +1 -1
- package/dist/preview/.next/server/app/preview/[...slug]/page_client-reference-manifest.js +1 -1
- package/dist/preview/.next/server/app-paths-manifest.json +1 -1
- package/dist/preview/.next/server/chunks/446.js +6 -0
- package/dist/preview/.next/server/chunks/600.js +8 -0
- package/dist/preview/.next/server/chunks/811.js +13 -0
- package/dist/preview/.next/server/chunks/816.js +14 -0
- package/dist/preview/.next/server/chunks/943.js +1 -0
- package/dist/preview/.next/server/functions-config-manifest.json +4 -1
- package/dist/preview/.next/server/middleware-build-manifest.js +1 -1
- package/dist/preview/.next/server/next-font-manifest.js +1 -1
- package/dist/preview/.next/server/next-font-manifest.json +1 -1
- package/dist/preview/.next/server/pages/500.html +1 -1
- package/dist/preview/.next/server/pages/_app.js +1 -1
- package/dist/preview/.next/server/pages/_app.js.nft.json +1 -1
- package/dist/preview/.next/server/pages/_document.js +1 -1
- package/dist/preview/.next/server/pages/_document.js.nft.json +1 -1
- package/dist/preview/.next/server/pages/_error.js +1 -1
- package/dist/preview/.next/server/pages/_error.js.nft.json +1 -1
- package/dist/preview/.next/server/pages-manifest.json +5 -1
- package/dist/preview/.next/server/server-reference-manifest.js +1 -1
- package/dist/preview/.next/server/server-reference-manifest.json +1 -1
- package/dist/preview/.next/server/webpack-runtime.js +1 -1
- package/dist/preview/.next/static/Pms2orsQgT5xpttCfZfH5/_buildManifest.js +1 -0
- package/dist/preview/.next/static/chunks/287-7864b805e6bdc854.js +1 -0
- package/dist/preview/.next/static/chunks/412-31817e53b50a3e73.js +1 -0
- package/dist/preview/.next/static/chunks/683-1fb40795502f6e63.js +1 -0
- package/dist/preview/.next/static/chunks/744-79730358b37b2212.js +1 -0
- package/dist/preview/.next/static/chunks/781-5f16c6bc9d9d4cc1.js +1 -0
- package/dist/preview/.next/static/chunks/832ad4be-cb988facfb8f955f.js +1 -0
- package/dist/preview/.next/static/chunks/880-9c0b721328117b8b.js +1 -0
- package/dist/preview/.next/static/chunks/{afa401a5-a600c227dacf3ab4.js → afa401a5-3e949a1cfd317dd3.js} +3 -3
- package/dist/preview/.next/static/chunks/app/_not-found/page-09d694081cc9d4dc.js +1 -0
- package/dist/preview/.next/static/chunks/app/layout-ffdee5cc1be30e7b.js +1 -0
- package/dist/preview/.next/static/chunks/app/page-9ea0bd45cd6294b0.js +1 -0
- package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-9e22979a25c836c0.js +1 -0
- package/dist/preview/.next/static/chunks/framework-c2bd6d936e3077bc.js +1 -0
- package/dist/preview/.next/static/chunks/main-44463a8301435b64.js +1 -0
- package/dist/preview/.next/static/chunks/main-app-256b213b179a95cc.js +1 -0
- package/dist/preview/.next/static/chunks/pages/_app-f3011d3f00bb8dba.js +1 -0
- package/dist/preview/.next/static/chunks/pages/_error-39a87dee2e97a2a3.js +1 -0
- package/dist/preview/.next/static/chunks/{webpack-2eb145a20ee6cb77.js → webpack-41e2667c9f086a4f.js} +1 -1
- package/dist/preview/.next/static/css/eaae8ce545b295f9.css +3 -0
- package/dist/preview/.next/trace +26 -22
- package/dist/preview/.next/types/app/layout.ts +1 -1
- package/dist/preview/.next/types/app/page.ts +84 -0
- package/dist/preview/.next/types/app/preview/[...slug]/page.ts +1 -1
- package/dist/preview/.next/types/cache-life.d.ts +3 -3
- package/package.json +14 -9
- package/scripts/build-preview-server.mjs +32 -0
- package/scripts/fill-caniemail-data.mjs +36 -0
- package/src/actions/email-validation/caniemail-data.ts +85993 -0
- package/src/actions/email-validation/check-compatibility.ts +321 -0
- package/src/actions/email-validation/check-images.spec.tsx +15 -13
- package/src/actions/email-validation/check-images.ts +8 -2
- package/src/actions/email-validation/check-links.spec.tsx +27 -15
- package/src/actions/email-validation/check-links.ts +8 -2
- package/src/actions/email-validation/get-code-location-from-ast-element.ts +18 -0
- package/src/actions/get-email-path-from-slug.ts +1 -1
- package/src/actions/render-email-by-path.tsx +2 -1
- package/src/{utils/emails-directory-absolute-path.ts → app/env.ts} +5 -0
- package/src/app/layout.tsx +1 -1
- package/src/app/page.tsx +1 -1
- package/src/app/preview/[...slug]/page.tsx +89 -19
- package/src/app/preview/[...slug]/preview.tsx +25 -68
- package/src/components/code-container.tsx +90 -71
- package/src/components/code.tsx +106 -43
- package/src/components/icons/icon-info.tsx +18 -0
- package/src/components/icons/icon-reload.tsx +13 -14
- package/src/components/logo.tsx +3 -2
- package/src/components/resizable-wrapper.tsx +1 -4
- package/src/components/sidebar/file-tree-directory-children.tsx +1 -0
- package/src/components/sidebar/sidebar.tsx +2 -3
- package/src/components/toolbar/code-preview-line-link.tsx +40 -0
- package/src/components/toolbar/compatibility.tsx +113 -0
- package/src/components/toolbar/linter.tsx +226 -125
- package/src/components/toolbar/results.tsx +5 -2
- package/src/components/toolbar/spam-assassin.tsx +40 -43
- package/src/components/toolbar/toolbar-button.tsx +52 -0
- package/src/components/toolbar/use-cached-state.ts +33 -0
- package/src/components/toolbar.tsx +196 -110
- package/src/components/tooltip-content.tsx +1 -1
- package/src/components/topbar/view-size-controls.tsx +1 -1
- package/src/components/topbar.tsx +4 -29
- package/src/contexts/emails.tsx +2 -1
- package/src/contexts/fragment-identifier.tsx +46 -0
- package/src/contexts/preview.tsx +81 -0
- package/src/hooks/use-email-rendering-result.ts +2 -1
- package/src/hooks/use-fragment-identifier.ts +14 -0
- package/src/utils/__snapshots__/get-email-component.spec.ts.snap +1 -1
- package/src/utils/caniemail/all-css-properties.ts +358 -0
- package/src/utils/caniemail/ast/get-object-variables.ts +61 -0
- package/src/utils/caniemail/ast/get-used-style-properties.ts +91 -0
- package/src/utils/caniemail/get-compatibility-stats-for-entry.ts +118 -0
- package/src/utils/caniemail/get-css-functions.ts +25 -0
- package/src/utils/caniemail/get-css-property-names.ts +32 -0
- package/src/utils/caniemail/get-css-property-with-value.ts +14 -0
- package/src/utils/caniemail/get-css-unit.ts +3 -0
- package/src/utils/caniemail/get-element-attributes.ts +7 -0
- package/src/utils/caniemail/get-element-names.ts +20 -0
- package/src/utils/caniemail/tailwind/generate-tailwind-rules.ts +30 -0
- package/src/utils/caniemail/tailwind/get-tailwind-config.ts +203 -0
- package/src/utils/caniemail/tailwind/get-tailwind-metadata.spec.ts +25 -0
- package/src/utils/caniemail/tailwind/get-tailwind-metadata.ts +45 -0
- package/src/utils/caniemail/tailwind/setup-tailwind-context.ts +15 -0
- package/src/utils/get-email-component.ts +34 -67
- package/src/utils/get-line-and-column-from-offset.spec.ts +11 -0
- package/src/utils/get-line-and-column-from-offset.ts +11 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/linting.ts +60 -0
- package/src/utils/load-stream.ts +15 -0
- package/src/utils/result.ts +49 -0
- package/src/utils/run-bundled-code.ts +64 -0
- package/src/utils/sanitize.ts +6 -0
- package/tailwind-internals.d.ts +133 -0
- package/tsconfig.json +9 -3
- package/build-preview-server.mjs +0 -25
- package/dist/preview/.next/cache/images/TcyzHbFXGFjrOu3wEMvDoSmqCh3qP3iiNqJf0QbED9Y/60.1741728556140.cQ5qicbpvoXZ7leVmWqG2ElLwXB1ynYeSv8MBSA-QeM.Vy8iMWM3MGUtMTk1ODcxYmIyNzMi.webp +0 -0
- package/dist/preview/.next/cache/webpack/client-development/0.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/1.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/10.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/11.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/12.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/13.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/2.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/3.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/4.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/5.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/6.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/7.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/8.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/9.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/index.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/client-development/index.pack.gz.old +0 -0
- package/dist/preview/.next/cache/webpack/server-development/0.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/1.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/2.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/3.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/4.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/5.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/6.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/7.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/8.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/9.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/index.pack.gz +0 -0
- package/dist/preview/.next/cache/webpack/server-development/index.pack.gz.old +0 -0
- package/dist/preview/.next/server/chunks/143.js +0 -6
- package/dist/preview/.next/server/chunks/409.js +0 -5
- package/dist/preview/.next/server/chunks/46.js +0 -1
- package/dist/preview/.next/server/chunks/478.js +0 -14
- package/dist/preview/.next/server/chunks/707.js +0 -13
- package/dist/preview/.next/static/B4EYZiVzdylEG9lAIl-aO/_buildManifest.js +0 -1
- package/dist/preview/.next/static/chunks/575-bc52750855c25df4.js +0 -2
- package/dist/preview/.next/static/chunks/684-0f1ef7361c499798.js +0 -1
- package/dist/preview/.next/static/chunks/684c6b30-0c65da32762fc4ee.js +0 -1
- package/dist/preview/.next/static/chunks/81-e7539b08d9d3fb4d.js +0 -1
- package/dist/preview/.next/static/chunks/883-70c8267c50bc4133.js +0 -1
- package/dist/preview/.next/static/chunks/921-d1dc8c63f49e85d6.js +0 -1
- package/dist/preview/.next/static/chunks/app/_not-found/page-03ce767859c36d4e.js +0 -1
- package/dist/preview/.next/static/chunks/app/layout-7cf14e28880544f1.js +0 -1
- package/dist/preview/.next/static/chunks/app/page-065cb49b0a078541.js +0 -1
- package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-656510fd180c803c.js +0 -1
- package/dist/preview/.next/static/chunks/framework-2a724981073c3a29.js +0 -1
- package/dist/preview/.next/static/chunks/main-552b9719bbc3a274.js +0 -1
- package/dist/preview/.next/static/chunks/main-app-914a73336fd45af5.js +0 -1
- package/dist/preview/.next/static/chunks/pages/_app-77ca34bce25ac75c.js +0 -1
- package/dist/preview/.next/static/chunks/pages/_error-73f611c46abbb495.js +0 -1
- package/dist/preview/.next/static/css/2df96d9ee014e8de.css +0 -3
- package/src/actions/email-validation/get-line-and-column-from-index.spec.ts +0 -22
- package/src/actions/email-validation/get-line-and-column-from-index.ts +0 -43
- package/src/components/icons/icon-scanner.tsx +0 -19
- package/src/components/icons/icon-scissors.tsx +0 -19
- /package/dist/preview/.next/static/{B4EYZiVzdylEG9lAIl-aO → Pms2orsQgT5xpttCfZfH5}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createContext } from 'tailwindcss/lib/lib/setupContextUtils';
|
|
2
|
+
import resolveConfig from 'tailwindcss/resolveConfig';
|
|
3
|
+
import type { TailwindConfig } from './get-tailwind-config';
|
|
4
|
+
|
|
5
|
+
export const setupTailwindContext = (config: TailwindConfig) => {
|
|
6
|
+
return createContext(
|
|
7
|
+
resolveConfig({
|
|
8
|
+
...config,
|
|
9
|
+
content: [],
|
|
10
|
+
corePlugins: {
|
|
11
|
+
preflight: false,
|
|
12
|
+
},
|
|
13
|
+
}),
|
|
14
|
+
);
|
|
15
|
+
};
|
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
1
|
import path from 'node:path';
|
|
3
|
-
import vm from 'node:vm';
|
|
4
2
|
import type { render } from '@react-email/render';
|
|
5
3
|
import { type BuildFailure, type OutputFile, build } from 'esbuild';
|
|
6
4
|
import type React from 'react';
|
|
7
5
|
import type { RawSourceMap } from 'source-map-js';
|
|
6
|
+
import { z } from 'zod';
|
|
8
7
|
import { renderingUtilitiesExporter } from './esbuild/renderring-utilities-exporter';
|
|
9
8
|
import { improveErrorWithSourceMap } from './improve-error-with-sourcemap';
|
|
10
|
-
import {
|
|
9
|
+
import { isErr } from './result';
|
|
10
|
+
import { runBundledCode } from './run-bundled-code';
|
|
11
11
|
import type { EmailTemplate as EmailComponent } from './types/email-template';
|
|
12
12
|
import type { ErrorObject } from './types/error-object';
|
|
13
13
|
|
|
14
|
+
const EmailComponentModule = z.object({
|
|
15
|
+
default: z.any(),
|
|
16
|
+
render: z.function(),
|
|
17
|
+
reactEmailCreateReactElement: z.function(),
|
|
18
|
+
});
|
|
19
|
+
|
|
14
20
|
export const getEmailComponent = async (
|
|
15
21
|
emailPath: string,
|
|
16
22
|
): Promise<
|
|
@@ -61,79 +67,38 @@ export const getEmailComponent = async (
|
|
|
61
67
|
const bundledEmailFile = outputFiles[1]!;
|
|
62
68
|
const builtEmailCode = bundledEmailFile.text;
|
|
63
69
|
|
|
64
|
-
const fakeContext = {
|
|
65
|
-
...global,
|
|
66
|
-
console,
|
|
67
|
-
Buffer,
|
|
68
|
-
AbortSignal,
|
|
69
|
-
Event,
|
|
70
|
-
EventTarget,
|
|
71
|
-
TextDecoder,
|
|
72
|
-
Request,
|
|
73
|
-
Response,
|
|
74
|
-
TextDecoderStream,
|
|
75
|
-
TextEncoder,
|
|
76
|
-
TextEncoderStream,
|
|
77
|
-
ReadableStream,
|
|
78
|
-
URL,
|
|
79
|
-
URLSearchParams,
|
|
80
|
-
Headers,
|
|
81
|
-
module: {
|
|
82
|
-
exports: {
|
|
83
|
-
default: undefined as unknown,
|
|
84
|
-
render: undefined as unknown,
|
|
85
|
-
reactEmailCreateReactElement: undefined as unknown,
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
__filename: emailPath,
|
|
89
|
-
__dirname: path.dirname(emailPath),
|
|
90
|
-
require: (specifiedModule: string) => {
|
|
91
|
-
let m = specifiedModule;
|
|
92
|
-
if (specifiedModule.startsWith('node:')) {
|
|
93
|
-
m = m.split(':')[1]!;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (m in staticNodeModulesForVM) {
|
|
97
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
98
|
-
return staticNodeModulesForVM[m];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-useless-template-literals
|
|
102
|
-
return require(`${specifiedModule}`) as unknown;
|
|
103
|
-
// this stupid string templating was necessary to not have
|
|
104
|
-
// webpack warnings like:
|
|
105
|
-
//
|
|
106
|
-
// Import trace for requested module:
|
|
107
|
-
// ./src/utils/get-email-component.tsx
|
|
108
|
-
// ./src/app/page.tsx
|
|
109
|
-
// ⚠ ./src/utils/get-email-component.tsx
|
|
110
|
-
// Critical dependency: the request of a dependency is an expression
|
|
111
|
-
},
|
|
112
|
-
process,
|
|
113
|
-
};
|
|
114
70
|
const sourceMapToEmail = JSON.parse(sourceMapFile.text) as RawSourceMap;
|
|
115
71
|
// because it will have a path like <tsconfigLocation>/stdout/email.js.map
|
|
116
72
|
sourceMapToEmail.sourceRoot = path.resolve(sourceMapFile.path, '../..');
|
|
117
73
|
sourceMapToEmail.sources = sourceMapToEmail.sources.map((source) =>
|
|
118
74
|
path.resolve(sourceMapFile.path, '..', source),
|
|
119
75
|
);
|
|
120
|
-
try {
|
|
121
|
-
vm.runInNewContext(builtEmailCode, fakeContext, { filename: emailPath });
|
|
122
|
-
} catch (exception) {
|
|
123
|
-
const error = exception as Error;
|
|
124
76
|
|
|
125
|
-
|
|
77
|
+
const runningResult = runBundledCode(builtEmailCode, emailPath);
|
|
126
78
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
79
|
+
if (isErr(runningResult)) {
|
|
80
|
+
const { error } = runningResult;
|
|
81
|
+
if (error instanceof Error) {
|
|
82
|
+
error.stack &&= error.stack.split('at Script.runInContext (node:vm')[0];
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
error: improveErrorWithSourceMap(error, emailPath, sourceMapToEmail),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
throw error;
|
|
130
90
|
}
|
|
131
91
|
|
|
132
|
-
|
|
92
|
+
const parseResult = EmailComponentModule.safeParse(runningResult.value);
|
|
93
|
+
|
|
94
|
+
if (parseResult.error) {
|
|
133
95
|
return {
|
|
134
96
|
error: improveErrorWithSourceMap(
|
|
135
97
|
new Error(
|
|
136
|
-
`The email component at ${emailPath} does not contain
|
|
98
|
+
`The email component at ${emailPath} does not contain the expected exports`,
|
|
99
|
+
{
|
|
100
|
+
cause: parseResult.error,
|
|
101
|
+
},
|
|
137
102
|
),
|
|
138
103
|
emailPath,
|
|
139
104
|
sourceMapToEmail,
|
|
@@ -141,11 +106,13 @@ export const getEmailComponent = async (
|
|
|
141
106
|
};
|
|
142
107
|
}
|
|
143
108
|
|
|
109
|
+
const { data: componentModule } = parseResult;
|
|
110
|
+
|
|
144
111
|
return {
|
|
145
|
-
emailComponent:
|
|
146
|
-
render:
|
|
147
|
-
createElement:
|
|
148
|
-
.reactEmailCreateReactElement as typeof React.createElement,
|
|
112
|
+
emailComponent: componentModule.default as EmailComponent,
|
|
113
|
+
render: componentModule.render as typeof render,
|
|
114
|
+
createElement:
|
|
115
|
+
componentModule.reactEmailCreateReactElement as typeof React.createElement,
|
|
149
116
|
|
|
150
117
|
sourceMapToOriginalFile: sourceMapToEmail,
|
|
151
118
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getLineAndColumnFromOffset } from './get-line-and-column-from-offset';
|
|
2
|
+
|
|
3
|
+
test('getLineAndColumnFromOffset()', () => {
|
|
4
|
+
const content = `export default function MyEmail() {
|
|
5
|
+
return <div className="testing classes to make sure this is not removed" id="my-div" aria-label="my beautiful div">
|
|
6
|
+
inside the div, should also stay unchanged
|
|
7
|
+
</div>;
|
|
8
|
+
}`;
|
|
9
|
+
const offset = content.indexOf('className');
|
|
10
|
+
expect(getLineAndColumnFromOffset(offset, content)).toEqual([2, 15]);
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const getLineAndColumnFromOffset = (
|
|
2
|
+
offset: number,
|
|
3
|
+
content: string,
|
|
4
|
+
): [line: number, column: number] => {
|
|
5
|
+
const lineBreaks = [...content.slice(0, offset).matchAll(/\n|\r|\r\n/g)];
|
|
6
|
+
|
|
7
|
+
const line = lineBreaks.length + 1;
|
|
8
|
+
const column = offset - (lineBreaks[lineBreaks.length - 1]?.index ?? 0);
|
|
9
|
+
|
|
10
|
+
return [line, column];
|
|
11
|
+
};
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { checkImages } from '../actions/email-validation/check-images';
|
|
2
|
+
import { checkLinks } from '../actions/email-validation/check-links';
|
|
3
|
+
import type { LintingRow } from '../components/toolbar/linter';
|
|
4
|
+
import { loadStream } from './load-stream';
|
|
5
|
+
|
|
6
|
+
export interface LintingSource<T> {
|
|
7
|
+
getStream(): Promise<ReadableStream<T>>;
|
|
8
|
+
mapValue(value: NoInfer<T>): LintingRow | undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function createSource<T>(source: LintingSource<T>): LintingSource<T> {
|
|
12
|
+
return source;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getLintingSources(
|
|
16
|
+
markup: string,
|
|
17
|
+
|
|
18
|
+
urlBase: string,
|
|
19
|
+
): LintingSource<unknown>[] {
|
|
20
|
+
return [
|
|
21
|
+
createSource({
|
|
22
|
+
getStream() {
|
|
23
|
+
return checkImages(markup, urlBase);
|
|
24
|
+
},
|
|
25
|
+
mapValue(result) {
|
|
26
|
+
if (result && result.status !== 'success') {
|
|
27
|
+
return {
|
|
28
|
+
result: result,
|
|
29
|
+
source: 'image',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
createSource({
|
|
35
|
+
getStream() {
|
|
36
|
+
return checkLinks(markup);
|
|
37
|
+
},
|
|
38
|
+
mapValue(result) {
|
|
39
|
+
if (result && result.status !== 'success') {
|
|
40
|
+
return {
|
|
41
|
+
result: result,
|
|
42
|
+
source: 'link',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function* loadLintingRowsFrom(sources: LintingSource<unknown>[]) {
|
|
51
|
+
for await (const source of sources) {
|
|
52
|
+
const stream = await source.getStream();
|
|
53
|
+
for await (const value of loadStream(stream)) {
|
|
54
|
+
const row = source.mapValue(value);
|
|
55
|
+
if (row) {
|
|
56
|
+
yield row;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export async function* loadStream<T>(stream: ReadableStream<T>) {
|
|
2
|
+
const reader = stream.getReader();
|
|
3
|
+
try {
|
|
4
|
+
while (true) {
|
|
5
|
+
const { value, done } = await reader.read();
|
|
6
|
+
if (done) {
|
|
7
|
+
break;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
yield value;
|
|
11
|
+
}
|
|
12
|
+
} finally {
|
|
13
|
+
reader.releaseLock();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
type Ok<T, _E> = {
|
|
2
|
+
value: T;
|
|
3
|
+
};
|
|
4
|
+
type Error<_T, E> = {
|
|
5
|
+
error: E;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Do not destructure this object, it is meant to have all fields together
|
|
10
|
+
* in the same object
|
|
11
|
+
*/
|
|
12
|
+
export type Result<T, E> = Ok<T, E> | Error<T, E>;
|
|
13
|
+
|
|
14
|
+
export function isErr<T, E>(result: Result<T, E>): result is Error<T, E> {
|
|
15
|
+
return 'error' in result;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isOk<T, E>(result: Result<T, E>): result is Ok<T, E> {
|
|
19
|
+
return 'value' in result && !('error' in result);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function mapResult<T, E, B>(
|
|
23
|
+
result: Result<T, E>,
|
|
24
|
+
callback: (value: T) => B,
|
|
25
|
+
): Result<B, E> {
|
|
26
|
+
if (isOk(result)) {
|
|
27
|
+
return ok(callback(result.value));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ok<T, E>(value: NoInfer<T>): Ok<T, E>;
|
|
34
|
+
// biome-ignore lint/suspicious/noConfusingVoidType: This is required for void return types on functions that can still error
|
|
35
|
+
export function ok<T extends void = void, E = never>(value: void): Ok<void, E>;
|
|
36
|
+
export function ok<T, E>(value: NoInfer<T>): Ok<T, E> {
|
|
37
|
+
return {
|
|
38
|
+
value,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function err<T, E>(error: NoInfer<E>): Error<T, E>;
|
|
43
|
+
// biome-ignore lint/suspicious/noConfusingVoidType: This is required for void return types on functions that can still error
|
|
44
|
+
export function err<T, E extends void = void>(error: void): Error<T, void>;
|
|
45
|
+
export function err<T, E>(error: NoInfer<E>): Error<T, E> {
|
|
46
|
+
return {
|
|
47
|
+
error,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import vm from 'node:vm';
|
|
3
|
+
import { type Result, err, ok } from './result';
|
|
4
|
+
import { staticNodeModulesForVM } from './static-node-modules-for-vm';
|
|
5
|
+
|
|
6
|
+
export const runBundledCode = (
|
|
7
|
+
code: string,
|
|
8
|
+
filename: string,
|
|
9
|
+
): Result<unknown, unknown> => {
|
|
10
|
+
const fakeContext = {
|
|
11
|
+
...global,
|
|
12
|
+
console,
|
|
13
|
+
Buffer,
|
|
14
|
+
AbortSignal,
|
|
15
|
+
Event,
|
|
16
|
+
EventTarget,
|
|
17
|
+
TextDecoder,
|
|
18
|
+
Request,
|
|
19
|
+
Response,
|
|
20
|
+
TextDecoderStream,
|
|
21
|
+
TextEncoder,
|
|
22
|
+
TextEncoderStream,
|
|
23
|
+
ReadableStream,
|
|
24
|
+
URL,
|
|
25
|
+
URLSearchParams,
|
|
26
|
+
Headers,
|
|
27
|
+
module: {
|
|
28
|
+
exports: {},
|
|
29
|
+
},
|
|
30
|
+
__filename: filename,
|
|
31
|
+
__dirname: path.dirname(filename),
|
|
32
|
+
require: (specifiedModule: string) => {
|
|
33
|
+
let m = specifiedModule;
|
|
34
|
+
if (specifiedModule.startsWith('node:')) {
|
|
35
|
+
m = m.split(':')[1]!;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (m in staticNodeModulesForVM) {
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
40
|
+
return staticNodeModulesForVM[m];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-useless-template-literals
|
|
44
|
+
return require(`${specifiedModule}`) as unknown;
|
|
45
|
+
// this stupid string templating was necessary to not have
|
|
46
|
+
// webpack warnings like:
|
|
47
|
+
//
|
|
48
|
+
// Import trace for requested module:
|
|
49
|
+
// ./src/utils/get-email-component.tsx
|
|
50
|
+
// ./src/app/page.tsx
|
|
51
|
+
// ⚠ ./src/utils/get-email-component.tsx
|
|
52
|
+
// Critical dependency: the request of a dependency is an expression
|
|
53
|
+
},
|
|
54
|
+
process,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
vm.runInNewContext(code, fakeContext, { filename });
|
|
59
|
+
} catch (exception) {
|
|
60
|
+
return err(exception);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return ok(fakeContext.module.exports as unknown);
|
|
64
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
|
|
3
|
+
declare module "tailwindcss/lib/lib/evaluateTailwindFunctions" {
|
|
4
|
+
import type { JitContext } from "tailwindcss/lib/lib/setupContextUtils";
|
|
5
|
+
import type { Root } from "postcss";
|
|
6
|
+
|
|
7
|
+
export default function evaluateTailwindFunctions(
|
|
8
|
+
context: JITContext,
|
|
9
|
+
): (root: Root) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare module "tailwindcss/lib/lib/resolveDefaultsAtRules" {
|
|
13
|
+
import type { JitContext } from "tailwindcss/lib/lib/setupContextUtils";
|
|
14
|
+
import type { Root } from "postcss";
|
|
15
|
+
|
|
16
|
+
export default function expandApplyAtRules(
|
|
17
|
+
context: JITContext,
|
|
18
|
+
): (root: Root) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare module "tailwindcss/lib/lib/partitionApplyAtRules" {
|
|
22
|
+
import type { Root } from "postcss";
|
|
23
|
+
|
|
24
|
+
export default function partitionApplyAtRules(): (root: Root) => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
declare module "tailwindcss/lib/lib/substituteScreenAtRules" {
|
|
28
|
+
import type { Root } from "postcss";
|
|
29
|
+
|
|
30
|
+
export default function substituteScreenAtRules(
|
|
31
|
+
context: JITContext,
|
|
32
|
+
): (root: Root) => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare module "tailwindcss/lib/lib/resolveDefaultsAtRules" {
|
|
36
|
+
import type { JitContext } from "tailwindcss/lib/lib/setupContextUtils";
|
|
37
|
+
import type { Root } from "postcss";
|
|
38
|
+
|
|
39
|
+
export default function expandApplyAtRules(
|
|
40
|
+
context: JITContext,
|
|
41
|
+
): (root: Root) => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
declare module "tailwindcss/lib/lib/expandApplyAtRules" {
|
|
45
|
+
import type { JitContext } from "tailwindcss/lib/lib/setupContextUtils";
|
|
46
|
+
import type { Root } from "postcss";
|
|
47
|
+
|
|
48
|
+
export default function expandApplyAtRules(
|
|
49
|
+
context: JITContext,
|
|
50
|
+
): (root: Root) => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare module "tailwindcss/lib/lib/expandTailwindAtRules" {
|
|
54
|
+
import type { JitContext } from "tailwindcss/lib/lib/setupContextUtils";
|
|
55
|
+
import type { Root } from "postcss";
|
|
56
|
+
|
|
57
|
+
export default async function expandTailwindAtRules(
|
|
58
|
+
context: JITContext,
|
|
59
|
+
): (root: Root) => Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare module "tailwindcss/lib/lib/collapseAdjacentRules" {
|
|
63
|
+
import type { JitContext } from "tailwindcss/lib/lib/setupContextUtils";
|
|
64
|
+
import type { Root } from "postcss";
|
|
65
|
+
|
|
66
|
+
export default async function collapseAdjacentRules(
|
|
67
|
+
context: JITContext,
|
|
68
|
+
): (root: Root) => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
declare module "tailwindcss/lib/lib/collapseDuplicateDeclarations" {
|
|
72
|
+
import type { JitContext } from "tailwindcss/lib/lib/setupContextUtils";
|
|
73
|
+
import type { Root } from "postcss";
|
|
74
|
+
|
|
75
|
+
export default async function collapseDuplicateDeclarations(
|
|
76
|
+
context: JITContext,
|
|
77
|
+
): (root: Root) => void;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
declare module "tailwindcss/lib/lib/generateRules" {
|
|
81
|
+
import type { JitContext } from "tailwindcss/lib/lib/setupContextUtils";
|
|
82
|
+
import type { Rule } from "postcss";
|
|
83
|
+
|
|
84
|
+
export function generateRules(
|
|
85
|
+
classNames: Set<string>,
|
|
86
|
+
context: JITContext,
|
|
87
|
+
): [bigint, Rule][];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// taken from https://github.com/vinicoder/tw-to-css/blob/main/types.d.ts
|
|
91
|
+
// thanks vinicoder!
|
|
92
|
+
declare module "tailwindcss/lib/lib/setupContextUtils" {
|
|
93
|
+
import type { Container, Node } from "postcss";
|
|
94
|
+
import type { Config } from "tailwindcss";
|
|
95
|
+
import type resolveConfig from "tailwindcss/resolveConfig";
|
|
96
|
+
|
|
97
|
+
interface ChangedContent {
|
|
98
|
+
content: string;
|
|
99
|
+
extension?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface Api {
|
|
103
|
+
container: Container;
|
|
104
|
+
separator: string;
|
|
105
|
+
format: (def: string) => void;
|
|
106
|
+
wrap: (rule: Container) => void;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
type VariantPreview = string;
|
|
110
|
+
|
|
111
|
+
type VariantFn = [number, (api: Api) => VariantPreview | undefined];
|
|
112
|
+
|
|
113
|
+
type VariantName = string;
|
|
114
|
+
|
|
115
|
+
export interface JitContext {
|
|
116
|
+
changedContent: ChangedContent[];
|
|
117
|
+
ruleCache: Set<unknown>;
|
|
118
|
+
candidateRuleCache: Map<unknown, unknown>;
|
|
119
|
+
classCache: Map<unknown, unknown>;
|
|
120
|
+
applyClassCache: Map<unknown, unknown>;
|
|
121
|
+
notClassCache: Set<unknown>;
|
|
122
|
+
postCssNodeCache: Map<unknown, unknown>;
|
|
123
|
+
|
|
124
|
+
getClassList: () => string[];
|
|
125
|
+
tailwindConfig: TailwindConfig;
|
|
126
|
+
variantMap: Map<VariantName, VariantFn[]>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function createContext(
|
|
130
|
+
config: ReturnType<typeof resolveConfig>,
|
|
131
|
+
changedContent?: ChangedContent[],
|
|
132
|
+
): JitContext;
|
|
133
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -24,16 +24,22 @@
|
|
|
24
24
|
"declarationMap": false,
|
|
25
25
|
"incremental": false,
|
|
26
26
|
"jsx": "preserve",
|
|
27
|
-
"lib": ["dom", "dom.iterable", "esnext"],
|
|
27
|
+
"lib": ["dom", "dom.iterable", "esnext", "ESNext.AsyncIterable"],
|
|
28
28
|
"noEmit": true,
|
|
29
29
|
"strict": false,
|
|
30
|
-
"target": "
|
|
30
|
+
"target": "ESNext",
|
|
31
31
|
"module": "CommonJS",
|
|
32
32
|
"noUncheckedIndexedAccess": true,
|
|
33
33
|
"resolveJsonModule": true,
|
|
34
34
|
"types": ["vitest/globals"],
|
|
35
35
|
"outDir": "dist"
|
|
36
36
|
},
|
|
37
|
-
"include": [
|
|
37
|
+
"include": [
|
|
38
|
+
"next-env.d.ts",
|
|
39
|
+
"tailwind-internals.d.ts",
|
|
40
|
+
"**/*.ts",
|
|
41
|
+
"**/*.tsx",
|
|
42
|
+
".next/types/**/*.ts"
|
|
43
|
+
],
|
|
38
44
|
"exclude": [".next", "dist", "node_modules"]
|
|
39
45
|
}
|
package/build-preview-server.mjs
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
|
|
4
|
-
const nextBuildProcess = spawn('pnpm', ['next', 'build'], {
|
|
5
|
-
detached: true,
|
|
6
|
-
shell: true,
|
|
7
|
-
stdio: 'inherit',
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
process.on('SIGINT', () => {
|
|
11
|
-
nextBuildProcess.kill('SIGINT');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
nextBuildProcess.on('exit', (code) => {
|
|
15
|
-
if (code !== 0) {
|
|
16
|
-
console.error(`next build failed with exit code ${code}`);
|
|
17
|
-
process.exit(code);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (fs.existsSync('dist/preview')) {
|
|
21
|
-
fs.rmSync('dist/preview', { recursive: true });
|
|
22
|
-
}
|
|
23
|
-
fs.mkdirSync('dist/preview', { recursive: true });
|
|
24
|
-
fs.renameSync('.next', 'dist/preview/.next');
|
|
25
|
-
});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|