From cd7550fbe4fc20099c358cd6baa31599a34efe4d Mon Sep 17 00:00:00 2001 From: nayounsang Date: Sat, 28 Jun 2025 16:22:04 +0900 Subject: [PATCH 1/6] test: add test case --- .../typescript-estree/tests/lib/parse.test.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/packages/typescript-estree/tests/lib/parse.test.ts b/packages/typescript-estree/tests/lib/parse.test.ts index c371a6e9e7ac..a44bb4952b5c 100644 --- a/packages/typescript-estree/tests/lib/parse.test.ts +++ b/packages/typescript-estree/tests/lib/parse.test.ts @@ -926,4 +926,58 @@ describe(parser.parseAndGenerateServices, () => { }); }, ); + + describe('template literal cooked values', () => { + const getTemplateElement = ( + code: string, + ): parser.TSESTree.TemplateElement | null => { + const result = parser.parse(code, { + comment: true, + loc: true, + range: true, + tokens: true, + }); + + const taggedTemplate = result.body.find( + b => b.type === parser.AST_NODE_TYPES.ExpressionStatement, + ); + const expression = taggedTemplate?.expression; + if (expression?.type !== parser.AST_NODE_TYPES.TaggedTemplateExpression) { + return null; + } + return expression.quasi.quasis[0]; + }; + + it('should set cooked to null for invalid escape sequences in tagged template literals', () => { + const code = 'String.raw`\\uXXXX`'; + const templateElement = getTemplateElement(code); + + expect(templateElement?.value.cooked).toBeNull(); + expect(templateElement?.value.raw).toBe('\\uXXXX'); + }); + + it('should set cooked to null for other invalid escape sequences', () => { + const code = 'String.raw`\\unicode and \\u{55}`'; + const templateElement = getTemplateElement(code); + + expect(templateElement?.value.cooked).toBeNull(); + expect(templateElement?.value.raw).toBe('\\unicode and \\u{55}'); + }); + + it('should set cooked to parsed value for valid escape sequences', () => { + const code = 'String.raw`\\n\\t\\u0041`'; + const templateElement = getTemplateElement(code); + + expect(templateElement?.value.cooked).toBe('\n\tA'); + expect(templateElement?.value.raw).toBe('\\n\\t\\u0041'); + }); + + it('should handle mixed valid and invalid escape sequences', () => { + const code = 'String.raw`\\n\\uXXXX\\t`'; + const templateElement = getTemplateElement(code); + + expect(templateElement?.value.cooked).toBeNull(); + expect(templateElement?.value.raw).toBe('\\n\\uXXXX\\t'); + }); + }); }); From 156b3844a11521c3c22b61b404da53c9eb0e98fb Mon Sep 17 00:00:00 2001 From: nayounsang Date: Sat, 28 Jun 2025 16:52:51 +0900 Subject: [PATCH 2/6] feat: make flag whether node is inside tag --- packages/typescript-estree/src/convert.ts | 33 ++++++++++++++--------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index f2a504aa4a1d..3e194027229c 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -93,6 +93,7 @@ export class Converter { private allowPattern = false; private readonly ast: ts.SourceFile; private readonly esTreeNodeToTSNodeMap = new WeakMap(); + private isInTaggedTemplate = false; private readonly options: ConverterOptions; private readonly tsNodeToESTreeNodeMap = new WeakMap(); @@ -1917,19 +1918,25 @@ export class Converter { return result; } - case SyntaxKind.TaggedTemplateExpression: - return this.createNode(node, { - type: AST_NODE_TYPES.TaggedTemplateExpression, - quasi: this.convertChild(node.template), - tag: this.convertChild(node.tag), - typeArguments: - node.typeArguments && - this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ), - }); - + case SyntaxKind.TaggedTemplateExpression: { + this.isInTaggedTemplate = true; + const result = this.createNode( + node, + { + type: AST_NODE_TYPES.TaggedTemplateExpression, + quasi: this.convertChild(node.template), + tag: this.convertChild(node.tag), + typeArguments: + node.typeArguments && + this.convertTypeArgumentsToTypeParameterInstantiation( + node.typeArguments, + node, + ), + }, + ); + this.isInTaggedTemplate = false; + return result; + } case SyntaxKind.TemplateHead: case SyntaxKind.TemplateMiddle: case SyntaxKind.TemplateTail: { From 6103105154dfdba15c0fe3014b49daa9e01e0d30 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Sat, 28 Jun 2025 17:55:50 +0900 Subject: [PATCH 3/6] fix: if template literal is tagged and the text has an invalid escape, cooked will be null --- packages/typescript-estree/src/convert.ts | 48 ++++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 3e194027229c..d31f512eabed 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -90,10 +90,10 @@ function isEntityNameExpression( } export class Converter { + #isInTaggedTemplate = false; private allowPattern = false; private readonly ast: ts.SourceFile; private readonly esTreeNodeToTSNodeMap = new WeakMap(); - private isInTaggedTemplate = false; private readonly options: ConverterOptions; private readonly tsNodeToESTreeNodeMap = new WeakMap(); @@ -402,6 +402,38 @@ export class Converter { } } + #isValidEscape(arg: string): boolean { + const unicode = /\\u([0-9a-fA-F]{4})/g; + const unicodeBracket = /\\u\{([0-9a-fA-F]+)\}/g; // supports ES6+ + const hex = /\\x([0-9a-fA-F]{2})/g; + const validShort = /\\[nrtbfv0\\'"]/g; + + const allEscapes = + /\\(u\{[^}]*\}|u[0-9a-fA-F]{0,4}|x[0-9a-fA-F]{0,2}|[^ux])/g; + + let match: RegExpExecArray | null; + while ((match = allEscapes.exec(arg)) != null) { + const escape = match[0]; + + if ( + unicode.test(escape) || + (unicodeBracket.test(escape) && + (() => { + const cp = parseInt(escape.match(unicodeBracket)![1], 16); + return cp <= 0x10ffff; + })()) || + hex.test(escape) || + validShort.test(escape) + ) { + continue; + } + + return false; + } + + return true; + } + #throwError(node: number | ts.Node, message: string): asserts node is never { let start; let end; @@ -1890,7 +1922,10 @@ export class Converter { type: AST_NODE_TYPES.TemplateElement, tail: true, value: { - cooked: node.text, + cooked: + this.#isValidEscape(node.text) && this.#isInTaggedTemplate + ? node.text + : null, raw: this.ast.text.slice( node.getStart(this.ast) + 1, node.end - 1, @@ -1919,7 +1954,7 @@ export class Converter { } case SyntaxKind.TaggedTemplateExpression: { - this.isInTaggedTemplate = true; + this.#isInTaggedTemplate = true; const result = this.createNode( node, { @@ -1934,7 +1969,7 @@ export class Converter { ), }, ); - this.isInTaggedTemplate = false; + this.#isInTaggedTemplate = false; return result; } case SyntaxKind.TemplateHead: @@ -1945,7 +1980,10 @@ export class Converter { type: AST_NODE_TYPES.TemplateElement, tail, value: { - cooked: node.text, + cooked: + this.#isValidEscape(node.text) && this.#isInTaggedTemplate + ? node.text + : null, raw: this.ast.text.slice( node.getStart(this.ast) + 1, node.end - (tail ? 1 : 2), From cbeb8d7afd937ddee7bd9f063874e67fce86b64e Mon Sep 17 00:00:00 2001 From: nayounsang Date: Sat, 28 Jun 2025 22:37:28 +0900 Subject: [PATCH 4/6] fix: type error --- packages/ast-spec/src/special/TemplateElement/spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ast-spec/src/special/TemplateElement/spec.ts b/packages/ast-spec/src/special/TemplateElement/spec.ts index cb5d1c6e76f8..dda44172c500 100644 --- a/packages/ast-spec/src/special/TemplateElement/spec.ts +++ b/packages/ast-spec/src/special/TemplateElement/spec.ts @@ -5,7 +5,7 @@ export interface TemplateElement extends BaseNode { type: AST_NODE_TYPES.TemplateElement; tail: boolean; value: { - cooked: string; + cooked: string | null; raw: string; }; } From 954e84e62f20eceb828b9dfa0df97015ab18c497 Mon Sep 17 00:00:00 2001 From: nayounsang Date: Sat, 28 Jun 2025 23:17:59 +0900 Subject: [PATCH 5/6] chore: add snapshot --- .../snapshots/1-TSESTree-AST.shot | 2 +- .../snapshots/5-AST-Alignment-AST.shot | 74 ++++++++++++++++++- .../snapshots/1-TSESTree-AST.shot | 4 +- .../snapshots/5-AST-Alignment-AST.shot | 4 +- .../snapshots/1-TSESTree-AST.shot | 4 +- .../snapshots/5-AST-Alignment-AST.shot | 4 +- .../snapshots/1-TSESTree-AST.shot | 10 +-- .../snapshots/5-AST-Alignment-AST.shot | 10 +-- .../tests/fixtures-with-differences-ast.shot | 1 + 9 files changed, 93 insertions(+), 20 deletions(-) diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-1/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-1/snapshots/1-TSESTree-AST.shot index 88e028508e34..5e0c9090807d 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-1/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-1/snapshots/1-TSESTree-AST.shot @@ -26,7 +26,7 @@ Program { type: "TemplateElement", tail: true, value: { - "cooked": "foo", + "cooked": null, "raw": "foo", }, diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-1/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-1/snapshots/5-AST-Alignment-AST.shot index cdda4e0f0b5b..a24c8bb9f5d6 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-1/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-1/snapshots/5-AST-Alignment-AST.shot @@ -2,4 +2,76 @@ exports[`AST Fixtures > legacy-fixtures > types > template-literal-type-1 > AST Alignment - AST`] Snapshot Diff: -Compared values have no visual difference. +- TSESTree ++ Babel + + Program { + type: 'Program', + body: Array [ + TSTypeAliasDeclaration { + type: 'TSTypeAliasDeclaration', + declare: false, + id: Identifier { + type: 'Identifier', + decorators: Array [], + name: 'T', + optional: false, + + range: [78, 79], + loc: { + start: { column: 5, line: 3 }, + end: { column: 6, line: 3 }, + }, + }, + typeAnnotation: TSLiteralType { + type: 'TSLiteralType', + literal: TemplateLiteral { + type: 'TemplateLiteral', + expressions: Array [], + quasis: Array [ + TemplateElement { + type: 'TemplateElement', + tail: true, + value: Object { +- 'cooked': null, ++ 'cooked': 'foo', + 'raw': 'foo', + }, + + range: [82, 87], + loc: { + start: { column: 9, line: 3 }, + end: { column: 14, line: 3 }, + }, + }, + ], + + range: [82, 87], + loc: { + start: { column: 9, line: 3 }, + end: { column: 14, line: 3 }, + }, + }, + + range: [82, 87], + loc: { + start: { column: 9, line: 3 }, + end: { column: 14, line: 3 }, + }, + }, + + range: [73, 88], + loc: { + start: { column: 0, line: 3 }, + end: { column: 15, line: 3 }, + }, + }, + ], + sourceType: 'script', + + range: [73, 89], + loc: { + start: { column: 0, line: 3 }, + end: { column: 0, line: 4 }, + }, + } diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-2/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-2/snapshots/1-TSESTree-AST.shot index 1065df8f8712..5c59dc2dffde 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-2/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-2/snapshots/1-TSESTree-AST.shot @@ -23,7 +23,7 @@ Program { type: "TemplateElement", tail: false, value: { - "cooked": "foo", + "cooked": null, "raw": "foo", }, @@ -37,7 +37,7 @@ Program { type: "TemplateElement", tail: true, value: { - "cooked": "", + "cooked": null, "raw": "", }, diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-2/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-2/snapshots/5-AST-Alignment-AST.shot index dc7e97974018..b337acde0a87 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-2/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-2/snapshots/5-AST-Alignment-AST.shot @@ -30,7 +30,7 @@ Snapshot Diff: - type: 'TemplateElement', - tail: false, - value: Object { -- 'cooked': 'foo', +- 'cooked': null, - 'raw': 'foo', - }, + typeAnnotation: TSLiteralType { @@ -55,7 +55,7 @@ Snapshot Diff: - type: 'TemplateElement', - tail: true, - value: Object { -- 'cooked': '', +- 'cooked': null, - 'raw': '', - }, + range: [88, 93], diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-3/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-3/snapshots/1-TSESTree-AST.shot index 86bdf5a0561f..7c3b8088d938 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-3/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-3/snapshots/1-TSESTree-AST.shot @@ -169,7 +169,7 @@ Program { type: "TemplateElement", tail: false, value: { - "cooked": "", + "cooked": null, "raw": "", }, @@ -183,7 +183,7 @@ Program { type: "TemplateElement", tail: true, value: { - "cooked": " fish", + "cooked": null, "raw": " fish", }, diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-3/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-3/snapshots/5-AST-Alignment-AST.shot index 5a6cfaa69dbb..d6e6c564df8d 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-3/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-3/snapshots/5-AST-Alignment-AST.shot @@ -176,7 +176,7 @@ Snapshot Diff: - type: 'TemplateElement', - tail: false, - value: Object { -- 'cooked': '', +- 'cooked': null, - 'raw': '', - }, + typeAnnotation: TSLiteralType { @@ -205,7 +205,7 @@ Snapshot Diff: - type: 'TemplateElement', - tail: true, - value: Object { -- 'cooked': ' fish', +- 'cooked': null, - 'raw': ' fish', - }, - diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-4/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-4/snapshots/1-TSESTree-AST.shot index 5a2aee247757..51ecc6523886 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-4/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-4/snapshots/1-TSESTree-AST.shot @@ -23,7 +23,7 @@ Program { type: "TemplateElement", tail: false, value: { - "cooked": "", + "cooked": null, "raw": "", }, @@ -37,7 +37,7 @@ Program { type: "TemplateElement", tail: false, value: { - "cooked": " - ", + "cooked": null, "raw": " - ", }, @@ -51,7 +51,7 @@ Program { type: "TemplateElement", tail: false, value: { - "cooked": " - ", + "cooked": null, "raw": " - ", }, @@ -65,7 +65,7 @@ Program { type: "TemplateElement", tail: false, value: { - "cooked": " - ", + "cooked": null, "raw": " - ", }, @@ -79,7 +79,7 @@ Program { type: "TemplateElement", tail: true, value: { - "cooked": "", + "cooked": null, "raw": "", }, diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-4/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-4/snapshots/5-AST-Alignment-AST.shot index a2d68af67a6b..0e993488ce92 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-4/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/template-literal-type-4/snapshots/5-AST-Alignment-AST.shot @@ -30,7 +30,7 @@ Snapshot Diff: - type: 'TemplateElement', - tail: false, - value: Object { -- 'cooked': '', +- 'cooked': null, - 'raw': '', - }, - @@ -44,7 +44,7 @@ Snapshot Diff: - type: 'TemplateElement', - tail: false, - value: Object { -- 'cooked': ' - ', +- 'cooked': null, - 'raw': ' - ', - }, - @@ -58,7 +58,7 @@ Snapshot Diff: - type: 'TemplateElement', - tail: false, - value: Object { -- 'cooked': ' - ', +- 'cooked': null, - 'raw': ' - ', - }, - @@ -72,7 +72,7 @@ Snapshot Diff: - type: 'TemplateElement', - tail: false, - value: Object { -- 'cooked': ' - ', +- 'cooked': null, - 'raw': ' - ', - }, + typeAnnotation: TSLiteralType { @@ -98,7 +98,7 @@ Snapshot Diff: - type: 'TemplateElement', - tail: true, - value: Object { -- 'cooked': '', +- 'cooked': null, - 'raw': '', - }, + range: [124, 133], diff --git a/packages/ast-spec/tests/fixtures-with-differences-ast.shot b/packages/ast-spec/tests/fixtures-with-differences-ast.shot index a64da89a8106..254d24375d39 100644 --- a/packages/ast-spec/tests/fixtures-with-differences-ast.shot +++ b/packages/ast-spec/tests/fixtures-with-differences-ast.shot @@ -215,6 +215,7 @@ exports[`AST Fixtures > List fixtures with AST differences`] "legacy-fixtures/types/fixtures/optional-variance-out/fixture.ts", "legacy-fixtures/types/fixtures/reference-generic-nested/fixture.ts", "legacy-fixtures/types/fixtures/reference-generic/fixture.ts", + "legacy-fixtures/types/fixtures/template-literal-type-1/fixture.ts", "legacy-fixtures/types/fixtures/template-literal-type-2/fixture.ts", "legacy-fixtures/types/fixtures/template-literal-type-3/fixture.ts", "legacy-fixtures/types/fixtures/template-literal-type-4/fixture.ts", From 8fdb4dbb240fb5356e59c6ec60b6c84c242a6149 Mon Sep 17 00:00:00 2001 From: JamesHenry Date: Tue, 1 Jul 2025 17:47:54 +0400 Subject: [PATCH 6/6] chore: try with SKIP_AST_SPEC_REBUILD false --- .github/actions/prepare-build/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/prepare-build/action.yml b/.github/actions/prepare-build/action.yml index d191358d6503..1c3356230272 100644 --- a/.github/actions/prepare-build/action.yml +++ b/.github/actions/prepare-build/action.yml @@ -24,7 +24,7 @@ runs: run: | yarn nx run types:build env: - SKIP_AST_SPEC_REBUILD: true + SKIP_AST_SPEC_REBUILD: false - name: Build if: steps['build-cache'].outputs.cache-hit != 'true' @@ -33,4 +33,4 @@ runs: run: | yarn nx run-many --target=build --parallel --exclude=website --exclude=website-eslint env: - SKIP_AST_SPEC_REBUILD: true + SKIP_AST_SPEC_REBUILD: false pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy