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 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/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; }; } 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", diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index f2a504aa4a1d..d31f512eabed 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -90,6 +90,7 @@ function isEntityNameExpression( } export class Converter { + #isInTaggedTemplate = false; private allowPattern = false; private readonly ast: ts.SourceFile; private readonly esTreeNodeToTSNodeMap = new WeakMap(); @@ -401,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; @@ -1889,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, @@ -1917,19 +1953,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: { @@ -1938,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), 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'); + }); + }); }); 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