@redpanda-data/docs-extensions-and-macros 5.14.0 → 5.22.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 (56) hide show
  1. package/bin/doc-tools-mcp.js +88 -0
  2. package/bin/doc-tools.js +334 -102
  3. package/bin/mcp-tools/index.js +20 -0
  4. package/bin/mcp-tools/lint-doc-strings.js +75 -0
  5. package/bin/mcp-tools/overrides-audit.js +100 -0
  6. package/bin/mcp-tools/preview-doc-string.js +71 -0
  7. package/cli-utils/diff-utils.js +77 -0
  8. package/cli-utils/helm-commented-values.js +172 -23
  9. package/cli-utils/validation-result.js +53 -0
  10. package/macros/REFERENCE.adoc +3 -2
  11. package/macros/prop.js +34 -6
  12. package/package.json +2 -1
  13. package/tools/generate-cli-docs.js +28 -67
  14. package/tools/lint-strings/diff.js +137 -0
  15. package/tools/lint-strings/engine.js +163 -0
  16. package/tools/lint-strings/fixtures/connect/lint_connect.go +67 -0
  17. package/tools/lint-strings/fixtures/crd/crd-ref-docs-config.yaml +15 -0
  18. package/tools/lint-strings/fixtures/crd/lint_types.go +57 -0
  19. package/tools/lint-strings/fixtures/helm/lint_values.yaml +47 -0
  20. package/tools/lint-strings/fixtures/metrics/lint_probe.cc +41 -0
  21. package/tools/lint-strings/fixtures/properties/lint_fixture.cc +51 -0
  22. package/tools/lint-strings/fixtures/properties/lint_fixture.h +33 -0
  23. package/tools/lint-strings/fixtures/rpk/lint_cmd.go +69 -0
  24. package/tools/lint-strings/go-source.js +327 -0
  25. package/tools/lint-strings/index.js +229 -0
  26. package/tools/lint-strings/rules/common.js +148 -0
  27. package/tools/lint-strings/rules/verbatim-asciidoc.js +108 -0
  28. package/tools/lint-strings/source-text.js +41 -0
  29. package/tools/lint-strings/surfaces/connect.js +233 -0
  30. package/tools/lint-strings/surfaces/crd.js +239 -0
  31. package/tools/lint-strings/surfaces/helm.js +159 -0
  32. package/tools/lint-strings/surfaces/metrics.js +368 -0
  33. package/tools/lint-strings/surfaces/properties.js +216 -0
  34. package/tools/lint-strings/surfaces/rpk.js +377 -0
  35. package/tools/overrides-audit/README.adoc +152 -0
  36. package/tools/overrides-audit/adapters/connect.js +144 -0
  37. package/tools/overrides-audit/adapters/properties.js +117 -0
  38. package/tools/overrides-audit/adapters/rpk.js +192 -0
  39. package/tools/overrides-audit/classify.js +546 -0
  40. package/tools/overrides-audit/index.js +185 -0
  41. package/tools/preview-string/index.js +245 -0
  42. package/tools/property-extractor/Makefile +9 -4
  43. package/tools/property-extractor/README.adoc +17 -3
  44. package/tools/property-extractor/helpers/index.js +1 -2
  45. package/tools/property-extractor/helpers/seeAlsoView.js +76 -0
  46. package/tools/property-extractor/parser.py +26 -0
  47. package/tools/property-extractor/property_extractor.py +65 -6
  48. package/tools/property-extractor/templates/property.hbs +25 -37
  49. package/tools/property-extractor/templates/topic-property.hbs +25 -37
  50. package/tools/property-extractor/transformers.py +15 -2
  51. package/tools/property-extractor/validate-overrides.js +120 -0
  52. package/tools/rpk-docs/detect-platform-commands.js +8 -1
  53. package/tools/rpk-docs/scan-deprecated-commands.js +15 -18
  54. package/tools/rpk-docs/validate-overrides.js +1 -45
  55. package/tools/property-extractor/helpers/allTopicsConditional.js +0 -19
  56. package/tools/property-extractor/helpers/parseRelatedTopic.js +0 -13
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Overrides Audit - Properties Adapter
3
+ *
4
+ * Reads docs-data/property-overrides.json and an extracted-properties JSON
5
+ * (the property extractor's raw --output, WITHOUT overrides applied) and
6
+ * classifies every override field. This is the complete reference adapter;
7
+ * rpk and connect adapters follow the same shape.
8
+ */
9
+
10
+ 'use strict'
11
+
12
+ const fs = require('fs')
13
+ const classify = require('../classify')
14
+ const { compareProperties } = require('../../property-extractor/compare-properties')
15
+
16
+ /**
17
+ * Load and parse a JSON file with a helpful error message.
18
+ *
19
+ * @param {string} filePath - Path to the JSON file.
20
+ * @param {string} label - Human label for error messages.
21
+ * @returns {Object} Parsed JSON.
22
+ */
23
+ function loadJson (filePath, label) {
24
+ let raw
25
+ try {
26
+ raw = fs.readFileSync(filePath, 'utf8')
27
+ } catch (err) {
28
+ throw new Error(`Cannot read ${label} file at ${filePath}: ${err.message}`)
29
+ }
30
+ try {
31
+ return JSON.parse(raw)
32
+ } catch (err) {
33
+ throw new Error(`Invalid JSON in ${label} file ${filePath}: ${err.message}`)
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Cross-check the audit with compare-properties.js: overlay the override
39
+ * descriptions onto a copy of the extracted properties and diff the two.
40
+ * Every description the raw diff reports as unchanged must have classified
41
+ * REDUNDANT (the audit's normalization only widens equality, never narrows
42
+ * it), so a violation indicates a classifier bug.
43
+ *
44
+ * @param {Object} extractedDoc - Extracted properties document.
45
+ * @param {Object} overrides - Override entries map.
46
+ * @param {Object[]} manifest - Classified manifest rows.
47
+ * @returns {Object} { changedDescriptions, emptyDescriptions, violations }.
48
+ */
49
+ function crossCheckWithCompare (extractedDoc, overrides, manifest) {
50
+ const extractedProps = (extractedDoc && extractedDoc.properties) || {}
51
+ const overlaidProps = {}
52
+ for (const [name, prop] of Object.entries(extractedProps)) {
53
+ const override = overrides[name]
54
+ overlaidProps[name] = override && typeof override.description === 'string'
55
+ ? { ...prop, description: override.description }
56
+ : prop
57
+ }
58
+
59
+ const report = compareProperties(
60
+ { properties: extractedProps },
61
+ { properties: overlaidProps },
62
+ 'source',
63
+ 'source+overrides'
64
+ )
65
+
66
+ // Normalization-only differences legitimately appear "changed" in the raw
67
+ // diff while classifying REDUNDANT. The inverse can never happen: a
68
+ // raw-equal description that did not classify REDUNDANT is a classifier bug.
69
+ const changedNames = new Set(report.changedDescriptions.map((entry) => entry.name))
70
+ const violations = manifest.filter(
71
+ (row) =>
72
+ row.field === 'description' &&
73
+ row.class !== classify.CLASSES.REDUNDANT &&
74
+ overrides[row.name] &&
75
+ typeof overrides[row.name].description === 'string' &&
76
+ extractedProps[row.name] &&
77
+ !changedNames.has(row.name)
78
+ )
79
+
80
+ return {
81
+ changedDescriptions: report.changedDescriptions.length,
82
+ emptyDescriptions: report.emptyDescriptions.length,
83
+ violations: violations.map((row) => row.name)
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Run the audit for the properties surface.
89
+ *
90
+ * @param {Object} args - { overridesPath, extractedPath, attrAllowlist }.
91
+ * @returns {Object} { surface, manifest, summary, cross_check }.
92
+ */
93
+ function audit ({ overridesPath, extractedPath, attrAllowlist }) {
94
+ const overridesDoc = loadJson(overridesPath, 'overrides')
95
+ const extractedDoc = loadJson(extractedPath, 'extracted properties')
96
+
97
+ if (!overridesDoc.properties || typeof overridesDoc.properties !== 'object') {
98
+ throw new Error(`Overrides file ${overridesPath} has no top-level "properties" object`)
99
+ }
100
+ if (!extractedDoc.properties || typeof extractedDoc.properties !== 'object') {
101
+ throw new Error(`Extracted file ${extractedPath} has no top-level "properties" object; pass the property extractor's JSON output`)
102
+ }
103
+
104
+ const { manifest, summary } = classify.classifyProperties(overridesDoc, extractedDoc, { attrAllowlist })
105
+ const crossCheck = crossCheckWithCompare(extractedDoc, overridesDoc.properties, manifest)
106
+
107
+ return {
108
+ surface: 'properties',
109
+ overrides_file: overridesPath,
110
+ extracted_file: extractedPath,
111
+ manifest,
112
+ summary,
113
+ cross_check: crossCheck
114
+ }
115
+ }
116
+
117
+ module.exports = { audit, loadJson }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Overrides Audit - rpk Adapter (structural)
3
+ *
4
+ * Reads docs-data/rpk-overrides.json (schema: docs-data/rpk-overrides.schema.json)
5
+ * and enumerates every override unit per command:
6
+ * - command `description` and `flags.<flag>.description` are the
7
+ * upstreamable prose fields (rpk cobra Short/Long and flag usage strings)
8
+ * - everything else (seeAlso, pageAliases, introducedInVersion, platforms,
9
+ * selfHostedOnly, pageAttributes, $refs, _note, content, prerequisites,
10
+ * descriptionScope, appendToDescription, exclude, asPartial) is docs-site
11
+ * structure that stays in the override layer by design.
12
+ *
13
+ * TODO(quality logic): source comparison for rpk descriptions is deferred.
14
+ * Two blockers, both known:
15
+ * 1. The versioned rpk-v<ver>.json files in docs repos are POST-override
16
+ * snapshots (rpk-docs generation applies rpk-overrides.json before
17
+ * writing them), so auditing against them classifies every override
18
+ * REDUNDANT. The audit needs a raw `rpk --print-tree` snapshot instead.
19
+ * 2. Generated docs pass source strings through formatDescription()
20
+ * (tools/rpk-docs/generate-rpk-docs.js) — ~40 regex passes (md->adoc,
21
+ * auto-backticking, ensurePeriod, ...). Equality must be computed on
22
+ * formatDescription(source) vs the override, not on the raw cobra string,
23
+ * or near-identical strings will misclassify as UPSTREAMABLE.
24
+ * Until then, prose fields classify REVIEW with an explicit note, and
25
+ * docs-structure fields classify KEEP, so the manifest shape is already
26
+ * stable for the retirement workflow.
27
+ */
28
+
29
+ 'use strict'
30
+
31
+ const classify = require('../classify')
32
+ const { loadJson } = require('./properties')
33
+
34
+ // Top-level keys of rpk-overrides.json that are not command entries.
35
+ const NON_COMMAND_KEYS = ['$schema', '_notes', 'textTransformations', 'definitions']
36
+
37
+ // Per-command fields that stay in the override layer by design.
38
+ const KEEP_BY_DESIGN_FIELDS = [
39
+ 'seeAlso',
40
+ 'pageAliases',
41
+ 'introducedInVersion',
42
+ 'platforms',
43
+ 'selfHostedOnly',
44
+ 'pageAttributes',
45
+ '$refs',
46
+ '_note',
47
+ 'content',
48
+ 'prerequisites',
49
+ 'descriptionScope',
50
+ 'appendToDescription',
51
+ 'exclude',
52
+ 'asPartial'
53
+ ]
54
+
55
+ /**
56
+ * Find a command node in an extracted `rpk --print-tree` JSON by its full
57
+ * name (for example "rpk topic create").
58
+ *
59
+ * @param {Object} tree - Root tree node ({ name, commands: [...] }).
60
+ * @param {string} fullName - Space-separated command path.
61
+ * @returns {Object|null} The command node, or null when absent.
62
+ */
63
+ function findCommandNode (tree, fullName) {
64
+ if (!tree || typeof tree !== 'object') return null
65
+ const parts = fullName.trim().split(/\s+/)
66
+ if (parts[0] !== tree.name) return null
67
+ let node = tree
68
+ for (const part of parts.slice(1)) {
69
+ const children = Array.isArray(node.commands) ? node.commands : []
70
+ node = children.find((child) => child && child.name === part) || null
71
+ if (!node) return null
72
+ }
73
+ return node
74
+ }
75
+
76
+ /**
77
+ * Build the manifest row for an rpk prose field (deferred quality logic).
78
+ *
79
+ * @param {string} name - Unit name ("rpk topic create" or "rpk topic create --partitions").
80
+ * @param {string} field - Manifest field label.
81
+ * @param {string} text - Override description text.
82
+ * @param {Object|undefined} upstreamRef - upstream_ref carried from the override.
83
+ * @param {boolean|null} foundInTree - Whether the command exists in the extracted tree (null when no tree given).
84
+ * @returns {Object} Manifest row.
85
+ */
86
+ function proseRow (name, field, text, upstreamRef, foundInTree) {
87
+ const row = {
88
+ name,
89
+ field,
90
+ class: classify.CLASSES.REVIEW,
91
+ content_hash: classify.contentHash(name, text)
92
+ }
93
+ if (upstreamRef !== undefined) row.upstream_ref = upstreamRef
94
+ if (foundInTree === false) {
95
+ row.note = 'Command not found in the extracted rpk tree; the override may be stale. TODO: rpk source comparison not implemented yet.'
96
+ } else {
97
+ row.note = 'TODO: rpk source comparison not implemented yet (needs a raw pre-override --print-tree snapshot and the formatDescription() pipeline).'
98
+ }
99
+ return row
100
+ }
101
+
102
+ /**
103
+ * Run the audit for the rpk surface (structural pass).
104
+ *
105
+ * @param {Object} args - { overridesPath, extractedPath }.
106
+ * @returns {Object} { surface, manifest, summary }.
107
+ */
108
+ function audit ({ overridesPath, extractedPath }) {
109
+ const overridesDoc = loadJson(overridesPath, 'rpk overrides')
110
+ const commands = overridesDoc.commands
111
+ if (!commands || typeof commands !== 'object') {
112
+ throw new Error(`rpk overrides file ${overridesPath} has no top-level "commands" object`)
113
+ }
114
+
115
+ let tree = null
116
+ if (extractedPath) {
117
+ const extractedDoc = loadJson(extractedPath, 'extracted rpk tree')
118
+ tree = extractedDoc.raw_tree || extractedDoc.tree || null
119
+ }
120
+
121
+ const manifest = []
122
+ for (const [commandName, entry] of Object.entries(commands)) {
123
+ if (typeof entry !== 'object' || entry === null) continue
124
+
125
+ const node = tree ? findCommandNode(tree, commandName) : null
126
+ const foundInTree = tree ? node !== null : null
127
+
128
+ for (const [field, value] of Object.entries(entry)) {
129
+ if (field === 'description') {
130
+ manifest.push(proseRow(commandName, 'description', value, entry.upstream_ref, foundInTree))
131
+ } else if (field === 'flags' && value && typeof value === 'object') {
132
+ for (const [flagName, flagEntry] of Object.entries(value)) {
133
+ if (flagEntry && typeof flagEntry.description === 'string') {
134
+ manifest.push(proseRow(
135
+ `${commandName} --${flagName}`,
136
+ 'flags.description',
137
+ flagEntry.description,
138
+ flagEntry.upstream_ref,
139
+ foundInTree
140
+ ))
141
+ }
142
+ }
143
+ } else if (field === 'upstream_ref' || field === '_comment') {
144
+ // Meta fields; upstream_ref is carried onto the prose rows above.
145
+ } else if (KEEP_BY_DESIGN_FIELDS.includes(field)) {
146
+ manifest.push({
147
+ name: commandName,
148
+ field,
149
+ class: classify.CLASSES.KEEP,
150
+ note: 'Docs-site structure; stays in the rpk override file by design.'
151
+ })
152
+ } else {
153
+ manifest.push({
154
+ name: commandName,
155
+ field,
156
+ class: classify.CLASSES.KEEP,
157
+ note: `Unrecognized rpk override field '${field}'; kept, verify it against docs-data/rpk-overrides.schema.json.`
158
+ })
159
+ }
160
+ }
161
+ }
162
+
163
+ // Shared flag definitions referenced via $refs also carry descriptions.
164
+ // Shape: definitions.<def-name>.<flag-name>.description
165
+ const definitions = overridesDoc.definitions
166
+ if (definitions && typeof definitions === 'object') {
167
+ for (const [defName, flags] of Object.entries(definitions)) {
168
+ if (!flags || typeof flags !== 'object') continue
169
+ for (const [flagName, flagEntry] of Object.entries(flags)) {
170
+ if (flagEntry && typeof flagEntry.description === 'string') {
171
+ manifest.push(proseRow(
172
+ `definitions/${defName} --${flagName}`,
173
+ 'flags.description',
174
+ flagEntry.description,
175
+ flagEntry.upstream_ref,
176
+ null
177
+ ))
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ return {
184
+ surface: 'rpk',
185
+ overrides_file: overridesPath,
186
+ extracted_file: extractedPath || null,
187
+ manifest,
188
+ summary: classify.summarize(manifest)
189
+ }
190
+ }
191
+
192
+ module.exports = { audit, findCommandNode, NON_COMMAND_KEYS }