From 47aefa81b90ecde6098c950740184811dfcab6f8 Mon Sep 17 00:00:00 2001 From: Sysix <3897725+Sysix@users.noreply.github.com> Date: Tue, 26 Aug 2025 02:39:53 +0000 Subject: [PATCH] feat(editor): add `oxc.typeAware` option for workspaces (#13147) closes #12914 closes #13274 --- editors/vscode/README.md | 1 + editors/vscode/client/WorkspaceConfig.ts | 25 ++++++++++++++++++++ editors/vscode/fixtures/type_aware/index.ts | 5 ++++ editors/vscode/package.json | 6 +++++ editors/vscode/tests/WorkspaceConfig.spec.ts | 8 +++++-- editors/vscode/tests/e2e_server.spec.ts | 15 ++++++++++++ 6 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 editors/vscode/fixtures/type_aware/index.ts diff --git a/editors/vscode/README.md b/editors/vscode/README.md index feee12d07704a..82a5a7779b6e9 100644 --- a/editors/vscode/README.md +++ b/editors/vscode/README.md @@ -44,6 +44,7 @@ Following configuration are supported via `settings.json` and can be changed for | `oxc.configPath` | `null` | `null` \| `` | Path to ESlint configuration. Keep it empty to enable nested configuration. | | `oxc.tsConfigPath` | `null` | `null` \| `` | Path to TypeScript configuration. If your `tsconfig.json` is not at the root, alias paths will not be resolve correctly for the `import` plugin. | | `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. | +| `oxc.typeAware` | `false` | `false` \| `true` | Enable type aware linting. | | `oxc.flags` | - | `Record` | Custom flags passed to the language server. | #### Flags diff --git a/editors/vscode/client/WorkspaceConfig.ts b/editors/vscode/client/WorkspaceConfig.ts index e86c8f247c6ff..ed7d2d2d3f4c0 100644 --- a/editors/vscode/client/WorkspaceConfig.ts +++ b/editors/vscode/client/WorkspaceConfig.ts @@ -44,6 +44,16 @@ export interface WorkspaceConfigInterface { * @default 'allow' */ unusedDisableDirectives: UnusedDisableDirectives; + + /** + * Whether to enable type-aware linting + * + * `oxc.typeAware` + * + * @default false + */ + typeAware: boolean; + /** * Additional flags to pass to the LSP binary * `oxc.flags` @@ -58,6 +68,7 @@ export class WorkspaceConfig { private _tsConfigPath: string | null = null; private _runTrigger: Trigger = 'onType'; private _unusedDisableDirectives: UnusedDisableDirectives = 'allow'; + private _typeAware: boolean = false; private _flags: Record = {}; constructor(private readonly workspace: WorkspaceFolder) { @@ -78,6 +89,7 @@ export class WorkspaceConfig { this._tsConfigPath = this.configuration.get('tsConfigPath') ?? null; this._unusedDisableDirectives = this.configuration.get('unusedDisableDirectives') ?? 'allow'; + this._typeAware = this.configuration.get('typeAware') ?? false; this._flags = flags; } @@ -94,6 +106,9 @@ export class WorkspaceConfig { if (event.affectsConfiguration(`${ConfigService.namespace}.unusedDisableDirectives`, this.workspace)) { return true; } + if (event.affectsConfiguration(`${ConfigService.namespace}.typeAware`, this.workspace)) { + return true; + } if (event.affectsConfiguration(`${ConfigService.namespace}.flags`, this.workspace)) { return true; } @@ -140,6 +155,15 @@ export class WorkspaceConfig { return this.configuration.update('unusedDisableDirectives', value, ConfigurationTarget.WorkspaceFolder); } + get typeAware(): boolean { + return this._typeAware; + } + + updateTypeAware(value: boolean): PromiseLike { + this._typeAware = value; + return this.configuration.update('typeAware', value, ConfigurationTarget.WorkspaceFolder); + } + get flags(): Record { return this._flags; } @@ -155,6 +179,7 @@ export class WorkspaceConfig { configPath: this.configPath ?? null, tsConfigPath: this.tsConfigPath ?? null, unusedDisableDirectives: this.unusedDisableDirectives, + typeAware: this.typeAware, flags: this.flags, }; } diff --git a/editors/vscode/fixtures/type_aware/index.ts b/editors/vscode/fixtures/type_aware/index.ts new file mode 100644 index 0000000000000..62f78de1c3fab --- /dev/null +++ b/editors/vscode/fixtures/type_aware/index.ts @@ -0,0 +1,5 @@ +async function returnsPromise() { + return "value"; +} + +returnsPromise().then(() => {}); diff --git a/editors/vscode/package.json b/editors/vscode/package.json index 71433d0463007..6e7178432e6b9 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -141,6 +141,12 @@ "default": "allow", "description": "Define how directive comments like `// oxlint-disable-line` should be reported, when no errors would have been reported on that line anyway." }, + "oxc.typeAware": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "Enable type-aware linting." + }, "oxc.flags": { "type": "object", "scope": "resource", diff --git a/editors/vscode/tests/WorkspaceConfig.spec.ts b/editors/vscode/tests/WorkspaceConfig.spec.ts index 1c5b2eafa70cb..87909e7fbf274 100644 --- a/editors/vscode/tests/WorkspaceConfig.spec.ts +++ b/editors/vscode/tests/WorkspaceConfig.spec.ts @@ -3,11 +3,13 @@ import { ConfigurationTarget, workspace } from 'vscode'; import { WorkspaceConfig } from '../client/WorkspaceConfig.js'; import { WORKSPACE_FOLDER } from './test-helpers.js'; +const keys = ['lint.run', 'configPath', 'tsConfigPath', 'flags', 'unusedDisableDirectives', 'typeAware']; + suite('WorkspaceConfig', () => { setup(async () => { const workspaceConfig = workspace.getConfiguration('oxc', WORKSPACE_FOLDER); const globalConfig = workspace.getConfiguration('oxc'); - const keys = ['lint.run', 'configPath', 'tsConfigPath', 'flags', 'unusedDisableDirectives']; + await Promise.all(keys.map(key => workspaceConfig.update(key, undefined, ConfigurationTarget.WorkspaceFolder))); // VSCode will not save different workspace configuration inside a `.code-workspace` file. @@ -18,7 +20,6 @@ suite('WorkspaceConfig', () => { teardown(async () => { const workspaceConfig = workspace.getConfiguration('oxc', WORKSPACE_FOLDER); const globalConfig = workspace.getConfiguration('oxc'); - const keys = ['lint.run', 'configPath', 'tsConfigPath', 'flags', 'unusedDisableDirectives']; await Promise.all(keys.map(key => workspaceConfig.update(key, undefined, ConfigurationTarget.WorkspaceFolder))); // VSCode will not save different workspace configuration inside a `.code-workspace` file. @@ -32,6 +33,7 @@ suite('WorkspaceConfig', () => { strictEqual(config.configPath, null); strictEqual(config.tsConfigPath, null); strictEqual(config.unusedDisableDirectives, 'allow'); + strictEqual(config.typeAware, false); deepStrictEqual(config.flags, {}); }); @@ -65,6 +67,7 @@ suite('WorkspaceConfig', () => { config.updateConfigPath('./somewhere'), config.updateTsConfigPath('./tsconfig.json'), config.updateUnusedDisableDirectives('deny'), + config.updateTypeAware(true), config.updateFlags({ test: 'value' }), ]); @@ -74,6 +77,7 @@ suite('WorkspaceConfig', () => { strictEqual(wsConfig.get('configPath'), './somewhere'); strictEqual(wsConfig.get('tsConfigPath'), './tsconfig.json'); strictEqual(wsConfig.get('unusedDisableDirectives'), 'deny'); + strictEqual(wsConfig.get('typeAware'), true); deepStrictEqual(wsConfig.get('flags'), { test: 'value' }); }); }); diff --git a/editors/vscode/tests/e2e_server.spec.ts b/editors/vscode/tests/e2e_server.spec.ts index 510b70c5603e7..64ba765f070ff 100644 --- a/editors/vscode/tests/e2e_server.spec.ts +++ b/editors/vscode/tests/e2e_server.spec.ts @@ -32,6 +32,7 @@ suiteSetup(async () => { teardown(async () => { await workspace.getConfiguration('oxc').update('flags', undefined); await workspace.getConfiguration('oxc').update('tsConfigPath', undefined); + await workspace.getConfiguration('oxc').update('typeAware', undefined); await workspace.saveAll(); }); @@ -237,6 +238,20 @@ suite('E2E Diagnostics', () => { strictEqual(secondDiagnostics[0].severity, DiagnosticSeverity.Error); }); + testSingleFolderMode('changing oxc.typeAware will revalidate the tsgolint diagnostics', async () => { + await loadFixture('type_aware'); + const firstDiagnostics = await getDiagnostics('index.ts'); + + strictEqual(firstDiagnostics.length, 0); + + await workspace.getConfiguration('oxc').update('typeAware', true); + await workspace.saveAll(); + await waitForDiagnosticChange(); + + const secondDiagnostics = await getDiagnostics('index.ts'); + assert(secondDiagnostics.length != 0); + }); + test('cross module', async () => { await loadFixture('cross_module'); const diagnostics = await getDiagnostics('dep-a.ts');