patram 0.4.0 → 0.6.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 +48 -7
- package/lib/build-graph.js +2 -2
- package/lib/check-directive-metadata.js +175 -6
- package/lib/check-directive-path-target.js +173 -0
- package/lib/check-directive-value.js +122 -125
- package/lib/directive-validation-test-helpers.js +87 -0
- package/lib/load-patram-config.js +263 -116
- package/lib/load-patram-config.types.ts +50 -152
- package/lib/load-project-graph.js +16 -6
- package/lib/parse-claims.js +97 -11
- package/lib/parse-claims.types.ts +7 -0
- package/lib/parse-jsdoc-claims.js +3 -3
- package/lib/parse-markdown-claims.js +9 -3
- package/lib/parse-markdown-directives.js +48 -25
- package/lib/parse-yaml-claims.js +472 -0
- package/lib/patram-config.js +26 -9
- package/lib/patram-config.types.ts +18 -36
- package/lib/render-output-view.js +8 -9
- package/lib/resolve-patram-graph-config.js +40 -2
- package/lib/source-file-defaults.js +3 -0
- package/package.json +2 -1
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* @import { MappingDefinition } from './patram-config.types.ts';
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
createPathClassDiagnostics,
|
|
9
|
+
createPathExistenceDiagnostics,
|
|
10
|
+
} from './check-directive-path-target.js';
|
|
8
11
|
import { createOriginDiagnostic } from './directive-diagnostics.js';
|
|
9
12
|
import {
|
|
10
13
|
formatQuotedList,
|
|
@@ -40,45 +43,31 @@ export function checkDirectiveValue(
|
|
|
40
43
|
directive_name,
|
|
41
44
|
mapping_definition,
|
|
42
45
|
);
|
|
43
|
-
|
|
44
46
|
if (!validation_field_name || typeof claim.value !== 'string') {
|
|
45
47
|
return [];
|
|
46
48
|
}
|
|
47
|
-
|
|
49
|
+
const directive_value = claim.value;
|
|
48
50
|
if (validation_field_name === '$class') {
|
|
49
|
-
return checkClassValue(claim, directive_name, repo_config);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (isStructuralDirectiveField(validation_field_name)) {
|
|
53
|
-
return [];
|
|
51
|
+
return checkClassValue(claim, directive_name, directive_value, repo_config);
|
|
54
52
|
}
|
|
55
|
-
|
|
56
53
|
const type_definition = repo_config.fields?.[validation_field_name];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const type_diagnostic = createInvalidTypeDiagnostic(
|
|
67
|
-
claim,
|
|
68
|
-
directive_name,
|
|
69
|
-
type_definition,
|
|
70
|
-
claim.value,
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
if (type_diagnostic) {
|
|
74
|
-
return [type_diagnostic];
|
|
54
|
+
if (isStructuralDirectiveField(validation_field_name) || !type_definition) {
|
|
55
|
+
return collectUntypedPathDiagnostics(
|
|
56
|
+
claim,
|
|
57
|
+
directive_name,
|
|
58
|
+
mapping_definition,
|
|
59
|
+
document_entity_keys,
|
|
60
|
+
document_node_references,
|
|
61
|
+
document_paths,
|
|
62
|
+
);
|
|
75
63
|
}
|
|
76
|
-
|
|
77
|
-
return createPathClassDiagnostics(
|
|
64
|
+
return checkTypedDirectiveValue(
|
|
78
65
|
claim,
|
|
79
66
|
directive_name,
|
|
67
|
+
directive_value,
|
|
80
68
|
mappings,
|
|
81
69
|
repo_config,
|
|
70
|
+
mapping_definition,
|
|
82
71
|
type_definition,
|
|
83
72
|
document_entity_keys,
|
|
84
73
|
document_node_references,
|
|
@@ -89,14 +78,12 @@ export function checkDirectiveValue(
|
|
|
89
78
|
/**
|
|
90
79
|
* @param {PatramClaim} claim
|
|
91
80
|
* @param {string} directive_name
|
|
81
|
+
* @param {string} class_name
|
|
92
82
|
* @param {PatramRepoConfig} repo_config
|
|
93
83
|
* @returns {PatramDiagnostic[]}
|
|
94
84
|
*/
|
|
95
|
-
function checkClassValue(claim, directive_name, repo_config) {
|
|
96
|
-
if (
|
|
97
|
-
typeof claim.value !== 'string' ||
|
|
98
|
-
repo_config.classes?.[claim.value] !== undefined
|
|
99
|
-
) {
|
|
85
|
+
function checkClassValue(claim, directive_name, class_name, repo_config) {
|
|
86
|
+
if (repo_config.classes?.[class_name] !== undefined) {
|
|
100
87
|
return [];
|
|
101
88
|
}
|
|
102
89
|
|
|
@@ -112,44 +99,85 @@ function checkClassValue(claim, directive_name, repo_config) {
|
|
|
112
99
|
/**
|
|
113
100
|
* @param {PatramClaim} claim
|
|
114
101
|
* @param {string} directive_name
|
|
115
|
-
* @param {string
|
|
102
|
+
* @param {string} directive_value
|
|
103
|
+
* @param {Record<string, MappingDefinition>} mappings
|
|
104
|
+
* @param {PatramRepoConfig} repo_config
|
|
105
|
+
* @param {MappingDefinition | null} mapping_definition
|
|
106
|
+
* @param {DirectiveTypeConfig} type_definition
|
|
107
|
+
* @param {Map<string, string>} document_entity_keys
|
|
108
|
+
* @param {Map<string, import('./document-node-identity.js').DocumentNodeReference>} document_node_references
|
|
109
|
+
* @param {Set<string>} document_paths
|
|
116
110
|
* @returns {PatramDiagnostic[]}
|
|
117
111
|
*/
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
|
|
112
|
+
function checkTypedDirectiveValue(
|
|
113
|
+
claim,
|
|
114
|
+
directive_name,
|
|
115
|
+
directive_value,
|
|
116
|
+
mappings,
|
|
117
|
+
repo_config,
|
|
118
|
+
mapping_definition,
|
|
119
|
+
type_definition,
|
|
120
|
+
document_entity_keys,
|
|
121
|
+
document_node_references,
|
|
122
|
+
document_paths,
|
|
123
|
+
) {
|
|
124
|
+
if (type_definition.type === 'enum') {
|
|
125
|
+
return checkEnumValue(
|
|
126
|
+
claim,
|
|
127
|
+
directive_name,
|
|
128
|
+
directive_value,
|
|
129
|
+
type_definition.values,
|
|
130
|
+
);
|
|
121
131
|
}
|
|
122
132
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
133
|
+
if (!isDirectiveValueValid(type_definition, directive_value)) {
|
|
134
|
+
return [
|
|
135
|
+
createOriginDiagnostic(
|
|
136
|
+
claim,
|
|
137
|
+
'directive.invalid_type',
|
|
138
|
+
getInvalidTypeMessage(directive_name, type_definition.type),
|
|
139
|
+
),
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return collectPathDiagnostics(
|
|
144
|
+
claim,
|
|
145
|
+
directive_name,
|
|
146
|
+
mappings,
|
|
147
|
+
repo_config,
|
|
148
|
+
mapping_definition,
|
|
149
|
+
type_definition,
|
|
150
|
+
document_entity_keys,
|
|
151
|
+
document_node_references,
|
|
152
|
+
document_paths,
|
|
153
|
+
);
|
|
130
154
|
}
|
|
131
155
|
|
|
132
156
|
/**
|
|
133
157
|
* @param {PatramClaim} claim
|
|
134
158
|
* @param {string} directive_name
|
|
135
|
-
* @param {
|
|
136
|
-
* @param {string}
|
|
137
|
-
* @
|
|
159
|
+
* @param {MappingDefinition | null} mapping_definition
|
|
160
|
+
* @param {Map<string, string>} document_entity_keys
|
|
161
|
+
* @param {Map<string, import('./document-node-identity.js').DocumentNodeReference>} document_node_references
|
|
162
|
+
* @param {Set<string>} document_paths
|
|
163
|
+
* @returns {PatramDiagnostic[]}
|
|
138
164
|
*/
|
|
139
|
-
function
|
|
165
|
+
function collectUntypedPathDiagnostics(
|
|
140
166
|
claim,
|
|
141
167
|
directive_name,
|
|
142
|
-
|
|
143
|
-
|
|
168
|
+
mapping_definition,
|
|
169
|
+
document_entity_keys,
|
|
170
|
+
document_node_references,
|
|
171
|
+
document_paths,
|
|
144
172
|
) {
|
|
145
|
-
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return createOriginDiagnostic(
|
|
173
|
+
return createPathExistenceDiagnostics(
|
|
150
174
|
claim,
|
|
151
|
-
|
|
152
|
-
|
|
175
|
+
directive_name,
|
|
176
|
+
mapping_definition,
|
|
177
|
+
undefined,
|
|
178
|
+
document_entity_keys,
|
|
179
|
+
document_node_references,
|
|
180
|
+
document_paths,
|
|
153
181
|
);
|
|
154
182
|
}
|
|
155
183
|
|
|
@@ -158,90 +186,70 @@ function createInvalidTypeDiagnostic(
|
|
|
158
186
|
* @param {string} directive_name
|
|
159
187
|
* @param {Record<string, MappingDefinition>} mappings
|
|
160
188
|
* @param {PatramRepoConfig} repo_config
|
|
189
|
+
* @param {MappingDefinition | null} mapping_definition
|
|
161
190
|
* @param {Exclude<DirectiveTypeConfig, { type: 'enum' }>} type_definition
|
|
162
191
|
* @param {Map<string, string>} document_entity_keys
|
|
163
192
|
* @param {Map<string, import('./document-node-identity.js').DocumentNodeReference>} document_node_references
|
|
164
193
|
* @param {Set<string>} document_paths
|
|
165
194
|
* @returns {PatramDiagnostic[]}
|
|
166
195
|
*/
|
|
167
|
-
function
|
|
196
|
+
function collectPathDiagnostics(
|
|
168
197
|
claim,
|
|
169
198
|
directive_name,
|
|
170
199
|
mappings,
|
|
171
200
|
repo_config,
|
|
201
|
+
mapping_definition,
|
|
172
202
|
type_definition,
|
|
173
203
|
document_entity_keys,
|
|
174
204
|
document_node_references,
|
|
175
205
|
document_paths,
|
|
176
206
|
) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
207
|
+
return createPathClassDiagnostics(
|
|
208
|
+
claim,
|
|
209
|
+
directive_name,
|
|
210
|
+
mappings,
|
|
211
|
+
repo_config,
|
|
212
|
+
type_definition,
|
|
213
|
+
document_entity_keys,
|
|
214
|
+
document_node_references,
|
|
215
|
+
document_paths,
|
|
216
|
+
).concat(
|
|
217
|
+
createPathExistenceDiagnostics(
|
|
182
218
|
claim,
|
|
183
|
-
|
|
219
|
+
directive_name,
|
|
220
|
+
mapping_definition,
|
|
221
|
+
type_definition,
|
|
184
222
|
document_entity_keys,
|
|
185
223
|
document_node_references,
|
|
186
224
|
document_paths,
|
|
187
|
-
repo_config,
|
|
188
|
-
)
|
|
189
|
-
) {
|
|
190
|
-
return [];
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return [
|
|
194
|
-
createOriginDiagnostic(
|
|
195
|
-
claim,
|
|
196
|
-
'directive.invalid_path_class',
|
|
197
|
-
`Directive "${directive_name}" must point to path class "${type_definition.path_class}".`,
|
|
198
225
|
),
|
|
199
|
-
|
|
226
|
+
);
|
|
200
227
|
}
|
|
201
228
|
|
|
202
229
|
/**
|
|
203
|
-
* @param {Record<string, MappingDefinition>} mappings
|
|
204
230
|
* @param {PatramClaim} claim
|
|
205
|
-
* @param {string}
|
|
206
|
-
* @param {
|
|
207
|
-
* @param {
|
|
208
|
-
* @
|
|
209
|
-
* @param {PatramRepoConfig} repo_config
|
|
210
|
-
* @returns {boolean}
|
|
231
|
+
* @param {string} directive_name
|
|
232
|
+
* @param {string} directive_value
|
|
233
|
+
* @param {string[]} allowed_values
|
|
234
|
+
* @returns {PatramDiagnostic[]}
|
|
211
235
|
*/
|
|
212
|
-
function
|
|
213
|
-
mappings,
|
|
236
|
+
function checkEnumValue(
|
|
214
237
|
claim,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
document_paths,
|
|
219
|
-
repo_config,
|
|
238
|
+
directive_name,
|
|
239
|
+
directive_value,
|
|
240
|
+
allowed_values,
|
|
220
241
|
) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (!path_class_definition) {
|
|
224
|
-
return true;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const mapping_definition = resolveDirectiveMapping(mappings, claim);
|
|
228
|
-
const target_kind = mapping_definition?.emit?.target_class ?? 'document';
|
|
229
|
-
const resolved_target = resolveTargetReference(
|
|
230
|
-
target_kind,
|
|
231
|
-
'path',
|
|
232
|
-
claim,
|
|
233
|
-
document_entity_keys,
|
|
234
|
-
document_node_references,
|
|
235
|
-
document_paths,
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
if (!resolved_target.path) {
|
|
239
|
-
return false;
|
|
242
|
+
if (allowed_values.includes(directive_value)) {
|
|
243
|
+
return [];
|
|
240
244
|
}
|
|
241
245
|
|
|
242
|
-
return
|
|
243
|
-
|
|
244
|
-
|
|
246
|
+
return [
|
|
247
|
+
createOriginDiagnostic(
|
|
248
|
+
claim,
|
|
249
|
+
'directive.invalid_enum',
|
|
250
|
+
`Directive "${directive_name}" must be one of ${formatQuotedList(allowed_values)}.`,
|
|
251
|
+
),
|
|
252
|
+
];
|
|
245
253
|
}
|
|
246
254
|
|
|
247
255
|
/**
|
|
@@ -263,11 +271,7 @@ function resolveDirectiveMapping(mappings, claim) {
|
|
|
263
271
|
* @returns {string}
|
|
264
272
|
*/
|
|
265
273
|
function getDirectiveValidationFieldName(directive_name, mapping_definition) {
|
|
266
|
-
|
|
267
|
-
return mapping_definition.node.field;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return directive_name;
|
|
274
|
+
return mapping_definition?.node?.field ?? directive_name;
|
|
271
275
|
}
|
|
272
276
|
|
|
273
277
|
/**
|
|
@@ -282,10 +286,3 @@ function isStructuralDirectiveField(field_name) {
|
|
|
282
286
|
field_name === 'title'
|
|
283
287
|
);
|
|
284
288
|
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* @param {PatramClaim} claim
|
|
288
|
-
* @param {string} code
|
|
289
|
-
* @param {string} message
|
|
290
|
-
* @returns {PatramDiagnostic}
|
|
291
|
-
*/
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { MappingDefinition } from './patram-config.types.ts';
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @returns {Record<string, { prefixes: string[] }>}
|
|
7
|
+
*/
|
|
8
|
+
export function createDirectivePathClasses() {
|
|
9
|
+
return {
|
|
10
|
+
decision_docs: {
|
|
11
|
+
prefixes: ['docs/decisions/'],
|
|
12
|
+
},
|
|
13
|
+
plan_docs: {
|
|
14
|
+
prefixes: ['docs/plans/'],
|
|
15
|
+
},
|
|
16
|
+
task_docs: {
|
|
17
|
+
prefixes: ['docs/tasks/'],
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @returns {Record<string, { from: string[], to: string[] }>}
|
|
24
|
+
*/
|
|
25
|
+
export function createDirectiveRelations() {
|
|
26
|
+
return {
|
|
27
|
+
decided_by: {
|
|
28
|
+
from: ['document'],
|
|
29
|
+
to: ['document'],
|
|
30
|
+
},
|
|
31
|
+
tracked_in: {
|
|
32
|
+
from: ['document'],
|
|
33
|
+
to: ['document'],
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @returns {Record<string, MappingDefinition>}
|
|
40
|
+
*/
|
|
41
|
+
export function createMarkdownDirectiveMappings() {
|
|
42
|
+
return {
|
|
43
|
+
'markdown.directive.decided_by': createRelationMapping('decided_by'),
|
|
44
|
+
'markdown.directive.kind': createNodeMapping('$class'),
|
|
45
|
+
'markdown.directive.status': createNodeMapping('status'),
|
|
46
|
+
'markdown.directive.tracked_in': createRelationMapping('tracked_in'),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @returns {Record<string, MappingDefinition>}
|
|
52
|
+
*/
|
|
53
|
+
export function createJsdocDirectiveMappings() {
|
|
54
|
+
return {
|
|
55
|
+
'jsdoc.directive.decided_by': createRelationMapping('decided_by'),
|
|
56
|
+
'jsdoc.directive.kind': createNodeMapping('$class'),
|
|
57
|
+
'jsdoc.directive.status': createNodeMapping('status'),
|
|
58
|
+
'jsdoc.directive.tracked_in': createRelationMapping('tracked_in'),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {string} field_name
|
|
64
|
+
* @returns {MappingDefinition}
|
|
65
|
+
*/
|
|
66
|
+
function createNodeMapping(field_name) {
|
|
67
|
+
return {
|
|
68
|
+
node: {
|
|
69
|
+
class: 'document',
|
|
70
|
+
field: field_name,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} relation_name
|
|
77
|
+
* @returns {MappingDefinition}
|
|
78
|
+
*/
|
|
79
|
+
function createRelationMapping(relation_name) {
|
|
80
|
+
return {
|
|
81
|
+
emit: {
|
|
82
|
+
relation: relation_name,
|
|
83
|
+
target: 'path',
|
|
84
|
+
target_class: 'document',
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|