@pie-element/cli 0.1.4-next.20261006160832 → 0.1.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 +1 -1
- package/dist/lib/docs/contracts.js +25 -123
- package/dist/lib/docs/contracts.js.map +1 -1
- package/dist/lib/docs/generator.js +81 -128
- package/dist/lib/docs/generator.js.map +1 -1
- package/dist/lib/docs/schema-properties.d.ts +10 -0
- package/dist/lib/docs/schema-properties.js +123 -0
- package/dist/lib/docs/schema-properties.js.map +1 -0
- package/dist/lib/docs/source-extractor.js +1 -1
- package/dist/lib/docs/source-extractor.js.map +1 -1
- package/dist/lib/docs/types.d.ts +10 -24
- package/dist/lib/docs/types.js.map +1 -1
- package/dist/lib/schemas/generate.d.ts +27 -0
- package/dist/lib/schemas/generate.js +101 -0
- package/dist/lib/schemas/generate.js.map +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ bun run cli packages:enable-publishing
|
|
|
58
58
|
|
|
59
59
|
#### `docs:generate`
|
|
60
60
|
|
|
61
|
-
Generate framework-agnostic HTML docs
|
|
61
|
+
Generate framework-agnostic HTML docs for elements: property rows from each element's JSON Schemas, defaults from the files its `docs.contract.json` points at.
|
|
62
62
|
The element demo runs this during `prebuild`/`predev`; the generated `static/element-docs`
|
|
63
63
|
directory is a local build artifact and is not committed.
|
|
64
64
|
|
|
@@ -2,92 +2,13 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join, relative } from 'node:path';
|
|
4
4
|
import { CONTRACT_FILENAME, inferViewsFromPackageExports } from './discovery.js';
|
|
5
|
+
import { loadPointerValue } from './source-extractor.js';
|
|
5
6
|
const MODES = ['gather', 'view', 'evaluate'];
|
|
6
7
|
const isObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
7
8
|
const isSourcePointer = (value) => isObject(value) &&
|
|
8
9
|
typeof value.file === 'string' &&
|
|
9
10
|
(value.exportName === undefined || typeof value.exportName === 'string') &&
|
|
10
11
|
(value.path === undefined || typeof value.path === 'string');
|
|
11
|
-
const isPrimitiveEnumValue = (value) => value === null || ['string', 'number', 'boolean'].includes(typeof value);
|
|
12
|
-
const isPropertyMetadata = (value) => {
|
|
13
|
-
if (!isObject(value)) {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
const allowed = [
|
|
17
|
-
'title',
|
|
18
|
-
'description',
|
|
19
|
-
'notes',
|
|
20
|
-
'type',
|
|
21
|
-
'required',
|
|
22
|
-
'enum',
|
|
23
|
-
'minimum',
|
|
24
|
-
'maximum',
|
|
25
|
-
'minLength',
|
|
26
|
-
'maxLength',
|
|
27
|
-
'pattern',
|
|
28
|
-
'itemType',
|
|
29
|
-
'examples',
|
|
30
|
-
];
|
|
31
|
-
for (const key of Object.keys(value)) {
|
|
32
|
-
if (!allowed.includes(key)) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
if (value.title !== undefined && typeof value.title !== 'string')
|
|
37
|
-
return false;
|
|
38
|
-
if (value.description !== undefined && typeof value.description !== 'string')
|
|
39
|
-
return false;
|
|
40
|
-
if (value.notes !== undefined && typeof value.notes !== 'string')
|
|
41
|
-
return false;
|
|
42
|
-
if (value.type !== undefined && typeof value.type !== 'string')
|
|
43
|
-
return false;
|
|
44
|
-
if (value.required !== undefined && typeof value.required !== 'boolean')
|
|
45
|
-
return false;
|
|
46
|
-
if (value.enum !== undefined) {
|
|
47
|
-
if (!Array.isArray(value.enum) || value.enum.some((entry) => !isPrimitiveEnumValue(entry))) {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
for (const key of ['minimum', 'maximum', 'minLength', 'maxLength']) {
|
|
52
|
-
if (value[key] !== undefined && typeof value[key] !== 'number')
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
if (value.pattern !== undefined && typeof value.pattern !== 'string')
|
|
56
|
-
return false;
|
|
57
|
-
if (value.itemType !== undefined && typeof value.itemType !== 'string')
|
|
58
|
-
return false;
|
|
59
|
-
if (value.examples !== undefined && !Array.isArray(value.examples))
|
|
60
|
-
return false;
|
|
61
|
-
return true;
|
|
62
|
-
};
|
|
63
|
-
const normalizeMetadataMap = (map) => {
|
|
64
|
-
if (!map) {
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
return Object.fromEntries(Object.entries(map)
|
|
68
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
69
|
-
.map(([path, metadata]) => [path, { ...metadata }]));
|
|
70
|
-
};
|
|
71
|
-
const isViewMetadata = (value) => {
|
|
72
|
-
if (!isObject(value)) {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
for (const key of Object.keys(value)) {
|
|
76
|
-
if (!['pie', 'config'].includes(key)) {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
const map = value[key];
|
|
80
|
-
if (map === undefined)
|
|
81
|
-
continue;
|
|
82
|
-
if (!isObject(map))
|
|
83
|
-
return false;
|
|
84
|
-
for (const entry of Object.values(map)) {
|
|
85
|
-
if (!isPropertyMetadata(entry))
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return true;
|
|
90
|
-
};
|
|
91
12
|
const normalizePointer = (pointer) => ({
|
|
92
13
|
file: pointer.file,
|
|
93
14
|
...(pointer.exportName ? { exportName: pointer.exportName } : {}),
|
|
@@ -105,45 +26,35 @@ const normalizeContract = (contract) => ({
|
|
|
105
26
|
view: view.view,
|
|
106
27
|
pie: normalizePointer(view.pie),
|
|
107
28
|
config: normalizePointer(view.config),
|
|
108
|
-
...(view.description ? { description: view.description } : {}),
|
|
109
|
-
...(view.metadata
|
|
110
|
-
? {
|
|
111
|
-
metadata: {
|
|
112
|
-
...(view.metadata.pie ? { pie: normalizeMetadataMap(view.metadata.pie) } : {}),
|
|
113
|
-
...(view.metadata.config
|
|
114
|
-
? { config: normalizeMetadataMap(view.metadata.config) }
|
|
115
|
-
: {}),
|
|
116
|
-
},
|
|
117
|
-
}
|
|
118
|
-
: {}),
|
|
119
29
|
})),
|
|
120
30
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
{ file: 'src/author/defaultConfiguration.ts', path: 'model' },
|
|
124
|
-
{ file: 'src/author/defaults.ts', path: 'model' },
|
|
125
|
-
{ file: 'src/controller/defaults.ts' },
|
|
126
|
-
];
|
|
31
|
+
// The first candidate whose file exists and whose path resolves in it.
|
|
32
|
+
const firstResolving = async (packageDir, candidates) => {
|
|
127
33
|
for (const candidate of candidates) {
|
|
128
|
-
if (existsSync(join(packageDir, candidate.file)))
|
|
34
|
+
if (!existsSync(join(packageDir, candidate.file)))
|
|
35
|
+
continue;
|
|
36
|
+
try {
|
|
37
|
+
await loadPointerValue(packageDir, candidate);
|
|
129
38
|
return candidate;
|
|
130
39
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
};
|
|
134
|
-
const inferConfigPointer = (packageDir) => {
|
|
135
|
-
const candidates = [
|
|
136
|
-
{ file: 'src/author/defaultConfiguration.ts', path: 'configuration' },
|
|
137
|
-
{ file: 'src/author/defaults.ts', path: 'configuration' },
|
|
138
|
-
{ file: 'src/controller/defaults.ts' },
|
|
139
|
-
];
|
|
140
|
-
for (const candidate of candidates) {
|
|
141
|
-
if (existsSync(join(packageDir, candidate.file))) {
|
|
142
|
-
return candidate;
|
|
40
|
+
catch {
|
|
41
|
+
// The path is absent from this file; try the next candidate.
|
|
143
42
|
}
|
|
144
43
|
}
|
|
145
|
-
return
|
|
44
|
+
return undefined;
|
|
146
45
|
};
|
|
46
|
+
// Controller defaults export either `{ model, configuration }` or the model itself.
|
|
47
|
+
const inferPiePointer = async (packageDir) => (await firstResolving(packageDir, [
|
|
48
|
+
{ file: 'src/author/defaultConfiguration.ts', path: 'model' },
|
|
49
|
+
{ file: 'src/author/defaults.ts', path: 'model' },
|
|
50
|
+
{ file: 'src/controller/defaults.ts', path: 'model' },
|
|
51
|
+
])) ?? { file: 'src/controller/defaults.ts' };
|
|
52
|
+
const inferConfigPointer = async (packageDir) => (await firstResolving(packageDir, [
|
|
53
|
+
{ file: 'src/author/defaultConfiguration.ts', path: 'configuration' },
|
|
54
|
+
{ file: 'src/author/defaults.ts', path: 'configuration' },
|
|
55
|
+
{ file: 'src/author/defaults.tsx', path: 'configuration' },
|
|
56
|
+
{ file: 'src/controller/defaults.ts', path: 'configuration' },
|
|
57
|
+
])) ?? { file: 'src/controller/defaults.ts', path: 'configuration' };
|
|
147
58
|
export const parseContract = (raw, location) => {
|
|
148
59
|
if (!isObject(raw)) {
|
|
149
60
|
throw new Error(`Invalid docs contract in ${location}: expected object`);
|
|
@@ -173,16 +84,7 @@ export const parseContract = (raw, location) => {
|
|
|
173
84
|
if (!isSourcePointer(view.config)) {
|
|
174
85
|
throw new Error(`Invalid docs contract in ${location}: views[${index}].config is invalid`);
|
|
175
86
|
}
|
|
176
|
-
|
|
177
|
-
throw new Error(`Invalid docs contract in ${location}: views[${index}].metadata is invalid`);
|
|
178
|
-
}
|
|
179
|
-
return {
|
|
180
|
-
view: view.view,
|
|
181
|
-
pie: view.pie,
|
|
182
|
-
config: view.config,
|
|
183
|
-
...(typeof view.description === 'string' ? { description: view.description } : {}),
|
|
184
|
-
...(view.metadata ? { metadata: view.metadata } : {}),
|
|
185
|
-
};
|
|
87
|
+
return { view: view.view, pie: view.pie, config: view.config };
|
|
186
88
|
});
|
|
187
89
|
const supportedModes = Array.isArray(raw.supportedModes)
|
|
188
90
|
? raw.supportedModes.filter((mode) => typeof mode === 'string')
|
|
@@ -216,8 +118,8 @@ export const seedContractForElement = async (element, rootDir, options) => {
|
|
|
216
118
|
if (existing && !options?.refresh) {
|
|
217
119
|
return { wrote: false, contractPath, contract: existing };
|
|
218
120
|
}
|
|
219
|
-
const piePointer = inferPiePointer(element.packageDir);
|
|
220
|
-
const configPointer = inferConfigPointer(element.packageDir);
|
|
121
|
+
const piePointer = await inferPiePointer(element.packageDir);
|
|
122
|
+
const configPointer = await inferConfigPointer(element.packageDir);
|
|
221
123
|
const views = inferViewsFromPackageExports(element.exportsMap).map((view) => ({
|
|
222
124
|
view,
|
|
223
125
|
pie: piePointer,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../../src/lib/docs/contracts.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport { CONTRACT_FILENAME, inferViewsFromPackageExports } from './discovery.js';\nimport type {\n ElementPackageInfo,\n PieDocsContract,\n PieDocsPropertyMetadata,\n PieDocsSourcePointer,\n PieDocsViewMetadata,\n} from './types.js';\n\nconst MODES = ['gather', 'view', 'evaluate'];\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n !!value && typeof value === 'object' && !Array.isArray(value);\n\nconst isSourcePointer = (value: unknown): value is PieDocsSourcePointer =>\n isObject(value) &&\n typeof value.file === 'string' &&\n (value.exportName === undefined || typeof value.exportName === 'string') &&\n (value.path === undefined || typeof value.path === 'string');\n\nconst isPrimitiveEnumValue = (value: unknown): value is string | number | boolean | null =>\n value === null || ['string', 'number', 'boolean'].includes(typeof value);\n\nconst isPropertyMetadata = (value: unknown): value is PieDocsPropertyMetadata => {\n if (!isObject(value)) {\n return false;\n }\n const allowed = [\n 'title',\n 'description',\n 'notes',\n 'type',\n 'required',\n 'enum',\n 'minimum',\n 'maximum',\n 'minLength',\n 'maxLength',\n 'pattern',\n 'itemType',\n 'examples',\n ];\n for (const key of Object.keys(value)) {\n if (!allowed.includes(key)) {\n return false;\n }\n }\n if (value.title !== undefined && typeof value.title !== 'string') return false;\n if (value.description !== undefined && typeof value.description !== 'string') return false;\n if (value.notes !== undefined && typeof value.notes !== 'string') return false;\n if (value.type !== undefined && typeof value.type !== 'string') return false;\n if (value.required !== undefined && typeof value.required !== 'boolean') return false;\n if (value.enum !== undefined) {\n if (!Array.isArray(value.enum) || value.enum.some((entry) => !isPrimitiveEnumValue(entry))) {\n return false;\n }\n }\n for (const key of ['minimum', 'maximum', 'minLength', 'maxLength'] as const) {\n if (value[key] !== undefined && typeof value[key] !== 'number') return false;\n }\n if (value.pattern !== undefined && typeof value.pattern !== 'string') return false;\n if (value.itemType !== undefined && typeof value.itemType !== 'string') return false;\n if (value.examples !== undefined && !Array.isArray(value.examples)) return false;\n return true;\n};\n\nconst normalizeMetadataMap = (\n map?: Record<string, PieDocsPropertyMetadata>\n): Record<string, PieDocsPropertyMetadata> | undefined => {\n if (!map) {\n return undefined;\n }\n return Object.fromEntries(\n Object.entries(map)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([path, metadata]) => [path, { ...metadata }])\n );\n};\n\nconst isViewMetadata = (value: unknown): value is PieDocsViewMetadata => {\n if (!isObject(value)) {\n return false;\n }\n for (const key of Object.keys(value)) {\n if (!['pie', 'config'].includes(key)) {\n return false;\n }\n const map = value[key];\n if (map === undefined) continue;\n if (!isObject(map)) return false;\n for (const entry of Object.values(map)) {\n if (!isPropertyMetadata(entry)) return false;\n }\n }\n return true;\n};\n\nconst normalizePointer = (pointer: PieDocsSourcePointer): PieDocsSourcePointer => ({\n file: pointer.file,\n ...(pointer.exportName ? { exportName: pointer.exportName } : {}),\n ...(pointer.path ? { path: pointer.path } : {}),\n});\n\nconst normalizeContract = (contract: PieDocsContract): PieDocsContract => ({\n elementName: contract.elementName,\n packageName: contract.packageName,\n framework: contract.framework,\n ...(contract.summary ? { summary: contract.summary } : {}),\n supportedModes: contract.supportedModes?.length ? [...contract.supportedModes] : MODES,\n views: [...contract.views]\n .sort((a, b) => a.view.localeCompare(b.view))\n .map((view) => ({\n view: view.view,\n pie: normalizePointer(view.pie),\n config: normalizePointer(view.config),\n ...(view.description ? { description: view.description } : {}),\n ...(view.metadata\n ? {\n metadata: {\n ...(view.metadata.pie ? { pie: normalizeMetadataMap(view.metadata.pie) } : {}),\n ...(view.metadata.config\n ? { config: normalizeMetadataMap(view.metadata.config) }\n : {}),\n },\n }\n : {}),\n })),\n});\n\nconst inferPiePointer = (packageDir: string): PieDocsSourcePointer => {\n const candidates: PieDocsSourcePointer[] = [\n { file: 'src/author/defaultConfiguration.ts', path: 'model' },\n { file: 'src/author/defaults.ts', path: 'model' },\n { file: 'src/controller/defaults.ts' },\n ];\n\n for (const candidate of candidates) {\n if (existsSync(join(packageDir, candidate.file))) {\n return candidate;\n }\n }\n\n return { file: 'src/controller/defaults.ts' };\n};\n\nconst inferConfigPointer = (packageDir: string): PieDocsSourcePointer => {\n const candidates: PieDocsSourcePointer[] = [\n { file: 'src/author/defaultConfiguration.ts', path: 'configuration' },\n { file: 'src/author/defaults.ts', path: 'configuration' },\n { file: 'src/controller/defaults.ts' },\n ];\n\n for (const candidate of candidates) {\n if (existsSync(join(packageDir, candidate.file))) {\n return candidate;\n }\n }\n\n return { file: 'src/controller/defaults.ts', path: 'configuration' };\n};\n\nexport const parseContract = (raw: unknown, location: string): PieDocsContract => {\n if (!isObject(raw)) {\n throw new Error(`Invalid docs contract in ${location}: expected object`);\n }\n if (typeof raw.elementName !== 'string' || !raw.elementName.trim()) {\n throw new Error(`Invalid docs contract in ${location}: elementName is required`);\n }\n if (typeof raw.packageName !== 'string' || !raw.packageName.startsWith('@pie-element/')) {\n throw new Error(`Invalid docs contract in ${location}: packageName is invalid`);\n }\n if (raw.framework !== 'react' && raw.framework !== 'svelte') {\n throw new Error(`Invalid docs contract in ${location}: framework must be react|svelte`);\n }\n if (!Array.isArray(raw.views) || raw.views.length === 0) {\n throw new Error(`Invalid docs contract in ${location}: at least one view is required`);\n }\n\n const views = raw.views.map((view, index) => {\n if (!isObject(view)) {\n throw new Error(`Invalid docs contract in ${location}: views[${index}] must be an object`);\n }\n if (typeof view.view !== 'string' || !view.view.trim()) {\n throw new Error(`Invalid docs contract in ${location}: views[${index}].view is required`);\n }\n if (!isSourcePointer(view.pie)) {\n throw new Error(`Invalid docs contract in ${location}: views[${index}].pie is invalid`);\n }\n if (!isSourcePointer(view.config)) {\n throw new Error(`Invalid docs contract in ${location}: views[${index}].config is invalid`);\n }\n if (view.metadata !== undefined && !isViewMetadata(view.metadata)) {\n throw new Error(`Invalid docs contract in ${location}: views[${index}].metadata is invalid`);\n }\n\n return {\n view: view.view,\n pie: view.pie,\n config: view.config,\n ...(typeof view.description === 'string' ? { description: view.description } : {}),\n ...(view.metadata ? { metadata: view.metadata } : {}),\n };\n });\n\n const supportedModes = Array.isArray(raw.supportedModes)\n ? raw.supportedModes.filter((mode): mode is string => typeof mode === 'string')\n : undefined;\n\n return normalizeContract({\n elementName: raw.elementName,\n packageName: raw.packageName,\n framework: raw.framework,\n summary: typeof raw.summary === 'string' ? raw.summary : undefined,\n supportedModes: supportedModes?.length ? supportedModes : MODES,\n views,\n });\n};\n\nexport const loadContract = async (packageDir: string): Promise<PieDocsContract | null> => {\n const contractPath = join(packageDir, CONTRACT_FILENAME);\n if (!existsSync(contractPath)) {\n return null;\n }\n const raw = JSON.parse(await readFile(contractPath, 'utf-8')) as unknown;\n return parseContract(raw, contractPath);\n};\n\nexport const writeContract = async (\n packageDir: string,\n contract: PieDocsContract\n): Promise<void> => {\n const contractPath = join(packageDir, CONTRACT_FILENAME);\n const normalized = normalizeContract(contract);\n await mkdir(packageDir, { recursive: true });\n await writeFile(contractPath, `${JSON.stringify(normalized, null, 2)}\\n`, 'utf-8');\n};\n\nexport const seedContractForElement = async (\n element: ElementPackageInfo,\n rootDir: string,\n options?: { refresh?: boolean }\n): Promise<{ wrote: boolean; contractPath: string; contract: PieDocsContract }> => {\n const contractPath = join(element.packageDir, CONTRACT_FILENAME);\n const existing = await loadContract(element.packageDir);\n if (existing && !options?.refresh) {\n return { wrote: false, contractPath, contract: existing };\n }\n\n const piePointer = inferPiePointer(element.packageDir);\n const configPointer = inferConfigPointer(element.packageDir);\n const views = inferViewsFromPackageExports(element.exportsMap).map((view) => ({\n view,\n pie: piePointer,\n config: configPointer,\n }));\n\n const contract: PieDocsContract = normalizeContract({\n elementName: element.elementName,\n packageName: element.packageName,\n framework: element.framework,\n summary: existing?.summary || element.packageDescription,\n supportedModes: existing?.supportedModes?.length ? existing.supportedModes : MODES,\n views,\n });\n\n await writeContract(element.packageDir, contract);\n\n return {\n wrote: true,\n contractPath: relative(rootDir, contractPath),\n contract,\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../../../src/lib/docs/contracts.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport { CONTRACT_FILENAME, inferViewsFromPackageExports } from './discovery.js';\nimport { loadPointerValue } from './source-extractor.js';\nimport type { ElementPackageInfo, PieDocsContract, PieDocsSourcePointer } from './types.js';\n\nconst MODES = ['gather', 'view', 'evaluate'];\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n !!value && typeof value === 'object' && !Array.isArray(value);\n\nconst isSourcePointer = (value: unknown): value is PieDocsSourcePointer =>\n isObject(value) &&\n typeof value.file === 'string' &&\n (value.exportName === undefined || typeof value.exportName === 'string') &&\n (value.path === undefined || typeof value.path === 'string');\n\nconst normalizePointer = (pointer: PieDocsSourcePointer): PieDocsSourcePointer => ({\n file: pointer.file,\n ...(pointer.exportName ? { exportName: pointer.exportName } : {}),\n ...(pointer.path ? { path: pointer.path } : {}),\n});\n\nconst normalizeContract = (contract: PieDocsContract): PieDocsContract => ({\n elementName: contract.elementName,\n packageName: contract.packageName,\n framework: contract.framework,\n ...(contract.summary ? { summary: contract.summary } : {}),\n supportedModes: contract.supportedModes?.length ? [...contract.supportedModes] : MODES,\n views: [...contract.views]\n .sort((a, b) => a.view.localeCompare(b.view))\n .map((view) => ({\n view: view.view,\n pie: normalizePointer(view.pie),\n config: normalizePointer(view.config),\n })),\n});\n\n// The first candidate whose file exists and whose path resolves in it.\nconst firstResolving = async (\n packageDir: string,\n candidates: PieDocsSourcePointer[]\n): Promise<PieDocsSourcePointer | undefined> => {\n for (const candidate of candidates) {\n if (!existsSync(join(packageDir, candidate.file))) continue;\n try {\n await loadPointerValue(packageDir, candidate);\n return candidate;\n } catch {\n // The path is absent from this file; try the next candidate.\n }\n }\n return undefined;\n};\n\n// Controller defaults export either `{ model, configuration }` or the model itself.\nconst inferPiePointer = async (packageDir: string): Promise<PieDocsSourcePointer> =>\n (await firstResolving(packageDir, [\n { file: 'src/author/defaultConfiguration.ts', path: 'model' },\n { file: 'src/author/defaults.ts', path: 'model' },\n { file: 'src/controller/defaults.ts', path: 'model' },\n ])) ?? { file: 'src/controller/defaults.ts' };\n\nconst inferConfigPointer = async (packageDir: string): Promise<PieDocsSourcePointer> =>\n (await firstResolving(packageDir, [\n { file: 'src/author/defaultConfiguration.ts', path: 'configuration' },\n { file: 'src/author/defaults.ts', path: 'configuration' },\n { file: 'src/author/defaults.tsx', path: 'configuration' },\n { file: 'src/controller/defaults.ts', path: 'configuration' },\n ])) ?? { file: 'src/controller/defaults.ts', path: 'configuration' };\n\nexport const parseContract = (raw: unknown, location: string): PieDocsContract => {\n if (!isObject(raw)) {\n throw new Error(`Invalid docs contract in ${location}: expected object`);\n }\n if (typeof raw.elementName !== 'string' || !raw.elementName.trim()) {\n throw new Error(`Invalid docs contract in ${location}: elementName is required`);\n }\n if (typeof raw.packageName !== 'string' || !raw.packageName.startsWith('@pie-element/')) {\n throw new Error(`Invalid docs contract in ${location}: packageName is invalid`);\n }\n if (raw.framework !== 'react' && raw.framework !== 'svelte') {\n throw new Error(`Invalid docs contract in ${location}: framework must be react|svelte`);\n }\n if (!Array.isArray(raw.views) || raw.views.length === 0) {\n throw new Error(`Invalid docs contract in ${location}: at least one view is required`);\n }\n\n const views = raw.views.map((view, index) => {\n if (!isObject(view)) {\n throw new Error(`Invalid docs contract in ${location}: views[${index}] must be an object`);\n }\n if (typeof view.view !== 'string' || !view.view.trim()) {\n throw new Error(`Invalid docs contract in ${location}: views[${index}].view is required`);\n }\n if (!isSourcePointer(view.pie)) {\n throw new Error(`Invalid docs contract in ${location}: views[${index}].pie is invalid`);\n }\n if (!isSourcePointer(view.config)) {\n throw new Error(`Invalid docs contract in ${location}: views[${index}].config is invalid`);\n }\n\n return { view: view.view, pie: view.pie, config: view.config };\n });\n\n const supportedModes = Array.isArray(raw.supportedModes)\n ? raw.supportedModes.filter((mode): mode is string => typeof mode === 'string')\n : undefined;\n\n return normalizeContract({\n elementName: raw.elementName,\n packageName: raw.packageName,\n framework: raw.framework,\n summary: typeof raw.summary === 'string' ? raw.summary : undefined,\n supportedModes: supportedModes?.length ? supportedModes : MODES,\n views,\n });\n};\n\nexport const loadContract = async (packageDir: string): Promise<PieDocsContract | null> => {\n const contractPath = join(packageDir, CONTRACT_FILENAME);\n if (!existsSync(contractPath)) {\n return null;\n }\n const raw = JSON.parse(await readFile(contractPath, 'utf-8')) as unknown;\n return parseContract(raw, contractPath);\n};\n\nexport const writeContract = async (\n packageDir: string,\n contract: PieDocsContract\n): Promise<void> => {\n const contractPath = join(packageDir, CONTRACT_FILENAME);\n const normalized = normalizeContract(contract);\n await mkdir(packageDir, { recursive: true });\n await writeFile(contractPath, `${JSON.stringify(normalized, null, 2)}\\n`, 'utf-8');\n};\n\nexport const seedContractForElement = async (\n element: ElementPackageInfo,\n rootDir: string,\n options?: { refresh?: boolean }\n): Promise<{ wrote: boolean; contractPath: string; contract: PieDocsContract }> => {\n const contractPath = join(element.packageDir, CONTRACT_FILENAME);\n const existing = await loadContract(element.packageDir);\n if (existing && !options?.refresh) {\n return { wrote: false, contractPath, contract: existing };\n }\n\n const piePointer = await inferPiePointer(element.packageDir);\n const configPointer = await inferConfigPointer(element.packageDir);\n const views = inferViewsFromPackageExports(element.exportsMap).map((view) => ({\n view,\n pie: piePointer,\n config: configPointer,\n }));\n\n const contract: PieDocsContract = normalizeContract({\n elementName: element.elementName,\n packageName: element.packageName,\n framework: element.framework,\n summary: existing?.summary || element.packageDescription,\n supportedModes: existing?.supportedModes?.length ? existing.supportedModes : MODES,\n views,\n });\n\n await writeContract(element.packageDir, contract);\n\n return {\n wrote: true,\n contractPath: relative(rootDir, contractPath),\n contract,\n };\n};\n"]}
|
|
@@ -1,56 +1,9 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join, relative } from 'node:path';
|
|
4
|
+
import { generateElementSchemas } from '../schemas/generate.js';
|
|
5
|
+
import { schemaDocs } from './schema-properties.js';
|
|
4
6
|
import { loadPointerValue } from './source-extractor.js';
|
|
5
|
-
const isRecord = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
6
|
-
const inferType = (value) => {
|
|
7
|
-
if (value === null)
|
|
8
|
-
return 'null';
|
|
9
|
-
if (Array.isArray(value))
|
|
10
|
-
return 'array';
|
|
11
|
-
switch (typeof value) {
|
|
12
|
-
case 'string':
|
|
13
|
-
case 'number':
|
|
14
|
-
case 'boolean':
|
|
15
|
-
return typeof value;
|
|
16
|
-
case 'object':
|
|
17
|
-
return 'object';
|
|
18
|
-
default:
|
|
19
|
-
return 'unknown';
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
const flattenProperties = (value, basePath = '') => {
|
|
23
|
-
if (isRecord(value)) {
|
|
24
|
-
const keys = Object.keys(value).sort();
|
|
25
|
-
if (keys.length === 0 && basePath) {
|
|
26
|
-
return [{ path: basePath, type: 'object', required: false, defaultValue: {} }];
|
|
27
|
-
}
|
|
28
|
-
const flattened = [];
|
|
29
|
-
for (const key of keys) {
|
|
30
|
-
const childPath = basePath ? `${basePath}.${key}` : key;
|
|
31
|
-
flattened.push(...flattenProperties(value[key], childPath));
|
|
32
|
-
}
|
|
33
|
-
return flattened;
|
|
34
|
-
}
|
|
35
|
-
if (Array.isArray(value)) {
|
|
36
|
-
return [
|
|
37
|
-
{
|
|
38
|
-
path: basePath,
|
|
39
|
-
type: 'array',
|
|
40
|
-
required: false,
|
|
41
|
-
defaultValue: value,
|
|
42
|
-
},
|
|
43
|
-
];
|
|
44
|
-
}
|
|
45
|
-
return [
|
|
46
|
-
{
|
|
47
|
-
path: basePath,
|
|
48
|
-
type: inferType(value),
|
|
49
|
-
required: false,
|
|
50
|
-
defaultValue: value,
|
|
51
|
-
},
|
|
52
|
-
];
|
|
53
|
-
};
|
|
54
7
|
const escapeHtml = (value) => value
|
|
55
8
|
.replaceAll('&', '&')
|
|
56
9
|
.replaceAll('<', '<')
|
|
@@ -66,7 +19,7 @@ const formatInline = (value) => {
|
|
|
66
19
|
};
|
|
67
20
|
const formatValue = (value) => {
|
|
68
21
|
if (typeof value === 'string') {
|
|
69
|
-
return `<code>${escapeHtml(value)}</code>`;
|
|
22
|
+
return `<code>${value === '' ? '""' : escapeHtml(value)}</code>`;
|
|
70
23
|
}
|
|
71
24
|
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
72
25
|
return `<code>${String(value)}</code>`;
|
|
@@ -123,8 +76,8 @@ const loadValueWithFallback = async (packageDir, pointer, kind) => {
|
|
|
123
76
|
pointer,
|
|
124
77
|
{ file: 'src/author/defaultConfiguration.ts', path: 'configuration' },
|
|
125
78
|
{ file: 'src/author/defaults.ts', path: 'configuration' },
|
|
79
|
+
{ file: 'src/author/defaults.tsx', path: 'configuration' },
|
|
126
80
|
{ file: 'src/controller/defaults.ts', path: 'configuration' },
|
|
127
|
-
{ file: 'src/controller/defaults.ts' },
|
|
128
81
|
];
|
|
129
82
|
let lastError;
|
|
130
83
|
for (const candidate of fallbackPointers) {
|
|
@@ -147,49 +100,27 @@ const loadValueWithFallback = async (packageDir, pointer, kind) => {
|
|
|
147
100
|
}
|
|
148
101
|
throw lastError || new Error(`Unable to load ${kind} defaults from ${pointer.file}`);
|
|
149
102
|
};
|
|
150
|
-
const applyMetadata = (properties, metadata) => {
|
|
151
|
-
if (!metadata || Object.keys(metadata).length === 0) {
|
|
152
|
-
return properties;
|
|
153
|
-
}
|
|
154
|
-
const byPath = new Map(properties.map((p) => [p.path, { ...p }]));
|
|
155
|
-
for (const [path, meta] of Object.entries(metadata)) {
|
|
156
|
-
const current = byPath.get(path) || {
|
|
157
|
-
path,
|
|
158
|
-
type: meta.type || 'unknown',
|
|
159
|
-
required: meta.required ?? false,
|
|
160
|
-
};
|
|
161
|
-
const merged = {
|
|
162
|
-
...current,
|
|
163
|
-
...(meta.type ? { type: meta.type } : {}),
|
|
164
|
-
...(meta.required !== undefined ? { required: meta.required } : {}),
|
|
165
|
-
...(meta.title ? { title: meta.title } : {}),
|
|
166
|
-
...(meta.description ? { description: meta.description } : {}),
|
|
167
|
-
...(meta.notes ? { notes: meta.notes } : {}),
|
|
168
|
-
...(meta.enum ? { enum: [...meta.enum] } : {}),
|
|
169
|
-
...(meta.minimum !== undefined ? { minimum: meta.minimum } : {}),
|
|
170
|
-
...(meta.maximum !== undefined ? { maximum: meta.maximum } : {}),
|
|
171
|
-
...(meta.minLength !== undefined ? { minLength: meta.minLength } : {}),
|
|
172
|
-
...(meta.maxLength !== undefined ? { maxLength: meta.maxLength } : {}),
|
|
173
|
-
...(meta.pattern ? { pattern: meta.pattern } : {}),
|
|
174
|
-
...(meta.itemType ? { itemType: meta.itemType } : {}),
|
|
175
|
-
...(meta.examples ? { examples: [...meta.examples] } : {}),
|
|
176
|
-
};
|
|
177
|
-
byPath.set(path, merged);
|
|
178
|
-
}
|
|
179
|
-
return Array.from(byPath.values()).sort((a, b) => a.path.localeCompare(b.path));
|
|
180
|
-
};
|
|
181
103
|
const propertyFingerprint = (properties) => JSON.stringify(properties.map((p) => ({
|
|
182
104
|
path: p.path,
|
|
183
105
|
type: p.type,
|
|
184
106
|
required: p.required,
|
|
185
107
|
defaultValue: p.defaultValue,
|
|
186
108
|
})));
|
|
187
|
-
|
|
109
|
+
// A type label names a documented type by its definition name; link those names to it.
|
|
110
|
+
const renderType = (type, anchor) => type
|
|
111
|
+
.split(' | ')
|
|
112
|
+
.map((member) => {
|
|
113
|
+
const id = anchor(member);
|
|
114
|
+
const code = `<code>${escapeHtml(member)}</code>`;
|
|
115
|
+
return id ? `<a href="#${id}">${code}</a>` : code;
|
|
116
|
+
})
|
|
117
|
+
.join(' | ');
|
|
118
|
+
const renderPropertyHtml = (prop, heading, anchor) => {
|
|
188
119
|
const rows = [];
|
|
189
|
-
rows.push(`<li><strong>Type:</strong>
|
|
120
|
+
rows.push(`<li><strong>Type:</strong> ${renderType(prop.type, anchor)}</li>`);
|
|
190
121
|
rows.push(`<li><strong>Required:</strong> <code>${String(prop.required)}</code></li>`);
|
|
191
122
|
if (prop.itemType) {
|
|
192
|
-
rows.push(`<li><strong>Item Type:</strong>
|
|
123
|
+
rows.push(`<li><strong>Item Type:</strong> ${renderType(prop.itemType, anchor)}</li>`);
|
|
193
124
|
}
|
|
194
125
|
if (prop.description) {
|
|
195
126
|
rows.push(`<li><strong>Description:</strong> ${escapeHtml(prop.description)}</li>`);
|
|
@@ -214,36 +145,40 @@ const renderPropertyHtml = (prop) => {
|
|
|
214
145
|
rows.push(`<li><strong>Constraints:</strong> ${constraints.join(', ')}</li>`);
|
|
215
146
|
}
|
|
216
147
|
rows.push(`<li><strong>Default:</strong> ${prop.defaultValue !== undefined ? formatValue(prop.defaultValue) : '<em>none</em>'}</li>`);
|
|
217
|
-
if (prop.examples && prop.examples.length > 0) {
|
|
218
|
-
rows.push(`<li><strong>Examples:</strong> ${prop.examples
|
|
219
|
-
.map((v) => `<code>${escapeHtml(formatInline(v))}</code>`)
|
|
220
|
-
.join(', ')}</li>`);
|
|
221
|
-
}
|
|
222
|
-
if (prop.notes) {
|
|
223
|
-
rows.push(`<li><strong>Notes:</strong> ${escapeHtml(prop.notes)}</li>`);
|
|
224
|
-
}
|
|
225
148
|
return `
|
|
226
149
|
<section class="doc-property">
|
|
227
|
-
|
|
150
|
+
<${heading}><code>${escapeHtml(prop.path)}</code></${heading}>
|
|
228
151
|
<ul>
|
|
229
152
|
${rows.join('\n ')}
|
|
230
153
|
</ul>
|
|
231
154
|
</section>`;
|
|
232
155
|
};
|
|
233
|
-
const renderPropertiesSectionHtml = (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
156
|
+
const renderPropertiesSectionHtml = (opts) => {
|
|
157
|
+
const sources = [
|
|
158
|
+
opts.schema && `<strong>Schema:</strong> <code>${escapeHtml(opts.schema)}</code>`,
|
|
159
|
+
opts.defaults && `<strong>Defaults:</strong> <code>${escapeHtml(opts.defaults)}</code>`,
|
|
160
|
+
].filter(Boolean);
|
|
161
|
+
// Each schema carries its own definitions, so anchors are scoped to the section.
|
|
162
|
+
const prefix = opts.title.toLowerCase();
|
|
163
|
+
const names = new Set(opts.docs.definitions.map((definition) => definition.name));
|
|
164
|
+
const anchor = (name) => names.has(name) ? `${prefix}-${encodeURIComponent(name)}` : undefined;
|
|
165
|
+
const body = opts.docs.properties.length > 0
|
|
166
|
+
? opts.docs.properties.map((prop) => renderPropertyHtml(prop, 'h3', anchor)).join('\n')
|
|
167
|
+
: '<p>No properties found.</p>';
|
|
168
|
+
const definitions = opts.docs.definitions
|
|
169
|
+
.map((definition) => `
|
|
170
|
+
<section class="doc-definition" id="${anchor(definition.name)}">
|
|
171
|
+
<h3>Type <code>${escapeHtml(definition.name)}</code></h3>
|
|
172
|
+
${definition.description ? `<p>${escapeHtml(definition.description)}</p>` : ''}
|
|
173
|
+
${definition.properties.map((prop) => renderPropertyHtml(prop, 'h4', anchor)).join('\n')}
|
|
174
|
+
</section>`)
|
|
175
|
+
.join('\n');
|
|
242
176
|
return `
|
|
243
177
|
<section class="doc-section">
|
|
244
|
-
<h2>${escapeHtml(title)}</h2>
|
|
245
|
-
|
|
246
|
-
${
|
|
178
|
+
<h2>${escapeHtml(opts.title)}</h2>
|
|
179
|
+
${sources.length > 0 ? `<p>${sources.join(' · ')}</p>` : ''}
|
|
180
|
+
${body}
|
|
181
|
+
${definitions}
|
|
247
182
|
</section>`;
|
|
248
183
|
};
|
|
249
184
|
const wrapHtmlPage = (title, body) => `
|
|
@@ -261,6 +196,7 @@ const wrapHtmlPage = (title, body) => `
|
|
|
261
196
|
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
262
197
|
.doc-section { margin-bottom: 2rem; }
|
|
263
198
|
.doc-property { margin: 0 0 1.25rem 0; padding-bottom: 0.5rem; border-bottom: 1px solid rgba(128,128,128,.25); }
|
|
199
|
+
.doc-definition { margin: 1.5rem 0 0 0; padding-left: 1rem; border-left: 3px solid rgba(128,128,128,.35); }
|
|
264
200
|
ul { padding-left: 1.25rem; }
|
|
265
201
|
.meta-list li { margin-bottom: .25rem; }
|
|
266
202
|
</style>
|
|
@@ -300,10 +236,28 @@ const renderElementIndexHtml = (opts) => wrapHtmlPage(`${opts.contract.elementNa
|
|
|
300
236
|
</ul>
|
|
301
237
|
</section>
|
|
302
238
|
`);
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
${renderPropertiesSectionHtml(
|
|
239
|
+
const schemaSpecifier = (contract, kind) => `${contract.packageName}/schemas/${kind}.schema.json`;
|
|
240
|
+
const renderViewHtml = (opts) => wrapHtmlPage(`${opts.contract.elementName} ${opts.view.view} docs`, `
|
|
241
|
+
<h1>${escapeHtml(opts.contract.elementName)} / ${escapeHtml(opts.view.view)}</h1>
|
|
242
|
+
${renderPropertiesSectionHtml({
|
|
243
|
+
title: 'Model',
|
|
244
|
+
schema: schemaSpecifier(opts.contract, 'model'),
|
|
245
|
+
defaults: opts.view.pieSource,
|
|
246
|
+
docs: opts.view.pieDocs,
|
|
247
|
+
})}
|
|
248
|
+
${opts.kinds.includes('configuration')
|
|
249
|
+
? renderPropertiesSectionHtml({
|
|
250
|
+
title: 'Configuration',
|
|
251
|
+
schema: schemaSpecifier(opts.contract, 'configuration'),
|
|
252
|
+
defaults: opts.view.configSource,
|
|
253
|
+
docs: opts.view.configDocs,
|
|
254
|
+
})
|
|
255
|
+
: ''}
|
|
256
|
+
${renderPropertiesSectionHtml({
|
|
257
|
+
title: 'Session',
|
|
258
|
+
schema: schemaSpecifier(opts.contract, 'session'),
|
|
259
|
+
docs: opts.sessionDocs,
|
|
260
|
+
})}
|
|
307
261
|
`);
|
|
308
262
|
const chooseViewDocs = (resolvedViews) => {
|
|
309
263
|
const map = new Map(resolvedViews.map((v) => [v.view, v]));
|
|
@@ -319,9 +273,9 @@ const chooseViewDocs = (resolvedViews) => {
|
|
|
319
273
|
}
|
|
320
274
|
if (print) {
|
|
321
275
|
const deliveryFingerprint = delivery
|
|
322
|
-
? `${propertyFingerprint(delivery.
|
|
276
|
+
? `${propertyFingerprint(delivery.pieDocs.properties)}|${propertyFingerprint(delivery.configDocs.properties)}`
|
|
323
277
|
: '';
|
|
324
|
-
const printFingerprint = `${propertyFingerprint(print.
|
|
278
|
+
const printFingerprint = `${propertyFingerprint(print.pieDocs.properties)}|${propertyFingerprint(print.configDocs.properties)}`;
|
|
325
279
|
const sourceDiffFromDelivery = delivery
|
|
326
280
|
? print.pieSource !== delivery.pieSource || print.configSource !== delivery.configSource
|
|
327
281
|
: true;
|
|
@@ -355,16 +309,22 @@ export const generateDocsForContract = async (packageDir, contract, options) =>
|
|
|
355
309
|
views: [],
|
|
356
310
|
sourcesUsed: [],
|
|
357
311
|
};
|
|
312
|
+
// Property rows come from the schemas the package publishes, generated here from source so the
|
|
313
|
+
// docs do not depend on the element having been built.
|
|
314
|
+
const schemas = generateElementSchemas(packageDir);
|
|
315
|
+
if (!schemas.model || !schemas.session) {
|
|
316
|
+
throw new Error(`${contract.packageName} publishes no model or session schema to document`);
|
|
317
|
+
}
|
|
318
|
+
const kinds = Object.keys(schemas);
|
|
319
|
+
const sessionDocs = schemaDocs(schemas.session);
|
|
358
320
|
const resolvedViews = [];
|
|
359
321
|
for (const view of contract.views) {
|
|
360
322
|
const pieResolved = await loadValueWithFallback(packageDir, view.pie, 'pie');
|
|
361
323
|
const configResolved = await loadValueWithFallback(packageDir, view.config, 'config');
|
|
362
|
-
const pieProperties = applyMetadata(flattenProperties(pieResolved.value).sort((a, b) => a.path.localeCompare(b.path)), view.metadata?.pie);
|
|
363
|
-
const configProperties = applyMetadata(flattenProperties(configResolved.value).sort((a, b) => a.path.localeCompare(b.path)), view.metadata?.config);
|
|
364
324
|
resolvedViews.push({
|
|
365
325
|
view: view.view,
|
|
366
|
-
|
|
367
|
-
|
|
326
|
+
pieDocs: schemaDocs(schemas.model, pieResolved.value),
|
|
327
|
+
configDocs: schemaDocs(schemas.configuration, configResolved.value),
|
|
368
328
|
pieSource: pieResolved.sourceLabel,
|
|
369
329
|
configSource: configResolved.sourceLabel,
|
|
370
330
|
});
|
|
@@ -390,8 +350,8 @@ export const generateDocsForContract = async (packageDir, contract, options) =>
|
|
|
390
350
|
file: `${view.view}.html`,
|
|
391
351
|
pieSource: view.pieSource,
|
|
392
352
|
configSource: view.configSource,
|
|
393
|
-
hasModelDefaults: view.
|
|
394
|
-
hasConfigDefaults: view.
|
|
353
|
+
hasModelDefaults: view.pieDocs.properties.length > 0,
|
|
354
|
+
hasConfigDefaults: view.configDocs.properties.length > 0,
|
|
395
355
|
})),
|
|
396
356
|
};
|
|
397
357
|
const candidates = [
|
|
@@ -402,14 +362,7 @@ export const generateDocsForContract = async (packageDir, contract, options) =>
|
|
|
402
362
|
},
|
|
403
363
|
...emittedViews.map((view) => ({
|
|
404
364
|
path: join(elementOutputDir, `${view.view}.html`),
|
|
405
|
-
content: renderViewHtml({
|
|
406
|
-
contract,
|
|
407
|
-
view: view.view,
|
|
408
|
-
pieSource: view.pieSource,
|
|
409
|
-
configSource: view.configSource,
|
|
410
|
-
pieProperties: view.pieProperties,
|
|
411
|
-
configProperties: view.configProperties,
|
|
412
|
-
}),
|
|
365
|
+
content: renderViewHtml({ contract, view, kinds, sessionDocs }),
|
|
413
366
|
})),
|
|
414
367
|
];
|
|
415
368
|
for (const candidate of candidates) {
|
|
@@ -434,8 +387,8 @@ export const generateDocsForContract = async (packageDir, contract, options) =>
|
|
|
434
387
|
sourcesUsed.add(view.configSource);
|
|
435
388
|
outputs.views.push({
|
|
436
389
|
view: view.view,
|
|
437
|
-
pieProperties: view.
|
|
438
|
-
configProperties: view.
|
|
390
|
+
pieProperties: view.pieDocs.properties,
|
|
391
|
+
configProperties: view.configDocs.properties,
|
|
439
392
|
});
|
|
440
393
|
}
|
|
441
394
|
outputs.sourcesUsed = Array.from(sourcesUsed).sort();
|