docusaurus-plugin-generate-schema-docs 1.8.3 → 1.8.5

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 (62) hide show
  1. package/README.md +12 -0
  2. package/__tests__/__fixtures__/validateSchemas/main-schema-with-not-allof.json +11 -0
  3. package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof-multi.json +12 -0
  4. package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof.json +30 -0
  5. package/__tests__/__fixtures__/validateSchemas/schema-with-not-edge-cases.json +24 -0
  6. package/__tests__/__fixtures__/validateSchemas/schema-with-not-non-object.json +15 -0
  7. package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +6 -0
  8. package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +6 -0
  9. package/__tests__/__snapshots__/generateEventDocs.test.js.snap +15 -0
  10. package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +6 -0
  11. package/__tests__/components/PropertiesTable.test.js +66 -0
  12. package/__tests__/components/PropertyRow.test.js +85 -4
  13. package/__tests__/components/SchemaJsonViewer.test.js +118 -0
  14. package/__tests__/generateEventDocs.anchor.test.js +1 -1
  15. package/__tests__/generateEventDocs.nested.test.js +1 -1
  16. package/__tests__/generateEventDocs.partials.test.js +1 -1
  17. package/__tests__/generateEventDocs.test.js +506 -1
  18. package/__tests__/generateEventDocs.versioned.test.js +1 -1
  19. package/__tests__/helpers/buildExampleFromSchema.test.js +240 -0
  20. package/__tests__/helpers/constraintSchemaPaths.test.js +208 -0
  21. package/__tests__/helpers/continuingLinesStyle.test.js +492 -0
  22. package/__tests__/helpers/example-helper.test.js +12 -0
  23. package/__tests__/helpers/exampleModel.test.js +209 -0
  24. package/__tests__/helpers/file-system.test.js +73 -1
  25. package/__tests__/helpers/getConstraints.test.js +43 -0
  26. package/__tests__/helpers/mergeSchema.test.js +94 -0
  27. package/__tests__/helpers/processSchema.test.js +309 -1
  28. package/__tests__/helpers/schema-doc-template.test.js +54 -0
  29. package/__tests__/helpers/schema-processing.test.js +122 -2
  30. package/__tests__/helpers/schemaToExamples.test.js +1007 -0
  31. package/__tests__/helpers/schemaToTableData.mutations.test.js +970 -0
  32. package/__tests__/helpers/schemaToTableData.test.js +157 -0
  33. package/__tests__/helpers/schemaTraversal.test.js +110 -0
  34. package/__tests__/helpers/snippetTargets.test.js +432 -0
  35. package/__tests__/helpers/trackingTargets.test.js +319 -0
  36. package/__tests__/helpers/validator.test.js +385 -1
  37. package/__tests__/index.test.js +436 -0
  38. package/__tests__/syncGtm.test.js +366 -6
  39. package/__tests__/update-schema-ids.test.js +70 -1
  40. package/__tests__/validateSchemas-integration.test.js +2 -2
  41. package/__tests__/validateSchemas.test.js +192 -1
  42. package/components/PropertiesTable.js +32 -2
  43. package/components/PropertyRow.js +29 -2
  44. package/components/SchemaJsonViewer.js +234 -131
  45. package/components/SchemaRows.css +40 -0
  46. package/components/SchemaViewer.js +11 -2
  47. package/generateEventDocs.js +21 -1
  48. package/helpers/constraintSchemaPaths.js +10 -14
  49. package/helpers/example-helper.js +2 -2
  50. package/helpers/getConstraints.js +20 -0
  51. package/helpers/processSchema.js +32 -1
  52. package/helpers/schema-doc-template.js +4 -0
  53. package/helpers/schemaToExamples.js +29 -35
  54. package/helpers/schemaToTableData.js +538 -492
  55. package/helpers/schemaTraversal.cjs +148 -0
  56. package/helpers/trackingTargets.js +26 -3
  57. package/helpers/validator.js +18 -4
  58. package/index.js +1 -2
  59. package/package.json +1 -1
  60. package/scripts/sync-gtm.js +65 -34
  61. package/test-data/payloadContracts.js +35 -0
  62. package/validateSchemas.js +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @jest-environment node
