docusaurus-plugin-generate-schema-docs 1.8.2 → 1.8.4
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 +2 -0
- package/__tests__/__fixtures__/validateSchemas/main-schema-with-not-allof.json +11 -0
- package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +21 -3
- package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +26 -4
- package/__tests__/__snapshots__/generateEventDocs.test.js.snap +45 -6
- package/__tests__/__snapshots__/generateEventDocs.versioned.test.js.snap +16 -2
- package/__tests__/components/ConditionalRows.test.js +28 -0
- package/__tests__/components/FoldableRows.test.js +31 -290
- package/__tests__/components/PropertiesTable.test.js +66 -0
- package/__tests__/components/PropertyRow.test.js +297 -0
- package/__tests__/components/SchemaJsonViewer.test.js +194 -10
- package/__tests__/components/SchemaRows.test.js +62 -12
- package/__tests__/components/__snapshots__/ConnectorLines.visualRegression.test.js.snap +3 -3
- package/__tests__/generateEventDocs.test.js +3 -0
- package/__tests__/helpers/example-helper.test.js +12 -0
- package/__tests__/helpers/getConstraints.test.js +16 -0
- package/__tests__/helpers/processSchema.test.js +18 -0
- package/__tests__/helpers/schemaToTableData.test.js +112 -0
- package/__tests__/helpers/schemaTraversal.test.js +110 -0
- package/__tests__/syncGtm.test.js +227 -3
- package/__tests__/validateSchemas.test.js +50 -0
- package/components/ConditionalRows.js +6 -3
- package/components/FoldableRows.js +9 -3
- package/components/PropertiesTable.js +34 -3
- package/components/PropertyRow.js +118 -6
- package/components/SchemaJsonViewer.js +324 -4
- package/components/SchemaRows.css +138 -7
- package/components/SchemaRows.js +11 -1
- package/components/SchemaViewer.js +11 -2
- package/generateEventDocs.js +87 -1
- package/helpers/choice-index-template.js +6 -2
- package/helpers/example-helper.js +2 -2
- package/helpers/file-system.js +28 -0
- package/helpers/getConstraints.js +20 -0
- package/helpers/processSchema.js +32 -1
- package/helpers/schema-doc-template.js +11 -1
- package/helpers/schemaToExamples.js +29 -35
- package/helpers/schemaToTableData.js +68 -7
- package/helpers/schemaTraversal.cjs +148 -0
- package/package.json +1 -1
- package/scripts/sync-gtm.js +41 -28
- package/test-data/payloadContracts.js +35 -0
- package/validateSchemas.js +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.required
|
|
1
|
+
.property-cell--required {
|
|
2
2
|
background-color: rgba(var(--ifm-color-danger-rgb), 0.05);
|
|
3
3
|
}
|
|
4
4
|
|
|
@@ -26,6 +26,49 @@
|
|
|
26
26
|
margin-bottom: 1em;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
.schema-json-viewer__controls {
|
|
30
|
+
margin: 0.5rem 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.schema-json-viewer__link {
|
|
34
|
+
appearance: none;
|
|
35
|
+
background: none;
|
|
36
|
+
border: none;
|
|
37
|
+
color: var(--ifm-link-color);
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
font: inherit;
|
|
40
|
+
padding: 0;
|
|
41
|
+
text-decoration: none;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.schema-json-viewer__link:hover {
|
|
45
|
+
color: var(--ifm-link-hover-color);
|
|
46
|
+
text-decoration: underline;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.schema-json-viewer__ref-link {
|
|
50
|
+
text-decoration: underline;
|
|
51
|
+
text-decoration-thickness: 1.5px;
|
|
52
|
+
text-underline-offset: 0.14em;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.schema-json-viewer__ref-link:hover {
|
|
56
|
+
color: var(--ifm-link-hover-color) !important;
|
|
57
|
+
text-decoration-thickness: 2px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.schema-json-viewer__keyword {
|
|
61
|
+
font-weight: 600;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.schema-json-viewer__keyword--meta {
|
|
65
|
+
color: var(--ifm-color-info-light);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.schema-json-viewer__keyword--structural {
|
|
69
|
+
color: var(--ifm-color-success-light);
|
|
70
|
+
}
|
|
71
|
+
|
|
29
72
|
/* --- Property Name and Container Symbol Styles --- */
|
|
30
73
|
|
|
31
74
|
.property-name {
|
|
@@ -34,6 +77,78 @@
|
|
|
34
77
|
gap: 4px;
|
|
35
78
|
}
|
|
36
79
|
|
|
80
|
+
.property-name--keyword {
|
|
81
|
+
align-items: center;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.property-keyword-wrapper {
|
|
85
|
+
position: relative;
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
flex-wrap: wrap;
|
|
89
|
+
gap: 0.35rem;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.property-keyword-stack {
|
|
93
|
+
display: inline-flex;
|
|
94
|
+
flex-direction: column;
|
|
95
|
+
align-items: flex-start;
|
|
96
|
+
gap: 0.2rem;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.property-keyword {
|
|
100
|
+
font-size: 0.95em;
|
|
101
|
+
font-weight: 600;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.property-keyword-badge {
|
|
105
|
+
display: inline-flex;
|
|
106
|
+
align-items: center;
|
|
107
|
+
padding: 0.1rem 0.4rem;
|
|
108
|
+
border-radius: 999px;
|
|
109
|
+
border: 1px solid var(--ifm-table-border-color);
|
|
110
|
+
background-color: var(--ifm-color-emphasis-100);
|
|
111
|
+
color: var(--ifm-color-emphasis-800);
|
|
112
|
+
font-size: 0.68rem;
|
|
113
|
+
font-weight: 700;
|
|
114
|
+
letter-spacing: 0.02em;
|
|
115
|
+
text-transform: uppercase;
|
|
116
|
+
white-space: nowrap;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.property-keyword-pattern {
|
|
120
|
+
font-size: 0.85em;
|
|
121
|
+
color: var(--ifm-color-emphasis-700);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.property-keyword-tooltip {
|
|
125
|
+
position: absolute;
|
|
126
|
+
left: 0;
|
|
127
|
+
bottom: calc(100% + 0.4rem);
|
|
128
|
+
z-index: 3;
|
|
129
|
+
min-width: 14rem;
|
|
130
|
+
max-width: 18rem;
|
|
131
|
+
padding: 0.5rem 0.625rem;
|
|
132
|
+
border-radius: 0.375rem;
|
|
133
|
+
background: var(--ifm-background-surface-color);
|
|
134
|
+
border: 1px solid var(--ifm-table-border-color);
|
|
135
|
+
box-shadow: var(--ifm-global-shadow-lw);
|
|
136
|
+
color: var(--ifm-font-color-base);
|
|
137
|
+
font-size: 0.8rem;
|
|
138
|
+
line-height: 1.35;
|
|
139
|
+
opacity: 0;
|
|
140
|
+
pointer-events: none;
|
|
141
|
+
transform: translateY(0.125rem);
|
|
142
|
+
transition:
|
|
143
|
+
opacity 120ms ease,
|
|
144
|
+
transform 120ms ease;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.property-keyword-wrapper:hover .property-keyword-tooltip {
|
|
148
|
+
opacity: 1;
|
|
149
|
+
transform: translateY(0);
|
|
150
|
+
}
|
|
151
|
+
|
|
37
152
|
.container-symbol {
|
|
38
153
|
font-family: var(--ifm-font-family-monospace);
|
|
39
154
|
font-size: 0.9em;
|
|
@@ -67,6 +182,10 @@
|
|
|
67
182
|
border-left: none;
|
|
68
183
|
}
|
|
69
184
|
|
|
185
|
+
.schema-table td[colspan='5'] {
|
|
186
|
+
border-left: none;
|
|
187
|
+
}
|
|
188
|
+
|
|
70
189
|
/*
|
|
71
190
|
* Constraint-only continuation rows render a single cell; in those rows that
|
|
72
191
|
* cell is also :first-child, but it still needs the separator before the
|
|
@@ -85,6 +204,18 @@
|
|
|
85
204
|
box-shadow: inset 0 1px 0 var(--ifm-table-border-color);
|
|
86
205
|
}
|
|
87
206
|
|
|
207
|
+
.schema-table tbody tr.schema-row--zebra-even {
|
|
208
|
+
background-color: var(--ifm-table-stripe-background);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.schema-table tbody tr.schema-row--zebra-odd {
|
|
212
|
+
background-color: transparent;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.schema-table tbody tr.schema-row--control {
|
|
216
|
+
background-color: transparent;
|
|
217
|
+
}
|
|
218
|
+
|
|
88
219
|
/* --- Organigram Connector Line Styles --- */
|
|
89
220
|
|
|
90
221
|
/*
|
|
@@ -108,10 +239,12 @@ td.has-children {
|
|
|
108
239
|
td[class*='level-']::before {
|
|
109
240
|
content: '';
|
|
110
241
|
position: absolute;
|
|
111
|
-
top:
|
|
112
|
-
bottom:
|
|
242
|
+
top: -1px;
|
|
243
|
+
bottom: -1px;
|
|
113
244
|
width: 0;
|
|
114
245
|
border-left: 1px solid var(--ifm-table-border-color);
|
|
246
|
+
z-index: 1;
|
|
247
|
+
pointer-events: none;
|
|
115
248
|
}
|
|
116
249
|
|
|
117
250
|
/* Last items: stop vertical line at middle */
|
|
@@ -130,6 +263,8 @@ td[class*='level-']::after {
|
|
|
130
263
|
height: 0;
|
|
131
264
|
width: 0.75rem;
|
|
132
265
|
border-bottom: 1px solid var(--ifm-table-border-color);
|
|
266
|
+
z-index: 1;
|
|
267
|
+
pointer-events: none;
|
|
133
268
|
}
|
|
134
269
|
|
|
135
270
|
/* --- Level-based positioning --- */
|
|
@@ -172,10 +307,6 @@ td.level-6::after {
|
|
|
172
307
|
|
|
173
308
|
/* --- Choice Row Styles --- */
|
|
174
309
|
|
|
175
|
-
.choice-row {
|
|
176
|
-
background-color: var(--ifm-table-stripe-background);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
310
|
.choice-row:hover {
|
|
180
311
|
background-color: var(--ifm-hover-overlay);
|
|
181
312
|
}
|
package/components/SchemaRows.js
CHANGED
|
@@ -12,7 +12,11 @@ import ConditionalRows from './ConditionalRows';
|
|
|
12
12
|
* @param {Array} props.tableData - Flat array of row objects
|
|
13
13
|
* @param {Array} [props.bracketEnds] - Bracket descriptors that end on the last row
|
|
14
14
|
*/
|
|
15
|
-
export default function SchemaRows({
|
|
15
|
+
export default function SchemaRows({
|
|
16
|
+
tableData,
|
|
17
|
+
bracketEnds,
|
|
18
|
+
stripeState = { current: 0 },
|
|
19
|
+
}) {
|
|
16
20
|
if (!tableData) {
|
|
17
21
|
return null;
|
|
18
22
|
}
|
|
@@ -20,12 +24,15 @@ export default function SchemaRows({ tableData, bracketEnds }) {
|
|
|
20
24
|
return tableData.map((row, index) => {
|
|
21
25
|
const key = row.path.join('.');
|
|
22
26
|
const isLast = index === tableData.length - 1;
|
|
27
|
+
const stripeIndex = stripeState.current++;
|
|
23
28
|
|
|
24
29
|
if (row.type === 'choice') {
|
|
25
30
|
return (
|
|
26
31
|
<FoldableRows
|
|
27
32
|
key={key}
|
|
28
33
|
row={row}
|
|
34
|
+
stripeIndex={stripeIndex}
|
|
35
|
+
stripeState={stripeState}
|
|
29
36
|
bracketEnds={isLast ? bracketEnds : undefined}
|
|
30
37
|
/>
|
|
31
38
|
);
|
|
@@ -36,6 +43,8 @@ export default function SchemaRows({ tableData, bracketEnds }) {
|
|
|
36
43
|
<ConditionalRows
|
|
37
44
|
key={key}
|
|
38
45
|
row={row}
|
|
46
|
+
stripeIndex={stripeIndex}
|
|
47
|
+
stripeState={stripeState}
|
|
39
48
|
bracketEnds={isLast ? bracketEnds : undefined}
|
|
40
49
|
/>
|
|
41
50
|
);
|
|
@@ -46,6 +55,7 @@ export default function SchemaRows({ tableData, bracketEnds }) {
|
|
|
46
55
|
<PropertyRow
|
|
47
56
|
key={key}
|
|
48
57
|
row={row}
|
|
58
|
+
stripeIndex={stripeIndex}
|
|
49
59
|
isLastInGroup={row.isLastInGroup}
|
|
50
60
|
bracketEnds={isLast ? bracketEnds : undefined}
|
|
51
61
|
/>
|
|
@@ -3,7 +3,16 @@ import Heading from '@theme/Heading';
|
|
|
3
3
|
import ExampleDataLayer from './ExampleDataLayer';
|
|
4
4
|
import PropertiesTable from './PropertiesTable';
|
|
5
5
|
|
|
6
|
-
export default function SchemaViewer({
|
|
6
|
+
export default function SchemaViewer({
|
|
7
|
+
schema,
|
|
8
|
+
sourceSchema,
|
|
9
|
+
sourcePath,
|
|
10
|
+
schemaSources,
|
|
11
|
+
dataLayerName,
|
|
12
|
+
}) {
|
|
13
|
+
const resolvedSourceSchema =
|
|
14
|
+
(sourcePath && schemaSources?.[sourcePath]) || sourceSchema || schema;
|
|
15
|
+
|
|
7
16
|
const hasOneOfAnyOf =
|
|
8
17
|
schema.oneOf ||
|
|
9
18
|
schema.anyOf ||
|
|
@@ -21,7 +30,7 @@ export default function SchemaViewer({ schema, dataLayerName }) {
|
|
|
21
30
|
<ExampleDataLayer schema={schema} dataLayerName={dataLayerName} />
|
|
22
31
|
|
|
23
32
|
<Heading as="h2">Event Properties</Heading>
|
|
24
|
-
<PropertiesTable schema={schema} />
|
|
33
|
+
<PropertiesTable schema={schema} sourceSchema={resolvedSourceSchema} />
|
|
25
34
|
</div>
|
|
26
35
|
);
|
|
27
36
|
}
|
package/generateEventDocs.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import { getPathsForVersion } from './helpers/path-helpers.js';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
readSchemas,
|
|
6
|
+
readSchemaSources,
|
|
7
|
+
writeDoc,
|
|
8
|
+
createDir,
|
|
9
|
+
} from './helpers/file-system.js';
|
|
5
10
|
import { processOneOfSchema, slugify } from './helpers/schema-processing.js';
|
|
6
11
|
import SchemaDocTemplate from './helpers/schema-doc-template.js';
|
|
7
12
|
import ChoiceIndexTemplate from './helpers/choice-index-template.js';
|
|
@@ -37,6 +42,64 @@ function resolvePartial({
|
|
|
37
42
|
};
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
function isPlainObject(value) {
|
|
46
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function toSchemaSourceKey(schemaDir, filePath) {
|
|
50
|
+
return path.relative(schemaDir, filePath).split(path.sep).join('/');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveLocalSchemaRef(currentPath, ref) {
|
|
54
|
+
if (!ref || ref.startsWith('#') || /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(ref)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const [fileRef] = ref.split('#');
|
|
59
|
+
return path
|
|
60
|
+
.normalize(path.join(path.dirname(currentPath), fileRef))
|
|
61
|
+
.split(path.sep)
|
|
62
|
+
.join('/');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function collectReachableSchemaSources(sourcePath, allSchemaSources) {
|
|
66
|
+
if (!sourcePath || !allSchemaSources[sourcePath]) return {};
|
|
67
|
+
|
|
68
|
+
const visited = new Set();
|
|
69
|
+
const reachableSources = {};
|
|
70
|
+
|
|
71
|
+
function walkSchema(currentPath) {
|
|
72
|
+
if (visited.has(currentPath) || !allSchemaSources[currentPath]) return;
|
|
73
|
+
visited.add(currentPath);
|
|
74
|
+
|
|
75
|
+
const schema = allSchemaSources[currentPath];
|
|
76
|
+
reachableSources[currentPath] = schema;
|
|
77
|
+
|
|
78
|
+
function walkNode(node) {
|
|
79
|
+
if (Array.isArray(node)) {
|
|
80
|
+
node.forEach(walkNode);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!isPlainObject(node)) return;
|
|
85
|
+
|
|
86
|
+
if (typeof node.$ref === 'string') {
|
|
87
|
+
const resolvedRefPath = resolveLocalSchemaRef(currentPath, node.$ref);
|
|
88
|
+
if (resolvedRefPath) {
|
|
89
|
+
walkSchema(resolvedRefPath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Object.values(node).forEach(walkNode);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
walkNode(schema);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
walkSchema(sourcePath);
|
|
100
|
+
return reachableSources;
|
|
101
|
+
}
|
|
102
|
+
|
|
40
103
|
async function collectLeafEventNames(schema, filePath, eventNames) {
|
|
41
104
|
if (!schema.oneOf) {
|
|
42
105
|
eventNames.push(path.basename(filePath, '.json'));
|
|
@@ -86,6 +149,8 @@ async function generateAndWriteDoc(
|
|
|
86
149
|
alreadyMergedSchema = null,
|
|
87
150
|
editFilePath = null,
|
|
88
151
|
partialNameConflicts = new Set(),
|
|
152
|
+
schemaSources = {},
|
|
153
|
+
schemaDir,
|
|
89
154
|
) {
|
|
90
155
|
const { organizationName, projectName, siteDir, dataLayerName, version } =
|
|
91
156
|
options;
|
|
@@ -134,6 +199,11 @@ async function generateAndWriteDoc(
|
|
|
134
199
|
topPartialComponent: top.component,
|
|
135
200
|
bottomPartialComponent: bottom.component,
|
|
136
201
|
dataLayerName,
|
|
202
|
+
sourcePath: toSchemaSourceKey(schemaDir, editFilePath || filePath),
|
|
203
|
+
schemaSources: collectReachableSchemaSources(
|
|
204
|
+
toSchemaSourceKey(schemaDir, editFilePath || filePath),
|
|
205
|
+
schemaSources,
|
|
206
|
+
),
|
|
137
207
|
});
|
|
138
208
|
|
|
139
209
|
const outputFilename = path.basename(filePath).replace('.json', '.mdx');
|
|
@@ -147,6 +217,8 @@ async function generateOneOfDocs(
|
|
|
147
217
|
outputDir,
|
|
148
218
|
options,
|
|
149
219
|
partialNameConflicts,
|
|
220
|
+
schemaSources,
|
|
221
|
+
schemaDir,
|
|
150
222
|
) {
|
|
151
223
|
const { organizationName, projectName, siteDir } = options;
|
|
152
224
|
const editUrl = buildEditUrl(
|
|
@@ -165,6 +237,11 @@ async function generateOneOfDocs(
|
|
|
165
237
|
schema,
|
|
166
238
|
processedOptions: processed,
|
|
167
239
|
editUrl,
|
|
240
|
+
sourcePath: toSchemaSourceKey(schemaDir, filePath),
|
|
241
|
+
schemaSources: collectReachableSchemaSources(
|
|
242
|
+
toSchemaSourceKey(schemaDir, filePath),
|
|
243
|
+
schemaSources,
|
|
244
|
+
),
|
|
168
245
|
});
|
|
169
246
|
writeDoc(eventOutputDir, 'index.mdx', indexPageContent);
|
|
170
247
|
|
|
@@ -183,6 +260,8 @@ async function generateOneOfDocs(
|
|
|
183
260
|
eventOutputDir,
|
|
184
261
|
options,
|
|
185
262
|
partialNameConflicts,
|
|
263
|
+
schemaSources,
|
|
264
|
+
schemaDir,
|
|
186
265
|
);
|
|
187
266
|
} else {
|
|
188
267
|
await generateAndWriteDoc(
|
|
@@ -194,6 +273,8 @@ async function generateOneOfDocs(
|
|
|
194
273
|
processedSchema,
|
|
195
274
|
sourceFilePath || filePath,
|
|
196
275
|
partialNameConflicts,
|
|
276
|
+
schemaSources,
|
|
277
|
+
schemaDir,
|
|
197
278
|
);
|
|
198
279
|
}
|
|
199
280
|
}
|
|
@@ -205,6 +286,7 @@ export default async function generateEventDocs(options) {
|
|
|
205
286
|
|
|
206
287
|
createDir(outputDir);
|
|
207
288
|
const schemas = readSchemas(schemaDir);
|
|
289
|
+
const schemaSources = readSchemaSources(schemaDir);
|
|
208
290
|
const partialNameConflicts = await getPartialNameConflicts(schemas);
|
|
209
291
|
|
|
210
292
|
console.log(`🚀 Generating documentation for ${schemas.length} schemas...`);
|
|
@@ -229,6 +311,8 @@ export default async function generateEventDocs(options) {
|
|
|
229
311
|
outputDir,
|
|
230
312
|
options,
|
|
231
313
|
partialNameConflicts,
|
|
314
|
+
schemaSources,
|
|
315
|
+
schemaDir,
|
|
232
316
|
);
|
|
233
317
|
} else {
|
|
234
318
|
await generateAndWriteDoc(
|
|
@@ -240,6 +324,8 @@ export default async function generateEventDocs(options) {
|
|
|
240
324
|
null,
|
|
241
325
|
null,
|
|
242
326
|
partialNameConflicts,
|
|
327
|
+
schemaSources,
|
|
328
|
+
schemaDir,
|
|
243
329
|
);
|
|
244
330
|
}
|
|
245
331
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export default function ChoiceIndexTemplate(data) {
|
|
2
|
-
const { schema, processedOptions, editUrl } = data;
|
|
2
|
+
const { schema, processedOptions, editUrl, sourcePath, schemaSources } = data;
|
|
3
3
|
|
|
4
4
|
return `---
|
|
5
5
|
title: ${schema.title}
|
|
@@ -18,6 +18,10 @@ ${processedOptions
|
|
|
18
18
|
.map((option) => `- [${option.schema.title}](./${option.slug})`)
|
|
19
19
|
.join('\n')}
|
|
20
20
|
|
|
21
|
-
<SchemaJsonViewer
|
|
21
|
+
<SchemaJsonViewer
|
|
22
|
+
schema={${JSON.stringify(schema)}}
|
|
23
|
+
sourcePath={${JSON.stringify(sourcePath)}}
|
|
24
|
+
schemaSources={${JSON.stringify(schemaSources)}}
|
|
25
|
+
/>
|
|
22
26
|
`;
|
|
23
27
|
}
|
|
@@ -5,7 +5,7 @@ export function getSingleExampleValue(propSchema) {
|
|
|
5
5
|
if (propSchema.examples?.length > 0) {
|
|
6
6
|
return propSchema.examples[0];
|
|
7
7
|
}
|
|
8
|
-
if (propSchema
|
|
8
|
+
if (Object.prototype.hasOwnProperty.call(propSchema, 'example')) {
|
|
9
9
|
return propSchema.example;
|
|
10
10
|
}
|
|
11
11
|
if (Object.prototype.hasOwnProperty.call(propSchema, 'default')) {
|
|
@@ -25,7 +25,7 @@ export function getExamples(propSchema) {
|
|
|
25
25
|
examples.push(...propSchema.examples);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
if (propSchema
|
|
28
|
+
if (Object.prototype.hasOwnProperty.call(propSchema, 'example')) {
|
|
29
29
|
if (!examples.includes(propSchema.example)) {
|
|
30
30
|
examples.push(propSchema.example);
|
|
31
31
|
}
|
package/helpers/file-system.js
CHANGED
|
@@ -23,6 +23,34 @@ export function readSchemas(directory) {
|
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export function readSchemaSources(directory) {
|
|
27
|
+
const schemaSources = {};
|
|
28
|
+
|
|
29
|
+
function walk(currentDirectory) {
|
|
30
|
+
const entries = fs.readdirSync(currentDirectory, { withFileTypes: true });
|
|
31
|
+
|
|
32
|
+
entries.forEach((entry) => {
|
|
33
|
+
const entryPath = path.join(currentDirectory, entry.name);
|
|
34
|
+
if (entry.isDirectory()) {
|
|
35
|
+
walk(entryPath);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!entry.name.endsWith('.json')) return;
|
|
40
|
+
const relativePath = path
|
|
41
|
+
.relative(directory, entryPath)
|
|
42
|
+
.split(path.sep)
|
|
43
|
+
.join('/');
|
|
44
|
+
schemaSources[relativePath] = JSON.parse(
|
|
45
|
+
fs.readFileSync(entryPath, 'utf-8'),
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
walk(directory);
|
|
51
|
+
return schemaSources;
|
|
52
|
+
}
|
|
53
|
+
|
|
26
54
|
export function writeDoc(outputDir, fileName, content) {
|
|
27
55
|
fs.writeFileSync(path.join(outputDir, fileName), content);
|
|
28
56
|
console.log(
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
// A list of JSON Schema keywords that have simple key-value constraints.
|
|
2
|
+
function formatInlineConstraintValue(value) {
|
|
3
|
+
if (Array.isArray(value)) {
|
|
4
|
+
return `[${value.map((item) => formatInlineConstraintValue(item)).join(', ')}]`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
if (value && typeof value === 'object') {
|
|
8
|
+
const entries = Object.entries(value).map(
|
|
9
|
+
([key, nested]) => `${key}: ${formatInlineConstraintValue(nested)}`,
|
|
10
|
+
);
|
|
11
|
+
return `{ ${entries.join(', ')} }`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return JSON.stringify(value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function formatNotConstraint(val) {
|
|
18
|
+
return `not: ${formatInlineConstraintValue(val)}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
2
21
|
const constraintHandlers = {
|
|
3
22
|
// Simple key-value constraints
|
|
4
23
|
minLength: (val) => `minLength: ${val}`,
|
|
@@ -32,6 +51,7 @@ const constraintHandlers = {
|
|
|
32
51
|
contains: (val) => `contains: ${JSON.stringify(val)}`,
|
|
33
52
|
enum: (val) => `enum: [${val.join(', ')}]`,
|
|
34
53
|
const: (val) => `const: ${JSON.stringify(val)}`,
|
|
54
|
+
not: (val) => formatNotConstraint(val),
|
|
35
55
|
};
|
|
36
56
|
|
|
37
57
|
export const getConstraints = (prop, isReq) => {
|
package/helpers/processSchema.js
CHANGED
|
@@ -3,6 +3,37 @@ import fs from 'fs';
|
|
|
3
3
|
import { resolveConstraintSchemaPath } from './constraintSchemaPaths.js';
|
|
4
4
|
import { mergeSchema } from './mergeSchema.js';
|
|
5
5
|
|
|
6
|
+
function unwrapRedundantNotAnyOf(node) {
|
|
7
|
+
if (!node || typeof node !== 'object') {
|
|
8
|
+
return node;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (Array.isArray(node)) {
|
|
12
|
+
return node.map(unwrapRedundantNotAnyOf);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const normalized = {};
|
|
16
|
+
for (const [key, value] of Object.entries(node)) {
|
|
17
|
+
normalized[key] = unwrapRedundantNotAnyOf(value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (normalized.not && typeof normalized.not === 'object') {
|
|
21
|
+
let candidate = normalized.not;
|
|
22
|
+
while (
|
|
23
|
+
candidate &&
|
|
24
|
+
typeof candidate === 'object' &&
|
|
25
|
+
!Array.isArray(candidate) &&
|
|
26
|
+
Array.isArray(candidate.anyOf) &&
|
|
27
|
+
candidate.anyOf.length === 1
|
|
28
|
+
) {
|
|
29
|
+
candidate = candidate.anyOf[0];
|
|
30
|
+
}
|
|
31
|
+
normalized.not = candidate;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return normalized;
|
|
35
|
+
}
|
|
36
|
+
|
|
6
37
|
/**
|
|
7
38
|
* Processes a JSON schema file by bundling external references,
|
|
8
39
|
* dereferencing internal references, and merging allOf properties.
|
|
@@ -41,5 +72,5 @@ export default async function processSchema(filePath) {
|
|
|
41
72
|
// Then merge allOf properties
|
|
42
73
|
const mergedSchema = mergeSchema(dereferencedSchema);
|
|
43
74
|
|
|
44
|
-
return mergedSchema;
|
|
75
|
+
return unwrapRedundantNotAnyOf(mergedSchema);
|
|
45
76
|
}
|
|
@@ -9,7 +9,10 @@ export default function MdxTemplate(data) {
|
|
|
9
9
|
topPartialComponent,
|
|
10
10
|
bottomPartialComponent,
|
|
11
11
|
dataLayerName,
|
|
12
|
+
sourcePath,
|
|
13
|
+
schemaSources,
|
|
12
14
|
} = data;
|
|
15
|
+
const sourceSchema = schemaSources?.[sourcePath] || schema;
|
|
13
16
|
|
|
14
17
|
return `---
|
|
15
18
|
title: ${schema.title}
|
|
@@ -31,9 +34,16 @@ ${topPartialComponent}
|
|
|
31
34
|
|
|
32
35
|
<SchemaViewer
|
|
33
36
|
schema={${JSON.stringify(mergedSchema)}}
|
|
37
|
+
sourceSchema={${JSON.stringify(sourceSchema)}}
|
|
38
|
+
sourcePath={${JSON.stringify(sourcePath)}}
|
|
39
|
+
schemaSources={${JSON.stringify(schemaSources)}}
|
|
34
40
|
${dataLayerName ? ` dataLayerName={'${dataLayerName}'}` : ''}
|
|
35
41
|
/>
|
|
36
|
-
<SchemaJsonViewer
|
|
42
|
+
<SchemaJsonViewer
|
|
43
|
+
schema={${JSON.stringify(schema)}}
|
|
44
|
+
sourcePath={${JSON.stringify(sourcePath)}}
|
|
45
|
+
schemaSources={${JSON.stringify(schemaSources)}}
|
|
46
|
+
/>
|
|
37
47
|
|
|
38
48
|
${bottomPartialComponent}
|
|
39
49
|
`;
|
|
@@ -1,44 +1,31 @@
|
|
|
1
1
|
import buildExampleFromSchema from './buildExampleFromSchema';
|
|
2
2
|
import { mergeSchema } from './mergeSchema.js';
|
|
3
|
+
import traversalHelpers from './schemaTraversal.cjs';
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
-
if (!subSchema) {
|
|
6
|
-
return [];
|
|
7
|
-
}
|
|
5
|
+
const { visitSchemaNodes } = traversalHelpers;
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
: subSchema.anyOf
|
|
12
|
-
? 'anyOf'
|
|
13
|
-
: null;
|
|
14
|
-
const currentChoice = choiceType ? [{ path, schema: subSchema }] : [];
|
|
7
|
+
const findChoicePoints = (schema) => {
|
|
8
|
+
const choicePoints = [];
|
|
15
9
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
visitSchemaNodes(schema, (subSchema, context) => {
|
|
11
|
+
if (subSchema.oneOf || subSchema.anyOf) {
|
|
12
|
+
choicePoints.push({ path: context.path, schema: subSchema });
|
|
13
|
+
}
|
|
14
|
+
});
|
|
21
15
|
|
|
22
|
-
return
|
|
16
|
+
return choicePoints;
|
|
23
17
|
};
|
|
24
18
|
|
|
25
|
-
const findConditionalPoints = (
|
|
26
|
-
|
|
27
|
-
return [];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const currentConditional =
|
|
31
|
-
subSchema.if && (subSchema.then || subSchema.else)
|
|
32
|
-
? [{ path, schema: subSchema }]
|
|
33
|
-
: [];
|
|
19
|
+
const findConditionalPoints = (schema) => {
|
|
20
|
+
const conditionalPoints = [];
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
visitSchemaNodes(schema, (subSchema, context) => {
|
|
23
|
+
if (subSchema.if && (subSchema.then || subSchema.else)) {
|
|
24
|
+
conditionalPoints.push({ path: context.path, schema: subSchema });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
40
27
|
|
|
41
|
-
return
|
|
28
|
+
return conditionalPoints;
|
|
42
29
|
};
|
|
43
30
|
|
|
44
31
|
const generateExampleForChoice = (rootSchema, path, option) => {
|
|
@@ -151,10 +138,17 @@ export function schemaToExamples(rootSchema) {
|
|
|
151
138
|
|
|
152
139
|
if (choicePoints.length === 0 && conditionalPoints.length === 0) {
|
|
153
140
|
const example = buildExampleFromSchema(rootSchema);
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
141
|
+
if (typeof example !== 'undefined') {
|
|
142
|
+
const shouldIncludeObjectExample =
|
|
143
|
+
typeof example !== 'object' ||
|
|
144
|
+
example === null ||
|
|
145
|
+
Object.keys(example).length > 0;
|
|
146
|
+
|
|
147
|
+
if (shouldIncludeObjectExample) {
|
|
148
|
+
return [
|
|
149
|
+
{ property: 'default', options: [{ title: 'Example', example }] },
|
|
150
|
+
];
|
|
151
|
+
}
|
|
158
152
|
}
|
|
159
153
|
return [];
|
|
160
154
|
}
|