docusaurus-plugin-generate-schema-docs 1.1.0 → 1.2.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 +97 -0
- package/__tests__/ExampleDataLayer.test.js +169 -0
- package/__tests__/__fixtures__/static/schemas/add-to-cart-event.json +55 -0
- package/__tests__/__fixtures__/static/schemas/components/dataLayer.json +58 -0
- package/__tests__/__fixtures__/static/schemas/components/product.json +211 -0
- package/__tests__/__fixtures__/validateSchemas/circular-schema.json +8 -0
- package/__tests__/__fixtures__/validateSchemas/components/referenced.json +8 -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__/__snapshots__/generateEventDocs.test.js.snap +53 -0
- package/__tests__/components/PropertiesTable.test.js +24 -0
- package/__tests__/components/PropertyRow.test.js +67 -0
- package/__tests__/components/SchemaJsonViewer.test.js +32 -0
- package/__tests__/components/SchemaRows.test.js +80 -0
- package/__tests__/components/SchemaViewer.test.js +27 -0
- package/__tests__/components/TableHeader.test.js +20 -0
- package/__tests__/generateEventDocs.test.js +84 -0
- package/__tests__/helpers/buildExampleFromSchema.test.js +188 -0
- package/__tests__/helpers/getConstraints.test.js +58 -0
- package/__tests__/helpers/loadSchema.test.js +14 -0
- package/__tests__/helpers/processSchema.test.js +36 -0
- package/__tests__/validateSchemas.test.js +100 -0
- package/components/ExampleDataLayer.js +7 -5
- package/components/PropertiesTable.js +5 -10
- package/components/PropertyRow.js +74 -0
- package/components/SchemaJsonViewer.js +3 -2
- package/components/SchemaRows.css +28 -0
- package/components/SchemaRows.js +19 -47
- package/components/SchemaViewer.js +4 -2
- package/components/TableHeader.js +15 -0
- package/generateEventDocs.js +37 -36
- package/helpers/buildExampleFromSchema.js +3 -3
- package/helpers/getConstraints.js +53 -0
- package/helpers/loadSchema.js +11 -0
- package/helpers/mdx-template.js +36 -0
- package/helpers/processSchema.js +32 -0
- package/index.js +21 -11
- package/package.json +13 -2
- package/validateSchemas.js +6 -3
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getConstraints } from '../../helpers/getConstraints';
|
|
2
|
+
|
|
3
|
+
describe('getConstraints', () => {
|
|
4
|
+
it('should return "required" if isReq is true', () => {
|
|
5
|
+
const constraints = getConstraints({}, true);
|
|
6
|
+
expect(constraints).toContain('required');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should handle simple key-value constraints', () => {
|
|
10
|
+
const prop = {
|
|
11
|
+
minLength: 1,
|
|
12
|
+
maxLength: 10,
|
|
13
|
+
minimum: 0,
|
|
14
|
+
maximum: 100,
|
|
15
|
+
minItems: 1,
|
|
16
|
+
maxItems: 5,
|
|
17
|
+
};
|
|
18
|
+
const constraints = getConstraints(prop, false);
|
|
19
|
+
expect(constraints).toEqual([
|
|
20
|
+
'minLength: 1',
|
|
21
|
+
'maxLength: 10',
|
|
22
|
+
'minimum: 0',
|
|
23
|
+
'maximum: 100',
|
|
24
|
+
'minItems: 1',
|
|
25
|
+
'maxItems: 5',
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should handle pattern constraint', () => {
|
|
30
|
+
const prop = { pattern: '^[a-z]+$' };
|
|
31
|
+
const constraints = getConstraints(prop, false);
|
|
32
|
+
expect(constraints).toContain('pattern: /^[a-z]+$/');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should handle uniqueItems constraint', () => {
|
|
36
|
+
const prop = { uniqueItems: true };
|
|
37
|
+
const constraints = getConstraints(prop, false);
|
|
38
|
+
expect(constraints).toContain('uniqueItems: true');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should handle additionalProperties constraint', () => {
|
|
42
|
+
const prop = { additionalProperties: false };
|
|
43
|
+
const constraints = getConstraints(prop, false);
|
|
44
|
+
expect(constraints).toContain('additionalProperties: false');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should handle enum constraint', () => {
|
|
48
|
+
const prop = { enum: ['a', 'b', 'c'] };
|
|
49
|
+
const constraints = getConstraints(prop, false);
|
|
50
|
+
expect(constraints).toContain('enum: [a, b, c]');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should handle const constraint', () => {
|
|
54
|
+
const prop = { const: 'a' };
|
|
55
|
+
const constraints = getConstraints(prop, false);
|
|
56
|
+
expect(constraints).toContain('const: "a"');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import loadSchema from '../../helpers/loadSchema';
|
|
7
|
+
|
|
8
|
+
describe('loadSchema', () => {
|
|
9
|
+
it('should load and parse a JSON file', () => {
|
|
10
|
+
const filePath = path.join(__dirname, '..', '__fixtures__', 'validateSchemas', 'circular-schema.json');
|
|
11
|
+
const schema = loadSchema(filePath);
|
|
12
|
+
expect(schema.title).toBe('Circular Schema');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import processSchema from '../../helpers/processSchema';
|
|
7
|
+
|
|
8
|
+
describe('processSchema', () => {
|
|
9
|
+
it('should bundle refs and return a merged schema', async () => {
|
|
10
|
+
const filePath = path.join(__dirname, '..', '__fixtures__', 'validateSchemas', 'main-schema-with-ref.json');
|
|
11
|
+
const mergedSchema = await processSchema(filePath);
|
|
12
|
+
|
|
13
|
+
expect(mergedSchema.title).toBe('Main Schema with Ref');
|
|
14
|
+
expect(mergedSchema.properties.component.$ref).toBeUndefined();
|
|
15
|
+
expect(mergedSchema.properties.component.type).toBe('object');
|
|
16
|
+
expect(mergedSchema.properties.component.properties.prop.type).toBe('string');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should handle circular references correctly', async () => {
|
|
20
|
+
const filePath = path.join(__dirname, '..', '__fixtures__', 'validateSchemas', 'circular-schema.json');
|
|
21
|
+
const mergedSchema = await processSchema(filePath);
|
|
22
|
+
|
|
23
|
+
expect(mergedSchema.title).toBe('Circular Schema');
|
|
24
|
+
expect(mergedSchema.properties.parent.$ref).toBe('#');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should handle multi-file circular references and produce a self-contained schema', async () => {
|
|
28
|
+
const filePath = path.join(__dirname, '..', '__fixtures__', 'validateSchemas', 'schema-A.json');
|
|
29
|
+
const mergedSchema = await processSchema(filePath);
|
|
30
|
+
|
|
31
|
+
expect(mergedSchema.title).toBe('Schema A');
|
|
32
|
+
expect(mergedSchema.properties.b.$ref).toBeUndefined();
|
|
33
|
+
expect(mergedSchema.properties.b.title).toBe('Schema B');
|
|
34
|
+
expect(mergedSchema.properties.b.properties.a.$ref).toBe('#');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import validateSchemas from '../validateSchemas';
|
|
9
|
+
|
|
10
|
+
describe('validateSchemas', () => {
|
|
11
|
+
let tmpDir;
|
|
12
|
+
let consoleErrorSpy;
|
|
13
|
+
let consoleLogSpy;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'schema-test-'));
|
|
17
|
+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
18
|
+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
23
|
+
jest.restoreAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const writeSchema = (dir, fileName, content) => {
|
|
27
|
+
const filePath = path.join(dir, fileName);
|
|
28
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
29
|
+
fs.writeFileSync(filePath, JSON.stringify(content, null, 2));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const loadFixture = (fixtureName) => {
|
|
33
|
+
const fixturePath = path.resolve(__dirname, '__fixtures__', 'validateSchemas', fixtureName);
|
|
34
|
+
const schemaContent = fs.readFileSync(fixturePath, 'utf8');
|
|
35
|
+
return JSON.parse(schemaContent);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
it('should return true when all schemas are valid', async () => {
|
|
39
|
+
const validSchema = loadFixture('valid-schema.json');
|
|
40
|
+
writeSchema(tmpDir, 'valid-schema.json', validSchema);
|
|
41
|
+
|
|
42
|
+
const result = await validateSchemas(tmpDir);
|
|
43
|
+
expect(result).toBe(true);
|
|
44
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('✅ Schema valid-schema.json produced a valid example.');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should return false if an example fails validation', async () => {
|
|
48
|
+
const invalidExampleSchema = loadFixture('invalid-example-schema.json');
|
|
49
|
+
writeSchema(tmpDir, 'invalid-example-schema.json', invalidExampleSchema);
|
|
50
|
+
|
|
51
|
+
const result = await validateSchemas(tmpDir);
|
|
52
|
+
expect(result).toBe(false);
|
|
53
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith('❌ Schema invalid-example-schema.json example data failed validation:');
|
|
54
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.arrayContaining([
|
|
55
|
+
expect.objectContaining({
|
|
56
|
+
instancePath: '/age',
|
|
57
|
+
keyword: 'type',
|
|
58
|
+
message: 'must be number'
|
|
59
|
+
})
|
|
60
|
+
]));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should fail validation for missing required property', async () => {
|
|
64
|
+
const noValidExampleSchema = loadFixture('no-example-schema.json');
|
|
65
|
+
writeSchema(tmpDir, 'no-valid-example-schema.json', noValidExampleSchema);
|
|
66
|
+
|
|
67
|
+
const result = await validateSchemas(tmpDir);
|
|
68
|
+
expect(result).toBe(false);
|
|
69
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith('❌ Schema no-valid-example-schema.json example data failed validation:');
|
|
70
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.arrayContaining([
|
|
71
|
+
expect.objectContaining({
|
|
72
|
+
keyword: 'required',
|
|
73
|
+
params: { missingProperty: 'some_prop' },
|
|
74
|
+
})
|
|
75
|
+
]));
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should handle schemas with $refs correctly', async () => {
|
|
79
|
+
const componentSchema = loadFixture(path.join('components', 'referenced.json'));
|
|
80
|
+
const mainSchema = loadFixture('main-schema-with-ref.json');
|
|
81
|
+
|
|
82
|
+
writeSchema(path.join(tmpDir, 'components'), 'referenced.json', componentSchema);
|
|
83
|
+
writeSchema(tmpDir, 'main-schema-with-ref.json', mainSchema);
|
|
84
|
+
|
|
85
|
+
const result = await validateSchemas(tmpDir);
|
|
86
|
+
expect(result).toBe(true);
|
|
87
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('✅ Schema main-schema-with-ref.json produced a valid example.');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should reject if a referenced schema is missing', async () => {
|
|
91
|
+
const mainSchema = loadFixture('main-schema-with-missing-ref.json');
|
|
92
|
+
writeSchema(tmpDir, 'main-schema-with-missing-ref.json', mainSchema);
|
|
93
|
+
|
|
94
|
+
const expectedErrorPath = path.join(tmpDir, 'non-existent-component.json');
|
|
95
|
+
await expect(validateSchemas(tmpDir)).rejects.toThrow(expect.objectContaining({
|
|
96
|
+
message: expect.stringContaining(`Error opening file ${expectedErrorPath}`)
|
|
97
|
+
}));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
});
|
|
@@ -4,7 +4,7 @@ import buildExampleFromSchema from '../helpers/buildExampleFromSchema';
|
|
|
4
4
|
|
|
5
5
|
export default function ExampleDataLayer({ schema }) {
|
|
6
6
|
// 1. Identify properties that need to be reset (cleared) first
|
|
7
|
-
const
|
|
7
|
+
const clearableProperties = findClearableProperties(schema || {});
|
|
8
8
|
|
|
9
9
|
// 2. Build the main example data
|
|
10
10
|
const example = buildExampleFromSchema(schema || {});
|
|
@@ -12,11 +12,14 @@ export default function ExampleDataLayer({ schema }) {
|
|
|
12
12
|
// 3. Construct the code snippet
|
|
13
13
|
let codeSnippet = '';
|
|
14
14
|
|
|
15
|
+
// Filter properties to reset to only those present in the example
|
|
16
|
+
const propertiesToClear = clearableProperties.filter(prop => prop in example);
|
|
17
|
+
|
|
15
18
|
// If there are properties to reset, push them as null first
|
|
16
|
-
if (
|
|
19
|
+
if (propertiesToClear.length > 0)
|
|
17
20
|
{
|
|
18
21
|
const resetObject = {};
|
|
19
|
-
|
|
22
|
+
propertiesToClear.forEach(prop => {
|
|
20
23
|
resetObject[prop] = null;
|
|
21
24
|
});
|
|
22
25
|
codeSnippet += `window.dataLayer.push(${JSON.stringify(resetObject, null, 2)});\n`;
|
|
@@ -28,11 +31,10 @@ export default function ExampleDataLayer({ schema }) {
|
|
|
28
31
|
return <CodeBlock language="javascript">{codeSnippet}</CodeBlock>
|
|
29
32
|
};
|
|
30
33
|
|
|
31
|
-
const
|
|
34
|
+
export const findClearableProperties = (schema) => {
|
|
32
35
|
if (!schema || !schema.properties) return [];
|
|
33
36
|
|
|
34
37
|
return Object.entries(schema.properties)
|
|
35
38
|
.filter(([key, definition]) => definition["x-gtm-clear"] === true)
|
|
36
39
|
.map(([key]) => key);
|
|
37
40
|
}
|
|
38
|
-
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import SchemaRows from './SchemaRows';
|
|
3
|
+
import TableHeader from './TableHeader';
|
|
4
|
+
import { getConstraints } from '../helpers/getConstraints';
|
|
2
5
|
|
|
3
6
|
export default function PropertiesTable({ schema }) {
|
|
4
7
|
|
|
5
8
|
return <table>
|
|
6
|
-
<
|
|
7
|
-
<tr>
|
|
8
|
-
<th width="20%">Property</th>
|
|
9
|
-
<th width="15%">Type</th>
|
|
10
|
-
<th width="10%">Req</th>
|
|
11
|
-
<th with="15%">Examples</th>
|
|
12
|
-
<th>Description</th>
|
|
13
|
-
</tr>
|
|
14
|
-
</thead>
|
|
9
|
+
<TableHeader />
|
|
15
10
|
<tbody>
|
|
16
|
-
<SchemaRows properties={schema.properties} requiredList={schema.required} />
|
|
11
|
+
<SchemaRows properties={schema.properties} requiredList={schema.required} getConstraints={getConstraints} />
|
|
17
12
|
</tbody>
|
|
18
13
|
</table>
|
|
19
14
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
|
|
4
|
+
// Helper to format the property type
|
|
5
|
+
const getPropertyType = (type) => {
|
|
6
|
+
return Array.isArray(type) ? type.join(' | ') : type;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// Helper to format examples
|
|
10
|
+
const formatExamples = (examples) => {
|
|
11
|
+
if (!examples)
|
|
12
|
+
{
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
return examples
|
|
16
|
+
.map((example) =>
|
|
17
|
+
typeof example === 'object' ? JSON.stringify(example) : example
|
|
18
|
+
)
|
|
19
|
+
.join(', ');
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const PropertyRow = ({ propertyKey, prop, requiredList, getConstraints }) => {
|
|
23
|
+
const isReq = requiredList.includes(propertyKey);
|
|
24
|
+
const constraints = getConstraints(prop, isReq);
|
|
25
|
+
|
|
26
|
+
const childProperties = prop.properties || (prop.items && prop.items.properties);
|
|
27
|
+
const hasChildren = childProperties && Object.keys(childProperties).length > 0;
|
|
28
|
+
|
|
29
|
+
const isObject = prop.type === 'object';
|
|
30
|
+
const isArrayOfObjects = prop.type === 'array' && prop.items && prop.items.type === 'object';
|
|
31
|
+
|
|
32
|
+
if ((isObject || isArrayOfObjects) && !hasChildren)
|
|
33
|
+
{
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const numRows = Math.max(1, constraints.length);
|
|
38
|
+
const [firstConstraint, ...remainingConstraints] = constraints;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<React.Fragment>
|
|
42
|
+
<tr className={clsx(isReq && 'required-row')}>
|
|
43
|
+
<td rowSpan={numRows}>
|
|
44
|
+
<strong>{propertyKey}</strong>
|
|
45
|
+
{hasChildren && <span style={{ fontSize: '0.8em', marginLeft: '5px' }}>⤵</span>}
|
|
46
|
+
</td>
|
|
47
|
+
<td rowSpan={numRows}><code>{getPropertyType(prop.type)}</code></td>
|
|
48
|
+
<td>
|
|
49
|
+
{firstConstraint && (
|
|
50
|
+
<code className={clsx('constraint-code', firstConstraint === 'required' && 'required')}>
|
|
51
|
+
{firstConstraint}
|
|
52
|
+
</code>
|
|
53
|
+
)}
|
|
54
|
+
</td>
|
|
55
|
+
<td rowSpan={numRows}>
|
|
56
|
+
{formatExamples(prop.examples)}
|
|
57
|
+
</td>
|
|
58
|
+
<td rowSpan={numRows}>{prop.description || ''}</td>
|
|
59
|
+
</tr>
|
|
60
|
+
|
|
61
|
+
{remainingConstraints.map((constraint, index) => (
|
|
62
|
+
<tr className={clsx(isReq && 'required-row')} key={`${propertyKey}-constraint-${index}`}>
|
|
63
|
+
<td>
|
|
64
|
+
<code className={clsx('constraint-code', constraint === 'required' && 'required')}>
|
|
65
|
+
{constraint}
|
|
66
|
+
</code>
|
|
67
|
+
</td>
|
|
68
|
+
</tr>
|
|
69
|
+
))}
|
|
70
|
+
</React.Fragment>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default PropertyRow;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import CodeBlock from '@theme/CodeBlock';
|
|
2
3
|
|
|
3
4
|
export default function SchemaJsonViewer({ schema }) {
|
|
4
5
|
|
|
5
6
|
return (
|
|
6
|
-
<details>
|
|
7
|
+
<details className="schema-json-viewer">
|
|
7
8
|
<summary>View Raw JSON Schema</summary>
|
|
8
9
|
<CodeBlock language="json">{JSON.stringify(schema, null, 2)}</CodeBlock>
|
|
9
10
|
</details>);
|
|
10
|
-
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
.required-row {
|
|
2
|
+
background-color: rgba(255, 0, 0, 0.05);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.constraint-code {
|
|
6
|
+
white-space: pre-wrap;
|
|
7
|
+
margin-right: 4px;
|
|
8
|
+
display: inline-block;
|
|
9
|
+
margin-bottom: 4px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.constraint-code.required {
|
|
13
|
+
font-weight: bold;
|
|
14
|
+
color: #c7254e;
|
|
15
|
+
/* A reddish color */
|
|
16
|
+
background-color: #f9f2f4;
|
|
17
|
+
padding: 2px 4px;
|
|
18
|
+
border-radius: 4px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.nested-table-container {
|
|
22
|
+
padding-left: 20px;
|
|
23
|
+
border-left: 4px solid #eee;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.schema-json-viewer {
|
|
27
|
+
margin-bottom: 1em;
|
|
28
|
+
}
|
package/components/SchemaRows.js
CHANGED
|
@@ -1,71 +1,43 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import './SchemaRows.css';
|
|
3
|
+
import PropertyRow from './PropertyRow';
|
|
4
|
+
import TableHeader from './TableHeader';
|
|
2
5
|
|
|
3
|
-
const SchemaRows = ({ properties, requiredList = [], level = 0 }) => {
|
|
6
|
+
const SchemaRows = ({ properties, requiredList = [], level = 0, getConstraints }) => {
|
|
4
7
|
return (
|
|
5
8
|
<>
|
|
6
9
|
{Object.entries(properties).map(([key, prop]) => {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
// 1. Calculate if it has children (Same as before)
|
|
10
|
-
const hasChildren =
|
|
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);
|
|
18
|
-
|
|
19
|
-
const isObject = prop.type === 'object';
|
|
20
|
-
const isArrayOfObjects = prop.type === 'array' && prop.items && prop.items.type === 'object';
|
|
21
|
-
|
|
22
|
-
if ((isObject || isArrayOfObjects) && !hasChildren)
|
|
23
|
-
{
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
10
|
+
const childProperties = prop.properties || (prop.items && prop.items.properties);
|
|
11
|
+
const hasChildren = childProperties && Object.keys(childProperties).length > 0;
|
|
26
12
|
|
|
27
13
|
return (
|
|
28
14
|
<React.Fragment key={key}>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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 */}
|
|
15
|
+
<PropertyRow
|
|
16
|
+
propertyKey={key}
|
|
17
|
+
prop={prop}
|
|
18
|
+
requiredList={requiredList}
|
|
19
|
+
level={level}
|
|
20
|
+
getConstraints={getConstraints}
|
|
21
|
+
/>
|
|
42
22
|
{hasChildren && (
|
|
43
|
-
<tr>
|
|
44
|
-
<td colSpan="5"
|
|
23
|
+
<tr key={`${key}-nested`}>
|
|
24
|
+
<td colSpan="5" className="nested-table-container">
|
|
45
25
|
<strong>{prop.type === 'array' ? `${key} [ ]` : `${key} { }`}</strong>
|
|
46
26
|
<table style={{ width: '100%', marginTop: '5px' }}>
|
|
47
|
-
<
|
|
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>
|
|
27
|
+
<TableHeader />
|
|
56
28
|
<tbody>
|
|
57
29
|
<SchemaRows
|
|
58
30
|
properties={prop.type === 'object' ? prop.properties : prop.items.properties}
|
|
59
31
|
requiredList={prop.type === 'object' ? prop.required || [] : prop.items.required || []}
|
|
60
32
|
level={level + 1}
|
|
33
|
+
getConstraints={getConstraints}
|
|
61
34
|
/>
|
|
62
35
|
</tbody>
|
|
63
36
|
</table>
|
|
64
37
|
</td>
|
|
65
38
|
</tr>
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
</React.Fragment >
|
|
39
|
+
)}
|
|
40
|
+
</React.Fragment>
|
|
69
41
|
);
|
|
70
42
|
})}
|
|
71
43
|
</>
|
|
@@ -1,3 +1,5 @@
|
|
|
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
|
|
|
@@ -9,10 +11,10 @@ export default function SchemaViewer({ schema }) {
|
|
|
9
11
|
|
|
10
12
|
return (
|
|
11
13
|
<div>
|
|
12
|
-
<h2>DataLayer Example</
|
|
14
|
+
<Heading as="h2">DataLayer Example</Heading>
|
|
13
15
|
<ExampleDataLayer schema={schema} />
|
|
14
16
|
|
|
15
|
-
<h2>Event Properties</
|
|
17
|
+
<Heading as="h2">Event Properties</Heading>
|
|
16
18
|
<PropertiesTable schema={schema} />
|
|
17
19
|
</div>
|
|
18
20
|
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export default function TableHeader() {
|
|
4
|
+
return (
|
|
5
|
+
<thead>
|
|
6
|
+
<tr>
|
|
7
|
+
<th width="20%">Property</th>
|
|
8
|
+
<th width="15%">Type</th>
|
|
9
|
+
<th width="25%">Constraints</th>
|
|
10
|
+
<th width="15%">Examples</th>
|
|
11
|
+
<th>Description</th>
|
|
12
|
+
</tr>
|
|
13
|
+
</thead>
|
|
14
|
+
);
|
|
15
|
+
}
|
package/generateEventDocs.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import $RefParser from "@apidevtools/json-schema-ref-parser";
|
|
2
|
-
import mergeJsonSchema from "json-schema-merge-allof";
|
|
3
1
|
import fs from 'fs';
|
|
4
2
|
import path from 'path';
|
|
3
|
+
import loadSchema from './helpers/loadSchema';
|
|
4
|
+
import processSchema from './helpers/processSchema';
|
|
5
|
+
import MdxTemplate from './helpers/mdx-template.js';
|
|
5
6
|
|
|
6
|
-
export default async function generateEventDocs() {
|
|
7
|
+
export default async function generateEventDocs(options) {
|
|
8
|
+
const { organizationName, projectName, siteDir } = options || {};
|
|
9
|
+
const baseEditUrl = `https://github.com/${organizationName}/${projectName}/edit/main`;
|
|
7
10
|
// CONFIGURATION
|
|
8
|
-
const SCHEMA_DIR = 'schemas'; // Where your JSON files are
|
|
9
|
-
const OUTPUT_DIR = 'docs/events'; // Where MDX goes
|
|
11
|
+
const SCHEMA_DIR = path.join(siteDir, 'static/schemas'); // Where your JSON files are
|
|
12
|
+
const OUTPUT_DIR = path.join(siteDir, 'docs/events'); // Where MDX goes
|
|
13
|
+
const PARTIALS_DIR = path.join(siteDir, 'docs/partials'); // Where your partials are
|
|
10
14
|
|
|
11
15
|
// Ensure output dir exists
|
|
12
16
|
if (!fs.existsSync(OUTPUT_DIR))
|
|
@@ -22,41 +26,38 @@ export default async function generateEventDocs() {
|
|
|
22
26
|
for (const file of files)
|
|
23
27
|
{
|
|
24
28
|
const filePath = path.join(SCHEMA_DIR, file);
|
|
25
|
-
const
|
|
26
|
-
const
|
|
29
|
+
const schema = loadSchema(filePath);
|
|
30
|
+
const mergedSchema = await processSchema(filePath);
|
|
31
|
+
const eventName = file.replace('.json', '');
|
|
27
32
|
|
|
28
|
-
//
|
|
29
|
-
const
|
|
30
|
-
|
|
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
|
-
});
|
|
33
|
+
// Check for partials
|
|
34
|
+
const topPartialPath = path.join(PARTIALS_DIR, `${eventName}.mdx`);
|
|
35
|
+
const bottomPartialPath = path.join(PARTIALS_DIR, `${eventName}_bottom.mdx`);
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
---
|
|
37
|
+
let topPartialImport = '';
|
|
38
|
+
let topPartialComponent = '';
|
|
39
|
+
if (fs.existsSync(topPartialPath)) {
|
|
40
|
+
topPartialImport = `import TopPartial from '@site/docs/partials/${eventName}.mdx';`;
|
|
41
|
+
topPartialComponent = '<TopPartial />';
|
|
42
|
+
}
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
let bottomPartialImport = '';
|
|
45
|
+
let bottomPartialComponent = '';
|
|
46
|
+
if (fs.existsSync(bottomPartialPath)) {
|
|
47
|
+
bottomPartialImport = `import BottomPartial from '@site/docs/partials/${eventName}_bottom.mdx';`;
|
|
48
|
+
bottomPartialComponent = '<BottomPartial />';
|
|
49
|
+
}
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
const mdxContent = MdxTemplate({
|
|
52
|
+
schema,
|
|
53
|
+
mergedSchema,
|
|
54
|
+
baseEditUrl,
|
|
55
|
+
file,
|
|
56
|
+
topPartialImport,
|
|
57
|
+
bottomPartialImport,
|
|
58
|
+
topPartialComponent,
|
|
59
|
+
bottomPartialComponent
|
|
60
|
+
});
|
|
60
61
|
|
|
61
62
|
// Write the .mdx file
|
|
62
63
|
const outputFilename = file.replace('.json', '.mdx');
|
|
@@ -28,7 +28,7 @@ const buildExampleFromSchema = (schema) => {
|
|
|
28
28
|
return obj;
|
|
29
29
|
}
|
|
30
30
|
// Object with no properties defined
|
|
31
|
-
return
|
|
31
|
+
return undefined;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
if (type === 'array')
|
|
@@ -43,7 +43,7 @@ const buildExampleFromSchema = (schema) => {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
// Empty array or array of undefined items
|
|
46
|
-
return
|
|
46
|
+
return undefined;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
switch (type)
|
|
@@ -80,4 +80,4 @@ const buildExampleFromSchema = (schema) => {
|
|
|
80
80
|
|
|
81
81
|
return {};
|
|
82
82
|
}
|
|
83
|
-
export default buildExampleFromSchema;
|
|
83
|
+
export default buildExampleFromSchema;
|