From 37b9f1a6499f4feaa6a6a5beeb291d1fd27ef97a Mon Sep 17 00:00:00 2001 From: zan mlakar Date: Wed, 7 Oct 2026 21:19:30 +0200 Subject: [PATCH] fix: include string literal children in kind queries --- .../ts-morph/src/compiler/ast/common/Node.ts | 2 +- .../tests/compiler/ast/common/nodeTests.ts | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/ts-morph/src/compiler/ast/common/Node.ts b/packages/ts-morph/src/compiler/ast/common/Node.ts index c5c50025c..e73ec3d3c 100644 --- a/packages/ts-morph/src/compiler/ast/common/Node.ts +++ b/packages/ts-morph/src/compiler/ast/common/Node.ts @@ -4389,7 +4389,7 @@ function* getCompilerDescendantsIterator(node: ts.Node, sourceFile: ts.SourceFil * of having to parse all the tokens via `node.getChildren()`. */ function useParseTreeSearchForKind(thisNodeOrSyntaxKind: Node | SyntaxKind, searchingKind: SyntaxKind) { - return searchingKind >= SyntaxKind.FirstNode && searchingKind < SyntaxKind.FirstJSDocNode + return (searchingKind === SyntaxKind.StringLiteral || searchingKind >= SyntaxKind.FirstNode && searchingKind < SyntaxKind.FirstJSDocNode) && getThisKind() !== SyntaxKind.SyntaxList; function getThisKind() { diff --git a/packages/ts-morph/src/tests/compiler/ast/common/nodeTests.ts b/packages/ts-morph/src/tests/compiler/ast/common/nodeTests.ts index 4b1b2d5dd..a05423198 100644 --- a/packages/ts-morph/src/tests/compiler/ast/common/nodeTests.ts +++ b/packages/ts-morph/src/tests/compiler/ast/common/nodeTests.ts @@ -849,6 +849,40 @@ class MyClass { doTest("class C {} interface I {}", sourceFile => sourceFile.getChildSyntaxListOrThrow(), SyntaxKind.ClassDeclaration, ["class C {}"]); }); + it("should get string literal children of an array literal", () => { + doTest( + "[\"abc\", \"def\"];", + sourceFile => sourceFile.getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression), + SyntaxKind.StringLiteral, + ["\"abc\"", "\"def\""], + ); + }); + + it("should get string literal children of a call expression", () => { + doTest( + "fn(\"abc\", \"def\");", + sourceFile => sourceFile.getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression), + SyntaxKind.StringLiteral, + ["\"abc\"", "\"def\""], + ); + }); + + it("should get string literal children of a syntax list", () => { + doTest( + "[\"abc\", \"def\"];", + sourceFile => sourceFile.getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression).getFirstChildByKindOrThrow(SyntaxKind.SyntaxList), + SyntaxKind.StringLiteral, + ["\"abc\"", "\"def\""], + ); + }); + + it("should get the first and last string literal children by kind", () => { + const { sourceFile } = getInfoFromText("[\"abc\", \"def\"];"); + const arrayLiteral = sourceFile.getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression); + expect(arrayLiteral.getFirstChildByKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue()).to.equal("abc"); + expect(arrayLiteral.getLastChildByKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue()).to.equal("def"); + }); + it("should get children using .getChildren() when specifying a token kind", () => { doTest("class C {}", sourceFile => sourceFile.getClassOrThrow("C"), SyntaxKind.OpenBraceToken, ["{"]); }); @@ -859,6 +893,11 @@ class MyClass { }); describe(nameof("getDescendantsOfKind"), () => { + it("should get nested string literals once in source order", () => { + const { sourceFile } = getInfoFromText("fn(\"abc\", [\"def\", \"ghi\"]);"); + expect(sourceFile.getDescendantsOfKind(SyntaxKind.StringLiteral).map(node => node.getLiteralValue())).to.deep.equal(["abc", "def", "ghi"]); + }); + it("should get the descendant by a kind", () => { const { sourceFile } = getInfoFromText("interface Identifier { prop: string; }\ninterface MyInterface { nextProp: string; }"); const properties = sourceFile.getDescendantsOfKind(SyntaxKind.PropertySignature);