Skip to content

Instantly share code, notes, and snippets.

@NikhilVerma
Created September 16, 2020 07:27
Show Gist options
  • Save NikhilVerma/b22f8326d0b8f6d988245bb111b2770a to your computer and use it in GitHub Desktop.
Save NikhilVerma/b22f8326d0b8f6d988245bb111b2770a to your computer and use it in GitHub Desktop.
AST Finder
/**
* This file is a tool to "grep" for code by using Abstract syntax trees
* It allows developers to do refactorings by searching for code patterns
* which would be hard to express using regular expressions
*/
import { parse, ParserPlugin } from '@babel/parser';
import traverse, { NodePath, TraverseOptions } from '@babel/traverse';
import chalk from 'chalk';
import fs from 'fs';
import glob from 'glob';
import path from 'path';
import { argv } from 'yargs';
const globPattern = (argv.glob as string) || '**/*.{js,jsx,ts,tsx}';
const cwd = argv.cwd
? path.resolve(argv.cwd as string)
: path.resolve(__dirname, '../your/project/dir');
glob.sync(globPattern, {
absolute: true,
dot: true,
cwd,
ignore: [
// exclude
'**/node_modules/**',
],
}).forEach(astFinder);
function astFinder(fileName: string) {
const code = fs.readFileSync(fileName, 'utf8');
try {
const ast = parse(code, {
tokens: true,
sourceFilename: fileName,
sourceType: 'module',
plugins: [
'jsx',
'typescript',
'doExpressions',
'objectRestSpread',
'classProperties',
'exportExtensions',
'asyncGenerators',
'functionBind',
'functionSent',
'dynamicImport',
'templateInvalidEscapes',
] as ParserPlugin[],
});
const visitor = getVisitor(fileName);
traverse(ast, visitor);
} catch (e) {
console.log(chalk.bgRed(`Failed to parse ${fileName}. Call with --debug to see stack`));
if (argv.debug) {
console.error(e);
}
}
}
function getVisitor(fileName: string) {
return {
// Do the magic here...
} as TraverseOptions;
}
// Utility functions
function isObject(value: any): value is object {
return Object.prototype.toString.call(value) === '[object Object]';
}
function isArray(value: any): value is [] {
return Object.prototype.toString.call(value) === '[object Array]';
}
function matches(value: NodePath<any>, mask: any): boolean {
if (Object.prototype.toString.call(mask) === '[object RegExp]') {
return mask.test(value);
} else if (isObject(mask)) {
return (
isObject(value) &&
//@ts-ignore
!Object.entries(mask).some(([key, maskValue]) => !matches(value[key], maskValue))
);
} else if (isArray(mask) && isArray(value)) {
return mask.every((maskItem) => value.some((valueItem) => matches(valueItem, maskItem)));
}
return value === mask;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment