forked from phenomnomnominal/tsquery
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcli.js
More file actions
130 lines (119 loc) · 3.91 KB
/
cli.js
File metadata and controls
130 lines (119 loc) · 3.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
const os = require('os');
const fs = require('fs');
const path = require('path');
const { Console } = require('console');
const { Transform } = require('stream');
const typescript = require('typescript');
const tsquery = require('./dist/src/index').tsquery;
const glob = require('glob');
const defaultInlineProcessor = 'return $';
const tsqueryrc = `${os.homedir()}/.tsqueryrc`;
const shorthands = {
'class': 'ClassDeclaration > Identifier',
'interface': 'ClassDeclaration > Identifier',
'function': 'FunctionDeclaration > Identifier'
};
if (fs.existsSync(tsqueryrc)) {
const content = fs.readFileSync(tsqueryrc).toString('utf-8');
let config = {};
try {
config = JSON.parse(content);
} catch (e) {
console.error(`.tsqueryrc is not a valid JSON file!`);
}
for (let short in config) {
const val = config[short];
if (!val) continue;
shorthands[short] = val;
}
}
const pattern = process.argv[2];
let query = process.argv[3];
const callback = createNodeProcessor(query, process.argv[4]);
if (callback === -1) {
console.error(`Invalid shorthand ${query}`);
process.exit(-1);
}
if (process.argv.length <= 3) {
printUsage();
process.exit(0);
}
if (callback.query) query = callback.query;
const ts = new Transform({ transform(chunk, enc, cb) { cb(null, chunk) } });
const logger = new Console({ stdout: ts });
let count = 0;
let first = true;
const matched = glob.sync(pattern);
if (process.argv.indexOf('--list-file') > 0) {
console.log(matched);
process.exit(0);
}
process.stdout.write("{\n")
process.stdout.write(` query: "${query}",\n`)
process.stdout.write(` result: [`)
matched.forEach((file, idx) => {
if (fs.statSync(file).isDirectory()) return;
const content = fs.readFileSync(file).toString();
const basename = path.basename(file, path.extname(file));
const ast = tsquery.ast(content);
const nodes = tsquery(ast, query);
const json = {file, basename, count: nodes.length, result: null};
json.result = nodes.map((node) => callback.fn(
node, file, basename,
{matched, index:idx}, {ast, tsquery, code: content, ts: typescript})
).filter(x => x !== false);
if (!json.result.length) return;
if (!first) process.stdout.write(",");
if (first) first = false;
// indent
logger.log(json);
const output = (ts.read() || '').toString().trim().replace(/\t/g, ' ');
process.stdout.write(output.replace(/\n/g, `\n `));
count++;
});
process.stdout.write(`],\n`);
process.stdout.write(` count: ${count},\n`);
process.stdout.write(` script: "${callback.type}",\n`);
process.stdout.write(`}`);
function createNodeProcessor(query, callback) {
const ret = {type: '', fn: null, query: null};
if (callback && callback.endsWith('.js') && fs.existsSync(callback)) {
const p = path.resolve(process.cwd(), callback);
callback = require(p);
}
if (!callback) callback = defaultInlineProcessor;
const params = ['$','$filepath', '$filename', '$files', '$tsq'];
if (callback) {
if (typeof callback === 'function') {
ret.fn = function () {
try {
return callback(...arguments);
} catch (e) {}
}
ret.type = 'script';
} else {
if (query && query[0] === ':') {
const cfg = shorthands[query.substring(1)];
if (cfg) {
if (Array.isArray(cfg) && callback.length >= 2 && typeof cfg[0] === 'string' && typeof cfg[1] === 'string') {
ret.query = cfg[0];
callback = cfg[1];
} else {
ret.query = cfg; // callback is query string
}
} else {
return -1;
}
}
ret.fn = new Function(...params, callback || defaultInlineProcessor);
ret.type = 'inline';
}
} else {
ret.fn = new Function(...params, defaultInlineProcessor);
ret.type = 'default';
}
return ret;
}
function printUsage() {
console.log(`${path.basename(process.argv[1], '.js')} <glob-pattern> <ts-query-string> [inline-script | js-file-path]`);
}