@lupinum/nuxt-email 0.1.0
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 +49 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/THIRD_PARTY_NOTICES +75 -0
- package/dist/chunks/setup.mjs +149 -0
- package/dist/module.d.mts +15 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +300 -0
- package/dist/runtime/code-block/create-component.d.ts +16 -0
- package/dist/runtime/code-block/create-component.js +106 -0
- package/dist/runtime/components/EBody.d.ts +4 -0
- package/dist/runtime/components/EBody.js +75 -0
- package/dist/runtime/components/EButton.d.ts +11 -0
- package/dist/runtime/components/EButton.js +89 -0
- package/dist/runtime/components/EButton.padding.d.ts +12 -0
- package/dist/runtime/components/EButton.padding.js +118 -0
- package/dist/runtime/components/ECodeInline.d.ts +13 -0
- package/dist/runtime/components/ECodeInline.js +43 -0
- package/dist/runtime/components/EColumn.d.ts +4 -0
- package/dist/runtime/components/EColumn.js +12 -0
- package/dist/runtime/components/EContainer.d.ts +4 -0
- package/dist/runtime/components/EContainer.js +35 -0
- package/dist/runtime/components/EFont.d.ts +24 -0
- package/dist/runtime/components/EFont.js +124 -0
- package/dist/runtime/components/EHead.d.ts +4 -0
- package/dist/runtime/components/EHead.js +17 -0
- package/dist/runtime/components/EHeading.d.ts +7 -0
- package/dist/runtime/components/EHeading.js +23 -0
- package/dist/runtime/components/EHr.d.ts +4 -0
- package/dist/runtime/components/EHr.js +26 -0
- package/dist/runtime/components/EHtml.d.ts +4 -0
- package/dist/runtime/components/EHtml.js +17 -0
- package/dist/runtime/components/EImg.d.ts +7 -0
- package/dist/runtime/components/EImg.js +44 -0
- package/dist/runtime/components/ELink.d.ts +6 -0
- package/dist/runtime/components/ELink.js +35 -0
- package/dist/runtime/components/EMarkdown.d.ts +8 -0
- package/dist/runtime/components/EMarkdown.js +64 -0
- package/dist/runtime/components/EPreview.d.ts +8 -0
- package/dist/runtime/components/EPreview.js +85 -0
- package/dist/runtime/components/ERow.d.ts +4 -0
- package/dist/runtime/components/ERow.js +53 -0
- package/dist/runtime/components/ESection.d.ts +4 -0
- package/dist/runtime/components/ESection.js +35 -0
- package/dist/runtime/components/ETailwind.d.ts +24 -0
- package/dist/runtime/components/ETailwind.js +49 -0
- package/dist/runtime/components/EText.d.ts +4 -0
- package/dist/runtime/components/EText.js +20 -0
- package/dist/runtime/components/EText.margins.d.ts +18 -0
- package/dist/runtime/components/EText.margins.js +81 -0
- package/dist/runtime/components/attributes.d.ts +4 -0
- package/dist/runtime/components/attributes.js +11 -0
- package/dist/runtime/components/email-component-names.d.ts +2 -0
- package/dist/runtime/components/email-component-names.js +20 -0
- package/dist/runtime/components/email-component-registry.d.ts +8 -0
- package/dist/runtime/components/email-component-registry.js +2 -0
- package/dist/runtime/components/index.d.ts +23 -0
- package/dist/runtime/components/index.js +18 -0
- package/dist/runtime/components/markdown/parse-css-in-js-to-inline-css.d.ts +2 -0
- package/dist/runtime/components/markdown/parse-css-in-js-to-inline-css.js +60 -0
- package/dist/runtime/components/markdown/render-markdown.d.ts +2 -0
- package/dist/runtime/components/markdown/render-markdown.js +137 -0
- package/dist/runtime/components/markdown/styles.d.ts +30 -0
- package/dist/runtime/components/markdown/styles.js +91 -0
- package/dist/runtime/components/presentation-table.d.ts +7 -0
- package/dist/runtime/components/presentation-table.js +11 -0
- package/dist/runtime/components/style.d.ts +5 -0
- package/dist/runtime/components/style.js +32 -0
- package/dist/runtime/components/table-padding.d.ts +6 -0
- package/dist/runtime/components/table-padding.js +20 -0
- package/dist/runtime/define-email.d.ts +2 -0
- package/dist/runtime/define-email.js +5 -0
- package/dist/runtime/dev-preview/page-script.d.ts +1 -0
- package/dist/runtime/dev-preview/page-script.js +403 -0
- package/dist/runtime/dev-preview/page.css.d.ts +1 -0
- package/dist/runtime/dev-preview/page.css.js +602 -0
- package/dist/runtime/dev-preview/page.get.d.ts +4 -0
- package/dist/runtime/dev-preview/page.get.js +119 -0
- package/dist/runtime/dev-preview/render.get.d.ts +3 -0
- package/dist/runtime/dev-preview/render.get.js +123 -0
- package/dist/runtime/dev-preview/templates.get.d.ts +7 -0
- package/dist/runtime/dev-preview/templates.get.js +14 -0
- package/dist/runtime/errors.d.ts +4 -0
- package/dist/runtime/errors.js +7 -0
- package/dist/runtime/render/define-email.d.ts +56 -0
- package/dist/runtime/render/define-email.js +40 -0
- package/dist/runtime/render/document.d.ts +4 -0
- package/dist/runtime/render/document.js +75 -0
- package/dist/runtime/render/errors.d.ts +4 -0
- package/dist/runtime/render/errors.js +8 -0
- package/dist/runtime/render/plain-text.d.ts +1 -0
- package/dist/runtime/render/plain-text.js +15 -0
- package/dist/runtime/render/render-component.d.ts +4 -0
- package/dist/runtime/render/render-component.js +46 -0
- package/dist/runtime/render/render-email-component.d.ts +16 -0
- package/dist/runtime/render/render-email-component.js +46 -0
- package/dist/runtime/render/types.d.ts +9 -0
- package/dist/runtime/render/types.js +0 -0
- package/dist/runtime/tailwind/engine/compatibility/escape-class-name.d.ts +8 -0
- package/dist/runtime/tailwind/engine/compatibility/escape-class-name.js +10 -0
- package/dist/runtime/tailwind/engine/compatibility/sanitize-class-name.d.ts +7 -0
- package/dist/runtime/tailwind/engine/compatibility/sanitize-class-name.js +17 -0
- package/dist/runtime/tailwind/engine/compatibility/unescape-class.d.ts +1 -0
- package/dist/runtime/tailwind/engine/compatibility/unescape-class.js +3 -0
- package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.d.ts +63 -0
- package/dist/runtime/tailwind/engine/css/downlevel-for-email-clients.js +107 -0
- package/dist/runtime/tailwind/engine/css/extract-rules-per-class.d.ts +6 -0
- package/dist/runtime/tailwind/engine/css/extract-rules-per-class.js +54 -0
- package/dist/runtime/tailwind/engine/css/get-css-property.d.ts +17 -0
- package/dist/runtime/tailwind/engine/css/get-css-property.js +3 -0
- package/dist/runtime/tailwind/engine/css/get-custom-properties.d.ts +8 -0
- package/dist/runtime/tailwind/engine/css/get-custom-properties.js +37 -0
- package/dist/runtime/tailwind/engine/css/is-part-inlinable.d.ts +2 -0
- package/dist/runtime/tailwind/engine/css/is-part-inlinable.js +9 -0
- package/dist/runtime/tailwind/engine/css/is-rule-inlinable.d.ts +2 -0
- package/dist/runtime/tailwind/engine/css/is-rule-inlinable.js +9 -0
- package/dist/runtime/tailwind/engine/css/make-inline-styles-for.d.ts +21 -0
- package/dist/runtime/tailwind/engine/css/make-inline-styles-for.js +62 -0
- package/dist/runtime/tailwind/engine/css/resolve-all-css-variables.d.ts +8 -0
- package/dist/runtime/tailwind/engine/css/resolve-all-css-variables.js +138 -0
- package/dist/runtime/tailwind/engine/css/resolve-calc-expressions.d.ts +5 -0
- package/dist/runtime/tailwind/engine/css/resolve-calc-expressions.js +101 -0
- package/dist/runtime/tailwind/engine/css/sanitize-declarations.d.ts +15 -0
- package/dist/runtime/tailwind/engine/css/sanitize-declarations.js +358 -0
- package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.d.ts +17 -0
- package/dist/runtime/tailwind/engine/css/sanitize-non-inlinable-rules.js +30 -0
- package/dist/runtime/tailwind/engine/css/split-mixed-rule.d.ts +12 -0
- package/dist/runtime/tailwind/engine/css/split-mixed-rule.js +37 -0
- package/dist/runtime/tailwind/engine/css/strip-empty-tailwind-vars.d.ts +19 -0
- package/dist/runtime/tailwind/engine/css/strip-empty-tailwind-vars.js +44 -0
- package/dist/runtime/tailwind/engine/css/unwrap-value.d.ts +2 -0
- package/dist/runtime/tailwind/engine/css/unwrap-value.js +6 -0
- package/dist/runtime/tailwind/engine/index.d.ts +63 -0
- package/dist/runtime/tailwind/engine/index.js +53 -0
- package/dist/runtime/tailwind/engine/pixel-based-preset.d.ts +9 -0
- package/dist/runtime/tailwind/engine/pixel-based-preset.js +58 -0
- package/dist/runtime/tailwind/engine/sanitize-stylesheet.d.ts +10 -0
- package/dist/runtime/tailwind/engine/sanitize-stylesheet.js +8 -0
- package/dist/runtime/tailwind/engine/setup-tailwind.d.ts +23 -0
- package/dist/runtime/tailwind/engine/setup-tailwind.js +98 -0
- package/dist/runtime/tailwind/engine/stylesheets.d.ts +4 -0
- package/dist/runtime/tailwind/engine/stylesheets.js +1756 -0
- package/dist/runtime/tailwind/engine/types.d.ts +25 -0
- package/dist/runtime/tailwind/engine/types.js +0 -0
- package/dist/runtime/tailwind/errors.d.ts +7 -0
- package/dist/runtime/tailwind/errors.js +9 -0
- package/dist/runtime/tailwind/inline-utils.d.ts +19 -0
- package/dist/runtime/tailwind/inline-utils.js +24 -0
- package/dist/runtime/tailwind/nested.d.ts +59 -0
- package/dist/runtime/tailwind/nested.js +47 -0
- package/dist/runtime/tailwind/post-render.d.ts +7 -0
- package/dist/runtime/tailwind/post-render.js +156 -0
- package/dist/runtime/template-registry/errors.d.ts +5 -0
- package/dist/runtime/template-registry/errors.js +11 -0
- package/dist/runtime/template-registry/registry.d.ts +13 -0
- package/dist/runtime/testing/index.d.ts +13 -0
- package/dist/runtime/testing/index.js +2 -0
- package/dist/types.d.mts +5 -0
- package/package.json +122 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clone,
|
|
3
|
+
List,
|
|
4
|
+
walk
|
|
5
|
+
} from "css-tree";
|
|
6
|
+
export function downlevelForEmailClients(styleSheet) {
|
|
7
|
+
unnestMediaQueries(styleSheet);
|
|
8
|
+
downlevelRangeMediaQueries(styleSheet);
|
|
9
|
+
}
|
|
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
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
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
|
|
48
|
+
);
|
|
49
|
+
replacements.appendData(parentRule);
|
|
50
|
+
}
|
|
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);
|
|
70
|
+
}
|
|
71
|
+
parentList.replace(parentItem, replacements);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function downlevelRangeMediaQueries(styleSheet) {
|
|
75
|
+
const replacements = [];
|
|
76
|
+
walk(styleSheet, {
|
|
77
|
+
enter(originalNode, item) {
|
|
78
|
+
const node = originalNode;
|
|
79
|
+
if (item && node.type === "FeatureRange") {
|
|
80
|
+
const replacement = downlevelFeatureRange(node);
|
|
81
|
+
if (replacement) {
|
|
82
|
+
replacements.push({ item, replacement });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
for (const { item, replacement } of replacements) {
|
|
88
|
+
item.data = replacement;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
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-";
|
|
98
|
+
} else {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
type: "Feature",
|
|
103
|
+
kind: "media",
|
|
104
|
+
name: `${prefix}${range.left.name}`,
|
|
105
|
+
value: range.middle
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { string, walk } from "css-tree";
|
|
2
|
+
import { isRuleInlinable } from "./is-rule-inlinable.js";
|
|
3
|
+
import { splitMixedRule } from "./split-mixed-rule.js";
|
|
4
|
+
export function extractRulesPerClass(root, classes) {
|
|
5
|
+
const classSet = new Set(classes);
|
|
6
|
+
const inlinableRules = /* @__PURE__ */ new Map();
|
|
7
|
+
const nonInlinableRules = /* @__PURE__ */ new Map();
|
|
8
|
+
const appendRule = (map, className, rule) => {
|
|
9
|
+
const existing = map.get(className);
|
|
10
|
+
if (existing) {
|
|
11
|
+
existing.push(rule);
|
|
12
|
+
} else {
|
|
13
|
+
map.set(className, [rule]);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
walk(root, {
|
|
17
|
+
visit: "Rule",
|
|
18
|
+
enter(rule) {
|
|
19
|
+
const firstSelector = rule.prelude.type === "SelectorList" ? rule.prelude.children.first : null;
|
|
20
|
+
if (firstSelector?.type === "Selector" && firstSelector.children.first?.type === "NestingSelector") {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const selectorClasses = [];
|
|
24
|
+
walk(rule.prelude, {
|
|
25
|
+
visit: "ClassSelector",
|
|
26
|
+
enter(classSelector) {
|
|
27
|
+
selectorClasses.push(string.decode(classSelector.name));
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
if (isRuleInlinable(rule)) {
|
|
31
|
+
for (const className of selectorClasses) {
|
|
32
|
+
if (classSet.has(className)) {
|
|
33
|
+
appendRule(inlinableRules, className, rule);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
const { inlinablePart, nonInlinablePart } = splitMixedRule(rule);
|
|
38
|
+
for (const className of selectorClasses) {
|
|
39
|
+
if (!classSet.has(className)) continue;
|
|
40
|
+
if (inlinablePart) {
|
|
41
|
+
appendRule(inlinableRules, className, inlinablePart);
|
|
42
|
+
}
|
|
43
|
+
if (nonInlinablePart) {
|
|
44
|
+
appendRule(nonInlinableRules, className, nonInlinablePart);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
inlinable: inlinableRules,
|
|
52
|
+
nonInlinable: nonInlinableRules
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from React Email's `get-react-property.ts`.
|
|
3
|
+
*
|
|
4
|
+
* React Email converts a css-tree declaration property (already kebab-case CSS,
|
|
5
|
+
* e.g. `background-color`) into a React DOM style key (`backgroundColor`) so it
|
|
6
|
+
* can spread it onto a React element's `style` object. Its `-ms-` special case
|
|
7
|
+
* exists purely because React's camelCase convention is `msTransform` (lowercase
|
|
8
|
+
* `m`) unlike every other vendor prefix (`WebkitTransform`).
|
|
9
|
+
*
|
|
10
|
+
* This engine emits inline styles as real CSS `property: value` pairs, not React
|
|
11
|
+
* DOM keys, so no camelCase conversion is wanted. css-tree already yields the
|
|
12
|
+
* canonical kebab-case property name; we only lowercase it to match React Email's
|
|
13
|
+
* behavior (it lowercases before converting). Custom properties (`--tw-*`) never
|
|
14
|
+
* reach this function — `makeInlineStylesFor` skips `--` declarations — so the
|
|
15
|
+
* historical lowercasing of custom-property names is a non-issue here.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getCssProperty(prop: string): string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CssNode, type Declaration } from 'css-tree';
|
|
2
|
+
export interface CustomProperty {
|
|
3
|
+
syntax?: Declaration;
|
|
4
|
+
inherits?: Declaration;
|
|
5
|
+
initialValue?: Declaration;
|
|
6
|
+
}
|
|
7
|
+
export type CustomProperties = Map<string, CustomProperty>;
|
|
8
|
+
export declare function getCustomProperties(node: CssNode): CustomProperties;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { generate, walk } from "css-tree";
|
|
2
|
+
export function getCustomProperties(node) {
|
|
3
|
+
const customProperties = /* @__PURE__ */ new Map();
|
|
4
|
+
walk(node, {
|
|
5
|
+
visit: "Atrule",
|
|
6
|
+
enter(atrule) {
|
|
7
|
+
if (atrule.name === "property" && atrule.prelude) {
|
|
8
|
+
const prelude = generate(atrule.prelude);
|
|
9
|
+
if (prelude.startsWith("--")) {
|
|
10
|
+
let syntax;
|
|
11
|
+
let inherits;
|
|
12
|
+
let initialValue;
|
|
13
|
+
walk(atrule, {
|
|
14
|
+
visit: "Declaration",
|
|
15
|
+
enter(declaration) {
|
|
16
|
+
if (declaration.property === "syntax") {
|
|
17
|
+
syntax = declaration;
|
|
18
|
+
}
|
|
19
|
+
if (declaration.property === "inherits") {
|
|
20
|
+
inherits = declaration;
|
|
21
|
+
}
|
|
22
|
+
if (declaration.property === "initial-value") {
|
|
23
|
+
initialValue = declaration;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
customProperties.set(prelude, {
|
|
28
|
+
syntax,
|
|
29
|
+
inherits,
|
|
30
|
+
initialValue
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return customProperties;
|
|
37
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { find } from "css-tree";
|
|
2
|
+
export function isPartInlinable(part) {
|
|
3
|
+
const hasAtRuleInside = find(part, (node) => node.type === "Atrule") !== null;
|
|
4
|
+
const hasPseudoSelector = find(
|
|
5
|
+
part,
|
|
6
|
+
(node) => node.type === "PseudoClassSelector" || node.type === "PseudoElementSelector"
|
|
7
|
+
) !== null;
|
|
8
|
+
return !hasAtRuleInside && !hasPseudoSelector;
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { find } from "css-tree";
|
|
2
|
+
export function isRuleInlinable(rule) {
|
|
3
|
+
const hasAtRuleInside = find(rule, (node) => node.type === "Atrule") !== null;
|
|
4
|
+
const hasPseudoSelector = find(
|
|
5
|
+
rule,
|
|
6
|
+
(node) => node.type === "PseudoClassSelector" || node.type === "PseudoElementSelector"
|
|
7
|
+
) !== null;
|
|
8
|
+
return !hasAtRuleInside && !hasPseudoSelector;
|
|
9
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CssNode } from 'css-tree';
|
|
2
|
+
import type { CustomProperties } from './get-custom-properties.js';
|
|
3
|
+
/**
|
|
4
|
+
* Adapted from React Email's `make-inline-styles-for.ts`.
|
|
5
|
+
*
|
|
6
|
+
* The algorithm (local-variable resolution, custom-property fallbacks,
|
|
7
|
+
* empty-`--tw-*`-var stripping) is a faithful port. The only difference is the
|
|
8
|
+
* return shape: React Email produces `Record<reactStyleKey, value>` to spread
|
|
9
|
+
* onto a React element's `style` prop; this engine produces an ordered
|
|
10
|
+
* `Map<cssProperty, value>` of real kebab-case CSS declarations for a
|
|
11
|
+
* post-render HTML transform.
|
|
12
|
+
*
|
|
13
|
+
* A `Map` (rather than a joined string) is used because it preserves both:
|
|
14
|
+
* - insertion order — matching React's object-spread serialization order, and
|
|
15
|
+
* - per-property granularity — so the consumer can replicate React's
|
|
16
|
+
* `{ ...twStyle, ...authorStyle }` author-wins merge exactly (author overrides
|
|
17
|
+
* a tw property in place, appends new ones), which a flat string cannot
|
|
18
|
+
* express without re-parsing. Its `set` semantics (keep first position,
|
|
19
|
+
* overwrite value) match plain-object key assignment 1:1.
|
|
20
|
+
*/
|
|
21
|
+
export declare function makeInlineStylesFor(inlinableRules: CssNode[], customProperties: CustomProperties): Map<string, string>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { generate, walk } from "css-tree";
|
|
2
|
+
import { getCssProperty } from "./get-css-property.js";
|
|
3
|
+
import { stripEmptyTailwindVars } from "./strip-empty-tailwind-vars.js";
|
|
4
|
+
import { unwrapValue } from "./unwrap-value.js";
|
|
5
|
+
export function makeInlineStylesFor(inlinableRules, customProperties) {
|
|
6
|
+
const styles = /* @__PURE__ */ new Map();
|
|
7
|
+
const localVariableDeclarations = /* @__PURE__ */ new Map();
|
|
8
|
+
for (const rule of inlinableRules) {
|
|
9
|
+
walk(rule, {
|
|
10
|
+
visit: "Declaration",
|
|
11
|
+
enter(declaration) {
|
|
12
|
+
if (declaration.property.startsWith("--")) {
|
|
13
|
+
localVariableDeclarations.set(declaration.property, declaration);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
for (const rule of inlinableRules) {
|
|
19
|
+
walk(rule, {
|
|
20
|
+
visit: "Function",
|
|
21
|
+
enter(func, funcParentListItem) {
|
|
22
|
+
if (func.name === "var") {
|
|
23
|
+
let variableName;
|
|
24
|
+
walk(func, {
|
|
25
|
+
visit: "Identifier",
|
|
26
|
+
enter(identifier) {
|
|
27
|
+
variableName = identifier.name;
|
|
28
|
+
return this.break;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
if (variableName) {
|
|
32
|
+
const definition = localVariableDeclarations.get(variableName);
|
|
33
|
+
if (definition) {
|
|
34
|
+
funcParentListItem.data = unwrapValue(definition.value);
|
|
35
|
+
} else {
|
|
36
|
+
const customProperty = customProperties.get(variableName);
|
|
37
|
+
if (customProperty?.initialValue) {
|
|
38
|
+
funcParentListItem.data = unwrapValue(
|
|
39
|
+
customProperty.initialValue.value
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
walk(rule, {
|
|
48
|
+
visit: "Declaration",
|
|
49
|
+
enter(declaration) {
|
|
50
|
+
if (declaration.property.startsWith("--")) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
stripEmptyTailwindVars(declaration.value);
|
|
54
|
+
styles.set(
|
|
55
|
+
getCssProperty(declaration.property),
|
|
56
|
+
generate(declaration.value).trim() + (declaration.important ? "!important" : "")
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return styles;
|
|
62
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CssNode, type Declaration } from 'css-tree';
|
|
2
|
+
export interface VariableDefinition {
|
|
3
|
+
declaration: Declaration;
|
|
4
|
+
path: CssNode[];
|
|
5
|
+
variableName: string;
|
|
6
|
+
definition: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function resolveAllCssVariables(node: CssNode): void;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generate,
|
|
3
|
+
parse,
|
|
4
|
+
walk
|
|
5
|
+
} from "css-tree";
|
|
6
|
+
function doSelectorsIntersect(first, second) {
|
|
7
|
+
const firstStringified = generate(first);
|
|
8
|
+
const secondStringified = generate(second);
|
|
9
|
+
if (firstStringified === secondStringified) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
let hasSomeUniversal = false;
|
|
13
|
+
const walker = (node, _parentListItem, parentList) => {
|
|
14
|
+
if (hasSomeUniversal) return;
|
|
15
|
+
if (node.type === "PseudoClassSelector" && node.name === "root") {
|
|
16
|
+
hasSomeUniversal = true;
|
|
17
|
+
}
|
|
18
|
+
if (node.type === "TypeSelector" && node.name === "*" && parentList.size === 1) {
|
|
19
|
+
hasSomeUniversal = true;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
walk(first, walker);
|
|
23
|
+
walk(second, walker);
|
|
24
|
+
if (hasSomeUniversal) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
export function resolveAllCssVariables(node) {
|
|
30
|
+
const variableDefinitions = /* @__PURE__ */ new Set();
|
|
31
|
+
const variableUses = /* @__PURE__ */ new Set();
|
|
32
|
+
const path = [];
|
|
33
|
+
walk(node, {
|
|
34
|
+
leave() {
|
|
35
|
+
path.shift();
|
|
36
|
+
},
|
|
37
|
+
enter(node2) {
|
|
38
|
+
if (node2.type === "Declaration") {
|
|
39
|
+
const declaration = node2;
|
|
40
|
+
if (path.some(
|
|
41
|
+
(ancestor) => ancestor.type === "Atrule" && ancestor.name === "layer" && ancestor.prelude !== null && generate(ancestor.prelude).includes("properties")
|
|
42
|
+
)) {
|
|
43
|
+
path.unshift(node2);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (/--\S+/.test(declaration.property)) {
|
|
47
|
+
variableDefinitions.add({
|
|
48
|
+
declaration,
|
|
49
|
+
path: [...path],
|
|
50
|
+
variableName: declaration.property,
|
|
51
|
+
definition: generate(declaration.value)
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
const parseVariableUsesFrom = (fromNode) => {
|
|
55
|
+
walk(fromNode, {
|
|
56
|
+
visit: "Function",
|
|
57
|
+
enter(funcNode) {
|
|
58
|
+
if (funcNode.name === "var") {
|
|
59
|
+
const children = funcNode.children.toArray();
|
|
60
|
+
const name = generate(children[0]);
|
|
61
|
+
const fallback = children[2] ? generate(children[2]) : void 0;
|
|
62
|
+
variableUses.add({
|
|
63
|
+
declaration,
|
|
64
|
+
path: [...path],
|
|
65
|
+
fallback,
|
|
66
|
+
variableName: name,
|
|
67
|
+
raw: generate(funcNode)
|
|
68
|
+
});
|
|
69
|
+
if (fallback?.includes("var(")) {
|
|
70
|
+
const parsedFallback = parse(fallback, {
|
|
71
|
+
context: "value"
|
|
72
|
+
});
|
|
73
|
+
parseVariableUsesFrom(parsedFallback);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
parseVariableUsesFrom(declaration.value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
path.unshift(node2);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
for (const use of variableUses) {
|
|
86
|
+
let hasReplaced = false;
|
|
87
|
+
for (const definition of variableDefinitions) {
|
|
88
|
+
if (use.variableName !== definition.variableName) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (use.path[0]?.type === "Block" && use.path[1]?.type === "Atrule" && use.path[2]?.type === "Block" && use.path[3]?.type === "Rule" && definition.path[0].type === "Block" && definition.path[1].type === "Rule" && doSelectorsIntersect(use.path[3].prelude, definition.path[1].prelude)) {
|
|
92
|
+
use.declaration.value = parse(
|
|
93
|
+
generate(use.declaration.value).replaceAll(
|
|
94
|
+
use.raw,
|
|
95
|
+
definition.definition
|
|
96
|
+
),
|
|
97
|
+
{
|
|
98
|
+
context: "value"
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
hasReplaced = true;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
if (use.path[0]?.type === "Block" && use.path[1]?.type === "Rule" && definition.path[0]?.type === "Block" && definition.path[1]?.type === "Rule" && doSelectorsIntersect(use.path[1].prelude, definition.path[1].prelude)) {
|
|
105
|
+
use.declaration.value = parse(
|
|
106
|
+
generate(use.declaration.value).replaceAll(
|
|
107
|
+
use.raw,
|
|
108
|
+
definition.definition
|
|
109
|
+
),
|
|
110
|
+
{
|
|
111
|
+
context: "value"
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
hasReplaced = true;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
if (use.path[0]?.type === "Block" && use.path[1]?.type === "Atrule" && use.path[2]?.type === "Block" && use.path[3]?.type === "Rule" && definition.path[0]?.type === "Block" && definition.path[1]?.type === "Atrule" && definition.path[2]?.type === "Block" && definition.path[3]?.type === "Rule" && use.path[1].name === definition.path[1].name && (use.path[1].prelude ? definition.path[1].prelude ? generate(use.path[1].prelude) === generate(definition.path[1].prelude) : false : definition.path[1].prelude === null) && doSelectorsIntersect(use.path[3].prelude, definition.path[3].prelude)) {
|
|
118
|
+
use.declaration.value = parse(
|
|
119
|
+
generate(use.declaration.value).replaceAll(
|
|
120
|
+
use.raw,
|
|
121
|
+
definition.definition
|
|
122
|
+
),
|
|
123
|
+
{
|
|
124
|
+
context: "value"
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
hasReplaced = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (!hasReplaced && use.fallback) {
|
|
132
|
+
use.declaration.value = parse(
|
|
133
|
+
generate(use.declaration.value).replaceAll(use.raw, use.fallback),
|
|
134
|
+
{ context: "value" }
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { walk } from "css-tree";
|
|
2
|
+
export function resolveCalcExpressions(node) {
|
|
3
|
+
walk(node, {
|
|
4
|
+
visit: "Function",
|
|
5
|
+
enter(func, funcListItem) {
|
|
6
|
+
if (func.name === "calc") {
|
|
7
|
+
func.children.forEach((child, item) => {
|
|
8
|
+
const left = item.prev;
|
|
9
|
+
const right = item.next;
|
|
10
|
+
if (left && right && child.type === "Operator" && (left.data.type === "Dimension" || left.data.type === "Number" || left.data.type === "Percentage") && (right.data.type === "Dimension" || right.data.type === "Number" || right.data.type === "Percentage")) {
|
|
11
|
+
if (child.value === "*" || child.value === "/") {
|
|
12
|
+
const value = (() => {
|
|
13
|
+
if (child.value === "*") {
|
|
14
|
+
return String(
|
|
15
|
+
Number.parseFloat(left.data.value) * Number.parseFloat(right.data.value)
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
if (right.data.value === "0") {
|
|
19
|
+
return "0";
|
|
20
|
+
}
|
|
21
|
+
return String(
|
|
22
|
+
Number.parseFloat(left.data.value) / Number.parseFloat(right.data.value)
|
|
23
|
+
);
|
|
24
|
+
})();
|
|
25
|
+
if (left.data.type === "Dimension" && right.data.type === "Number") {
|
|
26
|
+
item.data = {
|
|
27
|
+
type: "Dimension",
|
|
28
|
+
unit: left.data.unit,
|
|
29
|
+
value
|
|
30
|
+
};
|
|
31
|
+
func.children.remove(left);
|
|
32
|
+
func.children.remove(right);
|
|
33
|
+
} else if (left.data.type === "Number" && right.data.type === "Dimension") {
|
|
34
|
+
item.data = {
|
|
35
|
+
type: "Dimension",
|
|
36
|
+
unit: right.data.unit,
|
|
37
|
+
value
|
|
38
|
+
};
|
|
39
|
+
func.children.remove(left);
|
|
40
|
+
func.children.remove(right);
|
|
41
|
+
} else if (left.data.type === "Number" && right.data.type === "Number") {
|
|
42
|
+
item.data = {
|
|
43
|
+
type: "Number",
|
|
44
|
+
value
|
|
45
|
+
};
|
|
46
|
+
func.children.remove(left);
|
|
47
|
+
func.children.remove(right);
|
|
48
|
+
} else if (left.data.type === "Dimension" && right.data.type === "Dimension" && left.data.unit === right.data.unit) {
|
|
49
|
+
if (child.value === "/") {
|
|
50
|
+
item.data = {
|
|
51
|
+
type: "Number",
|
|
52
|
+
value
|
|
53
|
+
};
|
|
54
|
+
} else {
|
|
55
|
+
item.data = {
|
|
56
|
+
type: "Dimension",
|
|
57
|
+
unit: left.data.unit,
|
|
58
|
+
value
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
func.children.remove(left);
|
|
62
|
+
func.children.remove(right);
|
|
63
|
+
} else if (left.data.type === "Percentage" && right.data.type === "Number") {
|
|
64
|
+
item.data = {
|
|
65
|
+
type: "Percentage",
|
|
66
|
+
value
|
|
67
|
+
};
|
|
68
|
+
func.children.remove(left);
|
|
69
|
+
func.children.remove(right);
|
|
70
|
+
} else if (left.data.type === "Number" && right.data.type === "Percentage") {
|
|
71
|
+
item.data = {
|
|
72
|
+
type: "Percentage",
|
|
73
|
+
value
|
|
74
|
+
};
|
|
75
|
+
func.children.remove(left);
|
|
76
|
+
func.children.remove(right);
|
|
77
|
+
} else if (left.data.type === "Percentage" && right.data.type === "Percentage") {
|
|
78
|
+
if (child.value === "/") {
|
|
79
|
+
item.data = {
|
|
80
|
+
type: "Number",
|
|
81
|
+
value
|
|
82
|
+
};
|
|
83
|
+
} else {
|
|
84
|
+
item.data = {
|
|
85
|
+
type: "Percentage",
|
|
86
|
+
value
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
func.children.remove(left);
|
|
90
|
+
func.children.remove(right);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
if (func.children.size === 1 && func.children.first) {
|
|
96
|
+
funcListItem.data = func.children.first;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type CssNode } from 'css-tree';
|
|
2
|
+
/**
|
|
3
|
+
* Meant to do all the things necessary, in a per-declaration basis, to have the best email client
|
|
4
|
+
* support possible.
|
|
5
|
+
*
|
|
6
|
+
* Here's the transformations it does so far:
|
|
7
|
+
* - convert all `rgb` with space-based syntax into a comma based one;
|
|
8
|
+
* - convert all `oklch` values into `rgb`;
|
|
9
|
+
* - convert all hex values into `rgb`;
|
|
10
|
+
* - convert `padding-inline` into `padding-left` and `padding-right`;
|
|
11
|
+
* - convert `padding-block` into `padding-top` and `padding-bottom`;
|
|
12
|
+
* - convert `margin-inline` into `margin-left` and `margin-right`;
|
|
13
|
+
* - convert `margin-block` into `margin-top` and `margin-bottom`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function sanitizeDeclarations(nodeContainingDeclarations: CssNode): void;
|