patram 0.7.0 → 0.9.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 (140) hide show
  1. package/bin/patram.js +2 -2
  2. package/lib/{parse-cli-color-options.js → cli/color-options.js} +2 -2
  3. package/lib/cli/command-helpers.js +35 -0
  4. package/lib/cli/commands/check.js +73 -0
  5. package/lib/cli/commands/fields.js +57 -0
  6. package/lib/cli/commands/queries.js +41 -0
  7. package/lib/cli/commands/query.js +239 -0
  8. package/lib/cli/commands/refs.js +72 -0
  9. package/lib/cli/commands/show.js +58 -0
  10. package/lib/{cli-help-metadata.js → cli/help-metadata.js} +54 -15
  11. package/lib/cli/main.js +76 -0
  12. package/lib/{parse-cli-arguments-helpers.js → cli/parse-arguments-helpers.js} +18 -11
  13. package/lib/{parse-cli-arguments.js → cli/parse-arguments.js} +12 -12
  14. package/lib/{parse-cli-query-pagination.js → cli/query-pagination.js} +2 -2
  15. package/lib/{render-cli-help.js → cli/render-help.js} +4 -4
  16. package/lib/{resolve-output-mode.js → cli/resolve-output-mode.js} +2 -2
  17. package/lib/cli/test-helpers.js +30 -0
  18. package/lib/config/defaults.d.ts +10 -0
  19. package/lib/config/defaults.js +80 -0
  20. package/lib/config/load-patram-config.d.ts +76 -0
  21. package/lib/config/load-patram-config.js +315 -0
  22. package/lib/config/load-patram-config.types.d.ts +45 -0
  23. package/lib/{patram-config.d.ts → config/patram-config.d.ts} +31 -31
  24. package/lib/{patram-config.js → config/patram-config.js} +3 -3
  25. package/lib/{patram-config.types.d.ts → config/patram-config.types.d.ts} +1 -1
  26. package/lib/{resolve-patram-graph-config.d.ts → config/resolve-patram-graph-config.d.ts} +2 -2
  27. package/lib/{resolve-patram-graph-config.js → config/resolve-patram-graph-config.js} +3 -3
  28. package/lib/{load-patram-config.d.ts → config/schema.d.ts} +147 -191
  29. package/lib/config/schema.js +324 -0
  30. package/lib/{source-file-defaults.d.ts → config/source-file-defaults.d.ts} +0 -1
  31. package/lib/{source-file-defaults.js → config/source-file-defaults.js} +1 -1
  32. package/lib/config/validation.d.ts +27 -0
  33. package/lib/config/validation.js +615 -0
  34. package/lib/directive-validation-test-helpers.js +1 -1
  35. package/lib/{build-graph-identity.d.ts → graph/build-graph-identity.d.ts} +2 -2
  36. package/lib/{build-graph-identity.js → graph/build-graph-identity.js} +1 -1
  37. package/lib/{build-graph.d.ts → graph/build-graph.d.ts} +3 -3
  38. package/lib/{build-graph.js → graph/build-graph.js} +17 -13
  39. package/lib/{build-graph.types.d.ts → graph/build-graph.types.d.ts} +1 -1
  40. package/lib/graph/check-directive-metadata.d.ts +23 -0
  41. package/lib/{check-directive-metadata.js → graph/check-directive-metadata.js} +7 -7
  42. package/lib/graph/check-directive-path-target.d.ts +32 -0
  43. package/lib/{check-directive-path-target.js → graph/check-directive-path-target.js} +4 -4
  44. package/lib/graph/check-directive-value.d.ts +19 -0
  45. package/lib/{check-directive-value.js → graph/check-directive-value.js} +3 -3
  46. package/lib/graph/check-graph.d.ts +29 -0
  47. package/lib/{check-graph.js → graph/check-graph.js} +6 -6
  48. package/lib/graph/directive-diagnostics.d.ts +20 -0
  49. package/lib/{directive-diagnostics.js → graph/directive-diagnostics.js} +2 -2
  50. package/lib/graph/directive-type-rules.d.ts +18 -0
  51. package/lib/{directive-type-rules.js → graph/directive-type-rules.js} +3 -3
  52. package/lib/{document-node-identity.d.ts → graph/document-node-identity.d.ts} +2 -2
  53. package/lib/{document-node-identity.js → graph/document-node-identity.js} +2 -2
  54. package/lib/graph/inspect-reverse-references.d.ts +22 -0
  55. package/lib/graph/inspect-reverse-references.js +184 -0
  56. package/lib/{load-project-graph.d.ts → graph/load-project-graph.d.ts} +10 -10
  57. package/lib/{load-project-graph.js → graph/load-project-graph.js} +12 -12
  58. package/lib/{parse-where-clause.types.d.ts → graph/parse-where-clause.types.d.ts} +1 -1
  59. package/lib/{query-graph.d.ts → graph/query/execute.d.ts} +11 -11
  60. package/lib/{query-graph.js → graph/query/execute.js} +12 -12
  61. package/lib/{query-inspection.d.ts → graph/query/inspect.d.ts} +10 -8
  62. package/lib/{query-inspection.js → graph/query/inspect.js} +16 -17
  63. package/lib/{parse-where-clause.d.ts → graph/query/parse.d.ts} +6 -6
  64. package/lib/{parse-where-clause.js → graph/query/parse.js} +2 -2
  65. package/lib/graph/query/resolve.d.ts +30 -0
  66. package/lib/{resolve-where-clause.js → graph/query/resolve.js} +1 -1
  67. package/lib/graph/reverse-reference-test-helpers.d.ts +55 -0
  68. package/lib/graph/reverse-reference-test-helpers.js +76 -0
  69. package/lib/{command-output.js → output/command-output.js} +6 -5
  70. package/lib/{derived-summary.js → output/derived-summary.js} +7 -7
  71. package/lib/output/layout-incoming-references.js +105 -0
  72. package/lib/output/layout-incoming-summary-lines.js +16 -0
  73. package/lib/{layout-stored-queries.js → output/layout-stored-queries.js} +9 -9
  74. package/lib/{list-queries.js → output/list-queries.js} +1 -1
  75. package/lib/{render-check-output.js → output/render-check-output.js} +1 -1
  76. package/lib/{render-field-discovery.js → output/render-field-discovery.js} +3 -3
  77. package/lib/output/render-output-view.js +56 -0
  78. package/lib/{render-json-output.js → output/renderers/json.js} +92 -63
  79. package/lib/{render-plain-output.js → output/renderers/plain.js} +62 -7
  80. package/lib/{render-rich-output.js → output/renderers/rich.js} +69 -8
  81. package/lib/{resolve-check-target.js → output/resolve-check-target.js} +1 -1
  82. package/lib/{render-rich-source.js → output/rich-source/render.js} +6 -6
  83. package/lib/{show-document.js → output/show-document.js} +54 -16
  84. package/lib/{render-output-view.js → output/view-model/index.js} +56 -47
  85. package/lib/{write-paged-output.js → output/write-paged-output.js} +9 -5
  86. package/lib/{claim-helpers.d.ts → parse/claim-helpers.d.ts} +2 -2
  87. package/lib/{parse-jsdoc-claims.d.ts → parse/jsdoc/parse-jsdoc-claims.d.ts} +2 -2
  88. package/lib/{parse-jsdoc-claims.js → parse/jsdoc/parse-jsdoc-claims.js} +9 -9
  89. package/lib/{parse-jsdoc-prose.d.ts → parse/jsdoc/parse-jsdoc-prose.d.ts} +1 -1
  90. package/lib/{parse-jsdoc-prose.js → parse/jsdoc/parse-jsdoc-prose.js} +1 -1
  91. package/lib/{parse-markdown-claims.d.ts → parse/markdown/parse-markdown-claims.d.ts} +3 -3
  92. package/lib/{parse-markdown-claims.js → parse/markdown/parse-markdown-claims.js} +8 -8
  93. package/lib/{parse-markdown-directives.d.ts → parse/markdown/parse-markdown-directives.d.ts} +2 -2
  94. package/lib/{parse-markdown-directives.js → parse/markdown/parse-markdown-directives.js} +3 -3
  95. package/lib/{parse-claims.d.ts → parse/parse-claims.d.ts} +4 -13
  96. package/lib/{parse-claims.js → parse/parse-claims.js} +18 -26
  97. package/lib/{parse-claims.types.d.ts → parse/parse-claims.types.d.ts} +1 -1
  98. package/lib/{tagged-fenced-block-error.d.ts → parse/tagged-fenced/tagged-fenced-block-error.d.ts} +2 -2
  99. package/lib/{tagged-fenced-block-parser.d.ts → parse/tagged-fenced/tagged-fenced-block-parser.d.ts} +3 -3
  100. package/lib/{tagged-fenced-blocks.d.ts → parse/tagged-fenced/tagged-fenced-blocks.d.ts} +7 -7
  101. package/lib/{tagged-fenced-blocks.js → parse/tagged-fenced/tagged-fenced-blocks.js} +3 -3
  102. package/lib/{parse-yaml-claims.d.ts → parse/yaml/parse-yaml-claims.d.ts} +4 -4
  103. package/lib/{parse-yaml-claims.js → parse/yaml/parse-yaml-claims.js} +22 -13
  104. package/lib/patram.d.ts +29 -28
  105. package/lib/patram.js +5 -6
  106. package/lib/{discover-fields.js → scan/discover-fields.js} +9 -8
  107. package/lib/scan/list-repo-files.d.ts +16 -0
  108. package/lib/{list-source-files.js → scan/list-repo-files.js} +2 -35
  109. package/lib/{list-source-files.d.ts → scan/list-source-files.d.ts} +4 -11
  110. package/lib/scan/list-source-files.js +45 -0
  111. package/package.json +8 -7
  112. package/lib/build-graph.types.ts +0 -27
  113. package/lib/discover-fields.types.ts +0 -52
  114. package/lib/load-patram-config.js +0 -1215
  115. package/lib/load-patram-config.types.d.ts +0 -45
  116. package/lib/load-patram-config.types.ts +0 -56
  117. package/lib/output-view.types.d.ts +0 -80
  118. package/lib/output-view.types.ts +0 -96
  119. package/lib/overlay-graph.d.ts +0 -43
  120. package/lib/overlay-graph.js +0 -191
  121. package/lib/parse-claims.types.ts +0 -41
  122. package/lib/parse-cli-arguments.types.ts +0 -69
  123. package/lib/parse-where-clause.types.ts +0 -87
  124. package/lib/patram-cli.js +0 -528
  125. package/lib/patram-config.types.ts +0 -22
  126. package/lib/tagged-fenced-blocks.types.ts +0 -38
  127. /package/lib/{format-derived-summary-row.js → output/format-derived-summary-row.js} +0 -0
  128. /package/lib/{format-node-header.js → output/format-node-header.js} +0 -0
  129. /package/lib/{format-output-item-block.js → output/format-output-item-block.js} +0 -0
  130. /package/lib/{format-output-metadata.js → output/format-output-metadata.js} +0 -0
  131. /package/lib/{claim-helpers.js → parse/claim-helpers.js} +0 -0
  132. /package/lib/{parse-jsdoc-blocks.d.ts → parse/jsdoc/parse-jsdoc-blocks.d.ts} +0 -0
  133. /package/lib/{parse-jsdoc-blocks.js → parse/jsdoc/parse-jsdoc-blocks.js} +0 -0
  134. /package/lib/{tagged-fenced-block-error.js → parse/tagged-fenced/tagged-fenced-block-error.js} +0 -0
  135. /package/lib/{tagged-fenced-block-markdown.d.ts → parse/tagged-fenced/tagged-fenced-block-markdown.d.ts} +0 -0
  136. /package/lib/{tagged-fenced-block-markdown.js → parse/tagged-fenced/tagged-fenced-block-markdown.js} +0 -0
  137. /package/lib/{tagged-fenced-block-metadata.d.ts → parse/tagged-fenced/tagged-fenced-block-metadata.d.ts} +0 -0
  138. /package/lib/{tagged-fenced-block-metadata.js → parse/tagged-fenced/tagged-fenced-block-metadata.js} +0 -0
  139. /package/lib/{tagged-fenced-block-parser.js → parse/tagged-fenced/tagged-fenced-block-parser.js} +0 -0
  140. /package/lib/{tagged-fenced-blocks.types.d.ts → parse/tagged-fenced/tagged-fenced-blocks.types.d.ts} +0 -0
