From 5465347eed3a042e8d753f86170b7393c02f8e78 Mon Sep 17 00:00:00 2001 From: Cameron Clark Date: Mon, 26 Jan 2026 14:55:22 -0500 Subject: [PATCH 1/2] feat(vscode): watch `oxlint.config.ts --- editors/vscode/README.md | 2 +- editors/vscode/client/VSCodeConfig.ts | 3 +- editors/vscode/client/tools/linter.ts | 60 ++++++++++++++---------- editors/vscode/package.json | 2 +- editors/vscode/tests/unit/linter.spec.ts | 46 ++++++++++++++++++ 5 files changed, 84 insertions(+), 29 deletions(-) create mode 100644 editors/vscode/tests/unit/linter.spec.ts diff --git a/editors/vscode/README.md b/editors/vscode/README.md index f43dc1efd190f..c2e84e048d10c 100644 --- a/editors/vscode/README.md +++ b/editors/vscode/README.md @@ -70,7 +70,7 @@ Following configurations are supported via `settings.json` and can be changed fo | `oxc.fixKind` | `safe_fix` | `safe_fix` \| `safe_fix_or_suggestion` \| `dangerous_fix` \| `dangerous_fix_or_suggestion` \| `none` \| `all` | Specify the kind of fixes to suggest/apply. | | `oxc.fmt.configPath` | `null` | `` | Path to an oxfmt configuration file | | `oxc.lint.run` | `onType` | `onSave` \| `onType` | Run the linter on save (onSave) or on type (onType) | -| `oxc.requireConfig` | `false` | `true` \| `false` | Start the language server only when a `.oxlintrc.json` file exists in one of the workspaces. | +| `oxc.requireConfig` | `false` | `true` \| `false` | Start the language server only when a `.oxlintrc.json` or `oxlint.config.ts` file exists in one of the workspaces. | | `oxc.tsConfigPath` | `null` | `` | Path to the project's TypeScript config file. If your `tsconfig.json` is not at the root, you will need this set for the `import` plugin rules to resolve imports correctly. | | `oxc.typeAware` | `false` | `true` \| `false` | Enable type-aware linting. Requires the `oxlint-tsgolint` package. See [the oxc website](https://oxc.rs/docs/guide/usage/linter/type-aware.html) for more information. | | `oxc.unusedDisableDirectives` | `allow` | `allow` \| `warn` \| `deny` | Define how directive comments like `// oxlint-disable-line` should be reported, when no errors would have been reported on that line anyway. | diff --git a/editors/vscode/client/VSCodeConfig.ts b/editors/vscode/client/VSCodeConfig.ts index 0d9e86c5938c7..7570e813762b9 100644 --- a/editors/vscode/client/VSCodeConfig.ts +++ b/editors/vscode/client/VSCodeConfig.ts @@ -138,7 +138,8 @@ interface VSCodeConfigInterface { nodePath: string | undefined; /** - * Start the language server only when a `.oxlintrc.json` file exists in one of the workspaces. + * Start the language server only when a `.oxlintrc.json` or `oxlint.config.ts` file exists + * in one of the workspaces. * `oxc.requireConfig` * @default false */ diff --git a/editors/vscode/client/tools/linter.ts b/editors/vscode/client/tools/linter.ts index 0fc977f23113a..ded03aecfb934 100644 --- a/editors/vscode/client/tools/linter.ts +++ b/editors/vscode/client/tools/linter.ts @@ -38,7 +38,7 @@ const enum LspCommands { export default class LinterTool implements ToolInterface { // Global flag to check if the user allows us to start the server. - // When `oxc.requireConfig` is `true`, make sure one `.oxlintrc.json` file is present. + // When `oxc.requireConfig` is `true`, make sure one oxlint config file is present. private allowedToStartServer: boolean = false; // LSP client instance @@ -76,7 +76,7 @@ export default class LinterTool implements ToolInterface { } this.allowedToStartServer = configService.vsCodeConfig.requireConfig - ? (await workspace.findFiles(`**/.oxlintrc.json`, "**/node_modules/**", 1)).length > 0 + ? await hasOxlintConfig() : true; const restartCommand = commands.registerCommand(OxcCommands.RestartServerLint, async () => { @@ -307,7 +307,7 @@ export default class LinterTool implements ToolInterface { if (!this.allowedToStartServer) { return { isEnabled: false, - tooltipText: "no .oxlintrc.json found", + tooltipText: "no oxlint config found", }; } else if (!enable) { return { @@ -351,32 +351,40 @@ export default class LinterTool implements ToolInterface { context: ExtensionContext, statusBarItemHandler: StatusBarItemHandler, ): void { - const watcher = workspace.createFileSystemWatcher( - "**/.oxlintrc.json", - false, - true, - !config.requireConfig, + const watchers = OXLINT_CONFIG_GLOBS.map((glob) => + workspace.createFileSystemWatcher(glob, false, true, !config.requireConfig), ); - watcher.onDidCreate(async () => { - this.allowedToStartServer = true; - this.updateStatusBar(statusBarItemHandler, config.enable); - if (this.client && !this.client.isRunning() && config.enable) { - await this.client.start(); - } - }); - watcher.onDidDelete(async () => { - // only can be called when config.requireConfig - this.allowedToStartServer = - (await workspace.findFiles(`**/.oxlintrc.json`, "**/node_modules/**", 1)).length > 0; - if (!this.allowedToStartServer) { - this.updateStatusBar(statusBarItemHandler, false); - if (this.client && this.client.isRunning()) { - await this.client.stop(); + for (const watcher of watchers) { + watcher.onDidCreate(async () => { + this.allowedToStartServer = true; + this.updateStatusBar(statusBarItemHandler, config.enable); + if (this.client && !this.client.isRunning() && config.enable) { + await this.client.start(); } - } - }); + }); + + watcher.onDidDelete(async () => { + // only can be called when config.requireConfig + this.allowedToStartServer = await hasOxlintConfig(); + if (!this.allowedToStartServer) { + this.updateStatusBar(statusBarItemHandler, false); + if (this.client && this.client.isRunning()) { + await this.client.stop(); + } + } + }); - context.subscriptions.push(watcher); + context.subscriptions.push(watcher); + } } } + +const OXLINT_CONFIG_GLOBS = ["**/.oxlintrc.json", "**/oxlint.config.ts"]; + +async function hasOxlintConfig(): Promise { + const configs = await Promise.all( + OXLINT_CONFIG_GLOBS.map((glob) => workspace.findFiles(glob, "**/node_modules/**", 1)), + ); + return configs.some((matches) => matches.length > 0); +} diff --git a/editors/vscode/package.json b/editors/vscode/package.json index f1c268d4be270..975676904a333 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -119,7 +119,7 @@ "scope": "resource", "type": "boolean", "default": false, - "markdownDescription": "Start the language server only when a `.oxlintrc.json` file exists in one of the workspaces." + "markdownDescription": "Start the language server only when a `.oxlintrc.json` or `oxlint.config.ts` file exists in one of the workspaces." }, "oxc.trace.server": { "type": "string", diff --git a/editors/vscode/tests/unit/linter.spec.ts b/editors/vscode/tests/unit/linter.spec.ts new file mode 100644 index 0000000000000..b6805341a3b71 --- /dev/null +++ b/editors/vscode/tests/unit/linter.spec.ts @@ -0,0 +1,46 @@ +import { ok } from "assert"; +import { ExtensionContext, FileSystemWatcher, workspace } from "vscode"; +import LinterTool from "../../client/tools/linter.js"; + +suite("LinterTool", () => { + test("watches oxlint.config.ts when requireConfig is enabled", () => { + const tool = new LinterTool(); + const createdPatterns: string[] = []; + const originalWatcher = workspace.createFileSystemWatcher; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- test stub + (workspace as any).createFileSystemWatcher = (pattern: string) => { + createdPatterns.push(pattern.toString()); + return { + onDidCreate: (_listener?: unknown) => ({ dispose() {} }), + onDidDelete: (_listener?: unknown) => ({ dispose() {} }), + dispose() {}, + } as FileSystemWatcher; + }; + + try { + const config = { requireConfig: true, enable: true } as unknown as { + requireConfig: boolean; + enable: boolean; + }; + const context = { subscriptions: [] } as ExtensionContext; + const statusBarItemHandler = { updateTool: () => {} } as unknown as { + updateTool: () => void; + }; + + tool.generateActivatorByConfig(config, context, statusBarItemHandler); + } finally { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- test cleanup + (workspace as any).createFileSystemWatcher = originalWatcher; + } + + ok( + createdPatterns.some((pattern) => pattern.includes(".oxlintrc.json")), + "expected watcher for .oxlintrc.json", + ); + ok( + createdPatterns.some((pattern) => pattern.includes("oxlint.config.ts")), + "expected watcher for oxlint.config.ts", + ); + }); +}); From 7cb56d4fa278c0c5e92b04e4a78f39310ddad894 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 27 Jan 2026 00:24:08 +0000 Subject: [PATCH 2/2] [autofix.ci] apply automated fixes --- editors/vscode/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/vscode/README.md b/editors/vscode/README.md index c2e84e048d10c..884d1a2e30ce8 100644 --- a/editors/vscode/README.md +++ b/editors/vscode/README.md @@ -70,7 +70,7 @@ Following configurations are supported via `settings.json` and can be changed fo | `oxc.fixKind` | `safe_fix` | `safe_fix` \| `safe_fix_or_suggestion` \| `dangerous_fix` \| `dangerous_fix_or_suggestion` \| `none` \| `all` | Specify the kind of fixes to suggest/apply. | | `oxc.fmt.configPath` | `null` | `` | Path to an oxfmt configuration file | | `oxc.lint.run` | `onType` | `onSave` \| `onType` | Run the linter on save (onSave) or on type (onType) | -| `oxc.requireConfig` | `false` | `true` \| `false` | Start the language server only when a `.oxlintrc.json` or `oxlint.config.ts` file exists in one of the workspaces. | +| `oxc.requireConfig` | `false` | `true` \| `false` | Start the language server only when a `.oxlintrc.json` or `oxlint.config.ts` file exists in one of the workspaces. | | `oxc.tsConfigPath` | `null` | `` | Path to the project's TypeScript config file. If your `tsconfig.json` is not at the root, you will need this set for the `import` plugin rules to resolve imports correctly. | | `oxc.typeAware` | `false` | `true` \| `false` | Enable type-aware linting. Requires the `oxlint-tsgolint` package. See [the oxc website](https://oxc.rs/docs/guide/usage/linter/type-aware.html) for more information. | | `oxc.unusedDisableDirectives` | `allow` | `allow` \| `warn` \| `deny` | Define how directive comments like `// oxlint-disable-line` should be reported, when no errors would have been reported on that line anyway. |