Skip to content

Commit 4957b13

Browse files
committed
Add monorepo support for ReScript projects in dependency graph generation
- Introduced helper functions to detect monorepo projects and prompt user for project selection. - Updated dependency graph generation logic to handle multiple projects in a monorepo setup. - Ensured user experience improvements by providing clear prompts and error messages when necessary.
1 parent e74d3f5 commit 4957b13

File tree

1 file changed

+111
-46
lines changed

1 file changed

+111
-46
lines changed

vscode-rescriptdep/src/extension.ts

Lines changed: 111 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,54 @@ async function findProjectRootForFile(filePath: string, workspaceRoot: string):
158158
return undefined;
159159
}
160160

161+
// Helper function to detect monorepo and find all projects with ReScript config
162+
async function detectMonorepoProjects(workspaceRoot: string): Promise<vscode.Uri[]> {
163+
// Find all bsconfig.json or rescript.json files in the workspace
164+
const bsconfigFiles = await vscode.workspace.findFiles(
165+
new vscode.RelativePattern(workspaceRoot, '**/bsconfig.json'),
166+
'**/node_modules/**', // exclude node_modules
167+
);
168+
169+
const rescriptFiles = await vscode.workspace.findFiles(
170+
new vscode.RelativePattern(workspaceRoot, '**/rescript.json'),
171+
'**/node_modules/**', // exclude node_modules
172+
);
173+
174+
// Combine both types of config files
175+
const allConfigFiles = [...bsconfigFiles, ...rescriptFiles];
176+
177+
// If there's more than one config file, it might be a monorepo
178+
if (allConfigFiles.length > 1) {
179+
return allConfigFiles;
180+
}
181+
182+
return [];
183+
}
184+
185+
// Function to prompt user to select a project from a monorepo
186+
async function selectMonorepoProject(projects: vscode.Uri[]): Promise<string | undefined> {
187+
// Create QuickPick items from project paths
188+
const items = projects.map(uri => {
189+
const relativePath = vscode.workspace.asRelativePath(uri);
190+
const projectDir = path.dirname(uri.fsPath);
191+
const projectName = path.basename(projectDir);
192+
193+
return {
194+
label: projectName,
195+
description: relativePath,
196+
projectRoot: projectDir
197+
};
198+
});
199+
200+
// Show QuickPick to user
201+
const selectedItem = await vscode.window.showQuickPick(items, {
202+
placeHolder: 'Select a ReScript project to analyze',
203+
title: 'Monorepo Projects'
204+
});
205+
206+
return selectedItem?.projectRoot;
207+
}
208+
161209
// Integrated common logic into a single function
162210
async function generateDependencyGraph(context: vscode.ExtensionContext, focusOnModule: boolean = false) {
163211
// Use withProgress API to show a progress notification in the bottom right
@@ -175,65 +223,82 @@ async function generateDependencyGraph(context: vscode.ExtensionContext, focusOn
175223
// Use the first workspace folder as the root for searching
176224
const workspaceRoot = workspaceFolders[0].uri.fsPath;
177225

178-
// Find the initial config file (used for full graph or as fallback)
179-
progress.report({ message: 'Finding ReScript config file...' });
180-
const initialConfigFileUri = await findConfigFile(workspaceRoot);
181-
182-
if (token.isCancellationRequested) return;
183-
184-
// If no config file is found anywhere, exit (should be caught by activationEvents)
185-
if (!initialConfigFileUri) {
186-
vscode.window.showErrorMessage('Could not find any bsconfig.json or rescript.json in the workspace (excluding node_modules).');
187-
return;
188-
}
189-
190226
let projectRoot: string;
191-
let bsDir: string;
192-
193-
try {
194-
let moduleName: string | undefined;
227+
let moduleName: string | undefined;
195228

229+
if (focusOnModule) {
196230
// --- Logic when focusing on a specific module ---
197-
if (focusOnModule) {
198-
progress.report({ message: 'Getting module information...' });
199-
if (token.isCancellationRequested) return;
231+
progress.report({ message: 'Getting module information...' });
232+
if (token.isCancellationRequested) return;
200233

201-
// Get module name (from editor or input)
202-
moduleName = getCurrentModuleNameFromActiveEditor();
203-
if (!moduleName) {
204-
moduleName = await vscode.window.showInputBox({
205-
prompt: 'Enter module name to focus on',
206-
placeHolder: 'ModuleName'
207-
});
208-
}
209-
if (!moduleName) return; // User cancelled
234+
// Get module name (from editor or input)
235+
moduleName = getCurrentModuleNameFromActiveEditor();
236+
if (!moduleName) {
237+
moduleName = await vscode.window.showInputBox({
238+
prompt: 'Enter module name to focus on',
239+
placeHolder: 'ModuleName'
240+
});
241+
}
242+
if (!moduleName) return; // User cancelled
243+
244+
// Find the source file for the target module
245+
progress.report({ message: `Finding source file for ${moduleName}...` });
246+
const moduleInfo = await findModuleInProject(moduleName);
247+
if (!moduleInfo) {
248+
vscode.window.showErrorMessage(`Could not find the source file for module: ${moduleName}`);
249+
return;
250+
}
251+
252+
// Find the project root specific to this module's source file
253+
progress.report({ message: `Finding project root for ${moduleName}...` });
254+
const moduleProjectRoot = await findProjectRootForFile(moduleInfo.path, workspaceRoot);
255+
if (!moduleProjectRoot) {
256+
vscode.window.showErrorMessage(`Could not determine the project root for module: ${moduleName} (no bsconfig/rescript.json found in parent directories).`);
257+
return;
258+
}
210259

211-
// Find the source file for the target module
212-
progress.report({ message: `Finding source file for ${moduleName}...` });
213-
const moduleInfo = await findModuleInProject(moduleName);
214-
if (!moduleInfo) {
215-
vscode.window.showErrorMessage(`Could not find the source file for module: ${moduleName}`);
260+
// Calculate bsDir based on the module's specific project root
261+
projectRoot = moduleProjectRoot;
262+
} else {
263+
// Only check for monorepo and ask for project selection when not focusing on a specific module
264+
// Check if this is a monorepo with multiple projects
265+
progress.report({ message: 'Checking workspace structure...' });
266+
const monorepoProjects = await detectMonorepoProjects(workspaceRoot);
267+
268+
// If it's a monorepo with multiple projects, ask user to select one
269+
if (monorepoProjects.length > 1) {
270+
progress.report({ message: 'Monorepo detected. Please select a project...' });
271+
const selectedProjectRoot = await selectMonorepoProject(monorepoProjects);
272+
273+
if (!selectedProjectRoot) {
274+
// User cancelled the selection
216275
return;
217276
}
218277

219-
// Find the project root specific to this module's source file
220-
progress.report({ message: `Finding project root for ${moduleName}...` });
221-
const moduleProjectRoot = await findProjectRootForFile(moduleInfo.path, workspaceRoot);
222-
if (!moduleProjectRoot) {
223-
vscode.window.showErrorMessage(`Could not determine the project root for module: ${moduleName} (no bsconfig/rescript.json found in parent directories).`);
278+
projectRoot = selectedProjectRoot;
279+
} else {
280+
// Find the initial config file (used for full graph or as fallback)
281+
progress.report({ message: 'Finding ReScript config file...' });
282+
const initialConfigFileUri = await findConfigFile(workspaceRoot);
283+
284+
if (token.isCancellationRequested) return;
285+
286+
// If no config file is found anywhere, exit (should be caught by activationEvents)
287+
if (!initialConfigFileUri) {
288+
vscode.window.showErrorMessage('Could not find any bsconfig.json or rescript.json in the workspace (excluding node_modules).');
224289
return;
225290
}
226291

227-
// Calculate bsDir based on the module's specific project root
228-
projectRoot = moduleProjectRoot;
229-
bsDir = path.join(projectRoot, 'lib', 'bs');
230-
231-
} else {
232-
// --- Logic for full dependency graph ---
233292
// Use the initially found config file's location
234293
projectRoot = path.dirname(initialConfigFileUri.fsPath);
235-
bsDir = path.join(projectRoot, 'lib', 'bs');
236294
}
295+
}
296+
297+
let bsDir: string;
298+
299+
try {
300+
// Calculate bsDir based on the determined project root
301+
bsDir = path.join(projectRoot, 'lib', 'bs');
237302

238303
// Check if the determined bsDir exists (common check)
239304
if (!fs.existsSync(bsDir)) {
@@ -1209,7 +1274,7 @@ function showGraphWebview(context: vscode.ExtensionContext, jsonContent: string,
12091274
const fullArgs = [...cacheArgs, ...coreArgs];
12101275

12111276
// Get JSON format data
1212-
const jsonContent = await runRescriptDep(cliPath, fullArgs, context); // Pass context
1277+
const jsonContent = await runRescriptDep(cliPath, fullArgs, context);
12131278

12141279
if (token.isCancellationRequested) return;
12151280

0 commit comments

Comments
 (0)