@lupinum/nuxt-email 0.1.1 → 1.0.0-beta.2

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 (55) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +63 -18
  3. package/dist/build.d.mts +13 -0
  4. package/dist/build.mjs +218 -0
  5. package/dist/module.d.mts +1 -1
  6. package/dist/module.json +1 -1
  7. package/dist/module.mjs +54 -145
  8. package/dist/runtime/components/EHtml.d.ts +3 -1
  9. package/dist/runtime/components/EHtml.js +12 -2
  10. package/dist/runtime/components/ELink.js +1 -6
  11. package/dist/runtime/dev-preview/render.get.d.ts +26 -1
  12. package/dist/runtime/dev-preview/render.get.js +2 -2
  13. package/dist/runtime/render/define-email.d.ts +19 -10
  14. package/dist/runtime/render/define-email.js +21 -4
  15. package/dist/runtime/render/errors.d.ts +2 -2
  16. package/dist/runtime/render/errors.js +4 -4
  17. package/dist/runtime/render/index.d.ts +6 -0
  18. package/dist/runtime/render/index.js +3 -0
  19. package/dist/runtime/render/render-email-component.d.ts +3 -6
  20. package/dist/runtime/render/render-email-component.js +16 -16
  21. package/dist/runtime/render/types.d.ts +7 -0
  22. package/dist/runtime/tailwind/css-tree.d.ts +10 -0
  23. package/dist/runtime/tailwind/css-tree.js +11 -0
  24. package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.d.ts +8 -38
  25. package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.js +186 -72
  26. package/dist/runtime/tailwind/engine/css/extract-rules-per-class.d.ts +3 -1
  27. package/dist/runtime/tailwind/engine/css/extract-rules-per-class.js +9 -2
  28. package/dist/runtime/tailwind/engine/css/get-custom-properties.d.ts +1 -1
  29. package/dist/runtime/tailwind/engine/css/get-custom-properties.js +1 -1
  30. package/dist/runtime/tailwind/engine/css/is-part-inlinable.d.ts +1 -1
  31. package/dist/runtime/tailwind/engine/css/is-part-inlinable.js +1 -1
  32. package/dist/runtime/tailwind/engine/css/is-rule-inlinable.d.ts +1 -1
  33. package/dist/runtime/tailwind/engine/css/is-rule-inlinable.js +1 -1
  34. package/dist/runtime/tailwind/engine/css/make-inline-styles-for.d.ts +1 -1
  35. package/dist/runtime/tailwind/engine/css/make-inline-styles-for.js +1 -1
  36. package/dist/runtime/tailwind/engine/css/resolve-all-css-variables.d.ts +1 -1
  37. package/dist/runtime/tailwind/engine/css/resolve-all-css-variables.js +1 -1
  38. package/dist/runtime/tailwind/engine/css/resolve-calc-expressions.d.ts +1 -1
  39. package/dist/runtime/tailwind/engine/css/resolve-calc-expressions.js +1 -1
  40. package/dist/runtime/tailwind/engine/css/sanitize-declarations.d.ts +1 -1
  41. package/dist/runtime/tailwind/engine/css/sanitize-declarations.js +1 -1
  42. package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.d.ts +1 -1
  43. package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.js +1 -1
  44. package/dist/runtime/tailwind/engine/css/split-mixed-rule.d.ts +1 -1
  45. package/dist/runtime/tailwind/engine/css/split-mixed-rule.js +1 -1
  46. package/dist/runtime/tailwind/engine/css/strip-empty-tailwind-vars.d.ts +1 -1
  47. package/dist/runtime/tailwind/engine/css/strip-empty-tailwind-vars.js +1 -1
  48. package/dist/runtime/tailwind/engine/index.d.ts +6 -6
  49. package/dist/runtime/tailwind/engine/index.js +70 -7
  50. package/dist/runtime/tailwind/engine/setup-tailwind.d.ts +1 -1
  51. package/dist/runtime/tailwind/engine/setup-tailwind.js +1 -1
  52. package/dist/runtime/tailwind/post-render.js +7 -8
  53. package/dist/shared/nuxt-email.B5ux0eBv.mjs +133 -0
  54. package/dist/types.d.mts +1 -1
  55. package/package.json +18 -7
