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.
Files changed (110) hide show
  1. package/bin/patram.js +4 -4
  2. package/lib/cli/commands/fields.js +0 -4
  3. package/lib/cli/commands/queries.js +10 -20
  4. package/lib/cli/commands/query.js +1 -8
  5. package/lib/cli/commands/refs.js +3 -10
  6. package/lib/cli/commands/show.js +1 -8
  7. package/lib/cli/help-metadata.js +71 -106
  8. package/lib/cli/main.js +10 -10
  9. package/lib/cli/parse-arguments-helpers.js +165 -59
  10. package/lib/cli/parse-arguments.js +4 -4
  11. package/lib/cli/render-help.js +2 -2
  12. package/lib/config/defaults.js +33 -25
  13. package/lib/config/load-patram-config.d.ts +8 -33
  14. package/lib/config/load-patram-config.js +9 -33
  15. package/lib/config/load-patram-config.types.d.ts +3 -40
  16. package/lib/config/manage-stored-queries-helpers.d.ts +4 -4
  17. package/lib/config/manage-stored-queries-helpers.js +91 -33
  18. package/lib/config/manage-stored-queries.d.ts +4 -4
  19. package/lib/config/manage-stored-queries.js +11 -5
  20. package/lib/config/patram-config.d.ts +34 -34
  21. package/lib/config/patram-config.js +3 -3
  22. package/lib/config/patram-config.types.d.ts +5 -11
  23. package/lib/config/resolve-patram-graph-config.d.ts +5 -1
  24. package/lib/config/resolve-patram-graph-config.js +3 -119
  25. package/lib/config/schema.d.ts +158 -269
  26. package/lib/config/schema.js +72 -210
  27. package/lib/config/validate-patram-config-value.js +6 -31
  28. package/lib/config/validation.d.ts +2 -12
  29. package/lib/config/validation.js +125 -483
  30. package/lib/find-close-match.d.ts +4 -1
  31. package/lib/graph/build-graph-identity.d.ts +1 -32
  32. package/lib/graph/build-graph-identity.js +5 -269
  33. package/lib/graph/build-graph.d.ts +13 -4
  34. package/lib/graph/build-graph.js +347 -488
  35. package/lib/graph/build-graph.types.d.ts +8 -9
  36. package/lib/graph/check-directive-metadata-helpers.d.ts +30 -0
  37. package/lib/graph/check-directive-metadata-helpers.js +126 -0
  38. package/lib/graph/check-directive-metadata.d.ts +8 -9
  39. package/lib/graph/check-directive-metadata.js +70 -561
  40. package/lib/graph/check-directive-path-target.d.ts +6 -13
  41. package/lib/graph/check-directive-path-target.js +26 -57
  42. package/lib/graph/check-directive-value.d.ts +1 -5
  43. package/lib/graph/check-directive-value.js +40 -180
  44. package/lib/graph/check-graph.d.ts +5 -5
  45. package/lib/graph/check-graph.js +8 -6
  46. package/lib/graph/document-node-identity.d.ts +23 -7
  47. package/lib/graph/document-node-identity.js +417 -160
  48. package/lib/graph/graph-node.d.ts +42 -0
  49. package/lib/graph/graph-node.js +83 -0
  50. package/lib/graph/inspect-reverse-references.js +16 -11
  51. package/lib/graph/load-project-graph.d.ts +7 -7
  52. package/lib/graph/load-project-graph.js +7 -7
  53. package/lib/graph/parse-where-clause.types.d.ts +3 -2
  54. package/lib/graph/query/cypher-reader.d.ts +59 -0
  55. package/lib/graph/query/cypher-reader.js +151 -0
  56. package/lib/graph/query/cypher-support.d.ts +79 -0
  57. package/lib/graph/query/cypher-support.js +213 -0
  58. package/lib/graph/query/cypher-tokenize.d.ts +13 -0
  59. package/lib/graph/query/cypher-tokenize.js +225 -0
  60. package/lib/graph/query/cypher.types.d.ts +43 -0
  61. package/lib/graph/query/execute.d.ts +7 -7
  62. package/lib/graph/query/execute.js +71 -33
  63. package/lib/graph/query/inspect.js +58 -24
  64. package/lib/graph/query/parse-cypher-patterns.d.ts +27 -0
  65. package/lib/graph/query/parse-cypher-patterns.js +382 -0
  66. package/lib/graph/query/parse-cypher.d.ts +7 -0
  67. package/lib/graph/query/parse-cypher.js +580 -0
  68. package/lib/graph/query/parse-query.d.ts +13 -0
  69. package/lib/graph/query/parse-query.js +97 -0
  70. package/lib/graph/query/resolve.js +77 -23
  71. package/lib/output/command-output.js +12 -5
  72. package/lib/output/compact-layout.js +221 -0
  73. package/lib/output/format-output-item-block.js +31 -1
  74. package/lib/output/format-output-metadata.js +16 -29
  75. package/lib/output/format-stored-query-block.js +95 -0
  76. package/lib/output/layout-incoming-references.js +101 -19
  77. package/lib/output/layout-stored-queries.js +23 -330
  78. package/lib/output/list-queries.js +1 -1
  79. package/lib/output/render-field-discovery.js +11 -2
  80. package/lib/output/render-output-view.js +9 -5
  81. package/lib/output/renderers/json.js +5 -26
  82. package/lib/output/renderers/plain.js +155 -35
  83. package/lib/output/renderers/rich.js +250 -36
  84. package/lib/output/resolved-link-layout.js +43 -0
  85. package/lib/output/rich-source/render.js +193 -35
  86. package/lib/output/show-document.js +25 -18
  87. package/lib/output/view-model/index.js +124 -103
  88. package/lib/parse/jsdoc/parse-jsdoc-blocks.js +1 -1
  89. package/lib/parse/jsdoc/parse-jsdoc-claims.js +12 -6
  90. package/lib/parse/markdown/parse-markdown-claims.js +99 -62
  91. package/lib/parse/markdown/parse-markdown-directives.d.ts +10 -6
  92. package/lib/parse/markdown/parse-markdown-directives.js +104 -18
  93. package/lib/parse/markdown/parse-markdown-prose.d.ts +27 -0
  94. package/lib/parse/markdown/parse-markdown-prose.js +243 -0
  95. package/lib/parse/parse-claims.d.ts +2 -6
  96. package/lib/parse/parse-claims.js +11 -53
  97. package/lib/parse/tagged-fenced/tagged-fenced-blocks.d.ts +4 -4
  98. package/lib/parse/tagged-fenced/tagged-fenced-blocks.js +4 -4
  99. package/lib/parse/yaml/parse-yaml-claims.js +4 -4
  100. package/lib/patram.d.ts +3 -5
  101. package/lib/patram.js +1 -1
  102. package/lib/scan/discover-fields.js +194 -55
  103. package/lib/scan/list-source-files.d.ts +4 -4
  104. package/lib/scan/list-source-files.js +4 -4
  105. package/package.json +1 -1
  106. package/lib/directive-validation-test-helpers.js +0 -87
  107. package/lib/graph/query/parse.d.ts +0 -75
  108. package/lib/graph/query/parse.js +0 -1064
  109. package/lib/output/derived-summary.js +0 -280
  110. package/lib/output/format-derived-summary-row.js +0 -9