2
+ * @jest-environment @stryker-mutator/jest-runner/jest-env/node
3
3
  */
4
4
 
5
5
  import path from 'path';
@@ -102,4 +102,195 @@ describe('validateSchemas', () => {
102
102
  expect(consoleErrorSpy).toHaveBeenCalled();
103
103
  expect(consoleErrorSpy.mock.calls[0][0]).toContain('x-tracking-targets');
104
104
  });
105
+
106
+ it('should treat falsy scalar examples as valid examples', async () => {
107
+ const schemaDir = path.join(tmpDir, 'schemas');
108
+ fs.mkdirSync(schemaDir, { recursive: true });
109
+ fs.writeFileSync(
110
+ path.join(schemaDir, 'event.json'),
111
+ JSON.stringify({
112
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
113
+ type: 'object',
114
+ properties: {
115
+ event: {
116
+ type: 'string',
117
+ const: 'test_event',
118
+ },
119
+ is_enabled: {
120
+ type: 'boolean',
121
+ example: false,
122
+ },
123
+ retry_count: {
124
+ type: 'integer',
125
+ example: 0,
126
+ },
127
+ note: {
128
+ type: 'string',
129
+ example: '',
130
+ },
131
+ },
132
+ required: ['event', 'is_enabled', 'retry_count', 'note'],
133
+ }),
134
+ );
135
+
136
+ const result = await validateSchemas(schemaDir);
137
+ expect(result).toBe(true);
138
+ });
139
+
140
+ it('should allow a top-level falsy example value', async () => {
141
+ const schemaDir = path.join(tmpDir, 'schemas');
142
+ fs.mkdirSync(schemaDir, { recursive: true });
143
+ fs.writeFileSync(
144
+ path.join(schemaDir, 'flag.json'),
145
+ JSON.stringify({
146
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
147
+ type: 'boolean',
148
+ example: false,
149
+ }),
150
+ );
151
+
152
+ const result = await validateSchemas(schemaDir);
153
+ expect(result).toBe(true);
154
+ });
155
+
156
+ it('should return false and log errors when a schema produces no examples', async () => {
157
+ const fixturePath = path.resolve(
158
+ __dirname,
159
+ '__fixtures__',
160
+ 'validateSchemas',
161
+ 'no-example-schema.json',
162
+ );
163
+ const schemaDir = path.join(tmpDir, 'schemas');
164
+ fs.mkdirSync(schemaDir, { recursive: true });
165
+ fs.copyFileSync(
166
+ fixturePath,
167
+ path.join(schemaDir, 'no-example-schema.json'),
168
+ );
169
+
170
+ const result = await validateSchemas(schemaDir);
171
+ expect(result).toBe(false);
172
+ expect(consoleErrorSpy).toHaveBeenCalled();
173
+ const errorMessages = consoleErrorSpy.mock.calls
174
+ .map((c) => c[0])
175
+ .join('\n');
176
+ expect(errorMessages).toContain('does not produce any examples');
177
+ });
178
+
179
+ it('should return false and log error when processing a schema throws', async () => {
180
+ const fixturePath = path.resolve(
181
+ __dirname,
182
+ '__fixtures__',
183
+ 'validateSchemas',
184
+ 'main-schema-with-missing-ref.json',
185
+ );
186
+ const schemaDir = path.join(tmpDir, 'schemas');
187
+ fs.mkdirSync(schemaDir, { recursive: true });
188
+ fs.copyFileSync(
189
+ fixturePath,
190
+ path.join(schemaDir, 'main-schema-with-missing-ref.json'),
191
+ );
192
+
193
+ const result = await validateSchemas(schemaDir);
194
+ expect(result).toBe(false);
195
+ expect(consoleErrorSpy).toHaveBeenCalled();
196
+ const errorMessages = consoleErrorSpy.mock.calls
197
+ .map((c) => c[0])
198
+ .join('\n');
199
+ expect(errorMessages).toContain('Error processing');
200
+ });
201
+
202
+ it('should report errors for all failing schemas when multiple schemas exist', async () => {
203
+ const schemaDir = path.join(tmpDir, 'schemas');
204
+ fs.mkdirSync(schemaDir, { recursive: true });
205
+
206
+ // First schema: valid
207
+ fs.writeFileSync(
208
+ path.join(schemaDir, 'valid.json'),
209
+ JSON.stringify({
210
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
211
+ type: 'object',
212
+ properties: { event: { type: 'string', const: 'ok' } },
213
+ required: ['event'],
214
+ }),
215
+ );
216
+
217
+ // Second schema: no examples produced
218
+ fs.writeFileSync(
219
+ path.join(schemaDir, 'no-examples.json'),
220
+ JSON.stringify({
221
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
222
+ type: 'object',
223
+ properties: { untyped: { description: 'no type or example' } },
224
+ required: ['untyped'],
225
+ }),
226
+ );
227
+
228
+ const result = await validateSchemas(schemaDir);
229
+ expect(result).toBe(false);
230
+ expect(consoleErrorSpy).toHaveBeenCalled();
231
+ });
232
+
233
+ it('should return false when a oneOf option produces an undefined example', async () => {
234
+ const schemaDir = path.join(tmpDir, 'schemas');
235
+ fs.mkdirSync(schemaDir, { recursive: true });
236
+
237
+ // Schema with a oneOf where one option has no resolvable example
238
+ fs.writeFileSync(
239
+ path.join(schemaDir, 'undef-option.json'),
240
+ JSON.stringify({
241
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
242
+ type: 'object',
243
+ oneOf: [
244
+ {
245
+ title: 'ValidOption',
246
+ properties: { event: { type: 'string', const: 'test' } },
247
+ required: ['event'],
248
+ },
249
+ {
250
+ title: 'UndefinedOption',
251
+ properties: { mystery: { description: 'no type, no example' } },
252
+ required: ['mystery'],
253
+ },
254
+ ],
255
+ }),
256
+ );
257
+
258
+ const result = await validateSchemas(schemaDir);
259
+ expect(result).toBe(false);
260
+ expect(consoleErrorSpy).toHaveBeenCalled();
261
+ const errorMessages = consoleErrorSpy.mock.calls
262
+ .map((c) => c[0])
263
+ .join('\n');
264
+ expect(errorMessages).toContain('does not produce a valid example');
265
+ });
266
+
267
+ it('should report all examples invalid when every example fails validation', async () => {
268
+ const schemaDir = path.join(tmpDir, 'schemas');
269
+ fs.mkdirSync(schemaDir, { recursive: true });
270
+
271
+ // Schema where every oneOf option produces an example that fails schema validation
272
+ fs.writeFileSync(
273
+ path.join(schemaDir, 'all-invalid.json'),
274
+ JSON.stringify({
275
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
276
+ type: 'object',
277
+ properties: {
278
+ count: {
279
+ type: 'integer',
280
+ minimum: 100,
281
+ example: 1,
282
+ },
283
+ },
284
+ required: ['count'],
285
+ }),
286
+ );
287
+
288
+ const result = await validateSchemas(schemaDir);
289
+ expect(result).toBe(false);
290
+ expect(consoleErrorSpy).toHaveBeenCalled();
291
+ const errorMessages = consoleErrorSpy.mock.calls
292
+ .map((c) => c[0])
293
+ .join('\n');
294
+ expect(errorMessages).toContain('example data failed validation');
295
+ });
105
296
  });
