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.
- package/lib/cli/arguments.types.d.ts +1 -0
- package/lib/cli/commands/check.js +27 -15
- package/lib/cli/commands/queries.js +189 -1
- package/lib/cli/help-metadata.js +43 -13
- package/lib/cli/parse-arguments-helpers.js +278 -34
- package/lib/cli/render-help.js +8 -2
- package/lib/config/load-patram-config.d.ts +11 -0
- package/lib/config/load-patram-config.js +9 -88
- package/lib/config/manage-stored-queries-helpers.d.ts +69 -0
- package/lib/config/manage-stored-queries-helpers.js +262 -0
- package/lib/config/manage-stored-queries-jsonc.d.ts +31 -0
- package/lib/config/manage-stored-queries-jsonc.js +95 -0
- package/lib/config/manage-stored-queries.d.ts +77 -0
- package/lib/config/manage-stored-queries.js +294 -0
- package/lib/config/validate-patram-config-value.d.ts +13 -0
- package/lib/config/validate-patram-config-value.js +119 -0
- package/lib/graph/query/resolve.d.ts +6 -0
- package/lib/graph/query/resolve.js +4 -1
- package/lib/output/resolve-check-target.js +120 -11
- package/lib/patram.d.ts +8 -0
- package/package.json +2 -1
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { CliParseError } from '../cli/arguments.types.ts';
|
|
3
|
+
* @import {
|
|
4
|
+
* PatramDiagnostic,
|
|
5
|
+
* PatramRepoConfig,
|
|
6
|
+
* } from './load-patram-config.types.ts';
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { resolve } from 'node:path';
|
|
10
|
+
|
|
11
|
+
import { loadPatramConfig } from './load-patram-config.js';
|
|
12
|
+
import {
|
|
13
|
+
createStoredQueryDefinition,
|
|
14
|
+
createUpdatedStoredQueryDefinition,
|
|
15
|
+
ensureRawQueries,
|
|
16
|
+
loadRawConfig,
|
|
17
|
+
persistStoredQueryMutation,
|
|
18
|
+
rawQueryValueToRecord,
|
|
19
|
+
} from './manage-stored-queries-helpers.js';
|
|
20
|
+
import { CONFIG_FILE_NAME } from './schema.js';
|
|
21
|
+
import { createUnknownStoredQueryError } from '../graph/query/resolve.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {{
|
|
25
|
+
* action: 'add',
|
|
26
|
+
* description?: string,
|
|
27
|
+
* name: string,
|
|
28
|
+
* where: string,
|
|
29
|
+
* } | {
|
|
30
|
+
* action: 'remove',
|
|
31
|
+
* name: string,
|
|
32
|
+
* } | {
|
|
33
|
+
* action: 'update',
|
|
34
|
+
* description?: string,
|
|
35
|
+
* name: string,
|
|
36
|
+
* next_name?: string,
|
|
37
|
+
* where?: string,
|
|
38
|
+
* }} StoredQueryMutation
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @typedef {{
|
|
43
|
+
* action: 'added',
|
|
44
|
+
* name: string,
|
|
45
|
+
* } | {
|
|
46
|
+
* action: 'removed',
|
|
47
|
+
* name: string,
|
|
48
|
+
* } | {
|
|
49
|
+
* action: 'updated',
|
|
50
|
+
* name: string,
|
|
51
|
+
* previous_name?: string,
|
|
52
|
+
* }} StoredQueryMutationResult
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {string} project_directory
|
|
57
|
+
* @param {StoredQueryMutation} stored_query_mutation
|
|
58
|
+
* @returns {Promise<
|
|
59
|
+
* | { success: true, value: StoredQueryMutationResult }
|
|
60
|
+
* | { diagnostics: PatramDiagnostic[], success: false }
|
|
61
|
+
* | { error: CliParseError, success: false }
|
|
62
|
+
* >}
|
|
63
|
+
*/
|
|
64
|
+
export async function manageStoredQueries(
|
|
65
|
+
project_directory,
|
|
66
|
+
stored_query_mutation,
|
|
67
|
+
) {
|
|
68
|
+
const load_result = await loadPatramConfig(project_directory);
|
|
69
|
+
|
|
70
|
+
if (load_result.diagnostics.length > 0) {
|
|
71
|
+
return {
|
|
72
|
+
diagnostics: load_result.diagnostics,
|
|
73
|
+
success: false,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const repo_config = load_result.config;
|
|
78
|
+
|
|
79
|
+
if (!repo_config) {
|
|
80
|
+
throw new Error('Expected a valid Patram repo config.');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const config_file_path = resolve(project_directory, CONFIG_FILE_NAME);
|
|
84
|
+
const raw_config_result = await loadRawConfig(config_file_path);
|
|
85
|
+
|
|
86
|
+
if (!raw_config_result.success) {
|
|
87
|
+
return {
|
|
88
|
+
diagnostics: [raw_config_result.diagnostic],
|
|
89
|
+
success: false,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (stored_query_mutation.action === 'add') {
|
|
94
|
+
return applyAddStoredQuery(
|
|
95
|
+
config_file_path,
|
|
96
|
+
raw_config_result.value,
|
|
97
|
+
repo_config,
|
|
98
|
+
stored_query_mutation,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (stored_query_mutation.action === 'remove') {
|
|
103
|
+
return applyRemoveStoredQuery(
|
|
104
|
+
config_file_path,
|
|
105
|
+
raw_config_result.value,
|
|
106
|
+
repo_config,
|
|
107
|
+
stored_query_mutation,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return applyUpdateStoredQuery(
|
|
112
|
+
config_file_path,
|
|
113
|
+
raw_config_result.value,
|
|
114
|
+
repo_config,
|
|
115
|
+
stored_query_mutation,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {string} config_file_path
|
|
121
|
+
* @param {Record<string, unknown>} raw_config
|
|
122
|
+
* @param {PatramRepoConfig} repo_config
|
|
123
|
+
* @param {{ description?: string, name: string, where: string }} stored_query_mutation
|
|
124
|
+
* @returns {Promise<
|
|
125
|
+
* | { success: true, value: StoredQueryMutationResult }
|
|
126
|
+
* | { diagnostics: PatramDiagnostic[], success: false }
|
|
127
|
+
* | { error: CliParseError, success: false }
|
|
128
|
+
* >}
|
|
129
|
+
*/
|
|
130
|
+
async function applyAddStoredQuery(
|
|
131
|
+
config_file_path,
|
|
132
|
+
raw_config,
|
|
133
|
+
repo_config,
|
|
134
|
+
stored_query_mutation,
|
|
135
|
+
) {
|
|
136
|
+
if (repo_config.queries[stored_query_mutation.name]) {
|
|
137
|
+
return {
|
|
138
|
+
error: {
|
|
139
|
+
code: 'message',
|
|
140
|
+
message: `Stored query already exists: ${stored_query_mutation.name}.`,
|
|
141
|
+
},
|
|
142
|
+
success: false,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const raw_queries = ensureRawQueries(raw_config);
|
|
147
|
+
raw_queries[stored_query_mutation.name] = createStoredQueryDefinition(
|
|
148
|
+
stored_query_mutation.where,
|
|
149
|
+
stored_query_mutation.description,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return persistStoredQueryMutation(config_file_path, raw_config, {
|
|
153
|
+
action: 'added',
|
|
154
|
+
name: stored_query_mutation.name,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {string} config_file_path
|
|
160
|
+
* @param {Record<string, unknown>} raw_config
|
|
161
|
+
* @param {PatramRepoConfig} repo_config
|
|
162
|
+
* @param {{ name: string }} stored_query_mutation
|
|
163
|
+
* @returns {Promise<
|
|
164
|
+
* | { success: true, value: StoredQueryMutationResult }
|
|
165
|
+
* | { diagnostics: PatramDiagnostic[], success: false }
|
|
166
|
+
* | { error: CliParseError, success: false }
|
|
167
|
+
* >}
|
|
168
|
+
*/
|
|
169
|
+
async function applyRemoveStoredQuery(
|
|
170
|
+
config_file_path,
|
|
171
|
+
raw_config,
|
|
172
|
+
repo_config,
|
|
173
|
+
stored_query_mutation,
|
|
174
|
+
) {
|
|
175
|
+
if (!repo_config.queries[stored_query_mutation.name]) {
|
|
176
|
+
return {
|
|
177
|
+
error: createQueryMutationUnknownStoredQueryError(
|
|
178
|
+
stored_query_mutation.name,
|
|
179
|
+
Object.keys(repo_config.queries),
|
|
180
|
+
'remove',
|
|
181
|
+
),
|
|
182
|
+
success: false,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const raw_queries = ensureRawQueries(raw_config);
|
|
187
|
+
delete raw_queries[stored_query_mutation.name];
|
|
188
|
+
|
|
189
|
+
return persistStoredQueryMutation(config_file_path, raw_config, {
|
|
190
|
+
action: 'removed',
|
|
191
|
+
name: stored_query_mutation.name,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @param {string} config_file_path
|
|
197
|
+
* @param {Record<string, unknown>} raw_config
|
|
198
|
+
* @param {PatramRepoConfig} repo_config
|
|
199
|
+
* @param {{ description?: string, name: string, next_name?: string, where?: string }} stored_query_mutation
|
|
200
|
+
* @returns {Promise<
|
|
201
|
+
* | { success: true, value: StoredQueryMutationResult }
|
|
202
|
+
* | { diagnostics: PatramDiagnostic[], success: false }
|
|
203
|
+
* | { error: CliParseError, success: false }
|
|
204
|
+
* >}
|
|
205
|
+
*/
|
|
206
|
+
async function applyUpdateStoredQuery(
|
|
207
|
+
config_file_path,
|
|
208
|
+
raw_config,
|
|
209
|
+
repo_config,
|
|
210
|
+
stored_query_mutation,
|
|
211
|
+
) {
|
|
212
|
+
const existing_query = repo_config.queries[stored_query_mutation.name];
|
|
213
|
+
|
|
214
|
+
if (!existing_query) {
|
|
215
|
+
return {
|
|
216
|
+
error: createQueryMutationUnknownStoredQueryError(
|
|
217
|
+
stored_query_mutation.name,
|
|
218
|
+
Object.keys(repo_config.queries),
|
|
219
|
+
'update',
|
|
220
|
+
),
|
|
221
|
+
success: false,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const next_name =
|
|
226
|
+
stored_query_mutation.next_name ?? stored_query_mutation.name;
|
|
227
|
+
|
|
228
|
+
if (
|
|
229
|
+
next_name !== stored_query_mutation.name &&
|
|
230
|
+
repo_config.queries[next_name]
|
|
231
|
+
) {
|
|
232
|
+
return {
|
|
233
|
+
error: {
|
|
234
|
+
code: 'message',
|
|
235
|
+
message: `Stored query already exists: ${next_name}.`,
|
|
236
|
+
},
|
|
237
|
+
success: false,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const raw_queries = ensureRawQueries(raw_config);
|
|
242
|
+
const raw_query_value = rawQueryValueToRecord(
|
|
243
|
+
raw_queries[stored_query_mutation.name],
|
|
244
|
+
);
|
|
245
|
+
const next_query = createUpdatedStoredQueryDefinition(
|
|
246
|
+
raw_query_value,
|
|
247
|
+
existing_query,
|
|
248
|
+
stored_query_mutation,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
if (next_name !== stored_query_mutation.name) {
|
|
252
|
+
delete raw_queries[stored_query_mutation.name];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
raw_queries[next_name] = next_query;
|
|
256
|
+
|
|
257
|
+
return persistStoredQueryMutation(config_file_path, raw_config, {
|
|
258
|
+
action: 'updated',
|
|
259
|
+
name: next_name,
|
|
260
|
+
previous_name:
|
|
261
|
+
next_name === stored_query_mutation.name
|
|
262
|
+
? undefined
|
|
263
|
+
: stored_query_mutation.name,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* @param {string} stored_query_name
|
|
269
|
+
* @param {string[]} stored_query_names
|
|
270
|
+
* @param {'remove' | 'update'} subcommand_name
|
|
271
|
+
* @returns {CliParseError}
|
|
272
|
+
*/
|
|
273
|
+
function createQueryMutationUnknownStoredQueryError(
|
|
274
|
+
stored_query_name,
|
|
275
|
+
stored_query_names,
|
|
276
|
+
subcommand_name,
|
|
277
|
+
) {
|
|
278
|
+
const parse_error = createUnknownStoredQueryError(
|
|
279
|
+
stored_query_name,
|
|
280
|
+
stored_query_names,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
if (
|
|
284
|
+
parse_error.code !== 'unknown_stored_query' ||
|
|
285
|
+
parse_error.suggestion === undefined
|
|
286
|
+
) {
|
|
287
|
+
return parse_error;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
...parse_error,
|
|
292
|
+
next_path: `patram queries ${subcommand_name} ${parse_error.suggestion}`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {unknown} config_value
|
|
3
|
+
* @returns {{ config: PatramRepoConfig, success: true } | { diagnostics: PatramDiagnostic[], success: false }}
|
|
4
|
+
*/
|
|
5
|
+
export function validatePatramConfigValue(config_value: unknown): {
|
|
6
|
+
config: PatramRepoConfig;
|
|
7
|
+
success: true;
|
|
8
|
+
} | {
|
|
9
|
+
diagnostics: PatramDiagnostic[];
|
|
10
|
+
success: false;
|
|
11
|
+
};
|
|
12
|
+
import type { PatramRepoConfig } from './load-patram-config.types.d.ts';
|
|
13
|
+
import type { PatramDiagnostic } from './load-patram-config.types.d.ts';
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import {
|
|
3
|
+
* PatramDiagnostic,
|
|
4
|
+
* PatramRepoConfig,
|
|
5
|
+
* } from './load-patram-config.types.ts';
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { patram_repo_config_schema } from './schema.js';
|
|
9
|
+
import { normalizeRepoConfig } from './defaults.js';
|
|
10
|
+
import {
|
|
11
|
+
validateDerivedSummaries,
|
|
12
|
+
validateFieldSchemaConfig,
|
|
13
|
+
validateGraphSchema,
|
|
14
|
+
validateLegacyConfigShape,
|
|
15
|
+
validateStoredQueries,
|
|
16
|
+
} from './validation.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {unknown} config_value
|
|
20
|
+
* @returns {{ config: PatramRepoConfig, success: true } | { diagnostics: PatramDiagnostic[], success: false }}
|
|
21
|
+
*/
|
|
22
|
+
export function validatePatramConfigValue(config_value) {
|
|
23
|
+
const legacy_config_diagnostics = validateLegacyConfigShape(config_value);
|
|
24
|
+
|
|
25
|
+
if (legacy_config_diagnostics.length > 0) {
|
|
26
|
+
return {
|
|
27
|
+
diagnostics: legacy_config_diagnostics,
|
|
28
|
+
success: false,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const config_result = patram_repo_config_schema.safeParse(config_value);
|
|
33
|
+
|
|
34
|
+
if (!config_result.success) {
|
|
35
|
+
return {
|
|
36
|
+
diagnostics: config_result.error.issues.map(createValidationDiagnostic),
|
|
37
|
+
success: false,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const graph_schema_diagnostics = validateGraphSchema(config_result.data);
|
|
42
|
+
|
|
43
|
+
if (graph_schema_diagnostics.length > 0) {
|
|
44
|
+
return {
|
|
45
|
+
diagnostics: graph_schema_diagnostics,
|
|
46
|
+
success: false,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const normalized_config = normalizeRepoConfig(config_result.data);
|
|
51
|
+
const field_schema_diagnostics = validateFieldSchemaConfig(normalized_config);
|
|
52
|
+
|
|
53
|
+
if (field_schema_diagnostics.length > 0) {
|
|
54
|
+
return {
|
|
55
|
+
diagnostics: field_schema_diagnostics,
|
|
56
|
+
success: false,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const stored_query_diagnostics = validateStoredQueries(normalized_config);
|
|
61
|
+
|
|
62
|
+
if (stored_query_diagnostics.length > 0) {
|
|
63
|
+
return {
|
|
64
|
+
diagnostics: stored_query_diagnostics,
|
|
65
|
+
success: false,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const derived_summary_diagnostics =
|
|
70
|
+
validateDerivedSummaries(normalized_config);
|
|
71
|
+
|
|
72
|
+
if (derived_summary_diagnostics.length > 0) {
|
|
73
|
+
return {
|
|
74
|
+
diagnostics: derived_summary_diagnostics,
|
|
75
|
+
success: false,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
config: normalized_config,
|
|
81
|
+
success: true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {import('zod').core.$ZodIssue} issue
|
|
87
|
+
* @returns {PatramDiagnostic}
|
|
88
|
+
*/
|
|
89
|
+
function createValidationDiagnostic(issue) {
|
|
90
|
+
const issue_path = formatIssuePath(issue.path);
|
|
91
|
+
|
|
92
|
+
if (issue_path) {
|
|
93
|
+
return {
|
|
94
|
+
code: 'config.invalid',
|
|
95
|
+
column: 1,
|
|
96
|
+
level: 'error',
|
|
97
|
+
line: 1,
|
|
98
|
+
message: `Invalid config at "${issue_path}": ${issue.message}`,
|
|
99
|
+
path: '.patram.json',
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
code: 'config.invalid',
|
|
105
|
+
column: 1,
|
|
106
|
+
level: 'error',
|
|
107
|
+
line: 1,
|
|
108
|
+
message: `Invalid config: ${issue.message}`,
|
|
109
|
+
path: '.patram.json',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {(string | number | symbol | undefined)[]} issue_path
|
|
115
|
+
* @returns {string}
|
|
116
|
+
*/
|
|
117
|
+
function formatIssuePath(issue_path) {
|
|
118
|
+
return issue_path.map(String).join('.');
|
|
119
|
+
}
|
|
@@ -18,6 +18,12 @@ export function resolveWhereClause(repo_config: PatramRepoConfig, command_argume
|
|
|
18
18
|
error: CliParseError;
|
|
19
19
|
success: false;
|
|
20
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} stored_query_name
|
|
23
|
+
* @param {string[]} stored_query_names
|
|
24
|
+
* @returns {CliParseError}
|
|
25
|
+
*/
|
|
26
|
+
export function createUnknownStoredQueryError(stored_query_name: string, stored_query_names: string[]): CliParseError;
|
|
21
27
|
export type QuerySource = {
|
|
22
28
|
kind: "ad_hoc";
|
|
23
29
|
} | {
|
|
@@ -82,7 +82,10 @@ export function resolveWhereClause(repo_config, command_arguments) {
|
|
|
82
82
|
* @param {string[]} stored_query_names
|
|
83
83
|
* @returns {CliParseError}
|
|
84
84
|
*/
|
|
85
|
-
function createUnknownStoredQueryError(
|
|
85
|
+
export function createUnknownStoredQueryError(
|
|
86
|
+
stored_query_name,
|
|
87
|
+
stored_query_names,
|
|
88
|
+
) {
|
|
86
89
|
const suggestion = findCloseMatch(stored_query_name, stored_query_names);
|
|
87
90
|
|
|
88
91
|
if (!suggestion) {
|
|
@@ -21,7 +21,7 @@ const CONFIG_FILE_NAME = '.patram.json';
|
|
|
21
21
|
* @param {string | undefined} target_argument
|
|
22
22
|
* @returns {Promise<ResolvedCheckTarget>}
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
async function resolveCheckTarget(target_argument) {
|
|
25
25
|
if (target_argument === undefined) {
|
|
26
26
|
return {
|
|
27
27
|
project_directory: process.cwd(),
|
|
@@ -35,23 +35,19 @@ export async function resolveCheckTarget(target_argument) {
|
|
|
35
35
|
? absolute_target_path
|
|
36
36
|
: dirname(absolute_target_path);
|
|
37
37
|
const project_directory = await findProjectDirectory(target_directory);
|
|
38
|
-
|
|
39
38
|
if (target_stats.isFile()) {
|
|
40
39
|
const target_path = normalizeRepoRelativePath(
|
|
41
40
|
relative(project_directory, absolute_target_path),
|
|
42
41
|
);
|
|
43
|
-
|
|
44
42
|
return {
|
|
45
43
|
project_directory,
|
|
46
44
|
target_kind: 'file',
|
|
47
45
|
target_path,
|
|
48
46
|
};
|
|
49
47
|
}
|
|
50
|
-
|
|
51
48
|
const target_path = normalizeRepoRelativePath(
|
|
52
49
|
relative(project_directory, absolute_target_path),
|
|
53
50
|
);
|
|
54
|
-
|
|
55
51
|
if (target_path.length === 0) {
|
|
56
52
|
return {
|
|
57
53
|
project_directory,
|
|
@@ -65,7 +61,36 @@ export async function resolveCheckTarget(target_argument) {
|
|
|
65
61
|
target_path,
|
|
66
62
|
};
|
|
67
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* @param {string[]} target_arguments
|
|
66
|
+
* @returns {Promise<ResolvedCheckTarget[]>}
|
|
67
|
+
*/
|
|
68
|
+
export async function resolveCheckTargets(target_arguments) {
|
|
69
|
+
if (target_arguments.length === 0) {
|
|
70
|
+
return [await resolveCheckTarget(undefined)];
|
|
71
|
+
}
|
|
68
72
|
|
|
73
|
+
return Promise.all(
|
|
74
|
+
target_arguments.map((target_argument) =>
|
|
75
|
+
resolveCheckTarget(target_argument),
|
|
76
|
+
),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
/** @param {ResolvedCheckTarget[]} resolved_targets
|
|
80
|
+
* @returns {string | null}
|
|
81
|
+
*/
|
|
82
|
+
export function resolveCheckTargetProjectDirectory(resolved_targets) {
|
|
83
|
+
const project_directory = resolved_targets[0]?.project_directory;
|
|
84
|
+
if (!project_directory) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
for (const resolved_target of resolved_targets) {
|
|
88
|
+
if (resolved_target.project_directory !== project_directory) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return project_directory;
|
|
93
|
+
}
|
|
69
94
|
/**
|
|
70
95
|
* Select the source files covered by one resolved `check` target.
|
|
71
96
|
*
|
|
@@ -73,10 +98,7 @@ export async function resolveCheckTarget(target_argument) {
|
|
|
73
98
|
* @param {ResolvedCheckTarget} resolved_target
|
|
74
99
|
* @returns {string[]}
|
|
75
100
|
*/
|
|
76
|
-
|
|
77
|
-
source_file_paths,
|
|
78
|
-
resolved_target,
|
|
79
|
-
) {
|
|
101
|
+
function selectCheckTargetSourceFiles(source_file_paths, resolved_target) {
|
|
80
102
|
if (resolved_target.target_kind === 'project') {
|
|
81
103
|
return source_file_paths;
|
|
82
104
|
}
|
|
@@ -91,7 +113,22 @@ export function selectCheckTargetSourceFiles(
|
|
|
91
113
|
isPathInsideDirectory(source_file_path, resolved_target.target_path),
|
|
92
114
|
);
|
|
93
115
|
}
|
|
94
|
-
|
|
116
|
+
/**
|
|
117
|
+
* @param {string[]} source_file_paths
|
|
118
|
+
* @param {ResolvedCheckTarget[]} resolved_targets
|
|
119
|
+
* @returns {string[]}
|
|
120
|
+
*/
|
|
121
|
+
export function selectCheckTargetsSourceFiles(
|
|
122
|
+
source_file_paths,
|
|
123
|
+
resolved_targets,
|
|
124
|
+
) {
|
|
125
|
+
return selectCheckTargetValues(
|
|
126
|
+
source_file_paths,
|
|
127
|
+
resolved_targets,
|
|
128
|
+
selectCheckTargetSourceFiles,
|
|
129
|
+
(source_file_path) => source_file_path,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
95
132
|
/**
|
|
96
133
|
* Filter diagnostics to one resolved `check` target.
|
|
97
134
|
*
|
|
@@ -99,7 +136,7 @@ export function selectCheckTargetSourceFiles(
|
|
|
99
136
|
* @param {ResolvedCheckTarget} resolved_target
|
|
100
137
|
* @returns {PatramDiagnostic[]}
|
|
101
138
|
*/
|
|
102
|
-
|
|
139
|
+
function selectCheckTargetDiagnostics(diagnostics, resolved_target) {
|
|
103
140
|
if (resolved_target.target_kind === 'project') {
|
|
104
141
|
return diagnostics;
|
|
105
142
|
}
|
|
@@ -115,6 +152,20 @@ export function selectCheckTargetDiagnostics(diagnostics, resolved_target) {
|
|
|
115
152
|
);
|
|
116
153
|
}
|
|
117
154
|
|
|
155
|
+
/**
|
|
156
|
+
* @param {PatramDiagnostic[]} diagnostics
|
|
157
|
+
* @param {ResolvedCheckTarget[]} resolved_targets
|
|
158
|
+
* @returns {PatramDiagnostic[]}
|
|
159
|
+
*/
|
|
160
|
+
export function selectCheckTargetsDiagnostics(diagnostics, resolved_targets) {
|
|
161
|
+
return selectCheckTargetValues(
|
|
162
|
+
diagnostics,
|
|
163
|
+
resolved_targets,
|
|
164
|
+
selectCheckTargetDiagnostics,
|
|
165
|
+
createDiagnosticKey,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
118
169
|
/**
|
|
119
170
|
* @param {string} start_directory
|
|
120
171
|
* @returns {Promise<string>}
|
|
@@ -155,6 +206,49 @@ async function hasConfigFile(directory_path) {
|
|
|
155
206
|
return true;
|
|
156
207
|
}
|
|
157
208
|
|
|
209
|
+
/**
|
|
210
|
+
* @template ValueType
|
|
211
|
+
* @param {ValueType[]} values
|
|
212
|
+
* @param {ResolvedCheckTarget[]} resolved_targets
|
|
213
|
+
* @param {(values: ValueType[], resolved_target: ResolvedCheckTarget) => ValueType[]} select_values
|
|
214
|
+
* @param {(value: ValueType) => string} get_value_key
|
|
215
|
+
* @returns {ValueType[]}
|
|
216
|
+
*/
|
|
217
|
+
function selectCheckTargetValues(
|
|
218
|
+
values,
|
|
219
|
+
resolved_targets,
|
|
220
|
+
select_values,
|
|
221
|
+
get_value_key,
|
|
222
|
+
) {
|
|
223
|
+
if (
|
|
224
|
+
resolved_targets.some(
|
|
225
|
+
(resolved_target) => resolved_target.target_kind === 'project',
|
|
226
|
+
)
|
|
227
|
+
) {
|
|
228
|
+
return values;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** @type {Set<string>} */
|
|
232
|
+
const selected_keys = new Set();
|
|
233
|
+
/** @type {ValueType[]} */
|
|
234
|
+
const selected_values = [];
|
|
235
|
+
|
|
236
|
+
for (const resolved_target of resolved_targets) {
|
|
237
|
+
for (const value of select_values(values, resolved_target)) {
|
|
238
|
+
const value_key = get_value_key(value);
|
|
239
|
+
|
|
240
|
+
if (selected_keys.has(value_key)) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
selected_keys.add(value_key);
|
|
245
|
+
selected_values.push(value);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return selected_values;
|
|
250
|
+
}
|
|
251
|
+
|
|
158
252
|
/**
|
|
159
253
|
* @param {string} source_path
|
|
160
254
|
* @param {string} directory_path
|
|
@@ -175,6 +269,21 @@ function normalizeRepoRelativePath(source_path) {
|
|
|
175
269
|
return source_path.replaceAll('\\', '/');
|
|
176
270
|
}
|
|
177
271
|
|
|
272
|
+
/**
|
|
273
|
+
* @param {PatramDiagnostic} diagnostic
|
|
274
|
+
* @returns {string}
|
|
275
|
+
*/
|
|
276
|
+
function createDiagnosticKey(diagnostic) {
|
|
277
|
+
return [
|
|
278
|
+
diagnostic.path,
|
|
279
|
+
diagnostic.line,
|
|
280
|
+
diagnostic.column,
|
|
281
|
+
diagnostic.level,
|
|
282
|
+
diagnostic.code,
|
|
283
|
+
diagnostic.message,
|
|
284
|
+
].join(':');
|
|
285
|
+
}
|
|
286
|
+
|
|
178
287
|
/**
|
|
179
288
|
* @param {unknown} error
|
|
180
289
|
* @returns {error is NodeJS.ErrnoException}
|
package/lib/patram.d.ts
CHANGED
|
@@ -48,6 +48,8 @@ export type PatramParsedTerm =
|
|
|
48
48
|
import('./graph/parse-where-clause.types.d.ts').ParsedTerm;
|
|
49
49
|
export type PatramParsedExpression =
|
|
50
50
|
import('./graph/parse-where-clause.types.d.ts').ParsedExpression;
|
|
51
|
+
export type PatramParseResult =
|
|
52
|
+
import('./graph/parse-where-clause.types.d.ts').ParseWhereClauseResult;
|
|
51
53
|
export type PatramParseWhereClauseResult =
|
|
52
54
|
import('./graph/parse-where-clause.types.d.ts').ParseWhereClauseResult;
|
|
53
55
|
export type PatramQuerySource =
|
|
@@ -59,6 +61,12 @@ export type PatramQuerySource =
|
|
|
59
61
|
name: string;
|
|
60
62
|
};
|
|
61
63
|
|
|
64
|
+
export interface PatramQueryGraphOptions {
|
|
65
|
+
bindings?: Record<string, string>;
|
|
66
|
+
limit?: number;
|
|
67
|
+
offset?: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
export interface PatramProjectGraphResult {
|
|
63
71
|
claims: import('./parse/parse-claims.types.d.ts').PatramClaim[];
|
|
64
72
|
config: import('./config/load-patram-config.types.d.ts').PatramRepoConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "patram",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./lib/patram.js",
|
|
6
6
|
"types": "./lib/patram.d.ts",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"ansis": "^4.2.0",
|
|
68
68
|
"beautiful-mermaid": "^1.1.3",
|
|
69
69
|
"globby": "^16.1.1",
|
|
70
|
+
"jsonc-parser": "^3.3.1",
|
|
70
71
|
"md4x": "^0.0.25",
|
|
71
72
|
"shiki": "^4.0.2",
|
|
72
73
|
"string-width": "^8.2.0",
|