Skip to content
Closed
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ Following configurations are supported via `settings.json` and can be changed fo

| Key | Default Value | Possible Values | Description |
| ----------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `oxc.configField` | `null` | `<string>` \| `<null>` | Field name to extract from the config file as oxlint configuration (e.g., `lint` to use the `lint` field from `vite.config.ts`). Requires `oxc.configPath` to be set. |
| `oxc.configPath` | `null` | `<string>` \| `<null>` | Path to oxlint configuration. Keep it empty to enable nested configuration. |
| `oxc.disableNestedConfig` | `false` | `true` \| `false` | Disable searching for nested configuration files. When set to true, only the configuration file specified in `oxc.configPath` (if any) will be used. |
| `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.configField` | `null` | `<string>` \| `<null>` | Field name to extract from the config file as oxfmt configuration (e.g., `fmt` to use the `fmt` field from `vite.config.ts`). Requires `oxc.fmt.configPath` to be set. |
| `oxc.fmt.configPath` | `null` | `<string>` \| `<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(c)` or `oxlint.config.ts` file exists in one of the workspaces. |
Expand Down
55 changes: 53 additions & 2 deletions client/WorkspaceConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ interface WorkspaceConfigInterface {
* @default null
*/
configPath?: string | null;
/**
* Field name to extract from config file as oxlint configuration
*
* `oxc.configField`
*
* @default null
*/
configField?: string | null;
/**
* typescript config path
*
Expand Down Expand Up @@ -92,21 +100,34 @@ interface WorkspaceConfigInterface {
* `oxc.fmt.configPath`
*/
["fmt.configPath"]?: string | null;
/**
* Field name to extract from config file as oxfmt configuration
* `oxc.fmt.configField`
*/
["fmt.configField"]?: string | null;
}

export type OxlintWorkspaceConfigInterface = Omit<WorkspaceConfigInterface, "fmt.configPath">;
export type OxlintWorkspaceConfigInterface = Omit<
WorkspaceConfigInterface,
"fmt.configPath" | "fmt.configField"
>;

export type OxfmtWorkspaceConfigInterface = Pick<WorkspaceConfigInterface, "fmt.configPath">;
export type OxfmtWorkspaceConfigInterface = Pick<
WorkspaceConfigInterface,
"fmt.configPath" | "fmt.configField"
>;

