@metamask-previews/platform-api-docs 0.0.0-preview-3e34650c2 → 0.0.0-preview-4f4c98e

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +34 -1
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/{cli.mjs → cli.js} +114 -57
  6. package/dist/cli.js.map +1 -0
  7. package/dist/{discovery.d.cts → discovery.d.ts} +1 -1
  8. package/dist/discovery.d.ts.map +1 -0
  9. package/dist/{discovery.mjs → discovery.js} +2 -2
  10. package/dist/discovery.js.map +1 -0
  11. package/dist/extraction.d.ts +83 -0
  12. package/dist/extraction.d.ts.map +1 -0
  13. package/dist/{extraction.mjs → extraction.js} +71 -47
  14. package/dist/extraction.js.map +1 -0
  15. package/dist/{generate.d.cts → generate.d.ts} +35 -11
  16. package/dist/generate.d.ts.map +1 -0
  17. package/dist/{generate.mjs → generate.js} +83 -15
  18. package/dist/generate.js.map +1 -0
  19. package/dist/{markdown.d.cts → markdown.d.ts} +2 -2
  20. package/dist/markdown.d.ts.map +1 -0
  21. package/dist/{markdown.mjs → markdown.js} +1 -1
  22. package/dist/markdown.js.map +1 -0
  23. package/dist/root-messenger-discovery.d.ts +61 -0
  24. package/dist/root-messenger-discovery.d.ts.map +1 -0
  25. package/dist/root-messenger-discovery.js +352 -0
  26. package/dist/root-messenger-discovery.js.map +1 -0
  27. package/dist/{types.d.cts → types.d.ts} +1 -1
  28. package/dist/types.d.ts.map +1 -0
  29. package/dist/types.js +2 -0
  30. package/dist/types.js.map +1 -0
  31. package/package.json +12 -8
  32. package/dist/cli.cjs +0 -241
  33. package/dist/cli.cjs.map +0 -1
  34. package/dist/cli.d.cts +0 -3
  35. package/dist/cli.d.cts.map +0 -1
  36. package/dist/cli.d.mts +0 -3
  37. package/dist/cli.d.mts.map +0 -1
  38. package/dist/cli.mjs.map +0 -1
  39. package/dist/discovery.cjs +0 -53
  40. package/dist/discovery.cjs.map +0 -1
  41. package/dist/discovery.d.cts.map +0 -1
  42. package/dist/discovery.d.mts +0 -22
  43. package/dist/discovery.d.mts.map +0 -1
  44. package/dist/discovery.mjs.map +0 -1
  45. package/dist/extraction.cjs +0 -754
  46. package/dist/extraction.cjs.map +0 -1
  47. package/dist/extraction.d.cts +0 -27
  48. package/dist/extraction.d.cts.map +0 -1
  49. package/dist/extraction.d.mts +0 -27
  50. package/dist/extraction.d.mts.map +0 -1
  51. package/dist/extraction.mjs.map +0 -1
  52. package/dist/generate.cjs +0 -394
  53. package/dist/generate.cjs.map +0 -1
  54. package/dist/generate.d.cts.map +0 -1
  55. package/dist/generate.d.mts +0 -46
  56. package/dist/generate.d.mts.map +0 -1
  57. package/dist/generate.mjs.map +0 -1
  58. package/dist/markdown.cjs +0 -239
  59. package/dist/markdown.cjs.map +0 -1
  60. package/dist/markdown.d.cts.map +0 -1
  61. package/dist/markdown.d.mts +0 -52
  62. package/dist/markdown.d.mts.map +0 -1
  63. package/dist/markdown.mjs.map +0 -1
  64. package/dist/types.cjs +0 -3
  65. package/dist/types.cjs.map +0 -1
  66. package/dist/types.d.cts.map +0 -1
  67. package/dist/types.d.mts +0 -47
  68. package/dist/types.d.mts.map +0 -1
  69. package/dist/types.mjs +0 -2
  70. package/dist/types.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -9,6 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
