diff --git a/oxlint-plugin-t3code/test/utils.ts b/oxlint-plugin-t3code/test/utils.ts index eb91d32d7d4..740da8a4f2b 100644 --- a/oxlint-plugin-t3code/test/utils.ts +++ b/oxlint-plugin-t3code/test/utils.ts @@ -9,6 +9,17 @@ import * as Predicate from "effect/Predicate"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; +import * as NodeModule from "node:module"; + +// oxlint is only a transitive dependency (via vite-plus), so its bin placement +// varies by package manager: pnpm hoists it into the virtual store, while +// other layouts expose vite-plus's LSP-only wrapper instead. Resolve the real +// package through vite-plus rather than hardcoding either layout, and do it at +// module scope so a broken install fails once with a resolution error instead +// of as an opaque defect in every test. +const oxlintPackageJsonPath = NodeModule.createRequire( + NodeModule.createRequire(import.meta.url).resolve("vite-plus/package.json"), +).resolve("oxlint/package.json"); class OxlintFixtureFailure extends Data.TaggedError("OxlintFixtureFailure")<{ readonly exitCode: number; @@ -93,14 +104,7 @@ export const createOxlintRuleHarness = ( const configPath = path.join(fixtureDir, ".oxlintrc.json"); const sourcePath = path.join(fixtureDir, options.filename ?? "fixture.ts"); const repoRoot = path.join(import.meta.dirname, "..", ".."); - const oxlintBin = path.join( - repoRoot, - "node_modules", - ".pnpm", - "node_modules", - ".bin", - "oxlint", - ); + const oxlintBin = path.join(path.dirname(oxlintPackageJsonPath), "bin", "oxlint"); const pluginPath = path.join(repoRoot, "oxlint-plugin-t3code", "index.ts"); yield* fs.writeFileString( @@ -112,8 +116,13 @@ export const createOxlintRuleHarness = ( ); yield* fs.writeFileString(sourcePath, source); + // Run through the current Node binary: oxlint's bin is an extensionless + // shebang script, which Windows cannot spawn directly and which would + // otherwise pick up whatever node is first on PATH. const output = yield* spawnAndCollectOutput( - ChildProcess.make(oxlintBin, ["--config", configPath, sourcePath], { cwd: repoRoot }), + ChildProcess.make(process.execPath, [oxlintBin, "--config", configPath, sourcePath], { + cwd: repoRoot, + }), ); if (output.exitCode !== 0) {