export class WorkspaceConfig {
private _configPath: string | null = null;
private _configField: string | null = null;
private _tsConfigPath: string | null = null;
private _runTrigger: DiagnosticPullMode = DiagnosticPullMode.onType;
private _unusedDisableDirectives: UnusedDisableDirectives | null = null;
private _typeAware: boolean | null = null;
private _disableNestedConfig: boolean = false;
private _fixKind: FixKind = FixKind.SafeFix;
private _formattingConfigPath: string | null = null;
private _formattingConfigField: string | null = null;

constructor(private readonly workspace: WorkspaceFolder) {
this.refresh();
Expand Down Expand Up @@ -139,19 +160,24 @@ export class WorkspaceConfig {
this._runTrigger =
this.configuration.get<DiagnosticPullMode>("lint.run") || DiagnosticPullMode.onType;
this._configPath = this.configuration.get<string | null>("configPath") ?? null;
this._configField = this.configuration.get<string | null>("configField") ?? null;
this._tsConfigPath = this.configuration.get<string | null>("tsConfigPath") ?? null;
this._unusedDisableDirectives =
this.configuration.get<UnusedDisableDirectives | null>("unusedDisableDirectives") ?? null;
this._typeAware = this.configuration.get<boolean | null>("typeAware") ?? null;
this._disableNestedConfig = disableNestedConfig ?? false;
this._fixKind = fixKind ?? FixKind.SafeFix;
this._formattingConfigPath = this.configuration.get<string | null>("fmt.configPath") ?? null;
this._formattingConfigField = this.configuration.get<string | null>("fmt.configField") ?? null;
}

public effectsConfigChange(event: ConfigurationChangeEvent): boolean {
if (event.affectsConfiguration(`${ConfigService.namespace}.configPath`, this.workspace)) {
return true;
}
if (event.affectsConfiguration(`${ConfigService.namespace}.configField`, this.workspace)) {
return true;
}
if (event.affectsConfiguration(`${ConfigService.namespace}.tsConfigPath`, this.workspace)) {
return true;
}
Expand Down Expand Up @@ -180,6 +206,9 @@ export class WorkspaceConfig {
if (event.affectsConfiguration(`${ConfigService.namespace}.fmt.configPath`, this.workspace)) {
return true;
}
if (event.affectsConfiguration(`${ConfigService.namespace}.fmt.configField`, this.workspace)) {
return true;
}
// deprecated settings in flags
if (event.affectsConfiguration(`${ConfigService.namespace}.flags`, this.workspace)) {
return true;
Expand Down Expand Up @@ -209,6 +238,15 @@ export class WorkspaceConfig {
return this.configuration.update("configPath", value, ConfigurationTarget.WorkspaceFolder);
}

get configField(): string | null {
return this._configField;
}

updateConfigField(value: string | null): PromiseLike<void> {
this._configField = value;
return this.configuration.update("configField", value, ConfigurationTarget.WorkspaceFolder);
}

get tsConfigPath(): string | null {
return this._tsConfigPath;
}
Expand Down Expand Up @@ -271,13 +309,23 @@ export class WorkspaceConfig {
return this.configuration.update("fmt.configPath", value, ConfigurationTarget.WorkspaceFolder);
}

get formattingConfigField(): string | null {
return this._formattingConfigField;
}

updateFormattingConfigField(value: string | null): PromiseLike<void> {
this._formattingConfigField = value;
return this.configuration.update("fmt.configField", value, ConfigurationTarget.WorkspaceFolder);
}

public shouldRequestDiagnostics(diagnosticPullMode: DiagnosticPullMode): boolean {
return diagnosticPullMode === this.runTrigger;
}

public toOxlintConfig(): OxlintWorkspaceConfigInterface {
return {
configPath: this.configPath ?? undefined,
configField: this.configPath ? (this.configField ?? undefined) : undefined,
tsConfigPath: this.tsConfigPath ?? undefined,
unusedDisableDirectives: this.unusedDisableDirectives ?? undefined,
typeAware: this.typeAware ?? undefined,
Expand All @@ -298,6 +346,9 @@ export class WorkspaceConfig {
// @ts-expect-error -- deprecated setting, kept for backward compatibility
["fmt.experimental"]: true,
["fmt.configPath"]: this.formattingConfigPath ?? undefined,
["fmt.configField"]: this.formattingConfigPath
? (this.formattingConfigField ?? undefined)
: undefined,
};
}
}
18 changes: 18 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@
"default": null,
"markdownDescription": "Path to oxlint configuration. Keep it empty to enable nested configuration."
},
"oxc.configField": {
"type": [
"string",
"null"
],
"scope": "resource",
"default": null,
"markdownDescription": "Field name to extract from the config file as oxlint configuration (e.g., `lint` to use the `lint` field from `vite.config.ts`). Requires `oxc.configPath` to be set."
},
"oxc.tsConfigPath": {
"type": [
"string",
Expand Down Expand Up @@ -258,6 +267,15 @@
"default": null,
"markdownDescription": "Path to an oxfmt configuration file"
},
"oxc.fmt.configField": {
"type": [
"string",
"null"
],
"scope": "resource",
"default": null,
"markdownDescription": "Field name to extract from the config file as oxfmt configuration (e.g., `fmt` to use the `fmt` field from `vite.config.ts`). Requires `oxc.fmt.configPath` to be set."
},
"oxc.suppressProgramErrors": {
"type": "boolean",
"scope": "window",
Expand Down
32 changes: 31 additions & 1 deletion tests/unit/WorkspaceConfig.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import { WORKSPACE_FOLDER } from "../test-helpers.js";
const keys = [
"lint.run",
"configPath",
"configField",
"tsConfigPath",
"unusedDisableDirectives",
"typeAware",
"disableNestedConfig",
"fixKind",
"fmt.configPath",
"fmt.configField",
// deprecated
"flags",
];
Expand Down Expand Up @@ -42,12 +44,14 @@ suite("WorkspaceConfig", () => {
const config = new WorkspaceConfig(WORKSPACE_FOLDER);
strictEqual(config.runTrigger, "onType");
strictEqual(config.configPath, null);
strictEqual(config.configField, null);
strictEqual(config.tsConfigPath, null);
strictEqual(config.unusedDisableDirectives, null);
strictEqual(config.typeAware, null);
strictEqual(config.disableNestedConfig, false);
strictEqual(config.fixKind, "safe_fix");
strictEqual(config.formattingConfigPath, null);
strictEqual(config.formattingConfigField, null);
});

test("deprecated values are respected", async () => {
Expand All @@ -67,24 +71,28 @@ suite("WorkspaceConfig", () => {
await Promise.all([
config.updateRunTrigger(DiagnosticPullMode.onSave),
config.updateConfigPath("./somewhere"),
config.updateConfigField("lint"),
config.updateTsConfigPath("./tsconfig.json"),
config.updateUnusedDisableDirectives("deny"),
config.updateTypeAware(true),
config.updateDisableNestedConfig(true),
config.updateFixKind(FixKind.DangerousFix),
config.updateFormattingConfigPath("./oxfmt.json"),
config.updateFormattingConfigField("fmt"),
]);

const wsConfig = workspace.getConfiguration("oxc", WORKSPACE_FOLDER);

strictEqual(wsConfig.get("lint.run"), "onSave");
strictEqual(wsConfig.get("configPath"), "./somewhere");
strictEqual(wsConfig.get("configField"), "lint");
strictEqual(wsConfig.get("tsConfigPath"), "./tsconfig.json");
strictEqual(wsConfig.get("unusedDisableDirectives"), "deny");
strictEqual(wsConfig.get("typeAware"), true);
strictEqual(wsConfig.get("disableNestedConfig"), true);
strictEqual(wsConfig.get("fixKind"), "dangerous_fix");
strictEqual(wsConfig.get("fmt.configPath"), "./oxfmt.json");
strictEqual(wsConfig.get("fmt.configField"), "fmt");
});

test("toOxlintConfig method", async () => {
Expand All @@ -93,6 +101,7 @@ suite("WorkspaceConfig", () => {
const oxlintConfig = config.toOxlintConfig();
strictEqual(oxlintConfig.run, "onType");
strictEqual(oxlintConfig.configPath, undefined);
strictEqual(oxlintConfig.configField, undefined);
strictEqual(oxlintConfig.tsConfigPath, undefined);
strictEqual(oxlintConfig.unusedDisableDirectives, undefined);
strictEqual(oxlintConfig.typeAware, undefined);
Expand All @@ -102,6 +111,7 @@ suite("WorkspaceConfig", () => {
await Promise.all([
config.updateRunTrigger(DiagnosticPullMode.onSave),
config.updateConfigPath("./somewhere"),
config.updateConfigField("lint"),
config.updateTsConfigPath("./tsconfig.json"),
config.updateUnusedDisableDirectives("deny"),
config.updateTypeAware(true),
Expand All @@ -114,25 +124,45 @@ suite("WorkspaceConfig", () => {

strictEqual(oxlintConfigUpdated.run, "onSave");
strictEqual(oxlintConfigUpdated.configPath, "./somewhere");
strictEqual(oxlintConfigUpdated.configField, "lint");
strictEqual(oxlintConfigUpdated.tsConfigPath, "./tsconfig.json");
strictEqual(oxlintConfigUpdated.unusedDisableDirectives, "deny");
strictEqual(oxlintConfigUpdated.typeAware, true);
strictEqual(oxlintConfigUpdated.disableNestedConfig, true);
strictEqual(oxlintConfigUpdated.fixKind, "dangerous_fix");
});

test("configField is ignored without configPath", async () => {
const config = new WorkspaceConfig(WORKSPACE_FOLDER);

await config.updateConfigField("lint");

const oxlintConfig = config.toOxlintConfig();
strictEqual(oxlintConfig.configField, undefined);

await config.updateFormattingConfigField("fmt");

const oxfmtConfig = config.toOxfmtConfig();
strictEqual(oxfmtConfig["fmt.configField"], undefined);
});

test("toOxfmtConfig method", async () => {
const config = new WorkspaceConfig(WORKSPACE_FOLDER);

const oxfmtConfig = config.toOxfmtConfig();
strictEqual(oxfmtConfig["fmt.configPath"], undefined);
strictEqual(oxfmtConfig["fmt.configField"], undefined);

await config.updateFormattingConfigPath("./oxfmt.json");
await Promise.all([
config.updateFormattingConfigPath("./oxfmt.json"),
config.updateFormattingConfigField("fmt"),
]);

const oxfmtConfigUpdated = config.toOxfmtConfig();

// @ts-expect-error -- deprecated setting, kept for backward compatibility
strictEqual(oxfmtConfigUpdated["fmt.experimental"], true);
strictEqual(oxfmtConfigUpdated["fmt.configPath"], "./oxfmt.json");
strictEqual(oxfmtConfigUpdated["fmt.configField"], "fmt");
});
});