@@ -1,38 +1,34 @@
1
- /* eslint-disable max-lines */
2
1
  /**
3
2
  * @import { BuildGraphResult } from './build-graph.types.ts';
4
- * @import { ClassFieldRuleConfig, MetadataFieldConfig, MetadataSchemaConfig, PatramDiagnostic, PatramRepoConfig } from '../config/load-patram-config.types.ts';
3
+ * @import { PatramDiagnostic, PatramRepoConfig } from '../config/load-patram-config.types.ts';
5
4
  * @import { PatramClaim } from '../parse/parse-claims.types.ts';
6
- * @import { MappingDefinition } from '../config/patram-config.types.ts';
7
5
  */
8
6
 
9
7
  import {
10
8
  collectDocumentEntityKeys,
11
9
  collectDocumentNodeReferences,
12
10
  normalizeRepoRelativePath,
13
- resolveDocumentNodeId,
14
11
  } from './build-graph-identity.js';
15
12
  import { checkDirectiveValue } from './check-directive-value.js';
16
13
  import {
17
- createDocumentDiagnostic,
18
- createOriginDiagnostic,
19
- } from './directive-diagnostics.js';
20
- import { formatQuotedList } from './directive-type-rules.js';
21
- import { resolvePatramGraphConfig } from '../config/resolve-patram-graph-config.js';
14
+ collectRequiredFieldDiagnostics,
15
+ collectTypePlacementDiagnostics,
16
+ groupDirectiveClaimsByDocument,
17
+ resolveDocumentType,
18
+ } from './check-directive-metadata-helpers.js';
19
+ import { createOriginDiagnostic } from './directive-diagnostics.js';
22
20
 