@@ -3,7 +3,7 @@
3
3
  * @import { OutputStoredQueryItem } from './output-view.types.ts';
4
4
  */
5
5
 
6
- import { parseWhereClause } from './parse-where-clause.js';
6
+ import { parseWhereClause } from '../graph/query/parse.js';
7
7
 
8
8
  const MAX_STORED_QUERY_WIDTH = 100;
9
9
  const MIN_TERM_COLUMN_WIDTH = 20;
@@ -96,7 +96,7 @@ function createStoredQueryPhrases(where_clause) {
96
96
  }
97
97
 
98
98
  /**
99
- * @param {import('./parse-where-clause.types.ts').ParsedExpression} expression
99
+ * @param {import('../graph/parse-where-clause.types.ts').ParsedExpression} expression
100
100
  * @returns {StoredQuerySegment[][]}
101
101
  */
102
102
  function createExpressionPhrases(expression) {
@@ -114,7 +114,7 @@ function createExpressionPhrases(expression) {
114
114
  }
115
115
 
116
116
  /**
117
- * @param {import('./parse-where-clause.types.ts').ParsedExpression} expression
117
+ * @param {import('../graph/parse-where-clause.types.ts').ParsedExpression} expression
118
118
  * @param {'and' | 'or'} operator
119
119
  * @param {boolean} should_prefix_operator
120
120
  * @returns {StoredQuerySegment[]}
@@ -139,7 +139,7 @@ function createExpressionPhrase(expression, operator, should_prefix_operator) {
139
139
  }
140
140
 
141
141
  /**
142
- * @param {import('./parse-where-clause.types.ts').ParsedExpression} expression
142
+ * @param {import('../graph/parse-where-clause.types.ts').ParsedExpression} expression
143
143
  * @param {number} parent_precedence
144
144
  * @returns {StoredQuerySegment[]}
145
145
  */
@@ -159,7 +159,7 @@ function createExpressionSegments(expression, parent_precedence) {
159
159
  }
160
160
 
161
161
  /**
162
- * @param {import('./parse-where-clause.types.ts').ParsedExpression} expression
162
+ * @param {import('../graph/parse-where-clause.types.ts').ParsedExpression} expression
163
163
  * @returns {StoredQuerySegment[]}
164
164
  */
165
165
  function createRawExpressionSegments(expression) {
@@ -202,7 +202,7 @@ function createRawExpressionSegments(expression) {
202
202
  }
203
203
 
204
204
  /**
205
- * @param {import('./parse-where-clause.types.ts').ParsedExpression} expression
205
+ * @param {import('../graph/parse-where-clause.types.ts').ParsedExpression} expression
206
206
  * @returns {number}
207
207
  */
208
208
  function getExpressionPrecedence(expression) {
@@ -230,7 +230,7 @@ function getBooleanExpressionPrecedence(operator) {
230
230
  }
231
231
 
232
232
  /**
233
- * @param {import('./parse-where-clause.types.ts').ParsedTerm} term
233
+ * @param {import('../graph/parse-where-clause.types.ts').ParsedTerm} term
234
234
  * @returns {StoredQuerySegment[]}
235
235
  */
236
236
  function createTermSegments(term) {
@@ -265,7 +265,7 @@ function createTermSegments(term) {
265
265
  }
266
266
 
267
267
  /**
268
- * @param {import('./parse-where-clause.types.ts').ParsedFieldSetTerm} term
268
+ * @param {import('../graph/parse-where-clause.types.ts').ParsedFieldSetTerm} term
269
269
  * @returns {StoredQuerySegment[]}
270
270
  */
271
271
  function createFieldSetSegments(term) {
@@ -281,7 +281,7 @@ function createFieldSetSegments(term) {
281
281
  }
282
282
 
283
283
  /**
284
- * @param {import('./parse-where-clause.types.ts').ParsedAggregateTerm} term
284
+ * @param {import('../graph/parse-where-clause.types.ts').ParsedAggregateTerm} term
285
285
  * @returns {StoredQuerySegment[]}
286
286
  */
287
287
  function createAggregateSegments(term) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @import { StoredQueryConfig } from './load-patram-config.types.ts';
2
+ * @import { StoredQueryConfig } from '../config/load-patram-config.types.ts';
3
3
  */
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @import { PatramDiagnostic } from './load-patram-config.types.ts';
2
+ * @import { PatramDiagnostic } from '../config/load-patram-config.types.ts';
3
3
  * @import { ResolvedOutputMode } from './output-view.types.ts';
4
4
  */
5
5
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @import { ResolvedOutputMode } from './output-view.types.ts';
3
- * @import { FieldDiscoveryResult, FieldDiscoverySuggestion } from './discover-fields.types.ts';
3
+ * @import { FieldDiscoveryResult, FieldDiscoverySuggestion } from '../scan/discover-fields.types.ts';
4
4
  */
5
5
 
6
6
  import { Ansis } from 'ansis';
@@ -135,7 +135,7 @@ function formatTextFieldSuggestion(field_suggestion, render_options) {
135
135
 
136
136
  /**
137
137
  * @param {string} section_title
138
- * @param {import('./discover-fields.types.ts').FieldDiscoveryEvidenceReference[]} evidence_references
138
+ * @param {import('../scan/discover-fields.types.ts').FieldDiscoveryEvidenceReference[]} evidence_references
139
139
  * @param {{ header: (value: string) => string, label: (value: string) => string }} render_options
140
140
  * @returns {string[]}
141
141
  */
@@ -168,7 +168,7 @@ function formatTextEvidenceSection(
168
168
  }
169
169
 
170
170
  /**
171
- * @param {import('./discover-fields.types.ts').FieldDiscoveryEvidenceReference} evidence_reference
171
+ * @param {import('../scan/discover-fields.types.ts').FieldDiscoveryEvidenceReference} evidence_reference
172
172
  * @returns {string}
173
173
  */
174
174
  function formatEvidenceReference(evidence_reference) {
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @import { ParsedCliArguments } from '../cli/arguments.types.ts';
3
+ * @import { ResolvedOutputMode, OutputView } from './output-view.types.ts';
4
+ */
5
+
6
+ import { renderJsonOutput } from './renderers/json.js';
7
+ import { renderPlainOutput } from './renderers/plain.js';
8
+ import { renderRichOutput } from './renderers/rich.js';
9
+
10
+ /**
11
+ * Shared command output views.
12
+ *
13
+ * Normalizes `query`, `queries`, and `show` results into renderer-specific
14
+ * output models.
15
+ *
16
+ * Kind: output
17
+ * Status: active
18
+ * Tracked in: ../../docs/plans/v0/source-anchor-dogfooding.md
19
+ * Decided by: ../../docs/decisions/cli-output-architecture.md
20
+ * @patram
21
+ * @see {@link ./show-document.js}
22
+ * @see {@link ../../docs/decisions/cli-output-architecture.md}
23
+ */
24
+
25
+ export {
26
+ createOutputView,
27
+ createRefsOutputView,
28
+ createShowOutputView,
29
+ } from './view-model/index.js';
30
+
31
+ /**
32
+ * Render one shared output view through the resolved renderer.
33
+ *
34
+ * @param {OutputView} output_view
35
+ * @param {ResolvedOutputMode} output_mode
36
+ * @param {ParsedCliArguments} parsed_arguments
37
+ * @returns {Promise<string>}
38
+ */
39
+ export async function renderOutputView(
40
+ output_view,
41
+ output_mode,
42
+ parsed_arguments,
43
+ ) {
44
+ if (output_mode.renderer_name === 'json') {
45
+ return renderJsonOutput(output_view);
46
+ }
47
+
48
+ if (output_mode.renderer_name === 'plain') {
49
+ return renderPlainOutput(output_view);
50
+ }
51
+
52
+ return renderRichOutput(output_view, {
53
+ color_enabled: output_mode.color_enabled,
54
+ color_mode: parsed_arguments.color_mode,
55
+ });
56
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView } from './output-view.types.ts';
2
+ * @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView } from '../output-view.types.ts';
3
3
  */
4
4
 
5
5
  /**
@@ -10,54 +10,113 @@
10
10
  */
11
11
  export function renderJsonOutput(output_view) {
12
12
  if (output_view.command === 'query') {
13
- return `${JSON.stringify(
14
- {
15
- results: output_view.items.map(formatJsonQueryItem),
16
- summary: {
17
- shown_count: output_view.summary.count,
18
- total_count: output_view.summary.total_count,
19
- offset: output_view.summary.offset,
20
- limit: output_view.summary.limit,
21
- },
22
- hints: output_view.hints,
23
- },
24
- null,
25
- 2,
26
- )}\n`;
13
+ return renderJsonQueryOutput(output_view);
27
14
  }
28
15
 
29
16
  if (output_view.command === 'queries') {
30
- return `${JSON.stringify(
31
- {
32
- queries: output_view.items.map(formatJsonStoredQuery),
33
- },
34
- null,
35
- 2,
36
- )}\n`;
17
+ return renderJsonQueriesOutput(output_view.items);
18
+ }
19
+
20
+ if (output_view.command === 'refs') {
21
+ return renderJsonRefsOutput(output_view);
37
22
  }
38
23
 
39
24
  if (output_view.command === 'show') {
40
- return `${JSON.stringify(
41
- {
42
- document: output_view.document
43
- ? formatJsonShowDocument(output_view.document)
44
- : undefined,
45
- source: output_view.source,
46
- resolved_links: output_view.items.map(formatJsonResolvedLink),
47
- },
48
- null,
49
- 2,
50
- )}\n`;
25
+ return renderJsonShowOutput(output_view);
51
26
  }
52
27
 
53
28
  throw new Error('Unsupported output view command.');
54
29
  }
55
30
 
31
+ /**
32
+ * @param {Extract<OutputView, { command: 'query' }>} output_view
33
+ * @returns {string}
34
+ */
35
+ function renderJsonQueryOutput(output_view) {
36
+ return `${JSON.stringify(
37
+ {
38
+ results: output_view.items.map(formatJsonQueryItem),
39
+ summary: {
40
+ shown_count: output_view.summary.count,
41
+ total_count: output_view.summary.total_count,
42
+ offset: output_view.summary.offset,
43
+ limit: output_view.summary.limit,
44
+ },
45
+ hints: output_view.hints,
46
+ },
47
+ null,
48
+ 2,
49
+ )}\n`;
50
+ }
51
+
52
+ /**
53
+ * @param {OutputStoredQueryItem[]} output_items
54
+ * @returns {string}
55
+ */
56
+ function renderJsonQueriesOutput(output_items) {
57
+ return `${JSON.stringify(
58
+ {
59
+ queries: output_items.map(formatJsonStoredQuery),
60
+ },
61
+ null,
62
+ 2,
63
+ )}\n`;
64
+ }
65
+
66
+ /**
67
+ * @param {Extract<OutputView, { command: 'refs' }>} output_view
68
+ * @returns {string}
69
+ */
70
+ function renderJsonRefsOutput(output_view) {
71
+ return `${JSON.stringify(
72
+ {
73
+ node: formatJsonNodeItem(output_view.node),
74
+ incoming: Object.fromEntries(
75
+ Object.entries(output_view.incoming).map(
76
+ ([relation_name, output_items]) => [
77
+ relation_name,
78
+ output_items.map(formatJsonNodeItem),
79
+ ],
80
+ ),
81
+ ),
82
+ },
83
+ null,
84
+ 2,
85
+ )}\n`;
86
+ }
87
+
88
+ /**
89
+ * @param {Extract<OutputView, { command: 'show' }>} output_view
90
+ * @returns {string}
91
+ */
92
+ function renderJsonShowOutput(output_view) {
93
+ return `${JSON.stringify(
94
+ {
95
+ document: output_view.document
96
+ ? formatJsonNodeItem(output_view.document)
97
+ : undefined,
98
+ incoming_summary: output_view.incoming_summary,
99
+ source: output_view.source,
100
+ resolved_links: output_view.items.map(formatJsonResolvedLink),
101
+ },
102
+ null,
103
+ 2,
104
+ )}\n`;
105
+ }
106
+
56
107
  /**
57
108
  * @param {OutputNodeItem} output_item
58
109
  * @returns {{ '$class': string, '$id': string, '$path'?: string, derived?: Record<string, boolean | number | string | null>, derived_summary?: string, fields: Record<string, string | string[]>, title: string }}
59
110
  */
60
111
  function formatJsonQueryItem(output_item) {
112
+ return formatJsonNodeItem(output_item);
113
+ }
114
+
115
+ /**
116
+ * @param {OutputNodeItem} output_item
117
+ * @returns {{ '$class': string, '$id': string, '$path'?: string, derived?: Record<string, boolean | number | string | null>, derived_summary?: string, fields: Record<string, string | string[]>, title: string }}
118
+ */
119
+ function formatJsonNodeItem(output_item) {
61
120
  /** @type {{ '$class': string, '$id': string, '$path'?: string, derived?: Record<string, boolean | number | string | null>, derived_summary?: string, fields: Record<string, string | string[]>, title: string }} */
62
121
  const query_item = {
63
122
  $class: output_item.node_kind,
@@ -128,33 +187,3 @@ function formatJsonResolvedLink(output_item) {
128
187
 
129
188
  return resolved_link;
130
189
  }
131
-
132
- /**
133
- * @param {OutputNodeItem} output_item
134
- * @returns {{ '$class': string, '$id': string, '$path'?: string, derived?: Record<string, boolean | number | string | null>, derived_summary?: string, fields: Record<string, string | string[]>, title: string }}
135
- */
136
- function formatJsonShowDocument(output_item) {
137
- /** @type {{ '$class': string, '$id': string, '$path'?: string, derived?: Record<string, boolean | number | string | null>, derived_summary?: string, fields: Record<string, string | string[]>, title: string }} */
138
- const document_summary = {
139
- $class: output_item.node_kind,
140
- $id: output_item.id,
141
- fields: output_item.fields,
142
- title: output_item.title,
143
- };
144
-
145
- if (output_item.path) {
146
- document_summary.$path = output_item.path;
147
- }
148
-
149
- if (output_item.derived_summary) {
150
- document_summary.derived_summary = output_item.derived_summary.name;
151
- document_summary.derived = Object.fromEntries(
152
- output_item.derived_summary.fields.map((field) => [
153
- field.name,
154
- field.value,
155
- ]),
156
- );
157
- }
158
-
159
- return document_summary;
160
- }
@@ -1,14 +1,16 @@
1
1
  /**
2
- * @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView, QueryOutputView, ShowOutputView } from './output-view.types.ts';
2
+ * @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView, QueryOutputView, RefsOutputView, ShowOutputView } from '../output-view.types.ts';
3
3
  */
4
4
 
5
5
  import {
6
6
  formatOutputNodeMetadataRows,
7
7
  formatResolvedLinkMetadataRows,
8
- } from './format-output-metadata.js';
9
- import { formatNodeHeader } from './format-node-header.js';
10
- import { formatOutputItemBlock } from './format-output-item-block.js';
11
- import { layoutStoredQueries } from './layout-stored-queries.js';
8
+ } from '../format-output-metadata.js';
9
+ import { formatNodeHeader } from '../format-node-header.js';
10
+ import { formatOutputItemBlock } from '../format-output-item-block.js';
11
+ import { layoutIncomingReferenceLines } from '../layout-incoming-references.js';
12
+ import { layoutIncomingSummaryLines } from '../layout-incoming-summary-lines.js';
13
+ import { layoutStoredQueries } from '../layout-stored-queries.js';
12
14
 
13
15
  /**
14
16
  * Render the canonical plain output for one output view.
@@ -25,6 +27,10 @@ export function renderPlainOutput(output_view) {
25
27
  return renderPlainStoredQueries(output_view.items);
26
28
  }
27
29
 
30
+ if (output_view.command === 'refs') {
31
+ return renderPlainRefsOutput(output_view);
32
+ }
33
+
28
34
  if (output_view.command === 'show') {
29
35
  return renderPlainShowOutput(output_view);
30
36
  }
@@ -85,8 +91,13 @@ function renderPlainShowOutput(output_view) {
85
91
  const document_summary = output_view.document
86
92
  ? formatPlainNodeItem(output_view.document)
87
93
  : '';
94
+ const incoming_summary = renderPlainIncomingSummary(output_view);
88
95
 
89
- if (document_summary.length === 0 && output_view.items.length === 0) {
96
+ if (
97
+ document_summary.length === 0 &&
98
+ output_view.items.length === 0 &&
99
+ incoming_summary.length === 0
100
+ ) {
90
101
  return `${rendered_source}\n`;
91
102
  }
92
103
 
@@ -99,7 +110,28 @@ function renderPlainShowOutput(output_view) {
99
110
 
100
111
  summary_items.push(...output_view.items.map(formatPlainResolvedLinkItem));
101
112
 
102
- return `${rendered_source}\n\n----------------\n${summary_items.join('\n\n')}\n`;
113
+ const summary_blocks = summary_items.filter((summary_item) => summary_item);
114
+
115
+ if (incoming_summary.length > 0) {
116
+ summary_blocks.push(incoming_summary);
117
+ }
118
+
119
+ return `${rendered_source}\n\n----------------\n${summary_blocks.join('\n\n')}\n`;
120
+ }
121
+
122
+ /**
123
+ * @param {RefsOutputView} output_view
124
+ * @returns {string}
125
+ */
126
+ function renderPlainRefsOutput(output_view) {
127
+ const node_summary = formatPlainNodeItem(output_view.node);
128
+ const output_lines = layoutIncomingReferenceLines(output_view.incoming);
129
+
130
+ if (output_lines.length === 0) {
131
+ return `${node_summary}\n\nNo incoming references.\n`;
132
+ }
133
+
134
+ return `${node_summary}\n\n${output_lines.join('\n')}\n`;
103
135
  }
104
136
 
105
137
  /**
@@ -135,6 +167,29 @@ function formatPlainResolvedLinkItem(output_item) {
135
167
  });
136
168
  }
137
169
 
170
+ /**
171
+ * @param {ShowOutputView} output_view
172
+ * @returns {string}
173
+ */
174
+ function renderPlainIncomingSummary(output_view) {
175
+ if (!hasIncomingSummary(output_view.incoming_summary)) {
176
+ return '';
177
+ }
178
+
179
+ const output_lines = layoutIncomingSummaryLines(output_view.incoming_summary);
180
+ output_lines.push('', `Hint: patram refs ${output_view.path}`);
181
+
182
+ return output_lines.join('\n');
183
+ }
184
+
185
+ /**
186
+ * @param {Record<string, number>} incoming_summary
187
+ * @returns {boolean}
188
+ */
189
+ function hasIncomingSummary(incoming_summary) {
190
+ return Object.keys(incoming_summary).length > 0;
191
+ }
192
+
138
193
  /**
139
194
  * @param {string} value
140
195
  * @returns {string}
@@ -1,6 +1,6 @@
1
1
  /**
2
- * @import { CliColorMode } from './parse-cli-arguments.types.ts';
3
- * @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView, QueryOutputView, ShowOutputView } from './output-view.types.ts';
2
+ * @import { CliColorMode } from '../../cli/arguments.types.ts';
3
+ * @import { OutputNodeItem, OutputResolvedLinkItem, OutputStoredQueryItem, OutputView, QueryOutputView, RefsOutputView, ShowOutputView } from '../output-view.types.ts';
4
4
  */
5
5
 
6
6
  import { Ansis } from 'ansis';
@@ -8,11 +8,13 @@ import { Ansis } from 'ansis';
8
8
  import {
9
9
  formatOutputNodeMetadataRows,
10
10
  formatResolvedLinkMetadataRows,
11
- } from './format-output-metadata.js';
12
- import { formatNodeHeader } from './format-node-header.js';
13
- import { formatOutputItemBlock } from './format-output-item-block.js';
14
- import { layoutStoredQueries } from './layout-stored-queries.js';
15
- import { renderRichSource } from './render-rich-source.js';
11
+ } from '../format-output-metadata.js';
12
+ import { formatNodeHeader } from '../format-node-header.js';
13
+ import { formatOutputItemBlock } from '../format-output-item-block.js';
14
+ import { layoutIncomingReferenceLines } from '../layout-incoming-references.js';
15
+ import { layoutIncomingSummaryLines } from '../layout-incoming-summary-lines.js';
16
+ import { layoutStoredQueries } from '../layout-stored-queries.js';
17
+ import { renderRichSource } from '../rich-source/render.js';
16
18
 
17
19
  const FULL_WIDTH_DIVIDER = ` ${'─'.repeat(78)} `;
18
20
 
@@ -34,6 +36,9 @@ export async function renderRichOutput(output_view, render_options) {
34
36
  return renderRichStoredQueries(output_view.items, ansi);
35
37
  }
36
38
 
39
+ if (output_view.command === 'refs') {
40
+ return renderRichRefsOutput(output_view, ansi);
41
+ }
37
42
  if (output_view.command === 'show') {
38
43
  return renderRichShowOutput(output_view, render_options, ansi);
39
44
  }
@@ -105,8 +110,13 @@ async function renderRichShowOutput(output_view, render_options, ansi) {
105
110
  const document_summary = output_view.document
106
111
  ? formatRichNodeItem(output_view.document, ansi)
107
112
  : '';
113
+ const incoming_summary = renderRichIncomingSummary(output_view, ansi);
108
114
 
109
- if (document_summary.length === 0 && output_view.items.length === 0) {
115
+ if (
116
+ document_summary.length === 0 &&
117
+ output_view.items.length === 0 &&
118
+ incoming_summary.length === 0
119
+ ) {
110
120
  return `${rendered_source}\n`;
111
121
  }
112
122
 
@@ -121,9 +131,34 @@ async function renderRichShowOutput(output_view, render_options, ansi) {
121
131
  ...output_view.items.map((item) => formatRichResolvedLinkItem(item, ansi)),
122
132
  );
123
133
 
134
+ if (incoming_summary.length > 0) {
135
+ summary_items.push(incoming_summary);
136
+ }
137
+
124
138
  return `${rendered_source}\n\n${ansi.gray(FULL_WIDTH_DIVIDER)}\n\n${summary_items.join('\n\n')}\n`;
125
139
  }
126
140
 
141
+ /**
142
+ * @param {RefsOutputView} output_view
143
+ * @param {Ansis} ansi
144
+ * @returns {string}
145
+ */
146
+ function renderRichRefsOutput(output_view, ansi) {
147
+ const node_summary = formatRichNodeItem(output_view.node, ansi);
148
+ const output_lines = layoutIncomingReferenceLines(output_view.incoming, {
149
+ format_node_header(output_item) {
150
+ return ansi.green(formatNodeHeader(output_item));
151
+ },
152
+ format_relation_header(relation_name, relation_count) {
153
+ return `${ansi.bold(relation_name)} ${ansi.gray(`(${relation_count})`)}`;
154
+ },
155
+ });
156
+
157
+ return output_lines.length === 0
158
+ ? `${node_summary}\n\n${ansi.yellow('No incoming references.')}\n`
159
+ : `${node_summary}\n\n${output_lines.join('\n')}\n`;
160
+ }
161
+
127
162
  /**
128
163
  * @param {OutputNodeItem} output_item
129
164
  * @param {Ansis} ansi
@@ -162,6 +197,32 @@ function formatRichResolvedLinkItem(output_item, ansi) {
162
197
  });
163
198
  }
164
199
 
200
+ /**
201
+ * @param {ShowOutputView} output_view
202
+ * @param {Ansis} ansi
203
+ * @returns {string}
204
+ */
205
+ function renderRichIncomingSummary(output_view, ansi) {
206
+ if (!hasIncomingSummary(output_view.incoming_summary)) {
207
+ return '';
208
+ }
209
+
210
+ const output_lines = layoutIncomingSummaryLines(output_view.incoming_summary);
211
+ output_lines[0] = ansi.bold(output_lines[0]);
212
+
213
+ output_lines.push('', ansi.gray(`Hint: patram refs ${output_view.path}`));
214
+
215
+ return output_lines.join('\n');
216
+ }
217
+
218
+ /**
219
+ * @param {Record<string, number>} incoming_summary
220
+ * @returns {boolean}
221
+ */
222
+ function hasIncomingSummary(incoming_summary) {
223
+ return Object.keys(incoming_summary).length > 0;
224
+ }
225
+
165
226
  /**
166
227
  * @param {boolean} color_enabled
167
228
  * @returns {Ansis}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @import { PatramDiagnostic } from './load-patram-config.types.ts';
2
+ * @import { PatramDiagnostic } from '../config/load-patram-config.types.ts';
3
3
  */
4
4
  import { access, stat } from 'node:fs/promises';
5
5
  import { dirname, relative, resolve } from 'node:path';
@@ -2,8 +2,8 @@
2
2
  /**
3
3
  * @import { ComarkElement, ComarkNode } from 'md4x';
4
4
  * @import { BundledLanguage } from 'shiki';
5
- * @import { CliColorMode } from './parse-cli-arguments.types.ts';
6
- * @import { OutputResolvedLinkItem, ShowOutputView } from './output-view.types.ts';
5
+ * @import { CliColorMode } from '../../cli/arguments.types.ts';
6
+ * @import { OutputResolvedLinkItem, ShowOutputView } from '../output-view.types.ts';
7
7
  */
8
8
 
9
9
  import { extname } from 'node:path';
@@ -969,8 +969,8 @@ function shouldRenderListItemGap(previous_item, next_item, has_blank_line_gap) {
969
969
  }
970
970
 
971
971
  return (
972
- isSimpleTopLevelListItem(previous_item) &&
973
- isSimpleTopLevelListItem(next_item)
972
+ isSingleParagraphTopLevelListItem(previous_item) &&
973
+ isSingleParagraphTopLevelListItem(next_item)
974
974
  );
975
975
  }
976
976
 
@@ -978,7 +978,7 @@ function shouldRenderListItemGap(previous_item, next_item, has_blank_line_gap) {
978
978
  * @param {ComarkElement} item_node
979
979
  * @returns {boolean}
980
980
  */
981
- function isSimpleTopLevelListItem(item_node) {
981
+ function isSingleParagraphTopLevelListItem(item_node) {
982
982
  const item_children = getElementChildren(item_node);
983
983
 
984
984
  if (item_children.length !== 1) {
@@ -994,7 +994,7 @@ function isSimpleTopLevelListItem(item_node) {
994
994
  return false;
995
995
  }
996
996
 
997
- return !extractInlineText(getElementChildren(paragraph_node)).includes('\n');
997
+ return true;
998
998
  }
999
999
 
1000
1000
  /**