Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
19 changes: 18 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 ./",
Expand Down
26 changes: 22 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,29 @@ export async function getJsonConfig(uri: vscode.Uri): Promise<Configs> {
return config;
}

export async function getExtensionsJson(verbose = false): Promise<Configs> {
export async function findExtensionConfigFiles(): Promise<vscode.Uri[]> {
// 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<boolean>('recursiveSearch', true);

if (recursiveSearch) {
const excludePattern = settings.get<string>('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<Configs> {
const files = await findExtensionConfigFiles();

logger.appendLine(`Found ${files.length} extension configuration files`);
files.forEach(file => logger.appendLine(`- ${file.fsPath}`));
Expand Down