Skip to content

Commit 5628afa

Browse files
author
Kanchalai Tanglertsampan
committed
Convert jsxAttributes to be based on ObjectLiteralExpressionBase
1 parent a0793ae commit 5628afa

File tree

10 files changed

+110
-39
lines changed

10 files changed

+110
-39
lines changed

src/compiler/binder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,6 +2880,7 @@ namespace ts {
28802880
case SyntaxKind.JsxText:
28812881
case SyntaxKind.JsxClosingElement:
28822882
case SyntaxKind.JsxAttribute:
2883+
case SyntaxKind.JsxAttributes:
28832884
case SyntaxKind.JsxSpreadAttribute:
28842885
case SyntaxKind.JsxExpression:
28852886
// These nodes are Jsx syntax.

src/compiler/checker.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10144,7 +10144,7 @@ namespace ts {
1014410144

1014510145
function getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute) {
1014610146
const kind = attribute.kind;
10147-
const jsxElement = attribute.parent as JsxOpeningLikeElement;
10147+
const jsxElement = attribute.parent.parent as JsxOpeningLikeElement;
1014810148
const attrsType = getJsxElementAttributesType(jsxElement);
1014910149

1015010150
if (attribute.kind === SyntaxKind.JsxAttribute) {
@@ -10703,7 +10703,8 @@ namespace ts {
1070310703
// Look up the corresponding property for this attribute
1070410704
if (elementAttributesType === emptyObjectType && isUnhyphenatedJsxName(node.name.text)) {
1070510705
// If there is no 'props' property, you may not have non-"data-" attributes
10706-
error(node.parent, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, getJsxElementPropertiesName());
10706+
// We do node.parent.parent to report an error at JsxOpeningLikeElement
10707+
error(node.parent.parent, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, getJsxElementPropertiesName());
1070710708
}
1070810709
else if (elementAttributesType && !isTypeAny(elementAttributesType)) {
1070910710
const correspondingPropSymbol = getPropertyOfType(elementAttributesType, node.name.text);
@@ -11028,7 +11029,7 @@ namespace ts {
1102811029
* that have no matching element attributes type property.
1102911030
*/
1103011031
function getJsxAttributePropertySymbol(attrib: JsxAttribute): Symbol {
11031-
const attributesType = getJsxElementAttributesType(<JsxOpeningElement>attrib.parent);
11032+
const attributesType = getJsxElementAttributesType(attrib.parent.parent as JsxOpeningElement);
1103211033
const prop = getPropertyOfType(attributesType, attrib.name.text);
1103311034
return prop || unknownSymbol;
1103411035
}
@@ -11079,13 +11080,14 @@ namespace ts {
1107911080
// attributes (mostly from spreads) are being overwritten and
1108011081
// thus should have their types ignored
1108111082
let sawSpreadedAny = false;
11082-
for (let i = node.attributes.length - 1; i >= 0; i--) {
11083-
if (node.attributes[i].kind === SyntaxKind.JsxAttribute) {
11084-
checkJsxAttribute(<JsxAttribute>(node.attributes[i]), targetAttributesType, nameTable);
11083+
const attributes = node.attributes.properties;
11084+
for (let i = attributes.length - 1; i >= 0; i--) {
11085+
if (attributes[i].kind === SyntaxKind.JsxAttribute) {
11086+
checkJsxAttribute(<JsxAttribute>(attributes[i]), targetAttributesType, nameTable);
1108511087
}
1108611088
else {
11087-
Debug.assert(node.attributes[i].kind === SyntaxKind.JsxSpreadAttribute);
11088-
const spreadType = checkJsxSpreadAttribute(<JsxSpreadAttribute>(node.attributes[i]), targetAttributesType, nameTable);
11089+
Debug.assert(attributes[i].kind === SyntaxKind.JsxSpreadAttribute);
11090+
const spreadType = checkJsxSpreadAttribute(<JsxSpreadAttribute>(attributes[i]), targetAttributesType, nameTable);
1108911091
if (isTypeAny(spreadType)) {
1109011092
sawSpreadedAny = true;
1109111093
}
@@ -20279,7 +20281,7 @@ namespace ts {
2027920281

2028020282
function checkGrammarJsxElement(node: JsxOpeningLikeElement) {
2028120283
const seen = createMap<boolean>();
20282-
for (const attr of node.attributes) {
20284+
for (const attr of node.attributes.properties) {
2028320285
if (attr.kind === SyntaxKind.JsxSpreadAttribute) {
2028420286
continue;
2028520287
}

src/compiler/emitter.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,8 @@ const _super = (function (geti, seti) {
714714
return emitJsxClosingElement(<JsxClosingElement>node);
715715
case SyntaxKind.JsxAttribute:
716716
return emitJsxAttribute(<JsxAttribute>node);
717+
case SyntaxKind.JsxAttributes:
718+
return emitJsxAttributes(<JsxAttributes>node);
717719
case SyntaxKind.JsxSpreadAttribute:
718720
return emitJsxSpreadAttribute(<JsxSpreadAttribute>node);
719721
case SyntaxKind.JsxExpression:
@@ -1957,15 +1959,21 @@ const _super = (function (geti, seti) {
19571959
write("<");
19581960
emitJsxTagName(node.tagName);
19591961
write(" ");
1960-
emitList(node, node.attributes, ListFormat.JsxElementAttributes);
1962+
// We are checking here so we won't re-enter the emiting pipeline and emit extra sourcemap
1963+
if (node.attributes.properties && node.attributes.properties.length > 0) {
1964+
emit(node.attributes);
1965+
}
19611966
write("/>");
19621967
}
19631968

19641969
function emitJsxOpeningElement(node: JsxOpeningElement) {
19651970
write("<");
19661971
emitJsxTagName(node.tagName);
1967-
writeIfAny(node.attributes, " ");
1968-
emitList(node, node.attributes, ListFormat.JsxElementAttributes);
1972+
writeIfAny(node.attributes.properties, " ");
1973+
// We are checking here so we won't re-enter the emiting pipeline and emit extra sourcemap
1974+
if (node.attributes.properties && node.attributes.properties.length > 0) {
1975+
emit(node.attributes);
1976+
}
19691977
write(">");
19701978
}
19711979

@@ -1979,6 +1987,10 @@ const _super = (function (geti, seti) {
19791987
write(">");
19801988
}
19811989

1990+
function emitJsxAttributes(node: JsxAttributes) {
1991+
emitList(node, node.properties, ListFormat.JsxElementAttributes);
1992+
}
1993+
19821994
function emitJsxAttribute(node: JsxAttribute) {
19831995
emit(node.name);
19841996
emitWithPrefix("=", node.initializer);

src/compiler/factory.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,28 +1249,28 @@ namespace ts {
12491249
return node;
12501250
}
12511251

1252-
export function createJsxSelfClosingElement(tagName: JsxTagNameExpression, attributes: JsxAttributeLike[], location?: TextRange) {
1252+
export function createJsxSelfClosingElement(tagName: JsxTagNameExpression, attributes: JsxAttributes, location?: TextRange) {
12531253
const node = <JsxSelfClosingElement>createNode(SyntaxKind.JsxSelfClosingElement, location);
12541254
node.tagName = tagName;
1255-
node.attributes = createNodeArray(attributes);
1255+
node.attributes = attributes;
12561256
return node;
12571257
}
12581258

1259-
export function updateJsxSelfClosingElement(node: JsxSelfClosingElement, tagName: JsxTagNameExpression, attributes: JsxAttributeLike[]) {
1259+
export function updateJsxSelfClosingElement(node: JsxSelfClosingElement, tagName: JsxTagNameExpression, attributes: JsxAttributes) {
12601260
if (node.tagName !== tagName || node.attributes !== attributes) {
12611261
return updateNode(createJsxSelfClosingElement(tagName, attributes, node), node);
12621262
}
12631263
return node;
12641264
}
12651265

1266-
export function createJsxOpeningElement(tagName: JsxTagNameExpression, attributes: JsxAttributeLike[], location?: TextRange) {
1266+
export function createJsxOpeningElement(tagName: JsxTagNameExpression, attributes: JsxAttributes, location?: TextRange) {
12671267
const node = <JsxOpeningElement>createNode(SyntaxKind.JsxOpeningElement, location);
12681268
node.tagName = tagName;
1269-
node.attributes = createNodeArray(attributes);
1269+
node.attributes = attributes;
12701270
return node;
12711271
}
12721272

1273-
export function updateJsxOpeningElement(node: JsxOpeningElement, tagName: JsxTagNameExpression, attributes: JsxAttributeLike[]) {
1273+
export function updateJsxOpeningElement(node: JsxOpeningElement, tagName: JsxTagNameExpression, attributes: JsxAttributes) {
12741274
if (node.tagName !== tagName || node.attributes !== attributes) {
12751275
return updateNode(createJsxOpeningElement(tagName, attributes, node), node);
12761276
}
@@ -1290,6 +1290,20 @@ namespace ts {
12901290
return node;
12911291
}
12921292

1293+
export function createJsxAttributes(properties: JsxAttributeLike[], location?: TextRange) {
1294+
const jsxAttributes = <JsxAttributes>createNode(SyntaxKind.JsxAttributes, location);
1295+
jsxAttributes.emitFlags |= NodeEmitFlags.NoSourceMap;
1296+
jsxAttributes.properties = createNodeArray(properties);
1297+
return jsxAttributes;
1298+
}
1299+
1300+
export function updateJsxAttributes(jsxAttributes: JsxAttributes, properties: JsxAttributeLike[]) {
1301+
if (jsxAttributes.properties !== properties) {
1302+
return updateNode(createJsxAttributes(properties, jsxAttributes), jsxAttributes);
1303+
}
1304+
return jsxAttributes;
1305+
}
1306+
12931307
export function createJsxAttribute(name: Identifier, initializer: StringLiteral | JsxExpression, location?: TextRange) {
12941308
const node = <JsxAttribute>createNode(SyntaxKind.JsxAttribute, location);
12951309
node.name = name;

src/compiler/parser.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,9 @@ namespace ts {
359359
case SyntaxKind.JsxSelfClosingElement:
360360
case SyntaxKind.JsxOpeningElement:
361361
return visitNode(cbNode, (<JsxOpeningLikeElement>node).tagName) ||
362-
visitNodes(cbNodes, (<JsxOpeningLikeElement>node).attributes);
362+
visitNode(cbNode, (<JsxOpeningLikeElement>node).attributes);
363+
case SyntaxKind.JsxAttributes:
364+
return visitNodes(cbNodes, (<JsxAttributes>node).properties);
363365
case SyntaxKind.JsxAttribute:
364366
return visitNode(cbNode, (<JsxAttribute>node).name) ||
365367
visitNode(cbNode, (<JsxAttribute>node).initializer);
@@ -3773,14 +3775,20 @@ namespace ts {
37733775
return result;
37743776
}
37753777

3778+
function parseJsxAttributes(): JsxAttributes {
3779+
const jsxAttributes = <JsxAttributes>createNode(SyntaxKind.JsxAttributes);
3780+
jsxAttributes.properties = parseList(ParsingContext.JsxAttributes, parseJsxAttribute);
3781+
return finishNode(jsxAttributes);
3782+
}
3783+
37763784
function parseJsxOpeningOrSelfClosingElement(inExpressionContext: boolean): JsxOpeningElement | JsxSelfClosingElement {
37773785
const fullStart = scanner.getStartPos();
37783786

37793787
parseExpected(SyntaxKind.LessThanToken);
37803788

37813789
const tagName = parseJsxElementName();
3790+
const attributes = parseJsxAttributes();
37823791

3783-
const attributes = parseList(ParsingContext.JsxAttributes, parseJsxAttribute);
37843792
let node: JsxOpeningLikeElement;
37853793

37863794
if (token() === SyntaxKind.GreaterThanToken) {

src/compiler/transformers/jsx.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ namespace ts {
8282
function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: JsxChild[], isChild: boolean, location: TextRange) {
8383
const tagName = getTagName(node);
8484
let objectProperties: Expression;
85-
const attrs = node.attributes;
85+
const attrs = node.attributes.properties;
8686
if (attrs.length === 0) {
8787
// When there are no attributes, React wants "null"
8888
objectProperties = createNull();

src/compiler/types.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ namespace ts {
304304
JsxText,
305305
JsxClosingElement,
306306
JsxAttribute,
307+
JsxAttributes,
307308
JsxSpreadAttribute,
308309
JsxExpression,
309310

@@ -1149,14 +1150,22 @@ namespace ts {
11491150
closingElement: JsxClosingElement;
11501151
}
11511152

1153+
/// Either the opening tag in a <Tag>...</Tag> pair, or the lone <Tag /> in a self-closing form
1154+
export type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement;
1155+
1156+
export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute;
1157+
11521158
export type JsxTagNameExpression = PrimaryExpression | PropertyAccessExpression;
11531159

1160+
export interface JsxAttributes extends ObjectLiteralExpressionBase<JsxAttributeLike> {
1161+
}
1162+
11541163
/// The opening element of a <Tag>...</Tag> JsxElement
11551164
// @kind(SyntaxKind.JsxOpeningElement)
11561165
export interface JsxOpeningElement extends Expression {
11571166
_openingElementBrand?: any;
11581167
tagName: JsxTagNameExpression;
1159-
attributes: NodeArray<JsxAttribute | JsxSpreadAttribute>;
1168+
attributes: JsxAttributes;
11601169
}
11611170

11621171
/// A JSX expression of the form <TagName attrs />
@@ -1165,20 +1174,15 @@ namespace ts {
11651174
_selfClosingElementBrand?: any;
11661175
}
11671176

1168-
/// Either the opening tag in a <Tag>...</Tag> pair, or the lone <Tag /> in a self-closing form
1169-
export type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement;
1170-
1171-
export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute;
1172-
11731177
// @kind(SyntaxKind.JsxAttribute)
1174-
export interface JsxAttribute extends Node {
1178+
export interface JsxAttribute extends ObjectLiteralElement {
11751179
name: Identifier;
11761180
/// JSX attribute initializers are optional; <X y /> is sugar for <X y={true} />
11771181
initializer?: StringLiteral | JsxExpression;
11781182
}
11791183

11801184
// @kind(SyntaxKind.JsxSpreadAttribute)
1181-
export interface JsxSpreadAttribute extends Node {
1185+
export interface JsxSpreadAttribute extends SpreadElementExpression {
11821186
expression: Expression;
11831187
}
11841188

src/compiler/utilities.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4112,6 +4112,11 @@ namespace ts {
41124112
|| kind === SyntaxKind.JsxText;
41134113
}
41144114

4115+
export function isJsxAttributes(node: Node): node is JsxAttributes {
4116+
const kind = node.kind;
4117+
return kind === SyntaxKind.JsxAttributes;
4118+
}
4119+
41154120
export function isJsxAttributeLike(node: Node): node is JsxAttributeLike {
41164121
const kind = node.kind;
41174122
return kind === SyntaxKind.JsxAttribute

src/compiler/visitor.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -461,13 +461,17 @@ namespace ts {
461461
case SyntaxKind.JsxSelfClosingElement:
462462
case SyntaxKind.JsxOpeningElement:
463463
result = reduceNode((<JsxSelfClosingElement | JsxOpeningElement>node).tagName, f, result);
464-
result = reduceLeft((<JsxSelfClosingElement | JsxOpeningElement>node).attributes, f, result);
464+
result = reduceNode((<JsxSelfClosingElement | JsxOpeningElement>node).attributes, f, result);
465465
break;
466466

467467
case SyntaxKind.JsxClosingElement:
468468
result = reduceNode((<JsxClosingElement>node).tagName, f, result);
469469
break;
470470

471+
case SyntaxKind.JsxAttributes:
472+
result = reduceLeft((<JsxAttributes>node).properties, f, result);
473+
break;
474+
471475
case SyntaxKind.JsxAttribute:
472476
result = reduceNode((<JsxAttribute>node).name, f, result);
473477
result = reduceNode((<JsxAttribute>node).initializer, f, result);
@@ -1067,15 +1071,19 @@ namespace ts {
10671071
visitNodes((<JsxElement>node).children, visitor, isJsxChild),
10681072
visitNode((<JsxElement>node).closingElement, visitor, isJsxClosingElement));
10691073

1074+
case SyntaxKind.JsxAttributes:
1075+
return updateJsxAttributes(<JsxAttributes>node,
1076+
visitNodes((<JsxAttributes>node).properties, visitor, isJsxAttributeLike));
1077+
10701078
case SyntaxKind.JsxSelfClosingElement:
10711079
return updateJsxSelfClosingElement(<JsxSelfClosingElement>node,
10721080
visitNode((<JsxSelfClosingElement>node).tagName, visitor, isJsxTagNameExpression),
1073-
visitNodes((<JsxSelfClosingElement>node).attributes, visitor, isJsxAttributeLike));
1081+
visitNode((<JsxSelfClosingElement>node).attributes, visitor, isJsxAttributes));
10741082

10751083
case SyntaxKind.JsxOpeningElement:
10761084
return updateJsxOpeningElement(<JsxOpeningElement>node,
10771085
visitNode((<JsxOpeningElement>node).tagName, visitor, isJsxTagNameExpression),
1078-
visitNodes((<JsxOpeningElement>node).attributes, visitor, isJsxAttributeLike));
1086+
visitNode((<JsxOpeningElement>node).attributes, visitor, isJsxAttributes));
10791087

10801088
case SyntaxKind.JsxClosingElement:
10811089
return updateJsxClosingElement(<JsxClosingElement>node,

0 commit comments

Comments
 (0)