@styleframe/transpiler 1.0.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/.tsbuildinfo +1 -0
- package/CHANGELOG.md +12 -0
- package/package.json +43 -0
- package/src/constants.ts +4 -0
- package/src/consume/at-rule.test.ts +339 -0
- package/src/consume/at-rule.ts +34 -0
- package/src/consume/consume.test.ts +259 -0
- package/src/consume/consume.ts +60 -0
- package/src/consume/container.test.ts +501 -0
- package/src/consume/container.ts +73 -0
- package/src/consume/css.test.ts +184 -0
- package/src/consume/css.ts +17 -0
- package/src/consume/declarations.test.ts +210 -0
- package/src/consume/declarations.ts +17 -0
- package/src/consume/index.ts +12 -0
- package/src/consume/primitive.test.ts +52 -0
- package/src/consume/primitive.ts +16 -0
- package/src/consume/ref.test.ts +84 -0
- package/src/consume/ref.ts +22 -0
- package/src/consume/root.test.ts +353 -0
- package/src/consume/root.ts +19 -0
- package/src/consume/selector.test.ts +441 -0
- package/src/consume/selector.ts +17 -0
- package/src/consume/theme.test.ts +215 -0
- package/src/consume/theme.ts +15 -0
- package/src/consume/utility.test.ts +696 -0
- package/src/consume/utility.ts +31 -0
- package/src/consume/variable.test.ts +197 -0
- package/src/consume/variable.ts +20 -0
- package/src/defaults.ts +21 -0
- package/src/generator/genAtRuleQuery.test.ts +148 -0
- package/src/generator/genAtRuleQuery.ts +3 -0
- package/src/generator/genDeclaration.test.ts +283 -0
- package/src/generator/genDeclaration.ts +9 -0
- package/src/generator/genDeclarationsBlock.test.ts +278 -0
- package/src/generator/genDeclarationsBlock.ts +7 -0
- package/src/generator/genDeclareVariable.test.ts +323 -0
- package/src/generator/genDeclareVariable.ts +6 -0
- package/src/generator/genInlineAtRule.test.ts +351 -0
- package/src/generator/genInlineAtRule.ts +5 -0
- package/src/generator/genReferenceVariable.test.ts +392 -0
- package/src/generator/genReferenceVariable.ts +5 -0
- package/src/generator/genSafePropertyName.test.ts +489 -0
- package/src/generator/genSafePropertyName.ts +5 -0
- package/src/generator/genSafeVariableName.test.ts +358 -0
- package/src/generator/genSafeVariableName.ts +21 -0
- package/src/generator/genSelector.test.ts +357 -0
- package/src/generator/genSelector.ts +5 -0
- package/src/generator/index.ts +9 -0
- package/src/index.ts +6 -0
- package/src/transpile.test.ts +825 -0
- package/src/transpile.ts +21 -0
- package/src/types.ts +15 -0
- package/src/utils.ts +18 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +7 -0
- package/vite.config.ts +5 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { Root, StyleframeOptions } from "@styleframe/core";
|
|
2
|
+
import {
|
|
3
|
+
createRefFunction,
|
|
4
|
+
createRoot,
|
|
5
|
+
createVariableFunction,
|
|
6
|
+
} from "@styleframe/core";
|
|
7
|
+
import { createVariableConsumer } from "./variable";
|
|
8
|
+
import { consume } from "./consume";
|
|
9
|
+
|
|
10
|
+
describe("createVariableConsumer", () => {
|
|
11
|
+
let root: Root;
|
|
12
|
+
let variable: ReturnType<typeof createVariableFunction>;
|
|
13
|
+
let ref: ReturnType<typeof createRefFunction>;
|
|
14
|
+
|
|
15
|
+
const consumeVariable = createVariableConsumer(consume);
|
|
16
|
+
const options: StyleframeOptions = {};
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
root = createRoot();
|
|
20
|
+
variable = createVariableFunction(root, root);
|
|
21
|
+
ref = createRefFunction(root, root);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should convert a basic variable to CSS variable declaration", () => {
|
|
25
|
+
const colorVar = variable("color-primary", "#006cff");
|
|
26
|
+
const result = consumeVariable(colorVar, options);
|
|
27
|
+
|
|
28
|
+
expect(result).toBe("--color-primary: #006cff;");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should handle variable with dashed names", () => {
|
|
32
|
+
const spacingVar = variable("spacing--md", "1rem");
|
|
33
|
+
const result = consumeVariable(spacingVar, options);
|
|
34
|
+
|
|
35
|
+
expect(result).toBe("--spacing--md: 1rem;");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should convert variable with numeric value", () => {
|
|
39
|
+
const zIndexVar = variable("z-index-header", 100);
|
|
40
|
+
const result = consumeVariable(zIndexVar, options);
|
|
41
|
+
|
|
42
|
+
expect(result).toBe("--z-index-header: 100;");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should handle variable with empty string value", () => {
|
|
46
|
+
const emptyVar = variable("empty-var", "");
|
|
47
|
+
const result = consumeVariable(emptyVar, options);
|
|
48
|
+
|
|
49
|
+
expect(result).toBe("--empty-var: ;");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should respect prefix in options if provided", () => {
|
|
53
|
+
const prefixOptions: StyleframeOptions = {
|
|
54
|
+
variables: {
|
|
55
|
+
name: ({ name }) => `--sf-${name}`,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const colorVar = variable("color-primary", "#006cff");
|
|
60
|
+
const result = consumeVariable(colorVar, prefixOptions);
|
|
61
|
+
|
|
62
|
+
expect(result).toBe("--sf-color-primary: #006cff;");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should handle variable with complex values like calc expressions", () => {
|
|
66
|
+
const calcVar = variable("sidebar-width", "calc(100% - 200px)");
|
|
67
|
+
const result = consumeVariable(calcVar, options);
|
|
68
|
+
|
|
69
|
+
expect(result).toBe("--sidebar-width: calc(100% - 200px);");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should handle variable referencing another variable", () => {
|
|
73
|
+
// Create a base variable
|
|
74
|
+
const baseColorVar = variable("color-base", "#006cff");
|
|
75
|
+
|
|
76
|
+
// Create a reference to the base variable
|
|
77
|
+
const baseColorRef = ref(baseColorVar);
|
|
78
|
+
|
|
79
|
+
// Create a variable that uses the reference
|
|
80
|
+
const colorPrimaryVar = variable("color-primary", baseColorRef);
|
|
81
|
+
|
|
82
|
+
const result = consumeVariable(colorPrimaryVar, options);
|
|
83
|
+
|
|
84
|
+
expect(result).toBe("--color-primary: var(--color-base);");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should handle variable with string reference", () => {
|
|
88
|
+
// Create a reference using a string name
|
|
89
|
+
const colorRef = ref("color-base");
|
|
90
|
+
|
|
91
|
+
// Create a variable that uses the reference
|
|
92
|
+
const colorPrimaryVar = variable("color-primary", colorRef);
|
|
93
|
+
|
|
94
|
+
const result = consumeVariable(colorPrimaryVar, options);
|
|
95
|
+
|
|
96
|
+
expect(result).toBe("--color-primary: var(--color-base);");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should handle variable with reference that has fallback", () => {
|
|
100
|
+
// Create a reference with fallback value
|
|
101
|
+
const fontSizeRef = ref("font-size", "16px");
|
|
102
|
+
|
|
103
|
+
// Create a variable that uses the reference with fallback
|
|
104
|
+
const bodyFontVar = variable("body-font-size", fontSizeRef);
|
|
105
|
+
|
|
106
|
+
const result = consumeVariable(bodyFontVar, options);
|
|
107
|
+
|
|
108
|
+
expect(result).toBe("--body-font-size: var(--font-size, 16px);");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should handle variable with null value", () => {
|
|
112
|
+
const nullVar = variable("null-var", null);
|
|
113
|
+
const result = consumeVariable(nullVar, options);
|
|
114
|
+
|
|
115
|
+
expect(result).toBe("--null-var: ;");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should handle variable with undefined value", () => {
|
|
119
|
+
const undefinedVar = variable("undefined-var", undefined);
|
|
120
|
+
const result = consumeVariable(undefinedVar, options);
|
|
121
|
+
|
|
122
|
+
expect(result).toBe("--undefined-var: ;");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("should handle variable with boolean value", () => {
|
|
126
|
+
const boolVar = variable("feature-enabled", true);
|
|
127
|
+
const result = consumeVariable(boolVar, options);
|
|
128
|
+
|
|
129
|
+
expect(result).toBe("--feature-enabled: true;");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should handle variable with array value", () => {
|
|
133
|
+
const arrayVar = variable("font-stack", [
|
|
134
|
+
"Helvetica",
|
|
135
|
+
"Arial",
|
|
136
|
+
"sans-serif",
|
|
137
|
+
]);
|
|
138
|
+
const result = consumeVariable(arrayVar, options);
|
|
139
|
+
|
|
140
|
+
expect(result).toBe("--font-stack: Helvetica,Arial,sans-serif;");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("should handle variable with complex CSS values", () => {
|
|
144
|
+
const shadowVar = variable(
|
|
145
|
+
"box-shadow",
|
|
146
|
+
"0 2px 4px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.05)",
|
|
147
|
+
);
|
|
148
|
+
const result = consumeVariable(shadowVar, options);
|
|
149
|
+
|
|
150
|
+
expect(result).toBe(
|
|
151
|
+
"--box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.05);",
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should handle chained variable references", () => {
|
|
156
|
+
// Create a base color variable
|
|
157
|
+
const baseColorVar = variable("color-base", "#006cff");
|
|
158
|
+
|
|
159
|
+
// Create an intermediate variable that references the base
|
|
160
|
+
const primaryColorVar = variable("color-primary", ref(baseColorVar));
|
|
161
|
+
|
|
162
|
+
// Create a final variable that references the intermediate one
|
|
163
|
+
const buttonColorVar = variable("button-color", ref(primaryColorVar));
|
|
164
|
+
|
|
165
|
+
const result = consumeVariable(buttonColorVar, options);
|
|
166
|
+
|
|
167
|
+
expect(result).toBe("--button-color: var(--color-primary);");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("should handle variable with object value by converting to string", () => {
|
|
171
|
+
// @ts-expect-error - Ignoring type to test object handling
|
|
172
|
+
const objVar = variable("object-var", { key: "value" });
|
|
173
|
+
const result = consumeVariable(objVar, options);
|
|
174
|
+
|
|
175
|
+
// Object should be converted to string
|
|
176
|
+
expect(result).toBe("--object-var: [object Object];");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("should apply variable prefix consistently when referencing other variables", () => {
|
|
180
|
+
const prefixOptions: StyleframeOptions = {
|
|
181
|
+
variables: {
|
|
182
|
+
name: ({ name }) => `--sf-${name}`,
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Create a base color variable
|
|
187
|
+
const baseColorVar = variable("color-base", "#006cff");
|
|
188
|
+
|
|
189
|
+
// Create a variable that references the base
|
|
190
|
+
const primaryColorVar = variable("color-primary", ref(baseColorVar));
|
|
191
|
+
|
|
192
|
+
const result = consumeVariable(primaryColorVar, prefixOptions);
|
|
193
|
+
|
|
194
|
+
// Both the variable name and the referenced variable should have the prefix
|
|
195
|
+
expect(result).toBe("--sf-color-primary: var(--sf-color-base);");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consumes a variable instance, equivalent to setting a CSS variable
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { StyleframeOptions, Variable } from "@styleframe/core";
|
|
6
|
+
import { defaultVariableNameFn } from "../defaults";
|
|
7
|
+
import { genDeclareVariable } from "../generator";
|
|
8
|
+
import type { ConsumeFunction } from "../types";
|
|
9
|
+
|
|
10
|
+
export function createVariableConsumer(consume: ConsumeFunction) {
|
|
11
|
+
return function consumeVariable(
|
|
12
|
+
instance: Variable,
|
|
13
|
+
options: StyleframeOptions,
|
|
14
|
+
): string {
|
|
15
|
+
const variableNameFn = options.variables?.name ?? defaultVariableNameFn;
|
|
16
|
+
const variableName = variableNameFn({ name: instance.name });
|
|
17
|
+
|
|
18
|
+
return genDeclareVariable(variableName, consume(instance.value, options));
|
|
19
|
+
};
|
|
20
|
+
}
|
package/src/defaults.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ThemeSelectorFn,
|
|
3
|
+
UtilitySelectorFn,
|
|
4
|
+
VariableNameFn,
|
|
5
|
+
} from "@styleframe/core";
|
|
6
|
+
|
|
7
|
+
export const defaultThemeSelectorFn: ThemeSelectorFn = ({ name }) => {
|
|
8
|
+
return `[data-theme="${name}"]`;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const defaultUtilitySelectorFn: UtilitySelectorFn = ({
|
|
12
|
+
name,
|
|
13
|
+
value,
|
|
14
|
+
modifiers,
|
|
15
|
+
}) => {
|
|
16
|
+
return `._${[...modifiers, name, value].filter(Boolean).join("\\:")}`;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const defaultVariableNameFn: VariableNameFn = ({ name }) => {
|
|
20
|
+
return name;
|
|
21
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { genAtRuleQuery } from "./genAtRuleQuery";
|
|
3
|
+
|
|
4
|
+
describe("genAtRuleQuery", () => {
|
|
5
|
+
it("should generate a basic media query", () => {
|
|
6
|
+
const result = genAtRuleQuery("media", "(min-width: 768px)");
|
|
7
|
+
expect(result).toBe("@media (min-width: 768px)");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("should generate a supports query", () => {
|
|
11
|
+
const result = genAtRuleQuery("supports", "(display: grid)");
|
|
12
|
+
expect(result).toBe("@supports (display: grid)");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should generate a container query", () => {
|
|
16
|
+
const result = genAtRuleQuery("container", "sidebar (min-width: 400px)");
|
|
17
|
+
expect(result).toBe("@container sidebar (min-width: 400px)");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should generate a layer rule", () => {
|
|
21
|
+
const result = genAtRuleQuery("layer", "utilities");
|
|
22
|
+
expect(result).toBe("@layer utilities");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should generate a scope rule", () => {
|
|
26
|
+
const result = genAtRuleQuery("scope", "(.component)");
|
|
27
|
+
expect(result).toBe("@scope (.component)");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("should handle complex media queries", () => {
|
|
31
|
+
const result = genAtRuleQuery(
|
|
32
|
+
"media",
|
|
33
|
+
"screen and (min-width: 768px) and (max-width: 1024px)",
|
|
34
|
+
);
|
|
35
|
+
expect(result).toBe(
|
|
36
|
+
"@media screen and (min-width: 768px) and (max-width: 1024px)",
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should handle print media type", () => {
|
|
41
|
+
const result = genAtRuleQuery("media", "print");
|
|
42
|
+
expect(result).toBe("@media print");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should handle import rules", () => {
|
|
46
|
+
const result = genAtRuleQuery("import", "url('styles.css')");
|
|
47
|
+
expect(result).toBe("@import url('styles.css')");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should handle charset rules", () => {
|
|
51
|
+
const result = genAtRuleQuery("charset", '"UTF-8"');
|
|
52
|
+
expect(result).toBe('@charset "UTF-8"');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should handle namespace rules", () => {
|
|
56
|
+
const result = genAtRuleQuery(
|
|
57
|
+
"namespace",
|
|
58
|
+
"url(http://www.w3.org/1999/xhtml)",
|
|
59
|
+
);
|
|
60
|
+
expect(result).toBe("@namespace url(http://www.w3.org/1999/xhtml)");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should handle page rules", () => {
|
|
64
|
+
const result = genAtRuleQuery("page", ":first");
|
|
65
|
+
expect(result).toBe("@page :first");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should handle font-face (even though typically used without a rule)", () => {
|
|
69
|
+
const result = genAtRuleQuery("font-face", "");
|
|
70
|
+
expect(result).toBe("@font-face");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should handle keyframes", () => {
|
|
74
|
+
const result = genAtRuleQuery("keyframes", "slideIn");
|
|
75
|
+
expect(result).toBe("@keyframes slideIn");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should handle counter-style", () => {
|
|
79
|
+
const result = genAtRuleQuery("counter-style", "custom-counter");
|
|
80
|
+
expect(result).toBe("@counter-style custom-counter");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should handle property rules", () => {
|
|
84
|
+
const result = genAtRuleQuery("property", "--my-color");
|
|
85
|
+
expect(result).toBe("@property --my-color");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should handle document rules (deprecated but still valid)", () => {
|
|
89
|
+
const result = genAtRuleQuery("document", "url(http://www.example.com/)");
|
|
90
|
+
expect(result).toBe("@document url(http://www.example.com/)");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should handle empty rule string", () => {
|
|
94
|
+
const result = genAtRuleQuery("media", "");
|
|
95
|
+
expect(result).toBe("@media");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should handle whitespace in rule string", () => {
|
|
99
|
+
const result = genAtRuleQuery("media", " (min-width: 768px) ");
|
|
100
|
+
expect(result).toBe("@media (min-width: 768px) ");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should handle special characters in identifier", () => {
|
|
104
|
+
const result = genAtRuleQuery("-webkit-keyframes", "bounce");
|
|
105
|
+
expect(result).toBe("@-webkit-keyframes bounce");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should handle multiple conditions in supports", () => {
|
|
109
|
+
const result = genAtRuleQuery(
|
|
110
|
+
"supports",
|
|
111
|
+
"(display: grid) and (aspect-ratio: 1/1)",
|
|
112
|
+
);
|
|
113
|
+
expect(result).toBe("@supports (display: grid) and (aspect-ratio: 1/1)");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should handle not operator in supports", () => {
|
|
117
|
+
const result = genAtRuleQuery("supports", "not (display: grid)");
|
|
118
|
+
expect(result).toBe("@supports not (display: grid)");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should handle or operator in supports", () => {
|
|
122
|
+
const result = genAtRuleQuery(
|
|
123
|
+
"supports",
|
|
124
|
+
"(display: flex) or (display: grid)",
|
|
125
|
+
);
|
|
126
|
+
expect(result).toBe("@supports (display: flex) or (display: grid)");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("should preserve exact spacing in complex queries", () => {
|
|
130
|
+
const result = genAtRuleQuery(
|
|
131
|
+
"media",
|
|
132
|
+
"(prefers-color-scheme: dark) and (min-width: 900px)",
|
|
133
|
+
);
|
|
134
|
+
expect(result).toBe(
|
|
135
|
+
"@media (prefers-color-scheme: dark) and (min-width: 900px)",
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should handle nested parentheses in queries", () => {
|
|
140
|
+
const result = genAtRuleQuery(
|
|
141
|
+
"supports",
|
|
142
|
+
"((display: flex) and (gap: 1rem)) or (display: grid)",
|
|
143
|
+
);
|
|
144
|
+
expect(result).toBe(
|
|
145
|
+
"@supports ((display: flex) and (gap: 1rem)) or (display: grid)",
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { genDeclaration } from "./genDeclaration";
|
|
3
|
+
|
|
4
|
+
describe("genDeclaration", () => {
|
|
5
|
+
it("should generate a basic CSS declaration", () => {
|
|
6
|
+
const result = genDeclaration("color", "red");
|
|
7
|
+
expect(result).toBe("color: red;");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("should generate a declaration with hex color value", () => {
|
|
11
|
+
const result = genDeclaration("background-color", "#ff0000");
|
|
12
|
+
expect(result).toBe("background-color: #ff0000;");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should generate a declaration with rgb value", () => {
|
|
16
|
+
const result = genDeclaration("color", "rgb(255, 0, 0)");
|
|
17
|
+
expect(result).toBe("color: rgb(255, 0, 0);");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should generate a declaration with rgba value", () => {
|
|
21
|
+
const result = genDeclaration("background", "rgba(0, 0, 0, 0.5)");
|
|
22
|
+
expect(result).toBe("background: rgba(0, 0, 0, 0.5);");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("should generate a declaration with hsl value", () => {
|
|
26
|
+
const result = genDeclaration("color", "hsl(120, 100%, 50%)");
|
|
27
|
+
expect(result).toBe("color: hsl(120, 100%, 50%);");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("should generate a declaration with pixel value", () => {
|
|
31
|
+
const result = genDeclaration("width", "100px");
|
|
32
|
+
expect(result).toBe("width: 100px;");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should generate a declaration with percentage value", () => {
|
|
36
|
+
const result = genDeclaration("width", "50%");
|
|
37
|
+
expect(result).toBe("width: 50%;");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should generate a declaration with em value", () => {
|
|
41
|
+
const result = genDeclaration("font-size", "1.5em");
|
|
42
|
+
expect(result).toBe("font-size: 1.5em;");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should generate a declaration with rem value", () => {
|
|
46
|
+
const result = genDeclaration("margin", "2rem");
|
|
47
|
+
expect(result).toBe("margin: 2rem;");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should generate a declaration with viewport units", () => {
|
|
51
|
+
const result = genDeclaration("height", "100vh");
|
|
52
|
+
expect(result).toBe("height: 100vh;");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should generate a declaration with multiple values", () => {
|
|
56
|
+
const result = genDeclaration("margin", "10px 20px 30px 40px");
|
|
57
|
+
expect(result).toBe("margin: 10px 20px 30px 40px;");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should generate a declaration with important flag", () => {
|
|
61
|
+
const result = genDeclaration("color", "blue !important");
|
|
62
|
+
expect(result).toBe("color: blue !important;");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should handle CSS custom properties", () => {
|
|
66
|
+
const result = genDeclaration("--primary-color", "#007bff");
|
|
67
|
+
expect(result).toBe("--primary-color: #007bff;");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should handle var() function", () => {
|
|
71
|
+
const result = genDeclaration("color", "var(--primary-color)");
|
|
72
|
+
expect(result).toBe("color: var(--primary-color);");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should handle var() with fallback", () => {
|
|
76
|
+
const result = genDeclaration("color", "var(--text-color, black)");
|
|
77
|
+
expect(result).toBe("color: var(--text-color, black);");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should handle calc() function", () => {
|
|
81
|
+
const result = genDeclaration("width", "calc(100% - 20px)");
|
|
82
|
+
expect(result).toBe("width: calc(100% - 20px);");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should handle url() function", () => {
|
|
86
|
+
const result = genDeclaration("background-image", "url('image.jpg')");
|
|
87
|
+
expect(result).toBe("background-image: url('image.jpg');");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("should handle linear-gradient", () => {
|
|
91
|
+
const result = genDeclaration(
|
|
92
|
+
"background",
|
|
93
|
+
"linear-gradient(to right, red, blue)",
|
|
94
|
+
);
|
|
95
|
+
expect(result).toBe("background: linear-gradient(to right, red, blue);");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should handle transform functions", () => {
|
|
99
|
+
const result = genDeclaration("transform", "rotate(45deg) scale(1.5)");
|
|
100
|
+
expect(result).toBe("transform: rotate(45deg) scale(1.5);");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should handle transition shorthand", () => {
|
|
104
|
+
const result = genDeclaration("transition", "all 0.3s ease-in-out");
|
|
105
|
+
expect(result).toBe("transition: all 0.3s ease-in-out;");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should handle box-shadow", () => {
|
|
109
|
+
const result = genDeclaration("box-shadow", "0 4px 6px rgba(0, 0, 0, 0.1)");
|
|
110
|
+
expect(result).toBe("box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should handle font shorthand", () => {
|
|
114
|
+
const result = genDeclaration(
|
|
115
|
+
"font",
|
|
116
|
+
"italic bold 16px/1.5 Arial, sans-serif",
|
|
117
|
+
);
|
|
118
|
+
expect(result).toBe("font: italic bold 16px/1.5 Arial, sans-serif;");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should handle grid template", () => {
|
|
122
|
+
const result = genDeclaration("grid-template-columns", "repeat(3, 1fr)");
|
|
123
|
+
expect(result).toBe("grid-template-columns: repeat(3, 1fr);");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("should handle flex shorthand", () => {
|
|
127
|
+
const result = genDeclaration("flex", "1 1 auto");
|
|
128
|
+
expect(result).toBe("flex: 1 1 auto;");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should handle animation shorthand", () => {
|
|
132
|
+
const result = genDeclaration("animation", "slide 2s ease-in-out infinite");
|
|
133
|
+
expect(result).toBe("animation: slide 2s ease-in-out infinite;");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should handle vendor prefixes", () => {
|
|
137
|
+
const result = genDeclaration("-webkit-transform", "rotate(45deg)");
|
|
138
|
+
expect(result).toBe("-webkit-transform: rotate(45deg);");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should handle empty string value", () => {
|
|
142
|
+
const result = genDeclaration("content", "");
|
|
143
|
+
expect(result).toBe("content: ;");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should handle quoted string value", () => {
|
|
147
|
+
const result = genDeclaration("content", '"Hello World"');
|
|
148
|
+
expect(result).toBe('content: "Hello World";');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("should handle single quoted string value", () => {
|
|
152
|
+
const result = genDeclaration(
|
|
153
|
+
"font-family",
|
|
154
|
+
"'Helvetica Neue', sans-serif",
|
|
155
|
+
);
|
|
156
|
+
expect(result).toBe("font-family: 'Helvetica Neue', sans-serif;");
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("should preserve whitespace in value", () => {
|
|
160
|
+
const result = genDeclaration("margin", " 10px 20px ");
|
|
161
|
+
expect(result).toBe("margin: 10px 20px ;");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should handle special CSS keywords", () => {
|
|
165
|
+
const result = genDeclaration("display", "none");
|
|
166
|
+
expect(result).toBe("display: none;");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("should handle inherit keyword", () => {
|
|
170
|
+
const result = genDeclaration("color", "inherit");
|
|
171
|
+
expect(result).toBe("color: inherit;");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("should handle initial keyword", () => {
|
|
175
|
+
const result = genDeclaration("margin", "initial");
|
|
176
|
+
expect(result).toBe("margin: initial;");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("should handle unset keyword", () => {
|
|
180
|
+
const result = genDeclaration("padding", "unset");
|
|
181
|
+
expect(result).toBe("padding: unset;");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("should handle revert keyword", () => {
|
|
185
|
+
const result = genDeclaration("font-size", "revert");
|
|
186
|
+
expect(result).toBe("font-size: revert;");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should handle auto keyword", () => {
|
|
190
|
+
const result = genDeclaration("margin", "auto");
|
|
191
|
+
expect(result).toBe("margin: auto;");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("should handle multiple box-shadow values", () => {
|
|
195
|
+
const result = genDeclaration(
|
|
196
|
+
"box-shadow",
|
|
197
|
+
"0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)",
|
|
198
|
+
);
|
|
199
|
+
expect(result).toBe(
|
|
200
|
+
"box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);",
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("should handle CSS math functions", () => {
|
|
205
|
+
const result = genDeclaration("width", "min(100%, 500px)");
|
|
206
|
+
expect(result).toBe("width: min(100%, 500px);");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("should handle clamp function", () => {
|
|
210
|
+
const result = genDeclaration("font-size", "clamp(1rem, 2vw, 3rem)");
|
|
211
|
+
expect(result).toBe("font-size: clamp(1rem, 2vw, 3rem);");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("should handle max function", () => {
|
|
215
|
+
const result = genDeclaration("width", "max(50%, 300px)");
|
|
216
|
+
expect(result).toBe("width: max(50%, 300px);");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("should handle filter functions", () => {
|
|
220
|
+
const result = genDeclaration("filter", "blur(5px) brightness(1.5)");
|
|
221
|
+
expect(result).toBe("filter: blur(5px) brightness(1.5);");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("should handle cubic-bezier timing function", () => {
|
|
225
|
+
const result = genDeclaration(
|
|
226
|
+
"transition-timing-function",
|
|
227
|
+
"cubic-bezier(0.4, 0, 0.2, 1)",
|
|
228
|
+
);
|
|
229
|
+
expect(result).toBe(
|
|
230
|
+
"transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);",
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("should handle steps timing function", () => {
|
|
235
|
+
const result = genDeclaration("animation-timing-function", "steps(4, end)");
|
|
236
|
+
expect(result).toBe("animation-timing-function: steps(4, end);");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should handle negative values", () => {
|
|
240
|
+
const result = genDeclaration("margin-top", "-10px");
|
|
241
|
+
expect(result).toBe("margin-top: -10px;");
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should handle decimal values", () => {
|
|
245
|
+
const result = genDeclaration("opacity", "0.75");
|
|
246
|
+
expect(result).toBe("opacity: 0.75;");
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("should handle zero without unit", () => {
|
|
250
|
+
const result = genDeclaration("margin", "0");
|
|
251
|
+
expect(result).toBe("margin: 0;");
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("should handle CSS Grid line names", () => {
|
|
255
|
+
const result = genDeclaration(
|
|
256
|
+
"grid-template-rows",
|
|
257
|
+
"[header-start] 100px [header-end content-start] 1fr [content-end]",
|
|
258
|
+
);
|
|
259
|
+
expect(result).toBe(
|
|
260
|
+
"grid-template-rows: [header-start] 100px [header-end content-start] 1fr [content-end];",
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("should handle attr() function", () => {
|
|
265
|
+
const result = genDeclaration("content", "attr(data-label)");
|
|
266
|
+
expect(result).toBe("content: attr(data-label);");
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("should handle counter() function", () => {
|
|
270
|
+
const result = genDeclaration("content", "counter(section)");
|
|
271
|
+
expect(result).toBe("content: counter(section);");
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("should handle env() function", () => {
|
|
275
|
+
const result = genDeclaration("padding-top", "env(safe-area-inset-top)");
|
|
276
|
+
expect(result).toBe("padding-top: env(safe-area-inset-top);");
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("should handle complex nested calc", () => {
|
|
280
|
+
const result = genDeclaration("width", "calc(100% - calc(20px + 2em))");
|
|
281
|
+
expect(result).toBe("width: calc(100% - calc(20px + 2em));");
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { genSafePropertyName } from "./genSafePropertyName";
|
|
2
|
+
|
|
3
|
+
export function genDeclaration(property: string, value: string): string {
|
|
4
|
+
const normalizedProperty = property.startsWith("--")
|
|
5
|
+
? property
|
|
6
|
+
: genSafePropertyName(property);
|
|
7
|
+
|
|
8
|
+
return `${normalizedProperty}: ${value};`;
|
|
9
|
+
}
|