@hypergood/css-core 0.0.2 → 0.0.3

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.3",
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,27 +19,63 @@ 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);
37
46
  }
38
47
  }
39
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
+ }
74
+ }
75
+
76
+ return known(elements);
77
+ }
78
+
40
79
  function evaluateBinaryExpression(
41
80
  node: babel.types.BinaryExpression,
42
81
  knownConstants: Record<string, any>,
@@ -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,9 @@ function evaluateObjectExpression(
106
191
  for (const property of node.properties) {
107
192
  switch (property.type) {
108
193
  case "ObjectMethod":
109
- return UNKNOWN;
194
+ return UNKNOWN(property);
110
195
  case "SpreadElement":
111
- return UNKNOWN;
196
+ return UNKNOWN(property);
112
197
  case "ObjectProperty": {
113
198
  const keyNode = property.key;
114
199
  let key: EvaluationResult;
@@ -120,11 +205,12 @@ function evaluateObjectExpression(
120
205
  } else if (keyNode.type === "Identifier") {
121
206
  key = known(keyNode.name);
122
207
  } else {
123
- return UNKNOWN;
208
+ return UNKNOWN(property);
124
209
  }
125
210
  }
126
211
  const value = evaluateBabelExpression(property.value, knownConstants);
127
- if (!key || !key.confident || !value.confident) return UNKNOWN;
212
+ if (!key || !key.confident || !value.confident)
213
+ return UNKNOWN(property);
128
214
  result[key.value] = value.value;
129
215
  }
130
216
  }
@@ -148,7 +234,7 @@ function evaluateTemplateLiteral(
148
234
  evaluateBabelExpression(e, knownConstants),
149
235
  );
150
236
  if (expressions.some((e) => !e.confident)) {
151
- return UNKNOWN;
237
+ return UNKNOWN(node);
152
238
  }
153
239
  const expressionValues = expressions.map((e) => e.value);
154
240
  let result = "";
@@ -161,3 +247,29 @@ function evaluateTemplateLiteral(
161
247
  }
162
248
  return known(result);
163
249
  }
250
+
251
+ function evaluateUnaryExpression(
252
+ node: babel.types.UnaryExpression,
253
+ knownConstants: Record<string, any>,
254
+ ): EvaluationResult {
255
+ const argument = evaluateBabelExpression(node.argument, knownConstants);
256
+
257
+ if (!argument.confident) return UNKNOWN(argument.badNode);
258
+
259
+ switch (node.operator) {
260
+ case "+":
261
+ return known(+argument.value);
262
+ case "-":
263
+ return known(-argument.value);
264
+ case "void":
265
+ return known(void argument.value);
266
+ case "!":
267
+ return known(!argument.value);
268
+ case "~":
269
+ return known(~argument.value);
270
+ case "typeof":
271
+ return known(typeof argument.value);
272
+ default:
273
+ return UNKNOWN(node);
274
+ }
275
+ }
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,73 @@
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
+ });
53
+ it("handles TemplateLiteral", () => {
54
+ expect(evaluateString("`Something`")).toStrictEqual(KNOWN("Something"));
55
+ expect(evaluateString('`Something ${"cool"}`')).toStrictEqual(
56
+ KNOWN("Something cool"),
57
+ );
58
+ expect(evaluateString("`Something ${1}`")).toStrictEqual(
59
+ KNOWN("Something 1"),
60
+ );
61
+ expect(evaluateString("`Something ${1} ${2} ${3}`")).toStrictEqual(
62
+ KNOWN("Something 1 2 3"),
63
+ );
64
+ });
65
+ it("handles UnaryExpression", () => {
66
+ expect(evaluateString("+1")).toStrictEqual(KNOWN(1));
67
+ expect(evaluateString("-1")).toStrictEqual(KNOWN(-1));
68
+ expect(evaluateString("void 0")).toStrictEqual(KNOWN(undefined));
69
+ expect(evaluateString("!1")).toStrictEqual(KNOWN(false));
70
+ expect(evaluateString("~1")).toStrictEqual(KNOWN(-2));
71
+ expect(evaluateString("typeof 1")).toStrictEqual(KNOWN("number"));
72
+ });
73
+ });