fix(windows): disable native jiti setup loaders

This commit is contained in:
Peter Steinberger
2026-04-06 13:47:29 +01:00
parent 1c41987876
commit 6ed33d29c8
5 changed files with 178 additions and 4 deletions

View File

@@ -0,0 +1,84 @@
import fs from "node:fs";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { cleanupTrackedTempDirs, makeTrackedTempDir } from "./test-helpers/fs-fixtures.js";
const tempDirs: string[] = [];
const mocks = vi.hoisted(() => ({
createJiti: vi.fn(),
discoverOpenClawPlugins: vi.fn(),
loadPluginManifestRegistry: vi.fn(),
}));
vi.mock("jiti", () => ({
createJiti: (...args: Parameters<typeof mocks.createJiti>) => mocks.createJiti(...args),
}));
vi.mock("./discovery.js", () => ({
discoverOpenClawPlugins: (...args: Parameters<typeof mocks.discoverOpenClawPlugins>) =>
mocks.discoverOpenClawPlugins(...args),
}));
vi.mock("./manifest-registry.js", () => ({
loadPluginManifestRegistry: (...args: Parameters<typeof mocks.loadPluginManifestRegistry>) =>
mocks.loadPluginManifestRegistry(...args),
}));
import {
clearPluginDoctorContractRegistryCache,
listPluginDoctorLegacyConfigRules,
} from "./doctor-contract-registry.js";
function makeTempDir(): string {
return makeTrackedTempDir("openclaw-doctor-contract-registry", tempDirs);
}
afterEach(() => {
cleanupTrackedTempDirs(tempDirs);
});
describe("doctor-contract-registry getJiti", () => {
beforeEach(() => {
clearPluginDoctorContractRegistryCache();
mocks.createJiti.mockReset();
mocks.discoverOpenClawPlugins.mockReset();
mocks.loadPluginManifestRegistry.mockReset();
mocks.discoverOpenClawPlugins.mockReturnValue({
candidates: [],
diagnostics: [],
});
mocks.createJiti.mockImplementation(
(_modulePath: string, _options?: Record<string, unknown>) => {
return () => ({ default: {} });
},
);
});
it("disables native jiti loading on Windows for contract-api modules", () => {
const pluginRoot = makeTempDir();
fs.writeFileSync(path.join(pluginRoot, "contract-api.js"), "export default {};\n", "utf-8");
mocks.loadPluginManifestRegistry.mockReturnValue({
plugins: [{ id: "test-plugin", rootDir: pluginRoot }],
diagnostics: [],
});
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
try {
listPluginDoctorLegacyConfigRules({
workspaceDir: pluginRoot,
env: {},
});
} finally {
platformSpy.mockRestore();
}
expect(mocks.createJiti).toHaveBeenCalledTimes(1);
expect(mocks.createJiti.mock.calls[0]?.[0]).toBe(path.join(pluginRoot, "contract-api.js"));
expect(mocks.createJiti.mock.calls[0]?.[1]).toEqual(
expect.objectContaining({
tryNative: false,
}),
);
});
});

View File

@@ -32,15 +32,19 @@ const doctorContractCache = new Map<string, PluginDoctorContractEntry[]>();
function getJiti(modulePath: string) {
const aliasMap = buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url);
const tryNative = shouldPreferNativeJiti(modulePath);
const cacheKey = JSON.stringify({
tryNative: shouldPreferNativeJiti(modulePath),
tryNative,
aliasMap: Object.entries(aliasMap).toSorted(([left], [right]) => left.localeCompare(right)),
});
const cached = jitiLoaders.get(cacheKey);
if (cached) {
return cached;
}
const loader = createJiti(modulePath, buildPluginLoaderJitiOptions(aliasMap));
const loader = createJiti(modulePath, {
...buildPluginLoaderJitiOptions(aliasMap),
tryNative,
});
jitiLoaders.set(cacheKey, loader);
return loader;
}

View File

@@ -0,0 +1,81 @@
import fs from "node:fs";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { cleanupTrackedTempDirs, makeTrackedTempDir } from "./test-helpers/fs-fixtures.js";
const tempDirs: string[] = [];
const mocks = vi.hoisted(() => ({
createJiti: vi.fn(),
discoverOpenClawPlugins: vi.fn(),
loadPluginManifestRegistry: vi.fn(),
}));
vi.mock("jiti", () => ({
createJiti: (...args: Parameters<typeof mocks.createJiti>) => mocks.createJiti(...args),
}));
vi.mock("./discovery.js", () => ({
discoverOpenClawPlugins: (...args: Parameters<typeof mocks.discoverOpenClawPlugins>) =>
mocks.discoverOpenClawPlugins(...args),
}));
vi.mock("./manifest-registry.js", () => ({
loadPluginManifestRegistry: (...args: Parameters<typeof mocks.loadPluginManifestRegistry>) =>
mocks.loadPluginManifestRegistry(...args),
}));
import { clearPluginSetupRegistryCache, resolvePluginSetupRegistry } from "./setup-registry.js";
function makeTempDir(): string {
return makeTrackedTempDir("openclaw-setup-registry", tempDirs);
}
afterEach(() => {
cleanupTrackedTempDirs(tempDirs);
});
describe("setup-registry getJiti", () => {
beforeEach(() => {
clearPluginSetupRegistryCache();
mocks.createJiti.mockReset();
mocks.discoverOpenClawPlugins.mockReset();
mocks.loadPluginManifestRegistry.mockReset();
mocks.discoverOpenClawPlugins.mockReturnValue({
candidates: [],
diagnostics: [],
});
mocks.createJiti.mockImplementation(
(_modulePath: string, _options?: Record<string, unknown>) => {
return () => ({ default: {} });
},
);
});
it("disables native jiti loading on Windows for setup-api modules", () => {
const pluginRoot = makeTempDir();
fs.writeFileSync(path.join(pluginRoot, "setup-api.js"), "export default {};\n", "utf-8");
mocks.loadPluginManifestRegistry.mockReturnValue({
plugins: [{ id: "test-plugin", rootDir: pluginRoot }],
diagnostics: [],
});
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("win32");
try {
resolvePluginSetupRegistry({
workspaceDir: pluginRoot,
env: {},
});
} finally {
platformSpy.mockRestore();
}
expect(mocks.createJiti).toHaveBeenCalledTimes(1);
expect(mocks.createJiti.mock.calls[0]?.[0]).toBe(path.join(pluginRoot, "setup-api.js"));
expect(mocks.createJiti.mock.calls[0]?.[1]).toEqual(
expect.objectContaining({
tryNative: false,
}),
);
});
});

View File

@@ -79,15 +79,19 @@ export function clearPluginSetupRegistryCache(): void {
function getJiti(modulePath: string) {
const aliasMap = buildPluginLoaderAliasMap(modulePath, process.argv[1], import.meta.url);
const tryNative = shouldPreferNativeJiti(modulePath);
const cacheKey = JSON.stringify({
tryNative: shouldPreferNativeJiti(modulePath),
tryNative,
aliasMap: Object.entries(aliasMap).toSorted(([left], [right]) => left.localeCompare(right)),
});
const cached = jitiLoaders.get(cacheKey);
if (cached) {
return cached;
}
const loader = createJiti(modulePath, buildPluginLoaderJitiOptions(aliasMap));
const loader = createJiti(modulePath, {
...buildPluginLoaderJitiOptions(aliasMap),
tryNative,
});
jitiLoaders.set(cacheKey, loader);
return loader;
}