@hypergood/css-core 0.0.2 → 0.0.4
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/dist/plugin.cjs +145 -25
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.js +145 -25
- package/dist/plugin.js.map +1 -1
- package/package.json +2 -1
- package/src/evaluate.ts +146 -21
- package/src/plugin.ts +13 -4
- package/src/print.ts +1 -9
- package/tests/evaluate.test.ts +76 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypergood/css-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@babel/core": "^7.28.5",
|
|
30
30
|
"@babel/helper-module-imports": "^7.27.1",
|
|
31
|
+
"@babel/parser": "^7.29.3",
|
|
31
32
|
"@babel/preset-typescript": "^7.28.5",
|
|
32
33
|
"@types/babel__core": "^7.20.5",
|
|
33
34
|
"@types/babel__helper-module-imports": "^7.18.3",
|
package/src/evaluate.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
export type EvaluationResult = {
|
|
2
2
|
confident: boolean;
|
|
3
3
|
value: any;
|
|
4
|
+
badNode?: babel.types.Node | null | undefined;
|
|
4
5
|
};
|
|
5
6
|
|
|
6
|
-
const UNKNOWN
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const UNKNOWN = (badNode?: babel.types.Node | null | undefined) =>
|
|
8
|
+
({
|
|
9
|
+
confident: false,
|
|
10
|
+
value: undefined,
|
|
11
|
+
badNode,
|
|
12
|
+
}) as EvaluationResult;
|
|
10
13
|
const known = (value: any) => ({
|
|
11
14
|
confident: true,
|
|
12
15
|
value,
|
|
@@ -16,25 +19,61 @@ export function evaluateBabelExpression(
|
|
|
16
19
|
node: babel.types.Node | null | undefined,
|
|
17
20
|
knownConstants: Record<string, any>,
|
|
18
21
|
): EvaluationResult {
|
|
19
|
-
if (!node) return UNKNOWN;
|
|
22
|
+
if (!node) return UNKNOWN(node);
|
|
20
23
|
switch (node.type) {
|
|
24
|
+
case "ArrayExpression":
|
|
25
|
+
return evaluateArrayExpression(node, knownConstants);
|
|
21
26
|
case "BinaryExpression":
|
|
22
27
|
return evaluateBinaryExpression(node, knownConstants);
|
|
23
28
|
case "Identifier":
|
|
24
29
|
return evaluateIdentifier(node, knownConstants);
|
|
25
|
-
case "NumericLiteral":
|
|
26
|
-
return evaluateNumericLiteral(node);
|
|
27
30
|
case "MemberExpression":
|
|
28
31
|
return evaluateMemberExpression(node, knownConstants);
|
|
32
|
+
case "NullLiteral":
|
|
33
|
+
return evaluateNullLiteral(node);
|
|
34
|
+
case "NumericLiteral":
|
|
35
|
+
return evaluateNumericLiteral(node);
|
|
29
36
|
case "ObjectExpression":
|
|
30
37
|
return evaluateObjectExpression(node, knownConstants);
|
|
31
38
|
case "StringLiteral":
|
|
32
39
|
return evaluateStringLiteral(node);
|
|
33
40
|
case "TemplateLiteral":
|
|
34
41
|
return evaluateTemplateLiteral(node, knownConstants);
|
|
42
|
+
case "UnaryExpression":
|
|
43
|
+
return evaluateUnaryExpression(node, knownConstants);
|
|
35
44
|
default:
|
|
36
|
-
return UNKNOWN;
|
|
45
|
+
return UNKNOWN(node);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function evaluateArrayExpression(
|
|
50
|
+
node: babel.types.ArrayExpression,
|
|
51
|
+
knownConstants: Record<string, any>,
|
|
52
|
+
): EvaluationResult {
|
|
53
|
+
const elements: any[] = [];
|
|
54
|
+
for (const item of node.elements) {
|
|
55
|
+
if (!item) return UNKNOWN(node);
|
|
56
|
+
if (item.type === "SpreadElement") {
|
|
57
|
+
const spreadElements = evaluateBabelExpression(
|
|
58
|
+
item.argument,
|
|
59
|
+
knownConstants,
|
|
60
|
+
);
|
|
61
|
+
if (spreadElements.confident) {
|
|
62
|
+
elements.push(...spreadElements.value);
|
|
63
|
+
continue;
|
|
64
|
+
} else {
|
|
65
|
+
return UNKNOWN(spreadElements.badNode);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const result = evaluateBabelExpression(item, knownConstants);
|
|
69
|
+
if (result.confident) {
|
|
70
|
+
elements.push(result.value);
|
|
71
|
+
} else {
|
|
72
|
+
return UNKNOWN(result.badNode);
|
|
73
|
+
}
|
|
37
74
|
}
|
|
75
|
+
|
|
76
|
+
return known(elements);
|
|
38
77
|
}
|
|
39
78
|
|
|
40
79
|
function evaluateBinaryExpression(
|
|
@@ -44,17 +83,56 @@ function evaluateBinaryExpression(
|
|
|
44
83
|
const left = evaluateBabelExpression(node.left, knownConstants);
|
|
45
84
|
const right = evaluateBabelExpression(node.right, knownConstants);
|
|
46
85
|
|
|
47
|
-
if (!left.confident
|
|
86
|
+
if (!left.confident) return UNKNOWN(left.badNode);
|
|
87
|
+
if (!right.confident) return UNKNOWN(right.badNode);
|
|
48
88
|
|
|
49
89
|
switch (node.operator) {
|
|
50
|
-
case "*":
|
|
51
|
-
return known(left.value * right.value);
|
|
52
90
|
case "+":
|
|
53
91
|
return known(left.value + right.value);
|
|
54
92
|
case "-":
|
|
55
93
|
return known(left.value - right.value);
|
|
94
|
+
case "/":
|
|
95
|
+
return known(left.value / right.value);
|
|
96
|
+
case "%":
|
|
97
|
+
return known(left.value % right.value);
|
|
98
|
+
case "*":
|
|
99
|
+
return known(left.value * right.value);
|
|
100
|
+
case "**":
|
|
101
|
+
return known(left.value ** right.value);
|
|
102
|
+
case "&":
|
|
103
|
+
return known(left.value & right.value);
|
|
104
|
+
case "|":
|
|
105
|
+
return known(left.value | right.value);
|
|
106
|
+
case ">>":
|
|
107
|
+
return known(left.value >> right.value);
|
|
108
|
+
case ">>>":
|
|
109
|
+
return known(left.value >>> right.value);
|
|
110
|
+
case "<<":
|
|
111
|
+
return known(left.value << right.value);
|
|
112
|
+
case "^":
|
|
113
|
+
return known(left.value ^ right.value);
|
|
114
|
+
case "==":
|
|
115
|
+
return known(left.value == right.value);
|
|
116
|
+
case "===":
|
|
117
|
+
return known(left.value === right.value);
|
|
118
|
+
case "!=":
|
|
119
|
+
return known(left.value != right.value);
|
|
120
|
+
case "!==":
|
|
121
|
+
return known(left.value !== right.value);
|
|
122
|
+
case "in":
|
|
123
|
+
return known(left.value in right.value);
|
|
124
|
+
case "instanceof":
|
|
125
|
+
return known(left.value instanceof right.value);
|
|
126
|
+
case ">":
|
|
127
|
+
return known(left.value > right.value);
|
|
128
|
+
case "<":
|
|
129
|
+
return known(left.value < right.value);
|
|
130
|
+
case ">=":
|
|
131
|
+
return known(left.value >= right.value);
|
|
132
|
+
case "<=":
|
|
133
|
+
return known(left.value <= right.value);
|
|
56
134
|
default:
|
|
57
|
-
return UNKNOWN;
|
|
135
|
+
return UNKNOWN(node);
|
|
58
136
|
}
|
|
59
137
|
}
|
|
60
138
|
|
|
@@ -65,7 +143,10 @@ function evaluateIdentifier(
|
|
|
65
143
|
if (node.name in knownConstants) {
|
|
66
144
|
return known(knownConstants[node.name]);
|
|
67
145
|
}
|
|
68
|
-
|
|
146
|
+
if (node.name === "undefined") {
|
|
147
|
+
return known(undefined);
|
|
148
|
+
}
|
|
149
|
+
return UNKNOWN(node);
|
|
69
150
|
}
|
|
70
151
|
|
|
71
152
|
function evaluateMemberExpression(
|
|
@@ -82,15 +163,19 @@ function evaluateMemberExpression(
|
|
|
82
163
|
if (propertyNode.type === "Identifier") {
|
|
83
164
|
property = known(propertyNode.name);
|
|
84
165
|
} else {
|
|
85
|
-
return UNKNOWN;
|
|
166
|
+
return UNKNOWN(node);
|
|
86
167
|
}
|
|
87
168
|
}
|
|
88
169
|
|
|
89
|
-
if (!obj.confident || !property.confident) return UNKNOWN;
|
|
170
|
+
if (!obj.confident || !property.confident) return UNKNOWN(node);
|
|
90
171
|
|
|
91
172
|
return known(obj.value[property.value]);
|
|
92
173
|
}
|
|
93
174
|
|
|
175
|
+
function evaluateNullLiteral(node: babel.types.NullLiteral): EvaluationResult {
|
|
176
|
+
return known(null);
|
|
177
|
+
}
|
|
178
|
+
|
|
94
179
|
function evaluateNumericLiteral(
|
|
95
180
|
node: babel.types.NumericLiteral,
|
|
96
181
|
): EvaluationResult {
|
|
@@ -106,9 +191,22 @@ function evaluateObjectExpression(
|
|
|
106
191
|
for (const property of node.properties) {
|
|
107
192
|
switch (property.type) {
|
|
108
193
|
case "ObjectMethod":
|
|
109
|
-
return UNKNOWN;
|
|
110
|
-
case "SpreadElement":
|
|
111
|
-
|
|
194
|
+
return UNKNOWN(property);
|
|
195
|
+
case "SpreadElement": {
|
|
196
|
+
const values = evaluateBabelExpression(
|
|
197
|
+
property.argument,
|
|
198
|
+
knownConstants,
|
|
199
|
+
);
|
|
200
|
+
if (values.confident) {
|
|
201
|
+
result = {
|
|
202
|
+
...result,
|
|
203
|
+
...values.value,
|
|
204
|
+
};
|
|
205
|
+
continue;
|
|
206
|
+
} else {
|
|
207
|
+
return UNKNOWN(property);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
112
210
|
case "ObjectProperty": {
|
|
113
211
|
const keyNode = property.key;
|
|
114
212
|
let key: EvaluationResult;
|
|
@@ -120,11 +218,12 @@ function evaluateObjectExpression(
|
|
|
120
218
|
} else if (keyNode.type === "Identifier") {
|
|
121
219
|
key = known(keyNode.name);
|
|
122
220
|
} else {
|
|
123
|
-
return UNKNOWN;
|
|
221
|
+
return UNKNOWN(property);
|
|
124
222
|
}
|
|
125
223
|
}
|
|
126
224
|
const value = evaluateBabelExpression(property.value, knownConstants);
|
|
127
|
-
if (!key || !key.confident || !value.confident)
|
|
225
|
+
if (!key || !key.confident || !value.confident)
|
|
226
|
+
return UNKNOWN(property);
|
|
128
227
|
result[key.value] = value.value;
|
|
129
228
|
}
|
|
130
229
|
}
|
|
@@ -148,7 +247,7 @@ function evaluateTemplateLiteral(
|
|
|
148
247
|
evaluateBabelExpression(e, knownConstants),
|
|
149
248
|
);
|
|
150
249
|
if (expressions.some((e) => !e.confident)) {
|
|
151
|
-
return UNKNOWN;
|
|
250
|
+
return UNKNOWN(node);
|
|
152
251
|
}
|
|
153
252
|
const expressionValues = expressions.map((e) => e.value);
|
|
154
253
|
let result = "";
|
|
@@ -161,3 +260,29 @@ function evaluateTemplateLiteral(
|
|
|
161
260
|
}
|
|
162
261
|
return known(result);
|
|
163
262
|
}
|
|
263
|
+
|
|
264
|
+
function evaluateUnaryExpression(
|
|
265
|
+
node: babel.types.UnaryExpression,
|
|
266
|
+
knownConstants: Record<string, any>,
|
|
267
|
+
): EvaluationResult {
|
|
268
|
+
const argument = evaluateBabelExpression(node.argument, knownConstants);
|
|
269
|
+
|
|
270
|
+
if (!argument.confident) return UNKNOWN(argument.badNode);
|
|
271
|
+
|
|
272
|
+
switch (node.operator) {
|
|
273
|
+
case "+":
|
|
274
|
+
return known(+argument.value);
|
|
275
|
+
case "-":
|
|
276
|
+
return known(-argument.value);
|
|
277
|
+
case "void":
|
|
278
|
+
return known(void argument.value);
|
|
279
|
+
case "!":
|
|
280
|
+
return known(!argument.value);
|
|
281
|
+
case "~":
|
|
282
|
+
return known(~argument.value);
|
|
283
|
+
case "typeof":
|
|
284
|
+
return known(typeof argument.value);
|
|
285
|
+
default:
|
|
286
|
+
return UNKNOWN(node);
|
|
287
|
+
}
|
|
288
|
+
}
|
package/src/plugin.ts
CHANGED
|
@@ -180,7 +180,7 @@ function babelPluginReplaceCssProp({
|
|
|
180
180
|
}
|
|
181
181
|
},
|
|
182
182
|
},
|
|
183
|
-
JSXAttribute(path) {
|
|
183
|
+
JSXAttribute(path, state) {
|
|
184
184
|
if (
|
|
185
185
|
path.node.name &&
|
|
186
186
|
path.node.name.type === "JSXIdentifier" &&
|
|
@@ -199,9 +199,18 @@ function babelPluginReplaceCssProp({
|
|
|
199
199
|
);
|
|
200
200
|
let styleObject = styleObjectResult.value;
|
|
201
201
|
if (!styleObjectResult.confident) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
const badNode = styleObjectResult.badNode;
|
|
203
|
+
if (badNode) {
|
|
204
|
+
const { start, end } = badNode;
|
|
205
|
+
const rawCode = state.file.code.slice(start, end);
|
|
206
|
+
console.error(`COULD NOT EVALUATE CSS: \`${rawCode}\``);
|
|
207
|
+
return;
|
|
208
|
+
} else {
|
|
209
|
+
console.error(
|
|
210
|
+
"COULD NOT EVALUATE CSS: L" + expression.loc?.start.line,
|
|
211
|
+
);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
205
214
|
}
|
|
206
215
|
const classNames = styleObjectToClassNames(styleObject);
|
|
207
216
|
|
package/src/print.ts
CHANGED
|
@@ -25,15 +25,7 @@ ${styleRules.map((rule) => ` ${rule.property}: ${rule.value};`).join("\n")}
|
|
|
25
25
|
|
|
26
26
|
const tree = groupStyleRules(styleRules, []);
|
|
27
27
|
const rulesSection = renderStyleRuleNode(tree, styleRuleToClassName);
|
|
28
|
-
return
|
|
29
|
-
keyframesSection +
|
|
30
|
-
"\n" +
|
|
31
|
-
rulesSection +
|
|
32
|
-
"\n" +
|
|
33
|
-
"/* " +
|
|
34
|
-
JSON.stringify(styleRules, null, 2) +
|
|
35
|
-
" */"
|
|
36
|
-
);
|
|
28
|
+
return keyframesSection + "\n" + rulesSection;
|
|
37
29
|
}
|
|
38
30
|
|
|
39
31
|
function renderStyleRuleNode(
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import parser from "@babel/parser";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { evaluateBabelExpression } from "../src/evaluate.js";
|
|
4
|
+
|
|
5
|
+
const KNOWN = (value: any) => ({ confident: true, value });
|
|
6
|
+
|
|
7
|
+
const evaluateString = (str: string) =>
|
|
8
|
+
evaluateBabelExpression(parser.parseExpression(str), {});
|
|
9
|
+
|
|
10
|
+
describe("evaluateBabelExpression", () => {
|
|
11
|
+
it("handles primitive literals", () => {
|
|
12
|
+
expect(evaluateString("1")).toStrictEqual(KNOWN(1));
|
|
13
|
+
expect(evaluateString("1.1")).toStrictEqual(KNOWN(1.1));
|
|
14
|
+
expect(evaluateString("1e2")).toStrictEqual(KNOWN(100));
|
|
15
|
+
expect(evaluateString('"1"')).toStrictEqual(KNOWN("1"));
|
|
16
|
+
expect(evaluateString("null")).toStrictEqual(KNOWN(null));
|
|
17
|
+
expect(evaluateString("undefined")).toStrictEqual(KNOWN(undefined));
|
|
18
|
+
});
|
|
19
|
+
it("handles ArrayExpression", () => {
|
|
20
|
+
expect(evaluateString('[1,"2",3]')).toStrictEqual(KNOWN([1, "2", 3]));
|
|
21
|
+
expect(evaluateString("[1,...([2,3])]")).toStrictEqual(KNOWN([1, 2, 3]));
|
|
22
|
+
});
|
|
23
|
+
it("handles BinaryExpression", () => {
|
|
24
|
+
expect(evaluateString("1+1")).toStrictEqual(KNOWN(2));
|
|
25
|
+
expect(evaluateString("1-1")).toStrictEqual(KNOWN(0));
|
|
26
|
+
expect(evaluateString("4/2")).toStrictEqual(KNOWN(2));
|
|
27
|
+
expect(evaluateString("4%3")).toStrictEqual(KNOWN(1));
|
|
28
|
+
expect(evaluateString("2*3")).toStrictEqual(KNOWN(6));
|
|
29
|
+
expect(evaluateString("2**3")).toStrictEqual(KNOWN(8));
|
|
30
|
+
expect(evaluateString("2 & 3")).toStrictEqual(KNOWN(2));
|
|
31
|
+
expect(evaluateString("2 | 3")).toStrictEqual(KNOWN(3));
|
|
32
|
+
expect(evaluateString("8 >> 2")).toStrictEqual(KNOWN(2));
|
|
33
|
+
expect(evaluateString("8 >>> 2")).toStrictEqual(KNOWN(2));
|
|
34
|
+
expect(evaluateString("8 << 2")).toStrictEqual(KNOWN(32));
|
|
35
|
+
expect(evaluateString("8 ^ 2")).toStrictEqual(KNOWN(10));
|
|
36
|
+
expect(evaluateString("1 == 1")).toStrictEqual(KNOWN(true));
|
|
37
|
+
expect(evaluateString("1 === 1")).toStrictEqual(KNOWN(true));
|
|
38
|
+
expect(evaluateString("1 != 1")).toStrictEqual(KNOWN(false));
|
|
39
|
+
expect(evaluateString("1 !== 1")).toStrictEqual(KNOWN(false));
|
|
40
|
+
expect(evaluateString("1 > 0")).toStrictEqual(KNOWN(true));
|
|
41
|
+
expect(evaluateString("1 < 0")).toStrictEqual(KNOWN(false));
|
|
42
|
+
expect(evaluateString("1 >= 0")).toStrictEqual(KNOWN(true));
|
|
43
|
+
expect(evaluateString("1 <= 0")).toStrictEqual(KNOWN(false));
|
|
44
|
+
});
|
|
45
|
+
it("handles MemberExpression", () => {
|
|
46
|
+
expect(evaluateString("({a:1}).a")).toStrictEqual(KNOWN(1));
|
|
47
|
+
expect(evaluateString('({a:1})["a"]')).toStrictEqual(KNOWN(1));
|
|
48
|
+
});
|
|
49
|
+
it("handles ObjectExpression", () => {
|
|
50
|
+
expect(evaluateString("{a:1}")).toStrictEqual(KNOWN({ a: 1 }));
|
|
51
|
+
expect(evaluateString('{["a"]:1}')).toStrictEqual(KNOWN({ a: 1 }));
|
|
52
|
+
expect(evaluateString("{a:1, ...({a:2,b:2}) }")).toStrictEqual(
|
|
53
|
+
KNOWN({ a: 2, b: 2 }),
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
it("handles TemplateLiteral", () => {
|
|
57
|
+
expect(evaluateString("`Something`")).toStrictEqual(KNOWN("Something"));
|
|
58
|
+
expect(evaluateString('`Something ${"cool"}`')).toStrictEqual(
|
|
59
|
+
KNOWN("Something cool"),
|
|
60
|
+
);
|
|
61
|
+
expect(evaluateString("`Something ${1}`")).toStrictEqual(
|
|
62
|
+
KNOWN("Something 1"),
|
|
63
|
+
);
|
|
64
|
+
expect(evaluateString("`Something ${1} ${2} ${3}`")).toStrictEqual(
|
|
65
|
+
KNOWN("Something 1 2 3"),
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
it("handles UnaryExpression", () => {
|
|
69
|
+
expect(evaluateString("+1")).toStrictEqual(KNOWN(1));
|
|
70
|
+
expect(evaluateString("-1")).toStrictEqual(KNOWN(-1));
|
|
71
|
+
expect(evaluateString("void 0")).toStrictEqual(KNOWN(undefined));
|
|
72
|
+
expect(evaluateString("!1")).toStrictEqual(KNOWN(false));
|
|
73
|
+
expect(evaluateString("~1")).toStrictEqual(KNOWN(-2));
|
|
74
|
+
expect(evaluateString("typeof 1")).toStrictEqual(KNOWN("number"));
|
|
75
|
+
});
|
|
76
|
+
});
|