@@ -1,41 +1,12 @@
1
1
  /**
2
- * Downlevels modern CSS features that email clients don't support,
3
- * operating on a css-tree StyleSheet AST.
4
- *
5
- * 1. CSS Nesting: unnests @media rules from inside selectors
6
- * `.sm_p-4{@media (min-width:40rem){padding:1rem!important}}`
7
- * → `@media (min-width:40rem){.sm_p-4{padding:1rem!important}}`
8
- *
9
- * 2. Media Queries Level 4 range syntax → legacy min-width/max-width
10
- * `(width>=40rem)` → `(min-width:40rem)`
11
- *
12
- * Gmail, Outlook, Yahoo, and most email clients don't support either feature.
13
- * See: https://www.caniemail.com/features/css-at-media/
14
- * https://www.caniemail.com/features/css-nesting/
2
+ * Downlevel the modern CSS emitted by Tailwind to shapes that email clients
3
+ * can parse. The transform is deliberately strict: leaving one nested rule in
4
+ * the output is worse than failing the render with an actionable error.
15
5
  */
16
- import { type StyleSheet } from 'css-tree';
6
+ import { type StyleSheet } from '../../css-tree.js';
17
7
  /**
18
- * css-tree 3.x introduced new AST node types for query-related at-rules that
19
- * `@types/css-tree` (still on 2.x at the time of writing) doesn't expose.
20
- * Augmenting the module here lets the rest of this file work with strong
21
- * types instead of scattering `as` casts everywhere.
22
- *
23
- * - `FeatureRange` is what the parser emits for Media Queries Level 4 range
24
- * syntax: `(width >= 40rem)`.
25
- * - `Feature` is the legacy form (`(min-width: 40rem)`) we construct as the
26
- * downleveled output.
27
- *
28
- * Note: we cannot extend the `CssNode` union itself (it's a `type` alias, not
29
- * an interface), so two narrow casts remain in this file:
30
- * 1. Widening `node` inside `walk()` so we can narrow against
31
- * `FeatureRange.type` (`CssNode` doesn't list `'FeatureRange'`).
32
- * 2. Assigning a constructed `Feature` back to `ListItem<CssNode>.data`.
33
- *
34
- * Both are flagged inline and reference back to this block.
35
- *
36
- * See:
37
- * https://github.com/csstree/csstree/blob/master/lib/syntax/node/FeatureRange.js
38
- * https://github.com/csstree/csstree/blob/master/lib/syntax/node/Feature.js
8
+ * css-tree 3.x parses media range syntax, but the currently published type
9
+ * package does not expose the corresponding nodes yet.
39
10
  */
40
11
  declare module 'css-tree' {
41
12
  interface FeatureRange extends CssNodeCommon {
@@ -55,9 +26,8 @@ declare module 'css-tree' {
55
26
  }
56
27
  }
57
28
  /**
58
- * Unnest @media at-rules from inside regular rules, and downlevel
59
- * range media query syntax to legacy min-width/max-width.
60
- *
29
+ * Recursively flatten selector nesting and nested @media/@supports rules,
30
+ * then convert media range syntax such as `(width >= 40rem)` to `min-width`.
61
31
  * Mutates the stylesheet in place.
62
32
  */
63
33
  export declare function downlevelForEmailClients(styleSheet: StyleSheet): void;
@@ -1,75 +1,171 @@
1
1
  import {
2
2
  clone,
3
+ generate,
3
4
  List,
5
+ parse,
4
6
  walk
5
- } from "css-tree";
7
+ } from "../../css-tree.js";
6
8
  export function downlevelForEmailClients(styleSheet) {
7
- unnestMediaQueries(styleSheet);
9
+ styleSheet.children = flattenStyleSheet(styleSheet);
8
10
  downlevelRangeMediaQueries(styleSheet);
11
+ walk(styleSheet, (node) => {
12
+ if (node.type === "NestingSelector") {
13
+ throw new TypeError(
14
+ "Unable to render Tailwind CSS: an unsupported nesting selector remained after downleveling."
15
+ );
16
+ }
17
+ });
9
18
  }
