patram 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/build-graph-identity.js +39 -7
- package/lib/build-graph.js +14 -1
- package/lib/cli-help-metadata.js +552 -0
- package/lib/derived-summary.js +278 -0
- package/lib/format-derived-summary-row.js +9 -0
- package/lib/format-node-header.js +19 -0
- package/lib/format-output-item-block.js +22 -0
- package/lib/format-output-metadata.js +62 -0
- package/lib/layout-stored-queries.js +150 -2
- package/lib/load-patram-config.js +401 -2
- package/lib/load-patram-config.types.ts +31 -0
- package/lib/output-view.types.ts +15 -0
- package/lib/parse-cli-arguments-helpers.js +263 -90
- package/lib/parse-cli-arguments.js +160 -8
- package/lib/parse-cli-arguments.types.ts +48 -3
- package/lib/parse-where-clause.js +604 -209
- package/lib/parse-where-clause.types.ts +70 -0
- package/lib/patram-cli.js +144 -17
- package/lib/patram.js +6 -0
- package/lib/query-graph.js +231 -119
- package/lib/query-inspection.js +523 -0
- package/lib/render-check-output.js +1 -1
- package/lib/render-cli-help.js +419 -0
- package/lib/render-json-output.js +57 -4
- package/lib/render-output-view.js +37 -8
- package/lib/render-plain-output.js +31 -86
- package/lib/render-rich-output.js +34 -87
- package/lib/resolve-where-clause.js +18 -3
- package/lib/tagged-fenced-block-error.js +17 -0
- package/lib/tagged-fenced-block-markdown.js +111 -0
- package/lib/tagged-fenced-block-metadata.js +97 -0
- package/lib/tagged-fenced-block-parser.js +292 -0
- package/lib/tagged-fenced-blocks.js +100 -0
- package/lib/tagged-fenced-blocks.types.ts +38 -0
- package/package.json +8 -3
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { PatramDiagnostic } from './load-patram-config.types.ts';
|
|
2
|
+
|
|
3
|
+
export type ParsedFieldName = 'id' | 'kind' | 'path' | 'status' | 'title';
|
|
4
|
+
|
|
5
|
+
export interface ParsedFieldTerm {
|
|
6
|
+
field_name: ParsedFieldName;
|
|
7
|
+
kind: 'field';
|
|
8
|
+
operator: '=' | '^=' | '~';
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ParsedFieldSetTerm {
|
|
13
|
+
field_name: ParsedFieldName;
|
|
14
|
+
kind: 'field_set';
|
|
15
|
+
operator: 'in' | 'not in';
|
|
16
|
+
values: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ParsedTraversalTerm {
|
|
20
|
+
column: number;
|
|
21
|
+
direction: 'in' | 'out';
|
|
22
|
+
relation_name: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ParsedRelationTerm {
|
|
26
|
+
column: number;
|
|
27
|
+
kind: 'relation';
|
|
28
|
+
relation_name: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ParsedRelationTargetTerm {
|
|
32
|
+
column: number;
|
|
33
|
+
kind: 'relation_target';
|
|
34
|
+
relation_name: string;
|
|
35
|
+
target_id: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ParsedAggregateComparison = '!=' | '<' | '<=' | '=' | '>' | '>=';
|
|
39
|
+
export type ParsedAggregateName = 'any' | 'count' | 'none';
|
|
40
|
+
|
|
41
|
+
export interface ParsedAggregateTerm {
|
|
42
|
+
aggregate_name: ParsedAggregateName;
|
|
43
|
+
clauses: ParsedClause[];
|
|
44
|
+
comparison?: ParsedAggregateComparison;
|
|
45
|
+
kind: 'aggregate';
|
|
46
|
+
traversal: ParsedTraversalTerm;
|
|
47
|
+
value?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type ParsedTerm =
|
|
51
|
+
| ParsedAggregateTerm
|
|
52
|
+
| ParsedFieldSetTerm
|
|
53
|
+
| ParsedFieldTerm
|
|
54
|
+
| ParsedRelationTargetTerm
|
|
55
|
+
| ParsedRelationTerm;
|
|
56
|
+
|
|
57
|
+
export interface ParsedClause {
|
|
58
|
+
is_negated: boolean;
|
|
59
|
+
term: ParsedTerm;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type ParseWhereClauseResult =
|
|
63
|
+
| {
|
|
64
|
+
clauses: ParsedClause[];
|
|
65
|
+
success: true;
|
|
66
|
+
}
|
|
67
|
+
| {
|
|
68
|
+
diagnostic: PatramDiagnostic;
|
|
69
|
+
success: false;
|
|
70
|
+
};
|
package/lib/patram-cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable max-lines */
|
|
2
2
|
/**
|
|
3
|
-
* @import {
|
|
3
|
+
* @import { ParsedCliCommandRequest } from './parse-cli-arguments.types.ts';
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import process from 'node:process';
|
|
@@ -16,10 +16,17 @@ import { loadPatramConfig } from './load-patram-config.js';
|
|
|
16
16
|
import { loadProjectGraph } from './load-project-graph.js';
|
|
17
17
|
import { parseCliArguments } from './parse-cli-arguments.js';
|
|
18
18
|
import { DEFAULT_QUERY_LIMIT, queryGraph } from './query-graph.js';
|
|
19
|
+
import { inspectQuery, renderQueryInspection } from './query-inspection.js';
|
|
20
|
+
import { createDerivedSummaryEvaluator } from './derived-summary.js';
|
|
19
21
|
import {
|
|
20
22
|
renderCheckDiagnostics,
|
|
21
23
|
renderCheckSuccess,
|
|
22
24
|
} from './render-check-output.js';
|
|
25
|
+
import {
|
|
26
|
+
renderCliParseError,
|
|
27
|
+
renderHelpRequest,
|
|
28
|
+
renderInvalidWhereDiagnostic,
|
|
29
|
+
} from './render-cli-help.js';
|
|
23
30
|
import {
|
|
24
31
|
resolveCheckTarget,
|
|
25
32
|
selectCheckTargetDiagnostics,
|
|
@@ -64,12 +71,20 @@ export async function main(cli_arguments, io_context) {
|
|
|
64
71
|
const parsed_arguments = parseCliArguments(cli_arguments);
|
|
65
72
|
|
|
66
73
|
if (!parsed_arguments.success) {
|
|
67
|
-
io_context.stderr.write(
|
|
74
|
+
io_context.stderr.write(renderCliParseError(parsed_arguments.error));
|
|
68
75
|
|
|
69
76
|
return 1;
|
|
70
77
|
}
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
if (parsed_arguments.value.kind === 'help') {
|
|
80
|
+
io_context.stdout.write(renderHelpRequest(parsed_arguments.value));
|
|
81
|
+
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const parsed_command = /** @type {ParsedCliCommandRequest} */ (
|
|
86
|
+
parsed_arguments.value
|
|
87
|
+
);
|
|
73
88
|
|
|
74
89
|
if (parsed_command.command_name === 'check') {
|
|
75
90
|
return runCheckCommand(parsed_command, io_context);
|
|
@@ -93,7 +108,7 @@ export async function main(cli_arguments, io_context) {
|
|
|
93
108
|
}
|
|
94
109
|
|
|
95
110
|
/**
|
|
96
|
-
* @param {
|
|
111
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
97
112
|
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean }, write_paged_output?: (output_text: string) => Promise<void> }} io_context
|
|
98
113
|
* @returns {Promise<number>}
|
|
99
114
|
*/
|
|
@@ -147,12 +162,27 @@ async function runCheckCommand(parsed_command, io_context) {
|
|
|
147
162
|
}
|
|
148
163
|
|
|
149
164
|
/**
|
|
150
|
-
* @param {
|
|
165
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
151
166
|
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean }, write_paged_output?: (output_text: string) => Promise<void> }} io_context
|
|
152
167
|
* @returns {Promise<number>}
|
|
153
168
|
*/
|
|
154
169
|
async function runQueryCommand(parsed_command, io_context) {
|
|
155
170
|
const use_pager = shouldPageCommandOutput(parsed_command, io_context.stdout);
|
|
171
|
+
const output_mode = resolveOutputMode(parsed_command, {
|
|
172
|
+
is_tty: io_context.stdout.isTTY === true,
|
|
173
|
+
no_color: process.env.NO_COLOR !== undefined,
|
|
174
|
+
term: process.env.TERM,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
if (parsed_command.query_inspection_mode) {
|
|
178
|
+
return runQueryInspectionCommand(
|
|
179
|
+
parsed_command,
|
|
180
|
+
io_context,
|
|
181
|
+
output_mode,
|
|
182
|
+
use_pager,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
156
186
|
const project_graph_result = await loadProjectGraph(process.cwd());
|
|
157
187
|
|
|
158
188
|
if (project_graph_result.diagnostics.length > 0) {
|
|
@@ -174,31 +204,103 @@ async function runQueryCommand(parsed_command, io_context) {
|
|
|
174
204
|
|
|
175
205
|
const query_result = queryGraph(
|
|
176
206
|
project_graph_result.graph,
|
|
177
|
-
where_clause.value,
|
|
207
|
+
where_clause.value.where_clause,
|
|
178
208
|
createQueryPaginationOptions(parsed_command, use_pager),
|
|
179
209
|
);
|
|
180
210
|
|
|
181
211
|
if (query_result.diagnostics.length > 0) {
|
|
182
|
-
|
|
212
|
+
io_context.stderr.write(
|
|
213
|
+
renderInvalidWhereDiagnostic(query_result.diagnostics[0]),
|
|
214
|
+
);
|
|
183
215
|
|
|
184
216
|
return 1;
|
|
185
217
|
}
|
|
186
218
|
|
|
219
|
+
const derived_summary_evaluator = createDerivedSummaryEvaluator(
|
|
220
|
+
project_graph_result.config,
|
|
221
|
+
project_graph_result.graph,
|
|
222
|
+
);
|
|
223
|
+
|
|
187
224
|
await writeCommandOutput(
|
|
188
225
|
io_context,
|
|
189
226
|
parsed_command,
|
|
190
|
-
createOutputView(
|
|
191
|
-
|
|
192
|
-
query_result
|
|
193
|
-
|
|
194
|
-
),
|
|
227
|
+
createOutputView('query', query_result.nodes, {
|
|
228
|
+
derived_summary_evaluator,
|
|
229
|
+
...createQueryOutputOptions(parsed_command, query_result, use_pager),
|
|
230
|
+
}),
|
|
195
231
|
);
|
|
196
232
|
|
|
197
233
|
return 0;
|
|
198
234
|
}
|
|
199
235
|
|
|
200
236
|
/**
|
|
201
|
-
* @param {
|
|
237
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
238
|
+
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean } }} io_context
|
|
239
|
+
* @param {import('./output-view.types.ts').ResolvedOutputMode} output_mode
|
|
240
|
+
* @param {boolean} use_pager
|
|
241
|
+
* @returns {Promise<number>}
|
|
242
|
+
*/
|
|
243
|
+
async function runQueryInspectionCommand(
|
|
244
|
+
parsed_command,
|
|
245
|
+
io_context,
|
|
246
|
+
output_mode,
|
|
247
|
+
use_pager,
|
|
248
|
+
) {
|
|
249
|
+
const query_inspection_mode = parsed_command.query_inspection_mode;
|
|
250
|
+
|
|
251
|
+
if (!query_inspection_mode) {
|
|
252
|
+
throw new Error('Expected a query inspection mode.');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const load_result = await loadPatramConfig(process.cwd());
|
|
256
|
+
|
|
257
|
+
if (load_result.diagnostics.length > 0) {
|
|
258
|
+
io_context.stderr.write(
|
|
259
|
+
renderCheckDiagnostics(load_result.diagnostics, output_mode),
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
return 1;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const repo_config = load_result.config;
|
|
266
|
+
|
|
267
|
+
if (!repo_config) {
|
|
268
|
+
throw new Error('Expected a valid Patram repo config.');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const where_clause = resolveWhereClause(
|
|
272
|
+
repo_config,
|
|
273
|
+
parsed_command.command_arguments,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
if (!where_clause.success) {
|
|
277
|
+
io_context.stderr.write(`${where_clause.message}\n`);
|
|
278
|
+
|
|
279
|
+
return 1;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const query_inspection = inspectQuery(repo_config, where_clause.value, {
|
|
283
|
+
inspection_mode: query_inspection_mode,
|
|
284
|
+
...createQueryExecutionOptions(parsed_command, use_pager),
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
if (!query_inspection.success) {
|
|
288
|
+
io_context.stderr.write(
|
|
289
|
+
renderCheckDiagnostics(query_inspection.diagnostics, output_mode),
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
return 1;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
io_context.stdout.write(
|
|
296
|
+
renderQueryInspection(query_inspection.value, output_mode),
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
return 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
202
304
|
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean }, write_paged_output?: (output_text: string) => Promise<void> }} io_context
|
|
203
305
|
* @returns {Promise<number>}
|
|
204
306
|
*/
|
|
@@ -227,7 +329,7 @@ async function runQueriesCommand(parsed_command, io_context) {
|
|
|
227
329
|
}
|
|
228
330
|
|
|
229
331
|
/**
|
|
230
|
-
* @param {
|
|
332
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
231
333
|
* @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean }, write_paged_output?: (output_text: string) => Promise<void> }} io_context
|
|
232
334
|
* @returns {Promise<number>}
|
|
233
335
|
*/
|
|
@@ -252,10 +354,18 @@ async function runShowCommand(parsed_command, io_context) {
|
|
|
252
354
|
return 1;
|
|
253
355
|
}
|
|
254
356
|
|
|
357
|
+
const derived_summary_evaluator = createDerivedSummaryEvaluator(
|
|
358
|
+
project_graph_result.config,
|
|
359
|
+
project_graph_result.graph,
|
|
360
|
+
);
|
|
361
|
+
|
|
255
362
|
await writeCommandOutput(
|
|
256
363
|
io_context,
|
|
257
364
|
parsed_command,
|
|
258
|
-
createShowOutputView(show_output.value
|
|
365
|
+
createShowOutputView(show_output.value, {
|
|
366
|
+
derived_summary_evaluator,
|
|
367
|
+
graph_nodes: project_graph_result.graph.nodes,
|
|
368
|
+
}),
|
|
259
369
|
);
|
|
260
370
|
|
|
261
371
|
return 0;
|
|
@@ -272,7 +382,7 @@ function writeDiagnostics(output_stream, diagnostics) {
|
|
|
272
382
|
}
|
|
273
383
|
|
|
274
384
|
/**
|
|
275
|
-
* @param {
|
|
385
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
276
386
|
* @param {{ total_count: number, nodes: import('./build-graph.types.ts').GraphNode[] }} query_result
|
|
277
387
|
* @param {boolean} use_pager
|
|
278
388
|
* @returns {{ hints: string[], limit: number, offset: number, total_count: number }}
|
|
@@ -309,7 +419,7 @@ function createQueryOutputOptions(parsed_command, query_result, use_pager) {
|
|
|
309
419
|
}
|
|
310
420
|
|
|
311
421
|
/**
|
|
312
|
-
* @param {
|
|
422
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
313
423
|
* @param {boolean} use_pager
|
|
314
424
|
* @returns {{ limit?: number, offset: number }}
|
|
315
425
|
*/
|
|
@@ -328,6 +438,23 @@ function createQueryPaginationOptions(parsed_command, use_pager) {
|
|
|
328
438
|
return pagination_options;
|
|
329
439
|
}
|
|
330
440
|
|
|
441
|
+
/**
|
|
442
|
+
* @param {ParsedCliCommandRequest} parsed_command
|
|
443
|
+
* @param {boolean} use_pager
|
|
444
|
+
* @returns {{ limit: number | null, offset: number }}
|
|
445
|
+
*/
|
|
446
|
+
function createQueryExecutionOptions(parsed_command, use_pager) {
|
|
447
|
+
const pagination_options = createQueryPaginationOptions(
|
|
448
|
+
parsed_command,
|
|
449
|
+
use_pager,
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
limit: pagination_options.limit ?? null,
|
|
454
|
+
offset: pagination_options.offset,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
331
458
|
/**
|
|
332
459
|
* @param {import('./load-patram-config.types.ts').PatramDiagnostic} diagnostic
|
|
333
460
|
* @returns {string}
|