- - Initial release of the platform-api-docs package ([#8012](https://github.com/MetaMask/core/pull/8012))
12
+ - Initial release of the platform-api-docs package ([#8012](https://github.com/MetaMask/core/pull/8012), [#9913](https://github.com/MetaMask/core/pull/9913))
13
13
 
14
14
  [Unreleased]: https://github.com/MetaMask/core/
package/README.md CHANGED
@@ -35,12 +35,45 @@ Options:
35
35
  --build Generate docs and build static site
36
36
  --serve Generate docs, build, and serve static site
37
37
  --dev Generate docs and start dev server with hot reload
38
- --scan-dir <dir> Extra source directory to scan (repeatable)
38
+ --strategy <name> How to find actions and events: "scan" (default) or
39
+ "root-messenger" (see below)
40
+ --scan-dir <dir> Extra source directory to scan (repeatable; --strategy scan only)
41
+ --root-actions <ref> Type aliasing the union of every action, as "<file>#<TypeName>"
42
+ (required with --strategy root-messenger)
43
+ --root-events <ref> Type aliasing the union of every event, as "<file>#<TypeName>"
44
+ (required with --strategy root-messenger)
39
45
  --output <dir> Output directory (default: <project-path>/.platform-api-docs)
40
46
  --project-label <label> Short label identifying the project (e.g. "Core", "Extension")
41
47
  --help Show this help message
42
48
  ```
43
49
 
50
+ ## Strategies
51
+
52
+ Which strategy to use depends on whether the project has a single messenger carrying every action and event.
53
+
54
+ ### `scan` (default)
55
+
56
+ Parses every TypeScript source and declaration file it can find — the scan directories, `packages/*/src`, and `node_modules/@metamask/*/dist` — and reads every `*Messenger` type alias it encounters.
57
+
58
+ Use this when no single messenger aggregates every capability, as in a monorepo of independently published packages.
59
+
60
+ ### `root-messenger`
61
+
62
+ Resolves the two types the project declares for its root messenger capabilities — the collection of every action and the collection of every event — and lets the TypeScript type checker walk them. Only the files named on the command line are opened.
63
+
64
+ Use this when the project has one root messenger carrying every action and event, as a client application built on these packages does. It is substantially faster than `scan`, because it reads what the project already declares instead of re-deriving it, and it documents only what is reachable through that messenger.
65
+
66
+ ```
67
+ platform-api-docs \
68
+ --strategy root-messenger \
69
+ --root-actions 'src/messenger.ts#RootActions' \
70
+ --root-events 'src/messenger.ts#RootEvents'
71
+ ```
72
+
73
+ Each reference names a type alias, written by hand or computed — the type checker resolves either.
74
+
75
+ The docs contain exactly what the named types contain, so those types should be the ones carrying every capability rather than a narrowed subset. Capability types that can't be documented are reported rather than dropped silently: those declared inline in the capability collection type (with no name or JSDoc to document), and those whose shape can't be read (most often a `type` property that isn't a namespaced string literal).
76
+
44
77
  ## Contributing
45
78
 
46
79
  This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
@@ -1,36 +1,11 @@
1
1
  #!/usr/bin/env node
2
- function $__filename(fileUrl) {
3
- const url = new URL(fileUrl);
4
- return url.pathname.replace(/^\/([a-zA-Z]:)/u, "$1");
5
- }
6
- function $getDirname(path) {
7
- const sanitisedPath = path.toString().replace(/\\/gu, "/").replace(/\/$/u, "");
8
- const index = sanitisedPath.lastIndexOf("/");
9
- if (index === -1) {
10
- return path;
11
- }
12
- if (index === 0) {
13
- return "/";
14
- }
15
- return sanitisedPath.slice(0, index);
16
- }
17
- function $__dirname(url) {
18
- return $getDirname($__filename(url));
19
- }
20
- function $importDefault(module) {
21
- if (module?.__esModule) {
22
- return module.default;
23
- }
24
- return module;
25
- }
26
- import $execa from "execa/index.js";
27
- const execa = $importDefault($execa);
28
- import * as fs from "node:fs/promises";
29
- import * as path from "node:path";
30
- import $npmWhich from "npm-which";
31
- const npmWhich = $importDefault($npmWhich);
32
- import yargs from "yargs";
33
- import { generate, resolveRepoUrl } from "./generate.mjs";
2
+ import execa from 'execa';
3
+ import * as fs from 'node:fs/promises';
4
+ import * as path from 'node:path';
5
+ import npmWhich from 'npm-which';
6
+ import yargs from 'yargs';
7
+ import { generate, resolveRepoUrl } from './generate.js';
8
+ import { parseRootCapabilitiesTypeReference } from './root-messenger-discovery.js';
34
9
  /**
35
10
  * Locate the Docusaurus binary in this package's `node_modules/.bin`. Using
36
11
  * `npm-which` lets the lookup track wherever the installed Docusaurus puts
@@ -39,7 +14,7 @@ import { generate, resolveRepoUrl } from "./generate.mjs";
39
14
  * @returns Absolute path to the `docusaurus` executable.
40
15
  */
41
16
  function resolveDocusaurus() {
42
- return npmWhich($__dirname(import.meta.url)).sync('docusaurus');
17
+ return npmWhich(import.meta.dirname).sync('docusaurus');
43
18
  }
44
19
  /**
45
20
  * Run a Docusaurus command.
@@ -68,15 +43,11 @@ async function runDocusaurus(command, cwd, extraEnv = {}) {
68
43
  * @param outDir - The output directory to set up.
69
44
  */
70
45
  async function setupSite(outDir) {
71
- const packageDir = path.resolve($__dirname(import.meta.url), '..');
46
+ const packageDir = path.resolve(import.meta.dirname, '..');
72
47
  const siteDir = path.join(packageDir, 'site');
73
48
  const packageNodeModules = path.join(packageDir, 'node_modules');
74
49
  const skip = new Set(['node_modules', 'docs', 'tsconfig.json']);
75
50
  console.log(`\nSetting up Docusaurus site in ${outDir}...`);
76
- // `fs.cp` has been available since Node 16.7 and only got the "stable"
77
- // marker in 22.3 — it's functional throughout our supported Node range
78
- // (`^18.18 || >=20`), even though the linter flags the older versions.
79
- // eslint-disable-next-line n/no-unsupported-features/node-builtins
80
51
  await fs.cp(siteDir, outDir, {
81
52
  recursive: true,
82
53
  filter: (source) => !skip.has(path.basename(source)),
@@ -127,10 +98,45 @@ async function resolveCommitSha(projectPath) {
127
98
  }
128
99
  }
129
100
  /**
130
- * Main CLI entry point.
101
+ * Reject flag combinations that don't make sense, so a mistaken invocation
102
+ * fails instead of silently producing docs built the wrong way.
103
+ *
104
+ * Flags belonging to the strategy that wasn't selected are errors rather than
105
+ * ignored. Used as a yargs `.check`, so failures print alongside usage.
106
+ *
107
+ * @param argv - The parsed arguments.
108
+ * @param argv.strategy - The selected discovery strategy.
109
+ * @param argv.rootActions - The parsed root actions type reference.
110
+ * @param argv.rootEvents - The parsed root events type reference.
111
+ * @param argv.scanDir - The additional directories to scan.
112
+ * @returns True when the combination is valid.
131
113
  */
132
- async function main() {
133
- const argv = await yargs(process.argv.slice(2))
114
+ function checkStrategyArgs({ strategy, rootActions, rootEvents, scanDir = [], }) {
115
+ if (strategy !== 'root-messenger') {
116
+ if (rootActions !== undefined || rootEvents !== undefined) {
117
+ throw new Error('--root-actions and --root-events only apply to --strategy root-messenger.');
118
+ }
119
+ return { strategy: 'scan', scanDir };
120
+ }
121
+ if (rootActions === undefined || rootEvents === undefined) {
122
+ throw new Error('--strategy root-messenger requires both --root-actions and --root-events, ' +
123
+ 'each written as "<file>#<TypeName>".');
124
+ }
125
+ if (scanDir.length > 0) {
126
+ throw new Error('--scan-dir only applies to --strategy scan; --strategy root-messenger reads ' +
127
+ 'only the files named by --root-actions and --root-events.');
128
+ }
129
+ return { strategy, rootActions, rootEvents };
130
+ }
131
+ /**
132
+ * Parse and validate CLI arguments.
133
+ *
134
+ * @param argumentsForParsing - Arguments to parse, excluding the Node and
135
+ * script paths.
136
+ * @returns Parsed arguments, discriminated by discovery strategy.
137
+ */
138
+ async function parseArguments(argumentsForParsing = process.argv.slice(2)) {
139
+ const argv = await yargs(argumentsForParsing)
134
140
  .command('$0 [project-path]', 'Produces documentation for the platform API, the set of actions and events available in clients through the message bus.', (yargsInstance) => {
135
141
  yargsInstance.positional('project-path', {
136
142
  type: 'string',
@@ -153,9 +159,25 @@ async function main() {
153
159
  description: 'Generate platform API docs and serve a development-only site',
154
160
  default: false,
155
161
  })
156
- .option('scan-dir', {
162
+ .option('strategy', {
163
+ type: 'string',
164
+ description: 'How to find messenger actions and events. "scan" parses every source and declaration file looking for messenger types. "root-messenger" instead resolves the two types the project declares for its root messenger',
165
+ default: 'scan',
166
+ })
167
+ .choices('strategy', ['scan', 'root-messenger'])
168
+ .option('root-actions', {
169
+ type: 'string',
170
+ coerce: parseRootCapabilitiesTypeReference,
171
+ description: 'Type aliasing the union of every action on the root messenger, written as "<file>#<TypeName>" (required with --strategy root-messenger)',
172
+ })
173
+ .option('root-events', {
157
174
  type: 'string',
158
- array: true,
175
+ coerce: parseRootCapabilitiesTypeReference,
176
+ description: 'Type aliasing the union of every event on the root messenger, written as "<file>#<TypeName>" (required with --strategy root-messenger)',
177
+ })
178
+ .option('scan-dir', {
179
+ type: 'array',
180
+ string: true,
159
181
  description: 'Additional directories within the project to scan for messenger actions and events (note: may be specified multiple times)',
160
182
  default: [],
161
183
  })
@@ -175,25 +197,60 @@ async function main() {
175
197
  type: 'string',
176
198
  description: 'Path prefix the built site will be served under, e.g. /core/platform-api/',
177
199
  })
178
- .help().argv;
179
- const projectPathArg = argv['project-path'];
180
- const resolvedProjectPath = path.resolve(typeof projectPathArg === 'string' ? projectPathArg : '.');
200
+ .help()
201
+ .parse();
202
+ const strategyArguments = checkStrategyArgs(argv);
203
+ const projectPath = argv['project-path'];
204
+ if (typeof projectPath !== 'string') {
205
+ throw new Error('Expected --project-path to be a string.');
206
+ }
207
+ return {
208
+ build: argv.build,
209
+ serve: argv.serve,
210
+ dev: argv.dev,
211
+ 'project-path': projectPath,
212
+ output: argv.output,
213
+ 'project-label': argv['project-label'],
214
+ 'site-url': argv['site-url'],
215
+ 'site-base-url': argv['site-base-url'],
216
+ ...strategyArguments,
217
+ };
218
+ }
219
+ /**
220
+ * Main CLI entry point.
221
+ */
222
+ async function main() {
223
+ const argv = await parseArguments();
224
+ const resolvedProjectPath = path.resolve(argv['project-path']);
181
225
  const resolvedOutputDir = path.resolve(argv.output ?? path.join(resolvedProjectPath, '.platform-api-docs'));
182
- const scanDirs = ['src', ...argv['scan-dir']].filter((dir, index, dirs) => dirs.indexOf(dir) === index);
183
- const projectLabel = typeof argv['project-label'] === 'string' &&
184
- argv['project-label'].length > 0
226
+ const projectLabel = argv['project-label'] && argv['project-label'].length > 0
185
227
  ? argv['project-label']
186
228
  : null;
187
229
  const commitSha = await resolveCommitSha(resolvedProjectPath);
188
230
  const repoUrl = await resolveRepoUrl(resolvedProjectPath);
189
231
  // Step 1: Generate docs
190
- await generate({
191
- projectPath: resolvedProjectPath,
192
- outputDir: resolvedOutputDir,
193
- scanDirs,
194
- projectLabel,
195
- commitSha,
196
- });
232
+ if (argv.strategy === 'root-messenger') {
233
+ await generate({
234
+ projectPath: resolvedProjectPath,
235
+ outputDir: resolvedOutputDir,
236
+ projectLabel,
237
+ commitSha,
238
+ strategy: 'root-messenger',
239
+ rootActions: argv.rootActions,
240
+ rootEvents: argv.rootEvents,
241
+ });
242
+ }
243
+ else {
244
+ const scanDirs = ['src', ...argv.scanDir].filter((dir, index, dirs) => dirs.indexOf(dir) === index);
245
+ await generate({
246
+ projectPath: resolvedProjectPath,
247
+ outputDir: resolvedOutputDir,
248
+ projectLabel,
249
+ commitSha,
250
+ strategy: 'scan',
251
+ scanDirs,
252
+ });
253
+ }
197
254
  // Step 2: If --build, --serve, or --dev, set up and run Docusaurus
198
255
  if (argv.build || argv.serve || argv.dev) {
199
256
  await setupSite(resolvedOutputDir);
@@ -236,4 +293,4 @@ main().catch((error) => {
236
293
  console.error(error);
237
294
  process.exitCode = 1;
238
295
  });
239
- //# sourceMappingURL=cli.mjs.map
296
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport execa from 'execa';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport npmWhich from 'npm-which';\nimport yargs from 'yargs';\n\nimport { generate, resolveRepoUrl } from './generate.js';\nimport type { RootCapabilitiesTypeReference } from './root-messenger-discovery.js';\nimport { parseRootCapabilitiesTypeReference } from './root-messenger-discovery.js';\n\n/**\n * Arguments shared by both discovery strategies.\n */\ntype CommonArguments = {\n /** Whether to build the generated site. */\n build: boolean;\n /** Whether to serve the production build. */\n serve: boolean;\n /** Whether to serve the development site. */\n dev: boolean;\n /** Path to the project being documented. */\n 'project-path': string;\n /** Directory where generated documentation is written. */\n output?: string;\n /** Label displayed in the generated documentation. */\n 'project-label'?: string;\n /** URL where the generated site is served. */\n 'site-url'?: string;\n /** Base path where the generated site is served. */\n 'site-base-url'?: string;\n};\n\n/**\n * Arguments for the strategy that scans configured source directories.\n */\ntype ScanStrategyArguments = {\n /** The selected strategy. */\n strategy: 'scan';\n /** Directories, relative to the project root, to scan. */\n scanDir: string[];\n};\n\n/**\n * Arguments for the strategy that resolves a root messenger's type unions.\n */\ntype RootMessengerStrategyArguments = {\n /** The selected strategy. */\n strategy: 'root-messenger';\n /** The root messenger actions type reference. */\n rootActions: RootCapabilitiesTypeReference;\n /** The root messenger events type reference. */\n rootEvents: RootCapabilitiesTypeReference;\n};\n\n/**\n * Parsed CLI arguments, discriminated by the selected discovery strategy.\n */\ntype ParsedArguments = CommonArguments &\n (ScanStrategyArguments | RootMessengerStrategyArguments);\n\n/**\n * Locate the Docusaurus binary in this package's `node_modules/.bin`. Using\n * `npm-which` lets the lookup track wherever the installed Docusaurus puts\n * its binary, so a future Docusaurus upgrade can't break this path.\n *\n * @returns Absolute path to the `docusaurus` executable.\n */\nfunction resolveDocusaurus(): string {\n return npmWhich(import.meta.dirname).sync('docusaurus');\n}\n\n/**\n * Run a Docusaurus command.\n *\n * @param command - The docusaurus command (start, build, serve).\n * @param cwd - The site directory.\n * @param extraEnv - Extra environment variables passed through to the\n * Docusaurus process (e.g. `DOCS_PROJECT_LABEL`, `DOCS_COMMIT_SHA`,\n * `DOCS_REPO_URL`).\n */\nasync function runDocusaurus(\n command: string,\n cwd: string,\n extraEnv: Record<string, string> = {},\n): Promise<void> {\n await execa(resolveDocusaurus(), [command], {\n cwd,\n stdio: 'inherit',\n env: { ...process.env, ...extraEnv },\n });\n}\n\n/**\n * Copy site files into the output directory, skipping `node_modules`, `docs`,\n * and `tsconfig.json`. `docs` is owned by the doc generator and shouldn't be\n * carried over from the source `site/` directory. `tsconfig.json` extends the\n * monorepo's `tsconfig.base.json` via a relative path that only resolves from\n * the source location — it's there for IDE / lint inheritance, not for\n * Docusaurus, which uses `jiti` and doesn't consult the tsconfig at runtime.\n *\n * @param outDir - The output directory to set up.\n */\nasync function setupSite(outDir: string): Promise<void> {\n const packageDir = path.resolve(import.meta.dirname, '..');\n const siteDir = path.join(packageDir, 'site');\n const packageNodeModules = path.join(packageDir, 'node_modules');\n const skip = new Set(['node_modules', 'docs', 'tsconfig.json']);\n\n console.log(`\\nSetting up Docusaurus site in ${outDir}...`);\n\n await fs.cp(siteDir, outDir, {\n recursive: true,\n filter: (source) => !skip.has(path.basename(source)),\n });\n\n // Symlink this package's `node_modules` into the output so the copied\n // `docusaurus.config.ts` and the rest of Docusaurus's bundling pipeline can\n // resolve their deps the same way they do in the source tree. Without it,\n // Node's resolver walks up from the output and can't reach our nested deps\n // when the package is installed as a regular dependency by an external\n // consumer (e.g. `metamask-extension`, `metamask-mobile`).\n const linkPath = path.join(outDir, 'node_modules');\n try {\n // `'junction'` works cross-platform (POSIX ignores it; Windows uses it\n // without admin) — `'dir'` would require admin on Windows.\n await fs.symlink(packageNodeModules, linkPath, 'junction');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n\n // Write a minimal package.json so Docusaurus doesn't warn about a missing one\n const pkgJsonPath = path.join(outDir, 'package.json');\n try {\n await fs.access(pkgJsonPath);\n } catch {\n await fs.writeFile(\n pkgJsonPath,\n JSON.stringify(\n { name: 'platform-api-docs-site', private: true },\n null,\n 2,\n ),\n );\n }\n}\n\n/**\n * Resolve the short Git commit SHA the docs are being generated from.\n * Returns null when the project isn't a git repo or git isn't available.\n *\n * @param projectPath - The project root path.\n * @returns The short SHA, or null on failure.\n */\nasync function resolveCommitSha(projectPath: string): Promise<string | null> {\n try {\n const { stdout } = await execa('git', ['rev-parse', '--short', 'HEAD'], {\n cwd: projectPath,\n });\n const trimmed = stdout.trim();\n return trimmed.length > 0 ? trimmed : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Reject flag combinations that don't make sense, so a mistaken invocation\n * fails instead of silently producing docs built the wrong way.\n *\n * Flags belonging to the strategy that wasn't selected are errors rather than\n * ignored. Used as a yargs `.check`, so failures print alongside usage.\n *\n * @param argv - The parsed arguments.\n * @param argv.strategy - The selected discovery strategy.\n * @param argv.rootActions - The parsed root actions type reference.\n * @param argv.rootEvents - The parsed root events type reference.\n * @param argv.scanDir - The additional directories to scan.\n * @returns True when the combination is valid.\n */\nfunction checkStrategyArgs({\n strategy,\n rootActions,\n rootEvents,\n scanDir = [],\n}: {\n strategy?: 'root-messenger' | 'scan';\n rootActions?: RootCapabilitiesTypeReference;\n rootEvents?: RootCapabilitiesTypeReference;\n scanDir?: string[];\n}): ScanStrategyArguments | RootMessengerStrategyArguments {\n if (strategy !== 'root-messenger') {\n if (rootActions !== undefined || rootEvents !== undefined) {\n throw new Error(\n '--root-actions and --root-events only apply to --strategy root-messenger.',\n );\n }\n return { strategy: 'scan', scanDir };\n }\n\n if (rootActions === undefined || rootEvents === undefined) {\n throw new Error(\n '--strategy root-messenger requires both --root-actions and --root-events, ' +\n 'each written as \"<file>#<TypeName>\".',\n );\n }\n if (scanDir.length > 0) {\n throw new Error(\n '--scan-dir only applies to --strategy scan; --strategy root-messenger reads ' +\n 'only the files named by --root-actions and --root-events.',\n );\n }\n\n return { strategy, rootActions, rootEvents };\n}\n\n/**\n * Parse and validate CLI arguments.\n *\n * @param argumentsForParsing - Arguments to parse, excluding the Node and\n * script paths.\n * @returns Parsed arguments, discriminated by discovery strategy.\n */\nasync function parseArguments(\n argumentsForParsing: string[] = process.argv.slice(2),\n): Promise<ParsedArguments> {\n const argv = await yargs(argumentsForParsing)\n .command(\n '$0 [project-path]',\n 'Produces documentation for the platform API, the set of actions and events available in clients through the message bus.',\n (yargsInstance) => {\n yargsInstance.positional('project-path', {\n type: 'string',\n description: 'Path to the project to scan',\n default: '.',\n });\n },\n )\n .option('build', {\n type: 'boolean',\n description:\n 'Generate platform API docs and build a production-ready site',\n default: false,\n })\n .option('serve', {\n type: 'boolean',\n description:\n 'Generate platform API docs and serve a production-ready site',\n default: false,\n })\n .option('dev', {\n type: 'boolean',\n description:\n 'Generate platform API docs and serve a development-only site',\n default: false,\n })\n .option('strategy', {\n type: 'string',\n description:\n 'How to find messenger actions and events. \"scan\" parses every source and declaration file looking for messenger types. \"root-messenger\" instead resolves the two types the project declares for its root messenger',\n default: 'scan',\n })\n .choices('strategy', ['scan', 'root-messenger'] as const)\n .option('root-actions', {\n type: 'string',\n coerce: parseRootCapabilitiesTypeReference,\n description:\n 'Type aliasing the union of every action on the root messenger, written as \"<file>#<TypeName>\" (required with --strategy root-messenger)',\n })\n .option('root-events', {\n type: 'string',\n coerce: parseRootCapabilitiesTypeReference,\n description:\n 'Type aliasing the union of every event on the root messenger, written as \"<file>#<TypeName>\" (required with --strategy root-messenger)',\n })\n .option('scan-dir', {\n type: 'array',\n string: true,\n description:\n 'Additional directories within the project to scan for messenger actions and events (note: may be specified multiple times)',\n default: [],\n })\n .option('output', {\n type: 'string',\n description: 'Output directory',\n })\n .option('project-label', {\n type: 'string',\n description:\n 'Short label identifying the project (e.g. \"Core\", \"Extension\") — stamped on the site title and headings',\n })\n .option('site-url', {\n type: 'string',\n description:\n 'Absolute URL the built site will be served from, e.g. https://metamask.github.io',\n })\n .option('site-base-url', {\n type: 'string',\n description:\n 'Path prefix the built site will be served under, e.g. /core/platform-api/',\n })\n .help()\n .parse();\n\n const strategyArguments = checkStrategyArgs(argv);\n\n const projectPath = argv['project-path'];\n if (typeof projectPath !== 'string') {\n throw new Error('Expected --project-path to be a string.');\n }\n\n return {\n build: argv.build,\n serve: argv.serve,\n dev: argv.dev,\n 'project-path': projectPath,\n output: argv.output,\n 'project-label': argv['project-label'],\n 'site-url': argv['site-url'],\n 'site-base-url': argv['site-base-url'],\n ...strategyArguments,\n };\n}\n\n/**\n * Main CLI entry point.\n */\nasync function main(): Promise<void> {\n const argv = await parseArguments();\n\n const resolvedProjectPath = path.resolve(argv['project-path']);\n const resolvedOutputDir = path.resolve(\n argv.output ?? path.join(resolvedProjectPath, '.platform-api-docs'),\n );\n const projectLabel =\n argv['project-label'] && argv['project-label'].length > 0\n ? argv['project-label']\n : null;\n const commitSha = await resolveCommitSha(resolvedProjectPath);\n const repoUrl = await resolveRepoUrl(resolvedProjectPath);\n\n // Step 1: Generate docs\n if (argv.strategy === 'root-messenger') {\n await generate({\n projectPath: resolvedProjectPath,\n outputDir: resolvedOutputDir,\n projectLabel,\n commitSha,\n strategy: 'root-messenger',\n rootActions: argv.rootActions,\n rootEvents: argv.rootEvents,\n });\n } else {\n const scanDirs = ['src', ...argv.scanDir].filter(\n (dir, index, dirs) => dirs.indexOf(dir) === index,\n );\n\n await generate({\n projectPath: resolvedProjectPath,\n outputDir: resolvedOutputDir,\n projectLabel,\n commitSha,\n strategy: 'scan',\n scanDirs,\n });\n }\n\n // Step 2: If --build, --serve, or --dev, set up and run Docusaurus\n if (argv.build || argv.serve || argv.dev) {\n await setupSite(resolvedOutputDir);\n\n // Translate CLI flags into the environment variables Docusaurus's\n // config reads. Keeping the CLI surface flag-only means consumers\n // (workflow files, package.json scripts) don't have to know how the\n // values are plumbed through to Docusaurus.\n const docusaurusEnv: Record<string, string> = {};\n if (projectLabel) {\n docusaurusEnv.DOCS_PROJECT_LABEL = projectLabel;\n }\n if (commitSha) {\n docusaurusEnv.DOCS_COMMIT_SHA = commitSha;\n }\n if (repoUrl) {\n docusaurusEnv.DOCS_REPO_URL = repoUrl;\n }\n if (typeof argv['site-url'] === 'string' && argv['site-url'].length > 0) {\n docusaurusEnv.DOCS_URL = argv['site-url'];\n }\n if (\n typeof argv['site-base-url'] === 'string' &&\n argv['site-base-url'].length > 0\n ) {\n docusaurusEnv.DOCS_BASE_URL = argv['site-base-url'];\n }\n\n if (argv.dev) {\n console.log('\\nStarting dev server...');\n await runDocusaurus('start', resolvedOutputDir, docusaurusEnv);\n } else if (argv.build || argv.serve) {\n console.log('\\nBuilding static site...');\n await runDocusaurus('build', resolvedOutputDir, docusaurusEnv);\n\n if (argv.serve) {\n console.log('\\nServing static site...');\n await runDocusaurus('serve', resolvedOutputDir, docusaurusEnv);\n }\n }\n }\n}\n\nmain().catch((error) => {\n console.error(error);\n process.exitCode = 1;\n});\n"]}
@@ -19,4 +19,4 @@ export declare function findTsFiles(dir: string): Promise<string[]>;
19
19
  * @returns A promise that resolves to a sorted array of absolute file paths.
20
20
  */
21
21
  export declare function findDtsFiles(dir: string): Promise<string[]>;
22
- //# sourceMappingURL=discovery.d.cts.map
22
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkBhE;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOjE"}
@@ -1,4 +1,4 @@
1
- import { glob } from "glob";
1
+ import { glob } from 'glob';
2
2
  /**
3
3
  * Find all non-test TypeScript source files in a directory.
4
4
  * Skips node_modules, dist, test directories, and declaration files.
@@ -45,4 +45,4 @@ export async function findDtsFiles(dir) {
45
45
  });
46
46
  return matches.sort();
47
47
  }
48
- //# sourceMappingURL=discovery.mjs.map
48
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW;IAC3C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE;QACpC,GAAG,EAAE,GAAG;QACR,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE;YACN,oBAAoB;YACpB,YAAY;YACZ,iBAAiB;YACjB,aAAa;YACb,YAAY;YACZ,iBAAiB;YACjB,cAAc;YACd,gBAAgB;YAChB,cAAc;YACd,WAAW;SACZ;KACF,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW;IAC5C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE;QACvC,GAAG,EAAE,GAAG;QACR,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,CAAC,oBAAoB,CAAC;KAC/B,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC","sourcesContent":["import { glob } from 'glob';\n\n/**\n * Find all non-test TypeScript source files in a directory.\n * Skips node_modules, dist, test directories, and declaration files.\n *\n * Results are sorted lexicographically so that downstream consumers\n * (extraction, deduplication, output ordering) behave deterministically\n * across filesystems.\n *\n * @param dir - The directory to search.\n * @returns A promise that resolves to a sorted array of absolute file paths.\n */\nexport async function findTsFiles(dir: string): Promise<string[]> {\n const matches = await glob('**/*.ts', {\n cwd: dir,\n absolute: true,\n ignore: [\n '**/node_modules/**',\n '**/dist/**',\n '**/__tests__/**',\n '**/tests/**',\n '**/test/**',\n '**/__mocks__/**',\n '**/*.test.ts',\n '**/*.test-d.ts',\n '**/*.spec.ts',\n '**/*.d.ts',\n ],\n });\n return matches.sort();\n}\n\n/**\n * Find all `.d.cts` declaration files in a directory.\n * Skips nested node_modules subdirectories. See {@link findTsFiles} for the\n * note about sorting.\n *\n * @param dir - The directory to search.\n * @returns A promise that resolves to a sorted array of absolute file paths.\n */\nexport async function findDtsFiles(dir: string): Promise<string[]> {\n const matches = await glob('**/*.d.cts', {\n cwd: dir,\n absolute: true,\n ignore: ['**/node_modules/**'],\n });\n return matches.sort();\n}\n"]}
@@ -0,0 +1,83 @@
1
+ import type { Identifier, InterfaceDeclaration, SourceFile, TypeAliasDeclaration, TypeReferenceNode } from 'ts-morph';
2
+ import { Project } from 'ts-morph';
3
+ import type { MessengerCapabilityPacket } from './types.js';
4
+ /**
5
+ * A messenger capability type whose body invokes a capability-type-constructor
6
+ * utility such as `ControllerGetStateAction<...>` or
7
+ * `ControllerStateChangeEvent<...>`. The walker classifies the body once when
8
+ * it captures the declaration so the extractor can read the body's type name
9
+ * and type arguments without re-running the AST guards.
10
+ */
11
+ type ConstructorMessengerCapabilityTypeDeclaration = {
12
+ bodyShape: 'constructor';
13
+ kind: 'action' | 'event';
14
+ declaration: TypeAliasDeclaration;
15
+ body: TypeReferenceNode;
16
+ typeName: Identifier;
17
+ };
18
+ /**
19
+ * A messenger capability type whose declaration carries the action/event
20
+ * shape directly — either an interface or a type alias for a type literal.
21
+ * The extractor reads `type`, `handler`, and `payload` from the members.
22
+ */
23
+ type ObjectMessengerCapabilityTypeDeclaration = {
24
+ bodyShape: 'object';
25
+ kind: 'action' | 'event';
26
+ declaration: TypeAliasDeclaration | InterfaceDeclaration;
27
+ };
28
+ /**
29
+ * Represents a type declaration (type alias or interface) for an individual
30
+ * messenger action or event, tagged with the body shape the walker
31
+ * identified.
32
+ */
33
+ type MessengerCapabilityTypeDeclaration = ConstructorMessengerCapabilityTypeDeclaration | ObjectMessengerCapabilityTypeDeclaration;
34
+ /**
35
+ * Tag a capability type declaration with the body shape it has, so the right
36
+ * extractor can read it: 'constructor' for a capability-type-constructor
37
+ * invocation such as `ControllerGetStateAction<...>`, 'object' otherwise.
38
+ *
39
+ * Callers must resolve unions and bare type references first, so that the
40
+ * declaration reaching here is a leaf.
41
+ *
42
+ * @param declaration - The type alias or interface to classify.
43
+ * @param kind - Whether to tag the declaration as 'action' or 'event'.
44
+ * @returns The tagged declaration, or null for a qualified-name reference.
45
+ */
46
+ export declare function classifyMessengerCapabilityTypeDeclaration(declaration: TypeAliasDeclaration | InterfaceDeclaration, kind: 'action' | 'event'): MessengerCapabilityTypeDeclaration | null;
47
+ /**
48
+ * Given the declaration of a messenger capability type, extract information
49
+ * about it (action/event type string, handler/payload arguments and return
50
+ * type, etc.)
51
+ *
52
+ * @param capabilityTypeDeclaration - The statement that declared the type for a
53
+ * messenger action or event, extracted in a previous step.
54
+ * @param projectPath - Project root, used for computing relative source paths.
55
+ * @returns Information that may be extracted from the messenger capability type
56
+ * (may be `null` if the type is ineligible for extraction).
57
+ */
58
+ export declare function extractFromMessengerCapabilityTypeDeclaration(capabilityTypeDeclaration: MessengerCapabilityTypeDeclaration, projectPath: string): MessengerCapabilityPacket | null;
59
+ /**
60
+ * Create a ts-morph Project configured for messenger-docs extraction. The
61
+ * caller should add every source file that may be referenced (directly or
62
+ * transitively) before calling {@link extractFromSourceFile}, so the type
63
+ * checker can resolve cross-file references.
64
+ *
65
+ * @returns A new ts-morph Project.
66
+ */
67
+ export declare function createExtractionProject(): Project;
68
+ /**
69
+ * Extract information (action/event type string, handler/payload arguments and
70
+ * return type, etc.) about every messenger action or event type which is
71
+ * reachable through all of a source file's `*Messenger` type declarations.
72
+ *
73
+ * The caller is responsible for ensuring `sourceFile` (plus any files it
74
+ * imports from) belongs to a `ts-morph` Project so cross-file symbol resolution
75
+ * works.
76
+ *
77
+ * @param sourceFile - The TypeScript source file to extract from.
78
+ * @param projectPath - Project root, used for computing relative source paths.
79
+ * @returns The extracted information about actions and events.
80
+ */
81
+ export declare function extractFromSourceFile(sourceFile: SourceFile, projectPath: string): MessengerCapabilityPacket[];
82
+ export {};
83
+ //# sourceMappingURL=extraction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../src/extraction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EAMpB,UAAU,EACV,oBAAoB,EAGpB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAsB,OAAO,EAAM,MAAM,UAAU,CAAC;AAE3D,OAAO,KAAK,EACV,yBAAyB,EAE1B,MAAM,YAAY,CAAC;AAiQpB;;;;;;GAMG;AACH,KAAK,6CAA6C,GAAG;IACnD,SAAS,EAAE,aAAa,CAAC;IACzB,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,WAAW,EAAE,oBAAoB,CAAC;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,UAAU,CAAC;CACtB,CAAC;AAEF;;;;GAIG;AACH,KAAK,wCAAwC,GAAG;IAC9C,SAAS,EAAE,QAAQ,CAAC;IACpB,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,WAAW,EAAE,oBAAoB,GAAG,oBAAoB,CAAC;CAC1D,CAAC;AAEF;;;;GAIG;AACH,KAAK,kCAAkC,GACnC,6CAA6C,GAC7C,wCAAwC,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,wBAAgB,0CAA0C,CACxD,WAAW,EAAE,oBAAoB,GAAG,oBAAoB,EACxD,IAAI,EAAE,QAAQ,GAAG,OAAO,GACvB,kCAAkC,GAAG,IAAI,CA4C3C;AA2QD;;;;;;;;;;GAUG;AACH,wBAAgB,6CAA6C,CAC3D,yBAAyB,EAAE,kCAAkC,EAC7D,WAAW,EAAE,MAAM,GAClB,yBAAyB,GAAG,IAAI,CAWlC;AA4QD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAgBjD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,MAAM,GAClB,yBAAyB,EAAE,CAkB7B"}
@@ -1,5 +1,5 @@
1
- import * as path from "node:path";
2
- import { Node as NodeGuards, Project, ts } from "ts-morph";
1
+ import * as path from 'node:path';
2
+ import { Node as NodeGuards, Project, ts } from 'ts-morph';
3
3
  // ---------------------------------------------------------------------------
4
4
  // NOTE: `ts-morph` is used heavily in this file to parse and extract
5
5
  // information from TypeScript files. Although this library is not well
@@ -19,15 +19,11 @@ import { Node as NodeGuards, Project, ts } from "ts-morph";
19
19
  */
20
20
  function escapeJsDocTextForMdx(text) {
21
21
  const withLinksResolved = text.replace(/\{@link\s+([^}]+)\}/gu, '`$1`');
22
- return withLinksResolved.replace(/`[^`]*`|(\{)|(\})/gu, (match, open, close) => {
23
- if (open) {
24
- return '\\{';
25
- }
26
- if (close) {
27
- return '\\}';
28
- }
29
- return match;
30
- });
22
+ // Escape the characters MDX reads as syntax rather than text: `{` and `}`
23
+ // open an expression, and `<` opens a JSX tag — so an unescaped return type
24
+ // like `Promise<Foo[]>` fails the site build. Content already inside a code
25
+ // span is left alone.
26
+ return withLinksResolved.replace(/`[^`]*`|([{}<])/gu, (match, special) => special === undefined ? match : `\\${special}`);
31
27
  }
32
28
  /**
33
29
  * Extract the comment text of a JSDoc tag — the part that comes after the tag
@@ -216,6 +212,59 @@ function buildMethodSignature(method) {
216
212
  // so we don't need to wrap again.
217
213
  return `(${signatureParams}) => ${returnType}`;
218
214
  }
215
+ /**
216
+ * Tag a capability type declaration with the body shape it has, so the right
217
+ * extractor can read it: 'constructor' for a capability-type-constructor
218
+ * invocation such as `ControllerGetStateAction<...>`, 'object' otherwise.
219
+ *
220
+ * Callers must resolve unions and bare type references first, so that the
221
+ * declaration reaching here is a leaf.
222
+ *
223
+ * @param declaration - The type alias or interface to classify.
224
+ * @param kind - Whether to tag the declaration as 'action' or 'event'.
225
+ * @returns The tagged declaration, or null for a qualified-name reference.
226
+ */
227
+ export function classifyMessengerCapabilityTypeDeclaration(declaration, kind) {
228
+ // Interfaces always carry their members directly.
229
+ // EXAMPLE:
230
+ // interface FooControllerSomeAction { ... }
231
+ if (NodeGuards.isInterfaceDeclaration(declaration)) {
232
+ return { bodyShape: 'object', kind, declaration };
233
+ }
234
+ const body = declaration.getTypeNode();
235
+ // A TypeReference body is a capability-type-constructor invocation (e.g.
236
+ // `ControllerGetStateAction<typeof name, State>`). Tag it so the constructor
237
+ // extractor can read `body` directly without re-checking its shape.
238
+ // EXAMPLE:
239
+ // type FooControllerSomeAction = ControllerGetStateAction<...>
240
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
241
+ if (body && NodeGuards.isTypeReference(body)) {
242
+ // Reject qualified-name constructor type names, as we need a plain
243
+ // identifier to match the constructor by name.
244
+ // EXAMPLE:
245
+ // import * as somePackage from '....js';
246
+ // type FooControllerSomeAction = somePackage.ControllerGetStateAction<...>
247
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
248
+ const constructorTypeName = body.getTypeName();
249
+ if (!NodeGuards.isIdentifier(constructorTypeName)) {
250
+ return null;
251
+ }
252
+ return {
253
+ bodyShape: 'constructor',
254
+ kind,
255
+ declaration,
256
+ body,
257
+ typeName: constructorTypeName,
258
+ };
259
+ }
260
+ // Anything else (a type literal, intersection, conditional, …) goes to the
261
+ // literal extractor, which knows how to read members off a type literal and
262
+ // rejects exotic shapes.
263
+ // EXAMPLE:
264
+ // type FooControllerSomeAction = { ... }
265
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
266
+ return { bodyShape: 'object', kind, declaration };
267
+ }
219
268
  /**
220
269
  * Looks for Messenger types in the source file (that is, those that are type
221
270
  * aliases whose names end with "Messenger"), then extracts the `Actions` and
@@ -379,44 +428,19 @@ function recursivelyFindMessengerCapabilityTypeDeclarations(node, kind, visitedT
379
428
  }
380
429
  continue;
381
430
  }
382
- // A TypeReference body with type arguments is a capability-type-
431
+ // Everything else is a leaf capability type — a capability-type-
383
432
  // constructor invocation (e.g. `ControllerGetStateAction<typeof name,
384
- // State>`). Tag it so the constructor extractor can read `body`
385
- // directly without re-checking its shape.
386
- // EXAMPLE:
433
+ // State>`) or a literal object type. Tag it so the matching extractor
434
+ // can read it without re-checking its shape.
435
+ // EXAMPLES:
387
436
  // type FooControllerSomeAction = ControllerGetStateAction<...>
388
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
389
- if (body && NodeGuards.isTypeReference(body)) {
390
- // Reject qualified-name constructor type names, as we need a plain
391
- // identifier to match the constructor by name.
392
- // EXAMPLE:
393
- // // Bad
394
- // import * as somePackage from '....js';
395
- // type FooControllerSomeAction = somePackage.ControllerGetStateAction<...>
396
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
397
- const constructorTypeName = body.getTypeName();
398
- if (NodeGuards.isIdentifier(constructorTypeName)) {
399
- result.capabilityTypeDeclarations.push({
400
- bodyShape: 'constructor',
401
- kind,
402
- declaration,
403
- body,
404
- typeName: constructorTypeName,
405
- });
406
- }
407
- continue;
408
- }
409
- // Anything else (a type literal, intersection, conditional, …) gets
410
- // tagged for the literal extractor, which knows how to read members
411
- // off a type literal and rejects exotic shapes.
412
- // EXAMPLE:
437
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
413
438
  // type FooControllerSomeAction = { ... }
414
439
  // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
415
- result.capabilityTypeDeclarations.push({
416
- bodyShape: 'object',
417
- kind,
418
- declaration,
419
- });
440
+ const classified = classifyMessengerCapabilityTypeDeclaration(declaration, kind);
441
+ if (classified) {
442
+ result.capabilityTypeDeclarations.push(classified);
443
+ }
420
444
  }
421
445
  // Interfaces always carry their members directly — tag for the literal
422
446
  // extractor.
@@ -447,7 +471,7 @@ function recursivelyFindMessengerCapabilityTypeDeclarations(node, kind, visitedT
447
471
  * @returns Information that may be extracted from the messenger capability type
448
472
  * (may be `null` if the type is ineligible for extraction).
449
473
  */
450
- function extractFromMessengerCapabilityTypeDeclaration(capabilityTypeDeclaration, projectPath) {
474
+ export function extractFromMessengerCapabilityTypeDeclaration(capabilityTypeDeclaration, projectPath) {
451
475
  if (capabilityTypeDeclaration.bodyShape === 'constructor') {
452
476
  return tryToExtractFromCapabilityTypeConstructor(capabilityTypeDeclaration, projectPath);
453
477
  }
@@ -723,4 +747,4 @@ export function extractFromSourceFile(sourceFile, projectPath) {
723
747
  }
724
748
  return messengerCapabilityPackets;
725
749
  }
726
- //# sourceMappingURL=extraction.mjs.map
750
+ //# sourceMappingURL=extraction.js.map