docusaurus-plugin-generate-schema-docs 1.1.1 → 1.3.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.
- package/README.md +60 -2
- package/__tests__/ExampleDataLayer.test.js +92 -0
- package/__tests__/__fixtures__/static/schemas/add-to-cart-event.json +44 -0
- package/__tests__/__fixtures__/static/schemas/choice-event.json +72 -0
- package/__tests__/__fixtures__/static/schemas/components/dataLayer.json +56 -0
- package/__tests__/__fixtures__/static/schemas/components/product.json +125 -0
- package/__tests__/__fixtures__/static/schemas/nested/child-event.json +10 -0
- package/__tests__/__fixtures__/static/schemas/nested/grandchild-a.json +9 -0
- package/__tests__/__fixtures__/static/schemas/nested/grandchild-b.json +9 -0
- package/__tests__/__fixtures__/static/schemas/nested/parent-event.json +7 -0
- package/__tests__/__fixtures__/static/schemas/root-any-of-event.json +34 -0
- package/__tests__/__fixtures__/static/schemas/root-choice-event.json +36 -0
- package/__tests__/__fixtures__/validateSchemas/circular-schema.json +8 -0
- package/__tests__/__fixtures__/validateSchemas/components/referenced.json +10 -0
- package/__tests__/__fixtures__/validateSchemas/invalid-example-schema.json +8 -0
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-missing-ref.json +8 -0
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-ref.json +8 -0
- package/__tests__/__fixtures__/validateSchemas/no-example-schema.json +12 -0
- package/__tests__/__fixtures__/validateSchemas/schema-A.json +7 -0
- package/__tests__/__fixtures__/validateSchemas/schema-B.json +7 -0
- package/__tests__/__fixtures__/validateSchemas/valid-schema.json +8 -0
- package/__tests__/__fixtures_versioned__/static/schemas/1.1.1/add-to-cart-event.json +44 -0
- package/__tests__/__fixtures_versioned__/static/schemas/1.1.1/components/dataLayer.json +56 -0
- package/__tests__/__fixtures_versioned__/static/schemas/1.1.1/components/product.json +125 -0
- package/__tests__/__fixtures_versioned__/static/schemas/next/add-to-cart-event.json +44 -0
- package/__tests__/__fixtures_versioned__/static/schemas/next/components/dataLayer.json +56 -0
- package/__tests__/__fixtures_versioned__/static/schemas/next/components/product.json +125 -0
- package/__tests__/__fixtures_versioned__/versions.json +1 -0
- package/__tests__/__snapshots__/ExampleDataLayer.test.js.snap +117 -0
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +92 -0
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +151 -0
- package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +53 -0
- package/__tests__/components/FoldableRows.test.js +330 -0
- package/__tests__/components/PropertiesTable.test.js +41 -0
- package/__tests__/components/PropertyRow.test.js +487 -0
- package/__tests__/components/SchemaJsonViewer.test.js +36 -0
- package/__tests__/components/SchemaRows.test.js +110 -0
- package/__tests__/components/SchemaViewer.test.js +44 -0
- package/__tests__/components/TableHeader.test.js +20 -0
- package/__tests__/generateEventDocs.nested.test.js +80 -0
- package/__tests__/generateEventDocs.test.js +90 -0
- package/__tests__/generateEventDocs.versioned.test.js +69 -0
- package/__tests__/helpers/buildExampleFromSchema.test.js +188 -0
- package/__tests__/helpers/file-system.test.js +44 -0
- package/__tests__/helpers/getConstraints.test.js +58 -0
- package/__tests__/helpers/loadSchema.test.js +20 -0
- package/__tests__/helpers/path-helpers.test.js +34 -0
- package/__tests__/helpers/processSchema.test.js +56 -0
- package/__tests__/helpers/schema-processing.test.js +82 -0
- package/__tests__/helpers/schemaToExamples.test.js +56 -0
- package/__tests__/helpers/schemaToTableData.filtering.test.js +65 -0
- package/__tests__/helpers/schemaToTableData.hierarchicalLines.test.js +539 -0
- package/__tests__/helpers/schemaToTableData.test.js +222 -0
- package/__tests__/helpers/update-schema-ids.test.js +107 -0
- package/__tests__/update-schema-ids.test.js +39 -0
- package/__tests__/validateSchemas.test.js +137 -0
- package/components/ExampleDataLayer.js +60 -27
- package/components/FoldableRows.js +164 -0
- package/components/PropertiesTable.js +12 -14
- package/components/PropertyRow.js +183 -0
- package/components/SchemaJsonViewer.js +8 -7
- package/components/SchemaRows.css +250 -0
- package/components/SchemaRows.js +24 -69
- package/components/SchemaViewer.js +21 -13
- package/components/TableHeader.js +15 -0
- package/generateEventDocs.js +141 -60
- package/helpers/buildExampleFromSchema.js +59 -73
- package/helpers/choice-index-template.js +22 -0
- package/helpers/file-system.js +32 -0
- package/helpers/getConstraints.js +52 -0
- package/helpers/loadSchema.js +11 -0
- package/helpers/path-helpers.js +22 -0
- package/helpers/processSchema.js +32 -0
- package/helpers/schema-doc-template.js +36 -0
- package/helpers/schema-processing.js +75 -0
- package/helpers/schemaToExamples.js +99 -0
- package/helpers/schemaToTableData.js +311 -0
- package/helpers/update-schema-ids.js +47 -0
- package/index.js +146 -47
- package/package.json +6 -3
- package/validateSchemas.js +56 -70
package/components/SchemaRows.js
CHANGED
|
@@ -1,75 +1,30 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import './SchemaRows.css';
|
|
3
|
+
import PropertyRow from './PropertyRow';
|
|
4
|
+
import FoldableRows from './FoldableRows';
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Renders the rows of the schema table from a flat `tableData` array.
|
|
8
|
+
* It maps over the array and renders the appropriate component for each row.
|
|
9
|
+
*/
|
|
10
|
+
export default function SchemaRows({ tableData }) {
|
|
11
|
+
if (!tableData) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
(prop.type === 'object' &&
|
|
12
|
-
prop.properties &&
|
|
13
|
-
Object.keys(prop.properties).length > 0) ||
|
|
14
|
-
(prop.type === 'array' &&
|
|
15
|
-
prop.items &&
|
|
16
|
-
prop.items.properties &&
|
|
17
|
-
Object.keys(prop.items.properties).length > 0);
|
|
15
|
+
return tableData.map((row) => {
|
|
16
|
+
const key = row.path.join('.');
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (row.type === 'choice') {
|
|
19
|
+
return <FoldableRows key={key} row={row} />;
|
|
20
|
+
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
if (row.type === 'property') {
|
|
23
|
+
return (
|
|
24
|
+
<PropertyRow key={key} row={row} isLastInGroup={row.isLastInGroup} />
|
|
25
|
+
);
|
|
26
|
+
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<tr style={{ backgroundColor: isReq ? 'rgba(255,0,0,0.05)' : 'transparent' }}>
|
|
31
|
-
<td>
|
|
32
|
-
<strong>{key}</strong>
|
|
33
|
-
{hasChildren && <span style={{ fontSize: '0.8em', marginLeft: '5px' }}>⤵</span>}
|
|
34
|
-
</td>
|
|
35
|
-
<td><code>{Array.isArray(prop.type) ? prop.type.join('|') : prop.type}</code></td>
|
|
36
|
-
<td style={{ textAlign: 'center' }}>{isReq ? '✅' : ''}</td>
|
|
37
|
-
<td>{prop.examples ? prop.examples.join(', ') : ''}</td>
|
|
38
|
-
<td>{prop.description || ''}</td>
|
|
39
|
-
</tr>
|
|
40
|
-
|
|
41
|
-
{/* Nested children rendered immediately after parent */}
|
|
42
|
-
{hasChildren && (
|
|
43
|
-
<tr>
|
|
44
|
-
<td colSpan="5" style={{ paddingLeft: '20px', borderLeft: '4px solid #eee' }}>
|
|
45
|
-
<strong>{prop.type === 'array' ? `${key} [ ]` : `${key} { }`}</strong>
|
|
46
|
-
<table style={{ width: '100%', marginTop: '5px' }}>
|
|
47
|
-
<thead>
|
|
48
|
-
<tr>
|
|
49
|
-
<th width="20%">Property</th>
|
|
50
|
-
<th width="15%">Type</th>
|
|
51
|
-
<th width="10%">Req</th>
|
|
52
|
-
<th width="15%">Examples</th>
|
|
53
|
-
<th>Description</th>
|
|
54
|
-
</tr>
|
|
55
|
-
</thead>
|
|
56
|
-
<tbody>
|
|
57
|
-
<SchemaRows
|
|
58
|
-
properties={prop.type === 'object' ? prop.properties : prop.items.properties}
|
|
59
|
-
requiredList={prop.type === 'object' ? prop.required || [] : prop.items.required || []}
|
|
60
|
-
level={level + 1}
|
|
61
|
-
/>
|
|
62
|
-
</tbody>
|
|
63
|
-
</table>
|
|
64
|
-
</td>
|
|
65
|
-
</tr>
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
</React.Fragment >
|
|
69
|
-
);
|
|
70
|
-
})}
|
|
71
|
-
</>
|
|
72
|
-
);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
export default SchemaRows;
|
|
28
|
+
return null;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -1,19 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Heading from '@theme/Heading';
|
|
1
3
|
import ExampleDataLayer from './ExampleDataLayer';
|
|
2
4
|
import PropertiesTable from './PropertiesTable';
|
|
3
5
|
|
|
4
|
-
// --- Main Exported Component ---
|
|
5
|
-
// Helper: Build an example payload from per-property `examples` or other hints
|
|
6
|
-
|
|
7
|
-
|
|
8
6
|
export default function SchemaViewer({ schema }) {
|
|
7
|
+
const hasOneOfAnyOf =
|
|
8
|
+
schema.oneOf ||
|
|
9
|
+
schema.anyOf ||
|
|
10
|
+
(schema.properties &&
|
|
11
|
+
Object.values(schema.properties).some(
|
|
12
|
+
(prop) => prop.oneOf || prop.anyOf,
|
|
13
|
+
));
|
|
14
|
+
const exampleTitle = hasOneOfAnyOf
|
|
15
|
+
? 'DataLayer Examples'
|
|
16
|
+
: 'DataLayer Example';
|
|
9
17
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
return (
|
|
19
|
+
<div>
|
|
20
|
+
<Heading as="h2">{exampleTitle}</Heading>
|
|
21
|
+
<ExampleDataLayer schema={schema} />
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
23
|
+
<Heading as="h2">Event Properties</Heading>
|
|
24
|
+
<PropertiesTable schema={schema} />
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
package/generateEventDocs.js
CHANGED
|
@@ -1,68 +1,149 @@
|
|
|
1
|
-
import $RefParser from "@apidevtools/json-schema-ref-parser";
|
|
2
|
-
import mergeJsonSchema from "json-schema-merge-allof";
|
|
3
|
-
import fs from 'fs';
|
|
4
1
|
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { getPathsForVersion } from './helpers/path-helpers.js';
|
|
4
|
+
import { readSchemas, writeDoc, createDir } from './helpers/file-system.js';
|
|
5
|
+
import { processOneOfSchema, slugify } from './helpers/schema-processing.js';
|
|
6
|
+
import SchemaDocTemplate from './helpers/schema-doc-template.js';
|
|
7
|
+
import ChoiceIndexTemplate from './helpers/choice-index-template.js';
|
|
8
|
+
import processSchema from './helpers/processSchema.js';
|
|
9
|
+
|
|
10
|
+
async function generateAndWriteDoc(
|
|
11
|
+
filePath,
|
|
12
|
+
schema,
|
|
13
|
+
eventName,
|
|
14
|
+
outputDir,
|
|
15
|
+
options,
|
|
16
|
+
alreadyMergedSchema = null,
|
|
17
|
+
) {
|
|
18
|
+
const { organizationName, projectName, siteDir } = options;
|
|
19
|
+
const baseEditUrl = `https://github.com/${organizationName}/${projectName}/edit/main`;
|
|
20
|
+
const PARTIALS_DIR = path.join(siteDir, 'docs/partials');
|
|
21
|
+
|
|
22
|
+
const mergedSchema = alreadyMergedSchema || (await processSchema(filePath));
|
|
23
|
+
|
|
24
|
+
// Check for partials
|
|
25
|
+
const topPartialPath = path.join(PARTIALS_DIR, `${eventName}.mdx`);
|
|
26
|
+
const bottomPartialPath = path.join(PARTIALS_DIR, `${eventName}_bottom.mdx`);
|
|
27
|
+
|
|
28
|
+
let topPartialImport = '';
|
|
29
|
+
let topPartialComponent = '';
|
|
30
|
+
if (fs.existsSync(topPartialPath)) {
|
|
31
|
+
topPartialImport = `import TopPartial from '@site/docs/partials/${eventName}.mdx';`;
|
|
32
|
+
topPartialComponent = '<TopPartial />';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let bottomPartialImport = '';
|
|
36
|
+
let bottomPartialComponent = '';
|
|
37
|
+
if (fs.existsSync(bottomPartialPath)) {
|
|
38
|
+
bottomPartialImport = `import BottomPartial from '@site/docs/partials/${eventName}_bottom.mdx';`;
|
|
39
|
+
bottomPartialComponent = '<BottomPartial />';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const editUrl = `${baseEditUrl}/${path.relative(
|
|
43
|
+
path.join(siteDir, '..'),
|
|
44
|
+
filePath,
|
|
45
|
+
)}`;
|
|
46
|
+
|
|
47
|
+
const mdxContent = SchemaDocTemplate({
|
|
48
|
+
schema,
|
|
49
|
+
mergedSchema,
|
|
50
|
+
editUrl,
|
|
51
|
+
file: path.basename(filePath),
|
|
52
|
+
topPartialImport,
|
|
53
|
+
bottomPartialImport,
|
|
54
|
+
topPartialComponent,
|
|
55
|
+
bottomPartialComponent,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const outputFilename = path.basename(filePath).replace('.json', '.mdx');
|
|
59
|
+
writeDoc(outputDir, outputFilename, mdxContent);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function generateOneOfDocs(
|
|
63
|
+
eventName,
|
|
64
|
+
schema,
|
|
65
|
+
filePath,
|
|
66
|
+
outputDir,
|
|
67
|
+
options,
|
|
68
|
+
) {
|
|
69
|
+
const eventOutputDir = path.join(outputDir, eventName);
|
|
70
|
+
createDir(eventOutputDir);
|
|
71
|
+
|
|
72
|
+
const processed = await processOneOfSchema(schema, filePath);
|
|
73
|
+
|
|
74
|
+
const indexPageContent = ChoiceIndexTemplate({
|
|
75
|
+
schema,
|
|
76
|
+
processedOptions: processed,
|
|
77
|
+
});
|
|
78
|
+
writeDoc(eventOutputDir, 'index.mdx', indexPageContent);
|
|
79
|
+
|
|
80
|
+
for (const [
|
|
81
|
+
index,
|
|
82
|
+
{ slug, schema: processedSchema },
|
|
83
|
+
] of processed.entries()) {
|
|
84
|
+
const subChoiceType = processedSchema.oneOf ? 'oneOf' : null;
|
|
85
|
+
const prefixedSlug = `${(index + 1).toString().padStart(2, '0')}-${slug}`;
|
|
86
|
+
|
|
87
|
+
if (subChoiceType) {
|
|
88
|
+
const tempFilePath = path.join(eventOutputDir, `${slug}.json`);
|
|
89
|
+
fs.writeFileSync(tempFilePath, JSON.stringify(processedSchema, null, 2));
|
|
90
|
+
await generateOneOfDocs(
|
|
91
|
+
prefixedSlug,
|
|
92
|
+
processedSchema,
|
|
93
|
+
tempFilePath,
|
|
94
|
+
eventOutputDir,
|
|
95
|
+
options,
|
|
96
|
+
);
|
|
97
|
+
fs.unlinkSync(tempFilePath);
|
|
98
|
+
} else {
|
|
99
|
+
const tempFilePath = path.join(eventOutputDir, `${prefixedSlug}.json`);
|
|
100
|
+
fs.writeFileSync(tempFilePath, JSON.stringify(processedSchema, null, 2));
|
|
101
|
+
await generateAndWriteDoc(
|
|
102
|
+
tempFilePath,
|
|
103
|
+
processedSchema,
|
|
104
|
+
slug,
|
|
105
|
+
eventOutputDir,
|
|
106
|
+
options,
|
|
107
|
+
processedSchema,
|
|
108
|
+
);
|
|
109
|
+
fs.unlinkSync(tempFilePath);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export default async function generateEventDocs(options) {
|
|
115
|
+
const { siteDir, version, url } = options || {};
|
|
116
|
+
const { schemaDir, outputDir } = getPathsForVersion(version, siteDir);
|
|
117
|
+
|
|
118
|
+
createDir(outputDir);
|
|
119
|
+
const schemas = readSchemas(schemaDir);
|
|
120
|
+
|
|
121
|
+
console.log(`🚀 Generating documentation for ${schemas.length} schemas...`);
|
|
5
122
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const SCHEMA_DIR = 'schemas'; // Where your JSON files are
|
|
9
|
-
const OUTPUT_DIR = 'docs/events'; // Where MDX goes
|
|
123
|
+
for (const { fileName, filePath, schema } of schemas) {
|
|
124
|
+
const eventName = fileName.replace('.json', '');
|
|
10
125
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
126
|
+
if (version) {
|
|
127
|
+
const baseUrl = url.endsWith('/') ? url.slice(0, -1) : url;
|
|
128
|
+
if (version !== 'current') {
|
|
129
|
+
schema.$id = `${baseUrl}/schemas/${version}/${fileName}`;
|
|
130
|
+
} else {
|
|
131
|
+
schema.$id = `${baseUrl}/schemas/next/${fileName}`;
|
|
132
|
+
}
|
|
15
133
|
}
|
|
16
134
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// First, dereference all $ref properties
|
|
29
|
-
const clonedSchema = await $RefParser.dereference(filePath, {
|
|
30
|
-
mutateInputSchema: false, dereference: {
|
|
31
|
-
circular: 'ignore'
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// Then merge allOf properties
|
|
36
|
-
const mergedSchema = mergeJsonSchema(clonedSchema, {
|
|
37
|
-
resolvers: {
|
|
38
|
-
defaultResolver: mergeJsonSchema.options.resolvers.title
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Define the MDX Content
|
|
43
|
-
// We embed the JSON directly into the file to avoid Webpack import issues
|
|
44
|
-
const mdxContent = `---
|
|
45
|
-
title: ${schema.title}
|
|
46
|
-
description: ${schema.description}
|
|
47
|
-
sidebar_label: ${schema.title}
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
import SchemaViewer from '@theme/SchemaViewer';
|
|
51
|
-
import SchemaJsonViewer from '@theme/SchemaJsonViewer';
|
|
52
|
-
|
|
53
|
-
# ${schema.title}
|
|
54
|
-
|
|
55
|
-
${schema.description}
|
|
56
|
-
|
|
57
|
-
<SchemaViewer schema={${JSON.stringify(mergedSchema)}} />
|
|
58
|
-
<SchemaJsonViewer schema={${JSON.stringify(schema)}} />
|
|
59
|
-
`;
|
|
60
|
-
|
|
61
|
-
// Write the .mdx file
|
|
62
|
-
const outputFilename = file.replace('.json', '.mdx');
|
|
63
|
-
fs.writeFileSync(path.join(OUTPUT_DIR, outputFilename), mdxContent);
|
|
64
|
-
console.log(`✅ Generated docs/events/${outputFilename}`);
|
|
135
|
+
if (schema.oneOf) {
|
|
136
|
+
await generateOneOfDocs(eventName, schema, filePath, outputDir, options);
|
|
137
|
+
} else {
|
|
138
|
+
await generateAndWriteDoc(
|
|
139
|
+
filePath,
|
|
140
|
+
schema,
|
|
141
|
+
eventName,
|
|
142
|
+
outputDir,
|
|
143
|
+
options,
|
|
144
|
+
);
|
|
65
145
|
}
|
|
146
|
+
}
|
|
66
147
|
|
|
67
|
-
|
|
148
|
+
console.log('🎉 Documentation generation complete!');
|
|
68
149
|
}
|
|
@@ -1,83 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively builds a single, default example object from a JSON schema.
|
|
3
|
+
* It prefers explicit examples, consts, or defaults. For choices (`oneOf`/`anyOf`),
|
|
4
|
+
* it defaults to the first option.
|
|
5
|
+
*
|
|
6
|
+
* @param {object} schema The schema to build an example from.
|
|
7
|
+
* @returns {object|undefined} The generated example.
|
|
8
|
+
*/
|
|
1
9
|
const buildExampleFromSchema = (schema) => {
|
|
2
|
-
|
|
3
|
-
if (!prop) return undefined;
|
|
10
|
+
if (!schema) return undefined;
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
// For choices, default to the first option.
|
|
13
|
+
if (schema.oneOf?.length > 0) {
|
|
14
|
+
return buildExampleFromSchema(schema.oneOf[0]);
|
|
15
|
+
}
|
|
16
|
+
if (schema.anyOf?.length > 0) {
|
|
17
|
+
return buildExampleFromSchema(schema.anyOf[0]);
|
|
18
|
+
}
|
|
9
19
|
|
|
10
|
-
|
|
20
|
+
// Prefer const, explicit examples, or default values.
|
|
21
|
+
if (typeof schema.const !== 'undefined') return schema.const;
|
|
22
|
+
if (schema.examples?.length > 0) return schema.examples[0];
|
|
23
|
+
if (typeof schema.default !== 'undefined') return schema.default;
|
|
11
24
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const obj = {};
|
|
17
|
-
Object.entries(prop.properties).forEach(([k, v]) => {
|
|
18
|
-
const val = buildValue(v);
|
|
19
|
-
// Only add the property if it has a valid value (not undefined)
|
|
20
|
-
if (val !== undefined)
|
|
21
|
-
{
|
|
22
|
-
obj[k] = val;
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
+
let type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
26
|
+
if (!type && schema.properties) {
|
|
27
|
+
type = 'object';
|
|
28
|
+
}
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
if (type === 'object') {
|
|
31
|
+
if (schema.properties) {
|
|
32
|
+
const obj = {};
|
|
33
|
+
Object.entries(schema.properties).forEach(([key, propSchema]) => {
|
|
34
|
+
const value = buildExampleFromSchema(propSchema);
|
|
35
|
+
if (typeof value !== 'undefined') {
|
|
36
|
+
obj[key] = value;
|
|
32
37
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (prop.items)
|
|
37
|
-
{
|
|
38
|
-
const itemVal = buildValue(prop.items);
|
|
39
|
-
// If the inner item is valid, return it as an array
|
|
40
|
-
if (itemVal !== undefined)
|
|
41
|
-
{
|
|
42
|
-
return [itemVal];
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
// Empty array or array of undefined items
|
|
46
|
-
return [];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
switch (type)
|
|
50
|
-
{
|
|
51
|
-
case 'string': return '';
|
|
52
|
-
case 'integer':
|
|
53
|
-
case 'number': return 0;
|
|
54
|
-
case 'boolean': return false;
|
|
55
|
-
default: return undefined;
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// If the schema provides a top-level examples array with an object, prefer it
|
|
60
|
-
if (schema && schema.examples && schema.examples.length)
|
|
61
|
-
{
|
|
62
|
-
const first = schema.examples[0];
|
|
63
|
-
if (typeof first === 'object' && first !== null) return first;
|
|
38
|
+
});
|
|
39
|
+
// Return undefined for objects that end up empty.
|
|
40
|
+
return Object.keys(obj).length > 0 ? obj : undefined;
|
|
64
41
|
}
|
|
42
|
+
// No properties defined, treat as an empty object (which we filter out).
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
65
45
|
|
|
66
|
-
|
|
67
|
-
if (schema
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const val = buildValue(p);
|
|
72
|
-
// Only add top-level keys if they are not undefined
|
|
73
|
-
if (val !== undefined)
|
|
74
|
-
{
|
|
75
|
-
out[k] = val;
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
return out;
|
|
46
|
+
if (type === 'array') {
|
|
47
|
+
if (schema.items) {
|
|
48
|
+
const itemValue = buildExampleFromSchema(schema.items);
|
|
49
|
+
// Only return an array if the item schema generates a value.
|
|
50
|
+
return typeof itemValue !== 'undefined' ? [itemValue] : undefined;
|
|
79
51
|
}
|
|
52
|
+
return undefined; // No items defined.
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Return placeholder values for primitive types if no other value is found.
|
|
56
|
+
switch (type) {
|
|
57
|
+
case 'string':
|
|
58
|
+
return '';
|
|
59
|
+
case 'integer':
|
|
60
|
+
case 'number':
|
|
61
|
+
return 0;
|
|
62
|
+
case 'boolean':
|
|
63
|
+
return false;
|
|
64
|
+
default:
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
80
68
|
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
export default buildExampleFromSchema;
|
|
69
|
+
export default buildExampleFromSchema;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export default function ChoiceIndexTemplate(data) {
|
|
2
|
+
const { schema, processedOptions } = data;
|
|
3
|
+
|
|
4
|
+
return `---
|
|
5
|
+
title: ${schema.title}
|
|
6
|
+
description: "${schema.description}"
|
|
7
|
+
---
|
|
8
|
+
import SchemaJsonViewer from '@theme/SchemaJsonViewer';
|
|
9
|
+
|
|
10
|
+
# ${schema.title}
|
|
11
|
+
|
|
12
|
+
${schema.description}
|
|
13
|
+
|
|
14
|
+
Please select one of the following options:
|
|
15
|
+
|
|
16
|
+
${processedOptions
|
|
17
|
+
.map((option) => `- [${option.schema.title}](./${option.slug})`)
|
|
18
|
+
.join('\n')}
|
|
19
|
+
|
|
20
|
+
<SchemaJsonViewer schema={${JSON.stringify(schema)}} />
|
|
21
|
+
`;
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import loadSchema from './loadSchema';
|
|
4
|
+
|
|
5
|
+
export function createDir(directory) {
|
|
6
|
+
if (!fs.existsSync(directory)) {
|
|
7
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function readSchemas(directory) {
|
|
12
|
+
const files = fs
|
|
13
|
+
.readdirSync(directory)
|
|
14
|
+
.filter((file) => file.endsWith('.json'));
|
|
15
|
+
|
|
16
|
+
return files.map((file) => {
|
|
17
|
+
const filePath = path.join(directory, file);
|
|
18
|
+
const schema = loadSchema(filePath);
|
|
19
|
+
return {
|
|
20
|
+
fileName: file,
|
|
21
|
+
filePath,
|
|
22
|
+
schema,
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function writeDoc(outputDir, fileName, content) {
|
|
28
|
+
fs.writeFileSync(path.join(outputDir, fileName), content);
|
|
29
|
+
console.log(
|
|
30
|
+
`✅ Generated ${path.relative(process.cwd(), path.join(outputDir, fileName))}`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// A list of JSON Schema keywords that have simple key-value constraints.
|
|
2
|
+
const constraintHandlers = {
|
|
3
|
+
// Simple key-value constraints
|
|
4
|
+
minLength: (val) => `minLength: ${val}`,
|
|
5
|
+
maxLength: (val) => `maxLength: ${val}`,
|
|
6
|
+
minimum: (val) => `minimum: ${val}`,
|
|
7
|
+
maximum: (val) => `maximum: ${val}`,
|
|
8
|
+
exclusiveMinimum: (val) => `exclusiveMinimum: ${val}`,
|
|
9
|
+
exclusiveMaximum: (val) => `exclusiveMaximum: ${val}`,
|
|
10
|
+
minItems: (val) => `minItems: ${val}`,
|
|
11
|
+
maxItems: (val) => `maxItems: ${val}`,
|
|
12
|
+
minProperties: (val) => `minProperties: ${val}`,
|
|
13
|
+
maxProperties: (val) => `maxProperties: ${val}`,
|
|
14
|
+
multipleOf: (val) => `multipleOf: ${val}`,
|
|
15
|
+
format: (val) => `format: ${val}`,
|
|
16
|
+
minContains: (val) => `minContains: ${val}`,
|
|
17
|
+
maxContains: (val) => `maxContains: ${val}`,
|
|
18
|
+
|
|
19
|
+
// Special-cased constraints
|
|
20
|
+
pattern: (val) => `pattern: /${val}/`,
|
|
21
|
+
uniqueItems: (val) => (val ? 'uniqueItems: true' : null),
|
|
22
|
+
additionalProperties: (val) =>
|
|
23
|
+
val === false ? 'additionalProperties: false' : null,
|
|
24
|
+
propertyNames: (val) => `propertyNames: ${JSON.stringify(val)}`,
|
|
25
|
+
dependentRequired: (val) => {
|
|
26
|
+
const deps = Object.entries(val)
|
|
27
|
+
.map(([key, depVal]) => `${key} -> [${depVal.join(', ')}]`)
|
|
28
|
+
.join('; ');
|
|
29
|
+
return `dependentRequired: ${deps}`;
|
|
30
|
+
},
|
|
31
|
+
contains: (val) => `contains: ${JSON.stringify(val)}`,
|
|
32
|
+
enum: (val) => `enum: [${val.join(', ')}]`,
|
|
33
|
+
const: (val) => `const: ${JSON.stringify(val)}`,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const getConstraints = (prop, isReq) => {
|
|
37
|
+
const constraints = [];
|
|
38
|
+
if (isReq) {
|
|
39
|
+
constraints.push('required');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const keyword in constraintHandlers) {
|
|
43
|
+
if (prop[keyword] !== undefined) {
|
|
44
|
+
const handler = constraintHandlers[keyword];
|
|
45
|
+
const result = handler(prop[keyword]);
|
|
46
|
+
if (result) {
|
|
47
|
+
constraints.push(result);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return constraints;
|
|
52
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Loads a JSON schema file and returns its content as a JSON object.
|
|
5
|
+
* @param {string} filePath Path to the JSON schema file.
|
|
6
|
+
* @returns {object} The parsed JSON schema.
|
|
7
|
+
*/
|
|
8
|
+
export default function loadSchema(filePath) {
|
|
9
|
+
const rawContent = fs.readFileSync(filePath, 'utf-8');
|
|
10
|
+
return JSON.parse(rawContent);
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
export function getPathsForVersion(version, siteDir) {
|
|
4
|
+
let schemaDir;
|
|
5
|
+
let outputDir;
|
|
6
|
+
|
|
7
|
+
if (version) {
|
|
8
|
+
if (version !== 'current') {
|
|
9
|
+
schemaDir = path.join(siteDir, 'static/schemas', version);
|
|
10
|
+
outputDir = path.join(siteDir, 'versioned_docs', `version-${version}`);
|
|
11
|
+
} else {
|
|
12
|
+
schemaDir = path.join(siteDir, 'static/schemas', 'next');
|
|
13
|
+
outputDir = path.join(siteDir, 'docs');
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
// Non-versioned
|
|
17
|
+
schemaDir = path.join(siteDir, 'static/schemas');
|
|
18
|
+
outputDir = path.join(siteDir, 'docs');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return { schemaDir, outputDir };
|
|
22
|
+
}
|