Skip to content

Commit 658f090

Browse files
committed
fix: resolve React 18 via webpack plugin, add --scan-dir flag
1 parent 188601e commit 658f090

File tree

4 files changed

+100
-42
lines changed

4 files changed

+100
-42
lines changed

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const config = createConfig([
1616
'.yarn/**',
1717
'scripts/create-package/package-template/**',
1818
'.messenger-docs/**',
19+
'packages/messenger-docs/template/**',
1920
],
2021
},
2122
{

packages/messenger-docs/src/cli.ts

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ async function main(): Promise<void> {
8585
let dev = false;
8686
let outputDir: string | undefined;
8787
let projectPath: string | undefined;
88+
const scanDirs: string[] = [];
8889

8990
for (let i = 0; i < args.length; i += 1) {
9091
const arg = args[i];
@@ -107,6 +108,15 @@ async function main(): Promise<void> {
107108
return;
108109
}
109110
break;
111+
case '--scan-dir':
112+
i += 1;
113+
if (!args[i] || args[i].startsWith('-')) {
114+
console.error('Error: --scan-dir requires a path argument');
115+
process.exitCode = 1;
116+
return;
117+
}
118+
scanDirs.push(args[i]);
119+
break;
110120
case '--help':
111121
printHelp();
112122
return;
@@ -132,6 +142,7 @@ async function main(): Promise<void> {
132142
await generate({
133143
projectPath: resolvedProjectPath,
134144
outputDir: resolvedOutputDir,
145+
...(scanDirs.length > 0 ? { scanDirs } : {}),
135146
});
136147

137148
// Step 2: If --build, --serve, or --dev, set up and run Docusaurus
@@ -140,25 +151,6 @@ async function main(): Promise<void> {
140151

141152
const { bin: docusaurus, nodeModules } = resolveDocusaurus();
142153

143-
// Symlink node_modules into the output directory so Docusaurus resolves
144-
// React 18 and other deps from this package, not the host project.
145-
const nmLink = path.join(resolvedOutputDir, 'node_modules');
146-
try {
147-
await fs.lstat(nmLink);
148-
// Already exists — remove only if it's a symlink we previously created.
149-
const target = await fs.readlink(nmLink).catch(() => null);
150-
if (target !== null) {
151-
await fs.unlink(nmLink);
152-
}
153-
} catch {
154-
// Doesn't exist yet — nothing to remove.
155-
}
156-
try {
157-
await fs.access(nmLink);
158-
} catch {
159-
await fs.symlink(nodeModules, nmLink);
160-
}
161-
162154
if (dev) {
163155
console.log('\nStarting dev server...');
164156
await runDocusaurus(docusaurus, 'start', resolvedOutputDir, nodeModules);
@@ -242,25 +234,27 @@ function printHelp(): void {
242234
Usage: messenger-docs [project-path] [options]
243235
244236
Generate Messenger API documentation for MetaMask controller packages.
245-
Automatically scans both packages/*/src (.ts) and node_modules/@metamask (.d.cts).
237+
Scans packages/*/src (.ts), configured source dirs, and node_modules/@metamask (.d.cts).
246238
247239
Arguments:
248-
project-path Path to the project to scan (default: current directory)
240+
project-path Path to the project to scan (default: current directory)
249241
250242
Options:
251-
--build Generate docs and build static site
252-
--serve Generate docs, build, and serve static site
253-
--dev Generate docs and start dev server with hot reload
254-
--output <dir> Output directory (default: <project-path>/.messenger-docs)
255-
--help Show this help message
243+
--build Generate docs and build static site
244+
--serve Generate docs, build, and serve static site
245+
--dev Generate docs and start dev server with hot reload
246+
--scan-dir <dir> Extra source directory to scan (repeatable, default: src)
247+
--output <dir> Output directory (default: <project-path>/.messenger-docs)
248+
--help Show this help message
249+
250+
Source directories can also be configured in package.json:
251+
"messenger-docs": { "scanDirs": ["app", "src"] }
256252
257253
Examples:
258-
messenger-docs # Scan cwd for controller packages
259-
messenger-docs /path/to/project # Scan a specific project
260-
messenger-docs --dev # Dev server with hot reload
261-
messenger-docs --build # Generate and build static site
262-
messenger-docs --serve # Generate, build, and serve
263-
messenger-docs --output ./my-docs # Custom output directory
254+
messenger-docs # Scan cwd
255+
messenger-docs --serve # Generate, build, and serve
256+
messenger-docs --scan-dir app --scan-dir shared # Scan app/ and shared/
257+
messenger-docs --output ./my-docs # Custom output directory
264258
`);
265259
}
266260

packages/messenger-docs/src/generate.ts

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ export type GenerateOptions = {
5050
projectPath: string;
5151
/** Absolute path to the output directory for generated docs. */
5252
outputDir: string;
53+
/**
54+
* Extra directories (relative to projectPath) to scan for .ts source files.
55+
* When omitted, falls back to `"messenger-docs".scanDirs` in the project's
56+
* package.json, then to `["src"]`.
57+
*/
58+
scanDirs?: string[];
5359
};
5460

5561
/**
@@ -70,21 +76,47 @@ export type GenerateResult = {
7076
export async function generate(
7177
options: GenerateOptions,
7278
): Promise<GenerateResult> {
73-
const { projectPath, outputDir } = options;
79+
const { projectPath, outputDir, scanDirs: scanDirsOption } = options;
80+
81+
// Resolve scanDirs: CLI flag → package.json config → default ["src"]
82+
let scanDirs = scanDirsOption;
83+
if (!scanDirs) {
84+
try {
85+
const pkgRaw = await fs.readFile(
86+
path.join(projectPath, 'package.json'),
87+
'utf8',
88+
);
89+
const pkg = JSON.parse(pkgRaw) as Record<string, unknown>;
90+
const config = pkg['messenger-docs'] as
91+
| { scanDirs?: string[] }
92+
| undefined;
93+
if (Array.isArray(config?.scanDirs)) {
94+
scanDirs = config.scanDirs;
95+
}
96+
} catch {
97+
// No package.json or invalid — use default.
98+
}
99+
scanDirs ??= ['src'];
100+
}
74101

75102
const allItems: MessengerItemDoc[] = [];
76103

77104
// Check which sources are available
78-
const rootSrcDir = path.join(projectPath, 'src');
79-
const hasRootSrc = await pathExists(rootSrcDir);
105+
const existingScanDirs: string[] = [];
106+
for (const dir of scanDirs) {
107+
const abs = path.join(projectPath, dir);
108+
if (await pathExists(abs)) {
109+
existingScanDirs.push(dir);
110+
}
111+
}
80112
const packagesDir = path.join(projectPath, 'packages');
81113
const hasPackages = await pathExists(packagesDir);
82114
const nmDir = path.join(projectPath, 'node_modules', '@metamask');
83115
const hasNodeModules = await pathExists(nmDir);
84116

85117
const sources: string[] = [];
86-
if (hasRootSrc) {
87-
sources.push('src/ (.ts)');
118+
for (const dir of existingScanDirs) {
119+
sources.push(`${dir}/ (.ts)`);
88120
}
89121
if (hasPackages) {
90122
sources.push('packages/*/src (.ts)');
@@ -96,9 +128,10 @@ export async function generate(
96128
`Scanning ${sources.join(', ')} for Messenger action/event types...`,
97129
);
98130

99-
// Scan project-level src/ for .ts source files
100-
if (hasRootSrc) {
101-
const tsFiles = await findTsFiles(rootSrcDir);
131+
// Scan configured source directories for .ts files
132+
for (const dir of existingScanDirs) {
133+
const abs = path.join(projectPath, dir);
134+
const tsFiles = await findTsFiles(abs);
102135
for (const file of tsFiles) {
103136
try {
104137
const items = await extractFromFile(file, projectPath);
@@ -172,9 +205,10 @@ export async function generate(
172205
}
173206
}
174207

175-
if (!hasRootSrc && !hasPackages && !hasNodeModules) {
208+
if (existingScanDirs.length === 0 && !hasPackages && !hasNodeModules) {
176209
throw new Error(
177-
`No src/, packages/, or node_modules/@metamask/ found in ${projectPath}.`,
210+
`No scannable directories found in ${projectPath}. ` +
211+
`Looked for: ${scanDirs.join(', ')}, packages/, node_modules/@metamask/`,
178212
);
179213
}
180214

packages/messenger-docs/template/docusaurus.config.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
1+
import * as path from 'node:path';
2+
13
import type * as Preset from '@docusaurus/preset-classic';
24
import type { Config } from '@docusaurus/types';
35
import { themes } from 'prism-react-renderer';
46

57
const codeTheme = themes.dracula;
68

9+
// When running inside a host project (e.g. metamask-extension) whose React
10+
// version differs from Docusaurus's, we alias react/react-dom to the copies
11+
// installed alongside this package.
12+
const extraNodeModules = process.env.NODE_PATH; // eslint-disable-line no-process-env
13+
const reactAlias: Record<string, string> = {};
14+
if (extraNodeModules) {
15+
for (const pkg of ['react', 'react-dom', '@mdx-js/react']) {
16+
reactAlias[pkg] = path.join(extraNodeModules, pkg);
17+
}
18+
}
19+
720
const config: Config = {
821
title: 'Messenger API',
922
tagline: 'Action and event reference for MetaMask controller messengers',
@@ -24,6 +37,22 @@ const config: Config = {
2437
locales: ['en'],
2538
},
2639

40+
plugins: [
41+
function resolvePlugin() {
42+
return {
43+
name: 'resolve-deps',
44+
configureWebpack() {
45+
if (Object.keys(reactAlias).length === 0) {
46+
return {};
47+
}
48+
return {
49+
resolve: { alias: reactAlias },
50+
};
51+
},
52+
};
53+
},
54+
],
55+
2756
themes: [
2857
[
2958
'@easyops-cn/docusaurus-search-local',

0 commit comments

Comments
 (0)