From c8aac6d99a023dc227f046384840526993190294 Mon Sep 17 00:00:00 2001 From: Szymon Szulc Date: Mon, 31 Aug 2026 21:17:27 +0200 Subject: [PATCH] quotes around non-identifiers keys --- packages/typegpu/src/shared/tseynit.ts | 12 ++++++++-- .../typegpu/tests/internal/tseynit.test.ts | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/typegpu/src/shared/tseynit.ts b/packages/typegpu/src/shared/tseynit.ts index 0e6aca8f53..3ca71fa4ef 100644 --- a/packages/typegpu/src/shared/tseynit.ts +++ b/packages/typegpu/src/shared/tseynit.ts @@ -153,7 +153,7 @@ function stringifyExpression(node: tinyest.Expression, ident: string): string { if (node[0] === NODE.objectExpr) { const entries = Object.entries(node[1]).map( - ([key, val]) => `${key}: ${stringifyExpression(val, ident)}`, + ([key, val]) => `${stringifyObjectPropertyKey(key)}: ${stringifyExpression(val, ident)}`, ); return `{ ${entries.join(', ')} }`; } @@ -174,9 +174,17 @@ function stringifyExpression(node: tinyest.Expression, ident: string): string { assertExhaustive(node); } +const JS_IDENTIFIER = /^[$_\p{ID_Start}][$_\u{200c}\u{200d}\p{ID_Continue}]*$/u; + +function stringifyObjectPropertyKey(key: string): string { + return JS_IDENTIFIER.test(key) ? key : JSON.stringify(key); +} + function stringifyObjectProperty(node: tinyest.ObjectProperty): string { const computed = node[3]; - const key = computed ? `[${stringifyExpression(node[1], '')}]` : stringifyExpression(node[1], ''); + const key = computed + ? `[${stringifyExpression(node[1], '')}]` + : stringifyObjectPropertyKey(node[1]); const value = stringifyExpression(node[2], ''); return `${key}: ${value}`; } diff --git a/packages/typegpu/tests/internal/tseynit.test.ts b/packages/typegpu/tests/internal/tseynit.test.ts index 2e900f58d7..41093d48a2 100644 --- a/packages/typegpu/tests/internal/tseynit.test.ts +++ b/packages/typegpu/tests/internal/tseynit.test.ts @@ -169,6 +169,28 @@ describe('ast to JS transformation', () => { ); }); + it('quotes non-identifier object property keys', () => { + const node1: tinyest.ObjectExpression = [ + N.objectExpr, + { + 'foo-bar': 'x', + 'with space': 'y', + }, + ]; + expect(stringifyNode(node1)).toBe('{ "foo-bar": x, "with space": y }'); + + const node2: tinyest.ObjectExpressionWithComputedProps = [ + N.objectExprWithComputedProps, + [ + [N.objectProperty, 'foo-bar', 'y', false], + [N.objectProperty, 'with space', 'z', false], + [N.objectProperty, [N.stringLiteral, 'foo-baz'], 'v', true], + ], + ]; + + expect(stringifyNode(node2)).toBe('{ "foo-bar": y, "with space": z, ["foo-baz"]: v }'); + }); + it('handles conditional expressions', () => { const node: tinyest.ConditionalExpression = [ N.conditionalExpr,