@redpanda-data/docs-extensions-and-macros 5.38.1 → 5.38.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "5.38.1",
3
+ "version": "5.38.2",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",
@@ -33,7 +33,11 @@ const CONVENTION = {
33
33
  case: 'sentence',
34
34
  terminal_period: true,
35
35
  verbatim_asciidoc: true,
36
- state_default: true
36
+ // Not state_default. The rendered page builds a "Default:" row from the
37
+ // extractor's own field, so prose restating a concrete default duplicates
38
+ // structured data. Only a sentinel default (null, 0, -1, "") carries
39
+ // behavior that row cannot express.
40
+ state_sentinel_default: true
37
41
  }
38
42
 
39
43
  function run (cmd, args, options = {}) {
@@ -133,8 +137,9 @@ function mapExtractorJson (json, repo, options = {}) {
133
137
 
134
138
  const lineStart = prop.line_start != null ? prop.line_start : null
135
139
  const lineEnd = prop.line_end != null ? prop.line_end : lineStart
136
- const hasDefault = Object.prototype.hasOwnProperty.call(prop, 'default') &&
137
- prop.default !== null && prop.default !== ''
140
+ // null and "" are defaults too: the extractor emits null for every
141
+ // std::nullopt, and those are exactly the sentinels the rule below is for.
142
+ const hasDefault = Object.prototype.hasOwnProperty.call(prop, 'default')
138
143
 
139
144
  declarations.push({
140
145
  surface: 'properties',
@@ -148,6 +153,7 @@ function mapExtractorJson (json, repo, options = {}) {
148
153
  meta: {
149
154
  has_default: hasDefault,
150
155
  default: hasDefault ? prop.default : null,
156
+ type: typeof prop.type === 'string' ? prop.type : null,
151
157
  is_enum: Boolean(prop.is_enum),
152
158
  nullable: Boolean(prop.nullable)
153
159
  }
@@ -184,16 +190,52 @@ const RULES = [
184
190
  }
185
191
  },
186
192
  {
187
- name: 'default-not-stated',
188
- description: 'Property has a default the description never mentions',
193
+ name: 'sentinel-default-unexplained',
194
+ description: 'Property defaults to a sentinel whose meaning the description never explains',
189
195
  severity: 'info',
196
+ // The rendered page already carries a "Default:" row built from the
197
+ // extractor's own `default` field, so asking the prose to restate a
198
+ // concrete default duplicates structured data -- and duplicated facts go
199
+ // stale. A real example: tombstone_retention_ms's description claimed "a
200
+ // typical default setting is 86400000" while the actual default was null.
201
+ //
202
+ // A SENTINEL default is the exception. null, 0, -1 and "" usually mean
203
+ // disabled, unlimited or unset, and which one is not inferable from the
204
+ // value, so "Default: null" leaves the reader guessing. That meaning is
205
+ // information the structured field cannot carry, which is the only case
206
+ // worth a finding.
190
207
  check: (decl) => {
191
- const text = decl.string || ''
208
+ const text = (decl.string || '').trim()
192
209
  if (!text || !decl.meta || !decl.meta.has_default) return []
193
- const defaultText = String(decl.meta.default)
194
- if (/\bdefaults?\b/i.test(text)) return []
195
- if (defaultText.length > 0 && text.includes(defaultText)) return []
196
- return [{ message: `Property has a default (${JSON.stringify(decl.meta.default)}) that the description does not state.` }]
210
+ const value = decl.meta.default
211
+ // Booleans are never sentinels: `false` is a real setting, not a
212
+ // stand-in for "off".
213
+ if (typeof value === 'boolean') return []
214
+ // A string-typed property defaulting to null or "" is plainly "not
215
+ // configured" (credentials, bucket names, endpoints); the "Default:" row
216
+ // says that on its own, so only non-string sentinels are ambiguous.
217
+ if (decl.meta.type === 'string' && (value === null || value === '')) return []
218
+ const sentinels = [
219
+ { match: (v) => v === null || v === 'null', token: /\bnull\b/i, label: 'null' },
220
+ { match: (v) => v === '' , token: /\b(empty|blank)\b/i, label: 'an empty string' },
221
+ { match: (v) => v === 0 || v === '0', token: /\b(0|zero)\b/i, label: '0' },
222
+ { match: (v) => v === -1 || v === '-1', token: /-1\b/, label: '-1' }
223
+ ]
224
+ const sentinel = sentinels.find((s) => s.match(value))
225
+ if (!sentinel) return []
226
+ // Either name the sentinel, or say what it does. Both forms explain it;
227
+ // neither is satisfied by an unrelated digit elsewhere in the sentence.
228
+ // "default"/"falls back"/"inherits"/"omitted" cover descriptions that
229
+ // explain the null case by naming what is used instead, which is the
230
+ // explanation being asked for.
231
+ const MEANING = /\b(disabl\w*|unlimited|no limit|unbounded|infinite|unset|not set|never|off|ignored|no maximum|no minimum|defaults?|inherit\w*|blank|omitted|not specified|falls? back)\b/i
232
+ if (sentinel.token.test(text) || MEANING.test(text)) return []
233
+ return [{
234
+ message: `Property defaults to ${sentinel.label}, and the description does not say what that means. `
235
+ + 'A sentinel default carries behavior the rendered "Default:" row cannot: say whether it disables '
236
+ + 'the property, means unlimited, or leaves it unset. There is no need to restate a concrete '
237
+ + 'default value; the page already renders it from structured data.'
238
+ }]
197
239
  }
198
240
  }
199
241
  ]