Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@phenomnomnominal/tsquery

phenomnomnominal9.7mMIT6.1.3TypeScript support: included

Query TypeScript ASTs with the esquery API!

readme

TSQuery

npm version

TSQuery is a port of the ESQuery API for TypeScript! TSQuery allows you to query a TypeScript AST for patterns of syntax using a CSS style selector system.

Demos:

ESQuery demo - note that the demo requires JavaScript code, not TypeScript TSQuery demo by Uri Shaked

Installation

npm install @phenomnomnominal/tsquery --save-dev

Examples

Say we want to select all instances of an identifier with name "Animal", e.g. the identifier in the class declaration, and the identifier in the extends declaration.

We would do something like the following:

import { ast, query } from '@phenomnomnominal/tsquery';

const typescript = `

class Animal {
    constructor(public name: string) { }
    move(distanceInMeters: number = 0) {
        console.log(\`\${this.name} moved \${distanceInMeters}m.\`);
    }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move(distanceInMeters = 5) {
        console.log("Slithering...");
        super.move(distanceInMeters);
    }
}

`;

const ast = ast(typescript);
const nodes = query(ast, 'Identifier[name="Animal"]');
console.log(nodes.length); // 2

Selectors

The following selectors are supported:

Common AST node types

  • Identifier - any identifier (name of a function, class, variable, etc)
  • IfStatement, ForStatement, WhileStatement, DoStatement - control flow
  • FunctionDeclaration, ClassDeclaration, ArrowFunction - declarations
  • VariableStatement - var, const, let.
  • ImportDeclaration - any import statement
  • StringLiteral - any string
  • TrueKeyword, FalseKeyword, NullKeyword, AnyKeyword - various keywords
  • CallExpression - function call
  • NumericLiteral - any numeric constant
  • NoSubstitutionTemplateLiteral, TemplateExpression - template strings and expressions

API:

ast:

Parse a string of code into an Abstract Syntax Tree which can then be queried with TSQuery Selectors.

import { ast } from '@phenomnomnominal/tsquery';

const sourceFile = ast('const x = 1;');

includes:

Check for Nodes within a given string of code or AST Node matching a Selector.

import { includes } from '@phenomnomnominal/tsquery';

const hasIdentifier = includes('const x = 1;', 'Identifier');

map:

Transform AST Nodes within a given Node matching a Selector. Can be used to do Node-based replacement or removal of parts of the input AST.

import { factory } from 'typescript';
import { map } from '@phenomnomnominal/tsquery';

const tree = ast('const x = 1;')
const updatedTree = map(tree, 'Identifier', () => factory.createIdentifier('y'));

match:

Find AST Nodes within a given AST Node matching a Selector.

import { ast, match } from '@phenomnomnominal/tsquery';

const tree = ast('const x = 1;')
const [xNode] = match(tree, 'Identifier');

parse:

Parse a string into an ESQuery Selector.

import { parse } from '@phenomnomnominal/tsquery';

const selector = parse(':matches([attr] > :first-child, :last-child)');

Print a given Node or SourceFile to a string, using the default TypeScript printer.

import { print } from '@phenomnomnominal/tsquery';
import { factory } from 'typescript';

  // create synthetic node:
const node = factory.createArrowFunction(
  // ...
);
const code = print(node);

project:

Get all the SourceFiles included in a the TypeScript project described by a given config file.

import { project } from '@phenomnomnominal/tsquery';

const files = project('./tsconfig.json');

files:

Get all the file paths included ina the TypeScript project described by a given config file.

import { files } from '@phenomnomnominal/tsquery';

const filePaths = files('./tsconfig.json');

match:

Find AST Nodes within a given string of code or AST Node matching a Selector.

import {query } from '@phenomnomnominal/tsquery';

const [xNode] = query('const x = 1;', 'Identifier');

replace:

Transform AST Nodes within a given Node matching a Selector. Can be used to do string-based replacement or removal of parts of the input AST. The updated code will be printed with the TypeScript Printer, so you may need to run your own formatter on any output code.

import { replace } from '@phenomnomnominal/tsquery';

const updatedCode = replace('const x = 1;', 'Identifier', () => 'y'));

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[6.1.2] - 2023-07-12

Fixed

  • Restored working implementation of :nth-child() and :nth-last-child().

[6.1.1] - 2023-07-12

Fixed

  • Fixed the API signature of map, so it only works on a SourceFile.

[6.1.0] - 2023-07-11

Added

  • Made the print function public, it's useful when doing map operations.

[6.0.1] - 2023-07-11

Fixed

  • Add @types/esquery to dependencies.

[6.0.0] - 2023-07-11

I had to use TSQuery recently and found a few bugs, and wanted to add a few ergonomic things, so here's a major release.

The big breaking change here is that visitAllChildren is now the default behaviour. Less-specific queries that may have worked in previous versions may no longer work exactly the same. This is a pretty annoying change for a very early mistake, but I figured it was time to pull off the band-aid.

Added

  • scriptKind parameter to query so the caller can control how TypeScript parses the input code.
  • includes to simply check if there are any selector matches within some code.
  • Direct exports of public functions, e.g. import { ast } from '@phenomnomnominal/tquery';
  • Type exports for types used in the public API. This includes types from typescript and esquery.
  • This CHANGELOG file to hopefully list all API changes going forward.

Fixed

  • replace now uses the TypeScript Printer to format the output code. This means that it will handle AST Node removal better, but also means that you may need to run any formatters (e.g. Prettier) on the result to have it match your codebase.
  • :function selector will now match a MethodDeclaration.

Changed

  • TSQuery will now query all children by default. This means that less-specific queries that may have worked in previous versions may no longer work exactly the same.
  • Deprecated the old API, will remove in v7. Prefer importing the specific functions.
  • Deprecated the syntaxKindName function. This shouldn't have been in the public API.
  • Upgrade many dependencies.

Removed

  • visitAllChildren option. This is now the default behaviour.