ts-morph icon indicating copy to clipboard operation
ts-morph copied to clipboard

`getChildrenOfKind` does not work for `StringLiteral`

Open char101 opened this issue 6 months ago • 4 comments

Describe the bug

Hi,

getChildrenOfKind returns nothing for SyntaxKind.StringLiteral because useParseTreeSearchForKind returns false which should be true.

Version: 25.0.1

To Reproduce

import { Project, SyntaxKind } from 'ts-morph';

const src = '["abc", "def"];';
const project = new Project();
const source = project.createSourceFile('dummy.js', src);
for (const es of source.getChildrenOfKind(SyntaxKind.ExpressionStatement)) {
  for (const ale of es.getChildrenOfKind(SyntaxKind.ArrayLiteralExpression)) {
    for (const sl of ale.getChildrenOfKind(SyntaxKind.StringLiteral)) {
      console.log(sl); // not found
    }
    for (const sl of ale._getCompilerForEachChildren().filter((c) => c.kind === SyntaxKind.StringLiteral)) {
      console.log(sl); // found
    }
  }
}

Expected behavior

getChildrenOfKind returns the children of StringLiteral.

char101 avatar May 15 '25 09:05 char101

Array literal expressions do not have a direct StringLiteral child when using getChildren()/getChildrenOfKind(). There's a SyntaxList inbetween them.

Image

https://ts-ast-viewer.com/#code/NoIghgRgxiA0AEIAmBTAZiAugbiA (turn on Options > Tree mode > node.getChildren())

In this case, I would recommend using getElements() (or whatever it's called) on ArrayLiteralExpression then filter for string literals.

dsherret avatar May 19 '25 19:05 dsherret

There is also a SyntaxList under CallExpression in the example below, but why could call.getChildrenOfKind(SyntaxKind.ArrayLiteralExpression) find the ArrayLiteralExpression node?

import { Project, SyntaxKind } from 'ts-morph';

// SourceFile
//   SyntaxList
//     ExpressionStatement
//       CallExpression
//         Identifier
//         OpenParenToken
//         SyntaxList
//           ArrayLiteralExpression
//             OpenBracketToken
//             SyntaxList
//               StringLiteral
//               CommaToken
//               StringLiteral
//             CloseBracketToken
//           CloseParenToken
//         SemicolonToken
// EndOfFileToken

const project = new Project();
const source = project.createSourceFile('script.js', 'defineProps(["abc", "def"]);');
source.forEachChild((c) => {
  if (c.getKind() === SyntaxKind.ExpressionStatement) {
    const call = c.getFirstChildByKind(SyntaxKind.CallExpression);
    // there is a SyntaxList under call
    for (const ale of call.getChildrenOfKind(SyntaxKind.ArrayLiteralExpression)) {
      console.log(ale.getKindName()); // found: ArrayLiteralExpression
      // there is a SyntaxList under ArrayLiteralExpression
      for (const sl of ale.getChildrenOfKind(SyntaxKind.StringLiteral)) {
        console.log(sl.getKindName()); // nothing printed
      }
    }
  }
});

char101 avatar May 20 '25 00:05 char101

Oh, sorry. I just looked at the code and see that it's not always using getChildren() internally. It's too late to change this as it would be a breaking change and I highly recommend not using getChildrenOfKind in this case, but rather use ale.getElements().

dsherret avatar May 20 '25 00:05 dsherret

Actually, I kind of feel like it should just return it anyway. Probably a low chance of breaking anyone.

dsherret avatar May 20 '25 00:05 dsherret