23
21
  /**
24
- * Directive and placement validation.
22
+ * Validate declared metadata fields and typed document placement.
25
23
  *
26
- * Kind: graph
27
- * Status: active
28
- * Tracked in: ../../docs/plans/v0/directive-type-validation.md
29
- * Decided by: ../../docs/decisions/directive-type-validation.md
30
- * @see {@link ../../docs/decisions/directive-type-validation.md}
24
+ * kind: graph
25
+ * status: active
26
+ * tracked_in: ../../docs/plans/v2/types-and-fields-config.md
27
+ * decided_by: ../../docs/decisions/types-and-fields-config.md
31
28
  * @patram
32
- * @see {@link ./check-graph.js}
33
- */
34
-
35
- /**
29
+ * @see {@link ./check-directive-value.js}
30
+ * @see {@link ../../docs/decisions/types-and-fields-config.md}
31
+ *
36
32
  * @param {BuildGraphResult} graph
37
33
  * @param {PatramRepoConfig} repo_config
38
34
  * @param {PatramClaim[]} claims
@@ -45,17 +41,13 @@ export function checkDirectiveMetadata(
45
41
  claims,
46
42
  existing_file_paths,
47
43
  ) {
48
- if (claims.length === 0 || !hasDirectiveValidationConfig(repo_config)) {
44
+ if (claims.length === 0) {
49
45
  return [];
50
46
  }
51
47
 
52
- const graph_config = resolvePatramGraphConfig(repo_config);
53
- const document_entity_keys = collectDocumentEntityKeys(
54
- graph_config.mappings,
55
- claims,
56
- );
48
+ const document_entity_keys = collectDocumentEntityKeys(repo_config, claims);
57
49
  const document_node_references = collectDocumentNodeReferences(
58
- graph_config.mappings,
50
+ repo_config,
59
51
  claims,
60
52
  );
61
53
  const document_paths = new Set(
@@ -74,16 +66,16 @@ export function checkDirectiveMetadata(
74
66
  continue;
75
67
  }
76
68
 
77
- collectDocumentDiagnostics(
78
- diagnostics,
79
- graph,
80
- graph_config.mappings,
81
- repo_config,
82
- document_path,
83
- document_claims,
84
- document_entity_keys,
85
- document_node_references,
86
- document_paths,
69
+ diagnostics.push(
70
+ ...checkDocumentDirectiveMetadata(
71
+ graph,
72
+ repo_config,
73
+ document_path,
74
+ document_claims,
75
+ document_entity_keys,
76
+ document_node_references,
77
+ document_paths,
78
+ ),
87
79
  );
88
80
  }
89
81
 
@@ -91,60 +83,17 @@ export function checkDirectiveMetadata(
91
83
  }
92
84
 
93
85
  /**
94
- * @param {PatramRepoConfig} repo_config
95
- * @returns {boolean}
96
- */
97
- function hasDirectiveValidationConfig(repo_config) {
98
- return (
99
- repo_config.fields !== undefined ||
100
- hasMarkdownStyleValidationConfig(repo_config) ||
101
- hasConfiguredClassSchemas(repo_config) ||
102
- hasPathTargetDirectiveMappings(repo_config)
103
- );
104
- }
105
-
106
- /**
107
- * @param {PatramClaim[]} claims
108
- * @returns {Map<string, PatramClaim[]>}
109
- */
110
- function groupDirectiveClaimsByDocument(claims) {
111
- /** @type {Map<string, PatramClaim[]>} */
112
- const directive_claims_by_document = new Map();
113
-
114
- for (const claim of claims) {
115
- if (claim.type !== 'directive') {
116
- continue;
117
- }
118
-
119
- const document_path = normalizeRepoRelativePath(claim.origin.path);
120
- let document_claims = directive_claims_by_document.get(document_path);
121
-
122
- if (!document_claims) {
123
- document_claims = [];
124
- directive_claims_by_document.set(document_path, document_claims);
125
- }
126
-
127
- document_claims.push(claim);
128
- }
129
-
130
- return directive_claims_by_document;
131
- }
132
-
133
- /**
134
- * @param {PatramDiagnostic[]} diagnostics
135
86
  * @param {BuildGraphResult} graph
136
- * @param {Record<string, MappingDefinition>} mappings
137
87
  * @param {PatramRepoConfig} repo_config
138
88
  * @param {string} document_path
139
89
  * @param {PatramClaim[]} document_claims
140
90
  * @param {Map<string, string>} document_entity_keys
141
91
  * @param {Map<string, import('./document-node-identity.js').DocumentNodeReference>} document_node_references
142
92
  * @param {Set<string>} document_paths
93
+ * @returns {PatramDiagnostic[]}
143
94
  */
