@metamask-previews/platform-api-docs 0.0.0-preview-3c77cf4 → 0.0.0-preview-c05ed7142
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 +1 -1
- package/README.md +1 -50
- package/dist/cli.cjs +1 -54
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +1 -54
- package/dist/cli.mjs.map +1 -1
- package/dist/extraction.cjs +44 -70
- package/dist/extraction.cjs.map +1 -1
- package/dist/extraction.d.cts +1 -57
- package/dist/extraction.d.cts.map +1 -1
- package/dist/extraction.d.mts +1 -57
- package/dist/extraction.d.mts.map +1 -1
- package/dist/extraction.mjs +44 -68
- package/dist/extraction.mjs.map +1 -1
- package/dist/generate.cjs +6 -72
- package/dist/generate.cjs.map +1 -1
- package/dist/generate.d.cts +3 -28
- package/dist/generate.d.cts.map +1 -1
- package/dist/generate.d.mts +3 -28
- package/dist/generate.d.mts.map +1 -1
- package/dist/generate.mjs +6 -72
- package/dist/generate.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/root-messenger-discovery.cjs +0 -234
- package/dist/root-messenger-discovery.cjs.map +0 -1
- package/dist/root-messenger-discovery.d.cts +0 -58
- package/dist/root-messenger-discovery.d.cts.map +0 -1
- package/dist/root-messenger-discovery.d.mts +0 -58
- package/dist/root-messenger-discovery.d.mts.map +0 -1
- package/dist/root-messenger-discovery.mjs +0 -206
- package/dist/root-messenger-discovery.mjs.map +0 -1
package/dist/generate.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { RootTypeReference } from "./root-messenger-discovery.mjs";
|
|
2
1
|
/**
|
|
3
2
|
* Resolve the bare GitHub repository URL for a project by reading its
|
|
4
3
|
* `origin` remote.
|
|
@@ -8,31 +7,6 @@ import type { RootTypeReference } from "./root-messenger-discovery.mjs";
|
|
|
8
7
|
* isn't a GitHub URL or can't be read.
|
|
9
8
|
*/
|
|
10
9
|
export declare function resolveRepoUrl(projectPath: string): Promise<string | null>;
|
|
11
|
-
/**
|
|
12
|
-
* Options for the `scan` strategy, which reads every `*Messenger` type alias
|
|
13
|
-
* in every file it can find. Used when no single messenger aggregates every
|
|
14
|
-
* capability.
|
|
15
|
-
*/
|
|
16
|
-
type ScanStrategyOptions = {
|
|
17
|
-
strategy?: 'scan';
|
|
18
|
-
/** Directories (relative to projectPath) to scan for .ts source files. */
|
|
19
|
-
scanDirs: string[];
|
|
20
|
-
rootActions?: never;
|
|
21
|
-
rootEvents?: never;
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Options for the `root-messenger` strategy, which resolves the unions a
|
|
25
|
-
* project declares for its root messenger instead of scanning. Used when one
|
|
26
|
-
* messenger carries every action and event.
|
|
27
|
-
*/
|
|
28
|
-
type RootMessengerStrategyOptions = {
|
|
29
|
-
strategy: 'root-messenger';
|
|
30
|
-
scanDirs?: never;
|
|
31
|
-
/** Type aliasing the union of every action. */
|
|
32
|
-
rootActions: RootTypeReference;
|
|
33
|
-
/** Type aliasing the union of every event. */
|
|
34
|
-
rootEvents: RootTypeReference;
|
|
35
|
-
};
|
|
36
10
|
/**
|
|
37
11
|
* Options for the generate function.
|
|
38
12
|
*/
|
|
@@ -41,6 +15,8 @@ export type GenerateOptions = {
|
|
|
41
15
|
projectPath: string;
|
|
42
16
|
/** Absolute path to the output directory for generated docs. */
|
|
43
17
|
outputDir: string;
|
|
18
|
+
/** Directories (relative to projectPath) to scan for .ts source files. */
|
|
19
|
+
scanDirs: string[];
|
|
44
20
|
/**
|
|
45
21
|
* Short label identifying the project the docs were generated from (e.g.
|
|
46
22
|
* "Core", "Extension"). Stamped in the index page title.
|
|
@@ -51,7 +27,7 @@ export type GenerateOptions = {
|
|
|
51
27
|
* intro so engineers know how current the site is.
|
|
52
28
|
*/
|
|
53
29
|
commitSha?: string | null;
|
|
54
|
-
}
|
|
30
|
+
};
|
|
55
31
|
/**
|
|
56
32
|
* Result returned by the generate function.
|
|
57
33
|
*/
|
|
@@ -67,5 +43,4 @@ export type GenerateResult = {
|
|
|
67
43
|
* @returns A promise resolving to counts of generated namespaces, actions, and events.
|
|
68
44
|
*/
|
|
69
45
|
export declare function generate(options: GenerateOptions): Promise<GenerateResult>;
|
|
70
|
-
export {};
|
|
71
46
|
//# sourceMappingURL=generate.d.mts.map
|
package/dist/generate.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.mts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generate.d.mts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAuEA;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuBxB;AA0BD;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAgWF;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,cAAc,CAAC,CAiDzB"}
|
package/dist/generate.mjs
CHANGED
|
@@ -6,7 +6,6 @@ import { promisify } from "node:util";
|
|
|
6
6
|
import { findDtsFiles, findTsFiles } from "./discovery.mjs";
|
|
7
7
|
import { createExtractionProject, extractFromSourceFile } from "./extraction.mjs";
|
|
8
8
|
import { generateIndexPage, generateNamespacePage, generateSidebars } from "./markdown.mjs";
|
|
9
|
-
import { discoverFromRootMessenger } from "./root-messenger-discovery.mjs";
|
|
10
9
|
/**
|
|
11
10
|
* Compute a deduplication score for a messenger item, preferring items with
|
|
12
11
|
* JSDoc and from the "home" package whose name matches the namespace.
|
|
@@ -329,14 +328,13 @@ async function writeOutput(namespaces, outputDir, repoBaseUrl, indexOptions) {
|
|
|
329
328
|
await fs.writeFile(path.join(outputDir, 'sidebars.ts'), generateSidebars(namespaces));
|
|
330
329
|
}
|
|
331
330
|
/**
|
|
332
|
-
*
|
|
331
|
+
* Scan a project for messenger action/event types and generate documentation.
|
|
333
332
|
*
|
|
334
|
-
* @param
|
|
335
|
-
* @
|
|
336
|
-
* @returns The extracted capabilities.
|
|
337
|
-
* @throws If the project has no scannable directories at all.
|
|
333
|
+
* @param options - Generation options.
|
|
334
|
+
* @returns A promise resolving to counts of generated namespaces, actions, and events.
|
|
338
335
|
*/
|
|
339
|
-
async function
|
|
336
|
+
export async function generate(options) {
|
|
337
|
+
const { projectPath, outputDir, scanDirs, projectLabel, commitSha } = options;
|
|
340
338
|
const sources = await discoverScanSources(projectPath, scanDirs);
|
|
341
339
|
if (sources.scanDirs.length === 0 &&
|
|
342
340
|
!sources.packagesDir &&
|
|
@@ -345,71 +343,7 @@ async function collectByScanning(projectPath, scanDirs) {
|
|
|
345
343
|
`Looked for: ${scanDirs.join(', ')}, packages/, node_modules/@metamask/`);
|
|
346
344
|
}
|
|
347
345
|
logScanPlan(sources);
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* Collect capabilities by resolving the project's root messenger unions.
|
|
352
|
-
*
|
|
353
|
-
* @param projectPath - The project root path.
|
|
354
|
-
* @param options - The root-messenger strategy options.
|
|
355
|
-
* @returns The extracted capabilities.
|
|
356
|
-
*/
|
|
357
|
-
function collectFromRootMessenger(projectPath, options) {
|
|
358
|
-
const { rootActions, rootEvents } = options;
|
|
359
|
-
console.log(`Resolving actions from ${rootActions.filePath}#${rootActions.typeName} ` +
|
|
360
|
-
`and events from ${rootEvents.filePath}#${rootEvents.typeName}...`);
|
|
361
|
-
const { packets, skipped } = discoverFromRootMessenger({
|
|
362
|
-
projectPath,
|
|
363
|
-
actions: rootActions,
|
|
364
|
-
events: rootEvents,
|
|
365
|
-
});
|
|
366
|
-
// Report rather than drop silently: a jump in any of these usually means the
|
|
367
|
-
// project changed how it declares its capabilities.
|
|
368
|
-
warnSkipped('declared inline, with no name to document', skipped.unnamed);
|
|
369
|
-
warnSkipped('whose shape could not be read', skipped.unextractable);
|
|
370
|
-
// Both unions resolving to nothing is always a misconfiguration — a wrong
|
|
371
|
-
// type name, or imports that didn't resolve. Failing here matters because
|
|
372
|
-
// generation would otherwise replace an existing docs directory with an
|
|
373
|
-
// empty one and exit successfully.
|
|
374
|
-
if (packets.length === 0) {
|
|
375
|
-
throw new Error(`No messenger actions or events found in ` +
|
|
376
|
-
`${rootActions.filePath}#${rootActions.typeName} or ` +
|
|
377
|
-
`${rootEvents.filePath}#${rootEvents.typeName}. ` +
|
|
378
|
-
`Check that these types name the unions carrying every capability, ` +
|
|
379
|
-
`and that their imports resolve.`);
|
|
380
|
-
}
|
|
381
|
-
return packets;
|
|
382
|
-
}
|
|
383
|
-
/** How many skipped capability types to name before summarizing the rest. */
|
|
384
|
-
const MAX_SKIPPED_SHOWN = 10;
|
|
385
|
-
/**
|
|
386
|
-
* Warn about capability types that couldn't be documented, naming them so the
|
|
387
|
-
* warning is actionable.
|
|
388
|
-
*
|
|
389
|
-
* @param description - Why they were skipped, as a noun phrase.
|
|
390
|
-
* @param labels - Labels identifying each skipped type.
|
|
391
|
-
*/
|
|
392
|
-
function warnSkipped(description, labels) {
|
|
393
|
-
if (labels.length === 0) {
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
const shown = labels.slice(0, MAX_SKIPPED_SHOWN);
|
|
397
|
-
const remaining = labels.length - shown.length;
|
|
398
|
-
console.warn(`Warning: skipped ${labels.length} capability ` +
|
|
399
|
-
`${labels.length === 1 ? 'type' : 'types'} ${description}: ` +
|
|
400
|
-
`${shown.join(', ')}${remaining > 0 ? `, and ${remaining} more` : ''}`);
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Scan a project for messenger action/event types and generate documentation.
|
|
404
|
-
*
|
|
405
|
-
* @param options - Generation options.
|
|
406
|
-
* @returns A promise resolving to counts of generated namespaces, actions, and events.
|
|
407
|
-
*/
|
|
408
|
-
export async function generate(options) {
|
|
409
|
-
const { projectPath, outputDir, projectLabel, commitSha } = options;
|
|
410
|
-
const allItems = options.strategy === 'root-messenger'
|
|
411
|
-
? collectFromRootMessenger(projectPath, options)
|
|
412
|
-
: await collectByScanning(projectPath, options.scanDirs);
|
|
346
|
+
const allItems = await scanSources(projectPath, sources);
|
|
413
347
|
console.log(`Found ${allItems.length} messenger ${allItems.length === 1 ? 'item' : 'items'} total.`);
|
|
414
348
|
const namespaces = groupByNamespace(allItems);
|
|
415
349
|
const repoBaseUrl = await resolveRepoBaseUrl(projectPath, commitSha ?? null);
|
package/dist/generate.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.mjs","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"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","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.js';\nimport {\n createExtractionProject,\n extractFromSourceFile,\n} from './extraction.js';\nimport {\n generateIndexPage,\n generateNamespacePage,\n generateSidebars,\n} from './markdown.js';\nimport type { RootTypeReference } from './root-messenger-discovery.js';\nimport { discoverFromRootMessenger } from './root-messenger-discovery.js';\nimport type { MessengerCapabilityPacket, NamespaceGroup } from './types.js';\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: MessengerCapabilityPacket): 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 bare GitHub repository URL for a project by reading its\n * `origin` remote.\n *\n * @param projectPath - Absolute path to the project root.\n * @returns A URL like \"https://github.com/Owner/Repo\" or null when the remote\n * isn't a GitHub URL or can't be read.\n */\nexport async function resolveRepoUrl(\n projectPath: string,\n): 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 return `https://github.com/${match[1]}`;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the GitHub blob base URL used for per-line source links.\n *\n * Prefers the documented commit SHA when one is available so the links point\n * at the exact revision the docs were generated from; falls back to the\n * default branch otherwise.\n *\n * @param projectPath - Absolute path to the project root.\n * @param commitSha - Optional commit SHA to use as the ref. When null, the\n * default branch is used instead.\n * @returns A base URL like \"https://github.com/Owner/Repo/blob/<ref>/\" or null.\n */\nasync function resolveRepoBaseUrl(\n projectPath: string,\n commitSha: string | null,\n): Promise<string | null> {\n const repoUrl = await resolveRepoUrl(projectPath);\n if (!repoUrl) {\n return null;\n }\n const ref = commitSha ?? (await resolveDefaultBranch(projectPath));\n return `${repoUrl}/blob/${ref}/`;\n}\n\n/**\n * Options for the `scan` strategy, which reads every `*Messenger` type alias\n * in every file it can find. Used when no single messenger aggregates every\n * capability.\n */\ntype ScanStrategyOptions = {\n strategy?: 'scan';\n /** Directories (relative to projectPath) to scan for .ts source files. */\n scanDirs: string[];\n rootActions?: never;\n rootEvents?: never;\n};\n\n/**\n * Options for the `root-messenger` strategy, which resolves the unions a\n * project declares for its root messenger instead of scanning. Used when one\n * messenger carries every action and event.\n */\ntype RootMessengerStrategyOptions = {\n strategy: 'root-messenger';\n scanDirs?: never;\n /** Type aliasing the union of every action. */\n rootActions: RootTypeReference;\n /** Type aliasing the union of every event. */\n rootEvents: RootTypeReference;\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 /**\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} & (ScanStrategyOptions | RootMessengerStrategyOptions);\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<MessengerCapabilityPacket[]> {\n const items: MessengerCapabilityPacket[] = [];\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<MessengerCapabilityPacket[]> {\n const project = createExtractionProject();\n const allItems: MessengerCapabilityPacket[] = [];\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: MessengerCapabilityPacket,\n replacement: MessengerCapabilityPacket,\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: MessengerCapabilityPacket[],\n): NamespaceGroup[] {\n const byNamespace = new Map<string, NamespaceGroup>();\n const seen = new Map<string, MessengerCapabilityPacket>();\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 * Collect capabilities by scanning the project's files.\n *\n * @param projectPath - The project root path.\n * @param scanDirs - Directories (relative to projectPath) to scan.\n * @returns The extracted capabilities.\n * @throws If the project has no scannable directories at all.\n */\nasync function collectByScanning(\n projectPath: string,\n scanDirs: string[],\n): Promise<MessengerCapabilityPacket[]> {\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 return await scanSources(projectPath, sources);\n}\n\n/**\n * Collect capabilities by resolving the project's root messenger unions.\n *\n * @param projectPath - The project root path.\n * @param options - The root-messenger strategy options.\n * @returns The extracted capabilities.\n */\nfunction collectFromRootMessenger(\n projectPath: string,\n options: RootMessengerStrategyOptions,\n): MessengerCapabilityPacket[] {\n const { rootActions, rootEvents } = options;\n\n console.log(\n `Resolving actions from ${rootActions.filePath}#${rootActions.typeName} ` +\n `and events from ${rootEvents.filePath}#${rootEvents.typeName}...`,\n );\n\n const { packets, skipped } = discoverFromRootMessenger({\n projectPath,\n actions: rootActions,\n events: rootEvents,\n });\n\n // Report rather than drop silently: a jump in any of these usually means the\n // project changed how it declares its capabilities.\n warnSkipped('declared inline, with no name to document', skipped.unnamed);\n warnSkipped('whose shape could not be read', skipped.unextractable);\n\n // Both unions resolving to nothing is always a misconfiguration — a wrong\n // type name, or imports that didn't resolve. Failing here matters because\n // generation would otherwise replace an existing docs directory with an\n // empty one and exit successfully.\n if (packets.length === 0) {\n throw new Error(\n `No messenger actions or events found in ` +\n `${rootActions.filePath}#${rootActions.typeName} or ` +\n `${rootEvents.filePath}#${rootEvents.typeName}. ` +\n `Check that these types name the unions carrying every capability, ` +\n `and that their imports resolve.`,\n );\n }\n\n return packets;\n}\n\n/** How many skipped capability types to name before summarizing the rest. */\nconst MAX_SKIPPED_SHOWN = 10;\n\n/**\n * Warn about capability types that couldn't be documented, naming them so the\n * warning is actionable.\n *\n * @param description - Why they were skipped, as a noun phrase.\n * @param labels - Labels identifying each skipped type.\n */\nfunction warnSkipped(description: string, labels: string[]): void {\n if (labels.length === 0) {\n return;\n }\n\n const shown = labels.slice(0, MAX_SKIPPED_SHOWN);\n const remaining = labels.length - shown.length;\n console.warn(\n `Warning: skipped ${labels.length} capability ` +\n `${labels.length === 1 ? 'type' : 'types'} ${description}: ` +\n `${shown.join(', ')}${remaining > 0 ? `, and ${remaining} more` : ''}`,\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, projectLabel, commitSha } = options;\n\n const allItems =\n options.strategy === 'root-messenger'\n ? collectFromRootMessenger(projectPath, options)\n : await collectByScanning(projectPath, options.scanDirs);\n\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, commitSha ?? null);\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"]}
|
|
1
|
+
{"version":3,"file":"generate.mjs","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"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","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.js';\nimport {\n createExtractionProject,\n extractFromSourceFile,\n} from './extraction.js';\nimport {\n generateIndexPage,\n generateNamespacePage,\n generateSidebars,\n} from './markdown.js';\nimport type { MessengerCapabilityPacket, NamespaceGroup } from './types.js';\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: MessengerCapabilityPacket): 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 bare GitHub repository URL for a project by reading its\n * `origin` remote.\n *\n * @param projectPath - Absolute path to the project root.\n * @returns A URL like \"https://github.com/Owner/Repo\" or null when the remote\n * isn't a GitHub URL or can't be read.\n */\nexport async function resolveRepoUrl(\n projectPath: string,\n): 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 return `https://github.com/${match[1]}`;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the GitHub blob base URL used for per-line source links.\n *\n * Prefers the documented commit SHA when one is available so the links point\n * at the exact revision the docs were generated from; falls back to the\n * default branch otherwise.\n *\n * @param projectPath - Absolute path to the project root.\n * @param commitSha - Optional commit SHA to use as the ref. When null, the\n * default branch is used instead.\n * @returns A base URL like \"https://github.com/Owner/Repo/blob/<ref>/\" or null.\n */\nasync function resolveRepoBaseUrl(\n projectPath: string,\n commitSha: string | null,\n): Promise<string | null> {\n const repoUrl = await resolveRepoUrl(projectPath);\n if (!repoUrl) {\n return null;\n }\n const ref = commitSha ?? (await resolveDefaultBranch(projectPath));\n return `${repoUrl}/blob/${ref}/`;\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<MessengerCapabilityPacket[]> {\n const items: MessengerCapabilityPacket[] = [];\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<MessengerCapabilityPacket[]> {\n const project = createExtractionProject();\n const allItems: MessengerCapabilityPacket[] = [];\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: MessengerCapabilityPacket,\n replacement: MessengerCapabilityPacket,\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: MessengerCapabilityPacket[],\n): NamespaceGroup[] {\n const byNamespace = new Map<string, NamespaceGroup>();\n const seen = new Map<string, MessengerCapabilityPacket>();\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, commitSha ?? null);\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/platform-api-docs",
|
|
3
|
-
"version": "0.0.0-preview-
|
|
3
|
+
"version": "0.0.0-preview-c05ed7142",
|
|
4
4
|
"description": "Produces documentation for the platform API, the set of actions and events available in the clients through the message bus",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ethereum",
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.discoverFromRootMessenger = exports.parseRootTypeReference = void 0;
|
|
27
|
-
const path = __importStar(require("node:path"));
|
|
28
|
-
const ts_morph_1 = require("ts-morph");
|
|
29
|
-
const extraction_js_1 = require("./extraction.cjs");
|
|
30
|
-
/**
|
|
31
|
-
* Split a `<file>#<TypeName>` reference into its parts, on the last `#` so
|
|
32
|
-
* that paths containing a `#` still work.
|
|
33
|
-
*
|
|
34
|
-
* @param reference - The raw reference, e.g. `src/messenger.ts#RootActions`.
|
|
35
|
-
* @returns The parsed reference.
|
|
36
|
-
* @throws If the reference has no `#`, or either side of it is empty.
|
|
37
|
-
*/
|
|
38
|
-
function parseRootTypeReference(reference) {
|
|
39
|
-
const separatorIndex = reference.lastIndexOf('#');
|
|
40
|
-
if (separatorIndex === -1) {
|
|
41
|
-
throw new Error(`Expected a reference of the form "<file>#<TypeName>", got "${reference}".`);
|
|
42
|
-
}
|
|
43
|
-
const filePath = reference.slice(0, separatorIndex);
|
|
44
|
-
const typeName = reference.slice(separatorIndex + 1);
|
|
45
|
-
if (filePath.length === 0 || typeName.length === 0) {
|
|
46
|
-
throw new Error(`Expected a reference of the form "<file>#<TypeName>", got "${reference}".`);
|
|
47
|
-
}
|
|
48
|
-
return { filePath, typeName };
|
|
49
|
-
}
|
|
50
|
-
exports.parseRootTypeReference = parseRootTypeReference;
|
|
51
|
-
/**
|
|
52
|
-
* Create a ts-morph Project for resolving root messenger types.
|
|
53
|
-
*
|
|
54
|
-
* No file list is loaded: this strategy opens only the entry files and lets
|
|
55
|
-
* the checker pull in the rest.
|
|
56
|
-
*
|
|
57
|
-
* @returns A new ts-morph Project.
|
|
58
|
-
*/
|
|
59
|
-
function createRootMessengerProject() {
|
|
60
|
-
return new ts_morph_1.Project({
|
|
61
|
-
compilerOptions: {
|
|
62
|
-
noEmit: true,
|
|
63
|
-
// We need symbol resolution, not full typechecking, so a project's own
|
|
64
|
-
// strictness settings shouldn't be able to fail the docs build.
|
|
65
|
-
strict: false,
|
|
66
|
-
skipLibCheck: true,
|
|
67
|
-
target: ts_morph_1.ts.ScriptTarget.ESNext,
|
|
68
|
-
module: ts_morph_1.ts.ModuleKind.ESNext,
|
|
69
|
-
moduleResolution: ts_morph_1.ts.ModuleResolutionKind.NodeJs,
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Resolve the type alias a reference names.
|
|
75
|
-
*
|
|
76
|
-
* @param project - The ts-morph project to load the file into.
|
|
77
|
-
* @param projectPath - Absolute path to the project root.
|
|
78
|
-
* @param reference - The reference to resolve.
|
|
79
|
-
* @param flagName - The CLI flag the reference came from, used in errors.
|
|
80
|
-
* @returns The type alias declaration.
|
|
81
|
-
* @throws If the file can't be read or declares no such type alias.
|
|
82
|
-
*/
|
|
83
|
-
function resolveRootDeclaration(project, projectPath, reference, flagName) {
|
|
84
|
-
const absolutePath = path.resolve(projectPath, reference.filePath);
|
|
85
|
-
let sourceFile;
|
|
86
|
-
try {
|
|
87
|
-
sourceFile =
|
|
88
|
-
project.getSourceFile(absolutePath) ??
|
|
89
|
-
project.addSourceFileAtPath(absolutePath);
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
throw new Error(`Could not read ${absolutePath}, which was named by ${flagName}.`);
|
|
93
|
-
}
|
|
94
|
-
const declaration = sourceFile.getTypeAlias(reference.typeName);
|
|
95
|
-
if (!declaration) {
|
|
96
|
-
throw new Error(`No type alias named "${reference.typeName}" in ${reference.filePath}, which was named by ${flagName}.`);
|
|
97
|
-
}
|
|
98
|
-
return declaration;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Find the named declaration behind a union constituent.
|
|
102
|
-
*
|
|
103
|
-
* @param constituent - The resolved constituent type.
|
|
104
|
-
* @param rootDeclaration - The root union's own declaration.
|
|
105
|
-
* @param isLoneConstituent - Whether this is the root's only constituent.
|
|
106
|
-
* @returns The declaration, or undefined when the constituent is anonymous.
|
|
107
|
-
*/
|
|
108
|
-
function findCapabilityDeclaration(constituent, rootDeclaration, isLoneConstituent) {
|
|
109
|
-
// Prefer the alias symbol: for a type alias it carries the name and JSDoc,
|
|
110
|
-
// where the plain symbol points at the anonymous object type. Skip an alias
|
|
111
|
-
// resolving back to the root union itself, which is what the checker reports
|
|
112
|
-
// for a lone generic instantiation such as `type Actions = Foo<Bar>`.
|
|
113
|
-
const aliasDeclarations = (constituent.getAliasSymbol()?.getDeclarations() ?? []).filter((node) => node !== rootDeclaration);
|
|
114
|
-
// An interface has no alias symbol, being its own declaration.
|
|
115
|
-
const declarations = aliasDeclarations.length > 0
|
|
116
|
-
? aliasDeclarations
|
|
117
|
-
: (constituent.getSymbol()?.getDeclarations() ?? []);
|
|
118
|
-
const found = declarations.find((node) => ts_morph_1.Node.isTypeAliasDeclaration(node) ||
|
|
119
|
-
ts_morph_1.Node.isInterfaceDeclaration(node));
|
|
120
|
-
if (found) {
|
|
121
|
-
return found;
|
|
122
|
-
}
|
|
123
|
-
// Nothing named behind the type itself. When the root aliases a single
|
|
124
|
-
// generic instantiation, the declaration we want is the one its type node
|
|
125
|
-
// references — `Foo` in `type Actions = Foo<Bar>`. Only when it is the lone
|
|
126
|
-
// constituent, though: in a union, an anonymous member is genuinely
|
|
127
|
-
// anonymous, and attributing it to the wrapper would mislabel it.
|
|
128
|
-
return isLoneConstituent
|
|
129
|
-
? findDeclarationFromRootTypeNode(rootDeclaration)
|
|
130
|
-
: undefined;
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Resolve the declaration referenced by a root alias's type node.
|
|
134
|
-
*
|
|
135
|
-
* @param rootDeclaration - The root union's own declaration.
|
|
136
|
-
* @returns The referenced declaration, or undefined.
|
|
137
|
-
*/
|
|
138
|
-
function findDeclarationFromRootTypeNode(rootDeclaration) {
|
|
139
|
-
const typeNode = rootDeclaration.getTypeNode();
|
|
140
|
-
if (!typeNode || !ts_morph_1.Node.isTypeReference(typeNode)) {
|
|
141
|
-
return undefined;
|
|
142
|
-
}
|
|
143
|
-
const localSymbol = typeNode.getTypeName().getSymbol();
|
|
144
|
-
const symbol = localSymbol?.getAliasedSymbol() ?? localSymbol;
|
|
145
|
-
return symbol
|
|
146
|
-
?.getDeclarations()
|
|
147
|
-
.find((node) => ts_morph_1.Node.isTypeAliasDeclaration(node) ||
|
|
148
|
-
ts_morph_1.Node.isInterfaceDeclaration(node));
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Render a short, single-line label for an anonymous type.
|
|
152
|
-
*
|
|
153
|
-
* @param type - The type to describe.
|
|
154
|
-
* @param enclosingNode - Node to render the type relative to, so an aliased
|
|
155
|
-
* type reads as its name rather than `import("<absolute path>").Name`.
|
|
156
|
-
* @returns The label.
|
|
157
|
-
*/
|
|
158
|
-
function summarizeType(type, enclosingNode) {
|
|
159
|
-
const text = type.getText(enclosingNode).replace(/\s+/gu, ' ');
|
|
160
|
-
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Extract every documentable capability from one root union.
|
|
164
|
-
*
|
|
165
|
-
* @param rootDeclaration - The root union's declaration.
|
|
166
|
-
* @param kind - Whether these are actions or events.
|
|
167
|
-
* @param projectPath - Absolute path to the project root.
|
|
168
|
-
* @param skipped - Labels collected as undocumentable constituents are found.
|
|
169
|
-
* @param reference - The reference that named this type, used in errors.
|
|
170
|
-
* @param flagName - The CLI flag the reference came from, used in errors.
|
|
171
|
-
* @returns The extracted capabilities.
|
|
172
|
-
* @throws If the union resolved to `any` or `unknown`.
|
|
173
|
-
*/
|
|
174
|
-
function extractFromRootType(rootDeclaration, kind, projectPath, skipped, reference, flagName) {
|
|
175
|
-
const rootType = rootDeclaration.getTypeNodeOrThrow().getType();
|
|
176
|
-
// TypeScript absorbs `any | T` into `any` and `unknown | T` into `unknown`,
|
|
177
|
-
// so a single member the checker can't resolve — typically a failed import —
|
|
178
|
-
// erases every other capability in the union. Fail instead of emitting a
|
|
179
|
-
// catalog that looks complete but silently isn't.
|
|
180
|
-
if (rootType.isAny() || rootType.isUnknown()) {
|
|
181
|
-
throw new Error(`${reference.filePath}#${reference.typeName}, named by ${flagName}, ` +
|
|
182
|
-
`resolved to \`${rootType.getText()}\` rather than a union of ` +
|
|
183
|
-
`capabilities. This usually means an import in that file could not be ` +
|
|
184
|
-
`resolved; because TypeScript absorbs the rest of a union into ` +
|
|
185
|
-
`\`any\`, every other capability in it would be missing.`);
|
|
186
|
-
}
|
|
187
|
-
// A project with no capabilities of this kind aliases the union to `never`.
|
|
188
|
-
if (rootType.isNever()) {
|
|
189
|
-
return [];
|
|
190
|
-
}
|
|
191
|
-
const constituents = rootType.isUnion()
|
|
192
|
-
? rootType.getUnionTypes()
|
|
193
|
-
: [rootType];
|
|
194
|
-
const packets = [];
|
|
195
|
-
for (const constituent of constituents) {
|
|
196
|
-
const declaration = findCapabilityDeclaration(constituent, rootDeclaration, constituents.length === 1);
|
|
197
|
-
if (!declaration) {
|
|
198
|
-
skipped.unnamed.push(summarizeType(constituent, rootDeclaration));
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
const classified = (0, extraction_js_1.classifyMessengerCapabilityTypeDeclaration)(declaration, kind);
|
|
202
|
-
const packet = classified &&
|
|
203
|
-
(0, extraction_js_1.extractFromMessengerCapabilityTypeDeclaration)(classified, projectPath);
|
|
204
|
-
if (!packet) {
|
|
205
|
-
const sourceFile = declaration.getSourceFile().getFilePath();
|
|
206
|
-
skipped.unextractable.push(`${declaration.getName()} (${path.relative(projectPath, sourceFile)}:${declaration.getStartLineNumber()})`);
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
packets.push(packet);
|
|
210
|
-
}
|
|
211
|
-
return packets;
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Enumerate every action and event reachable from a project's root messenger.
|
|
215
|
-
*
|
|
216
|
-
* @param options - Discovery options.
|
|
217
|
-
* @returns The extracted capabilities plus anything skipped.
|
|
218
|
-
*/
|
|
219
|
-
function discoverFromRootMessenger(options) {
|
|
220
|
-
const { projectPath, actions, events } = options;
|
|
221
|
-
const project = createRootMessengerProject();
|
|
222
|
-
const skipped = { unnamed: [], unextractable: [] };
|
|
223
|
-
const packets = [];
|
|
224
|
-
for (const [reference, kind, flagName] of [
|
|
225
|
-
[actions, 'action', '--root-actions'],
|
|
226
|
-
[events, 'event', '--root-events'],
|
|
227
|
-
]) {
|
|
228
|
-
const rootDeclaration = resolveRootDeclaration(project, projectPath, reference, flagName);
|
|
229
|
-
packets.push(...extractFromRootType(rootDeclaration, kind, projectPath, skipped, reference, flagName));
|
|
230
|
-
}
|
|
231
|
-
return { packets, skipped };
|
|
232
|
-
}
|
|
233
|
-
exports.discoverFromRootMessenger = discoverFromRootMessenger;
|
|
234
|
-
//# sourceMappingURL=root-messenger-discovery.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"root-messenger-discovery.cjs","sourceRoot":"","sources":["../src/root-messenger-discovery.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'node:path';\nimport type {\n InterfaceDeclaration,\n Project as TsMorphProject,\n Type,\n TypeAliasDeclaration,\n} from 'ts-morph';\nimport { Node as NodeGuards, Project, ts } from 'ts-morph';\n\nimport {\n classifyMessengerCapabilityTypeDeclaration,\n extractFromMessengerCapabilityTypeDeclaration,\n} from './extraction.js';\nimport type { MessengerCapabilityPacket } from './types.js';\n\n// ---------------------------------------------------------------------------\n// The `root-messenger` strategy: resolve the unions a project declares for its\n// root messenger and let the type checker enumerate them. Going through the\n// checker rather than the AST means a union works whether it is written by\n// hand or computed (e.g. derived from a registry via `ReturnType<...>`). Each\n// capability it reports is handed to the shared extractor in `extraction.ts`,\n// so the output matches what the `scan` strategy produces.\n// ---------------------------------------------------------------------------\n\n/**\n * A reference to a type in a file, written as `<file>#<TypeName>`.\n */\nexport type RootTypeReference = {\n /** Path to the declaring file, relative to the project root. */\n filePath: string;\n /** Name of the type alias within that file. */\n typeName: string;\n};\n\n/**\n * Options for {@link discoverFromRootMessenger}.\n */\ntype RootMessengerDiscoveryOptions = {\n /** Absolute path to the project to scan. */\n projectPath: string;\n /** Type aliasing the union of every action on the root messenger. */\n actions: RootTypeReference;\n /** Type aliasing the union of every event on the root messenger. */\n events: RootTypeReference;\n};\n\n/**\n * Labels for capability types that were found but couldn't be documented,\n * grouped by why. Labels rather than counts, so warnings can name what to fix.\n */\ntype SkippedCapabilities = {\n /** Declared inline in the union, so there is no name or JSDoc to document. */\n unnamed: string[];\n /** Named, but of a shape the extractor rejects. */\n unextractable: string[];\n};\n\n/**\n * The result of {@link discoverFromRootMessenger}.\n */\ntype RootMessengerDiscoveryResult = {\n /** Every capability extracted, actions before events. */\n packets: MessengerCapabilityPacket[];\n /** Capabilities that couldn't be documented. */\n skipped: SkippedCapabilities;\n};\n\n/**\n * Split a `<file>#<TypeName>` reference into its parts, on the last `#` so\n * that paths containing a `#` still work.\n *\n * @param reference - The raw reference, e.g. `src/messenger.ts#RootActions`.\n * @returns The parsed reference.\n * @throws If the reference has no `#`, or either side of it is empty.\n */\nexport function parseRootTypeReference(reference: string): RootTypeReference {\n const separatorIndex = reference.lastIndexOf('#');\n if (separatorIndex === -1) {\n throw new Error(\n `Expected a reference of the form \"<file>#<TypeName>\", got \"${reference}\".`,\n );\n }\n\n const filePath = reference.slice(0, separatorIndex);\n const typeName = reference.slice(separatorIndex + 1);\n if (filePath.length === 0 || typeName.length === 0) {\n throw new Error(\n `Expected a reference of the form \"<file>#<TypeName>\", got \"${reference}\".`,\n );\n }\n\n return { filePath, typeName };\n}\n\n/**\n * Create a ts-morph Project for resolving root messenger types.\n *\n * No file list is loaded: this strategy opens only the entry files and lets\n * the checker pull in the rest.\n *\n * @returns A new ts-morph Project.\n */\nfunction createRootMessengerProject(): TsMorphProject {\n return new Project({\n compilerOptions: {\n noEmit: true,\n // We need symbol resolution, not full typechecking, so a project's own\n // strictness settings shouldn't be able to fail the docs build.\n strict: false,\n skipLibCheck: true,\n target: ts.ScriptTarget.ESNext,\n module: ts.ModuleKind.ESNext,\n moduleResolution: ts.ModuleResolutionKind.NodeJs,\n },\n });\n}\n\n/**\n * Resolve the type alias a reference names.\n *\n * @param project - The ts-morph project to load the file into.\n * @param projectPath - Absolute path to the project root.\n * @param reference - The reference to resolve.\n * @param flagName - The CLI flag the reference came from, used in errors.\n * @returns The type alias declaration.\n * @throws If the file can't be read or declares no such type alias.\n */\nfunction resolveRootDeclaration(\n project: TsMorphProject,\n projectPath: string,\n reference: RootTypeReference,\n flagName: string,\n): TypeAliasDeclaration {\n const absolutePath = path.resolve(projectPath, reference.filePath);\n\n let sourceFile;\n try {\n sourceFile =\n project.getSourceFile(absolutePath) ??\n project.addSourceFileAtPath(absolutePath);\n } catch {\n throw new Error(\n `Could not read ${absolutePath}, which was named by ${flagName}.`,\n );\n }\n\n const declaration = sourceFile.getTypeAlias(reference.typeName);\n if (!declaration) {\n throw new Error(\n `No type alias named \"${reference.typeName}\" in ${reference.filePath}, which was named by ${flagName}.`,\n );\n }\n\n return declaration;\n}\n\n/**\n * Find the named declaration behind a union constituent.\n *\n * @param constituent - The resolved constituent type.\n * @param rootDeclaration - The root union's own declaration.\n * @param isLoneConstituent - Whether this is the root's only constituent.\n * @returns The declaration, or undefined when the constituent is anonymous.\n */\nfunction findCapabilityDeclaration(\n constituent: Type,\n rootDeclaration: TypeAliasDeclaration,\n isLoneConstituent: boolean,\n): TypeAliasDeclaration | InterfaceDeclaration | undefined {\n // Prefer the alias symbol: for a type alias it carries the name and JSDoc,\n // where the plain symbol points at the anonymous object type. Skip an alias\n // resolving back to the root union itself, which is what the checker reports\n // for a lone generic instantiation such as `type Actions = Foo<Bar>`.\n const aliasDeclarations = (\n constituent.getAliasSymbol()?.getDeclarations() ?? []\n ).filter((node) => node !== rootDeclaration);\n\n // An interface has no alias symbol, being its own declaration.\n const declarations =\n aliasDeclarations.length > 0\n ? aliasDeclarations\n : (constituent.getSymbol()?.getDeclarations() ?? []);\n\n const found = declarations.find(\n (node): node is TypeAliasDeclaration | InterfaceDeclaration =>\n NodeGuards.isTypeAliasDeclaration(node) ||\n NodeGuards.isInterfaceDeclaration(node),\n );\n if (found) {\n return found;\n }\n\n // Nothing named behind the type itself. When the root aliases a single\n // generic instantiation, the declaration we want is the one its type node\n // references — `Foo` in `type Actions = Foo<Bar>`. Only when it is the lone\n // constituent, though: in a union, an anonymous member is genuinely\n // anonymous, and attributing it to the wrapper would mislabel it.\n return isLoneConstituent\n ? findDeclarationFromRootTypeNode(rootDeclaration)\n : undefined;\n}\n\n/**\n * Resolve the declaration referenced by a root alias's type node.\n *\n * @param rootDeclaration - The root union's own declaration.\n * @returns The referenced declaration, or undefined.\n */\nfunction findDeclarationFromRootTypeNode(\n rootDeclaration: TypeAliasDeclaration,\n): TypeAliasDeclaration | InterfaceDeclaration | undefined {\n const typeNode = rootDeclaration.getTypeNode();\n if (!typeNode || !NodeGuards.isTypeReference(typeNode)) {\n return undefined;\n }\n\n const localSymbol = typeNode.getTypeName().getSymbol();\n const symbol = localSymbol?.getAliasedSymbol() ?? localSymbol;\n return symbol\n ?.getDeclarations()\n .find(\n (node): node is TypeAliasDeclaration | InterfaceDeclaration =>\n NodeGuards.isTypeAliasDeclaration(node) ||\n NodeGuards.isInterfaceDeclaration(node),\n );\n}\n\n/**\n * Render a short, single-line label for an anonymous type.\n *\n * @param type - The type to describe.\n * @param enclosingNode - Node to render the type relative to, so an aliased\n * type reads as its name rather than `import(\"<absolute path>\").Name`.\n * @returns The label.\n */\nfunction summarizeType(\n type: Type,\n enclosingNode: TypeAliasDeclaration,\n): string {\n const text = type.getText(enclosingNode).replace(/\\s+/gu, ' ');\n return text.length > 80 ? `${text.slice(0, 77)}...` : text;\n}\n\n/**\n * Extract every documentable capability from one root union.\n *\n * @param rootDeclaration - The root union's declaration.\n * @param kind - Whether these are actions or events.\n * @param projectPath - Absolute path to the project root.\n * @param skipped - Labels collected as undocumentable constituents are found.\n * @param reference - The reference that named this type, used in errors.\n * @param flagName - The CLI flag the reference came from, used in errors.\n * @returns The extracted capabilities.\n * @throws If the union resolved to `any` or `unknown`.\n */\nfunction extractFromRootType(\n rootDeclaration: TypeAliasDeclaration,\n kind: 'action' | 'event',\n projectPath: string,\n skipped: SkippedCapabilities,\n reference: RootTypeReference,\n flagName: string,\n): MessengerCapabilityPacket[] {\n const rootType = rootDeclaration.getTypeNodeOrThrow().getType();\n\n // TypeScript absorbs `any | T` into `any` and `unknown | T` into `unknown`,\n // so a single member the checker can't resolve — typically a failed import —\n // erases every other capability in the union. Fail instead of emitting a\n // catalog that looks complete but silently isn't.\n if (rootType.isAny() || rootType.isUnknown()) {\n throw new Error(\n `${reference.filePath}#${reference.typeName}, named by ${flagName}, ` +\n `resolved to \\`${rootType.getText()}\\` rather than a union of ` +\n `capabilities. This usually means an import in that file could not be ` +\n `resolved; because TypeScript absorbs the rest of a union into ` +\n `\\`any\\`, every other capability in it would be missing.`,\n );\n }\n\n // A project with no capabilities of this kind aliases the union to `never`.\n if (rootType.isNever()) {\n return [];\n }\n\n const constituents = rootType.isUnion()\n ? rootType.getUnionTypes()\n : [rootType];\n const packets: MessengerCapabilityPacket[] = [];\n\n for (const constituent of constituents) {\n const declaration = findCapabilityDeclaration(\n constituent,\n rootDeclaration,\n constituents.length === 1,\n );\n if (!declaration) {\n skipped.unnamed.push(summarizeType(constituent, rootDeclaration));\n continue;\n }\n\n const classified = classifyMessengerCapabilityTypeDeclaration(\n declaration,\n kind,\n );\n const packet =\n classified &&\n extractFromMessengerCapabilityTypeDeclaration(classified, projectPath);\n if (!packet) {\n const sourceFile = declaration.getSourceFile().getFilePath();\n skipped.unextractable.push(\n `${declaration.getName()} (${path.relative(projectPath, sourceFile)}:${declaration.getStartLineNumber()})`,\n );\n continue;\n }\n\n packets.push(packet);\n }\n\n return packets;\n}\n\n/**\n * Enumerate every action and event reachable from a project's root messenger.\n *\n * @param options - Discovery options.\n * @returns The extracted capabilities plus anything skipped.\n */\nexport function discoverFromRootMessenger(\n options: RootMessengerDiscoveryOptions,\n): RootMessengerDiscoveryResult {\n const { projectPath, actions, events } = options;\n const project = createRootMessengerProject();\n const skipped: SkippedCapabilities = { unnamed: [], unextractable: [] };\n const packets: MessengerCapabilityPacket[] = [];\n\n for (const [reference, kind, flagName] of [\n [actions, 'action', '--root-actions'],\n [events, 'event', '--root-events'],\n ] as const) {\n const rootDeclaration = resolveRootDeclaration(\n project,\n projectPath,\n reference,\n flagName,\n );\n packets.push(\n ...extractFromRootType(\n rootDeclaration,\n kind,\n projectPath,\n skipped,\n reference,\n flagName,\n ),\n );\n }\n\n return { packets, skipped };\n}\n"]}
|