patram 0.10.0 → 0.11.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.
@@ -47,6 +47,7 @@ export type CliParseError = {
47
47
  } | {
48
48
  code: 'unknown_stored_query';
49
49
  name: string;
50
+ next_path?: string;
50
51
  suggestion?: string;
51
52
  } | {
52
53
  code: 'unknown_option';
@@ -9,13 +9,15 @@ import {
9
9
  renderCheckSuccess,
10
10
  } from '../../output/render-check-output.js';
11
11
  import {
12
- resolveCheckTarget,
13
- selectCheckTargetDiagnostics,
14
- selectCheckTargetSourceFiles,
12
+ resolveCheckTargetProjectDirectory,
13
+ resolveCheckTargets,
14
+ selectCheckTargetsDiagnostics,
15
+ selectCheckTargetsSourceFiles,
15
16
  } from '../../output/resolve-check-target.js';
16
17
  import { listRepoFiles } from '../../scan/list-repo-files.js';
17
18
 
18
19
  import { resolveCommandOutputMode } from '../command-helpers.js';
20
+ import { renderCliParseError } from '../render-help.js';
19
21
 
20
22
  /**
21
23
  * @param {ParsedCliCommandRequest} parsed_command
@@ -24,18 +26,28 @@ import { resolveCommandOutputMode } from '../command-helpers.js';
24
26
  */
25
27
  export async function runCheckCommand(parsed_command, io_context) {
26
28
  const output_mode = resolveCommandOutputMode(parsed_command, io_context);
27
- const resolved_target = await resolveCheckTarget(
28
- parsed_command.command_arguments[0],
29
+ const resolved_targets = await resolveCheckTargets(
30
+ parsed_command.command_arguments,
29
31
  );
30
- const project_graph_result = await loadProjectGraph(
31
- resolved_target.project_directory,
32
- );
33
- const repo_file_paths = await listRepoFiles(
34
- resolved_target.project_directory,
35
- );
36
- const selected_source_file_paths = selectCheckTargetSourceFiles(
32
+ const project_directory =
33
+ resolveCheckTargetProjectDirectory(resolved_targets);
34
+
35
+ if (!project_directory) {
36
+ io_context.stderr.write(
37
+ renderCliParseError({
38
+ code: 'message',
39
+ message: 'Check paths must resolve to the same project root.',
40
+ }),
41
+ );
42
+
43
+ return 1;
44
+ }
45
+
46
+ const project_graph_result = await loadProjectGraph(project_directory);
47
+ const repo_file_paths = await listRepoFiles(project_directory);
48
+ const selected_source_file_paths = selectCheckTargetsSourceFiles(
37
49
  project_graph_result.source_file_paths,
38
- resolved_target,
50
+ resolved_targets,
39
51
  );
40
52
 
41
53
  if (project_graph_result.diagnostics.length > 0) {
@@ -52,9 +64,9 @@ export async function runCheckCommand(parsed_command, io_context) {
52
64
  project_graph_result.config,
53
65
  project_graph_result.claims,
54
66
  );
55
- const selected_diagnostics = selectCheckTargetDiagnostics(
67
+ const selected_diagnostics = selectCheckTargetsDiagnostics(
56
68
  diagnostics,
57
- resolved_target,
69
+ resolved_targets,
58
70
  );
59
71
 
60
72
  if (selected_diagnostics.length > 0) {
@@ -4,12 +4,18 @@
4
4
 
5
5
  import process from 'node:process';
6
6
 
7
+ import { manageStoredQueries } from '../../config/manage-stored-queries.js';
7
8
  import { loadPatramConfig } from '../../config/load-patram-config.js';
9
+ import { renderCheckDiagnostics } from '../../output/render-check-output.js';
8
10
  import { writeCommandOutput } from '../../output/command-output.js';
9
11
  import { listQueries } from '../../output/list-queries.js';
10
12
  import { createOutputView } from '../../output/render-output-view.js';
13
+ import { renderCliParseError } from '../render-help.js';
11
14
 
12
- import { writeDiagnostics } from '../command-helpers.js';
15
+ import {
16
+ resolveCommandOutputMode,
17
+ writeDiagnostics,
18
+ } from '../command-helpers.js';
13
19
 
14
20
  /**
15
21
  * @param {ParsedCliCommandRequest} parsed_command
@@ -17,6 +23,10 @@ import { writeDiagnostics } from '../command-helpers.js';
17
23
  * @returns {Promise<number>}
18
24
  */
19
25
  export async function runQueriesCommand(parsed_command, io_context) {
26
+ if (parsed_command.command_arguments.length > 0) {
27
+ return runQueriesMutationCommand(parsed_command, io_context);
28
+ }
29
+
20
30
  const load_result = await loadPatramConfig(process.cwd());
21
31
 
22
32
  if (load_result.diagnostics.length > 0) {
@@ -39,3 +49,181 @@ export async function runQueriesCommand(parsed_command, io_context) {
39
49
 
40
50
  return 0;
41
51
  }
52
+
53
+ /**
54
+ * @param {ParsedCliCommandRequest} parsed_command
55
+ * @param {{ stderr: { write(chunk: string): boolean }, stdout: { isTTY?: boolean, write(chunk: string): boolean } }} io_context
56
+ * @returns {Promise<number>}
57
+ */
58
+ async function runQueriesMutationCommand(parsed_command, io_context) {
59
+ const mutation_request = parseStoredQueryMutation(
60
+ parsed_command.command_arguments,
61
+ );
62
+ const output_mode = resolveCommandOutputMode(parsed_command, io_context);
63
+
64
+ if (!mutation_request.success) {
65
+ io_context.stderr.write(renderCliParseError(mutation_request.error));
66
+
67
+ return 1;
68
+ }
69
+
70
+ const mutation_result = await manageStoredQueries(
71
+ process.cwd(),
72
+ mutation_request.value,
73
+ );
74
+
75
+ if (!mutation_result.success) {
76
+ if ('error' in mutation_result) {
77
+ io_context.stderr.write(renderCliParseError(mutation_result.error));
78
+
79
+ return 1;
80
+ }
81
+
82
+ io_context.stderr.write(
83
+ renderCheckDiagnostics(mutation_result.diagnostics, output_mode),
84
+ );
85
+
86
+ return 1;
87
+ }
88
+
89
+ io_context.stdout.write(
90
+ renderStoredQueryMutationResult(
91
+ mutation_result.value,
92
+ parsed_command.output_mode,
93
+ ),
94
+ );
95
+
96
+ return 0;
97
+ }
98
+
99
+ /**
100
+ * @param {string[]} command_arguments
101
+ * @returns {{
102
+ * success: true,
103
+ * value:
104
+ * | { action: 'add', description?: string, name: string, where: string }
105
+ * | { action: 'remove', name: string }
106
+ * | {
107
+ * action: 'update',
108
+ * description?: string,
109
+ * name: string,
110
+ * next_name?: string,
111
+ * where?: string,
112
+ * },
113
+ * } | { error: import('../arguments.types.ts').CliParseError, success: false }}
114
+ */
115
+ function parseStoredQueryMutation(command_arguments) {
116
+ const subcommand_name = command_arguments[0];
117
+
118
+ if (subcommand_name === 'add') {
119
+ return {
120
+ success: true,
121
+ value: {
122
+ action: 'add',
123
+ description: readOptionValue(command_arguments, '--desc'),
124
+ name: command_arguments[1],
125
+ where: readRequiredOptionValue(command_arguments, '--query'),
126
+ },
127
+ };
128
+ }
129
+
130
+ if (subcommand_name === 'remove') {
131
+ return {
132
+ success: true,
133
+ value: {
134
+ action: 'remove',
135
+ name: command_arguments[1],
136
+ },
137
+ };
138
+ }
139
+
140
+ if (subcommand_name === 'update') {
141
+ return {
142
+ success: true,
143
+ value: {
144
+ action: 'update',
145
+ description: readOptionValue(command_arguments, '--desc'),
146
+ name: command_arguments[1],
147
+ next_name: readOptionValue(command_arguments, '--name'),
148
+ where: readOptionValue(command_arguments, '--query'),
149
+ },
150
+ };
151
+ }
152
+
153
+ return {
154
+ error: {
155
+ code: 'unexpected_argument',
156
+ command_name: 'queries',
157
+ token: subcommand_name ?? '',
158
+ },
159
+ success: false,
160
+ };
161
+ }
162
+
163
+ /**
164
+ * @param {{
165
+ * action: 'added',
166
+ * name: string,
167
+ * } | {
168
+ * action: 'removed',
169
+ * name: string,
170
+ * } | {
171
+ * action: 'updated',
172
+ * name: string,
173
+ * previous_name?: string,
174
+ * }} mutation_result
175
+ * @param {ParsedCliCommandRequest['output_mode']} output_mode
176
+ * @returns {string}
177
+ */
178
+ function renderStoredQueryMutationResult(mutation_result, output_mode) {
179
+ if (output_mode === 'json') {
180
+ return `${JSON.stringify(mutation_result, null, 2)}\n`;
181
+ }
182
+
183
+ if (
184
+ mutation_result.action === 'updated' &&
185
+ mutation_result.previous_name !== undefined
186
+ ) {
187
+ return `Updated stored query: ${mutation_result.previous_name} -> ${mutation_result.name}\n`;
188
+ }
189
+
190
+ if (mutation_result.action === 'updated') {
191
+ return `Updated stored query: ${mutation_result.name}\n`;
192
+ }
193
+
194
+ if (mutation_result.action === 'added') {
195
+ return `Added stored query: ${mutation_result.name}\n`;
196
+ }
197
+
198
+ return `Removed stored query: ${mutation_result.name}\n`;
199
+ }
200
+
201
+ /**
202
+ * @param {string[]} command_arguments
203
+ * @param {'--desc' | '--name' | '--query'} option_name
204
+ * @returns {string}
205
+ */
206
+ function readRequiredOptionValue(command_arguments, option_name) {
207
+ const option_value = readOptionValue(command_arguments, option_name);
208
+
209
+ if (option_value === undefined) {
210
+ throw new Error(`Expected ${option_name} to be present.`);
211
+ }
212
+
213
+ return option_value;
214
+ }
215
+
216
+ /**
217
+ * @param {string[]} command_arguments
218
+ * @param {'--desc' | '--name' | '--query'} option_name
219
+ * @returns {string | undefined}
220
+ */
221
+ function readOptionValue(command_arguments, option_name) {
222
+ const option_index = command_arguments.indexOf(option_name);
223
+
224
+ if (option_index < 0) {
225
+ return undefined;
226
+ }
227
+
228
+ return command_arguments[option_index + 1];
229
+ }
@@ -87,14 +87,15 @@ const COMMAND_DEFINITIONS = {
87
87
  'patram check',
88
88
  'patram check docs',
89
89
  'patram check docs/patram.md',
90
+ 'patram check docs docs/patram.md',
90
91
  ],
91
- extra_positionals_message: 'Check accepts at most one path.',
92
+ extra_positionals_message: 'Check accepts zero or more paths.',
92
93
  help_topics: [],
93
- max_positionals: 1,
94
+ max_positionals: Number.POSITIVE_INFINITY,
94
95
  min_positionals: 0,
95
96
  missing_argument_examples: [],
96
97
  missing_argument_label: null,
97
- missing_usage_lines: ['patram check [path]'],
98
+ missing_usage_lines: ['patram check [path ...]'],
98
99
  option_column_width: 10,
99
100
  options: [
100
101
  {
@@ -110,7 +111,7 @@ const COMMAND_DEFINITIONS = {
110
111
  root_summary: 'Validate a project, directory, or file',
111
112
  summary:
112
113
  'Validate a project, directory, or file and report graph diagnostics.',
113
- usage_lines: ['patram check [path] [options]'],
114
+ usage_lines: ['patram check [path ...] [options]'],
114
115
  },
115
116
  fields: {
116
117
  allowed_option_names: new Set(),
@@ -221,17 +222,40 @@ const COMMAND_DEFINITIONS = {
221
222
  ],
222
223
  },
223
224
  queries: {
224
- allowed_option_names: new Set(),
225
- examples: ['patram queries'],
226
- extra_positionals_message: 'Queries does not accept positional arguments.',
225
+ allowed_option_names: new Set(['desc', 'name', 'query']),
226
+ examples: [
227
+ 'patram queries',
228
+ "patram queries add ready-tasks --query '$class=task and status=ready'",
229
+ "patram queries update ready-tasks --desc 'Show tasks that are ready.'",
230
+ 'patram queries remove ready-tasks',
231
+ ],
232
+ extra_positionals_message:
233
+ 'Queries accepts no positionals unless using add, update, or remove.',
227
234
  help_topics: [],
228
- max_positionals: 0,
235
+ max_positionals: 2,
229
236
  min_positionals: 0,
230
237
  missing_argument_examples: [],
231
238
  missing_argument_label: null,
232
- missing_usage_lines: ['patram queries'],
233
- option_column_width: 10,
239
+ missing_usage_lines: [
240
+ 'patram queries',
241
+ 'patram queries add <name> --query <clause>',
242
+ 'patram queries update <name> [--name <new_name>] [--query <clause>] [--desc <text>]',
243
+ 'patram queries remove <name>',
244
+ ],
245
+ option_column_width: 19,
234
246
  options: [
247
+ {
248
+ description: 'Persist a new stored query',
249
+ label: '--query <clause>',
250
+ },
251
+ {
252
+ description: 'Set or rename the stored query name for update',
253
+ label: '--name <new_name>',
254
+ },
255
+ {
256
+ description: 'Set or clear the stored query description',
257
+ label: '--desc <text>',
258
+ },
235
259
  {
236
260
  description: 'Print plain text output',
237
261
  label: '--plain',
@@ -242,9 +266,15 @@ const COMMAND_DEFINITIONS = {
242
266
  },
243
267
  ],
244
268
  related: ['query'],
245
- root_summary: 'List stored queries',
246
- summary: 'List the stored queries defined in the project configuration.',
247
- usage_lines: ['patram queries [options]'],
269
+ root_summary: 'List and manage stored queries',
270
+ summary:
271
+ 'List stored queries or mutate them through add, update, and remove.',
272
+ usage_lines: [
273
+ 'patram queries [options]',
274
+ 'patram queries add <name> --query <clause> [--desc <text>] [options]',
275
+ 'patram queries update <name> [--name <new_name>] [--query <clause>] [--desc <text>] [options]',
276
+ 'patram queries remove <name> [options]',
277
+ ],
248
278
  },
249
279
  refs: {
250
280
  allowed_option_names: new Set(['where']),