patram 0.11.0 → 0.12.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/bin/patram.js +4 -4
- package/lib/cli/commands/fields.js +0 -4
- package/lib/cli/commands/queries.js +10 -20
- package/lib/cli/commands/query.js +1 -8
- package/lib/cli/commands/refs.js +3 -10
- package/lib/cli/commands/show.js +1 -8
- package/lib/cli/help-metadata.js +71 -106
- package/lib/cli/main.js +10 -10
- package/lib/cli/parse-arguments-helpers.js +165 -59
- package/lib/cli/parse-arguments.js +4 -4
- package/lib/cli/render-help.js +2 -2
- package/lib/config/defaults.js +33 -25
- package/lib/config/load-patram-config.d.ts +8 -33
- package/lib/config/load-patram-config.js +9 -33
- package/lib/config/load-patram-config.types.d.ts +3 -40
- package/lib/config/manage-stored-queries-helpers.d.ts +4 -4
- package/lib/config/manage-stored-queries-helpers.js +91 -33
- package/lib/config/manage-stored-queries.d.ts +4 -4
- package/lib/config/manage-stored-queries.js +11 -5
- package/lib/config/patram-config.d.ts +34 -34
- package/lib/config/patram-config.js +3 -3
- package/lib/config/patram-config.types.d.ts +5 -11
- package/lib/config/resolve-patram-graph-config.d.ts +5 -1
- package/lib/config/resolve-patram-graph-config.js +3 -119
- package/lib/config/schema.d.ts +158 -269
- package/lib/config/schema.js +72 -210
- package/lib/config/validate-patram-config-value.js +6 -31
- package/lib/config/validation.d.ts +2 -12
- package/lib/config/validation.js +125 -483
- package/lib/find-close-match.d.ts +4 -1
- package/lib/graph/build-graph-identity.d.ts +1 -32
- package/lib/graph/build-graph-identity.js +5 -269
- package/lib/graph/build-graph.d.ts +13 -4
- package/lib/graph/build-graph.js +347 -488
- package/lib/graph/build-graph.types.d.ts +8 -9
- package/lib/graph/check-directive-metadata-helpers.d.ts +30 -0
- package/lib/graph/check-directive-metadata-helpers.js +126 -0
- package/lib/graph/check-directive-metadata.d.ts +8 -9
- package/lib/graph/check-directive-metadata.js +70 -561
- package/lib/graph/check-directive-path-target.d.ts +6 -13
- package/lib/graph/check-directive-path-target.js +26 -57
- package/lib/graph/check-directive-value.d.ts +1 -5
- package/lib/graph/check-directive-value.js +40 -180
- package/lib/graph/check-graph.d.ts +5 -5
- package/lib/graph/check-graph.js +8 -6
- package/lib/graph/document-node-identity.d.ts +23 -7
- package/lib/graph/document-node-identity.js +417 -160
- package/lib/graph/graph-node.d.ts +42 -0
- package/lib/graph/graph-node.js +83 -0
- package/lib/graph/inspect-reverse-references.js +16 -11
- package/lib/graph/load-project-graph.d.ts +7 -7
- package/lib/graph/load-project-graph.js +7 -7
- package/lib/graph/parse-where-clause.types.d.ts +3 -2
- package/lib/graph/query/cypher-reader.d.ts +59 -0
- package/lib/graph/query/cypher-reader.js +151 -0
- package/lib/graph/query/cypher-support.d.ts +79 -0
- package/lib/graph/query/cypher-support.js +213 -0
- package/lib/graph/query/cypher-tokenize.d.ts +13 -0
- package/lib/graph/query/cypher-tokenize.js +225 -0
- package/lib/graph/query/cypher.types.d.ts +43 -0
- package/lib/graph/query/execute.d.ts +7 -7
- package/lib/graph/query/execute.js +71 -33
- package/lib/graph/query/inspect.js +58 -24
- package/lib/graph/query/parse-cypher-patterns.d.ts +27 -0
- package/lib/graph/query/parse-cypher-patterns.js +382 -0
- package/lib/graph/query/parse-cypher.d.ts +7 -0
- package/lib/graph/query/parse-cypher.js +580 -0
- package/lib/graph/query/parse-query.d.ts +13 -0
- package/lib/graph/query/parse-query.js +97 -0
- package/lib/graph/query/resolve.js +77 -23
- package/lib/output/command-output.js +12 -5
- package/lib/output/compact-layout.js +221 -0
- package/lib/output/format-output-item-block.js +31 -1
- package/lib/output/format-output-metadata.js +16 -29
- package/lib/output/format-stored-query-block.js +95 -0
- package/lib/output/layout-incoming-references.js +101 -19
- package/lib/output/layout-stored-queries.js +23 -330
- package/lib/output/list-queries.js +1 -1
- package/lib/output/render-field-discovery.js +11 -2
- package/lib/output/render-output-view.js +9 -5
- package/lib/output/renderers/json.js +5 -26
- package/lib/output/renderers/plain.js +155 -35
- package/lib/output/renderers/rich.js +250 -36
- package/lib/output/resolved-link-layout.js +43 -0
- package/lib/output/rich-source/render.js +193 -35
- package/lib/output/show-document.js +25 -18
- package/lib/output/view-model/index.js +124 -103
- package/lib/parse/jsdoc/parse-jsdoc-blocks.js +1 -1
- package/lib/parse/jsdoc/parse-jsdoc-claims.js +12 -6
- package/lib/parse/markdown/parse-markdown-claims.js +99 -62
- package/lib/parse/markdown/parse-markdown-directives.d.ts +10 -6
- package/lib/parse/markdown/parse-markdown-directives.js +104 -18
- package/lib/parse/markdown/parse-markdown-prose.d.ts +27 -0
- package/lib/parse/markdown/parse-markdown-prose.js +243 -0
- package/lib/parse/parse-claims.d.ts +2 -6
- package/lib/parse/parse-claims.js +11 -53
- package/lib/parse/tagged-fenced/tagged-fenced-blocks.d.ts +4 -4
- package/lib/parse/tagged-fenced/tagged-fenced-blocks.js +4 -4
- package/lib/parse/yaml/parse-yaml-claims.js +4 -4
- package/lib/patram.d.ts +3 -5
- package/lib/patram.js +1 -1
- package/lib/scan/discover-fields.js +194 -55
- package/lib/scan/list-source-files.d.ts +4 -4
- package/lib/scan/list-source-files.js +4 -4
- package/package.json +1 -1
- package/lib/directive-validation-test-helpers.js +0 -87
- package/lib/graph/query/parse.d.ts +0 -75
- package/lib/graph/query/parse.js +0 -1064
- package/lib/output/derived-summary.js +0 -280
- package/lib/output/format-derived-summary-row.js +0 -9
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
/* eslint-disable complexity, max-lines */
|
|
1
2
|
/**
|
|
2
3
|
* @import { PatramClaim } from '../parse/parse-claims.types.ts';
|
|
3
|
-
* @import {
|
|
4
|
+
* @import { PatramConfig } from '../config/patram-config.types.ts';
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
import { posix } from 'node:path';
|
|
7
|
+
import { matchesGlob, posix } from 'node:path';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @typedef {{
|
|
@@ -17,39 +18,33 @@ import { posix } from 'node:path';
|
|
|
17
18
|
/**
|
|
18
19
|
* Collect semantic entity keys defined by canonical documents.
|
|
19
20
|
*
|
|
20
|
-
* @param {
|
|
21
|
+
* @param {PatramConfig} patram_config
|
|
21
22
|
* @param {PatramClaim[]} claims
|
|
22
23
|
* @returns {Map<string, string>}
|
|
23
24
|
*/
|
|
24
|
-
export function collectDocumentEntityKeys(
|
|
25
|
+
export function collectDocumentEntityKeys(patram_config, claims) {
|
|
25
26
|
/** @type {Map<string, string>} */
|
|
26
27
|
const document_entity_keys = new Map();
|
|
28
|
+
const document_node_references = collectDocumentNodeReferences(
|
|
29
|
+
patram_config,
|
|
30
|
+
claims,
|
|
31
|
+
);
|
|
27
32
|
|
|
28
|
-
for (const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
mapping_definition.node.class === 'document'
|
|
34
|
-
) {
|
|
33
|
+
for (const [
|
|
34
|
+
document_path,
|
|
35
|
+
document_node_reference,
|
|
36
|
+
] of document_node_references) {
|
|
37
|
+
if (document_node_reference.class_name === 'document') {
|
|
35
38
|
continue;
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
document_entity_keys.set(
|
|
42
|
+
getDocumentEntityMapKey(
|
|
43
|
+
document_path,
|
|
44
|
+
document_node_reference.class_name,
|
|
45
|
+
),
|
|
46
|
+
document_node_reference.key,
|
|
42
47
|
);
|
|
43
|
-
const entity_key = getStringClaimValue(claim);
|
|
44
|
-
const existing_entity_key = document_entity_keys.get(entity_map_key);
|
|
45
|
-
|
|
46
|
-
if (existing_entity_key && existing_entity_key !== entity_key) {
|
|
47
|
-
throw new Error(
|
|
48
|
-
`Document "${source_path}" defines multiple ${mapping_definition.node.class} ids.`,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
document_entity_keys.set(entity_map_key, entity_key);
|
|
53
48
|
}
|
|
54
49
|
|
|
55
50
|
return document_entity_keys;
|
|
@@ -58,35 +53,19 @@ export function collectDocumentEntityKeys(mappings, claims) {
|
|
|
58
53
|
/**
|
|
59
54
|
* Collect canonical graph identities for document-backed source paths.
|
|
60
55
|
*
|
|
61
|
-
* @param {
|
|
56
|
+
* @param {PatramConfig} patram_config
|
|
62
57
|
* @param {PatramClaim[]} claims
|
|
63
58
|
* @returns {Map<string, DocumentNodeReference>}
|
|
64
59
|
*/
|
|
65
|
-
export function collectDocumentNodeReferences(
|
|
60
|
+
export function collectDocumentNodeReferences(patram_config, claims) {
|
|
66
61
|
/** @type {Map<string, DocumentNodeReference>} */
|
|
67
62
|
const document_node_references = new Map();
|
|
68
|
-
|
|
69
|
-
const pending_document_keys = new Map();
|
|
63
|
+
const claims_by_path = groupClaimsByPath(claims);
|
|
70
64
|
|
|
71
|
-
for (const
|
|
72
|
-
|
|
73
|
-
const document_node_reference =
|
|
74
|
-
document_node_references.get(source_path) ??
|
|
75
|
-
createDefaultDocumentNodeReference(source_path);
|
|
76
|
-
const mapping_definition = resolveMappingDefinition(mappings, claim);
|
|
77
|
-
|
|
78
|
-
document_node_references.set(source_path, document_node_reference);
|
|
79
|
-
|
|
80
|
-
if (mapping_definition?.node?.class !== 'document') {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
applyDocumentNodeMapping(
|
|
85
|
-
document_node_reference,
|
|
86
|
-
mapping_definition.node,
|
|
87
|
-
claim,
|
|
88
|
-
pending_document_keys,
|
|
65
|
+
for (const [source_path, path_claims] of claims_by_path) {
|
|
66
|
+
document_node_references.set(
|
|
89
67
|
source_path,
|
|
68
|
+
resolveDocumentNodeReference(patram_config, source_path, path_claims),
|
|
90
69
|
);
|
|
91
70
|
}
|
|
92
71
|
|
|
@@ -96,12 +75,12 @@ export function collectDocumentNodeReferences(mappings, claims) {
|
|
|
96
75
|
/**
|
|
97
76
|
* Resolve the canonical node id for a source document path.
|
|
98
77
|
*
|
|
99
|
-
* @param {Record<string, string> | undefined}
|
|
78
|
+
* @param {Record<string, string> | undefined} document_path_ids
|
|
100
79
|
* @param {string} document_path
|
|
101
80
|
* @returns {string}
|
|
102
81
|
*/
|
|
103
|
-
export function resolveDocumentNodeId(
|
|
104
|
-
return
|
|
82
|
+
export function resolveDocumentNodeId(document_path_ids, document_path) {
|
|
83
|
+
return document_path_ids?.[document_path] ?? `doc:${document_path}`;
|
|
105
84
|
}
|
|
106
85
|
|
|
107
86
|
/**
|
|
@@ -115,192 +94,470 @@ export function normalizeRepoRelativePath(source_path) {
|
|
|
115
94
|
}
|
|
116
95
|
|
|
117
96
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
97
|
+
* Resolve one edge target key and canonical path.
|
|
98
|
+
*
|
|
99
|
+
* @param {string} target_class
|
|
100
|
+
* @param {'path' | 'value'} target_type
|
|
120
101
|
* @param {PatramClaim} claim
|
|
121
|
-
* @param {Map<string, string>}
|
|
122
|
-
* @param {string}
|
|
102
|
+
* @param {Map<string, string>} document_entity_keys
|
|
103
|
+
* @param {Map<string, DocumentNodeReference>} document_node_references
|
|
104
|
+
* @param {Set<string>} document_paths
|
|
105
|
+
* @returns {{ class_name: string, key: string, path?: string }}
|
|
123
106
|
*/
|
|
124
|
-
function
|
|
125
|
-
|
|
126
|
-
|
|
107
|
+
export function resolveTargetReference(
|
|
108
|
+
target_class,
|
|
109
|
+
target_type,
|
|
127
110
|
claim,
|
|
128
|
-
|
|
129
|
-
|
|
111
|
+
document_entity_keys,
|
|
112
|
+
document_node_references,
|
|
113
|
+
document_paths,
|
|
130
114
|
) {
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
document_node_reference,
|
|
134
|
-
getStringClaimValue(claim),
|
|
135
|
-
);
|
|
136
|
-
applyPendingDocumentKey(
|
|
137
|
-
document_node_reference,
|
|
138
|
-
pending_document_keys,
|
|
139
|
-
source_path,
|
|
140
|
-
);
|
|
141
|
-
return;
|
|
115
|
+
if (target_type === 'value') {
|
|
116
|
+
return resolveValueTargetReference(target_class, claim);
|
|
142
117
|
}
|
|
143
118
|
|
|
144
|
-
|
|
145
|
-
|
|
119
|
+
return resolvePathTargetReference(
|
|
120
|
+
target_class,
|
|
121
|
+
claim,
|
|
122
|
+
document_entity_keys,
|
|
123
|
+
document_node_references,
|
|
124
|
+
document_paths,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @param {PatramConfig} patram_config
|
|
130
|
+
* @param {string} source_path
|
|
131
|
+
* @param {PatramClaim[]} claims
|
|
132
|
+
* @returns {DocumentNodeReference}
|
|
133
|
+
*/
|
|
134
|
+
function resolveDocumentNodeReference(patram_config, source_path, claims) {
|
|
135
|
+
const defined_by_reference = resolveDefinedByReference(
|
|
136
|
+
patram_config.types,
|
|
137
|
+
claims,
|
|
138
|
+
source_path,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (defined_by_reference) {
|
|
142
|
+
return defined_by_reference;
|
|
146
143
|
}
|
|
147
144
|
|
|
148
|
-
const
|
|
145
|
+
const path_type_reference = resolvePathTypeReference(
|
|
146
|
+
patram_config.types,
|
|
147
|
+
source_path,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (path_type_reference) {
|
|
151
|
+
return path_type_reference;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return createDefaultDocumentNodeReference(source_path);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {PatramConfig['types']} types
|
|
159
|
+
* @param {PatramClaim[]} claims
|
|
160
|
+
* @param {string} source_path
|
|
161
|
+
* @returns {DocumentNodeReference | null}
|
|
162
|
+
*/
|
|
163
|
+
function resolveDefinedByReference(types, claims, source_path) {
|
|
164
|
+
/** @type {DocumentNodeReference | null} */
|
|
165
|
+
let document_node_reference = null;
|
|
166
|
+
|
|
167
|
+
for (const [type_name, type_definition] of Object.entries(types ?? {})) {
|
|
168
|
+
if (!type_definition.defined_by) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const node_key = resolveDefinedByKey(
|
|
173
|
+
type_definition.defined_by,
|
|
174
|
+
claims,
|
|
175
|
+
source_path,
|
|
176
|
+
type_name,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (!node_key) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
149
182
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
183
|
+
const next_reference = createDocumentNodeReference(
|
|
184
|
+
type_name,
|
|
185
|
+
node_key,
|
|
153
186
|
source_path,
|
|
154
|
-
document_node_key,
|
|
155
187
|
);
|
|
156
|
-
|
|
188
|
+
|
|
189
|
+
if (
|
|
190
|
+
document_node_reference &&
|
|
191
|
+
document_node_reference.id !== next_reference.id
|
|
192
|
+
) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`Document "${source_path}" defines multiple semantic types.`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
document_node_reference = next_reference;
|
|
157
199
|
}
|
|
158
200
|
|
|
159
|
-
|
|
201
|
+
return document_node_reference;
|
|
160
202
|
}
|
|
161
203
|
|
|
162
204
|
/**
|
|
163
|
-
* @param {
|
|
164
|
-
* @param {
|
|
205
|
+
* @param {string} field_name
|
|
206
|
+
* @param {PatramClaim[]} claims
|
|
165
207
|
* @param {string} source_path
|
|
208
|
+
* @param {string} type_name
|
|
209
|
+
* @returns {string | null}
|
|
166
210
|
*/
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
) {
|
|
172
|
-
|
|
173
|
-
|
|
211
|
+
function resolveDefinedByKey(field_name, claims, source_path, type_name) {
|
|
212
|
+
/** @type {string | null} */
|
|
213
|
+
let node_key = null;
|
|
214
|
+
|
|
215
|
+
for (const claim of claims) {
|
|
216
|
+
if (claim.type !== 'directive' || claim.name !== field_name) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const claim_value = getStringClaimValue(claim);
|
|
221
|
+
|
|
222
|
+
if (node_key && node_key !== claim_value) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`Document "${source_path}" defines multiple semantic ids for type "${type_name}".`,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
node_key = claim_value;
|
|
174
229
|
}
|
|
175
230
|
|
|
176
|
-
|
|
231
|
+
return node_key;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @param {PatramConfig['types']} types
|
|
236
|
+
* @param {string} source_path
|
|
237
|
+
* @returns {DocumentNodeReference | null}
|
|
238
|
+
*/
|
|
239
|
+
function resolvePathTypeReference(types, source_path) {
|
|
240
|
+
/** @type {{ prefix: string, type_name: string } | null} */
|
|
241
|
+
let best_match = null;
|
|
242
|
+
|
|
243
|
+
for (const [type_name, type_definition] of Object.entries(types ?? {})) {
|
|
244
|
+
for (const pattern of type_definition.in ?? []) {
|
|
245
|
+
if (!matchesGlob(source_path, pattern)) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const prefix = getGlobPrefix(pattern);
|
|
250
|
+
|
|
251
|
+
if (!best_match || prefix.length > best_match.prefix.length) {
|
|
252
|
+
best_match = {
|
|
253
|
+
prefix,
|
|
254
|
+
type_name,
|
|
255
|
+
};
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (
|
|
260
|
+
prefix.length === best_match.prefix.length &&
|
|
261
|
+
best_match.type_name !== type_name
|
|
262
|
+
) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`Document "${source_path}" matches multiple path-backed types.`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
177
269
|
|
|
178
|
-
if (!
|
|
179
|
-
return;
|
|
270
|
+
if (!best_match) {
|
|
271
|
+
return null;
|
|
180
272
|
}
|
|
181
273
|
|
|
182
|
-
|
|
274
|
+
return createDocumentNodeReference(
|
|
275
|
+
best_match.type_name,
|
|
276
|
+
deriveDocumentPathIdentityKey(source_path, best_match.prefix),
|
|
277
|
+
source_path,
|
|
278
|
+
);
|
|
183
279
|
}
|
|
184
280
|
|
|
185
281
|
/**
|
|
186
|
-
* @param {
|
|
282
|
+
* @param {string} target_class
|
|
187
283
|
* @param {PatramClaim} claim
|
|
188
|
-
* @returns {
|
|
284
|
+
* @returns {{ class_name: string, key: string, path?: string }}
|
|
189
285
|
*/
|
|
190
|
-
function
|
|
191
|
-
|
|
192
|
-
|
|
286
|
+
function resolveValueTargetReference(target_class, claim) {
|
|
287
|
+
const target_key = getStringClaimValue(claim);
|
|
288
|
+
|
|
289
|
+
if (target_class === 'document') {
|
|
290
|
+
return {
|
|
291
|
+
class_name: 'document',
|
|
292
|
+
key: target_key,
|
|
293
|
+
path: target_key,
|
|
294
|
+
};
|
|
193
295
|
}
|
|
194
296
|
|
|
195
|
-
return
|
|
297
|
+
return {
|
|
298
|
+
class_name: target_class,
|
|
299
|
+
key: target_key,
|
|
300
|
+
path: normalizeRepoRelativePath(claim.origin.path),
|
|
301
|
+
};
|
|
196
302
|
}
|
|
197
303
|
|
|
198
304
|
/**
|
|
199
|
-
* @param {
|
|
305
|
+
* @param {string} target_class
|
|
200
306
|
* @param {PatramClaim} claim
|
|
201
|
-
* @
|
|
307
|
+
* @param {Map<string, string>} document_entity_keys
|
|
308
|
+
* @param {Map<string, DocumentNodeReference>} document_node_references
|
|
309
|
+
* @param {Set<string>} document_paths
|
|
310
|
+
* @returns {{ class_name: string, key: string, path?: string }}
|
|
202
311
|
*/
|
|
203
|
-
function
|
|
204
|
-
|
|
205
|
-
|
|
312
|
+
function resolvePathTargetReference(
|
|
313
|
+
target_class,
|
|
314
|
+
claim,
|
|
315
|
+
document_entity_keys,
|
|
316
|
+
document_node_references,
|
|
317
|
+
document_paths,
|
|
318
|
+
) {
|
|
319
|
+
const raw_target = getPathTargetValue(claim);
|
|
320
|
+
const target_path = resolveDirectiveAwareTargetPath(
|
|
321
|
+
claim,
|
|
322
|
+
raw_target,
|
|
323
|
+
document_paths,
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
if (target_class === 'document') {
|
|
327
|
+
return resolveDocumentTargetReference(
|
|
328
|
+
target_path,
|
|
329
|
+
document_node_references,
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const document_node_reference = document_node_references.get(target_path);
|
|
334
|
+
|
|
335
|
+
if (document_node_reference?.class_name === target_class) {
|
|
336
|
+
return {
|
|
337
|
+
class_name: document_node_reference.class_name,
|
|
338
|
+
key: document_node_reference.key,
|
|
339
|
+
path: target_path,
|
|
340
|
+
};
|
|
206
341
|
}
|
|
207
342
|
|
|
208
|
-
return
|
|
343
|
+
return {
|
|
344
|
+
class_name: target_class,
|
|
345
|
+
key:
|
|
346
|
+
document_entity_keys.get(
|
|
347
|
+
getDocumentEntityMapKey(target_path, target_class),
|
|
348
|
+
) ?? target_path,
|
|
349
|
+
path: target_path,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* @param {string} target_path
|
|
355
|
+
* @param {Map<string, DocumentNodeReference>} document_node_references
|
|
356
|
+
* @returns {{ class_name: string, key: string, path?: string }}
|
|
357
|
+
*/
|
|
358
|
+
function resolveDocumentTargetReference(target_path, document_node_references) {
|
|
359
|
+
const document_node_reference = document_node_references.get(target_path);
|
|
360
|
+
|
|
361
|
+
if (!document_node_reference) {
|
|
362
|
+
return {
|
|
363
|
+
class_name: 'document',
|
|
364
|
+
key: target_path,
|
|
365
|
+
path: target_path,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
class_name: document_node_reference.class_name,
|
|
371
|
+
key: document_node_reference.key,
|
|
372
|
+
path: target_path,
|
|
373
|
+
};
|
|
209
374
|
}
|
|
210
375
|
|
|
211
376
|
/**
|
|
212
377
|
* @param {PatramClaim} claim
|
|
378
|
+
* @param {string} raw_target
|
|
379
|
+
* @param {Set<string>} document_paths
|
|
213
380
|
* @returns {string}
|
|
214
381
|
*/
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
382
|
+
function resolveDirectiveAwareTargetPath(claim, raw_target, document_paths) {
|
|
383
|
+
const normalized_raw_target = normalizeRepoRelativePath(raw_target);
|
|
384
|
+
|
|
385
|
+
if (
|
|
386
|
+
claim.type === 'directive' &&
|
|
387
|
+
shouldKeepDirectiveTargetRepoRelative(
|
|
388
|
+
raw_target,
|
|
389
|
+
normalized_raw_target,
|
|
390
|
+
document_paths,
|
|
391
|
+
)
|
|
392
|
+
) {
|
|
393
|
+
return normalized_raw_target;
|
|
218
394
|
}
|
|
219
395
|
|
|
220
|
-
|
|
396
|
+
const source_directory = posix.dirname(
|
|
397
|
+
normalizeRepoRelativePath(claim.origin.path),
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
return normalizeRepoRelativePath(posix.join(source_directory, raw_target));
|
|
221
401
|
}
|
|
222
402
|
|
|
223
403
|
/**
|
|
224
|
-
* @param {string}
|
|
225
|
-
* @param {string}
|
|
226
|
-
* @
|
|
404
|
+
* @param {string} raw_target
|
|
405
|
+
* @param {string} normalized_raw_target
|
|
406
|
+
* @param {Set<string>} document_paths
|
|
407
|
+
* @returns {boolean}
|
|
227
408
|
*/
|
|
228
|
-
function
|
|
229
|
-
|
|
409
|
+
function shouldKeepDirectiveTargetRepoRelative(
|
|
410
|
+
raw_target,
|
|
411
|
+
normalized_raw_target,
|
|
412
|
+
document_paths,
|
|
413
|
+
) {
|
|
414
|
+
if (raw_target.startsWith('./') || raw_target.startsWith('../')) {
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (document_paths.has(normalized_raw_target)) {
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const target_root_segment = normalized_raw_target.split('/')[0];
|
|
423
|
+
|
|
424
|
+
if (!target_root_segment) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
for (const document_path of document_paths) {
|
|
429
|
+
if (document_path.split('/')[0] === target_root_segment) {
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return false;
|
|
230
435
|
}
|
|
231
436
|
|
|
232
437
|
/**
|
|
233
438
|
* @param {string} source_path
|
|
234
|
-
* @
|
|
439
|
+
* @param {string} path_prefix
|
|
440
|
+
* @returns {string}
|
|
235
441
|
*/
|
|
236
|
-
function
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
442
|
+
function deriveDocumentPathIdentityKey(source_path, path_prefix) {
|
|
443
|
+
const normalized_prefix = normalizeRepoRelativePath(path_prefix);
|
|
444
|
+
const relative_path = source_path.startsWith(normalized_prefix)
|
|
445
|
+
? source_path.slice(normalized_prefix.length)
|
|
446
|
+
: source_path;
|
|
447
|
+
const relative_directory = posix.dirname(relative_path);
|
|
448
|
+
const base_name = posix.basename(relative_path, posix.extname(relative_path));
|
|
449
|
+
|
|
450
|
+
if (relative_directory === '.') {
|
|
451
|
+
return base_name;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return posix.join(relative_directory, base_name);
|
|
243
455
|
}
|
|
244
456
|
|
|
245
457
|
/**
|
|
246
|
-
* @param {
|
|
247
|
-
* @
|
|
458
|
+
* @param {string} glob_pattern
|
|
459
|
+
* @returns {string}
|
|
248
460
|
*/
|
|
249
|
-
function
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
) {
|
|
254
|
-
|
|
255
|
-
`Document "${document_node_reference.path}" defines multiple semantic classes.`,
|
|
256
|
-
);
|
|
461
|
+
function getGlobPrefix(glob_pattern) {
|
|
462
|
+
const normalized_pattern = normalizeRepoRelativePath(glob_pattern);
|
|
463
|
+
const wildcard_index = normalized_pattern.search(/[*?[{]/du);
|
|
464
|
+
|
|
465
|
+
if (wildcard_index < 0) {
|
|
466
|
+
return normalized_pattern;
|
|
257
467
|
}
|
|
258
468
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
469
|
+
const prefix = normalized_pattern.slice(0, wildcard_index);
|
|
470
|
+
|
|
471
|
+
return prefix.endsWith('/')
|
|
472
|
+
? prefix
|
|
473
|
+
: posix.dirname(prefix).replace(/\/?$/u, '/');
|
|
264
474
|
}
|
|
265
475
|
|
|
266
476
|
/**
|
|
267
|
-
* @param {
|
|
268
|
-
* @
|
|
477
|
+
* @param {PatramClaim} claim
|
|
478
|
+
* @returns {string}
|
|
269
479
|
*/
|
|
270
|
-
function
|
|
480
|
+
function getPathTargetValue(claim) {
|
|
481
|
+
if (typeof claim.value === 'string') {
|
|
482
|
+
return claim.value;
|
|
483
|
+
}
|
|
484
|
+
|
|
271
485
|
if (
|
|
272
|
-
|
|
273
|
-
|
|
486
|
+
claim.value &&
|
|
487
|
+
typeof claim.value === 'object' &&
|
|
488
|
+
'target' in claim.value
|
|
274
489
|
) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
)
|
|
490
|
+
const target_value = claim.value.target;
|
|
491
|
+
|
|
492
|
+
if (typeof target_value === 'string') {
|
|
493
|
+
return target_value;
|
|
494
|
+
}
|
|
278
495
|
}
|
|
279
496
|
|
|
280
|
-
|
|
281
|
-
document_node_reference.id = getNodeId(
|
|
282
|
-
document_node_reference.class_name,
|
|
283
|
-
document_node_reference.key,
|
|
284
|
-
);
|
|
497
|
+
throw new Error(`Claim "${claim.id}" does not carry a path target.`);
|
|
285
498
|
}
|
|
286
499
|
|
|
287
500
|
/**
|
|
288
|
-
* @param {
|
|
289
|
-
* @
|
|
501
|
+
* @param {PatramClaim[]} claims
|
|
502
|
+
* @returns {Map<string, PatramClaim[]>}
|
|
503
|
+
*/
|
|
504
|
+
function groupClaimsByPath(claims) {
|
|
505
|
+
/** @type {Map<string, PatramClaim[]>} */
|
|
506
|
+
const claims_by_path = new Map();
|
|
507
|
+
|
|
508
|
+
for (const claim of claims) {
|
|
509
|
+
const source_path = normalizeRepoRelativePath(claim.origin.path);
|
|
510
|
+
const path_claims = claims_by_path.get(source_path) ?? [];
|
|
511
|
+
|
|
512
|
+
path_claims.push(claim);
|
|
513
|
+
claims_by_path.set(source_path, path_claims);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return claims_by_path;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* @param {string} class_name
|
|
290
521
|
* @param {string} node_key
|
|
522
|
+
* @param {string} source_path
|
|
523
|
+
* @returns {DocumentNodeReference}
|
|
291
524
|
*/
|
|
292
|
-
function
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
525
|
+
function createDocumentNodeReference(class_name, node_key, source_path) {
|
|
526
|
+
return {
|
|
527
|
+
class_name,
|
|
528
|
+
id: getNodeId(class_name, node_key),
|
|
529
|
+
key: node_key,
|
|
530
|
+
path: source_path,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
298
533
|
|
|
299
|
-
|
|
300
|
-
|
|
534
|
+
/**
|
|
535
|
+
* @param {PatramClaim} claim
|
|
536
|
+
* @returns {string}
|
|
537
|
+
*/
|
|
538
|
+
function getStringClaimValue(claim) {
|
|
539
|
+
if (typeof claim.value === 'string') {
|
|
540
|
+
return claim.value;
|
|
301
541
|
}
|
|
302
542
|
|
|
303
|
-
|
|
543
|
+
throw new Error(`Claim "${claim.id}" does not carry a string value.`);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* @param {string} document_path
|
|
548
|
+
* @param {string} class_name
|
|
549
|
+
* @returns {string}
|
|
550
|
+
*/
|
|
551
|
+
function getDocumentEntityMapKey(document_path, class_name) {
|
|
552
|
+
return `${class_name}:${document_path}`;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* @param {string} source_path
|
|
557
|
+
* @returns {DocumentNodeReference}
|
|
558
|
+
*/
|
|
559
|
+
function createDefaultDocumentNodeReference(source_path) {
|
|
560
|
+
return createDocumentNodeReference('document', source_path, source_path);
|
|
304
561
|
}
|
|
305
562
|
|
|
306
563
|
/**
|