@metamask-previews/platform-api-docs 0.0.0-preview-1275d0fda
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/CHANGELOG.md +14 -0
- package/LICENSE +6 -0
- package/LICENSE.APACHE2 +201 -0
- package/LICENSE.MIT +21 -0
- package/README.md +39 -0
- package/dist/cli.cjs +214 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +3 -0
- package/dist/cli.d.cts.map +1 -0
- package/dist/cli.d.mts +3 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +212 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/discovery.cjs +53 -0
- package/dist/discovery.cjs.map +1 -0
- package/dist/discovery.d.cts +22 -0
- package/dist/discovery.d.cts.map +1 -0
- package/dist/discovery.d.mts +22 -0
- package/dist/discovery.d.mts.map +1 -0
- package/dist/discovery.mjs +48 -0
- package/dist/discovery.mjs.map +1 -0
- package/dist/extraction.cjs +745 -0
- package/dist/extraction.cjs.map +1 -0
- package/dist/extraction.d.cts +40 -0
- package/dist/extraction.d.cts.map +1 -0
- package/dist/extraction.d.mts +40 -0
- package/dist/extraction.d.mts.map +1 -0
- package/dist/extraction.mjs +716 -0
- package/dist/extraction.mjs.map +1 -0
- package/dist/generate.cjs +373 -0
- package/dist/generate.cjs.map +1 -0
- package/dist/generate.d.cts +37 -0
- package/dist/generate.d.cts.map +1 -0
- package/dist/generate.d.mts +37 -0
- package/dist/generate.d.mts.map +1 -0
- package/dist/generate.mjs +346 -0
- package/dist/generate.mjs.map +1 -0
- package/dist/markdown.cjs +239 -0
- package/dist/markdown.cjs.map +1 -0
- package/dist/markdown.d.cts +52 -0
- package/dist/markdown.d.cts.map +1 -0
- package/dist/markdown.d.mts +52 -0
- package/dist/markdown.d.mts.map +1 -0
- package/dist/markdown.mjs +232 -0
- package/dist/markdown.mjs.map +1 -0
- package/dist/types.cjs +3 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +63 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +63 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +2 -0
- package/dist/types.mjs.map +1 -0
- package/package.json +75 -0
- package/site/docusaurus.config.ts +103 -0
- package/site/src/css/custom.css +336 -0
- package/site/static/fonts/MM-Sans/MM_Sans_Mono-Regular.woff2 +0 -0
- package/site/static/img/favicons/favicon-96x96.png +0 -0
- package/site/static/img/metamask-fox.svg +12 -0
- package/site/static/img/metamask-logo-dark.svg +3 -0
- package/site/static/img/metamask-logo.svg +3 -0
- package/site/tsconfig.json +13 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { directoryExists } from "@metamask/utils/node";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import * as fs from "node:fs/promises";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { findDtsFiles, findTsFiles } from "./discovery.mjs";
|
|
7
|
+
import { createExtractionProject, extractFromSourceFile } from "./extraction.mjs";
|
|
8
|
+
import { generateIndexPage, generateNamespacePage, generateSidebars } from "./markdown.mjs";
|
|
9
|
+
/**
|
|
10
|
+
* Compute a deduplication score for a messenger item, preferring items with
|
|
11
|
+
* JSDoc and from the "home" package whose name matches the namespace.
|
|
12
|
+
*
|
|
13
|
+
* @param item - The messenger item to score.
|
|
14
|
+
* @returns A numeric score (higher is better).
|
|
15
|
+
*/
|
|
16
|
+
function deduplicationScore(item) {
|
|
17
|
+
const jsDocScore = item.jsDoc ? 2 : 0;
|
|
18
|
+
const namespacePrefix = item.typeString
|
|
19
|
+
.split(':')[0]
|
|
20
|
+
.replace(/(?:Controller|Service)$/u, '')
|
|
21
|
+
.toLowerCase();
|
|
22
|
+
const homeScore = namespacePrefix.length > 0 &&
|
|
23
|
+
item.sourceFile.toLowerCase().includes(namespacePrefix)
|
|
24
|
+
? 1
|
|
25
|
+
: 0;
|
|
26
|
+
return jsDocScore + homeScore;
|
|
27
|
+
}
|
|
28
|
+
const execFileAsync = promisify(execFile);
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the default branch of a project's `origin` remote by reading the
|
|
31
|
+
* symbolic ref `refs/remotes/origin/HEAD`. Falls back to `main` if the
|
|
32
|
+
* symbolic ref isn't set (e.g. in shallow CI clones).
|
|
33
|
+
*
|
|
34
|
+
* @param projectPath - Absolute path to the project root.
|
|
35
|
+
* @returns The default branch name (e.g. "main", "master", "develop").
|
|
36
|
+
*/
|
|
37
|
+
async function resolveDefaultBranch(projectPath) {
|
|
38
|
+
try {
|
|
39
|
+
const { stdout } = await execFileAsync('git', ['symbolic-ref', '--short', 'refs/remotes/origin/HEAD'], { cwd: projectPath });
|
|
40
|
+
// stdout looks like "origin/main"; strip the leading "origin/".
|
|
41
|
+
const trimmed = stdout.trim();
|
|
42
|
+
const slash = trimmed.indexOf('/');
|
|
43
|
+
return slash === -1
|
|
44
|
+
? // istanbul ignore next: defensive — `symbolic-ref --short` always
|
|
45
|
+
// returns `origin/<branch>` when the symbolic ref is set; this
|
|
46
|
+
// fallback only matters if git's output format ever changes.
|
|
47
|
+
trimmed || 'main'
|
|
48
|
+
: trimmed.slice(slash + 1);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return 'main';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the GitHub blob base URL for a project by reading its git remote
|
|
56
|
+
* and default branch.
|
|
57
|
+
*
|
|
58
|
+
* @param projectPath - Absolute path to the project root.
|
|
59
|
+
* @returns A base URL like "https://github.com/Owner/Repo/blob/<branch>/" or null.
|
|
60
|
+
*/
|
|
61
|
+
async function resolveRepoBaseUrl(projectPath) {
|
|
62
|
+
try {
|
|
63
|
+
const { stdout: remoteRaw } = await execFileAsync('git', ['remote', 'get-url', 'origin'], { cwd: projectPath });
|
|
64
|
+
const remote = remoteRaw.trim();
|
|
65
|
+
// Parse owner/repo from SSH or HTTPS remote URLs
|
|
66
|
+
// Handles aliases like github.com-Org used in SSH configs
|
|
67
|
+
const match = remote.match(/github\.com[^:/]*[:/]([^/]+\/[^/]+?)(?:\.git)?$/u);
|
|
68
|
+
if (!match) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const branch = await resolveDefaultBranch(projectPath);
|
|
72
|
+
return `https://github.com/${match[1]}/blob/${branch}/`;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Discover which configured source locations actually exist on disk.
|
|
80
|
+
*
|
|
81
|
+
* @param projectPath - The project root path.
|
|
82
|
+
* @param scanDirs - User-configured scan directories relative to projectPath.
|
|
83
|
+
* @returns A ScanSources object describing the locations to scan.
|
|
84
|
+
*/
|
|
85
|
+
async function discoverScanSources(projectPath, scanDirs) {
|
|
86
|
+
const existingScanDirs = [];
|
|
87
|
+
for (const dir of scanDirs) {
|
|
88
|
+
if (await directoryExists(path.join(projectPath, dir))) {
|
|
89
|
+
existingScanDirs.push(dir);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const packagesDir = path.join(projectPath, 'packages');
|
|
93
|
+
const nodeModulesDir = path.join(projectPath, 'node_modules', '@metamask');
|
|
94
|
+
return {
|
|
95
|
+
scanDirs: existingScanDirs,
|
|
96
|
+
packagesDir: (await directoryExists(packagesDir)) ? packagesDir : null,
|
|
97
|
+
nodeModulesDir: (await directoryExists(nodeModulesDir))
|
|
98
|
+
? nodeModulesDir
|
|
99
|
+
: null,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Log a human-readable description of which source locations will be scanned.
|
|
104
|
+
*
|
|
105
|
+
* @param sources - The resolved scan sources.
|
|
106
|
+
*/
|
|
107
|
+
function logScanPlan(sources) {
|
|
108
|
+
const summary = [];
|
|
109
|
+
for (const dir of sources.scanDirs) {
|
|
110
|
+
summary.push(`${dir}/ (.ts)`);
|
|
111
|
+
}
|
|
112
|
+
if (sources.packagesDir) {
|
|
113
|
+
summary.push('packages/*/src (.ts)');
|
|
114
|
+
}
|
|
115
|
+
if (sources.nodeModulesDir) {
|
|
116
|
+
summary.push('node_modules/@metamask/*/dist (.d.cts)');
|
|
117
|
+
}
|
|
118
|
+
console.log(`Scanning ${summary.join(', ')} for Messenger action/event types...`);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Run extraction against every file in a single directory, logging and
|
|
122
|
+
* swallowing per-file failures. All files are added to the shared `project`
|
|
123
|
+
* up front so the type checker can resolve cross-file references when the
|
|
124
|
+
* walker descends into imported types.
|
|
125
|
+
*
|
|
126
|
+
* @param project - The shared ts-morph project.
|
|
127
|
+
* @param directory - The directory to scan.
|
|
128
|
+
* @param projectPath - The project root, used for relative path display.
|
|
129
|
+
* @param findFiles - The function used to enumerate files in the directory.
|
|
130
|
+
* @returns The list of extracted messenger items.
|
|
131
|
+
*/
|
|
132
|
+
async function extractFromDirectory(project, directory, projectPath, findFiles) {
|
|
133
|
+
const items = [];
|
|
134
|
+
const files = await findFiles(directory);
|
|
135
|
+
for (const file of files) {
|
|
136
|
+
try {
|
|
137
|
+
const sourceFile = project.getSourceFile(file) ?? project.addSourceFileAtPath(file);
|
|
138
|
+
items.push(...extractFromSourceFile(sourceFile, projectPath));
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
console.warn(`Warning: failed to parse ${path.relative(projectPath, file)}`);
|
|
142
|
+
console.warn(error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return items;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Enumerate the subdirectories of a parent directory that match the expected
|
|
149
|
+
* layout (e.g., `packages/*/src` or `node_modules/@metamask/*/dist`), keeping
|
|
150
|
+
* only those that actually exist.
|
|
151
|
+
*
|
|
152
|
+
* @param parentDir - The parent directory to enumerate.
|
|
153
|
+
* @param subPath - The trailing path component appended to each entry.
|
|
154
|
+
* @param includeSymlinks - Whether to include symbolic links (true for
|
|
155
|
+
* node_modules where workspaces are symlinked).
|
|
156
|
+
* @returns The list of absolute paths to existing target subdirectories.
|
|
157
|
+
*/
|
|
158
|
+
async function listTargetSubdirectories(parentDir, subPath, includeSymlinks) {
|
|
159
|
+
const entries = await fs.readdir(parentDir, { withFileTypes: true });
|
|
160
|
+
const candidates = entries
|
|
161
|
+
.filter((entry) => entry.isDirectory() || (includeSymlinks && entry.isSymbolicLink()))
|
|
162
|
+
.map((entry) => path.join(parentDir, entry.name, subPath));
|
|
163
|
+
const existing = [];
|
|
164
|
+
for (const candidate of candidates) {
|
|
165
|
+
if (await directoryExists(candidate)) {
|
|
166
|
+
existing.push(candidate);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return existing;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Scan every source location described by `sources` and return all extracted
|
|
173
|
+
* messenger items. A single ts-morph Project is shared across every file so
|
|
174
|
+
* the type checker can resolve cross-file references (e.g. a `*Messenger`
|
|
175
|
+
* declaration in one file walking through an imported umbrella union into
|
|
176
|
+
* an auto-generated `*-method-action-types.ts` sibling).
|
|
177
|
+
*
|
|
178
|
+
* @param projectPath - The project root path.
|
|
179
|
+
* @param sources - The set of source locations to scan.
|
|
180
|
+
* @returns A flat list of all extracted messenger items.
|
|
181
|
+
*/
|
|
182
|
+
async function scanSources(projectPath, sources) {
|
|
183
|
+
const project = createExtractionProject();
|
|
184
|
+
const allItems = [];
|
|
185
|
+
for (const dir of sources.scanDirs) {
|
|
186
|
+
allItems.push(...(await extractFromDirectory(project, path.join(projectPath, dir), projectPath, findTsFiles)));
|
|
187
|
+
}
|
|
188
|
+
if (sources.packagesDir) {
|
|
189
|
+
const srcDirs = await listTargetSubdirectories(sources.packagesDir, 'src', false);
|
|
190
|
+
for (const srcDir of srcDirs) {
|
|
191
|
+
allItems.push(...(await extractFromDirectory(project, srcDir, projectPath, findTsFiles)));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (sources.nodeModulesDir) {
|
|
195
|
+
const distDirs = await listTargetSubdirectories(sources.nodeModulesDir, 'dist', true);
|
|
196
|
+
for (const distDir of distDirs) {
|
|
197
|
+
allItems.push(...(await extractFromDirectory(project, distDir, projectPath, findDtsFiles)));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return allItems;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Replace a previously-seen item in its existing namespace group with a
|
|
204
|
+
* higher-scoring duplicate. Handles the case where the duplicate is a
|
|
205
|
+
* different kind (action vs event) by moving it between lists.
|
|
206
|
+
*
|
|
207
|
+
* @param byNamespace - Map of namespace to its group.
|
|
208
|
+
* @param previous - The previously stored item.
|
|
209
|
+
* @param replacement - The new item to replace it with.
|
|
210
|
+
*/
|
|
211
|
+
function replaceDuplicateInGroup(byNamespace, previous, replacement) {
|
|
212
|
+
const namespace = replacement.typeString.split(':')[0];
|
|
213
|
+
const group = byNamespace.get(namespace);
|
|
214
|
+
// istanbul ignore next: `previous` and `replacement` have the same
|
|
215
|
+
// typeString, so they share a namespace, and we always insert the
|
|
216
|
+
// namespace into `byNamespace` before recording the original entry.
|
|
217
|
+
if (!group) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const previousList = previous.kind === 'action' ? group.actions : group.events;
|
|
221
|
+
const index = previousList.indexOf(previous);
|
|
222
|
+
// istanbul ignore next: `previous` was added to its kind's list by
|
|
223
|
+
// `groupByNamespace` before being recorded in `seen`, so it is always
|
|
224
|
+
// present when we look it up here.
|
|
225
|
+
if (index === -1) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (previous.kind === replacement.kind) {
|
|
229
|
+
previousList[index] = replacement;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
previousList.splice(index, 1);
|
|
233
|
+
const newList = replacement.kind === 'action' ? group.actions : group.events;
|
|
234
|
+
newList.push(replacement);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Group items by namespace, deduplicating duplicate typeStrings using
|
|
239
|
+
* `deduplicationScore`. Returns groups sorted alphabetically by namespace,
|
|
240
|
+
* with each group's items sorted alphabetically by typeString.
|
|
241
|
+
*
|
|
242
|
+
* @param items - The full list of extracted items.
|
|
243
|
+
* @returns The deduplicated and sorted namespace groups.
|
|
244
|
+
*/
|
|
245
|
+
function groupByNamespace(items) {
|
|
246
|
+
const byNamespace = new Map();
|
|
247
|
+
const seen = new Map();
|
|
248
|
+
for (const item of items) {
|
|
249
|
+
const existing = seen.get(item.typeString);
|
|
250
|
+
if (existing) {
|
|
251
|
+
if (deduplicationScore(item) <= deduplicationScore(existing)) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
replaceDuplicateInGroup(byNamespace, existing, item);
|
|
255
|
+
seen.set(item.typeString, item);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
seen.set(item.typeString, item);
|
|
259
|
+
const namespace = item.typeString.split(':')[0];
|
|
260
|
+
let group = byNamespace.get(namespace);
|
|
261
|
+
if (!group) {
|
|
262
|
+
group = { namespace, actions: [], events: [] };
|
|
263
|
+
byNamespace.set(namespace, group);
|
|
264
|
+
}
|
|
265
|
+
if (item.kind === 'action') {
|
|
266
|
+
group.actions.push(item);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
group.events.push(item);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const namespaces = Array.from(byNamespace.values()).sort((a, b) => a.namespace.localeCompare(b.namespace));
|
|
273
|
+
for (const ns of namespaces) {
|
|
274
|
+
ns.actions.sort((a, b) => a.typeString.localeCompare(b.typeString));
|
|
275
|
+
ns.events.sort((a, b) => a.typeString.localeCompare(b.typeString));
|
|
276
|
+
}
|
|
277
|
+
return namespaces;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Write generated docs (namespace pages, index page, sidebars) to disk,
|
|
281
|
+
* replacing any existing `docs/` directory.
|
|
282
|
+
*
|
|
283
|
+
* @param namespaces - The grouped namespaces to render.
|
|
284
|
+
* @param outputDir - The root output directory.
|
|
285
|
+
* @param repoBaseUrl - GitHub blob base URL for source links, or null.
|
|
286
|
+
* @param indexOptions - Options stamped on the index page header.
|
|
287
|
+
* @param indexOptions.projectLabel - Short label identifying the project.
|
|
288
|
+
* @param indexOptions.commitSha - Git commit SHA the docs were generated from.
|
|
289
|
+
* @returns Promise that resolves once all files are written.
|
|
290
|
+
*/
|
|
291
|
+
async function writeOutput(namespaces, outputDir, repoBaseUrl, indexOptions) {
|
|
292
|
+
const docsDir = path.join(outputDir, 'docs');
|
|
293
|
+
if (await directoryExists(docsDir)) {
|
|
294
|
+
await fs.rm(docsDir, { recursive: true });
|
|
295
|
+
}
|
|
296
|
+
await fs.mkdir(docsDir, { recursive: true });
|
|
297
|
+
for (const ns of namespaces) {
|
|
298
|
+
const nsDir = path.join(docsDir, ns.namespace);
|
|
299
|
+
await fs.mkdir(nsDir, { recursive: true });
|
|
300
|
+
if (ns.actions.length > 0) {
|
|
301
|
+
await fs.writeFile(path.join(nsDir, 'actions.md'), generateNamespacePage(ns, 'action', repoBaseUrl));
|
|
302
|
+
}
|
|
303
|
+
if (ns.events.length > 0) {
|
|
304
|
+
await fs.writeFile(path.join(nsDir, 'events.md'), generateNamespacePage(ns, 'event', repoBaseUrl));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
await fs.writeFile(path.join(docsDir, 'index.md'), generateIndexPage(namespaces, indexOptions));
|
|
308
|
+
await fs.writeFile(path.join(outputDir, 'sidebars.ts'), generateSidebars(namespaces));
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Scan a project for messenger action/event types and generate documentation.
|
|
312
|
+
*
|
|
313
|
+
* @param options - Generation options.
|
|
314
|
+
* @returns A promise resolving to counts of generated namespaces, actions, and events.
|
|
315
|
+
*/
|
|
316
|
+
export async function generate(options) {
|
|
317
|
+
const { projectPath, outputDir, scanDirs, projectLabel, commitSha } = options;
|
|
318
|
+
const sources = await discoverScanSources(projectPath, scanDirs);
|
|
319
|
+
if (sources.scanDirs.length === 0 &&
|
|
320
|
+
!sources.packagesDir &&
|
|
321
|
+
!sources.nodeModulesDir) {
|
|
322
|
+
throw new Error(`No scannable directories found in ${projectPath}. ` +
|
|
323
|
+
`Looked for: ${scanDirs.join(', ')}, packages/, node_modules/@metamask/`);
|
|
324
|
+
}
|
|
325
|
+
logScanPlan(sources);
|
|
326
|
+
const allItems = await scanSources(projectPath, sources);
|
|
327
|
+
console.log(`Found ${allItems.length} messenger ${allItems.length === 1 ? 'item' : 'items'} total.`);
|
|
328
|
+
const namespaces = groupByNamespace(allItems);
|
|
329
|
+
const repoBaseUrl = await resolveRepoBaseUrl(projectPath);
|
|
330
|
+
await writeOutput(namespaces, outputDir, repoBaseUrl, {
|
|
331
|
+
projectLabel,
|
|
332
|
+
commitSha,
|
|
333
|
+
});
|
|
334
|
+
const totalActions = namespaces.reduce((sum, ns) => sum + ns.actions.length, 0);
|
|
335
|
+
const totalEvents = namespaces.reduce((sum, ns) => sum + ns.events.length, 0);
|
|
336
|
+
console.log(`Generated docs for ${namespaces.length} ${namespaces.length === 1 ? 'namespace' : 'namespaces'}.`);
|
|
337
|
+
console.log(` Actions: ${totalActions}`);
|
|
338
|
+
console.log(` Events: ${totalEvents}`);
|
|
339
|
+
console.log(`Output: ${path.join(outputDir, 'docs')}/`);
|
|
340
|
+
return {
|
|
341
|
+
namespaces: namespaces.length,
|
|
342
|
+
actions: totalActions,
|
|
343
|
+
events: totalEvents,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
//# sourceMappingURL=generate.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.mjs","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,6BAA6B;AACvD,OAAO,EAAE,QAAQ,EAAE,2BAA2B;AAC9C,OAAO,KAAK,EAAE,yBAAyB;AACvC,OAAO,KAAK,IAAI,kBAAkB;AAClC,OAAO,EAAE,SAAS,EAAE,kBAAkB;AAGtC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,wBAAoB;AACxD,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,yBAAqB;AAC9E,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EACjB,uBAAmB;AAGpB;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAC,IAAsC;IAChE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU;SACpC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SACb,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC;SACvC,WAAW,EAAE,CAAC;IACjB,MAAM,SAAS,GACb,eAAe,CAAC,MAAM,GAAG,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC;QACrD,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,CAAC,CAAC;IACR,OAAO,UAAU,GAAG,SAAS,CAAC;AAChC,CAAC;AAED,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C;;;;;;;GAOG;AACH,KAAK,UAAU,oBAAoB,CAAC,WAAmB;IACrD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CACpC,KAAK,EACL,CAAC,cAAc,EAAE,SAAS,EAAE,0BAA0B,CAAC,EACvD,EAAE,GAAG,EAAE,WAAW,EAAE,CACrB,CAAC;QACF,gEAAgE;QAChE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,OAAO,KAAK,KAAK,CAAC,CAAC;YACjB,CAAC,CAAC,kEAAkE;gBAClE,+DAA+D;gBAC/D,6DAA6D;gBAC7D,OAAO,IAAI,MAAM;YACnB,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,kBAAkB,CAAC,WAAmB;IACnD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,aAAa,CAC/C,KAAK,EACL,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,EAC/B,EAAE,GAAG,EAAE,WAAW,EAAE,CACrB,CAAC;QAEF,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;QAEhC,iDAAiD;QACjD,0DAA0D;QAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CACxB,kDAAkD,CACnD,CAAC;QACF,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,WAAW,CAAC,CAAC;QACvD,OAAO,sBAAsB,KAAK,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AA8CD;;;;;;GAMG;AACH,KAAK,UAAU,mBAAmB,CAChC,WAAmB,EACnB,QAAkB;IAElB,MAAM,gBAAgB,GAAa,EAAE,CAAC;IACtC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;YACvD,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACvD,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC;IAE3E,OAAO;QACL,QAAQ,EAAE,gBAAgB;QAC1B,WAAW,EAAE,CAAC,MAAM,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;QACtE,cAAc,EAAE,CAAC,MAAM,eAAe,CAAC,cAAc,CAAC,CAAC;YACrD,CAAC,CAAC,cAAc;YAChB,CAAC,CAAC,IAAI;KACT,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,OAAoB;IACvC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,CAAC,GAAG,CACT,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,sCAAsC,CACrE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,oBAAoB,CACjC,OAAgB,EAChB,SAAiB,EACjB,WAAmB,EACnB,SAA6C;IAE7C,MAAM,KAAK,GAAuC,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,UAAU,GACd,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACnE,KAAK,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CACV,4BAA4B,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAC/D,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,KAAK,UAAU,wBAAwB,CACrC,SAAiB,EACjB,OAAe,EACf,eAAwB;IAExB,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,MAAM,UAAU,GAAG,OAAO;SACvB,MAAM,CACL,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,eAAe,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC,CACrE;SACA,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAE7D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,MAAM,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;GAUG;AACH,KAAK,UAAU,WAAW,CACxB,WAAmB,EACnB,OAAoB;IAEpB,MAAM,OAAO,GAAG,uBAAuB,EAAE,CAAC;IAC1C,MAAM,QAAQ,GAAuC,EAAE,CAAC;IAExD,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACnC,QAAQ,CAAC,IAAI,CACX,GAAG,CAAC,MAAM,oBAAoB,CAC5B,OAAO,EACP,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,EAC3B,WAAW,EACX,WAAW,CACZ,CAAC,CACH,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,MAAM,wBAAwB,CAC5C,OAAO,CAAC,WAAW,EACnB,KAAK,EACL,KAAK,CACN,CAAC;QACF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,QAAQ,CAAC,IAAI,CACX,GAAG,CAAC,MAAM,oBAAoB,CAC5B,OAAO,EACP,MAAM,EACN,WAAW,EACX,WAAW,CACZ,CAAC,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,MAAM,wBAAwB,CAC7C,OAAO,CAAC,cAAc,EACtB,MAAM,EACN,IAAI,CACL,CAAC;QACF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CACX,GAAG,CAAC,MAAM,oBAAoB,CAC5B,OAAO,EACP,OAAO,EACP,WAAW,EACX,YAAY,CACb,CAAC,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,uBAAuB,CAC9B,WAAwC,EACxC,QAA0C,EAC1C,WAA6C;IAE7C,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACzC,mEAAmE;IACnE,kEAAkE;IAClE,oEAAoE;IACpE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IACD,MAAM,YAAY,GAChB,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;IAC5D,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7C,mEAAmE;IACnE,sEAAsE;IACtE,mCAAmC;IACnC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,EAAE,CAAC;QACvC,YAAY,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC;IACpC,CAAC;SAAM,CAAC;QACN,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,OAAO,GACX,WAAW,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,gBAAgB,CACvB,KAAyC;IAEzC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA0B,CAAC;IACtD,MAAM,IAAI,GAAG,IAAI,GAAG,EAA4C,CAAC;IAEjE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7D,SAAS;YACX,CAAC;YACD,uBAAuB,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YACrD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YAChC,SAAS;QACX,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,IAAI,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;YAC/C,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC3B,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAChE,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CACvC,CAAC;IAEF,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;QAC5B,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;QACpE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,WAAW,CACxB,UAA4B,EAC5B,SAAiB,EACjB,WAA0B,EAC1B,YAGC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAE7C,IAAI,MAAM,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;QACnC,MAAM,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;QAC/C,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE3C,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,EAC9B,qBAAqB,CAAC,EAAE,EAAE,QAAQ,EAAE,WAAW,CAAC,CACjD,CAAC;QACJ,CAAC;QAED,IAAI,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,EAC7B,qBAAqB,CAAC,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,CAChD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAC9B,iBAAiB,CAAC,UAAU,EAAE,YAAY,CAAC,CAC5C,CAAC;IAEF,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EACnC,gBAAgB,CAAC,UAAU,CAAC,CAC7B,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAwB;IAExB,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAE9E,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAEjE,IACE,OAAO,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAC7B,CAAC,OAAO,CAAC,WAAW;QACpB,CAAC,OAAO,CAAC,cAAc,EACvB,CAAC;QACD,MAAM,IAAI,KAAK,CACb,qCAAqC,WAAW,IAAI;YAClD,eAAe,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,sCAAsC,CAC3E,CAAC;IACJ,CAAC;IAED,WAAW,CAAC,OAAO,CAAC,CAAC;IAErB,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CACT,SAAS,QAAQ,CAAC,MAAM,cAAc,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,SAAS,CACxF,CAAC;IAEF,MAAM,UAAU,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAE1D,MAAM,WAAW,CAAC,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE;QACpD,YAAY;QACZ,SAAS;KACV,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,CACpC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,EACpC,CAAC,CACF,CAAC;IACF,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAE9E,OAAO,CAAC,GAAG,CACT,sBAAsB,UAAU,CAAC,MAAM,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,GAAG,CACnG,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,cAAc,YAAY,EAAE,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,CAAC,aAAa,WAAW,EAAE,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAExD,OAAO;QACL,UAAU,EAAE,UAAU,CAAC,MAAM;QAC7B,OAAO,EAAE,YAAY;QACrB,MAAM,EAAE,WAAW;KACpB,CAAC;AACJ,CAAC","sourcesContent":["import { directoryExists } from '@metamask/utils/node';\nimport { execFile } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { promisify } from 'node:util';\nimport type { Project } from 'ts-morph';\n\nimport { findDtsFiles, findTsFiles } from './discovery';\nimport { createExtractionProject, extractFromSourceFile } from './extraction';\nimport {\n generateIndexPage,\n generateNamespacePage,\n generateSidebars,\n} from './markdown';\nimport type { ExtractedMessengerCapabilityType, NamespaceGroup } from './types';\n\n/**\n * Compute a deduplication score for a messenger item, preferring items with\n * JSDoc and from the \"home\" package whose name matches the namespace.\n *\n * @param item - The messenger item to score.\n * @returns A numeric score (higher is better).\n */\nfunction deduplicationScore(item: ExtractedMessengerCapabilityType): number {\n const jsDocScore = item.jsDoc ? 2 : 0;\n const namespacePrefix = item.typeString\n .split(':')[0]\n .replace(/(?:Controller|Service)$/u, '')\n .toLowerCase();\n const homeScore =\n namespacePrefix.length > 0 &&\n item.sourceFile.toLowerCase().includes(namespacePrefix)\n ? 1\n : 0;\n return jsDocScore + homeScore;\n}\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Resolve the default branch of a project's `origin` remote by reading the\n * symbolic ref `refs/remotes/origin/HEAD`. Falls back to `main` if the\n * symbolic ref isn't set (e.g. in shallow CI clones).\n *\n * @param projectPath - Absolute path to the project root.\n * @returns The default branch name (e.g. \"main\", \"master\", \"develop\").\n */\nasync function resolveDefaultBranch(projectPath: string): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['symbolic-ref', '--short', 'refs/remotes/origin/HEAD'],\n { cwd: projectPath },\n );\n // stdout looks like \"origin/main\"; strip the leading \"origin/\".\n const trimmed = stdout.trim();\n const slash = trimmed.indexOf('/');\n return slash === -1\n ? // istanbul ignore next: defensive — `symbolic-ref --short` always\n // returns `origin/<branch>` when the symbolic ref is set; this\n // fallback only matters if git's output format ever changes.\n trimmed || 'main'\n : trimmed.slice(slash + 1);\n } catch {\n return 'main';\n }\n}\n\n/**\n * Resolve the GitHub blob base URL for a project by reading its git remote\n * and default branch.\n *\n * @param projectPath - Absolute path to the project root.\n * @returns A base URL like \"https://github.com/Owner/Repo/blob/<branch>/\" or null.\n */\nasync function resolveRepoBaseUrl(projectPath: string): Promise<string | null> {\n try {\n const { stdout: remoteRaw } = await execFileAsync(\n 'git',\n ['remote', 'get-url', 'origin'],\n { cwd: projectPath },\n );\n\n const remote = remoteRaw.trim();\n\n // Parse owner/repo from SSH or HTTPS remote URLs\n // Handles aliases like github.com-Org used in SSH configs\n const match = remote.match(\n /github\\.com[^:/]*[:/]([^/]+\\/[^/]+?)(?:\\.git)?$/u,\n );\n if (!match) {\n return null;\n }\n\n const branch = await resolveDefaultBranch(projectPath);\n return `https://github.com/${match[1]}/blob/${branch}/`;\n } catch {\n return null;\n }\n}\n\n/**\n * Options for the generate function.\n */\nexport type GenerateOptions = {\n /** Absolute path to the project to scan. */\n projectPath: string;\n /** Absolute path to the output directory for generated docs. */\n outputDir: string;\n /** Directories (relative to projectPath) to scan for .ts source files. */\n scanDirs: string[];\n /**\n * Short label identifying the project the docs were generated from (e.g.\n * \"Core\", \"Extension\"). Stamped in the index page title.\n */\n projectLabel?: string | null;\n /**\n * Git commit SHA the docs were generated from. Stamped in the index page\n * intro so engineers know how current the site is.\n */\n commitSha?: string | null;\n};\n\n/**\n * Result returned by the generate function.\n */\nexport type GenerateResult = {\n namespaces: number;\n actions: number;\n events: number;\n};\n\n/**\n * The set of directories available to scan for messenger types, resolved from\n * the project's filesystem layout.\n */\ntype ScanSources = {\n /** User-configured scan dirs that exist on disk (relative to projectPath). */\n scanDirs: string[];\n /** Absolute path to `packages/` if it exists, otherwise null. */\n packagesDir: string | null;\n /** Absolute path to `node_modules/@metamask/` if it exists, otherwise null. */\n nodeModulesDir: string | null;\n};\n\n/**\n * Discover which configured source locations actually exist on disk.\n *\n * @param projectPath - The project root path.\n * @param scanDirs - User-configured scan directories relative to projectPath.\n * @returns A ScanSources object describing the locations to scan.\n */\nasync function discoverScanSources(\n projectPath: string,\n scanDirs: string[],\n): Promise<ScanSources> {\n const existingScanDirs: string[] = [];\n for (const dir of scanDirs) {\n if (await directoryExists(path.join(projectPath, dir))) {\n existingScanDirs.push(dir);\n }\n }\n\n const packagesDir = path.join(projectPath, 'packages');\n const nodeModulesDir = path.join(projectPath, 'node_modules', '@metamask');\n\n return {\n scanDirs: existingScanDirs,\n packagesDir: (await directoryExists(packagesDir)) ? packagesDir : null,\n nodeModulesDir: (await directoryExists(nodeModulesDir))\n ? nodeModulesDir\n : null,\n };\n}\n\n/**\n * Log a human-readable description of which source locations will be scanned.\n *\n * @param sources - The resolved scan sources.\n */\nfunction logScanPlan(sources: ScanSources): void {\n const summary: string[] = [];\n for (const dir of sources.scanDirs) {\n summary.push(`${dir}/ (.ts)`);\n }\n if (sources.packagesDir) {\n summary.push('packages/*/src (.ts)');\n }\n if (sources.nodeModulesDir) {\n summary.push('node_modules/@metamask/*/dist (.d.cts)');\n }\n console.log(\n `Scanning ${summary.join(', ')} for Messenger action/event types...`,\n );\n}\n\n/**\n * Run extraction against every file in a single directory, logging and\n * swallowing per-file failures. All files are added to the shared `project`\n * up front so the type checker can resolve cross-file references when the\n * walker descends into imported types.\n *\n * @param project - The shared ts-morph project.\n * @param directory - The directory to scan.\n * @param projectPath - The project root, used for relative path display.\n * @param findFiles - The function used to enumerate files in the directory.\n * @returns The list of extracted messenger items.\n */\nasync function extractFromDirectory(\n project: Project,\n directory: string,\n projectPath: string,\n findFiles: (dir: string) => Promise<string[]>,\n): Promise<ExtractedMessengerCapabilityType[]> {\n const items: ExtractedMessengerCapabilityType[] = [];\n const files = await findFiles(directory);\n for (const file of files) {\n try {\n const sourceFile =\n project.getSourceFile(file) ?? project.addSourceFileAtPath(file);\n items.push(...extractFromSourceFile(sourceFile, projectPath));\n } catch (error) {\n console.warn(\n `Warning: failed to parse ${path.relative(projectPath, file)}`,\n );\n console.warn(error);\n }\n }\n return items;\n}\n\n/**\n * Enumerate the subdirectories of a parent directory that match the expected\n * layout (e.g., `packages/*/src` or `node_modules/@metamask/*/dist`), keeping\n * only those that actually exist.\n *\n * @param parentDir - The parent directory to enumerate.\n * @param subPath - The trailing path component appended to each entry.\n * @param includeSymlinks - Whether to include symbolic links (true for\n * node_modules where workspaces are symlinked).\n * @returns The list of absolute paths to existing target subdirectories.\n */\nasync function listTargetSubdirectories(\n parentDir: string,\n subPath: string,\n includeSymlinks: boolean,\n): Promise<string[]> {\n const entries = await fs.readdir(parentDir, { withFileTypes: true });\n const candidates = entries\n .filter(\n (entry) =>\n entry.isDirectory() || (includeSymlinks && entry.isSymbolicLink()),\n )\n .map((entry) => path.join(parentDir, entry.name, subPath));\n\n const existing: string[] = [];\n for (const candidate of candidates) {\n if (await directoryExists(candidate)) {\n existing.push(candidate);\n }\n }\n return existing;\n}\n\n/**\n * Scan every source location described by `sources` and return all extracted\n * messenger items. A single ts-morph Project is shared across every file so\n * the type checker can resolve cross-file references (e.g. a `*Messenger`\n * declaration in one file walking through an imported umbrella union into\n * an auto-generated `*-method-action-types.ts` sibling).\n *\n * @param projectPath - The project root path.\n * @param sources - The set of source locations to scan.\n * @returns A flat list of all extracted messenger items.\n */\nasync function scanSources(\n projectPath: string,\n sources: ScanSources,\n): Promise<ExtractedMessengerCapabilityType[]> {\n const project = createExtractionProject();\n const allItems: ExtractedMessengerCapabilityType[] = [];\n\n for (const dir of sources.scanDirs) {\n allItems.push(\n ...(await extractFromDirectory(\n project,\n path.join(projectPath, dir),\n projectPath,\n findTsFiles,\n )),\n );\n }\n\n if (sources.packagesDir) {\n const srcDirs = await listTargetSubdirectories(\n sources.packagesDir,\n 'src',\n false,\n );\n for (const srcDir of srcDirs) {\n allItems.push(\n ...(await extractFromDirectory(\n project,\n srcDir,\n projectPath,\n findTsFiles,\n )),\n );\n }\n }\n\n if (sources.nodeModulesDir) {\n const distDirs = await listTargetSubdirectories(\n sources.nodeModulesDir,\n 'dist',\n true,\n );\n for (const distDir of distDirs) {\n allItems.push(\n ...(await extractFromDirectory(\n project,\n distDir,\n projectPath,\n findDtsFiles,\n )),\n );\n }\n }\n\n return allItems;\n}\n\n/**\n * Replace a previously-seen item in its existing namespace group with a\n * higher-scoring duplicate. Handles the case where the duplicate is a\n * different kind (action vs event) by moving it between lists.\n *\n * @param byNamespace - Map of namespace to its group.\n * @param previous - The previously stored item.\n * @param replacement - The new item to replace it with.\n */\nfunction replaceDuplicateInGroup(\n byNamespace: Map<string, NamespaceGroup>,\n previous: ExtractedMessengerCapabilityType,\n replacement: ExtractedMessengerCapabilityType,\n): void {\n const namespace = replacement.typeString.split(':')[0];\n const group = byNamespace.get(namespace);\n // istanbul ignore next: `previous` and `replacement` have the same\n // typeString, so they share a namespace, and we always insert the\n // namespace into `byNamespace` before recording the original entry.\n if (!group) {\n return;\n }\n const previousList =\n previous.kind === 'action' ? group.actions : group.events;\n const index = previousList.indexOf(previous);\n // istanbul ignore next: `previous` was added to its kind's list by\n // `groupByNamespace` before being recorded in `seen`, so it is always\n // present when we look it up here.\n if (index === -1) {\n return;\n }\n if (previous.kind === replacement.kind) {\n previousList[index] = replacement;\n } else {\n previousList.splice(index, 1);\n const newList =\n replacement.kind === 'action' ? group.actions : group.events;\n newList.push(replacement);\n }\n}\n\n/**\n * Group items by namespace, deduplicating duplicate typeStrings using\n * `deduplicationScore`. Returns groups sorted alphabetically by namespace,\n * with each group's items sorted alphabetically by typeString.\n *\n * @param items - The full list of extracted items.\n * @returns The deduplicated and sorted namespace groups.\n */\nfunction groupByNamespace(\n items: ExtractedMessengerCapabilityType[],\n): NamespaceGroup[] {\n const byNamespace = new Map<string, NamespaceGroup>();\n const seen = new Map<string, ExtractedMessengerCapabilityType>();\n\n for (const item of items) {\n const existing = seen.get(item.typeString);\n if (existing) {\n if (deduplicationScore(item) <= deduplicationScore(existing)) {\n continue;\n }\n replaceDuplicateInGroup(byNamespace, existing, item);\n seen.set(item.typeString, item);\n continue;\n }\n\n seen.set(item.typeString, item);\n const namespace = item.typeString.split(':')[0];\n let group = byNamespace.get(namespace);\n if (!group) {\n group = { namespace, actions: [], events: [] };\n byNamespace.set(namespace, group);\n }\n if (item.kind === 'action') {\n group.actions.push(item);\n } else {\n group.events.push(item);\n }\n }\n\n const namespaces = Array.from(byNamespace.values()).sort((a, b) =>\n a.namespace.localeCompare(b.namespace),\n );\n\n for (const ns of namespaces) {\n ns.actions.sort((a, b) => a.typeString.localeCompare(b.typeString));\n ns.events.sort((a, b) => a.typeString.localeCompare(b.typeString));\n }\n\n return namespaces;\n}\n\n/**\n * Write generated docs (namespace pages, index page, sidebars) to disk,\n * replacing any existing `docs/` directory.\n *\n * @param namespaces - The grouped namespaces to render.\n * @param outputDir - The root output directory.\n * @param repoBaseUrl - GitHub blob base URL for source links, or null.\n * @param indexOptions - Options stamped on the index page header.\n * @param indexOptions.projectLabel - Short label identifying the project.\n * @param indexOptions.commitSha - Git commit SHA the docs were generated from.\n * @returns Promise that resolves once all files are written.\n */\nasync function writeOutput(\n namespaces: NamespaceGroup[],\n outputDir: string,\n repoBaseUrl: string | null,\n indexOptions: {\n projectLabel?: string | null;\n commitSha?: string | null;\n },\n): Promise<void> {\n const docsDir = path.join(outputDir, 'docs');\n\n if (await directoryExists(docsDir)) {\n await fs.rm(docsDir, { recursive: true });\n }\n await fs.mkdir(docsDir, { recursive: true });\n\n for (const ns of namespaces) {\n const nsDir = path.join(docsDir, ns.namespace);\n await fs.mkdir(nsDir, { recursive: true });\n\n if (ns.actions.length > 0) {\n await fs.writeFile(\n path.join(nsDir, 'actions.md'),\n generateNamespacePage(ns, 'action', repoBaseUrl),\n );\n }\n\n if (ns.events.length > 0) {\n await fs.writeFile(\n path.join(nsDir, 'events.md'),\n generateNamespacePage(ns, 'event', repoBaseUrl),\n );\n }\n }\n\n await fs.writeFile(\n path.join(docsDir, 'index.md'),\n generateIndexPage(namespaces, indexOptions),\n );\n\n await fs.writeFile(\n path.join(outputDir, 'sidebars.ts'),\n generateSidebars(namespaces),\n );\n}\n\n/**\n * Scan a project for messenger action/event types and generate documentation.\n *\n * @param options - Generation options.\n * @returns A promise resolving to counts of generated namespaces, actions, and events.\n */\nexport async function generate(\n options: GenerateOptions,\n): Promise<GenerateResult> {\n const { projectPath, outputDir, scanDirs, projectLabel, commitSha } = options;\n\n const sources = await discoverScanSources(projectPath, scanDirs);\n\n if (\n sources.scanDirs.length === 0 &&\n !sources.packagesDir &&\n !sources.nodeModulesDir\n ) {\n throw new Error(\n `No scannable directories found in ${projectPath}. ` +\n `Looked for: ${scanDirs.join(', ')}, packages/, node_modules/@metamask/`,\n );\n }\n\n logScanPlan(sources);\n\n const allItems = await scanSources(projectPath, sources);\n console.log(\n `Found ${allItems.length} messenger ${allItems.length === 1 ? 'item' : 'items'} total.`,\n );\n\n const namespaces = groupByNamespace(allItems);\n const repoBaseUrl = await resolveRepoBaseUrl(projectPath);\n\n await writeOutput(namespaces, outputDir, repoBaseUrl, {\n projectLabel,\n commitSha,\n });\n\n const totalActions = namespaces.reduce(\n (sum, ns) => sum + ns.actions.length,\n 0,\n );\n const totalEvents = namespaces.reduce((sum, ns) => sum + ns.events.length, 0);\n\n console.log(\n `Generated docs for ${namespaces.length} ${namespaces.length === 1 ? 'namespace' : 'namespaces'}.`,\n );\n console.log(` Actions: ${totalActions}`);\n console.log(` Events: ${totalEvents}`);\n console.log(`Output: ${path.join(outputDir, 'docs')}/`);\n\n return {\n namespaces: namespaces.length,\n actions: totalActions,\n events: totalEvents,\n };\n}\n"]}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateSidebars = exports.generateIndexPage = exports.generateNamespacePage = exports.generateItemMarkdown = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Convert backtick-quoted action/event names in text into links when they
|
|
6
|
+
* match a known item in the same namespace. For example, `` `setActiveNetwork` ``
|
|
7
|
+
* becomes a link to `#networkcontrollersetactivenetwork` on the actions page.
|
|
8
|
+
*
|
|
9
|
+
* @param text - The markdown text to process.
|
|
10
|
+
* @param namespace - The current namespace (e.g. "NetworkController").
|
|
11
|
+
* @param knownNames - Map from short name (e.g. "setActiveNetwork") to the page-relative path and anchor.
|
|
12
|
+
* @returns The text with backtick references replaced by links.
|
|
13
|
+
*/
|
|
14
|
+
function linkifyReferences(text, namespace, knownNames) {
|
|
15
|
+
return text.replace(/`([a-zA-Z]\w*)`/gu, (match, name) => {
|
|
16
|
+
const link = knownNames.get(name);
|
|
17
|
+
if (link) {
|
|
18
|
+
return `[\`${name}\`](${link})`;
|
|
19
|
+
}
|
|
20
|
+
// Also try with namespace prefix (e.g. "NetworkController:setActiveNetwork")
|
|
21
|
+
const fullName = `${namespace}:${name}`;
|
|
22
|
+
const anchor = fullName.toLowerCase().replace(/[^a-z0-9]/gu, '');
|
|
23
|
+
const linkFull = knownNames.get(fullName);
|
|
24
|
+
if (linkFull) {
|
|
25
|
+
return `[\`${name}\`](${linkFull})`;
|
|
26
|
+
}
|
|
27
|
+
// Check if the anchor matches exactly in known names values
|
|
28
|
+
for (const [, href] of knownNames) {
|
|
29
|
+
if (href.endsWith(`#${anchor}`)) {
|
|
30
|
+
return `[\`${name}\`](${href})`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return match;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Generate markdown documentation for a single messenger item.
|
|
38
|
+
*
|
|
39
|
+
* @param item - The messenger item to document.
|
|
40
|
+
* @param namespace - The current namespace.
|
|
41
|
+
* @param knownNames - Map from short/full names to their link paths.
|
|
42
|
+
* @param repoBaseUrl - Optional GitHub blob base URL (e.g. "https://github.com/Owner/Repo/blob/sha/").
|
|
43
|
+
* @returns The generated markdown string.
|
|
44
|
+
*/
|
|
45
|
+
function generateItemMarkdown(item, namespace, knownNames, repoBaseUrl) {
|
|
46
|
+
const parts = [];
|
|
47
|
+
parts.push(`### \`${item.typeString}\``);
|
|
48
|
+
parts.push('');
|
|
49
|
+
if (item.deprecated) {
|
|
50
|
+
parts.push('> **Deprecated**');
|
|
51
|
+
parts.push('');
|
|
52
|
+
}
|
|
53
|
+
// For sources scanned out of an @metamask/*/dist directory we render an
|
|
54
|
+
// npm link, since the original .ts paths are not part of the repo we're
|
|
55
|
+
// documenting. Other `node_modules/` paths fall through to the normal
|
|
56
|
+
// source-link branches.
|
|
57
|
+
const metamaskPkgMatch = item.sourceFile.match(/node_modules\/(@metamask\/[^/]+)/u);
|
|
58
|
+
if (metamaskPkgMatch) {
|
|
59
|
+
const pkgName = metamaskPkgMatch[1];
|
|
60
|
+
const npmUrl = `https://www.npmjs.com/package/${pkgName}`;
|
|
61
|
+
parts.push(`**Package**: [\`${pkgName}\`](${npmUrl})`);
|
|
62
|
+
}
|
|
63
|
+
else if (repoBaseUrl) {
|
|
64
|
+
const ghUrl = `${repoBaseUrl}${item.sourceFile}#L${item.line}`;
|
|
65
|
+
parts.push(`**Source**: [${item.sourceFile}:${item.line}](${ghUrl})`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
parts.push(`**Source**: \`${item.sourceFile}:${item.line}\``);
|
|
69
|
+
}
|
|
70
|
+
parts.push('');
|
|
71
|
+
if (item.jsDoc) {
|
|
72
|
+
parts.push(linkifyReferences(item.jsDoc, namespace, knownNames));
|
|
73
|
+
parts.push('');
|
|
74
|
+
}
|
|
75
|
+
// Only actions get a parameters table — events carry a positional tuple
|
|
76
|
+
// payload, not named arguments, so a `@param` table doesn't fit.
|
|
77
|
+
if (item.kind === 'action' && item.params.length > 0) {
|
|
78
|
+
parts.push('**Parameters**:');
|
|
79
|
+
parts.push('');
|
|
80
|
+
parts.push('| Name | Description |');
|
|
81
|
+
parts.push('|------|-------------|');
|
|
82
|
+
for (const param of item.params) {
|
|
83
|
+
const description = linkifyReferences(param.description, namespace, knownNames);
|
|
84
|
+
parts.push(`| \`${param.name}\` | ${description} |`);
|
|
85
|
+
}
|
|
86
|
+
parts.push('');
|
|
87
|
+
}
|
|
88
|
+
if (item.kind === 'action' && item.returns) {
|
|
89
|
+
parts.push(`**Returns**: ${linkifyReferences(item.returns, namespace, knownNames)}`);
|
|
90
|
+
parts.push('');
|
|
91
|
+
}
|
|
92
|
+
const signatureLabel = item.kind === 'action' ? 'Handler' : 'Payload';
|
|
93
|
+
parts.push(`**${signatureLabel} signature**:`);
|
|
94
|
+
parts.push('');
|
|
95
|
+
parts.push('```typescript');
|
|
96
|
+
parts.push(item.handlerOrPayload);
|
|
97
|
+
parts.push('```');
|
|
98
|
+
parts.push('');
|
|
99
|
+
return parts.join('\n');
|
|
100
|
+
}
|
|
101
|
+
exports.generateItemMarkdown = generateItemMarkdown;
|
|
102
|
+
/**
|
|
103
|
+
* Generate a full markdown page for a namespace's actions or events.
|
|
104
|
+
*
|
|
105
|
+
* @param ns - The namespace group to generate a page for.
|
|
106
|
+
* @param kind - Whether to generate the actions or events page.
|
|
107
|
+
* @param repoBaseUrl - Optional GitHub blob base URL for source links.
|
|
108
|
+
* @returns The generated markdown string.
|
|
109
|
+
*/
|
|
110
|
+
function generateNamespacePage(ns, kind, repoBaseUrl = null) {
|
|
111
|
+
const items = kind === 'action' ? ns.actions : ns.events;
|
|
112
|
+
const title = kind === 'action' ? 'Actions' : 'Events';
|
|
113
|
+
const parts = [];
|
|
114
|
+
parts.push('---');
|
|
115
|
+
parts.push(`title: "${ns.namespace} ${title}"`);
|
|
116
|
+
parts.push(`sidebar_label: "${title}"`);
|
|
117
|
+
parts.push('---');
|
|
118
|
+
parts.push('');
|
|
119
|
+
parts.push(`# ${ns.namespace} ${title}`);
|
|
120
|
+
parts.push('');
|
|
121
|
+
if (items.length === 0) {
|
|
122
|
+
parts.push(`_No ${kind}s found for this namespace._`);
|
|
123
|
+
parts.push('');
|
|
124
|
+
return parts.join('\n');
|
|
125
|
+
}
|
|
126
|
+
parts.push(`${items.length} ${kind}${items.length === 1 ? '' : 's'} registered.`);
|
|
127
|
+
parts.push('');
|
|
128
|
+
// Build a map of known names → link paths for cross-referencing.
|
|
129
|
+
// Actions on same page get #anchor, actions/events on sibling page get relative path.
|
|
130
|
+
const knownNames = new Map();
|
|
131
|
+
for (const action of ns.actions) {
|
|
132
|
+
const shortName = action.typeString.split(':')[1];
|
|
133
|
+
const anchor = action.typeString.toLowerCase().replace(/[^a-z0-9]/gu, '');
|
|
134
|
+
const href = kind === 'action' ? `#${anchor}` : `./actions#${anchor}`;
|
|
135
|
+
knownNames.set(shortName, href);
|
|
136
|
+
knownNames.set(action.typeString, href);
|
|
137
|
+
}
|
|
138
|
+
for (const event of ns.events) {
|
|
139
|
+
const shortName = event.typeString.split(':')[1];
|
|
140
|
+
const anchor = event.typeString.toLowerCase().replace(/[^a-z0-9]/gu, '');
|
|
141
|
+
const href = kind === 'event' ? `#${anchor}` : `./events#${anchor}`;
|
|
142
|
+
knownNames.set(shortName, href);
|
|
143
|
+
knownNames.set(event.typeString, href);
|
|
144
|
+
}
|
|
145
|
+
// Table of contents
|
|
146
|
+
parts.push('| Name | Deprecated |');
|
|
147
|
+
parts.push('|------|-----------|');
|
|
148
|
+
for (const item of items) {
|
|
149
|
+
const name = item.typeString.split(':')[1];
|
|
150
|
+
// Docusaurus uses github-slugger: strips non-alphanumeric, lowercases, no dashes for special chars in code spans
|
|
151
|
+
const anchor = item.typeString.toLowerCase().replace(/[^a-z0-9]/gu, '');
|
|
152
|
+
const dep = item.deprecated ? 'Yes' : '';
|
|
153
|
+
parts.push(`| [\`${name}\`](#${anchor}) | ${dep} |`);
|
|
154
|
+
}
|
|
155
|
+
parts.push('');
|
|
156
|
+
parts.push('---');
|
|
157
|
+
parts.push('');
|
|
158
|
+
for (const item of items) {
|
|
159
|
+
parts.push(generateItemMarkdown(item, ns.namespace, knownNames, repoBaseUrl));
|
|
160
|
+
parts.push('---');
|
|
161
|
+
parts.push('');
|
|
162
|
+
}
|
|
163
|
+
return parts.join('\n');
|
|
164
|
+
}
|
|
165
|
+
exports.generateNamespacePage = generateNamespacePage;
|
|
166
|
+
/**
|
|
167
|
+
* Generate the index/overview page listing all namespaces.
|
|
168
|
+
*
|
|
169
|
+
* @param namespaces - All namespace groups sorted alphabetically.
|
|
170
|
+
* @param options - Optional project label and commit SHA to stamp in the header.
|
|
171
|
+
* @returns The generated markdown string.
|
|
172
|
+
*/
|
|
173
|
+
function generateIndexPage(namespaces, options = {}) {
|
|
174
|
+
const totalActions = namespaces.reduce((sum, ns) => sum + ns.actions.length, 0);
|
|
175
|
+
const totalEvents = namespaces.reduce((sum, ns) => sum + ns.events.length, 0);
|
|
176
|
+
const projectSuffix = options.projectLabel
|
|
177
|
+
? ` (${options.projectLabel})`
|
|
178
|
+
: '';
|
|
179
|
+
const parts = [];
|
|
180
|
+
parts.push('---');
|
|
181
|
+
parts.push(`title: "Platform API${projectSuffix} Reference"`);
|
|
182
|
+
parts.push('slug: "/"');
|
|
183
|
+
parts.push('---');
|
|
184
|
+
parts.push('');
|
|
185
|
+
parts.push(`# Platform API${projectSuffix}`);
|
|
186
|
+
parts.push('');
|
|
187
|
+
parts.push('This site documents every action and event registered on the Messenger — the type-safe message bus used across all controllers.');
|
|
188
|
+
parts.push('');
|
|
189
|
+
if (options.commitSha) {
|
|
190
|
+
parts.push(`Generated from commit \`${options.commitSha}\`.`);
|
|
191
|
+
parts.push('');
|
|
192
|
+
}
|
|
193
|
+
parts.push(`- **${namespaces.length}** namespaces`);
|
|
194
|
+
parts.push(`- **${totalActions}** actions`);
|
|
195
|
+
parts.push(`- **${totalEvents}** events`);
|
|
196
|
+
parts.push('');
|
|
197
|
+
parts.push('## Namespaces');
|
|
198
|
+
parts.push('');
|
|
199
|
+
parts.push('| Namespace | Actions | Events |');
|
|
200
|
+
parts.push('|-----------|---------|--------|');
|
|
201
|
+
for (const ns of namespaces) {
|
|
202
|
+
const firstLink = ns.actions.length > 0
|
|
203
|
+
? `${ns.namespace}/actions`
|
|
204
|
+
: `${ns.namespace}/events`;
|
|
205
|
+
parts.push(`| [${ns.namespace}](${firstLink}) | ${ns.actions.length} | ${ns.events.length} |`);
|
|
206
|
+
}
|
|
207
|
+
parts.push('');
|
|
208
|
+
return parts.join('\n');
|
|
209
|
+
}
|
|
210
|
+
exports.generateIndexPage = generateIndexPage;
|
|
211
|
+
/**
|
|
212
|
+
* Generate the sidebars.ts file content for Docusaurus.
|
|
213
|
+
*
|
|
214
|
+
* @param namespaces - All namespace groups sorted alphabetically.
|
|
215
|
+
* @returns The generated TypeScript source string.
|
|
216
|
+
*/
|
|
217
|
+
function generateSidebars(namespaces) {
|
|
218
|
+
const items = namespaces.map((ns) => ({
|
|
219
|
+
type: 'category',
|
|
220
|
+
label: ns.namespace,
|
|
221
|
+
items: [
|
|
222
|
+
...(ns.actions.length > 0 ? [`${ns.namespace}/actions`] : []),
|
|
223
|
+
...(ns.events.length > 0 ? [`${ns.namespace}/events`] : []),
|
|
224
|
+
],
|
|
225
|
+
}));
|
|
226
|
+
const sidebar = {
|
|
227
|
+
messengerSidebar: [
|
|
228
|
+
{
|
|
229
|
+
type: 'doc',
|
|
230
|
+
id: 'index',
|
|
231
|
+
label: 'Overview',
|
|
232
|
+
},
|
|
233
|
+
...items,
|
|
234
|
+
],
|
|
235
|
+
};
|
|
236
|
+
return `// This file is auto-generated by @metamask/platform-api-docs\n// Do not edit manually.\nconst sidebars = ${JSON.stringify(sidebar, null, 2)};\nexport default sidebars;\n`;
|
|
237
|
+
}
|
|
238
|
+
exports.generateSidebars = generateSidebars;
|
|
239
|
+
//# sourceMappingURL=markdown.cjs.map
|