react-email 4.0.0-alpha.5 → 4.0.0-alpha.6

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.
Files changed (173) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cli/index.js +1175 -2659
  3. package/dist/cli/index.mjs +16 -14
  4. package/dist/preview/.next/BUILD_ID +1 -1
  5. package/dist/preview/.next/app-build-manifest.json +31 -31
  6. package/dist/preview/.next/app-path-routes-manifest.json +6 -1
  7. package/dist/preview/.next/build-manifest.json +14 -14
  8. package/dist/preview/.next/cache/.rscinfo +1 -1
  9. package/dist/preview/.next/cache/webpack/client-production/0.pack +0 -0
  10. package/dist/preview/.next/cache/webpack/client-production/index.pack +0 -0
  11. package/dist/preview/.next/cache/webpack/edge-server-production/index.pack +0 -0
  12. package/dist/preview/.next/cache/webpack/server-production/0.pack +0 -0
  13. package/dist/preview/.next/cache/webpack/server-production/index.pack +0 -0
  14. package/dist/preview/.next/diagnostics/framework.json +1 -1
  15. package/dist/preview/.next/export-marker.json +6 -1
  16. package/dist/preview/.next/images-manifest.json +57 -1
  17. package/dist/preview/.next/next-minimal-server.js.nft.json +1 -1
  18. package/dist/preview/.next/next-server.js.nft.json +1 -1
  19. package/dist/preview/.next/prerender-manifest.json +41 -1
  20. package/dist/preview/.next/required-server-files.json +310 -1
  21. package/dist/preview/.next/routes-manifest.json +64 -1
  22. package/dist/preview/.next/server/app/_not-found/page.js +1 -1
  23. package/dist/preview/.next/server/app/_not-found/page.js.nft.json +1 -1
  24. package/dist/preview/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  25. package/dist/preview/.next/server/app/favicon.ico/route.js +1 -1
  26. package/dist/preview/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  27. package/dist/preview/.next/server/app/page.js +1 -1
  28. package/dist/preview/.next/server/app/page.js.nft.json +1 -1
  29. package/dist/preview/.next/server/app/page_client-reference-manifest.js +1 -1
  30. package/dist/preview/.next/server/app/preview/[...slug]/page.js +47 -11
  31. package/dist/preview/.next/server/app/preview/[...slug]/page.js.nft.json +1 -1
  32. package/dist/preview/.next/server/app/preview/[...slug]/page_client-reference-manifest.js +1 -1
  33. package/dist/preview/.next/server/chunks/171.js +14 -0
  34. package/dist/preview/.next/server/chunks/446.js +6 -0
  35. package/dist/preview/.next/server/chunks/600.js +8 -0
  36. package/dist/preview/.next/server/chunks/811.js +13 -0
  37. package/dist/preview/.next/server/chunks/833.js +1 -0
  38. package/dist/preview/.next/server/functions-config-manifest.json +4 -1
  39. package/dist/preview/.next/server/middleware-build-manifest.js +1 -1
  40. package/dist/preview/.next/server/pages/500.html +1 -1
  41. package/dist/preview/.next/server/pages/_app.js +1 -1
  42. package/dist/preview/.next/server/pages/_app.js.nft.json +1 -1
  43. package/dist/preview/.next/server/pages/_document.js +1 -1
  44. package/dist/preview/.next/server/pages/_document.js.nft.json +1 -1
  45. package/dist/preview/.next/server/pages/_error.js +1 -1
  46. package/dist/preview/.next/server/pages/_error.js.nft.json +1 -1
  47. package/dist/preview/.next/server/pages-manifest.json +5 -1
  48. package/dist/preview/.next/server/server-reference-manifest.js +1 -1
  49. package/dist/preview/.next/server/server-reference-manifest.json +1 -1
  50. package/dist/preview/.next/server/webpack-runtime.js +1 -1
  51. package/dist/preview/.next/static/chunks/416-56f79fc7e689f06f.js +1 -0
  52. package/dist/preview/.next/static/chunks/683-8bbfd191e5105f01.js +1 -0
  53. package/dist/preview/.next/static/chunks/744-79730358b37b2212.js +1 -0
  54. package/dist/preview/.next/static/chunks/781-5f16c6bc9d9d4cc1.js +1 -0
  55. package/dist/preview/.next/static/chunks/832ad4be-cb988facfb8f955f.js +1 -0
  56. package/dist/preview/.next/static/chunks/87-38e35f08507de015.js +1 -0
  57. package/dist/preview/.next/static/chunks/{afa401a5-a600c227dacf3ab4.js → afa401a5-3e949a1cfd317dd3.js} +3 -3
  58. package/dist/preview/.next/static/chunks/app/_not-found/page-09d694081cc9d4dc.js +1 -0
  59. package/dist/preview/.next/static/chunks/app/layout-a6640e62690d8fd6.js +1 -0
  60. package/dist/preview/.next/static/chunks/app/page-ba68f50b287e7478.js +1 -0
  61. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-4a5b026ab543e27f.js +1 -0
  62. package/dist/preview/.next/static/chunks/framework-c2bd6d936e3077bc.js +1 -0
  63. package/dist/preview/.next/static/chunks/main-44463a8301435b64.js +1 -0
  64. package/dist/preview/.next/static/chunks/main-app-c2e686acf8d370d7.js +1 -0
  65. package/dist/preview/.next/static/chunks/pages/_app-f3011d3f00bb8dba.js +1 -0
  66. package/dist/preview/.next/static/chunks/pages/_error-39a87dee2e97a2a3.js +1 -0
  67. package/dist/preview/.next/static/chunks/{webpack-2eb145a20ee6cb77.js → webpack-41e2667c9f086a4f.js} +1 -1
  68. package/dist/preview/.next/static/css/d7df9cfc3e182163.css +3 -0
  69. package/dist/preview/.next/static/gFk9UfWL8joM4iD7-wlKF/_buildManifest.js +1 -0
  70. package/dist/preview/.next/trace +26 -22
  71. package/dist/preview/.next/types/cache-life.d.ts +3 -3
  72. package/package.json +14 -9
  73. package/scripts/build-preview-server.mjs +32 -0
  74. package/scripts/fill-caniemail-data.mjs +36 -0
  75. package/src/actions/email-validation/caniemail-data.ts +85993 -0
  76. package/src/actions/email-validation/check-compatibility.ts +322 -0
  77. package/src/actions/email-validation/check-images.spec.tsx +2 -2
  78. package/src/actions/email-validation/check-images.ts +2 -2
  79. package/src/actions/email-validation/check-links.spec.tsx +4 -4
  80. package/src/actions/email-validation/check-links.ts +2 -2
  81. package/src/actions/get-email-path-from-slug.ts +1 -1
  82. package/src/actions/render-email-by-path.tsx +2 -1
  83. package/src/{utils/emails-directory-absolute-path.ts → app/env.ts} +2 -0
  84. package/src/app/layout.tsx +1 -1
  85. package/src/app/page.tsx +1 -1
  86. package/src/app/preview/[...slug]/page.tsx +73 -16
  87. package/src/app/preview/[...slug]/preview.tsx +11 -57
  88. package/src/components/code.tsx +0 -1
  89. package/src/components/toolbar/linter.tsx +267 -124
  90. package/src/components/toolbar/spam-assassin.tsx +20 -31
  91. package/src/components/toolbar/toolbar-button.tsx +50 -0
  92. package/src/components/toolbar/use-cached-state.ts +33 -0
  93. package/src/components/toolbar.tsx +106 -98
  94. package/src/components/topbar/view-size-controls.tsx +1 -0
  95. package/src/components/topbar.tsx +3 -9
  96. package/src/contexts/emails.tsx +2 -1
  97. package/src/contexts/preview.tsx +81 -0
  98. package/src/hooks/use-email-rendering-result.ts +2 -1
  99. package/src/utils/__snapshots__/get-email-component.spec.ts.snap +1 -1
  100. package/src/utils/caniemail/all-css-properties.ts +358 -0
  101. package/src/utils/caniemail/ast/get-object-variables.ts +61 -0
  102. package/src/utils/caniemail/ast/get-used-style-properties.ts +91 -0
  103. package/src/utils/caniemail/get-compatibility-stats-for-entry.ts +118 -0
  104. package/src/utils/caniemail/get-css-functions.ts +25 -0
  105. package/src/utils/caniemail/get-css-property-names.ts +32 -0
  106. package/src/utils/caniemail/get-css-property-with-value.ts +14 -0
  107. package/src/utils/caniemail/get-css-unit.ts +3 -0
  108. package/src/utils/caniemail/get-element-attributes.ts +7 -0
  109. package/src/utils/caniemail/get-element-names.ts +20 -0
  110. package/src/utils/caniemail/tailwind/generate-tailwind-rules.ts +30 -0
  111. package/src/utils/caniemail/tailwind/get-tailwind-config.ts +205 -0
  112. package/src/utils/caniemail/tailwind/get-tailwind-metadata.spec.ts +25 -0
  113. package/src/utils/caniemail/tailwind/get-tailwind-metadata.ts +45 -0
  114. package/src/utils/caniemail/tailwind/setup-tailwind-context.ts +15 -0
  115. package/src/utils/get-email-component.ts +34 -67
  116. package/src/utils/linting.ts +85 -0
  117. package/src/utils/result.ts +49 -0
  118. package/src/utils/run-bundled-code.ts +64 -0
  119. package/tailwind-internals.d.ts +133 -0
  120. package/tsconfig.json +9 -3
  121. package/build-preview-server.mjs +0 -25
  122. package/dist/preview/.next/cache/images/TcyzHbFXGFjrOu3wEMvDoSmqCh3qP3iiNqJf0QbED9Y/60.1741728556140.cQ5qicbpvoXZ7leVmWqG2ElLwXB1ynYeSv8MBSA-QeM.Vy8iMWM3MGUtMTk1ODcxYmIyNzMi.webp +0 -0
  123. package/dist/preview/.next/cache/webpack/client-development/0.pack.gz +0 -0
  124. package/dist/preview/.next/cache/webpack/client-development/1.pack.gz +0 -0
  125. package/dist/preview/.next/cache/webpack/client-development/10.pack.gz +0 -0
  126. package/dist/preview/.next/cache/webpack/client-development/11.pack.gz +0 -0
  127. package/dist/preview/.next/cache/webpack/client-development/12.pack.gz +0 -0
  128. package/dist/preview/.next/cache/webpack/client-development/13.pack.gz +0 -0
  129. package/dist/preview/.next/cache/webpack/client-development/2.pack.gz +0 -0
  130. package/dist/preview/.next/cache/webpack/client-development/3.pack.gz +0 -0
  131. package/dist/preview/.next/cache/webpack/client-development/4.pack.gz +0 -0
  132. package/dist/preview/.next/cache/webpack/client-development/5.pack.gz +0 -0
  133. package/dist/preview/.next/cache/webpack/client-development/6.pack.gz +0 -0
  134. package/dist/preview/.next/cache/webpack/client-development/7.pack.gz +0 -0
  135. package/dist/preview/.next/cache/webpack/client-development/8.pack.gz +0 -0
  136. package/dist/preview/.next/cache/webpack/client-development/9.pack.gz +0 -0
  137. package/dist/preview/.next/cache/webpack/client-development/index.pack.gz +0 -0
  138. package/dist/preview/.next/cache/webpack/client-development/index.pack.gz.old +0 -0
  139. package/dist/preview/.next/cache/webpack/server-development/0.pack.gz +0 -0
  140. package/dist/preview/.next/cache/webpack/server-development/1.pack.gz +0 -0
  141. package/dist/preview/.next/cache/webpack/server-development/2.pack.gz +0 -0
  142. package/dist/preview/.next/cache/webpack/server-development/3.pack.gz +0 -0
  143. package/dist/preview/.next/cache/webpack/server-development/4.pack.gz +0 -0
  144. package/dist/preview/.next/cache/webpack/server-development/5.pack.gz +0 -0
  145. package/dist/preview/.next/cache/webpack/server-development/6.pack.gz +0 -0
  146. package/dist/preview/.next/cache/webpack/server-development/7.pack.gz +0 -0
  147. package/dist/preview/.next/cache/webpack/server-development/8.pack.gz +0 -0
  148. package/dist/preview/.next/cache/webpack/server-development/9.pack.gz +0 -0
  149. package/dist/preview/.next/cache/webpack/server-development/index.pack.gz +0 -0
  150. package/dist/preview/.next/cache/webpack/server-development/index.pack.gz.old +0 -0
  151. package/dist/preview/.next/server/chunks/143.js +0 -6
  152. package/dist/preview/.next/server/chunks/409.js +0 -5
  153. package/dist/preview/.next/server/chunks/46.js +0 -1
  154. package/dist/preview/.next/server/chunks/478.js +0 -14
  155. package/dist/preview/.next/server/chunks/707.js +0 -13
  156. package/dist/preview/.next/static/B4EYZiVzdylEG9lAIl-aO/_buildManifest.js +0 -1
  157. package/dist/preview/.next/static/chunks/575-bc52750855c25df4.js +0 -2
  158. package/dist/preview/.next/static/chunks/684-0f1ef7361c499798.js +0 -1
  159. package/dist/preview/.next/static/chunks/684c6b30-0c65da32762fc4ee.js +0 -1
  160. package/dist/preview/.next/static/chunks/81-e7539b08d9d3fb4d.js +0 -1
  161. package/dist/preview/.next/static/chunks/883-70c8267c50bc4133.js +0 -1
  162. package/dist/preview/.next/static/chunks/921-d1dc8c63f49e85d6.js +0 -1
  163. package/dist/preview/.next/static/chunks/app/_not-found/page-03ce767859c36d4e.js +0 -1
  164. package/dist/preview/.next/static/chunks/app/layout-7cf14e28880544f1.js +0 -1
  165. package/dist/preview/.next/static/chunks/app/page-065cb49b0a078541.js +0 -1
  166. package/dist/preview/.next/static/chunks/app/preview/[...slug]/page-656510fd180c803c.js +0 -1
  167. package/dist/preview/.next/static/chunks/framework-2a724981073c3a29.js +0 -1
  168. package/dist/preview/.next/static/chunks/main-552b9719bbc3a274.js +0 -1
  169. package/dist/preview/.next/static/chunks/main-app-914a73336fd45af5.js +0 -1
  170. package/dist/preview/.next/static/chunks/pages/_app-77ca34bce25ac75c.js +0 -1
  171. package/dist/preview/.next/static/chunks/pages/_error-73f611c46abbb495.js +0 -1
  172. package/dist/preview/.next/static/css/2df96d9ee014e8de.css +0 -3
  173. /package/dist/preview/.next/static/{B4EYZiVzdylEG9lAIl-aO → gFk9UfWL8joM4iD7-wlKF}/_ssgManifest.js +0 -0
