diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts index ed19e7ed76..234f97ea4b 100644 --- a/compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts @@ -967,6 +967,33 @@ function lowerStatement( } } +function lowerObjectPropertyKey( + builder: HIRBuilder, + key: t.PrivateName | t.Expression +): { name: string; type: "string" | "identifier" } | null { + if (key.type === "Identifier") { + return { + name: key.name, + type: "identifier", + }; + } + + if (key.type === "StringLiteral") { + return { + name: key.value, + type: "string", + }; + } + + builder.errors.push({ + reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`, + severity: ErrorSeverity.Todo, + loc: key.loc ?? null, + suggestions: null, + }); + return null; +} + function lowerExpression( builder: HIRBuilder, exprPath: NodePath @@ -1009,19 +1036,11 @@ function lowerExpression( const properties: Array = []; for (const propertyPath of propertyPaths) { if (propertyPath.isObjectProperty()) { - const key = propertyPath.node.key; - let keyName: string; - if (key.type === "Identifier") { - keyName = key.name; - } else if (key.type === "StringLiteral") { - keyName = key.value; - } else { - builder.errors.push({ - reason: `(BuildHIR::lowerExpression) Expected Identifier, got ${key.type} key in ObjectExpression`, - severity: ErrorSeverity.Todo, - loc: propertyPath.node.loc ?? null, - suggestions: null, - }); + const loweredKey = lowerObjectPropertyKey( + builder, + propertyPath.node.key + ); + if (!loweredKey) { continue; } const valuePath = propertyPath.get("value"); @@ -1037,8 +1056,8 @@ function lowerExpression( const value = lowerExpressionToTemporary(builder, valuePath); properties.push({ kind: "ObjectProperty", - name: keyName, place: value, + ...loweredKey, }); } else if (propertyPath.isSpreadElement()) { const place = lowerExpressionToTemporary( @@ -2800,14 +2819,8 @@ function lowerAssignment( }); continue; } - const key = property.get("key"); - if (!key.isIdentifier()) { - builder.errors.push({ - reason: `(BuildHIR::lowerAssignment) Handle ${key.type} keys in ObjectPattern`, - severity: ErrorSeverity.Todo, - loc: key.node.loc ?? null, - suggestions: null, - }); + const loweredKey = lowerObjectPropertyKey(builder, property.node.key); + if (!loweredKey) { continue; } const element = property.get("value"); @@ -2832,8 +2845,8 @@ function lowerAssignment( } properties.push({ kind: "ObjectProperty", - name: key.node.name, place: identifier, + ...loweredKey, }); } else { const temp = buildTemporaryPlace( @@ -2842,8 +2855,8 @@ function lowerAssignment( ); properties.push({ kind: "ObjectProperty", - name: key.node.name, place: { ...temp }, + ...loweredKey, }); followups.push({ place: temp, path: element as NodePath }); // TODO remove type cast } diff --git a/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts b/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts index 0a092de106..ce89491a10 100644 --- a/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts +++ b/compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts @@ -501,6 +501,7 @@ export type ObjectPattern = { export type ObjectProperty = { kind: "ObjectProperty"; + type: "string" | "identifier"; name: string; // TODO: make a Place place: Place; }; diff --git a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts index aa249e6577..d3c4835c4f 100644 --- a/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -16,6 +16,7 @@ import { IdentifierId, InstructionKind, JsxAttribute, + ObjectProperty, Pattern, Place, ReactiveBlock, @@ -898,14 +899,14 @@ function codegenInstructionValue( const properties = []; for (const property of instrValue.properties) { if (property.kind === "ObjectProperty") { - const key = t.identifier(property.name); + const key = codegenObjectPropertyKey(property); const value = codegenPlace(cx, property.place); properties.push( t.objectProperty( key, value, false, - value.type === "Identifier" && value.name === key.name + value.type === "Identifier" && value.name === property.name ) ); } else { @@ -1310,6 +1311,17 @@ function convertMemberExpressionToJsx( } } +function codegenObjectPropertyKey( + property: ObjectProperty +): t.StringLiteral | t.Identifier { + switch (property.type) { + case "identifier": + return t.identifier(property.name); + case "string": + return t.stringLiteral(property.name); + } +} + function codegenLValue( pattern: Pattern | Place | SpreadPattern ): t.ArrayPattern | t.ObjectPattern | t.RestElement | t.Identifier { @@ -1328,13 +1340,13 @@ function codegenLValue( return t.objectPattern( pattern.properties.map((property) => { if (property.kind === "ObjectProperty") { - const key = t.identifier(property.name); + const key = codegenObjectPropertyKey(property); const value = codegenLValue(property.place); return t.objectProperty( key, value, false, - value.type === "Identifier" && value.name === key.name + value.type === "Identifier" && value.name === property.name ); } else { return t.restElement(codegenLValue(property.place)); diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-mixed-property-key-types.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-mixed-property-key-types.expect.md new file mode 100644 index 0000000000..6ed5e1ec88 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-mixed-property-key-types.expect.md @@ -0,0 +1,53 @@ + +## Input + +```javascript +function foo() { + const { + "data-foo-bar": x, + a: y, + data: z, + } = { "data-foo-bar": 1, a: 2, data: 3 }; + return [x, y, z]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], + isComponent: false, +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +function foo() { + const $ = useMemoCache(2); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = { "data-foo-bar": 1, a: 2, data: 3 }; + $[0] = t0; + } else { + t0 = $[0]; + } + const { "data-foo-bar": x, a: y, data: z } = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = [x, y, z]; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], + isComponent: false, +}; + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-mixed-property-key-types.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-mixed-property-key-types.js new file mode 100644 index 0000000000..9416e462b0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-mixed-property-key-types.js @@ -0,0 +1,14 @@ +function foo() { + const { + "data-foo-bar": x, + a: y, + data: z, + } = { "data-foo-bar": 1, a: 2, data: 3 }; + return [x, y, z]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], + isComponent: false, +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key-invalid-identifier.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key-invalid-identifier.expect.md new file mode 100644 index 0000000000..718e222870 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key-invalid-identifier.expect.md @@ -0,0 +1,32 @@ + +## Input + +```javascript +function foo({ "data-foo-bar": dataTestID }) { + return dataTestID; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [{ "data-foo-bar": {} }], + isComponent: false, +}; + +``` + +## Code + +```javascript +function foo(t5) { + const { "data-foo-bar": dataTestID } = t5; + return dataTestID; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [{ "data-foo-bar": {} }], + isComponent: false, +}; + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key-invalid-identifier.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key-invalid-identifier.js new file mode 100644 index 0000000000..9165466d9d --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key-invalid-identifier.js @@ -0,0 +1,9 @@ +function foo({ "data-foo-bar": dataTestID }) { + return dataTestID; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [{ "data-foo-bar": {} }], + isComponent: false, +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key.expect.md new file mode 100644 index 0000000000..15c0201bc4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key.expect.md @@ -0,0 +1,32 @@ + +## Input + +```javascript +function foo({ data: dataTestID }) { + return dataTestID; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [{ data: {} }], + isComponent: false, +}; + +``` + +## Code + +```javascript +function foo(t5) { + const { data: dataTestID } = t5; + return dataTestID; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [{ data: {} }], + isComponent: false, +}; + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key.js new file mode 100644 index 0000000000..e7aa51642f --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-param-string-literal-key.js @@ -0,0 +1,9 @@ +function foo({ data: dataTestID }) { + return dataTestID; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [{ data: {} }], + isComponent: false, +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-invalid-identifier-property-key.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-invalid-identifier-property-key.expect.md new file mode 100644 index 0000000000..d76baeb3a1 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-invalid-identifier-property-key.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +function foo() { + const { "data-foo-bar": t } = { "data-foo-bar": 1 }; + return t; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], + isComponent: false, +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +function foo() { + const $ = useMemoCache(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = { "data-foo-bar": 1 }; + $[0] = t0; + } else { + t0 = $[0]; + } + const { "data-foo-bar": t } = t0; + return t; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], + isComponent: false, +}; + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-invalid-identifier-property-key.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-invalid-identifier-property-key.js new file mode 100644 index 0000000000..79dc193bf6 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-invalid-identifier-property-key.js @@ -0,0 +1,10 @@ +function foo() { + const { "data-foo-bar": t } = { "data-foo-bar": 1 }; + return t; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], + isComponent: false, +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-property-key.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-property-key.expect.md new file mode 100644 index 0000000000..055655318c --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-property-key.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +function foo() { + const { data: t } = { data: 1 }; + return t; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], + isComponent: false, +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +function foo() { + const $ = useMemoCache(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = { data: 1 }; + $[0] = t0; + } else { + t0 = $[0]; + } + const { data: t } = t0; + return t; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], + isComponent: false, +}; + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-property-key.js b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-property-key.js new file mode 100644 index 0000000000..46960667a3 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/destructure-string-literal-property-key.js @@ -0,0 +1,10 @@ +function foo() { + const { data: t } = { data: 1 }; + return t; +} + +export const FIXTURE_ENTRYPOINT = { + fn: foo, + params: [], + isComponent: false, +}; diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-validate-identifiers-object-key.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-validate-identifiers-object-key.expect.md deleted file mode 100644 index 1f92566cea..0000000000 --- a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-validate-identifiers-object-key.expect.md +++ /dev/null @@ -1,36 +0,0 @@ - -## Input - -```javascript -function Foo() { - return { - "a.b": 1, - "a\b": 2, - "a/b": 3, - "a+b": 4, - "a b": 5, - }; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [], - isComponent: false, -}; - -``` - - -## Error - -``` -Unexpected token, expected "," (2:13) - 1 | import { unstable_useMemoCache as useMemoCache } from "react";function Foo() {const $ = useMemoCache(1);let t0;if ($[0] === Symbol.for("react.memo_cache_sentinel")) { -> 2 | t0 = { a.b: 1, a: 2, a/b: 3, a+b: 4, a b: 5 };$[0] = t0;} else {t0 = $[0];}return t0;} - | ^ - 3 | - 4 | - 5 | -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo-validate-identifiers-object-key.expect.md b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo-validate-identifiers-object-key.expect.md new file mode 100644 index 0000000000..a8f8e1ef34 --- /dev/null +++ b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo-validate-identifiers-object-key.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +function Foo() { + return { + "a.b": 1, + "a\b": 2, + "a/b": 3, + "a+b": 4, + "a b": 5, + }; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [], + isComponent: false, +}; + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +function Foo() { + const $ = useMemoCache(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = { "a.b": 1, "a\b": 2, "a/b": 3, "a+b": 4, "a b": 5 }; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [], + isComponent: false, +}; + +``` + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-validate-identifiers-object-key.ts b/compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo-validate-identifiers-object-key.ts similarity index 100% rename from compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-validate-identifiers-object-key.ts rename to compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/todo-validate-identifiers-object-key.ts