144
- function collectDocumentDiagnostics(
145
- diagnostics,
95
+ function checkDocumentDirectiveMetadata(
146
96
  graph,
147
- mappings,
148
97
  repo_config,
149
98
  document_path,
150
99
  document_claims,
@@ -152,27 +101,26 @@ function collectDocumentDiagnostics(
152
101
  document_node_references,
153
102
  document_paths,
154
103
  ) {
155
- const document_kind = resolveDocumentKind(graph, document_path);
156
- const schema_definition = repo_config.classes?.[document_kind]?.schema;
104
+ const document_type = resolveDocumentType(graph, document_path);
157
105
  /** @type {Map<string, number>} */
158
106
  const directive_counts = new Map();
159
- /** @type {Set<string>} */
160
- const seen_markdown_styles = new Set();
107
+ /** @type {PatramDiagnostic[]} */
108
+ const diagnostics = [];
161
109
 
162
110
  for (const claim of document_claims) {
163
111
  if (!claim.name) {
164
112
  continue;
165
113
  }
166
114
 
115
+ directive_counts.set(
116
+ claim.name,
117
+ (directive_counts.get(claim.name) ?? 0) + 1,
118
+ );
167
119
  diagnostics.push(
168
120
  ...collectClaimDiagnostics(
169
121
  claim,
170
- mappings,
171
122
  repo_config,
172
- document_kind,
173
- schema_definition,
174
- directive_counts,
175
- seen_markdown_styles,
123
+ document_type,
176
124
  document_entity_keys,
177
125
  document_node_references,
178
126
  document_paths,
@@ -181,24 +129,28 @@ function collectDocumentDiagnostics(
181
129
  }
182
130
 
183
131
  diagnostics.push(
184
- ...collectDocumentSummaryDiagnostics(
132
+ ...collectRequiredFieldDiagnostics(
185
133
  repo_config,
186
134
  document_path,
187
- document_kind,
188
- schema_definition,
135
+ document_type,
189
136
  directive_counts,
190
137
  ),
191
138
  );
139
+ diagnostics.push(
140
+ ...collectTypePlacementDiagnostics(
141
+ repo_config,
142
+ document_path,
143
+ document_type,
144
+ ),
145
+ );
146
+
147
+ return diagnostics;
192
148
  }
193
149
 
194
150
  /**
195
151
  * @param {PatramClaim} claim
196
- * @param {Record<string, MappingDefinition>} mappings
197
152
  * @param {PatramRepoConfig} repo_config
198
- * @param {string} document_kind
199
- * @param {MetadataSchemaConfig | undefined} schema_definition
200
- * @param {Map<string, number>} directive_counts
201
- * @param {Set<string>} seen_markdown_styles
153
+ * @param {string} document_type
202
154
  * @param {Map<string, string>} document_entity_keys
203
155
  * @param {Map<string, import('./document-node-identity.js').DocumentNodeReference>} document_node_references
204
156
  * @param {Set<string>} document_paths
@@ -206,12 +158,8 @@ function collectDocumentDiagnostics(
206
158
  */
207
159
  function collectClaimDiagnostics(
208
160
  claim,
209
- mappings,
210
161
  repo_config,
211
- document_kind,
212
- schema_definition,
213
- directive_counts,
214
- seen_markdown_styles,
162
+ document_type,
215
163
  document_entity_keys,
216
164
  document_node_references,
217
165
  document_paths,
@@ -220,484 +168,45 @@ function collectClaimDiagnostics(
220
168
  return [];
221
169
  }
222
170
 
223
- const mapping_definition = resolveDirectiveMapping(mappings, claim);
224
- const validation_field_name = getDirectiveValidationFieldName(
225
- claim.name,
226
- mapping_definition,
227
- );
228
- const has_node_mapping = mapping_definition?.node !== undefined;
229
- const next_count = (directive_counts.get(claim.name) ?? 0) + 1;
230
-
231
- directive_counts.set(claim.name, next_count);
232
-
233
- return [
234
- ...collectPresenceDiagnostics(
235
- claim,
236
- claim.name,
237
- validation_field_name,
238
- isEmitOnlyDirective(mapping_definition),
239
- has_node_mapping,
240
- repo_config,
241
- document_kind,
242
- schema_definition,
243
- next_count,
244
- ),
245
- ...collectMarkdownStyleDiagnostics(
246
- claim,
247
- document_kind,
248
- schema_definition,
249
- seen_markdown_styles,
250
- ),
251
- ...checkDirectiveValue(
171
+ if (claim.name === 'title' || claim.name === 'description') {
172
+ return checkDirectiveValue(
252
173
  claim,
253
174
  claim.name,
254
- mappings,
255
175
  repo_config,
256
- schema_definition?.fields[claim.name],
257
176
  document_entity_keys,
258
177
  document_node_references,
259
178
  document_paths,
260
- ),
261
- ];
262
- }
263
-
264
- /**
265
- * @param {PatramRepoConfig} repo_config
266
- * @returns {boolean}
267
- */
268
- function hasConfiguredClassSchemas(repo_config) {
269
- for (const class_definition of Object.values(repo_config.classes ?? {})) {
270
- if (class_definition.schema) {
271
- return true;
272
- }
273
- }
274
-
275
- return false;
276
- }
277
-
278
- /**
279
- * @param {PatramRepoConfig} repo_config
280
- * @returns {boolean}
281
- */
282
- function hasMarkdownStyleValidationConfig(repo_config) {
283
- for (const class_definition of Object.values(repo_config.classes ?? {})) {
284
- const schema_definition = class_definition.schema;
285
-
286
- if (!schema_definition) {
287
- continue;
288
- }
289
-
290
- if (
291
- schema_definition.markdown_styles !== undefined ||
292
- schema_definition.mixed_styles !== undefined
293
- ) {
294
- return true;
295
- }
296
-
297
- for (const field_rule of Object.values(schema_definition.fields)) {
298
- if (field_rule.markdown_styles !== undefined) {
299
- return true;
300
- }
301
- }
302
- }
303
-
304
- return false;
305
- }
306
-
307
- /**
308
- * @param {PatramRepoConfig} repo_config
309
- * @returns {boolean}
310
- */
311
- function hasPathTargetDirectiveMappings(repo_config) {
312
- for (const [mapping_name, mapping_definition] of Object.entries(
313
- repo_config.mappings ?? {},
314
- )) {
315
- if (
316
- mapping_name.includes('.directive.') &&
317
- mapping_definition.emit?.target === 'path'
318
- ) {
319
- return true;
320
- }
321
- }
322
-
323
- return false;
324
- }
325
-
326
- /**
327
- * @param {PatramRepoConfig} repo_config
328
- * @param {string} document_path
329
- * @param {string} document_kind
330
- * @param {MetadataSchemaConfig | undefined} schema_definition
331
- * @param {Map<string, number>} directive_counts
332
- * @returns {PatramDiagnostic[]}
333
- */
334
- function collectDocumentSummaryDiagnostics(
335
- repo_config,
336
- document_path,
337
- document_kind,
338
- schema_definition,
339
- directive_counts,
340
- ) {
341
- return [
342
- ...collectMissingDirectiveDiagnostics(
343
- document_path,
344
- document_kind,
345
- schema_definition,
346
- directive_counts,
347
- ),
348
- ...collectPlacementDiagnostics(
349
- repo_config,
350
- document_path,
351
- document_kind,
352
- schema_definition,
353
- ),
354
- ];
355
- }
356
-
357
- /**
358
- * @param {PatramClaim} claim
359
- * @param {string} directive_name
360
- * @param {string} validation_field_name
361
- * @param {boolean} emit_only
362
- * @param {boolean} has_node_mapping
363
- * @param {PatramRepoConfig} repo_config
364
- * @param {string} document_kind
365
- * @param {MetadataSchemaConfig | undefined} schema_definition
366
- * @param {number} directive_count
367
- * @returns {PatramDiagnostic[]}
368
- */
369
- function collectPresenceDiagnostics(
370
- claim,
371
- directive_name,
372
- validation_field_name,
373
- emit_only,
374
- has_node_mapping,
375
- repo_config,
376
- document_kind,
377
- schema_definition,
378
- directive_count,
379
- ) {
380
- const presence_context = resolveDirectivePresenceContext(
381
- validation_field_name,
382
- emit_only,
383
- has_node_mapping,
384
- repo_config,
385
- schema_definition,
386
- );
387
-
388
- if (presence_context === null) {
389
- return [];
179
+ );
390
180
  }
391
181
 
392
- const { directive_rule, field_definition } = presence_context;
182
+ const field_definition = repo_config.fields?.[claim.name];
393
183
 
394
- if (isForbiddenDirective(directive_rule, schema_definition)) {
184
+ if (!field_definition) {
395
185
  return [
396
186
  createOriginDiagnostic(
397
187
  claim,
398
- 'directive.forbidden',
399
- `Directive "${directive_name}" is forbidden for class "${document_kind}".`,
188
+ 'directive.unknown_field',
189
+ `Directive "${claim.name}" is not declared in config.`,
400
190
  ),
401
191
  ];
402
192
  }
403
193
 
404
- if (field_definition?.multiple === true || directive_count <= 1) {
405
- return [];
406
- }
407
-
408
- return [
409
- createOriginDiagnostic(
410
- claim,
411
- 'directive.duplicate',
412
- `Directive "${directive_name}" must appear at most once for class "${document_kind}".`,
413
- ),
414
- ];
415
- }
416
-
417
- /**
418
- * @param {{ presence: 'required' | 'optional' | 'forbidden' } | undefined} directive_rule
419
- * @param {MetadataSchemaConfig | undefined} schema_definition
420
- * @returns {boolean}
421
- */
422
- function isForbiddenDirective(directive_rule, schema_definition) {
423
- return (
424
- directive_rule?.presence === 'forbidden' ||
425
- (directive_rule === undefined &&
426
- schema_definition?.unknown_fields === 'error')
427
- );
428
- }
429
-
430
- /**
431
- * @param {string} validation_field_name
432
- * @param {boolean} emit_only
433
- * @param {boolean} has_node_mapping
434
- * @param {PatramRepoConfig} repo_config
435
- * @param {MetadataSchemaConfig | undefined} schema_definition
436
- * @returns {{ directive_rule: ClassFieldRuleConfig | undefined, field_definition: MetadataFieldConfig | undefined } | null}
437
- */
438
- function resolveDirectivePresenceContext(
439
- validation_field_name,
440
- emit_only,
441
- has_node_mapping,
442
- repo_config,
443
- schema_definition,
444
- ) {
445
- if (emit_only) {
446
- return null;
447
- }
448
-
449
- const directive_rule = schema_definition?.fields[validation_field_name];
450
- const field_definition = getDirectiveFieldDefinition(
451
- repo_config,
452
- validation_field_name,
453
- );
454
-
455
- if (
456
- !has_node_mapping &&
457
- directive_rule === undefined &&
458
- field_definition === undefined
459
- ) {
460
- return null;
461
- }
462
-
463
- return {
464
- directive_rule,
465
- field_definition,
466
- };
467
- }
468
-
469
- /**
470
- * @param {PatramRepoConfig} repo_config
471
- * @param {string} validation_field_name
472
- * @returns {MetadataFieldConfig | undefined}
473
- */
474
- function getDirectiveFieldDefinition(repo_config, validation_field_name) {
475
- if (
476
- validation_field_name.startsWith('$') ||
477
- validation_field_name === 'title'
478
- ) {
479
- return undefined;
480
- }
481
-
482
- return repo_config.fields?.[validation_field_name];
483
- }
484
-
485
- /**
486
- * @param {BuildGraphResult} graph
487
- * @param {string} document_path
488
- * @returns {string}
489
- */
490
- function resolveDocumentKind(graph, document_path) {
491
- const document_node_id = resolveDocumentNodeId(
492
- graph.document_node_ids,
493
- document_path,
494
- );
495
-
496
- return (
497
- graph.nodes[document_node_id]?.$class ??
498
- graph.nodes[document_node_id]?.kind ??
499
- 'document'
500
- );
501
- }
502
-
503
- /**
504
- * @param {Record<string, MappingDefinition>} mappings
505
- * @param {PatramClaim} claim
506
- * @returns {MappingDefinition | null}
507
- */
508
- function resolveDirectiveMapping(mappings, claim) {
509
- if (!claim.name || !claim.parser) {
510
- return null;
511
- }
512
-
513
- return mappings[`${claim.parser}.directive.${claim.name}`] ?? null;
514
- }
515
-
516
- /**
517
- * @param {string} directive_name
518
- * @param {MappingDefinition | null} mapping_definition
519
- * @returns {string}
520
- */
521
- function getDirectiveValidationFieldName(directive_name, mapping_definition) {
522
- if (mapping_definition?.node?.field) {
523
- return mapping_definition.node.field;
524
- }
525
-
526
- return directive_name;
527
- }
528
-
529
- /**
530
- * @param {MappingDefinition | null} mapping_definition
531
- * @returns {boolean}
532
- */
533
- function isEmitOnlyDirective(mapping_definition) {
534
- return (
535
- mapping_definition?.emit !== undefined &&
536
- mapping_definition.node === undefined
537
- );
538
- }
539
-
540
- /**
541
- * @param {string} document_path
542
- * @param {string} document_kind
543
- * @param {MetadataSchemaConfig | undefined} schema_definition
544
- * @param {Map<string, number>} directive_counts
545
- * @returns {PatramDiagnostic[]}
546
- */
547
- function collectMissingDirectiveDiagnostics(
548
- document_path,
549
- document_kind,
550
- schema_definition,
551
- directive_counts,
552
- ) {
553
- if (!schema_definition) {
554
- return [];
555
- }
556
-
557
- /** @type {PatramDiagnostic[]} */
558
- const diagnostics = [];
559
-
560
- for (const [directive_name, directive_rule] of Object.entries(
561
- schema_definition.fields,
562
- )) {
563
- if (
564
- directive_rule.presence === 'required' &&
565
- (directive_counts.get(directive_name) ?? 0) === 0
566
- ) {
567
- diagnostics.push(
568
- createDocumentDiagnostic(
569
- document_path,
570
- 'directive.missing_required',
571
- `Missing required directive "${directive_name}" for class "${document_kind}".`,
572
- ),
573
- );
574
- }
575
- }
576
-
577
- return diagnostics;
578
- }
579
-
580
- /**
581
- * @param {PatramRepoConfig} repo_config
582
- * @param {string} document_path
583
- * @param {string} document_kind
584
- * @param {MetadataSchemaConfig | undefined} schema_definition
585
- * @returns {PatramDiagnostic[]}
586
- */
587
- function collectPlacementDiagnostics(
588
- repo_config,
589
- document_path,
590
- document_kind,
591
- schema_definition,
592
- ) {
593
- const document_path_class = schema_definition?.document_path_class;
594
-
595
- if (!document_path_class) {
596
- return [];
597
- }
598
-
599
- const path_class_definition = repo_config.path_classes?.[document_path_class];
600
-
601
- if (
602
- !path_class_definition ||
603
- path_class_definition.prefixes.some((prefix) =>
604
- document_path.startsWith(prefix),
605
- )
606
- ) {
607
- return [];
608
- }
609
-
610
- return [
611
- createDocumentDiagnostic(
612
- document_path,
613
- 'document.invalid_placement',
614
- `Document class "${document_kind}" must be placed in path class "${document_path_class}".`,
615
- ),
616
- ];
617
- }
618
-
619
- /**
620
- * @param {PatramClaim} claim
621
- * @param {string} document_kind
622
- * @param {MetadataSchemaConfig | undefined} schema_definition
623
- * @param {Set<string>} seen_markdown_styles
624
- * @returns {PatramDiagnostic[]}
625
- */
626
- function collectMarkdownStyleDiagnostics(
627
- claim,
628
- document_kind,
629
- schema_definition,
630
- seen_markdown_styles,
631
- ) {
632
- const markdown_style = resolveClaimMarkdownStyle(claim);
633
-
634
- if (!markdown_style || !claim.name) {
635
- return [];
636
- }
637
-
638
- /** @type {PatramDiagnostic[]} */
639
- const diagnostics = [];
640
- const allowed_styles =
641
- schema_definition?.fields[claim.name]?.markdown_styles ??
642
- schema_definition?.markdown_styles;
643
-
644
- if (allowed_styles && !allowed_styles.includes(markdown_style)) {
645
- diagnostics.push(
646
- createOriginDiagnostic(
647
- claim,
648
- 'directive.invalid_style',
649
- `Directive "${claim.name}" uses markdown style "${markdown_style}" but only ${formatQuotedList(allowed_styles)} are allowed.`,
650
- ),
651
- );
652
- }
653
-
654
- if (
655
- shouldReportMixedStyles(
656
- schema_definition,
657
- seen_markdown_styles,
658
- markdown_style,
659
- )
660
- ) {
661
- diagnostics.push(
194
+ if (field_definition.on && !field_definition.on.includes(document_type)) {
195
+ return [
662
196
  createOriginDiagnostic(
663
197
  claim,
664
- 'document.mixed_styles',
665
- `Document mixes markdown directive style "${markdown_style}" with ${formatQuotedList([...seen_markdown_styles])} while class "${document_kind}" sets mixed_styles="error".`,
198
+ 'directive.invalid_type_scope',
199
+ `Directive "${claim.name}" is not allowed on type "${document_type}".`,
666
200
  ),
667
- );
668
- }
669
-
670
- seen_markdown_styles.add(markdown_style);
671
-
672
- return diagnostics;
673
- }
674
-
675
- /**
676
- * @param {PatramClaim} claim
677
- * @returns {string | null}
678
- */
679
- function resolveClaimMarkdownStyle(claim) {
680
- if (claim.parser !== 'markdown' || claim.markdown_style === undefined) {
681
- return null;
201
+ ];
682
202
  }
683
203
 
684
- return claim.markdown_style;
685
- }
686
-
687
- /**
688
- * @param {MetadataSchemaConfig | undefined} schema_definition
689
- * @param {Set<string>} seen_markdown_styles
690
- * @param {string} markdown_style
691
- * @returns {boolean}
692
- */
693
- function shouldReportMixedStyles(
694
- schema_definition,
695
- seen_markdown_styles,
696
- markdown_style,
697
- ) {
698
- return (
699
- schema_definition?.mixed_styles === 'error' &&
700
- seen_markdown_styles.size === 1 &&
701
- !seen_markdown_styles.has(markdown_style)
204
+ return checkDirectiveValue(
205
+ claim,
206
+ claim.name,
207
+ repo_config,
208
+ document_entity_keys,
209
+ document_node_references,
210
+ document_paths,
702
211
  );
703
212
  }