@reearth/core 0.0.7-alpha.60 → 0.0.7-alpha.62

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": "@reearth/core",
3
- "version": "0.0.7-alpha.60",
3
+ "version": "0.0.7-alpha.62",
4
4
  "author": "Re:Earth contributors <community@reearth.io>",
5
5
  "license": "Apache-2.0",
6
6
  "description": "A library that abstracts a map engine as one common API.",
@@ -204,6 +204,12 @@ export default function useHooks({
204
204
  ),
205
205
  );
206
206
 
207
+ // Store getComputedLayer in a ref to avoid recreating prototypes on every change
208
+ const getComputedLayerRef = useRef(getComputedLayer);
209
+ useEffect(() => {
210
+ getComputedLayerRef.current = getComputedLayer;
211
+ }, [getComputedLayer]);
212
+
207
213
  const lazyComputedLayerPrototype = useMemo<object>(() => {
208
214
  return objectFromGetter(
209
215
  // id and layer should not be accessible
@@ -212,12 +218,12 @@ export default function useHooks({
212
218
  const id: string | undefined = (this as any).id;
213
219
  if (!id || typeof id !== "string") throw new Error("layer ID is not specified");
214
220
 
215
- const layer = getComputedLayer(id);
221
+ const layer = getComputedLayerRef.current(id);
216
222
  if (!layer) return undefined;
217
223
  return (layer as any)[key];
218
224
  },
219
225
  );
220
- }, [getComputedLayer]);
226
+ }, []);
221
227
 
