diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 712c96a49f9..203b804adb6 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1331,6 +1331,7 @@ namespace ts { case SyntaxKind.TypeLiteral: case SyntaxKind.JSDocTypeLiteral: case SyntaxKind.JSDocRecordType: + case SyntaxKind.JsxAttributes: return ContainerFlags.IsContainer; case SyntaxKind.InterfaceDeclaration: @@ -1437,6 +1438,7 @@ namespace ts { case SyntaxKind.InterfaceDeclaration: case SyntaxKind.JSDocRecordType: case SyntaxKind.JSDocTypeLiteral: + case SyntaxKind.JsxAttributes: // Interface/Object-types always have their children added to the 'members' of // their container. They are only accessible through an instance of their // container, and are never in scope otherwise (even inside the body of the @@ -1621,6 +1623,14 @@ namespace ts { return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__object"); } + function bindJsxAttributes(node: JsxAttributes) { + return bindAnonymousDeclaration(node, SymbolFlags.ObjectLiteral, "__jsxAttributes"); + } + + function bindJsxAttribute(node: JsxAttribute, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { + return declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); + } + function bindAnonymousDeclaration(node: Declaration, symbolFlags: SymbolFlags, name: string) { const symbol = createSymbol(symbolFlags, name); addDeclarationToSymbol(symbol, node, symbolFlags); @@ -2042,6 +2052,12 @@ namespace ts { case SyntaxKind.ModuleDeclaration: return bindModuleDeclaration(node); + // Jsx-attributes + case SyntaxKind.JsxAttributes: + return bindJsxAttributes(node); + case SyntaxKind.JsxAttribute: + return bindJsxAttribute(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); + // Imports and exports case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.NamespaceImport: @@ -3118,6 +3134,7 @@ namespace ts { case SyntaxKind.JsxText: case SyntaxKind.JsxClosingElement: case SyntaxKind.JsxAttribute: + case SyntaxKind.JsxAttributes: case SyntaxKind.JsxSpreadAttribute: case SyntaxKind.JsxExpression: // These nodes are Jsx syntax. diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c795cab14a3..cedb4d44c5d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -368,7 +368,9 @@ namespace ts { case SyntaxKind.JsxSelfClosingElement: case SyntaxKind.JsxOpeningElement: return visitNode(cbNode, (node).tagName) || - visitNodes(cbNodes, (node).attributes); + visitNode(cbNode, (node).attributes); + case SyntaxKind.JsxAttributes: + return visitNodes(cbNodes, (node).properties); case SyntaxKind.JsxAttribute: return visitNode(cbNode, (node).name) || visitNode(cbNode, (node).initializer); @@ -3865,14 +3867,20 @@ namespace ts { return result; } + function parseJsxAttributes(): JsxAttributes { + const jsxAttributes = createNode(SyntaxKind.JsxAttributes); + jsxAttributes.properties = parseList(ParsingContext.JsxAttributes, parseJsxAttribute); + return finishNode(jsxAttributes); + } + function parseJsxOpeningOrSelfClosingElement(inExpressionContext: boolean): JsxOpeningElement | JsxSelfClosingElement { const fullStart = scanner.getStartPos(); parseExpected(SyntaxKind.LessThanToken); const tagName = parseJsxElementName(); + const attributes = parseJsxAttributes(); - const attributes = parseList(ParsingContext.JsxAttributes, parseJsxAttribute); let node: JsxOpeningLikeElement; if (token() === SyntaxKind.GreaterThanToken) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 51347c8d3df..e85e6f6aacd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -330,6 +330,7 @@ JsxOpeningElement, JsxClosingElement, JsxAttribute, + JsxAttributes, JsxSpreadAttribute, JsxExpression, @@ -725,6 +726,7 @@ // SyntaxKind.BindingElement // SyntaxKind.Property // SyntaxKind.PropertyAssignment + // SyntaxKind.JsxAttribute // SyntaxKind.ShorthandPropertyAssignment // SyntaxKind.EnumMember // SyntaxKind.JSDocPropertyTag @@ -1444,7 +1446,7 @@ template: TemplateLiteral; } - export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator; + export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement; export interface AsExpression extends Expression { kind: SyntaxKind.AsExpression; @@ -1481,35 +1483,39 @@ closingElement: JsxClosingElement; } + /// Either the opening tag in a ... pair, or the lone in a self-closing form + export type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement; + + export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute; + export type JsxTagNameExpression = PrimaryExpression | PropertyAccessExpression; + export interface JsxAttributes extends ObjectLiteralExpressionBase { + } + /// The opening element of a ... JsxElement export interface JsxOpeningElement extends Expression { kind: SyntaxKind.JsxOpeningElement; tagName: JsxTagNameExpression; - attributes: NodeArray; + attributes: JsxAttributes; } /// A JSX expression of the form export interface JsxSelfClosingElement extends PrimaryExpression { kind: SyntaxKind.JsxSelfClosingElement; tagName: JsxTagNameExpression; - attributes: NodeArray; + attributes: JsxAttributes; } - /// Either the opening tag in a ... pair, or the lone in a self-closing form - export type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement; - - export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute; - - export interface JsxAttribute extends Node { - kind: SyntaxKind.JsxAttribute; + // @kind(SyntaxKind.JsxAttribute) + export interface JsxAttribute extends ObjectLiteralElement { name: Identifier; /// JSX attribute initializers are optional; is sugar for initializer?: StringLiteral | JsxExpression; } - export interface JsxSpreadAttribute extends Node { + // @kind(SyntaxKind.JsxSpreadAttribute) + export interface JsxSpreadAttribute extends ObjectLiteralElement { kind: SyntaxKind.JsxSpreadAttribute; expression: Expression; } @@ -2397,7 +2403,7 @@ /** Unlike `getExportsOfModule`, this includes properties of an `export =` value. */ /* @internal */ getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[]; - getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type; + getAllAttributesTypeFromJsxOpeningLikeElement(elementNode: JsxOpeningLikeElement): Type; getJsxIntrinsicTagNames(): Symbol[]; isOptionalParameter(node: ParameterDeclaration): boolean; getAmbientModules(): Symbol[]; @@ -2697,6 +2703,7 @@ /* @internal */ isReferenced?: boolean; // True if the symbol is referenced elsewhere /* @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? /* @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments + /* @internal */ syntheticKind?: SyntheticSymbolKind; // Synthetic symbols are either spread or union/intersection } /* @internal */ @@ -2778,7 +2785,7 @@ isVisible?: boolean; // Is this node visible hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with - resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element + resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt. superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing switchTypes?: Type[]; // Cached array of switch case expression types @@ -2814,6 +2821,8 @@ /* @internal */ ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type NonPrimitive = 1 << 24, // intrinsic object type + /* @internal */ + JsxAttributes = 1 << 25, // Jsx attributes type /* @internal */ Nullable = Undefined | Null, @@ -2995,7 +3004,7 @@ /* @internal */ // Object literals are initially marked fresh. Freshness disappears following an assignment, - // before a type assertion, or when when an object literal's type is widened. The regular + // before a type assertion, or when an object literal's type is widened. The regular // version of a fresh type is identical except for the TypeFlags.FreshObjectLiteral flag. export interface FreshObjectLiteralType extends ResolvedType { regularType: ResolvedType; // Regular version of fresh type diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 932e75d0345..5d9362df242 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1133,6 +1133,8 @@ namespace ts { export function isCallLikeExpression(node: Node): node is CallLikeExpression { switch (node.kind) { + case SyntaxKind.JsxOpeningElement: + case SyntaxKind.JsxSelfClosingElement: case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: case SyntaxKind.TaggedTemplateExpression: @@ -1147,6 +1149,9 @@ namespace ts { if (node.kind === SyntaxKind.TaggedTemplateExpression) { return (node).tag; } + else if (isJsxOpeningLikeElement(node)) { + return node.tagName; + } // Will either be a CallExpression, NewExpression, or Decorator. return (node).expression; @@ -4048,6 +4053,7 @@ namespace ts { || kind === SyntaxKind.ImportEqualsDeclaration || kind === SyntaxKind.ImportSpecifier || kind === SyntaxKind.InterfaceDeclaration + || kind === SyntaxKind.JsxAttribute || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.ModuleDeclaration @@ -4160,6 +4166,11 @@ namespace ts { || kind === SyntaxKind.JsxText; } + export function isJsxAttributes(node: Node): node is JsxAttributes { + const kind = node.kind; + return kind === SyntaxKind.JsxAttributes; + } + export function isJsxAttributeLike(node: Node): node is JsxAttributeLike { const kind = node.kind; return kind === SyntaxKind.JsxAttribute @@ -4174,6 +4185,10 @@ namespace ts { return node.kind === SyntaxKind.JsxAttribute; } + export function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeElement { + return node.kind === SyntaxKind.JsxOpeningElement || node.kind === SyntaxKind.JsxSelfClosingElement; + } + export function isStringLiteralOrJsxExpression(node: Node): node is StringLiteral | JsxExpression { const kind = node.kind; return kind === SyntaxKind.StringLiteral