@@ -5,9 +5,39 @@ import WordWrapButton from './WordWrapButton';
5
5
  import { schemaToTableData } from '../helpers/schemaToTableData';
6
6
  import styles from './PropertiesTable.module.css';
7
7
 
8
- export default function PropertiesTable({ schema }) {
8
+ function filterInheritedTopLevelProperties(schema, sourceSchema) {
9
+ if (!schema?.properties || !sourceSchema?.properties) {
10
+ return schema;
11
+ }
12
+
13
+ const sourceKeys = new Set(Object.keys(sourceSchema.properties));
14
+ const filteredEntries = Object.entries(schema.properties).filter(
15
+ ([key, propSchema]) => {
16
+ if (sourceKeys.has(key)) {
17
+ return true;
18
+ }
19
+
20
+ const hasDescription =
21
+ typeof propSchema?.description === 'string' &&
22
+ propSchema.description.trim().length > 0;
23
+ const hasExamples =
24
+ Array.isArray(propSchema?.examples) && propSchema.examples.length > 0;
25
+ const hasExample = propSchema?.example !== undefined;
26
+
27
+ return hasDescription || hasExamples || hasExample;
28
+ },
29
+ );
30
+
31
+ return {
32
+ ...schema,
33
+ properties: Object.fromEntries(filteredEntries),
34
+ };
35
+ }
36
+
37
+ export default function PropertiesTable({ schema, sourceSchema }) {
9
38
  const [isWordWrapOn, setIsWordWrapOn] = useState(true);
10
- const tableData = schemaToTableData(schema);
39
+ const tableSchema = filterInheritedTopLevelProperties(schema, sourceSchema);
40
+ const tableData = schemaToTableData(tableSchema);
11
41
  const stripeState = { current: 0 };
12
42
 
13
43
  return (
@@ -26,6 +26,19 @@ const getContainerSymbol = (containerType) => {
26
26
  return '';
27
27
  };
28
28
 
29
+ const formatPropertyType = (value) => {
30
+ if (Array.isArray(value)) {
31
+ return value.join(', ');
32
+ }
33
+ if (typeof value === 'string') {
34
+ return value;
35
+ }
36
+ if (value === undefined || value === null) {
37
+ return '';
38
+ }
39
+ return JSON.stringify(value);
40
+ };
41
+
29
42
  const KEYWORD_HELP_TEXT = {
30
43
  additionalProperties:
31
44
  'Controls properties not listed in properties and not matched by patternProperties.',
@@ -33,6 +46,8 @@ const KEYWORD_HELP_TEXT = {
33
46
  'Applies the subschema to property names that match the given regular expression.',
34
47
  };
35
48
 
49
+ const SCHEMA_KEYWORD_BADGE_TEXT = 'Schema constraint';
50
+
36
51
  function splitKeywordLabel(name) {
37
52
  const match = /^patternProperties (\/.+\/)$/.exec(name);
38
53
  if (!match) {
@@ -45,6 +60,15 @@ function splitKeywordLabel(name) {
45
60
  };
46
61
  }
47
62
 
63
+ function buildKeywordHelpId(name, rowPath) {
64
+ if (!rowPath || rowPath.length === 0) {
65
+ return `schema-keyword-help-${name}`;
66
+ }
67
+
68
+ const normalizedPath = rowPath.join('-').replace(/[^a-zA-Z0-9_-]/g, '_');
69
+ return `schema-keyword-help-${normalizedPath}`;
70
+ }
71
+
48
72
  /**
49
73
  * Renders a single property row in the schema table.
50
74
  * All data is passed in via the `row` prop, which comes from `tableData`.
@@ -145,7 +169,7 @@ export default function PropertyRow({
145
169
  : name;
146
170
  const keywordHelpText = KEYWORD_HELP_TEXT[keywordHelpKey];
147
171
  const keywordHelpId = keywordHelpText
148
- ? `schema-keyword-help-${name}`
172
+ ? buildKeywordHelpId(name, row.path)
149
173
  : undefined;
150
174
  const zebraClassName =
151
175
  stripeIndex === undefined
@@ -208,6 +232,9 @@ export default function PropertyRow({
208
232
  {name}
209
233
  </code>
210
234
  )}
235
+ <span className="property-keyword-badge">
236
+ {SCHEMA_KEYWORD_BADGE_TEXT}
237
+ </span>
211
238
  <span
212
239
  id={keywordHelpId}
213
240
  className="property-keyword-tooltip"
@@ -222,7 +249,7 @@ export default function PropertyRow({
222
249
  </span>
223
250
  </td>
224
251
  <td rowSpan={rowSpan}>
225
- <code>{propertyType}</code>
252
+ <code>{formatPropertyType(propertyType)}</code>
226
253
  </td>
227
254
 
228
255
  {/* The first constraint cell */}