222
228
  const lazyLayerPrototype = useMemo<object>(() => {
223
229
  return objectFromGetter(layerKeys, function (key) {
@@ -237,7 +243,7 @@ export default function useHooks({
237
243
  else if (key === "isVisible") return layer.visible;
238
244
  // computed
239
245
  else if (key === "computed") {
240
- const computedLayer = getComputedLayer(layer.id);
246
+ const computedLayer = getComputedLayerRef.current(layer.id);
241
247
  if (!computedLayer) return undefined;
242
248
  const computed = Object.create(lazyComputedLayerPrototype);
243
249
  computed.id = id;
@@ -246,7 +252,7 @@ export default function useHooks({
246
252
 
247
253
  return (layer as any)[key];
248
254
  });
249
- }, [getComputedLayer, layerMap, lazyComputedLayerPrototype]);
255
+ }, [layerMap, lazyComputedLayerPrototype]);
250
256
 
251
257
  const findById = useCallback(
252
258
  (layerId: string): LazyLayer | undefined => {
@@ -0,0 +1,392 @@
1
+ # Expression Evaluator
2
+
3
+ A powerful expression evaluator for evaluating dynamic expressions with feature properties, supporting various operators, functions, and property access methods.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Basic Usage](#basic-usage)
8
+ - [Property Access](#property-access)
9
+ - [Operators](#operators)
10
+ - [Functions](#functions)
11
+ - [Data Types](#data-types)
12
+ - [Advanced Features](#advanced-features)
13
+
14
+ ## Basic Usage
15
+
16
+ ```typescript
17
+ import { Expression } from "./expression";
18
+
19
+ const feature = {
20
+ properties: {
21
+ height: 100,
22
+ name: "Building A",
23
+ },
24
+ };
25
+
26
+ const expr = new Expression("${height} > 50", feature);
27
+ const result = expr.evaluate(); // true
28
+ ```
29
+
30
+ ## Property Access
31
+
32
+ ### 1. Standard Property Names (No Spaces)
33
+
34
+ For properties without spaces or special characters, use the simple syntax:
35
+
36
+ ```typescript
37
+ ${propertyName}
38
+ ${height}
39
+ ${temperature}
40
+ ```
41
+
42
+ **Example:**
43
+ ```typescript
44
+ const feature = {
45
+ properties: {
46
+ height: 100,
47
+ width: 50,
48
+ },
49
+ };
50
+
51
+ const expr = new Expression("${height} * ${width}", feature);
52
+ expr.evaluate(); // 5000
53
+ ```
54
+
55
+ ### 2. Quoted Property Names (With Spaces)
56
+
57
+ **✨ NEW:** For properties with spaces or special characters, wrap the property name in quotes:
58
+
59
+ ```typescript
60
+ ${"property name"} // Double quotes (recommended)
61
+ ${'property name'} // Single quotes
62
+ ```
63
+
64
+ **Example:**
65
+ ```typescript
66
+ const feature = {
67
+ properties: {
68
+ "user name": "Alice",
69
+ "user score": 95,
70
+ "email@address": "alice@example.com",
71
+ "user-info:age": 25,
72
+ },
73
+ };
74
+
75
+ // Access properties with spaces
76
+ const expr1 = new Expression('${"user name"}', feature);
77
+ expr1.evaluate(); // "Alice"
78
+
79
+ // Use in conditionals
80
+ const expr2 = new Expression('${"user score"} > 90 ? "Excellent" : "Good"', feature);
81
+ expr2.evaluate(); // "Excellent"
82
+
83
+ // Arithmetic operations
84
+ const expr3 = new Expression('${"user-info:age"} + 5', feature);
85
+ expr3.evaluate(); // 30
86
+
87
+ // Properties with special characters
88
+ const expr4 = new Expression('${"email@address"} !== ""', feature);
89
+ expr4.evaluate(); // true
90
+ ```
91
+
92
+ **Supported Characters in Quoted Names:**
93
+ - Spaces: `${"user name"}`
94
+ - Hyphens: `${"user-info"}`
95
+ - Colons: `${"category:name"}`
96
+ - At signs: `${"email@domain"}`
97
+ - Any other special characters
98
+
99
+ ### 3. Special Variables
100
+
101
+ ```typescript
102
+ ${id} // Access feature.id
103
+ ${rootProperties} // Access entire feature.properties object
104
+ ```
105
+
106
+ **Example:**
107
+ ```typescript
108
+ const feature = {
109
+ id: "feature-123",
110
+ properties: {
111
+ height: 100,
112
+ },
113
+ };
114
+
115
+ const expr = new Expression('${id}', feature);
116
+ expr.evaluate(); // "feature-123"
117
+ ```
118
+
119
+ ## Operators
120
+
121
+ ### Comparison Operators
122
+
123
+ ```typescript
124
+ ${height} > 50 // Greater than
125
+ ${height} >= 50 // Greater than or equal
126
+ ${height} < 100 // Less than
127
+ ${height} <= 100 // Less than or equal
128
+ ${height} === 50 // Strict equality
129
+ ${height} !== 50 // Strict inequality
130
+ ${height} == 50 // Loose equality (with type coercion)
131
+ ${height} != 50 // Loose inequality
132
+ ```
133
+
134
+ ### Arithmetic Operators
135
+
136
+ ```typescript
137
+ ${a} + ${b} // Addition
138
+ ${a} - ${b} // Subtraction
139
+ ${a} * ${b} // Multiplication
140
+ ${a} / ${b} // Division
141
+ ${a} % ${b} // Modulo
142
+ ```
143
+
144
+ ### Logical Operators
145
+
146
+ ```typescript
147
+ ${a} && ${b} // Logical AND
148
+ ${a} || ${b} // Logical OR
149
+ !${a} // Logical NOT
150
+ ```
151
+
152
+ ### Conditional (Ternary) Operator
153
+
154
+ ```typescript
155
+ ${condition} ? ${trueValue} : ${falseValue}
156
+ ```
157
+
158
+ **Example:**
159
+ ```typescript
160
+ const expr = new Expression('${height} > 100 ? "Tall" : "Short"', feature);
161
+ ```
162
+
163
+ ## Functions
164
+
165
+ ### Type Conversion Functions
166
+
167
+ ```typescript
168
+ Boolean(${value}) // Convert to boolean
169
+ Number(${value}) // Convert to number
170
+ String(${value}) // Convert to string
171
+ ```
172
+
173
+ ### Math Functions
174
+
175
+ ```typescript
176
+ abs(${value}) // Absolute value
177
+ sqrt(${value}) // Square root
178
+ ceil(${value}) // Round up
179
+ floor(${value}) // Round down
180
+ round(${value}) // Round to nearest integer
181
+ sin(${value}) // Sine
182
+ cos(${value}) // Cosine
183
+ tan(${value}) // Tangent
184
+ ```
185
+
186
+ ### Utility Functions
187
+
188
+ ```typescript
189
+ isNaN(${value}) // Check if Not a Number
190
+ isFinite(${value}) // Check if finite number
191
+ ```
192
+
193
+ ### Color Functions
194
+
195
+ ```typescript
196
+ color("red") // Named color
197
+ color("#ff0000") // Hex color
198
+ color("#ff0000", 0.5) // Hex color with alpha
199
+ rgb(255, 0, 0) // RGB color
200
+ rgba(255, 0, 0, 0.5) // RGBA color
201
+ hsl(0, 100, 50) // HSL color
202
+ hsla(0, 100, 50, 0.5) // HSLA color
203
+ ```
204
+
205
+ **Example:**
206
+ ```typescript
207
+ const expr = new Expression('${height} > 100 ? color("red") : color("blue")', feature);
208
+ ```
209
+
210
+ ## Data Types
211
+
212
+ ### Supported Types
213
+
214
+ - **Numbers**: `42`, `3.14`, `Infinity`, `NaN`
215
+ - **Strings**: `"hello"`, `'world'`
216
+ - **Booleans**: `true`, `false`
217
+ - **Null**: `null`
218
+ - **Undefined**: `undefined`
219
+ - **Arrays**: `[1, 2, 3]`
220
+ - **Colors**: Result of color functions
221
+
222
+ ### Constants
223
+
224
+ ```typescript
225
+ Math.PI // 3.141592653589793
226
+ Math.E // 2.718281828459045
227
+ Number.POSITIVE_INFINITY
228
+ NaN
229
+ Infinity
230
+ undefined
231
+ ```
232
+
233
+ ## Advanced Features
234
+
235
+ ### String Interpolation
236
+
237
+ Within string literals, you can interpolate property values:
238
+
239
+ ```typescript
240
+ "Hello, ${name}!"
241
+ "Height: ${height}m"
242
+ ```
243
+
244
+ **Note:** String interpolation only supports simple property names (without spaces). For properties with spaces or special characters, use concatenation instead (see below).
245
+
246
+ **Example:**
247
+ ```typescript
248
+ const feature = {
249
+ properties: {
250
+ name: "Building A",
251
+ height: 100,
252
+ },
253
+ };
254
+
255
+ const expr = new Expression('"Building: ${name}, Height: ${height}m"', feature);
256
+ expr.evaluate(); // "Building: Building A, Height: 100m"
257
+ ```
258
+
259
+ **For properties with spaces, use concatenation:**
260
+ ```typescript
261
+ const feature = {
262
+ properties: {
263
+ "building name": "Tower A",
264
+ floors: 30,
265
+ },
266
+ };
267
+
268
+ // Use concatenation with + operator
269
+ const expr = new Expression('${"building name"} + " has " + ${floors} + " floors"', feature);
270
+ expr.evaluate(); // "Tower A has 30 floors"
271
+ ```
272
+
273
+ ### Array Comparisons
274
+
275
+ The equality operators support checking if a value is in an array:
276
+
277
+ ```typescript
278
+ ${value} == [1, 2, 3] // Check if value is in array
279
+ ${value} != [1, 2, 3] // Check if value is not in array
280
+ ```
281
+
282
+ **Example:**
283
+ ```typescript
284
+ const feature = {
285
+ properties: {
286
+ status: "active",
287
+ },
288
+ };
289
+
290
+ const expr = new Expression('${status} == ["active", "pending"]', feature);
291
+ expr.evaluate(); // true
292
+ ```
293
+
294
+ ### Defines (Variable Substitution)
295
+
296
+ You can define placeholder values that get substituted before evaluation:
297
+
298
+ ```typescript
299
+ const defines = {
300
+ MAX_HEIGHT: "100",
301
+ MIN_HEIGHT: "10",
302
+ };
303
+
304
+ const expr = new Expression("${height} > ${MAX_HEIGHT}", feature, defines);
305
+ // Becomes: "${height} > 100"
306
+ ```
307
+
308
+ ### Expression Caching
309
+
310
+ Expressions are cached automatically for better performance. To clear caches:
311
+
312
+ ```typescript
313
+ import { clearExpressionCaches } from "./expression";
314
+
315
+ clearExpressionCaches(expressionString, feature, defines);
316
+ ```
317
+
318
+ ## Property Name Comparison
319
+
320
+ | Syntax | Use Case | Example |
321
+ |--------|----------|---------|
322
+ | `${name}` | Simple properties (no spaces) | `${height}`, `${temperature}` |
323
+ | `${"property name"}` | Properties with spaces/special chars | `${"user name"}`, `${"email@domain"}` |
324
+ | `${rootProperties}` | Access entire properties object | `${rootProperties["dynamic-key"]}` |
325
+
326
+ ## Complete Example
327
+
328
+ ```typescript
329
+ import { Expression } from "./expression";
330
+
331
+ const feature = {
332
+ id: "building-001",
333
+ properties: {
334
+ "building name": "Tower A",
335
+ "building height": 150,
336
+ "building color": "blue",
337
+ floors: 30,
338
+ status: "active",
339
+ "contact info": {
340
+ "email address": "info@tower-a.com",
341
+ },
342
+ },
343
+ };
344
+
345
+ // Simple comparison
346
+ const expr1 = new Expression('${"building height"} > 100', feature);
347
+ console.log(expr1.evaluate()); // true
348
+
349
+ // Conditional with color
350
+ const expr2 = new Expression(
351
+ '${"building height"} > 100 ? color("red") : color("green")',
352
+ feature
353
+ );
354
+ console.log(expr2.evaluate()); // #ff0000 (red)
355
+
356
+ // String concatenation (for properties with spaces)
357
+ const expr3 = new Expression(
358
+ '${"building name"} + " has " + ${floors} + " floors"',
359
+ feature
360
+ );
361
+ console.log(expr3.evaluate()); // "Tower A has 30 floors"
362
+
363
+ // Array membership check
364
+ const expr4 = new Expression(
365
+ '${status} == ["active", "pending"]',
366
+ feature
367
+ );
368
+ console.log(expr4.evaluate()); // true
369
+ ```
370
+
371
+ ## Migration Guide
372
+
373
+ ### From Standard Syntax to Quoted Syntax
374
+
375
+ If you have properties with spaces, you need to update your expressions:
376
+
377
+ **Before (doesn't work):**
378
+ ```typescript
379
+ ${user name} // ❌ Fails: interpreted as two separate identifiers
380
+ ```
381
+
382
+ **After (works):**
383
+ ```typescript
384
+ ${"user name"} // ✅ Works: quoted property name
385
+ ```
386
+
387
+ ### Best Practices
388
+
389
+ 1. **Use simple syntax** for properties without spaces: `${height}`
390
+ 2. **Use quoted syntax** for properties with spaces: `${"user name"}`
391
+ 3. **Prefer double quotes** for consistency: `${"property"}` instead of `${'property'}`
392
+ 4. **Avoid special characters in property names** when possible to keep expressions simple
@@ -110,6 +110,71 @@ describe("Expression evaluation", () => {
110
110
  expression.evaluate();
111
111
  }).toThrow('Unexpected function call "czm_住所"');
112
112
  });
113
+
114
+ test("should evaluate expression with quoted property names containing spaces", () => {
115
+ const expressionString = '${"user name"}';
116
+ const feature = {
117
+ properties: {
118
+ "user name": "Alice",
119
+ "user age": 25,
120
+ },
121
+ } as Feature;
122
+
123
+ const expression = new Expression(expressionString, feature);
124
+ const result = expression.evaluate();
125
+
126
+ expect(result).toBe("Alice");
127
+ });
128
+
129
+ test("should evaluate conditional expression with quoted property names", () => {
130
+ const expressionString = '${"user score"} > 50 ? "Pass" : "Fail"';
131
+ const feature1 = {
132
+ properties: {
133
+ "user score": 75,
134
+ },
135
+ } as Feature;
136
+ const feature2 = {
137
+ properties: {
138
+ "user score": 30,
139
+ },
140
+ } as Feature;
141
+
142
+ const expression1 = new Expression(expressionString, feature1);
143
+ const expression2 = new Expression(expressionString, feature2);
144
+
145
+ expect(expression1.evaluate()).toBe("Pass");
146
+ expect(expression2.evaluate()).toBe("Fail");
147
+ });
148
+
149
+ test("should evaluate arithmetic expression with quoted property names", () => {
150
+ const expressionString = '${"item price"} * ${"item quantity"}';
151
+ const feature = {
152
+ properties: {
153
+ "item price": 10.5,
154
+ "item quantity": 3,
155
+ },
156
+ } as Feature;
157
+
158
+ const expression = new Expression(expressionString, feature);
159
+ const result = expression.evaluate();
160
+
161
+ expect(result).toBe(31.5);
162
+ });
163
+
164
+ test("should handle quoted property names with special characters", () => {
165
+ const expressionString = '${"user-info:name"} === "Bob Smith"';
166
+ const feature = {
167
+ properties: {
168
+ "user-info:name": "Bob Smith",
169
+ "email@address": "bob@example.com",
170
+ },
171
+ } as Feature;
172
+
173
+ const expression = new Expression(expressionString, feature);
174
+ const result = expression.evaluate();
175
+
176
+ expect(result).toBe(true);
177
+ });
113
178
  });
114
179
 
115
180
  describe("expression caches", () => {
@@ -51,4 +51,182 @@ describe("replaceVariables", () => {
51
51
  const [result, _] = replaceVariables("${vari-able}");
52
52
  expect(result).toBe(`czm_vari$reearth_hyphen_$able`);
53
53
  });
54
+
55
+ test("should handle property names with spaces using bracket notation with double quotes", () => {
56
+ const [, res] = replaceVariables('${$["property name"]}', {
57
+ "property name": "value with space",
58
+ normalProperty: "normal value",
59
+ });
60
+ expect(res[0].literalValue).toBe("value with space");
61
+ });
62
+
63
+ test("should handle property names with spaces using bracket notation with single quotes", () => {
64
+ const [, res] = replaceVariables("${$['user name']}", {
65
+ "user name": "John Doe",
66
+ normalProperty: "normal value",
67
+ });
68
+ expect(res[0].literalValue).toBe("John Doe");
69
+ });
70
+
71
+ test("should handle nested property names with spaces", () => {
72
+ const [, res] = replaceVariables('${$["contact info"]["email address"]}', {
73
+ "contact info": {
74
+ "email address": "john@example.com",
75
+ "phone number": "123-456-7890",
76
+ },
77
+ });
78
+ expect(res[0].literalValue).toBe("john@example.com");
79
+ });
80
+
81
+ test("should handle array elements with property names containing spaces", () => {
82
+ const [, res] = replaceVariables('${$.items[0]["item name"]}', {
83
+ items: [
84
+ { "item name": "Product A", "item price": 100 },
85
+ { "item name": "Product B", "item price": 200 },
86
+ ],
87
+ });
88
+ expect(res[0].literalValue).toBe("Product A");
89
+ });
90
+
91
+ test("should handle array slice with property names containing spaces", () => {
92
+ const [, res] = replaceVariables('${$.items[:1]["item price"]}', {
93
+ items: [
94
+ { "item name": "Product A", "item price": 100 },
95
+ { "item name": "Product B", "item price": 200 },
96
+ ],
97
+ });
98
+ expect(res[0].literalValue).toBe(100);
99
+ });
100
+
101
+ test("should handle quoted dot notation for property names with spaces", () => {
102
+ const [, res] = replaceVariables("${$.'property name'}", {
103
+ "property name": "value with space",
104
+ });
105
+ expect(res[0].literalValue).toBe("value with space");
106
+ });
107
+
108
+ test("should handle multiple property names with spaces in one expression", () => {
109
+ const [result, res] = replaceVariables('${$["user name"]} - ${$["property name"]}', {
110
+ "user name": "John Doe",
111
+ "property name": "value with space",
112
+ });
113
+ expect(res).toHaveLength(2);
114
+ expect(res[0].literalValue).toBe("John Doe");
115
+ expect(res[1].literalValue).toBe("value with space");
116
+ expect(result).toContain(res[0].literalName);
117
+ expect(result).toContain(res[1].literalName);
118
+ });
119
+
120
+ test("should handle quoted property names with double quotes", () => {
121
+ const [result, res] = replaceVariables('${"user info"}', {
122
+ "user info": "John Doe",
123
+ normalProperty: "normal value",
124
+ });
125
+ expect(res).toHaveLength(1);
126
+ expect(res[0].literalValue).toBe("John Doe");
127
+ expect(result).toBe(res[0].literalName);
128
+ });
129
+
130
+ test("should handle quoted property names with single quotes", () => {
131
+ const [result, res] = replaceVariables("${'property name'}", {
132
+ "property name": "value with space",
133
+ normalProperty: "normal value",
134
+ });
135
+ expect(res).toHaveLength(1);
136
+ expect(res[0].literalValue).toBe("value with space");
137
+ expect(result).toBe(res[0].literalName);
138
+ });
139
+
140
+ test("should handle multiple quoted property names in one expression", () => {
141
+ const [result, res] = replaceVariables('${"user name"} - ${"user age"}', {
142
+ "user name": "John Doe",
143
+ "user age": 30,
144
+ });
145
+ expect(res).toHaveLength(2);
146
+ expect(res[0].literalValue).toBe("John Doe");
147
+ expect(res[1].literalValue).toBe(30);
148
+ expect(result).toContain(res[0].literalName);
149
+ expect(result).toContain("-");
150
+ expect(result).toContain(res[1].literalName);
151
+ });
152
+
153
+ test("should handle quoted property names with special characters", () => {
154
+ const [result, res] = replaceVariables('${"user-info:name"}', {
155
+ "user-info:name": "Jane Smith",
156
+ });
157
+ expect(res).toHaveLength(1);
158
+ expect(res[0].literalValue).toBe("Jane Smith");
159
+ expect(result).toBe(res[0].literalName);
160
+ });
161
+
162
+ test("should reject mismatched quote types (double to single)", () => {
163
+ const [result, res] = replaceVariables("${\"user info'}", {
164
+ "user info": "John Doe",
165
+ });
166
+ // Should not match the quoted pattern, should fall back to variable name
167
+ expect(result).toContain("czm_");
168
+ expect(res).toHaveLength(0);
169
+ });
170
+
171
+ test("should reject mismatched quote types (single to double)", () => {
172
+ const [result, res] = replaceVariables("${'user info\"}", {
173
+ "user info": "Jane Doe",
174
+ });
175
+ // Should not match the quoted pattern, should fall back to variable name
176
+ expect(result).toContain("czm_");
177
+ expect(res).toHaveLength(0);
178
+ });
179
+
180
+ test("should correctly handle consecutive properties with different quote types", () => {
181
+ const [result, res] = replaceVariables("${\"prop1\"} + ${'prop2'}", {
182
+ prop1: "value1",
183
+ prop2: "value2",
184
+ });
185
+ expect(res).toHaveLength(2);
186
+ expect(res[0].literalValue).toBe("value1");
187
+ expect(res[1].literalValue).toBe("value2");
188
+ expect(result).toContain(res[0].literalName);
189
+ expect(result).toContain("+");
190
+ expect(result).toContain(res[1].literalName);
191
+ });
192
+
193
+ test("should return empty string when quoted property is missing (consistent with regular variables)", () => {
194
+ const [result, res] = replaceVariables('${"missing"}', {
195
+ existing: "value",
196
+ });
197
+ // Returns empty string for missing quoted property (consistent with regular variables)
198
+ expect(res).toHaveLength(1);
199
+ expect(res[0].literalValue).toBe("");
200
+ expect(result).toBe(res[0].literalName);
201
+ });
202
+
203
+ test("should pass through regular variable name when property might be missing", () => {
204
+ const [result, res] = replaceVariables("${missing}");
205
+ // Regular variables are passed through as czm_variableName
206
+ // They will be evaluated later by Node._evaluateVariable
207
+ expect(result).toBe("czm_missing");
208
+ expect(res).toHaveLength(0);
209
+ });
210
+
211
+ test("should return empty string for missing JSONPath properties (consistent with regular variables)", () => {
212
+ const [result, res] = replaceVariables("${$.missingPath}", {
213
+ existing: "value",
214
+ });
215
+ // Returns empty string for missing JSONPath property
216
+ expect(res).toHaveLength(1);
217
+ expect(res[0].literalValue).toBe("");
218
+ expect(result).toBe(res[0].literalName);
219
+ });
220
+
221
+ test("should handle mixed existing and missing properties consistently", () => {
222
+ const [result, res] = replaceVariables('${"existing"} - ${"missing"}', {
223
+ existing: "value",
224
+ });
225
+ expect(res).toHaveLength(2);
226
+ expect(res[0].literalValue).toBe("value");
227
+ expect(res[1].literalValue).toBe(""); // Missing property returns empty string
228
+ expect(result).toContain(res[0].literalName);
229
+ expect(result).toContain("-");
230
+ expect(result).toContain(res[1].literalName);
231
+ });
54
232
  });