Skip to content

Commit fc1beb0

Browse files
Revert "feat(7411): JSX namespaced attribute syntax not supported (#47356)"
This reverts commit 0c5be02.
1 parent bdcf8ab commit fc1beb0

File tree

52 files changed

+376
-828
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+376
-828
lines changed

src/compiler/checker.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ import {
275275
getEntityNameFromTypeNode,
276276
getErrorSpanForNode,
277277
getEscapedTextOfIdentifierOrLiteral,
278-
getEscapedTextOfJsxAttributeName,
279278
getESModuleInterop,
280279
getExpandoInitializer,
281280
getExportAssignmentExpression,
@@ -351,7 +350,6 @@ import {
351350
getSymbolNameForPrivateIdentifier,
352351
getTextOfIdentifierOrLiteral,
353352
getTextOfJSDocComment,
354-
getTextOfJsxAttributeName,
355353
getTextOfNode,
356354
getTextOfPropertyName,
357355
getThisContainer,
@@ -595,7 +593,6 @@ import {
595593
isJsxAttributeLike,
596594
isJsxAttributes,
597595
isJsxElement,
598-
isJsxNamespacedName,
599596
isJsxOpeningElement,
600597
isJsxOpeningFragment,
601598
isJsxOpeningLikeElement,
@@ -810,6 +807,7 @@ import {
810807
MappedTypeNode,
811808
MatchingKeys,
812809
maybeBind,
810+
MemberName,
813811
MemberOverrideStatus,
814812
memoize,
815813
MetaProperty,
@@ -13519,7 +13517,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1351913517
function isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean {
1352013518
const list = obj.properties as NodeArray<ObjectLiteralElementLike | JsxAttributeLike>;
1352113519
return list.some(property => {
13522-
const nameType = property.name && (isJsxNamespacedName(property.name) ? getStringLiteralType(getTextOfJsxAttributeName(property.name)) : getLiteralTypeFromPropertyName(property.name));
13520+
const nameType = property.name && getLiteralTypeFromPropertyName(property.name);
1352313521
const name = nameType && isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined;
1352413522
const expected = name === undefined ? undefined : getTypeOfPropertyOfType(contextualType, name);
1352513523
return !!expected && isLiteralType(expected) && !isTypeAssignableTo(getTypeOfNode(property), expected);
@@ -19592,8 +19590,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1959219590
function *generateJsxAttributes(node: JsxAttributes): ElaborationIterator {
1959319591
if (!length(node.properties)) return;
1959419592
for (const prop of node.properties) {
19595-
if (isJsxSpreadAttribute(prop) || isHyphenatedJsxName(getTextOfJsxAttributeName(prop.name))) continue;
19596-
yield { errorNode: prop.name, innerExpression: prop.initializer, nameType: getStringLiteralType(getTextOfJsxAttributeName(prop.name)) };
19593+
if (isJsxSpreadAttribute(prop) || isHyphenatedJsxName(idText(prop.name))) continue;
19594+
yield { errorNode: prop.name, innerExpression: prop.initializer, nameType: getStringLiteralType(idText(prop.name)) };
1959719595
}
1959819596
}
1959919597

@@ -29268,7 +29266,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2926829266
if (!attributesType || isTypeAny(attributesType)) {
2926929267
return undefined;
2927029268
}
29271-
return getTypeOfPropertyOfContextualType(attributesType, getEscapedTextOfJsxAttributeName(attribute.name));
29269+
return getTypeOfPropertyOfContextualType(attributesType, attribute.name.escapedText);
2927229270
}
2927329271
else {
2927429272
return getContextualType(attribute.parent, contextFlags);
@@ -30402,12 +30400,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3040230400
attributeSymbol.links.target = member;
3040330401
attributesTable.set(attributeSymbol.escapedName, attributeSymbol);
3040430402
allAttributesTable?.set(attributeSymbol.escapedName, attributeSymbol);
30405-
if (getEscapedTextOfJsxAttributeName(attributeDecl.name) === jsxChildrenPropertyName) {
30403+
if (attributeDecl.name.escapedText === jsxChildrenPropertyName) {
3040630404
explicitlySpecifyChildrenAttribute = true;
3040730405
}
3040830406
if (contextualType) {
3040930407
const prop = getPropertyOfType(contextualType, member.escapedName);
30410-
if (prop && prop.declarations && isDeprecatedSymbol(prop) && isIdentifier(attributeDecl.name)) {
30408+
if (prop && prop.declarations && isDeprecatedSymbol(prop)) {
3041130409
addDeprecatedSuggestion(attributeDecl.name, prop.declarations, attributeDecl.name.escapedText as string);
3041230410
}
3041330411
}
@@ -47854,9 +47852,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4785447852
}
4785547853

4785647854
const { name, initializer } = attr;
47857-
const escapedText = getEscapedTextOfJsxAttributeName(name);
47858-
if (!seen.get(escapedText)) {
47859-
seen.set(escapedText, true);
47855+
if (!seen.get(name.escapedText)) {
47856+
seen.set(name.escapedText, true);
4786047857
}
4786147858
else {
4786247859
return grammarErrorOnNode(name, Diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name);
@@ -47869,11 +47866,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4786947866
}
4787047867

4787147868
function checkGrammarJsxName(node: JsxTagNameExpression) {
47872-
if (isPropertyAccessExpression(node) && isJsxNamespacedName(node.expression)) {
47873-
return grammarErrorOnNode(node.expression, Diagnostics.JSX_property_access_expressions_cannot_include_JSX_namespace_names);
47869+
if (isPropertyAccessExpression(node)) {
47870+
let propName: JsxTagNameExpression = node;
47871+
do {
47872+
const check = checkGrammarJsxNestedIdentifier(propName.name);
47873+
if (check) {
47874+
return check;
47875+
}
47876+
propName = propName.expression;
47877+
} while (isPropertyAccessExpression(propName));
47878+
const check = checkGrammarJsxNestedIdentifier(propName);
47879+
if (check) {
47880+
return check;
47881+
}
4787447882
}
47875-
if (isJsxNamespacedName(node) && getJSXTransformEnabled(compilerOptions) && !isIntrinsicJsxName(node.namespace.escapedText)) {
47876-
return grammarErrorOnNode(node, Diagnostics.React_components_cannot_include_JSX_namespace_names);
47883+
47884+
function checkGrammarJsxNestedIdentifier(name: MemberName | ThisExpression) {
47885+
if (isIdentifier(name) && idText(name).indexOf(":") !== -1) {
47886+
return grammarErrorOnNode(name, Diagnostics.JSX_property_access_expressions_cannot_include_JSX_namespace_names);
47887+
}
4787747888
}
4787847889
}
4787947890

src/compiler/diagnosticMessages.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2893,10 +2893,6 @@
28932893
"category": "Error",
28942894
"code": 2638
28952895
},
2896-
"React components cannot include JSX namespace names": {
2897-
"category": "Error",
2898-
"code": 2639
2899-
},
29002896

29012897
"Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": {
29022898
"category": "Error",

src/compiler/emitter.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,6 @@ import {
289289
JsxEmit,
290290
JsxExpression,
291291
JsxFragment,
292-
JsxNamespacedName,
293292
JsxOpeningElement,
294293
JsxOpeningFragment,
295294
JsxSelfClosingElement,
@@ -2284,8 +2283,6 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
22842283
return emitJsxSelfClosingElement(node as JsxSelfClosingElement);
22852284
case SyntaxKind.JsxFragment:
22862285
return emitJsxFragment(node as JsxFragment);
2287-
case SyntaxKind.JsxNamespacedName:
2288-
return emitJsxNamespacedName(node as JsxNamespacedName);
22892286

22902287
// Synthesized list
22912288
case SyntaxKind.SyntaxList:
@@ -4228,12 +4225,6 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
42284225
}
42294226
}
42304227

4231-
function emitJsxNamespacedName(node: JsxNamespacedName) {
4232-
emitIdentifierName(node.namespace);
4233-
writePunctuation(":");
4234-
emitIdentifierName(node.name);
4235-
}
4236-
42374228
function emitJsxTagName(node: JsxTagNameExpression) {
42384229
if (node.kind === SyntaxKind.Identifier) {
42394230
emitExpression(node);

src/compiler/factory/nodeFactory.ts

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,6 @@ import {
267267
JSDocVariadicType,
268268
JsxAttribute,
269269
JsxAttributeLike,
270-
JsxAttributeName,
271270
JsxAttributes,
272271
JsxAttributeValue,
273272
JsxChild,
@@ -276,7 +275,6 @@ import {
276275
JsxElement,
277276
JsxExpression,
278277
JsxFragment,
279-
JsxNamespacedName,
280278
JsxOpeningElement,
281279
JsxOpeningFragment,
282280
JsxSelfClosingElement,
@@ -910,8 +908,6 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
910908
updateJsxSpreadAttribute,
911909
createJsxExpression,
912910
updateJsxExpression,
913-
createJsxNamespacedName,
914-
updateJsxNamespacedName,
915911
createCaseClause,
916912
updateCaseClause,
917913
createDefaultClause,
@@ -5586,7 +5582,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
55865582
}
55875583

55885584
// @api
5589-
function createJsxAttribute(name: JsxAttributeName, initializer: JsxAttributeValue | undefined) {
5585+
function createJsxAttribute(name: Identifier, initializer: JsxAttributeValue | undefined) {
55905586
const node = createBaseDeclaration<JsxAttribute>(SyntaxKind.JsxAttribute);
55915587
node.name = name;
55925588
node.initializer = initializer;
@@ -5598,7 +5594,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
55985594
}
55995595

56005596
// @api
5601-
function updateJsxAttribute(node: JsxAttribute, name: JsxAttributeName, initializer: JsxAttributeValue | undefined) {
5597+
function updateJsxAttribute(node: JsxAttribute, name: Identifier, initializer: JsxAttributeValue | undefined) {
56025598
return node.name !== name
56035599
|| node.initializer !== initializer
56045600
? update(createJsxAttribute(name, initializer), node)
@@ -5658,26 +5654,6 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
56585654
: node;
56595655
}
56605656

5661-
// @api
5662-
function createJsxNamespacedName(namespace: Identifier, name: Identifier) {
5663-
const node = createBaseNode<JsxNamespacedName>(SyntaxKind.JsxNamespacedName);
5664-
node.namespace = namespace;
5665-
node.name = name;
5666-
node.transformFlags |=
5667-
propagateChildFlags(node.namespace) |
5668-
propagateChildFlags(node.name) |
5669-
TransformFlags.ContainsJsx;
5670-
return node;
5671-
}
5672-
5673-
// @api
5674-
function updateJsxNamespacedName(node: JsxNamespacedName, namespace: Identifier, name: Identifier) {
5675-
return node.namespace !== namespace
5676-
|| node.name !== name
5677-
? update(createJsxNamespacedName(namespace, name), node)
5678-
: node;
5679-
}
5680-
56815657
//
56825658
// Clauses
56835659
//

src/compiler/factory/nodeTests.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ import {
128128
JsxElement,
129129
JsxExpression,
130130
JsxFragment,
131-
JsxNamespacedName,
132131
JsxOpeningElement,
133132
JsxOpeningFragment,
134133
JsxSelfClosingElement,
@@ -964,10 +963,6 @@ export function isJsxExpression(node: Node): node is JsxExpression {
964963
return node.kind === SyntaxKind.JsxExpression;
965964
}
966965

967-
export function isJsxNamespacedName(node: Node): node is JsxNamespacedName {
968-
return node.kind === SyntaxKind.JsxNamespacedName;
969-
}
970-
971966
// Clauses
972967

973968
export function isCaseClause(node: Node): node is CaseClause {

src/compiler/parser.ts

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,6 @@ import {
221221
JsxElement,
222222
JsxExpression,
223223
JsxFragment,
224-
JsxNamespacedName,
225224
JsxOpeningElement,
226225
JsxOpeningFragment,
227226
JsxOpeningLikeElement,
@@ -1031,10 +1030,6 @@ const forEachChildTable: ForEachChildTable = {
10311030
[SyntaxKind.JsxClosingElement]: function forEachChildInJsxClosingElement<T>(node: JsxClosingElement, cbNode: (node: Node) => T | undefined, _cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
10321031
return visitNode(cbNode, node.tagName);
10331032
},
1034-
[SyntaxKind.JsxNamespacedName]: function forEachChildInJsxNamespacedName<T>(node: JsxNamespacedName, cbNode: (node: Node) => T | undefined, _cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
1035-
return visitNode(cbNode, node.namespace) ||
1036-
visitNode(cbNode, node.name);
1037-
},
10381033
[SyntaxKind.OptionalType]: forEachChildInOptionalRestOrJSDocParameterModifier,
10391034
[SyntaxKind.RestType]: forEachChildInOptionalRestOrJSDocParameterModifier,
10401035
[SyntaxKind.JSDocTypeExpression]: forEachChildInOptionalRestOrJSDocParameterModifier,
@@ -6107,31 +6102,20 @@ namespace Parser {
61076102

61086103
function parseJsxElementName(): JsxTagNameExpression {
61096104
const pos = getNodePos();
6105+
scanJsxIdentifier();
61106106
// JsxElement can have name in the form of
61116107
// propertyAccessExpression
61126108
// primaryExpression in the form of an identifier and "this" keyword
61136109
// We can't just simply use parseLeftHandSideExpressionOrHigher because then we will start consider class,function etc as a keyword
61146110
// We only want to consider "this" as a primaryExpression
6115-
let expression: JsxTagNameExpression = parseJsxTagName();
6111+
let expression: JsxTagNameExpression = token() === SyntaxKind.ThisKeyword ?
6112+
parseTokenNode<ThisExpression>() : parseIdentifierName();
61166113
while (parseOptional(SyntaxKind.DotToken)) {
61176114
expression = finishNode(factoryCreatePropertyAccessExpression(expression, parseRightSideOfDot(/*allowIdentifierNames*/ true, /*allowPrivateIdentifiers*/ false)), pos) as JsxTagNamePropertyAccess;
61186115
}
61196116
return expression;
61206117
}
61216118

6122-
function parseJsxTagName(): Identifier | JsxNamespacedName | ThisExpression {
6123-
const pos = getNodePos();
6124-
scanJsxIdentifier();
6125-
6126-
const isThis = token() === SyntaxKind.ThisKeyword;
6127-
const tagName = parseIdentifierName();
6128-
if (parseOptional(SyntaxKind.ColonToken)) {
6129-
scanJsxIdentifier();
6130-
return finishNode(factory.createJsxNamespacedName(tagName, parseIdentifierName()), pos);
6131-
}
6132-
return isThis ? finishNode(factory.createToken(SyntaxKind.ThisKeyword), pos) : tagName;
6133-
}
6134-
61356119
function parseJsxExpression(inExpressionContext: boolean): JsxExpression | undefined {
61366120
const pos = getNodePos();
61376121
if (!parseExpected(SyntaxKind.OpenBraceToken)) {
@@ -6164,8 +6148,9 @@ namespace Parser {
61646148
return parseJsxSpreadAttribute();
61656149
}
61666150

6151+
scanJsxIdentifier();
61676152
const pos = getNodePos();
6168-
return finishNode(factory.createJsxAttribute(parseJsxAttributeName(), parseJsxAttributeValue()), pos);
6153+
return finishNode(factory.createJsxAttribute(parseIdentifierName(), parseJsxAttributeValue()), pos);
61696154
}
61706155

61716156
function parseJsxAttributeValue(): JsxAttributeValue | undefined {
@@ -6184,18 +6169,6 @@ namespace Parser {
61846169
return undefined;
61856170
}
61866171

6187-
function parseJsxAttributeName() {
6188-
const pos = getNodePos();
6189-
scanJsxIdentifier();
6190-
6191-
const attrName = parseIdentifierName();
6192-
if (parseOptional(SyntaxKind.ColonToken)) {
6193-
scanJsxIdentifier();
6194-
return finishNode(factory.createJsxNamespacedName(attrName, parseIdentifierName()), pos);
6195-
}
6196-
return attrName;
6197-
}
6198-
61996172
function parseJsxSpreadAttribute(): JsxSpreadAttribute {
62006173
const pos = getNodePos();
62016174
parseExpected(SyntaxKind.OpenBraceToken);
@@ -10452,11 +10425,6 @@ export function tagNamesAreEquivalent(lhs: JsxTagNameExpression, rhs: JsxTagName
1045210425
return true;
1045310426
}
1045410427

10455-
if (lhs.kind === SyntaxKind.JsxNamespacedName) {
10456-
return lhs.namespace.escapedText === (rhs as JsxNamespacedName).namespace.escapedText &&
10457-
lhs.name.escapedText === (rhs as JsxNamespacedName).name.escapedText;
10458-
}
10459-
1046010428
// If we are at this statement then we must have PropertyAccessExpression and because tag name in Jsx element can only
1046110429
// take forms of JsxTagNameExpression which includes an identifier, "this" expression, or another propertyAccessExpression
1046210430
// it is safe to case the expression property as such. See parseJsxElementName for how we parse tag name in Jsx element

src/compiler/scanner.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2543,19 +2543,32 @@ export function createScanner(languageVersion: ScriptTarget,
25432543
// everything after it to the token
25442544
// Do note that this means that `scanJsxIdentifier` effectively _mutates_ the visible token without advancing to a new token
25452545
// Any caller should be expecting this behavior and should only read the pos or token value after calling it.
2546+
let namespaceSeparator = false;
25462547
while (pos < end) {
25472548
const ch = text.charCodeAt(pos);
25482549
if (ch === CharacterCodes.minus) {
25492550
tokenValue += "-";
25502551
pos++;
25512552
continue;
25522553
}
2554+
else if (ch === CharacterCodes.colon && !namespaceSeparator) {
2555+
tokenValue += ":";
2556+
pos++;
2557+
namespaceSeparator = true;
2558+
token = SyntaxKind.Identifier; // swap from keyword kind to identifier kind
2559+
continue;
2560+
}
25532561
const oldPos = pos;
25542562
tokenValue += scanIdentifierParts(); // reuse `scanIdentifierParts` so unicode escapes are handled
25552563
if (pos === oldPos) {
25562564
break;
25572565
}
25582566
}
2567+
// Do not include a trailing namespace separator in the token, since this is against the spec.
2568+
if (tokenValue.slice(-1) === ":") {
2569+
tokenValue = tokenValue.slice(0, -1);
2570+
pos--;
2571+
}
25592572
return getIdentifierToken();
25602573
}
25612574
return token;

0 commit comments

Comments
 (0)