Skip to content

Commit 2d4c919

Browse files
committed
feat(oxlint): Support vite-plus/resolveConfig for vite.config.ts (#22456)
Fixes #21417 and #21057 Now Oxlint can use the common functions defined in `shared` as well.
1 parent 1a752ea commit 2d4c919

7 files changed

Lines changed: 34 additions & 34 deletions

File tree

apps/oxlint/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"tsdown": "catalog:",
6666
"tsx": "^4.21.0",
6767
"type-fest": "^5.2.0",
68+
"vite-plus": "catalog:",
6869
"vitest": "catalog:",
6970
"vscode-languageserver-protocol": "^3.17.5",
7071
"vscode-languageserver-textdocument": "^1.0.12"

apps/oxlint/src-js/js_config.ts

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { importJsConfig } from "@oxapps/shared";
1+
import { importJsConfig, loadViteConfigField } from "@oxapps/shared";
22
import { getErrorMessage } from "./utils/utils.ts";
33
import { DateNow, JSONStringify } from "./utils/globals.ts";
44

@@ -94,34 +94,14 @@ async function resolveJsConfig(path: string, cacheKey: number): Promise<JsConfig
9494
return { path, config };
9595
}
9696

97-
const VITE_OXLINT_CONFIG_FIELD = "lint";
98-
9997
/**
10098
* Resolve a single Vite+ config path to a `JsConfigResult`.
101-
* Extracts the `.lint` field. Returns `null` config when missing (signals "skip").
99+
* Extracts the `.lint` field via `vite-plus`. Returns `null` config when missing (signals "skip").
102100
*/
103-
async function resolveVitePlusConfig(path: string, cacheKey: number): Promise<JsConfigResult> {
104-
const config = await importJsConfig(path, cacheKey);
105-
106-
// NOTE: Vite configs may export a function via `defineConfig(() => ({ ... }))`,
107-
// but we don't know the arguments to call the function.
108-
// Treat non-object exports as "no config" and skip.
109-
if (!isObject(config)) {
110-
return { path, config: null };
111-
}
112-
113-
const lintConfig = (config as Record<string, unknown>)[VITE_OXLINT_CONFIG_FIELD];
114-
// NOTE: return `null` if `.lint` is missing which signals "skip" this
115-
if (lintConfig === undefined) {
116-
return { path, config: null };
117-
}
118-
119-
if (!isObject(lintConfig)) {
120-
throw new Error(
121-
`The \`${VITE_OXLINT_CONFIG_FIELD}\` field in the default export must be an object.`,
122-
);
123-
}
124-
validateConfigExtends(lintConfig as object);
101+
async function resolveVitePlusConfig(path: string): Promise<JsConfigResult> {
102+
const lintConfig = await loadViteConfigField(path, "lint");
103+
if (lintConfig === null) return { path, config: null };
104+
validateConfigExtends(lintConfig);
125105
return { path, config: lintConfig };
126106
}
127107

@@ -130,11 +110,10 @@ async function resolveVitePlusConfig(path: string, cacheKey: number): Promise<Js
130110
*/
131111
async function loadConfigs(
132112
paths: string[],
133-
resolver: (path: string, cacheKey: number) => Promise<JsConfigResult>,
113+
resolver: (path: string) => Promise<JsConfigResult>,
134114
): Promise<string> {
135115
try {
136-
const cacheKey = DateNow();
137-
const results = await Promise.allSettled(paths.map((path) => resolver(path, cacheKey)));
116+
const results = await Promise.allSettled(paths.map(resolver));
138117

139118
const successes: JsConfigResult[] = [];
140119
const errors: { path: string; error: string }[] = [];
@@ -168,7 +147,12 @@ export type ConfigLoader = (paths: string[]) => Promise<string>;
168147
/**
169148
* Load standard oxlint JS/TS config files in parallel.
170149
*/
171-
export const loadJsConfigs: ConfigLoader = (paths) => loadConfigs(paths, resolveJsConfig);
150+
export const loadJsConfigs: ConfigLoader = (paths) => {
151+
// Share one cache-busting key across the batch so that `?cache=<key>` is identical
152+
// for every path resolved in this call (consistent reload semantics for LSP).
153+
const cacheKey = DateNow();
154+
return loadConfigs(paths, (path) => resolveJsConfig(path, cacheKey));
155+
};
172156

173157
/**
174158
* Load Vite+ config files in parallel, extracting the `.lint` field from each.

apps/oxlint/test/fixtures/vite_plus_fn_export/vite.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const defineConfig = (config: unknown) => config;
1+
import { defineConfig } from "vite-plus";
22

33
export default defineConfig(() => ({
44
plugins: [],
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
export default {
1+
import { defineConfig } from "vite-plus";
2+
3+
export default defineConfig({
4+
fmt: {},
25
plugins: [],
3-
};
6+
});

apps/oxlint/tsdown.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ export default defineConfig([
103103
// External native bindings
104104
"./oxlint.*.node",
105105
"@oxlint/*",
106+
// Optional peer dependency; must be installed by the user
107+
"vite-plus",
106108
],
107109
},
108110
minify: minifyConfig,

npm/oxlint/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,15 @@
4646
"./package.json": "./package.json"
4747
},
4848
"peerDependencies": {
49-
"oxlint-tsgolint": ">=0.22.1"
49+
"oxlint-tsgolint": ">=0.22.1",
50+
"vite-plus": "*"
5051
},
5152
"peerDependenciesMeta": {
5253
"oxlint-tsgolint": {
5354
"optional": true
55+
},
56+
"vite-plus": {
57+
"optional": true
5458
}
5559
},
5660
"napi": {

pnpm-lock.yaml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)