mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
JSXNamespacedName support
This is kind of a hack, but i think it's worth it given that JSXNamespacedName
is relatively uncommon. Adding a new InstructionValue variant to represent a
namespaced name is one option, but then that isn't a valid expression and can't
appear as an operand anywhere else. Instead, we lower namespaced names as a
primitive (string) as `${namespace}:${name}` — exploiting the fact the namespace
and name can't have a colon, and non-namespaced tagnames also can't have colons.
It's a bit of a hack but it's contained to the JSX processing code. If folks
have strong opinions on this i'm happy to change but this felt reasonable as a
quick and reliable way to unblock support.
NOTE: there is a larger question of what to do about compiling `fbt` tags.
Before we can do anything with them, though, we need to parse them.
This commit is contained in:
@@ -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`,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+133
-144
@@ -16,10 +16,8 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
|
||||
const g = { b() {}, c: () => {} };
|
||||
const { z, aa = "aa" } = useCustom();
|
||||
|
||||
<Button xlink:href="localhost:3000"></Button>;
|
||||
<Button haha={1}></Button>;
|
||||
<Button>{/** empty */}</Button>;
|
||||
<DesignSystem.Button />;
|
||||
|
||||
const j = function bar([quz, qux], ...args) {};
|
||||
|
||||
@@ -127,7 +125,7 @@ let moduleLocal = false;
|
||||
| ^^^^^^
|
||||
13 | const { z, aa = "aa" } = useCustom();
|
||||
14 |
|
||||
15 | <Button xlink:href="localhost:3000"></Button>;
|
||||
15 | <Button haha={1}></Button>;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments
|
||||
11 |
|
||||
@@ -135,215 +133,206 @@ let moduleLocal = false;
|
||||
> 13 | const { z, aa = "aa" } = useCustom();
|
||||
| ^^^^^^^^^
|
||||
14 |
|
||||
15 | <Button xlink:href="localhost:3000"></Button>;
|
||||
16 | <Button haha={1}></Button>;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle JSXNamespacedName attribute names in JSXElement
|
||||
13 | const { z, aa = "aa" } = useCustom();
|
||||
14 |
|
||||
> 15 | <Button xlink:href="localhost:3000"></Button>;
|
||||
| ^^^^^^^^^^
|
||||
16 | <Button haha={1}></Button>;
|
||||
17 | <Button>{/** empty */}</Button>;
|
||||
18 | <DesignSystem.Button />;
|
||||
15 | <Button haha={1}></Button>;
|
||||
16 | <Button>{/** empty */}</Button>;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerJsxElement) Handle JSXEmptyExpression expressions
|
||||
15 | <Button xlink:href="localhost:3000"></Button>;
|
||||
16 | <Button haha={1}></Button>;
|
||||
> 17 | <Button>{/** empty */}</Button>;
|
||||
14 |
|
||||
15 | <Button haha={1}></Button>;
|
||||
> 16 | <Button>{/** empty */}</Button>;
|
||||
| ^^^^^^^^^^^^
|
||||
18 | <DesignSystem.Button />;
|
||||
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 | <DesignSystem.Button />;
|
||||
19 |
|
||||
> 20 | const j = function bar([quz, qux], ...args) {};
|
||||
16 | <Button>{/** empty */}</Button>;
|
||||
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();
|
||||
```
|
||||
|
||||
|
||||
@@ -12,10 +12,8 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
|
||||
const g = { b() {}, c: () => {} };
|
||||
const { z, aa = "aa" } = useCustom();
|
||||
|
||||
<Button xlink:href="localhost:3000"></Button>;
|
||||
<Button haha={1}></Button>;
|
||||
<Button>{/** empty */}</Button>;
|
||||
<DesignSystem.Button />;
|
||||
|
||||
const j = function bar([quz, qux], ...args) {};
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
return <xml:http protocol:version={props.version} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props.version;
|
||||
let t0;
|
||||
if (c_0) {
|
||||
t0 = <xml:http protocol:version={props.version} />;
|
||||
$[0] = props.version;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
function Component(props) {
|
||||
return <xml:http protocol:version={props.version} />;
|
||||
}
|
||||
Reference in New Issue
Block a user