Skip to content

Commit 5dfdcff

Browse files
committed
improve related docs, separate contractRegistry
1 parent 3533708 commit 5dfdcff

File tree

2 files changed

+219
-170
lines changed

2 files changed

+219
-170
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/**
2+
* Contract Registry System
3+
*
4+
* Tracks all contracts (modules and facets) for relationship detection
5+
* and cross-reference generation in documentation.
6+
*
7+
* Features:
8+
* - Register contracts with metadata (name, type, category, path)
9+
* - Find related contracts (module/facet pairs, same category, extensions)
10+
* - Enrich documentation data with relationship information
11+
*/
12+
13+
// ============================================================================
14+
// Registry State
15+
// ============================================================================
16+
17+
/**
18+
* Global registry to track all contracts for relationship detection
19+
* This allows us to find related contracts and generate cross-references
20+
*/
21+
const contractRegistry = {
22+
byName: new Map(),
23+
byCategory: new Map(),
24+
byType: { modules: [], facets: [] }
25+
};
26+
27+
// ============================================================================
28+
// Registry Management
29+
// ============================================================================
30+
31+
/**
32+
* Register a contract in the global registry
33+
* @param {object} contractData - Contract documentation data
34+
* @param {object} outputPath - Output path information from getOutputPath
35+
* @returns {object} Registered contract entry
36+
*/
37+
function registerContract(contractData, outputPath) {
38+
// Construct full path including filename (without .mdx extension)
39+
// This ensures RelatedDocs links point to the actual page, not the category index
40+
const fullPath = outputPath.relativePath
41+
? `${outputPath.relativePath}/${outputPath.fileName}`
42+
: outputPath.fileName;
43+
44+
const entry = {
45+
name: contractData.title,
46+
type: contractData.contractType, // 'module' or 'facet'
47+
category: outputPath.category,
48+
path: fullPath,
49+
sourcePath: contractData.sourceFilePath,
50+
functions: contractData.functions || [],
51+
storagePosition: contractData.storageInfo?.storagePosition
52+
};
53+
54+
contractRegistry.byName.set(contractData.title, entry);
55+
56+
if (!contractRegistry.byCategory.has(outputPath.category)) {
57+
contractRegistry.byCategory.set(outputPath.category, []);
58+
}
59+
contractRegistry.byCategory.get(outputPath.category).push(entry);
60+
61+
if (contractData.contractType === 'module') {
62+
contractRegistry.byType.modules.push(entry);
63+
} else {
64+
contractRegistry.byType.facets.push(entry);
65+
}
66+
67+
return entry;
68+
}
69+
70+
/**
71+
* Get the contract registry
72+
* @returns {object} The contract registry
73+
*/
74+
function getContractRegistry() {
75+
return contractRegistry;
76+
}
77+
78+
/**
79+
* Clear the contract registry (useful for testing or reset)
80+
*/
81+
function clearContractRegistry() {
82+
contractRegistry.byName.clear();
83+
contractRegistry.byCategory.clear();
84+
contractRegistry.byType.modules = [];
85+
contractRegistry.byType.facets = [];
86+
}
87+
88+
// ============================================================================
89+
// Relationship Detection
90+
// ============================================================================
91+
92+
/**
93+
* Find related contracts for a given contract
94+
* @param {string} contractName - Name of the contract
95+
* @param {string} contractType - Type of contract ('module' or 'facet')
96+
* @param {string} category - Category of the contract
97+
* @param {object} registry - Contract registry (optional, uses global if not provided)
98+
* @returns {Array} Array of related contract objects with title, href, description, icon
99+
*/
100+
function findRelatedContracts(contractName, contractType, category, registry = null) {
101+
const reg = registry || contractRegistry;
102+
const related = [];
103+
const contract = reg.byName.get(contractName);
104+
if (!contract) return related;
105+
106+
// 1. Find corresponding module/facet pair
107+
if (contractType === 'facet') {
108+
const moduleName = contractName.replace('Facet', 'Mod');
109+
const module = reg.byName.get(moduleName);
110+
if (module) {
111+
related.push({
112+
title: moduleName,
113+
href: `/docs/library/${module.path}`,
114+
description: `Module used by ${contractName}`,
115+
icon: '📦'
116+
});
117+
}
118+
} else if (contractType === 'module') {
119+
const facetName = contractName.replace('Mod', 'Facet');
120+
const facet = reg.byName.get(facetName);
121+
if (facet) {
122+
related.push({
123+
title: facetName,
124+
href: `/docs/library/${facet.path}`,
125+
description: `Facet using ${contractName}`,
126+
icon: '💎'
127+
});
128+
}
129+
}
130+
131+
// 2. Find related contracts in same category (excluding self)
132+
const sameCategory = reg.byCategory.get(category) || [];
133+
sameCategory.forEach(c => {
134+
if (c.name !== contractName && c.type === contractType) {
135+
related.push({
136+
title: c.name,
137+
href: `/docs/library/${c.path}`,
138+
description: `Related ${contractType} in ${category}`,
139+
icon: contractType === 'module' ? '📦' : '💎'
140+
});
141+
}
142+
});
143+
144+
// 3. Find extension contracts (e.g., ERC20Facet → ERC20BurnFacet)
145+
if (contractType === 'facet') {
146+
const baseName = contractName.replace(/BurnFacet$|PermitFacet$|BridgeableFacet$|EnumerableFacet$/, 'Facet');
147+
if (baseName !== contractName) {
148+
const base = reg.byName.get(baseName);
149+
if (base) {
150+
related.push({
151+
title: baseName,
152+
href: `/docs/library/${base.path}`,
153+
description: `Base facet for ${contractName}`,
154+
icon: '💎'
155+
});
156+
}
157+
}
158+
}
159+
160+
// 4. Find core dependencies (e.g., all facets depend on DiamondCutFacet)
161+
if (contractType === 'facet' && contractName !== 'DiamondCutFacet') {
162+
const diamondCut = reg.byName.get('DiamondCutFacet');
163+
if (diamondCut) {
164+
related.push({
165+
title: 'DiamondCutFacet',
166+
href: `/docs/library/${diamondCut.path}`,
167+
description: 'Required for adding facets to diamonds',
168+
icon: '🔧'
169+
});
170+
}
171+
}
172+
173+
return related.slice(0, 6); // Limit to 6 related items
174+
}
175+
176+
/**
177+
* Enrich contract data with relationship information
178+
* @param {object} data - Contract documentation data
179+
* @param {object} pathInfo - Output path information
180+
* @param {object} registry - Contract registry (optional, uses global if not provided)
181+
* @returns {object} Enriched data with relatedDocs property
182+
*/
183+
function enrichWithRelationships(data, pathInfo, registry = null) {
184+
const relatedDocs = findRelatedContracts(
185+
data.title,
186+
data.contractType,
187+
pathInfo.category,
188+
registry
189+
);
190+
191+
return {
192+
...data,
193+
relatedDocs: relatedDocs.length > 0 ? relatedDocs : null
194+
};
195+
}
196+
197+
// ============================================================================
198+
// Exports
199+
// ============================================================================
200+
201+
module.exports = {
202+
// Registry management
203+
registerContract,
204+
getContractRegistry,
205+
clearContractRegistry,
206+
207+
// Relationship detection
208+
findRelatedContracts,
209+
enrichWithRelationships,
210+
};
211+

0 commit comments

Comments
 (0)