From b97b1a8de65cfd187e91b6056ad2fa9692644cd6 Mon Sep 17 00:00:00 2001 From: Wenlu Wang Date: Tue, 12 Mar 2019 07:00:18 +0800 Subject: [PATCH] add jsx factory and hold text in jsxtext node (#29439) * add jsx factory and hold text in jsxtext node * update jsxtext prop name and factory --- src/compiler/checker.ts | 6 ++--- src/compiler/emitter.ts | 2 +- src/compiler/factory.ts | 22 +++++++++++++++++++ src/compiler/parser.ts | 3 ++- src/compiler/scanner.ts | 1 + src/compiler/transformers/jsx.ts | 2 +- src/compiler/types.ts | 4 ++-- src/services/utilities.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 8 +++++-- tests/baselines/reference/api/typescript.d.ts | 8 +++++-- 10 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc24a9bebae..b0629eb0400 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11576,7 +11576,7 @@ namespace ts { // child is of the type of the expression return { errorNode: child, innerExpression: child.expression, nameType }; case SyntaxKind.JsxText: - if (child.containsOnlyWhiteSpaces) { + if (child.containsOnlyTriviaWhiteSpaces) { break; // Whitespace only jsx text isn't real jsx text } // child is a string @@ -11600,7 +11600,7 @@ namespace ts { const childrenPropName = childPropName === undefined ? "children" : unescapeLeadingUnderscores(childPropName); const childrenNameType = getLiteralType(childrenPropName); const childrenTargetType = getIndexedAccessType(target, childrenNameType); - const validChildren = filter(containingElement.children, i => !isJsxText(i) || !i.containsOnlyWhiteSpaces); + const validChildren = filter(containingElement.children, i => !isJsxText(i) || !i.containsOnlyTriviaWhiteSpaces); if (!length(validChildren)) { return result; } @@ -18923,7 +18923,7 @@ namespace ts { // In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that // because then type of children property will have constituent of string type. if (child.kind === SyntaxKind.JsxText) { - if (!child.containsOnlyWhiteSpaces) { + if (!child.containsOnlyTriviaWhiteSpaces) { childrenTypes.push(stringType); } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e55e768bab3..97248f402a6 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2979,7 +2979,7 @@ namespace ts { } function emitJsxText(node: JsxText) { - writer.writeLiteral(getTextOfNode(node, /*includeTrivia*/ true)); + writer.writeLiteral(node.text); } function emitJsxClosingElementOrFragment(node: JsxClosingElement | JsxClosingFragment) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index a3bffb785a8..59fd4efb5fb 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2299,6 +2299,28 @@ namespace ts { return node; } + export function createJsxText(text: string, containsOnlyTriviaWhiteSpaces?: boolean) { + const node = createSynthesizedNode(SyntaxKind.JsxText); + node.text = text; + node.containsOnlyTriviaWhiteSpaces = !!containsOnlyTriviaWhiteSpaces; + return node; + } + + export function updateJsxText(node: JsxText, text: string, containsOnlyTriviaWhiteSpaces?: boolean) { + return node.text !== text + || node.containsOnlyTriviaWhiteSpaces !== containsOnlyTriviaWhiteSpaces + ? updateNode(createJsxText(text, containsOnlyTriviaWhiteSpaces), node) + : node; + } + + export function createJsxOpeningFragment() { + return createSynthesizedNode(SyntaxKind.JsxOpeningFragment); + } + + export function createJsxJsxClosingFragment() { + return createSynthesizedNode(SyntaxKind.JsxClosingFragment); + } + export function updateJsxFragment(node: JsxFragment, openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment) { return node.openingFragment !== openingFragment || node.children !== children diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1564b78ca61..03d0f166b81 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4256,7 +4256,8 @@ namespace ts { function parseJsxText(): JsxText { const node = createNode(SyntaxKind.JsxText); - node.containsOnlyWhiteSpaces = currentToken === SyntaxKind.JsxTextAllWhiteSpaces; + node.text = scanner.getTokenValue(); + node.containsOnlyTriviaWhiteSpaces = currentToken === SyntaxKind.JsxTextAllWhiteSpaces; currentToken = scanner.scanJsxToken(); return finishNode(node); } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 5af23cf084f..e31876962b7 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -2013,6 +2013,7 @@ namespace ts { pos++; } + tokenValue = text.substring(startPos, pos); return firstNonWhitespace === -1 ? SyntaxKind.JsxTextAllWhiteSpaces : SyntaxKind.JsxText; } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index c31cdd17f02..2fe343da134 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -182,7 +182,7 @@ namespace ts { } function visitJsxText(node: JsxText): StringLiteral | undefined { - const fixed = fixupWhitespaceAndDecodeEntities(getTextOfNode(node, /*includeTrivia*/ true)); + const fixed = fixupWhitespaceAndDecodeEntities(node.text); return fixed === undefined ? undefined : createLiteral(fixed); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8c0091f0ba6..d65172f8710 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1945,9 +1945,9 @@ namespace ts { expression?: Expression; } - export interface JsxText extends Node { + export interface JsxText extends LiteralLikeNode { kind: SyntaxKind.JsxText; - containsOnlyWhiteSpaces: boolean; + containsOnlyTriviaWhiteSpaces: boolean; parent: JsxElement; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 004135a526e..1e9c02fba96 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -905,7 +905,7 @@ namespace ts { } function isWhiteSpaceOnlyJsxText(node: Node): boolean { - return isJsxText(node) && node.containsOnlyWhiteSpaces; + return isJsxText(node) && node.containsOnlyTriviaWhiteSpaces; } export function isInTemplateString(sourceFile: SourceFile, position: number) { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index e99261be3f3..59ae825a8dc 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1203,9 +1203,9 @@ declare namespace ts { dotDotDotToken?: Token; expression?: Expression; } - interface JsxText extends Node { + interface JsxText extends LiteralLikeNode { kind: SyntaxKind.JsxText; - containsOnlyWhiteSpaces: boolean; + containsOnlyTriviaWhiteSpaces: boolean; parent: JsxElement; } type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment; @@ -3992,6 +3992,10 @@ declare namespace ts { function createJsxClosingElement(tagName: JsxTagNameExpression): JsxClosingElement; function updateJsxClosingElement(node: JsxClosingElement, tagName: JsxTagNameExpression): JsxClosingElement; function createJsxFragment(openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment): JsxFragment; + function createJsxText(text: string, containsOnlyTriviaWhiteSpaces?: boolean): JsxText; + function updateJsxText(node: JsxText, text: string, containsOnlyTriviaWhiteSpaces?: boolean): JsxText; + function createJsxOpeningFragment(): JsxOpeningFragment; + function createJsxJsxClosingFragment(): JsxClosingFragment; function updateJsxFragment(node: JsxFragment, openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment): JsxFragment; function createJsxAttribute(name: Identifier, initializer: StringLiteral | JsxExpression): JsxAttribute; function updateJsxAttribute(node: JsxAttribute, name: Identifier, initializer: StringLiteral | JsxExpression): JsxAttribute; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index d7b49496053..2b8056735a4 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1203,9 +1203,9 @@ declare namespace ts { dotDotDotToken?: Token; expression?: Expression; } - interface JsxText extends Node { + interface JsxText extends LiteralLikeNode { kind: SyntaxKind.JsxText; - containsOnlyWhiteSpaces: boolean; + containsOnlyTriviaWhiteSpaces: boolean; parent: JsxElement; } type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment; @@ -3992,6 +3992,10 @@ declare namespace ts { function createJsxClosingElement(tagName: JsxTagNameExpression): JsxClosingElement; function updateJsxClosingElement(node: JsxClosingElement, tagName: JsxTagNameExpression): JsxClosingElement; function createJsxFragment(openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment): JsxFragment; + function createJsxText(text: string, containsOnlyTriviaWhiteSpaces?: boolean): JsxText; + function updateJsxText(node: JsxText, text: string, containsOnlyTriviaWhiteSpaces?: boolean): JsxText; + function createJsxOpeningFragment(): JsxOpeningFragment; + function createJsxJsxClosingFragment(): JsxClosingFragment; function updateJsxFragment(node: JsxFragment, openingFragment: JsxOpeningFragment, children: ReadonlyArray, closingFragment: JsxClosingFragment): JsxFragment; function createJsxAttribute(name: Identifier, initializer: StringLiteral | JsxExpression): JsxAttribute; function updateJsxAttribute(node: JsxAttribute, name: Identifier, initializer: StringLiteral | JsxExpression): JsxAttribute;