diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index 85235ff528..7605164c26 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -1518,14 +1518,22 @@ function lowerExpression( }); continue; } - const name = attribute.get("name"); - if (!name.isJSXIdentifier()) { - builder.errors.push({ - reason: `(BuildHIR::lowerExpression) Handle ${name.type} attribute names in JSXElement`, - severity: ErrorSeverity.Todo, - nodePath: name, - }); - continue; + const namePath = attribute.get("name"); + let propName; + if (namePath.isJSXIdentifier()) { + propName = namePath.node.name; + if (propName.indexOf(":") !== -1) { + builder.errors.push({ + reason: `(BuildHIR::lowerExpression) Unexpected colon in attribute name '${name}'`, + severity: ErrorSeverity.Todo, + nodePath: namePath, + }); + } + } else { + invariant(namePath.isJSXNamespacedName(), "Refinement"); + const namespace = namePath.node.namespace.name; + const name = namePath.node.name.name; + propName = `${namespace}:${name}`; } const valueExpr = attribute.get("value"); let value; @@ -1551,8 +1559,7 @@ function lowerExpression( } value = lowerExpressionToTemporary(builder, expression); } - const prop: string = name.node.name; - props.push({ kind: "JsxAttribute", name: prop, place: value }); + props.push({ kind: "JsxAttribute", name: propName, place: value }); } return { kind: "JsxExpression", @@ -1922,6 +1929,13 @@ function lowerJsxElementName( if (tag.match(/^[A-Z]/)) { return lowerIdentifier(builder, exprPath); } else { + if (tag.indexOf(":") !== -1) { + builder.errors.push({ + reason: `(BuildHIR::lowerJsxElementName) JSXIdentifier to have no colons, got '${tag}'`, + severity: ErrorSeverity.InvalidInput, + nodePath: exprPath, + }); + } const place: Place = buildTemporaryPlace(builder, exprLoc); builder.push({ id: makeInstructionId(0), @@ -1937,6 +1951,29 @@ function lowerJsxElementName( } } else if (exprPath.isJSXMemberExpression()) { return lowerJsxMemberExpression(builder, exprPath); + } else if (exprPath.isJSXNamespacedName()) { + const namespace = exprPath.node.namespace.name; + const name = exprPath.node.name.name; + const tag = `${namespace}:${name}`; + if (namespace.indexOf(":") !== -1 || name.indexOf(":") !== -1) { + builder.errors.push({ + reason: `(BuildHIR::lowerJsxElementName) Expected JSXNamespacedName to have no colons in the namespace or name, got '${namespace}' : '${name}'`, + severity: ErrorSeverity.InvalidInput, + nodePath: exprPath, + }); + } + const place: Place = buildTemporaryPlace(builder, exprLoc); + builder.push({ + id: makeInstructionId(0), + value: { + kind: "Primitive", + value: tag, + loc: exprLoc, + }, + loc: exprLoc, + lvalue: { ...place }, + }); + return place; } else { builder.errors.push({ reason: `(BuildHIR::lowerJsxElementName) Handle ${exprPath.type} tags`, diff --git a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts index c8a132fd47..eda9cdd0f1 100644 --- a/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -658,9 +658,19 @@ function codegenInstructionValue( for (const attribute of instrValue.props) { switch (attribute.kind) { case "JsxAttribute": { + let propName: t.JSXIdentifier | t.JSXNamespacedName; + if (attribute.name.indexOf(":") === -1) { + propName = t.jsxIdentifier(attribute.name); + } else { + const [namespace, name] = attribute.name.split(":", 2); + propName = t.jsxNamespacedName( + t.jsxIdentifier(namespace), + t.jsxIdentifier(name) + ); + } attributes.push( t.jsxAttribute( - t.jsxIdentifier(attribute.name), + propName, t.jsxExpressionContainer(codegenPlace(cx, attribute.place)) ) ); @@ -681,9 +691,17 @@ function codegenInstructionValue( } } let tagValue = codegenPlace(cx, instrValue.tag); - let tag: t.JSXIdentifier | t.JSXMemberExpression; + let tag: t.JSXIdentifier | t.JSXNamespacedName | t.JSXMemberExpression; if (tagValue.type === "Identifier") { - tag = t.jsxIdentifier(tagValue.name); + if (tagValue.name.indexOf(":") >= 0) { + const [namespace, name] = tagValue.name.split(":", 2); + tag = t.jsxNamespacedName( + t.jsxIdentifier(namespace), + t.jsxIdentifier(name) + ); + } else { + tag = t.jsxIdentifier(tagValue.name); + } } else if (tagValue.type === "MemberExpression") { tag = convertMemberExpressionToJsx(tagValue); } else { diff --git a/compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md index 637caca4ca..144b6536d1 100644 --- a/compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md +++ b/compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md @@ -16,10 +16,8 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) { const g = { b() {}, c: () => {} }; const { z, aa = "aa" } = useCustom(); - ; ; ; - ; const j = function bar([quz, qux], ...args) {}; @@ -127,7 +125,7 @@ let moduleLocal = false; | ^^^^^^ 13 | const { z, aa = "aa" } = useCustom(); 14 | - 15 | ; + 15 | ; [ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments 11 | @@ -135,215 +133,206 @@ let moduleLocal = false; > 13 | const { z, aa = "aa" } = useCustom(); | ^^^^^^^^^ 14 | - 15 | ; - 16 | ; - -[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle JSXNamespacedName attribute names in JSXElement - 13 | const { z, aa = "aa" } = useCustom(); - 14 | -> 15 | ; - | ^^^^^^^^^^ - 16 | ; - 17 | ; - 18 | ; + 15 | ; + 16 | ; [ReactForget] TodoError: (BuildHIR::lowerJsxElement) Handle JSXEmptyExpression expressions - 15 | ; - 16 | ; -> 17 | ; + 14 | + 15 | ; +> 16 | ; | ^^^^^^^^^^^^ - 18 | ; + 17 | + 18 | const j = function bar([quz, qux], ...args) {}; 19 | - 20 | const j = function bar([quz, qux], ...args) {}; [ReactForget] TodoError: (BuildHIR::lower) Handle RestElement params - 18 | ; - 19 | -> 20 | const j = function bar([quz, qux], ...args) {}; + 16 | ; + 17 | +> 18 | const j = function bar([quz, qux], ...args) {}; | ^^^^^^^ - 21 | - 22 | for (; i < 3; i += 1) { - 23 | x.push(i); + 19 | + 20 | for (; i < 3; i += 1) { + 21 | x.push(i); [ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement - 20 | const j = function bar([quz, qux], ...args) {}; - 21 | -> 22 | for (; i < 3; i += 1) { + 18 | const j = function bar([quz, qux], ...args) {}; + 19 | +> 20 | for (; i < 3; i += 1) { | ^ - 23 | x.push(i); - 24 | } - 25 | for (; i < 3; ) { + 21 | x.push(i); + 22 | } + 23 | for (; i < 3; ) { [ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement - 23 | x.push(i); - 24 | } -> 25 | for (; i < 3; ) { + 21 | x.push(i); + 22 | } +> 23 | for (; i < 3; ) { | ^ - 26 | break; - 27 | } - 28 | for (;;) { + 24 | break; + 25 | } + 26 | for (;;) { [ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement - 23 | x.push(i); - 24 | } -> 25 | for (; i < 3; ) { + 21 | x.push(i); + 22 | } +> 23 | for (; i < 3; ) { | ^ - 26 | break; - 27 | } - 28 | for (;;) { + 24 | break; + 25 | } + 26 | for (;;) { [ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement - 26 | break; - 27 | } -> 28 | for (;;) { + 24 | break; + 25 | } +> 26 | for (;;) { | ^ - 29 | break; - 30 | } - 31 | + 27 | break; + 28 | } + 29 | [ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement - 26 | break; - 27 | } -> 28 | for (;;) { + 24 | break; + 25 | } +> 26 | for (;;) { | ^ - 29 | break; - 30 | } - 31 | + 27 | break; + 28 | } + 29 | [ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty test in ForStatement - 26 | break; - 27 | } -> 28 | for (;;) { + 24 | break; + 25 | } +> 26 | for (;;) { | ^ - 29 | break; - 30 | } - 31 | + 27 | break; + 28 | } + 29 | [ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template with interpolations - 30 | } - 31 | -> 32 | graphql` + 28 | } + 29 | +> 30 | graphql` | ^ - 33 | ${g} - 34 | `; - 35 | + 31 | ${g} + 32 | `; + 33 | [ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value - 34 | `; - 35 | -> 36 | graphql`\\t\n`; + 32 | `; + 33 | +> 34 | graphql`\\t\n`; | ^^^^^^^^^^^^^^ - 37 | - 38 | for (const c of [1, 2]) { - 39 | } + 35 | + 36 | for (const c of [1, 2]) { + 37 | } [ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForOfStatement statements - 36 | graphql`\\t\n`; - 37 | -> 38 | for (const c of [1, 2]) { + 34 | graphql`\\t\n`; + 35 | +> 36 | for (const c of [1, 2]) { | ^ - 39 | } - 40 | - 41 | for (let x in { a: 1 }) { + 37 | } + 38 | + 39 | for (let x in { a: 1 }) { [ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForInStatement statements - 39 | } - 40 | -> 41 | for (let x in { a: 1 }) { + 37 | } + 38 | +> 39 | for (let x in { a: 1 }) { | ^ - 42 | } - 43 | - 44 | let updateIdentifier = 0; + 40 | } + 41 | + 42 | let updateIdentifier = 0; [ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression - 43 | - 44 | let updateIdentifier = 0; -> 45 | --updateIdentifier; + 41 | + 42 | let updateIdentifier = 0; +> 43 | --updateIdentifier; | ^^^^^^^^^^^^^^^^^^ - 46 | ++updateIdentifier; - 47 | updateIdentifier.y++; - 48 | updateIdentifier.y--; + 44 | ++updateIdentifier; + 45 | updateIdentifier.y++; + 46 | updateIdentifier.y--; [ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression - 44 | let updateIdentifier = 0; - 45 | --updateIdentifier; -> 46 | ++updateIdentifier; + 42 | let updateIdentifier = 0; + 43 | --updateIdentifier; +> 44 | ++updateIdentifier; | ^^^^^^^^^^^^^^^^^^ - 47 | updateIdentifier.y++; - 48 | updateIdentifier.y--; - 49 | + 45 | updateIdentifier.y++; + 46 | updateIdentifier.y--; + 47 | [ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument - 45 | --updateIdentifier; - 46 | ++updateIdentifier; -> 47 | updateIdentifier.y++; + 43 | --updateIdentifier; + 44 | ++updateIdentifier; +> 45 | updateIdentifier.y++; | ^^^^^^^^^^^^^^^^^^^^ - 48 | updateIdentifier.y--; - 49 | - 50 | switch (i) { + 46 | updateIdentifier.y--; + 47 | + 48 | switch (i) { [ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument - 46 | ++updateIdentifier; - 47 | updateIdentifier.y++; -> 48 | updateIdentifier.y--; + 44 | ++updateIdentifier; + 45 | updateIdentifier.y++; +> 46 | updateIdentifier.y--; | ^^^^^^^^^^^^^^^^^^^^ - 49 | - 50 | switch (i) { - 51 | case 1 + 1: { + 47 | + 48 | switch (i) { + 49 | case 1 + 1: { [ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported - 53 | case foo(): { - 54 | } -> 55 | case x.y: { + 51 | case foo(): { + 52 | } +> 53 | case x.y: { | ^^^ - 56 | } - 57 | default: { - 58 | } - -[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported - 51 | case 1 + 1: { - 52 | } -> 53 | case foo(): { - | ^^^^^ 54 | } - 55 | case x.y: { + 55 | default: { 56 | } [ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported - 49 | - 50 | switch (i) { -> 51 | case 1 + 1: { + 49 | case 1 + 1: { + 50 | } +> 51 | case foo(): { | ^^^^^ 52 | } - 53 | case foo(): { + 53 | case x.y: { 54 | } +[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported + 47 | + 48 | switch (i) { +> 49 | case 1 + 1: { + | ^^^^^ + 50 | } + 51 | case foo(): { + 52 | } + [ReactForget] InvalidInputError: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported. - 60 | - 61 | // Cannot assign to globals -> 62 | someUnknownGlobal = true; + 58 | + 59 | // Cannot assign to globals +> 60 | someUnknownGlobal = true; | ^^^^^^^^^^^^^^^^^ - 63 | moduleLocal = true; - 64 | - 65 | function component(a) { + 61 | moduleLocal = true; + 62 | + 63 | function component(a) { [ReactForget] InvalidInputError: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported. - 61 | // Cannot assign to globals - 62 | someUnknownGlobal = true; -> 63 | moduleLocal = true; + 59 | // Cannot assign to globals + 60 | someUnknownGlobal = true; +> 61 | moduleLocal = true; | ^^^^^^^^^^^ - 64 | - 65 | function component(a) { - 66 | // Add support for function declarations once we support `var` hoisting. + 62 | + 63 | function component(a) { + 64 | // Add support for function declarations once we support `var` hoisting. [ReactForget] TodoError: (BuildHIR::lowerStatement) Handle FunctionDeclaration statements - 63 | moduleLocal = true; - 64 | -> 65 | function component(a) { + 61 | moduleLocal = true; + 62 | +> 63 | function component(a) { | ^ - 66 | // Add support for function declarations once we support `var` hoisting. - 67 | function t() {} - 68 | t(); + 64 | // Add support for function declarations once we support `var` hoisting. + 65 | function t() {} + 66 | t(); ``` \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.js b/compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.js index 6addd8d50e..ed442e4737 100644 --- a/compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.js +++ b/compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.js @@ -12,10 +12,8 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) { const g = { b() {}, c: () => {} }; const { z, aa = "aa" } = useCustom(); - ; ; ; - ; const j = function bar([quz, qux], ...args) {}; diff --git a/compiler/forget/src/__tests__/fixtures/compiler/jsx-namespaced-name.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/jsx-namespaced-name.expect.md new file mode 100644 index 0000000000..3ae8bb889f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/jsx-namespaced-name.expect.md @@ -0,0 +1,29 @@ + +## Input + +```javascript +function Component(props) { + return ; +} + +``` + +## Code + +```javascript +function Component(props) { + const $ = React.unstable_useMemoCache(2); + const c_0 = $[0] !== props.version; + let t0; + if (c_0) { + t0 = ; + $[0] = props.version; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/jsx-namespaced-name.js b/compiler/forget/src/__tests__/fixtures/compiler/jsx-namespaced-name.js new file mode 100644 index 0000000000..e09a13331d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/jsx-namespaced-name.js @@ -0,0 +1,3 @@ +function Component(props) { + return ; +}