@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypergood/css-core",
3
- "version": "0.0.2",
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: EvaluationResult = {
7
- confident: false,
8
- value: undefined,
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 || !right.confident) return UNKNOWN;
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
- return UNKNOWN;
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
- return UNKNOWN;
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) return UNKNOWN;
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
- throw new Error(
203
- "COULD NOT EVALUATE CSS: L" + expression.loc?.start.line,
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
+ });