diff --git a/CHANGELOG.md b/CHANGELOG.md index b1b28df..a78bf08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,3 +82,15 @@ Fixes Fixes - Do not open the output channel automatically + +## [Unreleased] + +Feature + +- Added setting `unwantedExtensions.recursiveSearch` to limit the search for configuration files to the workspace folder roots (default stays recursive) +- Added setting `unwantedExtensions.excludePattern` to define which folders are skipped during the recursive search + +Other + +- Log which search mode is used when looking for configuration files +- README updated diff --git a/README.md b/README.md index 379e887..7678709 100644 --- a/README.md +++ b/README.md @@ -92,10 +92,42 @@ Create a file `.vscode/extensionsVersionCheck.json`, or if you need comments in * There will be a maximum of 1 notification per extension * If you save it as `.json`, make sure to remove all the comments +### 🆕 Limit the search to the workspace root + +By default this extension searches the **whole workspace** for configuration files, so unwanted extensions defined in a subfolder are taken into account as well. That is useful in monorepos, but not always wanted: a checked out sample project, a submodule or a vendored package may bring its own `.vscode/extensions.json` along. + +If you only want to use the configuration files placed at the root of your workspace folder(s) - which is how **VSCode** itself resolves `.vscode/extensions.json` - disable the recursive search: + +```jsonc +// .vscode/settings.json +{ + "unwantedExtensions.recursiveSearch": false +} +``` + +Alternatively you can keep the recursive search and just skip specific folders. The pattern replaces the default one, so remember to keep `node_modules` in the list: + +```jsonc +// .vscode/settings.json +{ + "unwantedExtensions.excludePattern": "{**/node_modules/**,**/vendor/**}" +} +``` + +| Setting | Default | Description | +| --- | --- | --- | +| `unwantedExtensions.recursiveSearch` | `true` | Search for configuration files in all subfolders of the workspace | +| `unwantedExtensions.excludePattern` | `**/node_modules/**` | Folders to skip during the recursive search | + +> Note: The `***.code-workspace` file is always taken into account, independent of these settings. +> Both settings are window scoped, so in a **multi-root workspace** they belong into the `***.code-workspace` file instead of a folder's `.vscode/settings.json`. + ### 🆕 Logs You can check the logs if you need more details, what is happening during the checks: `VSCode -> OUTPUT -> Unwanted extensions` +The log also states which search mode was used and lists every configuration file which was found. + ## ▶️ Run the check manually This extensions runs automatically when you open your project including the one of the configuration files. @@ -117,6 +149,8 @@ Version numbers are only supported within the `.vscode/extensionsVersionCheck.js You can use the above approach if you are running a workspace. Just place the `.vscode/extensions.json` file into your workspace root directory. +> In a multi-root workspace the root of **every** workspace folder is taken into account. + Alternatively you can also put the unwanted extensions within your `***.code-workspace` file. ```json diff --git a/package.json b/package.json index 8398705..590b7ec 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,24 @@ "command": "vscode-unwanted-extensions.checkPackages", "title": "Check for unwanted extensions" } - ] + ], + "configuration": { + "title": "Unwanted Extensions", + "properties": { + "unwantedExtensions.recursiveSearch": { + "type": "boolean", + "default": true, + "scope": "window", + "markdownDescription": "Search for extension configuration files in all subfolders of the workspace. When disabled, only the `.vscode` folder at the root of each workspace folder is used, which matches how VS Code itself resolves `.vscode/extensions.json`." + }, + "unwantedExtensions.excludePattern": { + "type": "string", + "default": "**/node_modules/**", + "scope": "window", + "markdownDescription": "Glob pattern of folders to skip while searching for extension configuration files. Only applies when `#unwantedExtensions.recursiveSearch#` is enabled. Example to also skip vendored code: `{**/node_modules/**,**/vendor/**}`" + } + } + } }, "scripts": { "compile": "tsc -p ./", diff --git a/src/utils.ts b/src/utils.ts index d7b5b03..d7d43bb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -15,11 +15,29 @@ export async function getJsonConfig(uri: vscode.Uri): Promise { return config; } -export async function getExtensionsJson(verbose = false): Promise { +export async function findExtensionConfigFiles(): Promise { // Check for default extensions.json file as well as extended ones with version numbers - const fileGlob = '**/.vscode/extensions*.{json,jsonc}'; - const excludePattern = '**/node_modules/**'; - const files = await vscode.workspace.findFiles(fileGlob, excludePattern); + const filePattern = '.vscode/extensions*.{json,jsonc}'; + const settings = vscode.workspace.getConfiguration('unwantedExtensions'); + const recursiveSearch = settings.get('recursiveSearch', true); + + if (recursiveSearch) { + const excludePattern = settings.get('excludePattern', '**/node_modules/**'); + logger.appendLine(`Searching recursively in the whole workspace, excluding "${excludePattern}"`); + return vscode.workspace.findFiles(`**/${filePattern}`, excludePattern); + } + + // Only look at the root of every workspace folder, like VSCode itself does + logger.appendLine('Searching in the workspace folder roots only (unwantedExtensions.recursiveSearch is disabled)'); + const workspaceFolders = vscode.workspace.workspaceFolders ?? []; + const filesPerFolder = await Promise.all(workspaceFolders.map(workspaceFolder => + vscode.workspace.findFiles(new vscode.RelativePattern(workspaceFolder, filePattern)) + )); + return filesPerFolder.flat(); +} + +export async function getExtensionsJson(verbose = false): Promise { + const files = await findExtensionConfigFiles(); logger.appendLine(`Found ${files.length} extension configuration files`); files.forEach(file => logger.appendLine(`- ${file.fsPath}`));