@@ -0,0 +1,358 @@
1
+ // taken from https://www.w3schools.com/cssref/index.php
2
+ export const allCssProperties = [
3
+ 'accent-color',
4
+ 'align-content',
5
+ 'align-items',
6
+ 'align-self',
7
+ 'all',
8
+ 'animation',
9
+ 'animation-delay',
10
+ 'animation-direction',
11
+ 'animation-duration',
12
+ 'animation-fill-mode',
13
+ 'animation-iteration-count',
14
+ 'animation-name',
15
+ 'animation-play-state',
16
+ 'animation-timing-function',
17
+ 'aspect-ratio',
18
+ 'backdrop-filter',
19
+ 'backface-visibility',
20
+ 'background',
21
+ 'background-attachment',
22
+ 'background-blend-mode',
23
+ 'background-clip',
24
+ 'background-color',
25
+ 'background-image',
26
+ 'background-origin',
27
+ 'background-position',
28
+ 'background-position-x',
29
+ 'background-position-y',
30
+ 'background-repeat',
31
+ 'background-size',
32
+ 'block-size',
33
+ 'border',
34
+ 'border-block',
35
+ 'border-block-color',
36
+ 'border-block-end',
37
+ 'border-block-end-color',
38
+ 'border-block-end-style',
39
+ 'border-block-end-width',
40
+ 'border-block-start',
41
+ 'border-block-start-color',
42
+ 'border-block-start-style',
43
+ 'border-block-start-width',
44
+ 'border-block-style',
45
+ 'border-block-width',
46
+ 'border-bottom',
47
+ 'border-bottom-color',
48
+ 'border-bottom-left-radius',
49
+ 'border-bottom-right-radius',
50
+ 'border-bottom-style',
51
+ 'border-bottom-width',
52
+ 'border-collapse',
53
+ 'border-color',
54
+ 'border-end-end-radius',
55
+ 'border-end-start-radius',
56
+ 'border-image',
57
+ 'border-image-outset',
58
+ 'border-image-repeat',
59
+ 'border-image-slice',
60
+ 'border-image-source',
61
+ 'border-image-width',
62
+ 'border-inline',
63
+ 'border-inline-color',
64
+ 'border-inline-end',
65
+ 'border-inline-end-color',
66
+ 'border-inline-end-style',
67
+ 'border-inline-end-width',
68
+ 'border-inline-start',
69
+ 'border-inline-start-color',
70
+ 'border-inline-start-style',
71
+ 'border-inline-start-width',
72
+ 'border-inline-style',
73
+ 'border-inline-width',
74
+ 'border-left',
75
+ 'border-left-color',
76
+ 'border-left-style',
77
+ 'border-left-width',
78
+ 'border-radius',
79
+ 'border-right',
80
+ 'border-right-color',
81
+ 'border-right-style',
82
+ 'border-right-width',
83
+ 'border-spacing',
84
+ 'border-start-end-radius',
85
+ 'border-start-start-radius',
86
+ 'border-style',
87
+ 'border-top',
88
+ 'border-top-color',
89
+ 'border-top-left-radius',
90
+ 'border-top-right-radius',
91
+ 'border-top-style',
92
+ 'border-top-width',
93
+ 'border-width',
94
+ 'bottom',
95
+ 'box-decoration-break',
96
+ 'box-reflect',
97
+ 'box-shadow',
98
+ 'box-sizing',
99
+ 'break-after',
100
+ 'break-before',
101
+ 'break-inside',
102
+ 'caption-side',
103
+ 'caret-color',
104
+ '@charset',
105
+ 'clear',
106
+ 'clip',
107
+ 'clip-path',
108
+ 'color',
109
+ 'column-count',
110
+ 'column-fill',
111
+ 'column-gap',
112
+ 'column-rule',
113
+ 'column-rule-color',
114
+ 'column-rule-style',
115
+ 'column-rule-width',
116
+ 'column-span',
117
+ 'column-width',
118
+ 'columns',
119
+ 'content',
120
+ 'counter-increment',
121
+ 'counter-reset',
122
+ 'counter-set',
123
+ 'cursor',
124
+ 'direction',
125
+ 'display',
126
+ 'empty-cells',
127
+ 'filter',
128
+ 'flex',
129
+ 'flex-basis',
130
+ 'flex-direction',
131
+ 'flex-flow',
132
+ 'flex-grow',
133
+ 'flex-shrink',
134
+ 'flex-wrap',
135
+ 'float',
136
+ 'font',
137
+ '@font-face',
138
+ 'font-family',
139
+ 'font-feature-settings',
140
+ '@font-feature-values',
141
+ 'font-kerning',
142
+ 'font-language-override',
143
+ 'font-size',
144
+ 'font-size-adjust',
145
+ 'font-stretch',
146
+ 'font-style',
147
+ 'font-synthesis',
148
+ 'font-variant',
149
+ 'font-variant-alternates',
150
+ 'font-variant-caps',
151
+ 'font-variant-east-asian',
152
+ 'font-variant-ligatures',
153
+ 'font-variant-numeric',
154
+ 'font-variant-position',
155
+ 'font-weight',
156
+ 'gap',
157
+ 'grid',
158
+ 'grid-area',
159
+ 'grid-auto-columns',
160
+ 'grid-auto-flow',
161
+ 'grid-auto-rows',
162
+ 'grid-column',
163
+ 'grid-column-end',
164
+ 'grid-column-gap',
165
+ 'grid-column-start',
166
+ 'grid-gap',
167
+ 'grid-row',
168
+ 'grid-row-end',
169
+ 'grid-row-gap',
170
+ 'grid-row-start',
171
+ 'grid-template',
172
+ 'grid-template-areas',
173
+ 'grid-template-columns',
174
+ 'grid-template-rows',
175
+ 'hanging-punctuation',
176
+ 'height',
177
+ 'hyphens',
178
+ 'hypenate-character',
179
+ 'image-rendering',
180
+ '@import',
181
+ 'inline-size',
182
+ 'inset',
183
+ 'inset-block',
184
+ 'inset-block-end',
185
+ 'inset-block-start',
186
+ 'inset-inline',
187
+ 'inset-inline-end',
188
+ 'inset-inline-start',
189
+ 'isolation',
190
+ 'justify-content',
191
+ 'justify-items',
192
+ 'justify-self',
193
+ '@keyframes',
194
+ 'left',
195
+ 'letter-spacing',
196
+ 'line-break',
197
+ 'line-height',
198
+ 'list-style',
199
+ 'list-style-image',
200
+ 'list-style-position',
201
+ 'list-style-type',
202
+ 'margin',
203
+ 'shape-margin',
204
+ 'margin-block',
205
+ 'margin-block-end',
206
+ 'margin-block-start',
207
+ 'margin-bottom',
208
+ 'margin-inline',
209
+ 'margin-inline-end',
210
+ 'margin-inline-start',
211
+ 'margin-left',
212
+ 'margin-right',
213
+ 'margin-top',
214
+ 'mask',
215
+ 'mask-clip',
216
+ 'mask-composite',
217
+ 'mask-image',
218
+ 'mask-mode',
219
+ 'mask-origin',
220
+ 'mask-position',
221
+ 'mask-repeat',
222
+ 'mask-size',
223
+ 'mask-type',
224
+ 'max-height',
225
+ 'max-width',
226
+ '@media',
227
+ 'max-block-size',
228
+ 'max-inline-size',
229
+ 'min-block-size',
230
+ 'min-inline-size',
231
+ 'min-height',
232
+ 'min-width',
233
+ 'mix-blend-mode',
234
+ 'object-fit',
235
+ 'object-position',
236
+ 'offset',
237
+ 'offset-anchor',
238
+ 'offset-distance',
239
+ 'offset-path',
240
+ 'offset-rotate',
241
+ 'opacity',
242
+ 'order',
243
+ 'orphans',
244
+ 'outline',
245
+ 'outline-color',
246
+ 'outline-offset',
247
+ 'outline-style',
248
+ 'outline-width',
249
+ 'overflow',
250
+ 'overflow-anchor',
251
+ 'overflow-wrap',
252
+ 'overflow-x',
253
+ 'overflow-y',
254
+ 'overscroll-behavior',
255
+ 'overscroll-behavior-block',
256
+ 'overscroll-behavior-inline',
257
+ 'overscroll-behavior-x',
258
+ 'overscroll-behavior-y',
259
+ 'padding',
260
+ 'padding-block',
261
+ 'padding-block-end',
262
+ 'padding-block-start',
263
+ 'padding-bottom',
264
+ 'padding-inline',
265
+ 'padding-inline-end',
266
+ 'padding-inline-start',
267
+ 'padding-left',
268
+ 'padding-right',
269
+ 'padding-top',
270
+ 'page-break-after',
271
+ 'page-break-before',
272
+ 'page-break-inside',
273
+ 'paint-order',
274
+ 'perspective',
275
+ 'perspective-origin',
276
+ 'place-content',
277
+ 'place-items',
278
+ 'place-self',
279
+ 'pointer-events',
280
+ 'position',
281
+ 'quotes',
282
+ 'resize',
283
+ 'right',
284
+ 'rotate',
285
+ 'row-gap',
286
+ 'scale',
287
+ 'scroll-behavior',
288
+ 'scroll-margin',
289
+ 'scroll-margin-block',
290
+ 'scroll-margin-block-end',
291
+ 'scroll-margin-block-start',
292
+ 'scroll-margin-bottom',
293
+ 'scroll-margin-inline',
294
+ 'scroll-margin-inline-end',
295
+ 'scroll-margin-inline-start',
296
+ 'scroll-margin-left',
297
+ 'scroll-margin-right',
298
+ 'scroll-margin-top',
299
+ 'scroll-padding',
300
+ 'scroll-padding-block',
301
+ 'scroll-padding-block-end',
302
+ 'scroll-padding-block-start',
303
+ 'scroll-padding-bottom',
304
+ 'scroll-padding-inline',
305
+ 'scroll-padding-inline-end',
306
+ 'scroll-padding-inline-start',
307
+ 'scroll-padding-left',
308
+ 'scroll-padding-right',
309
+ 'scroll-padding-top',
310
+ 'scroll-snap-align',
311
+ 'scroll-snap-stop',
312
+ 'scroll-snap-type',
313
+ 'scrollbar-color',
314
+ 'tab-size',
315
+ 'table-layout',
316
+ 'text-align',
317
+ 'text-align-last',
318
+ 'text-combine-upright',
319
+ 'text-decoration',
320
+ 'text-decoration-color',
321
+ 'text-decoration-line',
322
+ 'text-decoration-style',
323
+ 'text-decoration-thickness',
324
+ 'text-emphasis',
325
+ 'text-emphasis-color',
326
+ 'text-emphasis-position',
327
+ 'text-emphasis-style',
328
+ 'text-indent',
329
+ 'text-justify',
330
+ 'text-orientation',
331
+ 'text-overflow',
332
+ 'text-shadow',
333
+ 'text-transform',
334
+ 'text-underline-offset',
335
+ 'text-underline-position',
336
+ 'top',
337
+ 'transform',
338
+ 'transform-origin',
339
+ 'transform-style',
340
+ 'transition',
341
+ 'transition-delay',
342
+ 'transition-duration',
343
+ 'transition-property',
344
+ 'transition-timing-function',
345
+ 'translate',
346
+ 'unicode-bidi',
347
+ 'user-select',
348
+ 'vertical-align',
349
+ 'visibility',
350
+ 'white-space',
351
+ 'widows',
352
+ 'width',
353
+ 'word-break',
354
+ 'word-spacing',
355
+ 'word-wrap',
356
+ 'writing-mode',
357
+ 'z-index',
358
+ ];
@@ -0,0 +1,61 @@
1
+ import type { Node } from '@babel/traverse';
2
+ import traverse from '@babel/traverse';
3
+ import type { AST } from '../../../actions/email-validation/check-compatibility';
4
+
5
+ export interface Position {
6
+ line: number;
7
+ column: number;
8
+ index: number;
9
+ }
10
+
11
+ export const convertLocationIntoObject = (
12
+ location: SourceLocation,
13
+ ): SourceLocation => {
14
+ return {
15
+ start: {
16
+ line: location.start.line,
17
+ column: location.start.column,
18
+ index: location.start.index,
19
+ },
20
+ end: {
21
+ line: location.end.line,
22
+ column: location.end.column,
23
+ index: location.end.index,
24
+ },
25
+ filename: location.filename,
26
+ identifierName: location.identifierName,
27
+ };
28
+ };
29
+
30
+ export interface SourceLocation {
31
+ start: Position;
32
+ end: Position;
33
+ filename: string;
34
+ identifierName: string | undefined | null;
35
+ }
36
+
37
+ type ObjectProperty = Node & { type: 'ObjectProperty' };
38
+
39
+ export type ObjectVariables = Record<string, ObjectProperty[]>;
40
+
41
+ export const getObjectVariables = (ast: AST) => {
42
+ const objectVariables: ObjectVariables = {};
43
+ traverse(ast, {
44
+ ObjectExpression(nodePath) {
45
+ if (nodePath.parent.type === 'VariableDeclarator') {
46
+ if (nodePath.parent.id.type === 'Identifier') {
47
+ const variableName = nodePath.parent.id.name;
48
+ const properties: ObjectProperty[] = [];
49
+ for (const property of nodePath.node.properties) {
50
+ if (property.type === 'ObjectProperty') {
51
+ properties.push(property);
52
+ }
53
+ }
54
+ objectVariables[variableName] = properties;
55
+ }
56
+ }
57
+ },
58
+ });
59
+
60
+ return objectVariables;
61
+ };
@@ -0,0 +1,91 @@
1
+ import traverse from '@babel/traverse';
2
+ import type { AST } from '../../../actions/email-validation/check-compatibility';
3
+ import { generateTailwindCssRules } from '../tailwind/generate-tailwind-rules';
4
+ import { getTailwindMetadata } from '../tailwind/get-tailwind-metadata';
5
+ import type { ObjectVariables, SourceLocation } from './get-object-variables';
6
+
7
+ export interface StylePropertyUsage {
8
+ location: SourceLocation | undefined | null;
9
+ name: string;
10
+ value: string;
11
+ }
12
+
13
+ export const doesPropertyHaveLocation = (
14
+ prop: StylePropertyUsage,
15
+ ): prop is StylePropertyUsage & { location: SourceLocation } => {
16
+ return prop.location !== undefined && prop.location !== null;
17
+ };
18
+
19
+ export const getUsedStyleProperties = async (
20
+ ast: AST,
21
+ sourceCode: string,
22
+ sourcePath: string,
23
+ objectVariables: ObjectVariables,
24
+ ) => {
25
+ const styleProperties: StylePropertyUsage[] = [];
26
+ const tailwindMetadata = await getTailwindMetadata(
27
+ ast,
28
+ sourceCode,
29
+ sourcePath,
30
+ );
31
+
32
+ if (tailwindMetadata.hasTailwind) {
33
+ traverse(ast, {
34
+ JSXAttribute(path) {
35
+ if (path.node.name.name === 'className') {
36
+ path.traverse({
37
+ StringLiteral(stringPath) {
38
+ const className = stringPath.node.value;
39
+ const { rules } = generateTailwindCssRules(
40
+ className.split(' '),
41
+ tailwindMetadata.context,
42
+ );
43
+ for (const rule of rules) {
44
+ rule.walkDecls((decl) => {
45
+ styleProperties.push({
46
+ location: stringPath.node.loc,
47
+ name: decl.prop,
48
+ value: decl.value,
49
+ });
50
+ });
51
+ }
52
+ },
53
+ });
54
+ }
55
+ },
56
+ });
57
+ }
58
+
59
+ traverse(ast, {
60
+ JSXAttribute(path) {
61
+ if (
62
+ path.node.value?.type === 'JSXExpressionContainer' &&
63
+ path.node.value.expression.type === 'Identifier' &&
64
+ path.node.name.name === 'style'
65
+ ) {
66
+ const styleVariable = objectVariables[path.node.value.expression.name];
67
+ if (styleVariable) {
68
+ for (const property of styleVariable) {
69
+ if (
70
+ (property.key.type === 'StringLiteral' ||
71
+ property.key.type === 'Identifier') &&
72
+ property.value.type === 'StringLiteral'
73
+ ) {
74
+ const propertyName =
75
+ property.key.type === 'StringLiteral'
76
+ ? property.key.value
77
+ : property.key.name;
78
+ styleProperties.push({
79
+ name: propertyName,
80
+ value: property.value.value,
81
+ location: property.loc,
82
+ });
83
+ }
84
+ }
85
+ }
86
+ }
87
+ },
88
+ });
89
+
90
+ return styleProperties;
91
+ };
@@ -0,0 +1,118 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+ import type {
3
+ EmailClient,
4
+ Platform,
5
+ SupportEntry,
6
+ } from '../../actions/email-validation/check-compatibility';
7
+
8
+ export type SupportStatus = DetailedSupportStatus['status'];
9
+
10
+ export type DetailedSupportStatus =
11
+ | {
12
+ status: 'success';
13
+ }
14
+ | {
15
+ status: 'error';
16
+ }
17
+ | {
18
+ status: 'warning';
19
+ notes: string;
20
+ };
21
+
22
+ type EmailClientStats = {
23
+ status: SupportStatus;
24
+ perPlatform: Partial<Record<Platform, DetailedSupportStatus>>;
25
+ };
26
+
27
+ export type CompatibilityStats = {
28
+ status: SupportStatus;
29
+ perEmailClient: Partial<Record<EmailClient, EmailClientStats>>;
30
+ };
31
+
32
+ const noteNumbersRegex = /#(?<noteNumber>\d+)/g;
33
+
34
+ export const getCompatibilityStatsForEntry = (
35
+ entry: SupportEntry,
36
+ emailClients: EmailClient[],
37
+ ) => {
38
+ const stats: CompatibilityStats = {
39
+ status: 'success',
40
+ perEmailClient: {},
41
+ };
42
+ for (const emailClient of emailClients) {
43
+ const rawStats = entry.stats[emailClient];
44
+ if (rawStats) {
45
+ const emailClientStats: EmailClientStats = {
46
+ status: 'success',
47
+ perPlatform: {},
48
+ };
49
+
50
+ for (const [platform, statusPerVersion] of Object.entries(rawStats)) {
51
+ const latestStatus = statusPerVersion[statusPerVersion.length - 1];
52
+ if (latestStatus === undefined)
53
+ throw new Error(
54
+ 'Cannot load in status because there are none recorded for this platform/email client',
55
+ {
56
+ cause: {
57
+ latestStatus,
58
+ statusPerVersion,
59
+ platform,
60
+ emailClient,
61
+ supportEntry: entry,
62
+ },
63
+ },
64
+ );
65
+ const statusString = latestStatus[Object.keys(latestStatus)[0]!]!;
66
+ if (statusString.startsWith('u')) continue;
67
+ if (statusString.startsWith('a')) {
68
+ const notes: string[] = [];
69
+ noteNumbersRegex.lastIndex = 0;
70
+ for (const match of statusString.matchAll(noteNumbersRegex)) {
71
+ if (match.groups?.noteNumber) {
72
+ const { noteNumber } = match.groups;
73
+ const note = entry.notes_by_num?.[Number.parseInt(noteNumber)];
74
+ if (note) {
75
+ notes.push(note);
76
+ }
77
+ // else if (isInternalDev) {
78
+ // console.warn(
79
+ // 'Could not get note by the number for a support entry',
80
+ // {
81
+ // platform,
82
+ // statusString,
83
+ // note,
84
+ // },
85
+ // );
86
+ // }
87
+ }
88
+ }
89
+ if (emailClientStats.status === 'success')
90
+ emailClientStats.status = 'warning';
91
+ if (stats.status === 'success') stats.status = 'warning';
92
+ emailClientStats.perPlatform[platform as Platform] = {
93
+ status: 'warning',
94
+ notes:
95
+ notes.length === 1
96
+ ? notes[0]!
97
+ : notes.map((note) => `- ${note}`).join('\n'),
98
+ };
99
+ } else if (statusString.startsWith('y')) {
100
+ emailClientStats.perPlatform[platform as Platform] = {
101
+ status: 'success',
102
+ };
103
+ } else if (statusString.startsWith('n')) {
104
+ if (emailClientStats.status !== 'error')
105
+ emailClientStats.status = 'error';
106
+ if (stats.status !== 'error') stats.status = 'error';
107
+ emailClientStats.perPlatform[platform as Platform] = {
108
+ status: 'error',
109
+ };
110
+ }
111
+ }
112
+
113
+ stats.perEmailClient[emailClient] = emailClientStats;
114
+ }
115
+ }
116
+
117
+ return stats;
118
+ };
@@ -0,0 +1,25 @@
1
+ export function getCssFunctions(title: string) {
2
+ if (/^[a-zA-Z]\(\)$/.test(title.trim())) {
3
+ return [title.replace('()', '')];
4
+ }
5
+
6
+ // ex: lch(), oklch(), lab(), oklab()
7
+ // this regex avoids matching entries that are for CSS properties listed
8
+ // separated by commas as well
9
+ if (/^(?:[^(),]+?\(\),?)*$/.test(title.trim())) {
10
+ return title
11
+ .split(/\s*,\s*/)
12
+ .map((functionCallWithoutParameters) =>
13
+ functionCallWithoutParameters.replace('()', ''),
14
+ );
15
+ }
16
+
17
+ // ex: CSS calc() function
18
+ if (/^CSS [a-z]+\(\) function$/.test(title.trim())) {
19
+ return [
20
+ title.replace('CSS ', '').replace(' function', '').replace('()', ''),
21
+ ];
22
+ }
23
+
24
+ return [];
25
+ }
@@ -0,0 +1,32 @@
1
+ import { allCssProperties } from './all-css-properties';
2
+
3
+ export const getCssPropertyNames = (title: string, keywords: string | null) => {
4
+ if (allCssProperties.includes(title.replace(' property', '')))
5
+ return [title.replace(' property', '')];
6
+
7
+ if (title.split('&').length > 1) {
8
+ return title
9
+ .split(/\s*&\s*/)
10
+ .map((piece) => piece.trim())
11
+ .filter((possiblePropertyName) =>
12
+ allCssProperties.includes(possiblePropertyName),
13
+ );
14
+ }
15
+
16
+ if (title.split(',').length > 1) {
17
+ return title
18
+ .split(/\s*,\s*/)
19
+ .map((piece) => piece.trim())
20
+ .filter((possiblePropertyName) =>
21
+ allCssProperties.includes(possiblePropertyName),
22
+ );
23
+ }
24
+
25
+ if (keywords) {
26
+ return keywords
27
+ .split(/\s*,\s*/)
28
+ .filter((keyword) => allCssProperties.includes(keyword));
29
+ }
30
+
31
+ return [];
32
+ };
@@ -0,0 +1,14 @@
1
+ const propertyRegex =
2
+ /(?<propertyName>[a-z-]+)\s*:\s*(?<propertyValue>[a-zA-Z\-0-9()+*/_ ]+)/;
3
+
4
+ export const getCssPropertyWithValue = (title: string) => {
5
+ const match = propertyRegex.exec(title.trim());
6
+ if (match) {
7
+ const [_full, propertyName, propertyValue] = match;
8
+ return {
9
+ name: propertyName,
10
+ value: propertyValue,
11
+ };
12
+ }
13
+ return undefined;
14
+ };
@@ -0,0 +1,3 @@
1
+ export const getCssUnit = (title: string) => {
2
+ return title.endsWith(' unit') ? title.replace(' unit', '') : undefined;
3
+ };