10
- function unnestMediaQueries(styleSheet) {
11
- const transforms = [];
12
- walk(styleSheet, {
13
- visit: "Rule",
14
- enter(rule, item, list) {
15
- if (!rule.block || !item) return;
16
- const nestedAtrules = [];
17
- const remainingChildren = [];
18
- rule.block.children.forEach((child) => {
19
- if (child.type === "Atrule" && (child.name === "media" || child.name === "supports")) {
20
- nestedAtrules.push(child);
21
- } else {
22
- remainingChildren.push(child);
23
- }
24
- });
25
- if (nestedAtrules.length > 0) {
26
- transforms.push({
27
- parentRule: rule,
28
- parentItem: item,
29
- parentList: list,
30
- nestedAtrules,
31
- remainingChildren
32
- });
33
- }
19
+ function flattenStyleSheet(styleSheet) {
20
+ const output = new List();
21
+ styleSheet.children.forEach((node) => {
22
+ if (node.type === "Rule") {
23
+ flattenRule(node, null, [], output);
24
+ return;
25
+ }
26
+ if (node.type === "Atrule" && isConditionalAtrule(node)) {
27
+ flattenConditionalAtrule(node, null, [], output);
28
+ return;
34
29
  }
30
+ output.appendData(clone(node));
35
31
  });
36
- for (let i = transforms.length - 1; i >= 0; i--) {
37
- const {
38
- parentRule,
39
- parentItem,
40
- parentList,
41
- nestedAtrules,
42
- remainingChildren
43
- } = transforms[i];
44
- const replacements = new List();
45
- if (remainingChildren.length > 0) {
46
- parentRule.block.children = new List().fromArray(
47
- remainingChildren
32
+ return output;
33
+ }
34
+ function flattenRule(rule, parentSelectors, wrappers, output) {
35
+ const selectors = resolveSelectors(rule, parentSelectors);
36
+ flattenBlock(rule.block.children, selectors, wrappers, output);
37
+ }
38
+ function flattenBlock(children, selectors, wrappers, output) {
39
+ let declarations = new List();
40
+ const flushDeclarations = () => {
41
+ if (declarations.isEmpty) return;
42
+ if (!selectors) {
43
+ throw new TypeError(
44
+ "Unable to render Tailwind CSS: declarations were found outside a selector."
48
45
  );
49
- replacements.appendData(parentRule);
50
46
  }
51
- for (const atrule of nestedAtrules) {
52
- const wrappedRule = {
53
- type: "Rule",
54
- prelude: clone(parentRule.prelude),
55
- block: {
56
- type: "Block",
57
- children: atrule.block ? atrule.block.children : new List()
58
- }
59
- };
60
- const newAtrule = {
61
- type: "Atrule",
62
- name: atrule.name,
63
- prelude: atrule.prelude,
64
- block: {
65
- type: "Block",
66
- children: new List().fromArray([wrappedRule])
67
- }
68
- };
69
- replacements.appendData(newAtrule);
47
+ appendWrappedRule(selectors, declarations, wrappers, output);
48
+ declarations = new List();
49
+ };
50
+ children.forEach((node) => {
51
+ if (node.type === "Raw") {
52
+ throw new TypeError(
53
+ `Unable to render Tailwind CSS: an unparsed nested CSS shape was encountered (${node.value}).`
54
+ );
55
+ }
56
+ if (node.type === "Rule") {
57
+ flushDeclarations();
58
+ flattenRule(node, selectors, wrappers, output);
59
+ return;
60
+ }
61
+ if (node.type === "Atrule") {
62
+ flushDeclarations();
63
+ if (!isConditionalAtrule(node)) {
64
+ throw new TypeError(
65
+ `Unable to render Tailwind CSS: nested @${node.name} rules are not supported.`
66
+ );
67
+ }
68
+ flattenConditionalAtrule(node, selectors, wrappers, output);
69
+ return;
70
70
  }
71
- parentList.replace(parentItem, replacements);
71
+ declarations.appendData(clone(node));
72
+ });
73
+ flushDeclarations();
74
+ }
75
+ function flattenConditionalAtrule(atrule, selectors, wrappers, output) {
76
+ if (!atrule.block) {
77
+ throw new TypeError(
78
+ `Unable to render Tailwind CSS: @${atrule.name} has no rule block.`
79
+ );
80
+ }
81
+ flattenBlock(
82
+ atrule.block.children,
83
+ selectors,
84
+ [
85
+ ...wrappers,
86
+ { name: atrule.name, prelude: clonePrelude(atrule.prelude) }
87
+ ],
88
+ output
89
+ );
90
+ }
91
+ function appendWrappedRule(selectors, declarations, wrappers, output) {
92
+ let node = {
93
+ type: "Rule",
94
+ prelude: clone(selectors),
95
+ block: { type: "Block", children: declarations }
96
+ };
97
+ for (let index = wrappers.length - 1; index >= 0; index--) {
98
+ const wrapper = wrappers[index];
99
+ node = {
100
+ type: "Atrule",
101
+ name: wrapper.name,
102
+ prelude: clonePrelude(wrapper.prelude),
103
+ block: {
104
+ type: "Block",
105
+ children: new List().fromArray([node])
106
+ }
107
+ };
108
+ }
109
+ output.appendData(node);
110
+ }
111
+ function resolveSelectors(rule, parentSelectors) {
112
+ if (rule.prelude.type !== "SelectorList") {
113
+ throw new TypeError(
114
+ "Unable to render Tailwind CSS: raw selectors cannot be downleveled safely."
115
+ );
116
+ }
117
+ if (!parentSelectors) {
118
+ assertNoNestingSelector(rule.prelude);
119
+ return clone(rule.prelude);
72
120
  }
121
+ const resolved = new List();
122
+ const nestedSelectors = rule.prelude;
123
+ parentSelectors.children.forEach((parentNode) => {
124
+ const parent = parentNode;
125
+ nestedSelectors.children.forEach((nestedNode) => {
126
+ const nested = nestedNode;
127
+ if (containsNestingSelector(nested)) {
128
+ const selector = clone(nested);
129
+ walk(selector, {
130
+ enter(node, item, list) {
131
+ if (node.type === "NestingSelector") {
132
+ list.replace(item, clone(parent).children);
133
+ }
134
+ }
135
+ });
136
+ resolved.appendData(selector);
137
+ } else {
138
+ const selector = parse(
139
+ `${generate(parent)} ${generate(nested)}`,
140
+ { context: "selector" }
141
+ );
142
+ resolved.appendData(selector);
143
+ }
144
+ });
145
+ });
146
+ return { type: "SelectorList", children: resolved };
147
+ }
148
+ function containsNestingSelector(selector) {
149
+ let found = false;
150
+ walk(selector, (node) => {
151
+ if (node.type === "NestingSelector") found = true;
152
+ });
153
+ return found;
154
+ }
155
+ function assertNoNestingSelector(selectors) {
156
+ walk(selectors, (node) => {
157
+ if (node.type === "NestingSelector") {
158
+ throw new TypeError(
159
+ "Unable to render Tailwind CSS: a top-level nesting selector has no parent selector."
160
+ );
161
+ }
162
+ });
163
+ }
164
+ function isConditionalAtrule(atrule) {
165
+ return atrule.name === "media" || atrule.name === "supports";
166
+ }
167
+ function clonePrelude(prelude) {
168
+ return prelude ? clone(prelude) : null;
73
169
  }
74
170
  function downlevelRangeMediaQueries(styleSheet) {
75
171
  const replacements = [];
@@ -77,10 +173,7 @@ function downlevelRangeMediaQueries(styleSheet) {
77
173
  enter(originalNode, item) {
78
174
  const node = originalNode;
79
175
  if (item && node.type === "FeatureRange") {
80
- const replacement = downlevelFeatureRange(node);
81
- if (replacement) {
82
- replacements.push({ item, replacement });
83
- }
176
+ replacements.push({ item, replacement: downlevelFeatureRange(node) });
84
177
  }
85
178
  }
86
179
  });
@@ -89,19 +182,40 @@ function downlevelRangeMediaQueries(styleSheet) {
89
182
  }
90
183
  }
91
184
  function downlevelFeatureRange(range) {
92
- if (range.left.type !== "Identifier") return null;
93
- let prefix;
94
- if (range.leftComparison === ">=" || range.leftComparison === ">") {
95
- prefix = "min-";
96
- } else if (range.leftComparison === "<=" || range.leftComparison === "<") {
97
- prefix = "max-";
185
+ const source = generate(range);
186
+ if (range.rightComparison !== null || range.right !== null) {
187
+ throw new TypeError(
188
+ `Unable to render Tailwind CSS: unsupported two-sided media range ${source}.`
189
+ );
190
+ }
191
+ let name;
192
+ let value;
193
+ let direct;
194
+ if (range.left.type === "Identifier") {
195
+ name = range.left.name;
196
+ value = range.middle;
197
+ direct = true;
198
+ } else if (range.middle.type === "Identifier") {
199
+ name = range.middle.name;
200
+ value = range.left;
201
+ direct = false;
98
202
  } else {
99
- return null;
203
+ throw new TypeError(
204
+ `Unable to render Tailwind CSS: unsupported media range ${source}.`
205
+ );
206
+ }
207
+ const comparison = range.leftComparison;
208
+ const minimum = direct ? comparison === ">=" || comparison === ">" : comparison === "<=" || comparison === "<";
209
+ const maximum = direct ? comparison === "<=" || comparison === "<" : comparison === ">=" || comparison === ">";
210
+ if (!minimum && !maximum) {
211
+ throw new TypeError(
212
+ `Unable to render Tailwind CSS: unsupported media range ${source}.`
213
+ );
100
214
  }
101
215
  return {
102
216
  type: "Feature",
103
217
  kind: "media",
104
- name: `${prefix}${range.left.name}`,
105
- value: range.middle
218
+ name: `${minimum ? "min-" : "max-"}${name}`,
219
+ value
106
220
  };
107
221
  }
@@ -1,6 +1,8 @@
1
- import { type CssNode, type Rule } from 'css-tree';
1
+ import { type CssNode, type Rule } from '../../css-tree.js';
2
2
  export interface ExtractedRules {
3
3
  inlinable: Map<string, Rule[]>;
4
4
  nonInlinable: Map<string, Rule[]>;
5
+ /** Canonical non-inlinable rules, once each and in stylesheet order. */
6
+ orderedNonInlinable: Rule[];
5
7
  }
6
8
  export declare function extractRulesPerClass(root: CssNode, classes: string[]): ExtractedRules;
@@ -1,10 +1,11 @@
1
- import { string, walk } from "css-tree";
1
+ import { string, walk } from "../../css-tree.js";
2
2
  import { isRuleInlinable } from "./is-rule-inlinable.js";
3
3
  import { splitMixedRule } from "./split-mixed-rule.js";
4
4
  export function extractRulesPerClass(root, classes) {
5
5
  const classSet = new Set(classes);
6
6
  const inlinableRules = /* @__PURE__ */ new Map();
7
7
  const nonInlinableRules = /* @__PURE__ */ new Map();
8
+ const orderedNonInlinableRules = [];
8
9
  const appendRule = (map, className, rule) => {
9
10
  const existing = map.get(className);
10
11
  if (existing) {
@@ -35,8 +36,10 @@ export function extractRulesPerClass(root, classes) {
35
36
  }
36
37
  } else {
37
38
  const { inlinablePart, nonInlinablePart } = splitMixedRule(rule);
39
+ let includesRequestedClass = false;
38
40
  for (const className of selectorClasses) {
39
41
  if (!classSet.has(className)) continue;
42
+ includesRequestedClass = true;
40
43
  if (inlinablePart) {
41
44
  appendRule(inlinableRules, className, inlinablePart);
42
45
  }
@@ -44,11 +47,15 @@ export function extractRulesPerClass(root, classes) {
44
47
  appendRule(nonInlinableRules, className, nonInlinablePart);
45
48
  }
46
49
  }
50
+ if (includesRequestedClass && nonInlinablePart) {
51
+ orderedNonInlinableRules.push(nonInlinablePart);
52
+ }
47
53
  }
48
54
  }
49
55
  });
50
56
  return {
51
57
  inlinable: inlinableRules,
52
- nonInlinable: nonInlinableRules
58
+ nonInlinable: nonInlinableRules,
59
+ orderedNonInlinable: orderedNonInlinableRules
53
60
  };
54
61
  }
@@ -1,4 +1,4 @@
1
- import { type CssNode, type Declaration } from 'css-tree';
1
+ import { type CssNode, type Declaration } from '../../css-tree.js';
2
2
  export interface CustomProperty {
3
3
  syntax?: Declaration;
4
4
  inherits?: Declaration;
@@ -1,4 +1,4 @@
1
- import { generate, walk } from "css-tree";
1
+ import { generate, walk } from "../../css-tree.js";
2
2
  export function getCustomProperties(node) {
3
3
  const customProperties = /* @__PURE__ */ new Map();
4
4
  walk(node, {
@@ -1,2 +1,2 @@
1
- import { type CssNode } from 'css-tree';
1
+ import { type CssNode } from '../../css-tree.js';
2
2
  export declare function isPartInlinable(part: CssNode): boolean;
@@ -1,4 +1,4 @@
1
- import { find } from "css-tree";
1
+ import { find } from "../../css-tree.js";
2
2
  export function isPartInlinable(part) {
3
3
  const hasAtRuleInside = find(part, (node) => node.type === "Atrule") !== null;
4
4
  const hasPseudoSelector = find(
@@ -1,2 +1,2 @@
1
- import { type Rule } from 'css-tree';
1
+ import { type Rule } from '../../css-tree.js';
2
2
  export declare function isRuleInlinable(rule: Rule): boolean;
@@ -1,4 +1,4 @@
1
- import { find } from "css-tree";
1
+ import { find } from "../../css-tree.js";
2
2
  export function isRuleInlinable(rule) {
3
3
  const hasAtRuleInside = find(rule, (node) => node.type === "Atrule") !== null;
4
4
  const hasPseudoSelector = find(
@@ -1,4 +1,4 @@
1
- import { type CssNode } from 'css-tree';
1
+ import { type CssNode } from '../../css-tree.js';
2
2
  import type { CustomProperties } from './get-custom-properties.js';
3
3
  /**
4
4
  * Adapted from React Email's `make-inline-styles-for.ts`.
@@ -1,4 +1,4 @@
1
- import { generate, walk } from "css-tree";
1
+ import { generate, walk } from "../../css-tree.js";
2
2
  import { getCssProperty } from "./get-css-property.js";
3
3
  import { stripEmptyTailwindVars } from "./strip-empty-tailwind-vars.js";
4
4
  import { unwrapValue } from "./unwrap-value.js";
@@ -1,4 +1,4 @@
1
- import { type CssNode, type Declaration } from 'css-tree';
1
+ import { type CssNode, type Declaration } from '../../css-tree.js';
2
2
  export interface VariableDefinition {
3
3
  declaration: Declaration;
4
4
  path: CssNode[];
@@ -2,7 +2,7 @@ import {
2
2
  generate,
3
3
  parse,
4
4
  walk
5
- } from "css-tree";
5
+ } from "../../css-tree.js";
6
6
  function doSelectorsIntersect(first, second) {
7
7
  const firstStringified = generate(first);
8
8
  const secondStringified = generate(second);
@@ -1,4 +1,4 @@
1
- import { type CssNode } from 'css-tree';
1
+ import { type CssNode } from '../../css-tree.js';
2
2
  /**
3
3
  * Intentionally only resolves `*` and `/` operations without dealing with parenthesis, because this is the only thing required to run Tailwind v4
4
4
  */
@@ -1,4 +1,4 @@
1
- import { walk } from "css-tree";
1
+ import { walk } from "../../css-tree.js";
2
2
  export function resolveCalcExpressions(node) {
3
3
  walk(node, {
4
4
  visit: "Function",
@@ -1,4 +1,4 @@
1
- import { type CssNode } from 'css-tree';
1
+ import { type CssNode } from '../../css-tree.js';
2
2
  /**
3
3
  * Meant to do all the things necessary, in a per-declaration basis, to have the best email client
4
4
  * support possible.
@@ -3,7 +3,7 @@ import {
3
3
  List,
4
4
  parse,
5
5
  walk
6
- } from "css-tree";
6
+ } from "../../css-tree.js";
7
7
  function rgbNode(r, g, b, alpha) {
8
8
  const children = new List();
9
9
  children.appendData({
@@ -1,4 +1,4 @@
1
- import { type CssNode } from 'css-tree';
1
+ import { type CssNode } from '../../css-tree.js';
2
2
  /**
3
3
  * This function goes through a few steps to ensure the best email client support and
4
4
  * to ensure that media queries and pseudo classes are applied correctly alongside
@@ -1,4 +1,4 @@
1
- import { string, walk } from "css-tree";
1
+ import { string, walk } from "../../css-tree.js";
2
2
  import { sanitizeClassName } from "../compatibility/sanitize-class-name.js";
3
3
  import { isRuleInlinable } from "./is-rule-inlinable.js";
4
4
  import { stripEmptyTailwindVars } from "./strip-empty-tailwind-vars.js";
@@ -1,4 +1,4 @@
1
- import { type Rule } from 'css-tree';
1
+ import { type Rule } from '../../css-tree.js';
2
2
  /**
3
3
  * Split a rule into inlinable and non-inlinable parts. Caller must only pass rules
4
4
  * for which isRuleInlinable(rule) is false. Returns clones so the original stylesheet is never mutated.
@@ -1,4 +1,4 @@
1
- import { clone, find, List } from "css-tree";
1
+ import { clone, find, List } from "../../css-tree.js";
2
2
  import { isPartInlinable } from "./is-part-inlinable.js";
3
3
  export function splitMixedRule(rule) {
4
4
  const selectorHasPseudo = rule.prelude !== null && find(
@@ -1,4 +1,4 @@
1
- import { type CssNode } from 'css-tree';
1
+ import { type CssNode } from '../../css-tree.js';
2
2
  /**
3
3
  * Tailwind v4 emits variant-stacking idioms like
4
4
  * font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) tabular-nums var(--tw-numeric-fraction,)
@@ -1,4 +1,4 @@
1
- import { generate, walk } from "css-tree";
1
+ import { generate, walk } from "../../css-tree.js";
2
2
  export function stripEmptyTailwindVars(node) {
3
3
  walk(node, {
4
4
  visit: "Function",
@@ -30,12 +30,12 @@ export interface ComputedStyles {
30
30
  */
31
31
  residualClassMap: Map<string, string>;
32
32
  /**
33
- * Original (unsanitized) names of every class that produced a non-inlinable
34
- * rule, in stylesheet-emission order — the exact list React Email interpolates
35
- * into its no-`<head>` error (`Array.from(nonInlinableRules.keys())`,
36
- * tailwind.tsx). This order (breakpoint/variant emission order) differs from the
37
- * authored class order, so it cannot be reconstructed from {@link residualClassMap}
38
- * and must be carried canonically.
33
+ * Original (unsanitized) names of classes that need the generated `<head>`
34
+ * CSS: non-inlinable rules and animation declarations with referenced
35
+ * keyframes. Non-inlinable rules keep stylesheet-emission order; animation-only
36
+ * classes follow in authored order. This list drives the missing-`<head>` error
37
+ * and cannot be reconstructed from {@link residualClassMap}, because an
38
+ * animation declaration itself can still be inlined.
39
39
  */
40
40
  nonInlinableClassNames: string[];
41
41
  }