docusaurus-plugin-generate-schema-docs 1.8.4 → 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.
- package/README.md +10 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof-multi.json +12 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-anyof.json +30 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-edge-cases.json +24 -0
- package/__tests__/__fixtures__/validateSchemas/schema-with-not-non-object.json +15 -0
- package/__tests__/generateEventDocs.anchor.test.js +1 -1
- package/__tests__/generateEventDocs.nested.test.js +1 -1
- package/__tests__/generateEventDocs.partials.test.js +1 -1
- package/__tests__/generateEventDocs.test.js +506 -1
- package/__tests__/generateEventDocs.versioned.test.js +1 -1
- package/__tests__/helpers/buildExampleFromSchema.test.js +240 -0
- package/__tests__/helpers/constraintSchemaPaths.test.js +208 -0
- package/__tests__/helpers/continuingLinesStyle.test.js +492 -0
- package/__tests__/helpers/exampleModel.test.js +209 -0
- package/__tests__/helpers/file-system.test.js +73 -1
- package/__tests__/helpers/getConstraints.test.js +27 -0
- package/__tests__/helpers/mergeSchema.test.js +94 -0
- package/__tests__/helpers/processSchema.test.js +291 -1
- package/__tests__/helpers/schema-doc-template.test.js +54 -0
- package/__tests__/helpers/schema-processing.test.js +122 -2
- package/__tests__/helpers/schemaToExamples.test.js +1007 -0
- package/__tests__/helpers/schemaToTableData.mutations.test.js +970 -0
- package/__tests__/helpers/schemaToTableData.test.js +157 -0
- package/__tests__/helpers/snippetTargets.test.js +432 -0
- package/__tests__/helpers/trackingTargets.test.js +319 -0
- package/__tests__/helpers/validator.test.js +385 -1
- package/__tests__/index.test.js +436 -0
- package/__tests__/syncGtm.test.js +139 -3
- package/__tests__/update-schema-ids.test.js +70 -1
- package/__tests__/validateSchemas-integration.test.js +2 -2
- package/__tests__/validateSchemas.test.js +142 -1
- package/generateEventDocs.js +21 -1
- package/helpers/constraintSchemaPaths.js +10 -14
- package/helpers/schemaToTableData.js +538 -492
- package/helpers/trackingTargets.js +26 -3
- package/helpers/validator.js +18 -4
- package/index.js +1 -2
- package/package.json +1 -1
- package/scripts/sync-gtm.js +25 -7
package/README.md
CHANGED
|
@@ -203,6 +203,16 @@ If you have `event-reference/add-to-cart-event` and `mobile-reference/add-to-car
|
|
|
203
203
|
- `docs/partials/event-reference/_add-to-cart-event.mdx`
|
|
204
204
|
- `docs/partials/event-reference/_add-to-cart-event_bottom.mdx`
|
|
205
205
|
|
|
206
|
+
## Related packages
|
|
207
|
+
|
|
208
|
+
### [`eslint-plugin-tracking-schema`](https://www.npmjs.com/package/eslint-plugin-tracking-schema)
|
|
209
|
+
|
|
210
|
+
An ESLint plugin that enforces annotation quality on the JSON Schema files you feed into this plugin. It ensures every property has a `description`, a `type`, and `examples` — catching incomplete schemas at author time before they produce misleading documentation.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
npm install --save-dev eslint-plugin-tracking-schema jsonc-eslint-parser
|
|
214
|
+
```
|
|
215
|
+
|
|
206
216
|
## Contributing
|
|
207
217
|
|
|
208
218
|
Contributions are welcome! Please open an issue or submit a pull request if you have any ideas or improvements.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://example.com/schema-with-not-anyof-multi.json",
|
|
4
|
+
"title": "Schema With Not AnyOf Multi",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"country": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"not": { "anyOf": [{ "const": "US" }, { "const": "CA" }] }
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://example.com/schema-with-not-anyof.json",
|
|
4
|
+
"title": "Schema With Not AnyOf",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"country": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"not": { "anyOf": [{ "const": "US" }] }
|
|
10
|
+
},
|
|
11
|
+
"nested": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"code": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"not": { "anyOf": [{ "anyOf": [{ "const": "XX" }] }] }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"items": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"items": [
|
|
23
|
+
{
|
|
24
|
+
"type": "string",
|
|
25
|
+
"not": { "anyOf": [{ "const": "forbidden" }] }
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://example.com/schema-with-not-edge-cases.json",
|
|
4
|
+
"title": "Schema With Not Edge Cases",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"falsy_not": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"not": false
|
|
10
|
+
},
|
|
11
|
+
"not_is_array": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"not": [{ "const": "A" }, { "const": "B" }]
|
|
14
|
+
},
|
|
15
|
+
"not_anyof_contains_array": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"not": { "anyOf": [[1, 2, 3]] }
|
|
18
|
+
},
|
|
19
|
+
"not_with_null_candidate": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"not": { "anyOf": [null] }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://example.com/schema-with-not-non-object.json",
|
|
4
|
+
"title": "Schema With Not Non Object",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"flag": {
|
|
8
|
+
"not": true
|
|
9
|
+
},
|
|
10
|
+
"name": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"not": { "const": "forbidden" }
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -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 generateEventDocs from '../generateEventDocs';
|
|
@@ -98,3 +98,508 @@ describe('generateEventDocs (non-versioned)', () => {
|
|
|
98
98
|
);
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
|
+
|
|
102
|
+
describe('generateEventDocs (edge cases)', () => {
|
|
103
|
+
beforeEach(() => {
|
|
104
|
+
fs.vol.reset();
|
|
105
|
+
console.log = jest.fn();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('handles being called with no arguments', async () => {
|
|
109
|
+
// Covers L284: options || {}
|
|
110
|
+
// getPathsForVersion(undefined, undefined) will produce a path that won't exist
|
|
111
|
+
await expect(generateEventDocs()).rejects.toThrow();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('handles being called with null options', async () => {
|
|
115
|
+
// Covers L284: options || {}
|
|
116
|
+
await expect(generateEventDocs(null)).rejects.toThrow();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('sets $id with non-trailing-slash URL for versioned schemas', async () => {
|
|
120
|
+
// Covers L298: url without trailing slash (no slice needed)
|
|
121
|
+
const realFs = jest.requireActual('fs');
|
|
122
|
+
const versionedFixturesDir = path.resolve(
|
|
123
|
+
__dirname,
|
|
124
|
+
'__fixtures_versioned__',
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
function readDirRecursive(dir) {
|
|
128
|
+
const entries = realFs.readdirSync(dir, { withFileTypes: true });
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
const entryPath = path.join(dir, entry.name);
|
|
131
|
+
if (entry.isDirectory()) {
|
|
132
|
+
fs.vol.mkdirSync(entryPath, { recursive: true });
|
|
133
|
+
readDirRecursive(entryPath);
|
|
134
|
+
} else {
|
|
135
|
+
fs.vol.writeFileSync(entryPath, realFs.readFileSync(entryPath));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
readDirRecursive(versionedFixturesDir);
|
|
140
|
+
|
|
141
|
+
const versionedOptions = {
|
|
142
|
+
organizationName: 'test-org',
|
|
143
|
+
projectName: 'test-project',
|
|
144
|
+
siteDir: versionedFixturesDir,
|
|
145
|
+
url: 'https://tracking-docs-demo.buchert.digital',
|
|
146
|
+
version: '1.1.1',
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
await generateEventDocs(versionedOptions);
|
|
150
|
+
|
|
151
|
+
const versionedOutputDir = path.join(
|
|
152
|
+
versionedFixturesDir,
|
|
153
|
+
'versioned_docs',
|
|
154
|
+
'version-1.1.1',
|
|
155
|
+
);
|
|
156
|
+
const content = fs.readFileSync(
|
|
157
|
+
path.join(versionedOutputDir, 'add-to-cart-event.mdx'),
|
|
158
|
+
'utf-8',
|
|
159
|
+
);
|
|
160
|
+
// URL had no trailing slash, so no slice applied; $id should still be set correctly
|
|
161
|
+
expect(content).toContain(
|
|
162
|
+
'https://tracking-docs-demo.buchert.digital/schemas/1.1.1/add-to-cart-event.json',
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('sets $id with next for current version', async () => {
|
|
167
|
+
const realFs = jest.requireActual('fs');
|
|
168
|
+
const versionedFixturesDir = path.resolve(
|
|
169
|
+
__dirname,
|
|
170
|
+
'__fixtures_versioned__',
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
function readDirRecursive(dir) {
|
|
174
|
+
const entries = realFs.readdirSync(dir, { withFileTypes: true });
|
|
175
|
+
for (const entry of entries) {
|
|
176
|
+
const entryPath = path.join(dir, entry.name);
|
|
177
|
+
if (entry.isDirectory()) {
|
|
178
|
+
fs.vol.mkdirSync(entryPath, { recursive: true });
|
|
179
|
+
readDirRecursive(entryPath);
|
|
180
|
+
} else {
|
|
181
|
+
fs.vol.writeFileSync(entryPath, realFs.readFileSync(entryPath));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
readDirRecursive(versionedFixturesDir);
|
|
186
|
+
|
|
187
|
+
const currentOptions = {
|
|
188
|
+
organizationName: 'test-org',
|
|
189
|
+
projectName: 'test-project',
|
|
190
|
+
siteDir: versionedFixturesDir,
|
|
191
|
+
url: 'https://tracking-docs-demo.buchert.digital',
|
|
192
|
+
version: 'current',
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
await generateEventDocs(currentOptions);
|
|
196
|
+
|
|
197
|
+
const currentOutputDir = path.join(versionedFixturesDir, 'docs');
|
|
198
|
+
const content = fs.readFileSync(
|
|
199
|
+
path.join(currentOutputDir, 'add-to-cart-event.mdx'),
|
|
200
|
+
'utf-8',
|
|
201
|
+
);
|
|
202
|
+
expect(content).toContain(
|
|
203
|
+
'https://tracking-docs-demo.buchert.digital/schemas/next/add-to-cart-event.json',
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('handles schemas with $ref pointing outside schema directory', async () => {
|
|
208
|
+
// Covers L72 (walkSchema with currentPath not in allSchemaSources)
|
|
209
|
+
// The component references a file outside the schema dir via ../
|
|
210
|
+
// which resolves to a key not present in schemaSources
|
|
211
|
+
const testSiteDir = path.resolve(__dirname, '__fixtures_outside_ref__');
|
|
212
|
+
const schemaDir = path.join(testSiteDir, 'static/schemas');
|
|
213
|
+
const componentsDir = path.join(schemaDir, 'components');
|
|
214
|
+
const outsideDir = path.join(testSiteDir, 'static');
|
|
215
|
+
fs.vol.mkdirSync(componentsDir, { recursive: true });
|
|
216
|
+
|
|
217
|
+
// A shared schema placed outside the schemas directory
|
|
218
|
+
const sharedSchema = {
|
|
219
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
220
|
+
$id: 'https://example.com/shared.json',
|
|
221
|
+
title: 'Shared',
|
|
222
|
+
type: 'object',
|
|
223
|
+
properties: {
|
|
224
|
+
shared_prop: { type: 'string' },
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
fs.vol.writeFileSync(
|
|
228
|
+
path.join(outsideDir, 'shared.json'),
|
|
229
|
+
JSON.stringify(sharedSchema, null, 2),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// A component that references the shared schema outside the schema dir
|
|
233
|
+
const componentSchema = {
|
|
234
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
235
|
+
$id: 'https://example.com/schemas/components/widget.json',
|
|
236
|
+
title: 'Widget',
|
|
237
|
+
type: 'object',
|
|
238
|
+
properties: {
|
|
239
|
+
name: { type: 'string' },
|
|
240
|
+
base: { $ref: '../../shared.json' },
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
fs.vol.writeFileSync(
|
|
244
|
+
path.join(componentsDir, 'widget.json'),
|
|
245
|
+
JSON.stringify(componentSchema, null, 2),
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
// Main schema referencing the component
|
|
249
|
+
const mainSchema = {
|
|
250
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
251
|
+
$id: 'https://example.com/schemas/outside-ref.json',
|
|
252
|
+
title: 'Outside Ref Event',
|
|
253
|
+
type: 'object',
|
|
254
|
+
properties: {
|
|
255
|
+
event: { type: 'string', const: 'outside_ref' },
|
|
256
|
+
widget: { $ref: './components/widget.json' },
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
fs.vol.writeFileSync(
|
|
260
|
+
path.join(schemaDir, 'outside-ref.json'),
|
|
261
|
+
JSON.stringify(mainSchema, null, 2),
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const outsideRefOptions = {
|
|
265
|
+
organizationName: 'test-org',
|
|
266
|
+
projectName: 'test-project',
|
|
267
|
+
siteDir: testSiteDir,
|
|
268
|
+
url: 'https://example.com',
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
await generateEventDocs(outsideRefOptions);
|
|
272
|
+
|
|
273
|
+
const outputDir = path.join(testSiteDir, 'docs');
|
|
274
|
+
expect(fs.existsSync(path.join(outputDir, 'outside-ref.mdx'))).toBe(true);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('handles schemas with circular $ref between source files', async () => {
|
|
278
|
+
// Covers L72 (walkSchema with visited path - circular reference)
|
|
279
|
+
const testSiteDir = path.resolve(__dirname, '__fixtures_circular_ref__');
|
|
280
|
+
const schemaDir = path.join(testSiteDir, 'static/schemas');
|
|
281
|
+
const componentsDir = path.join(schemaDir, 'components');
|
|
282
|
+
fs.vol.mkdirSync(componentsDir, { recursive: true });
|
|
283
|
+
|
|
284
|
+
const mainSchema = {
|
|
285
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
286
|
+
$id: 'https://example.com/schemas/circular.json',
|
|
287
|
+
title: 'Circular Ref Event',
|
|
288
|
+
type: 'object',
|
|
289
|
+
properties: {
|
|
290
|
+
event: { type: 'string', const: 'circular' },
|
|
291
|
+
node: { $ref: './components/node.json' },
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const nodeSchema = {
|
|
296
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
297
|
+
$id: 'https://example.com/schemas/components/node.json',
|
|
298
|
+
title: 'Node',
|
|
299
|
+
type: 'object',
|
|
300
|
+
properties: {
|
|
301
|
+
value: { type: 'string' },
|
|
302
|
+
parent: { $ref: '../circular.json' },
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
fs.vol.writeFileSync(
|
|
307
|
+
path.join(schemaDir, 'circular.json'),
|
|
308
|
+
JSON.stringify(mainSchema, null, 2),
|
|
309
|
+
);
|
|
310
|
+
fs.vol.writeFileSync(
|
|
311
|
+
path.join(componentsDir, 'node.json'),
|
|
312
|
+
JSON.stringify(nodeSchema, null, 2),
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
const circularOptions = {
|
|
316
|
+
organizationName: 'test-org',
|
|
317
|
+
projectName: 'test-project',
|
|
318
|
+
siteDir: testSiteDir,
|
|
319
|
+
url: 'https://example.com',
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
await generateEventDocs(circularOptions);
|
|
323
|
+
|
|
324
|
+
const outputDir = path.join(testSiteDir, 'docs');
|
|
325
|
+
expect(fs.existsSync(path.join(outputDir, 'circular.mdx'))).toBe(true);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('covers collectLeafEventNames for non-oneOf schemas via nested oneOf', async () => {
|
|
329
|
+
// Covers L104-106: collectLeafEventNames with schema that has no oneOf
|
|
330
|
+
// This requires a nested oneOf where processOneOfSchema returns a schema
|
|
331
|
+
// that itself has oneOf, so collectLeafEventNames recurses
|
|
332
|
+
const testSiteDir = path.resolve(__dirname, '__fixtures_nested_leaf__');
|
|
333
|
+
const schemaDir = path.join(testSiteDir, 'static/schemas');
|
|
334
|
+
fs.vol.mkdirSync(schemaDir, { recursive: true });
|
|
335
|
+
|
|
336
|
+
const realFs = jest.requireActual('fs');
|
|
337
|
+
|
|
338
|
+
// Copy the nested fixture files
|
|
339
|
+
const nestedSchemasDir = path.resolve(
|
|
340
|
+
__dirname,
|
|
341
|
+
'__fixtures__/static/schemas/nested',
|
|
342
|
+
);
|
|
343
|
+
const files = realFs.readdirSync(nestedSchemasDir);
|
|
344
|
+
for (const file of files) {
|
|
345
|
+
const content = realFs.readFileSync(path.join(nestedSchemasDir, file));
|
|
346
|
+
fs.vol.writeFileSync(path.join(schemaDir, file), content);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const nestedLeafOptions = {
|
|
350
|
+
organizationName: 'test-org',
|
|
351
|
+
projectName: 'test-project',
|
|
352
|
+
siteDir: testSiteDir,
|
|
353
|
+
url: 'https://example.com',
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
await generateEventDocs(nestedLeafOptions);
|
|
357
|
+
|
|
358
|
+
const outputDir = path.join(testSiteDir, 'docs');
|
|
359
|
+
const parentDir = path.join(outputDir, 'parent-event');
|
|
360
|
+
expect(fs.existsSync(parentDir)).toBe(true);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('uses fallback partial for oneOf sub-option when scoped partial does not exist', async () => {
|
|
364
|
+
// Covers L34 (cond-expr): fallback partial path is selected when
|
|
365
|
+
// the scoped partial does not exist but the basename fallback does
|
|
366
|
+
const testSiteDir = path.resolve(
|
|
367
|
+
__dirname,
|
|
368
|
+
'__fixtures_fallback_partial__',
|
|
369
|
+
);
|
|
370
|
+
const schemaDir = path.join(testSiteDir, 'static/schemas');
|
|
371
|
+
const outputDir = path.join(testSiteDir, 'docs');
|
|
372
|
+
const partialsDir = path.join(outputDir, 'partials');
|
|
373
|
+
fs.vol.mkdirSync(schemaDir, { recursive: true });
|
|
374
|
+
fs.vol.mkdirSync(partialsDir, { recursive: true });
|
|
375
|
+
|
|
376
|
+
const schema = {
|
|
377
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
378
|
+
$id: 'https://example.com/schemas/parent-fb.json',
|
|
379
|
+
title: 'Parent FB',
|
|
380
|
+
oneOf: [
|
|
381
|
+
{
|
|
382
|
+
title: 'Child Alpha',
|
|
383
|
+
type: 'object',
|
|
384
|
+
properties: {
|
|
385
|
+
event: { type: 'string', const: 'child_alpha' },
|
|
386
|
+
},
|
|
387
|
+
required: ['event'],
|
|
388
|
+
},
|
|
389
|
+
],
|
|
390
|
+
};
|
|
391
|
+
fs.vol.writeFileSync(
|
|
392
|
+
path.join(schemaDir, 'parent-fb.json'),
|
|
393
|
+
JSON.stringify(schema, null, 2),
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
// Create a fallback partial in base partials dir (not in the scoped subdir)
|
|
397
|
+
// The scoped path would be partials/parent-fb/_child-alpha.mdx which does not exist
|
|
398
|
+
// The fallback path is partials/_child-alpha.mdx which we create here
|
|
399
|
+
fs.vol.writeFileSync(
|
|
400
|
+
path.join(partialsDir, '_child-alpha.mdx'),
|
|
401
|
+
'## Fallback partial content',
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
const fbOptions = {
|
|
405
|
+
organizationName: 'test-org',
|
|
406
|
+
projectName: 'test-project',
|
|
407
|
+
siteDir: testSiteDir,
|
|
408
|
+
url: 'https://example.com',
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
await generateEventDocs(fbOptions);
|
|
412
|
+
|
|
413
|
+
const choiceOutput = fs.readFileSync(
|
|
414
|
+
path.join(outputDir, 'parent-fb', '01-child-alpha.mdx'),
|
|
415
|
+
'utf-8',
|
|
416
|
+
);
|
|
417
|
+
// The fallback partial should be used since the scoped one doesn't exist
|
|
418
|
+
expect(choiceOutput).toContain(
|
|
419
|
+
"import TopPartial from '@site/docs/partials/_child-alpha.mdx'",
|
|
420
|
+
);
|
|
421
|
+
expect(choiceOutput).toContain('<TopPartial />');
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('removes stale oneOf output files and directories between runs', async () => {
|
|
425
|
+
const testSiteDir = path.resolve(
|
|
426
|
+
__dirname,
|
|
427
|
+
'__fixtures_stale_oneof_cleanup__',
|
|
428
|
+
);
|
|
429
|
+
const schemaDir = path.join(testSiteDir, 'static/schemas');
|
|
430
|
+
const docsDir = path.join(testSiteDir, 'docs');
|
|
431
|
+
fs.vol.mkdirSync(schemaDir, { recursive: true });
|
|
432
|
+
|
|
433
|
+
const schemaFile = path.join(schemaDir, 'stale-choice.json');
|
|
434
|
+
const cleanupOptions = {
|
|
435
|
+
organizationName: 'test-org',
|
|
436
|
+
projectName: 'test-project',
|
|
437
|
+
siteDir: testSiteDir,
|
|
438
|
+
url: 'https://example.com',
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const initialSchema = {
|
|
442
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
443
|
+
$id: 'https://example.com/schemas/stale-choice.json',
|
|
444
|
+
title: 'Stale Choice',
|
|
445
|
+
oneOf: [
|
|
446
|
+
{
|
|
447
|
+
title: 'Leaf A',
|
|
448
|
+
type: 'object',
|
|
449
|
+
properties: {
|
|
450
|
+
event: { type: 'string', const: 'leaf_a' },
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
title: 'Group B',
|
|
455
|
+
oneOf: [
|
|
456
|
+
{
|
|
457
|
+
title: 'Leaf B',
|
|
458
|
+
type: 'object',
|
|
459
|
+
properties: {
|
|
460
|
+
event: { type: 'string', const: 'leaf_b' },
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
fs.vol.writeFileSync(schemaFile, JSON.stringify(initialSchema, null, 2));
|
|
469
|
+
await generateEventDocs(cleanupOptions);
|
|
470
|
+
|
|
471
|
+
expect(
|
|
472
|
+
fs.existsSync(path.join(docsDir, 'stale-choice', '01-leaf-a.mdx')),
|
|
473
|
+
).toBe(true);
|
|
474
|
+
expect(
|
|
475
|
+
fs.existsSync(path.join(docsDir, 'stale-choice', '02-group-b')),
|
|
476
|
+
).toBe(true);
|
|
477
|
+
|
|
478
|
+
const updatedSchema = {
|
|
479
|
+
...initialSchema,
|
|
480
|
+
oneOf: [initialSchema.oneOf[0]],
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
fs.vol.writeFileSync(schemaFile, JSON.stringify(updatedSchema, null, 2));
|
|
484
|
+
|
|
485
|
+
const unlinkSpy = jest.spyOn(fs, 'unlinkSync');
|
|
486
|
+
const rmSpy = jest.spyOn(fs, 'rmSync');
|
|
487
|
+
|
|
488
|
+
await generateEventDocs(cleanupOptions);
|
|
489
|
+
|
|
490
|
+
expect(
|
|
491
|
+
fs.existsSync(path.join(docsDir, 'stale-choice', '01-leaf-a.mdx')),
|
|
492
|
+
).toBe(true);
|
|
493
|
+
expect(
|
|
494
|
+
fs.existsSync(path.join(docsDir, 'stale-choice', '02-group-b')),
|
|
495
|
+
).toBe(false);
|
|
496
|
+
expect(unlinkSpy).not.toHaveBeenCalledWith(
|
|
497
|
+
path.join(docsDir, 'stale-choice', '01-leaf-a.mdx'),
|
|
498
|
+
);
|
|
499
|
+
expect(rmSpy).toHaveBeenCalledWith(
|
|
500
|
+
path.join(docsDir, 'stale-choice', '02-group-b'),
|
|
501
|
+
{ recursive: true },
|
|
502
|
+
);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it('generates docs for inline nested oneOf schemas', async () => {
|
|
506
|
+
// Covers L259 (sourceFilePath || filePath fallback in generateOneOfDocs)
|
|
507
|
+
// and L114 (sourceFilePath || filePath fallback in collectLeafEventNames)
|
|
508
|
+
// and L105-106 (collectLeafEventNames for non-oneOf leaf via recursion)
|
|
509
|
+
const testSiteDir = path.resolve(
|
|
510
|
+
__dirname,
|
|
511
|
+
'__fixtures_inline_nested_oneof__',
|
|
512
|
+
);
|
|
513
|
+
const schemaDir = path.join(testSiteDir, 'static/schemas');
|
|
514
|
+
fs.vol.mkdirSync(schemaDir, { recursive: true });
|
|
515
|
+
|
|
516
|
+
// A schema with inline nested oneOf (no $ref, so sourceFilePath is null)
|
|
517
|
+
const schema = {
|
|
518
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
519
|
+
$id: 'https://example.com/schemas/inline-nested.json',
|
|
520
|
+
title: 'Inline Nested',
|
|
521
|
+
oneOf: [
|
|
522
|
+
{
|
|
523
|
+
title: 'Group One',
|
|
524
|
+
oneOf: [
|
|
525
|
+
{
|
|
526
|
+
title: 'Leaf A',
|
|
527
|
+
type: 'object',
|
|
528
|
+
properties: {
|
|
529
|
+
event: { type: 'string', const: 'leaf_a' },
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
title: 'Leaf B',
|
|
534
|
+
type: 'object',
|
|
535
|
+
properties: {
|
|
536
|
+
event: { type: 'string', const: 'leaf_b' },
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
],
|
|
540
|
+
},
|
|
541
|
+
],
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
fs.vol.writeFileSync(
|
|
545
|
+
path.join(schemaDir, 'inline-nested.json'),
|
|
546
|
+
JSON.stringify(schema, null, 2),
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
const inlineOptions = {
|
|
550
|
+
organizationName: 'test-org',
|
|
551
|
+
projectName: 'test-project',
|
|
552
|
+
siteDir: testSiteDir,
|
|
553
|
+
url: 'https://example.com',
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
await generateEventDocs(inlineOptions);
|
|
557
|
+
|
|
558
|
+
const outputDir = path.join(testSiteDir, 'docs');
|
|
559
|
+
const parentDir = path.join(outputDir, 'inline-nested');
|
|
560
|
+
expect(fs.existsSync(parentDir)).toBe(true);
|
|
561
|
+
|
|
562
|
+
// The nested oneOf should create a subdirectory with index and leaf docs
|
|
563
|
+
const nestedDir = path.join(parentDir, '01-group-one');
|
|
564
|
+
expect(fs.existsSync(nestedDir)).toBe(true);
|
|
565
|
+
expect(fs.existsSync(path.join(nestedDir, 'index.mdx'))).toBe(true);
|
|
566
|
+
expect(fs.existsSync(path.join(nestedDir, '01-leaf-a.mdx'))).toBe(true);
|
|
567
|
+
expect(fs.existsSync(path.join(nestedDir, '02-leaf-b.mdx'))).toBe(true);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('handles collectReachableSchemaSources with empty sourcePath', async () => {
|
|
571
|
+
// Covers L66: collectReachableSchemaSources early return when sourcePath is not in schemaSources
|
|
572
|
+
// We create a schema with only local $ref (#) so the sourceKey for the
|
|
573
|
+
// oneOf sub-option maps to a synthetic filename not present in schemaSources
|
|
574
|
+
const testSiteDir = path.resolve(__dirname, '__fixtures_empty_source__');
|
|
575
|
+
const schemaDir = path.join(testSiteDir, 'static/schemas');
|
|
576
|
+
fs.vol.mkdirSync(schemaDir, { recursive: true });
|
|
577
|
+
|
|
578
|
+
// A minimal schema with no external refs
|
|
579
|
+
const schema = {
|
|
580
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
581
|
+
$id: 'https://example.com/schemas/simple.json',
|
|
582
|
+
title: 'Simple Event',
|
|
583
|
+
type: 'object',
|
|
584
|
+
properties: {
|
|
585
|
+
event: { type: 'string', const: 'simple' },
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
fs.vol.writeFileSync(
|
|
589
|
+
path.join(schemaDir, 'simple.json'),
|
|
590
|
+
JSON.stringify(schema, null, 2),
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
const simpleOptions = {
|
|
594
|
+
organizationName: 'test-org',
|
|
595
|
+
projectName: 'test-project',
|
|
596
|
+
siteDir: testSiteDir,
|
|
597
|
+
url: 'https://example.com',
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
await generateEventDocs(simpleOptions);
|
|
601
|
+
|
|
602
|
+
const outputDir = path.join(testSiteDir, 'docs');
|
|
603
|
+
expect(fs.existsSync(path.join(outputDir, 'simple.mdx'))).toBe(true);
|
|
604
|
+
});
|
|
605
|
+
});
|