Created
September 16, 2020 07:27
-
-
Save NikhilVerma/b22f8326d0b8f6d988245bb111b2770a to your computer and use it in GitHub Desktop.
AST Finder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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