GithubHelp home page GithubHelp logo

ninofiliu / esgrep Goto Github PK

View Code? Open in Web Editor NEW
12.0 3.0 0.0 405 KB

Syntactically-aware grep for JavaScript and TypeScript

Home Page: https://npmjs.com/package/esgrep

License: GNU General Public License v3.0

TypeScript 95.06% JavaScript 4.94%
ast cli grep javascript js regex ts typescript

esgrep's Introduction

example

ESGrep

Syntactically-aware grep for JavaScript and TypeScript

Usage as a CLI

Install it with npm install --global esgrep or the equivalent using pnpm/yarn/etc, then use it as esgrep [OPTION...] PATTERN [FILE...]. If FILE is not precised, reads from stdin.

The CLI is basically a wrapper around find and accepts the same options and a few more that handle help print and output format. This means that these are logically equivalent:

Reading from stdin:

echo 'const x: number = 10' | esgrep 'const x = ES_ANY'
find("const x: number = 10", "const x = ES_ANY");

Reading from files:

esgrep 'fetch(ES_ANY, { method: "POST" })' api.js lib.ts
find('fetch(ES_ANY, { method: "POST" })', readFileSomehow("api.js"));
find('fetch(ES_ANY, { method: "POST" })', readFileSomehow("lib.ts"));

Passing arguments:

esgrep --statement -- '(() => {})()' file.js
find("(() => {})()", readFileSomehow("file.js"), { statement: true });

Note

When you pass esgrep --foo bar baz, esgrep has no way to know whether bar is the value of --foo (current behavior) or if it is a positional argument just like baz and --foo is a flag option.

You can tell esgrep to explicitly start parsing positional arguments by using --:

esgrep --foo bar baz
# Parsed options: { foo: 'bar' }
# Parsed positional arguments: ['baz']
esgrep --foo -- bar baz
# Parsed options: { foo: true }
# parsed positional arguments: ['bar', 'baz']

Usage as a library

Install it with npm install esgrep or the equivalent using pnpm/yarn/etc, then import it:

import { find, findStrings } from "esgrep";
// or
const { find, findStrings } = require("esgrep");

For now, the lib only targets Node but it's in the roadmap to target Deno and the Web.

Types should be included in the build so refer to them for the exact types of arguments and returned values. This doc focuses on esgrep scenari rather than spec details.

find(pattern, haystack, options?)

pattern and haystack are strings that represent javascript or typescript code which will be matched based on their AST (and not string representation).

options is detailed in options.

find is a Generator that yields estree nodes.

import { find } from "esgrep";

const pattern = "const x = 10";
const haystack = "const x = /* a number */ 10; const y = 20;";
const matches = find(pattern, haystack);
for (const match of matches) {
  console.log(match);
  // {
  //   type: 'VariableDeclaration',
  //   declarations: [ ... ],
  //   kind: 'const',
  //   range: [ 0, 28 ],
  //   loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 28 } }
  // }
}

If you're not comfortable with generators or don't want to use the perf and streaming capabilities they can provide, you can just spread it out into a plain array:

console.log([...matches]);
// [ { type: 'VariableDeclaration', ... }]

findStrings(pattern, haystack, options?)

Basically the same as find but iterates over matched strings rather that matched nodes.

import { findStrings } from "esgrep";

const pattern = "const x = 10";
const haystack = "const x = /* a number */ 10; const y = 20;";
console.log([...findStrings(pattern, haystack)]);
// [ 'const x = /* a number */ 10;' ]

Options

-h, --help (CLI only)

Prints the synopsis and lists CLI options.

user@host$ esgrep -h

-f, --format {pretty,oneline,jsonl} (CLI only)

Defines the output format of the search

pretty (default): easy to read output

user@host$ esgrep 'const tasks = ES_ANY' src/cli/main.ts
─────────────────────┐
src/cli/main.ts:20:2 │
   ┌─────────────────┘
20 │   const tasks =
21 │     paths.length === 0
22 │       ? [{ path: "stdin", read: readStdin }]
23 │       : paths.map((path) => ({ path, read: () => readFile(path) }));

oneline: streams out lines of the shape $path:$line$column:$match_in_one_line

user@host$ esgrep --format oneline 'const tasks = ES_ANY' src/cli/main.ts
src/cli/main.ts:18:2:const tasks = paths.length === 0 ? [{ path: "stdin", read: readStdin }] : paths.map((path) => ({ path, read: () => readFile(path) }));

jsonl: streams out lines of the shape { path: string, match: Node } where Node is an ESTree node

user@host$ esgrep user users.ts
{"path":"users.ts","match":{"type":"Identifier","name":"user","range":[148,152],"loc":{"start":{"line":4,"column":24},"end":{"line":4,"column":28}}}}

count: do not show matches, only show the number of matches per file. column-separated

user@host$ esgrep user ./src/**/*.{ts,js}
./src/api.js:10
./src/users.js:4
./src/types.ts:0

Note

It is by design that ESGrep doesn't exactly match Grep output options, either because some option didn't make sense anymore, was not useful, or could be achieved by a little bit of bash-fu

  • grep --count ...: esgrep --format count ...
  • grep --files-without-match ...: esgrep --format count ... | grep ':0$' | cut -d ':' -f 1
  • grep --files-with-matches ...: esgrep --format count ... | grep -v ':0$' | cut -d ':' -f 1
  • grep --with-filename ...: always on
  • grep --no-filename ...: always off
  • grep --line-number ...: always on
  • grep --invert-match ...: not useful
  • grep --only-matching ...: not useful

-t, --ts

Include type annotations in the comparison

import { findStrings } from "esgrep";

const withTS = "const x: number = 10";
const withoutTS = "const x = 10";

console.log([...findStrings(withoutTS, withTS)]);
// [ 'const x: number = 10' ]
console.log([...findStrings(withoutTS, withTS, { ts: true })]);
// []

-r, --raw

Differentiate between strings in single quotes, double quotes, and template literals

import { findStrings } from "esgrep";

const haystack = `
const single = 'hello';
const double = "hello";
const template = \`hello\`
`;
console.log([...findStrings('"hello"', haystack)]);
// [ "'hello'", '"hello"', '`hello`' ]
console.log([...findStrings('"hello"', haystack, { raw: true })]);
// [ "'hello'" ]

-s, --statement

If the pattern is an expression statement, lookup the statement itself, and not the expression statement.

import { findStrings } from "esgrep";

const pattern = "10";
const haystack = "const x = 10";
console.log([...findStrings(pattern, haystack)]);
// [ '10' ]
console.log([...findStrings(pattern, haystack, { statement: true })]);
// []

Note The first search matches the 10 in const x = 10 because it looks up all expressions of 10. In plain English, it looks up all the occurrences of "just" the number 10. That is the most intuitive and default behavior.

The second search does not match the 10 in const x = 10 because it looks up all statements of consisting of only the expression 10. In plain English, statements are anything that makes the exact same sense when adding a ; at the end.

The difference is subtle and it usually takes people not familiar with the concept a few articles to have a good grasp on statements vs expressions. Why not start with this one?

ES Expressions

Additionally to its options, patterns are allowed to be flexible thanks to ES expressions - special expressions that allow for something else than perfect matches.

ES_ANY

Matches anything

import { findStrings } from "esgrep";

console.log([...findStrings("ES_ANY", "fn('foo', 'bar')")]);
// [
//   "fn('foo', 'bar')", // matches the expression
//   "fn('foo', 'bar')", // matches the statement (string is the same, not the node)
//   "fn",
//   "'foo'",
//   "'bar'"
// ];
console.log([...findStrings("x = ES_ANY", "x = 10; x = 'hello'")]);
// [
//   "x = 10",
//   "x = 'hello'"
// ];

ES_NOT

Matches anything but the first argument

import { findStrings } from "esgrep";

const pattern = "const x = ES_NOT(10)";
const haystack = "const x = 20";
console.log([...findStrings(pattern, haystack)]);
// [ 'const x = 20' ]

ES_EVERY

Matches if all expressions passed as argument match

import { findStrings } from "esgrep";

const pattern = `const x = ES_EVERY(
  'hello',
  "hello",
  ES_ANY
)`;
const haystack = "const x = 'hello'";
console.log([...findStrings(pattern, haystack)]);
// [ "const x = 'hello'" ]

ES_SOME

Matches if at least one expression passed as argument matches

import { findStrings } from "esgrep";

const pattern = `const x = ES_SOME(
  "hello",
  "goodbye"
)`;
const haystack = "const x = 'hello'";
console.log([...findStrings(pattern, haystack)]);
// [ "const x = 'hello'" ]

About

Want to report a bug? Don't understant the doc? Suggest an improvement? Open an issue!

Contributions are welcomed, the code is still small, clean, and all in TS, so it should be readable and extendable without additional guidance. For big changes, consider opening an issue before opening a PR.

Coded w/ ♡ by Nino Filiu

esgrep's People

Contributors

ninofiliu avatar

Stargazers

Ahmad Amireh avatar Pascal Duez avatar  avatar Kye Hohenberger avatar Luke Harold Miles avatar Web Converters avatar Aboubakar Sidik avatar darker avatar Kostas Minaidis avatar  avatar sonia v avatar Supermosh avatar

Watchers

Ahmad Amireh avatar James Cloos avatar  avatar

esgrep's Issues

use strict: command not found

Hello,

The built script is missing a shebang and so fails to run as-is:

$ esgrep ...
/home/baptiste/.n/bin/esgrep: line 1: use strict: command not found
/home/baptiste/.n/bin/esgrep: line 2: syntax error near unexpected token `('
/home/baptiste/.n/bin/esgrep: line 2: `var __importDefault = (this && this.__importDefault) || function (mod) {'

This is the same issue as browserslist/browserslist-useragent-regexp#11.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.