Skip to content

Commit bcf22a2

Browse files
committed
refactor increase cli performance
1 parent 49a383b commit bcf22a2

File tree

5 files changed

+1779
-96
lines changed

5 files changed

+1779
-96
lines changed

cli.ts

Lines changed: 81 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import crypto from "crypto";
44
import fs from "fs";
55
import path from "path";
6-
import child_process from "child_process";
6+
import spawn from "await-spawn";
7+
import flattenDeep from "lodash.flattendeep";
8+
import chunk from "lodash.chunk";
79

810
import ts from "typescript";
911
import { program } from "commander";
@@ -65,7 +67,7 @@ program
6567
.description(
6668
`Explain all your sqls in your code to test them. \n\nEg: ts-sql-plugin -p ./my_ts_projet/tsconfig.json -c 'psql -c'`,
6769
)
68-
.action(() => {
70+
.action(async () => {
6971
let cli_config: any = program.opts();
7072
if (typeof cli_config.command !== "string") {
7173
cli_config.command = undefined;
@@ -78,7 +80,7 @@ program
7880
}
7981
const { config: ts_config } = ts.parseConfigFileTextToJson(
8082
ts_config_path,
81-
fs.readFileSync(ts_config_path, { encoding: "utf8" }),
83+
await fs.promises.readFile(ts_config_path, { encoding: "utf8" }),
8284
);
8385
let plugin_config: TsSqlPluginConfig = (ts_config.compilerOptions.plugins as any[])?.find(
8486
it => it.name === "ts-sql-plugin",
@@ -120,105 +122,99 @@ program
120122
let has_error = false;
121123
let report_errors: [sourceFile: ts.SourceFile, node: ts.Node, message: string, level?: 1 | 2][] = [];
122124
console.log("-- Start init sql check and emit...");
123-
initProgram.getSourceFiles().forEach(f => {
124-
if (!cli_config.exclude.test(f.fileName)) {
125-
delint(f);
126-
}
127-
});
125+
for (const file of initProgram.getSourceFiles().filter(f => !cli_config.exclude.test(f.fileName))) {
126+
await delint(file);
127+
}
128128
if (has_error) {
129129
console.log("\n\n-- Your code can not pass all sql test!!!\n");
130130
report_errors.forEach(args => report(...args));
131131
process.exit(1);
132132
}
133133
console.log("\n\n-- Init sql check and emit finished.\n");
134134

135-
function delint(sourceFile: ts.SourceFile) {
136-
delintNode(sourceFile);
135+
function recursiveAllChildrenNodes(node: ts.Node): ts.Node[] {
136+
return (node.getChildren().map(n => [n, ...recursiveAllChildrenNodes(n)]) as unknown) as ts.Node[];
137+
}
138+
139+
async function delint(sourceFile: ts.SourceFile) {
140+
const treeNodes = recursiveAllChildrenNodes(sourceFile);
141+
const sqlTagNodes = flattenDeep(treeNodes).filter(
142+
(n): n is ts.TaggedTemplateExpression =>
143+
n.kind === ts.SyntaxKind.TaggedTemplateExpression &&
144+
(n as ts.TaggedTemplateExpression).tag.getText() === plugin_config.tags.sql,
145+
);
146+
147+
await Promise.all(sqlTagNodes.map(delintNode));
137148

138-
function delintNode(node: ts.Node) {
139-
if (node.kind === ts.SyntaxKind.TaggedTemplateExpression) {
140-
let n = node as ts.TaggedTemplateExpression;
141-
if (n.tag.getText() === plugin_config.tags.sql) {
142-
let query_configs = fake_expression(n);
143-
for (const qc of query_configs) {
144-
let s: string = trim_middle_comments(qc.text).replace(/\?\?/gm, plugin_config.mock);
149+
async function delintNode(node: ts.TaggedTemplateExpression) {
150+
let query_configs = fake_expression(node);
151+
for (const qc of query_configs) {
152+
let s: string = trim_middle_comments(qc.text).replace(/\?\?/gm, plugin_config.mock);
145153

146-
const directives = parseDirectives(s);
147-
if (cli_config.emit_dir) {
148-
const emit_directive = directives.find(d => d.directive === "emit");
149-
if (emit_directive) {
150-
const fileName = (emit_directive.arg as string) ?? crypto.createHash("sha1").update(s).digest("hex");
151-
const filePath = `${cli_config.emit_dir}/${fileName}.sql`;
152-
try {
153-
fs.writeFileSync(filePath, s + ";");
154-
} catch (err) {
155-
console.log(`-- Emit Error occured, when emitting file "${filePath}"`);
156-
}
157-
}
154+
const directives = parseDirectives(s);
155+
if (cli_config.emit_dir) {
156+
const emit_directive = directives.find(d => d.directive === "emit");
157+
if (emit_directive) {
158+
const fileName = (emit_directive.arg as string) ?? crypto.createHash("sha1").update(s).digest("hex");
159+
const filePath = `${cli_config.emit_dir}/${fileName}.sql`;
160+
try {
161+
fs.writeFileSync(filePath, s + ";");
162+
} catch (err) {
163+
console.log(`-- Emit Error occured, when emitting file "${filePath}"`);
158164
}
165+
}
166+
}
167+
168+
console.log(`\n\n-- EXPLAIN\n${s};`);
169+
const [_command, ..._command_args] = (shq
170+
.parse(plugin_config.command)
171+
.concat("EXPLAIN " + s) as any) as string[];
172+
const p = await spawn(_command, _command_args).catch((e: Error & { stderr: string }) => e);
173+
const stdout = p.toString();
174+
if (p instanceof Error) {
175+
has_error = true;
176+
report(sourceFile, node, p.stderr);
177+
report_errors.push([sourceFile, node, p.stderr]);
178+
break;
179+
}
159180

160-
console.log(`\n\n-- EXPLAIN\n${s};`);
161-
let stdout = "";
162-
// try {
163-
// stdout = child_process.execSync(`${plugin_config.command} ${quote([`EXPLAIN ${s}`])}`, { encoding: 'utf8', windowsHide: true });
164-
// } catch (error) {
165-
// has_error = true;
166-
// report(sourceFile, node, error.stderr);
167-
// break;
168-
// }
169-
const [_command, ..._command_args] = (shq
170-
.parse(plugin_config.command)
171-
.concat("EXPLAIN " + s) as any) as string[];
172-
const p = child_process.spawnSync(_command, _command_args, { encoding: "utf8" });
173-
stdout = p.stdout;
174-
if (p.status) {
181+
if (
182+
(plugin_config.error_cost || plugin_config.warn_cost || plugin_config.info_cost) &&
183+
!directives.some(d => d.directive === "ignore-cost")
184+
) {
185+
const match = stdout.match(cost_pattern);
186+
if (match) {
187+
const [_, cost_str] = match;
188+
const cost = Number(cost_str);
189+
if (cost > plugin_config.error_cost!) {
175190
has_error = true;
176-
report(sourceFile, node, p.stderr);
177-
report_errors.push([sourceFile, node, p.stderr]);
191+
report(sourceFile, node, `Error: explain cost is too high: ${cost}\n${s}`, 2);
192+
report_errors.push([sourceFile, node, `Error: explain cost is too high: ${cost}\n${s}`, 2]);
178193
break;
194+
} else if (cost > plugin_config.warn_cost!) {
195+
report(sourceFile, node, `Warn: explain cost is at warning: ${cost}\n${s}`, 2);
196+
} else if (cost > plugin_config.info_cost!) {
197+
report(sourceFile, node, `Info: explain cost is ok: ${cost}\n${s}`, 1);
179198
}
180-
181-
if (
182-
(plugin_config.error_cost || plugin_config.warn_cost || plugin_config.info_cost) &&
183-
!directives.some(d => d.directive === "ignore-cost")
184-
) {
185-
const match = stdout.match(cost_pattern);
186-
if (match) {
187-
const [_, cost_str] = match;
188-
const cost = Number(cost_str);
189-
if (cost > plugin_config.error_cost!) {
190-
has_error = true;
191-
report(sourceFile, node, `Error: explain cost is too high: ${cost}\n${s}`, 2);
192-
report_errors.push([sourceFile, node, `Error: explain cost is too high: ${cost}\n${s}`, 2]);
193-
break;
194-
} else if (cost > plugin_config.warn_cost!) {
195-
report(sourceFile, node, `Warn: explain cost is at warning: ${cost}\n${s}`, 2);
196-
} else if (cost > plugin_config.info_cost!) {
197-
report(sourceFile, node, `Info: explain cost is ok: ${cost}\n${s}`, 1);
198-
}
199-
} else {
200-
has_error = true;
201-
report(
202-
sourceFile,
203-
node,
204-
`Error: can not extract cost with cost_pattern: ${cost_pattern.source}\n${stdout}\n${s}`,
205-
2,
206-
);
207-
report_errors.push([
208-
sourceFile,
209-
node,
210-
`Error: can not extract cost with cost_pattern: ${cost_pattern.source}\n${stdout}\n${s}`,
211-
2,
212-
]);
213-
break;
214-
}
215-
}
199+
} else {
200+
has_error = true;
201+
report(
202+
sourceFile,
203+
node,
204+
`Error: can not extract cost with cost_pattern: ${cost_pattern.source}\n${stdout}\n${s}`,
205+
2,
206+
);
207+
report_errors.push([
208+
sourceFile,
209+
node,
210+
`Error: can not extract cost with cost_pattern: ${cost_pattern.source}\n${stdout}\n${s}`,
211+
2,
212+
]);
213+
break;
216214
}
217215
}
218216
}
219-
220-
ts.forEachChild(node, delintNode);
221217
}
222218
}
223219
})
224-
.parse(process.argv);
220+
.parseAsync(process.argv);

0 commit comments

Comments
 (0)