fix: resolve root aliases in ui dev

This commit is contained in:
Shakker
2026-05-30 19:41:00 +01:00
parent 54d42c7c9a
commit 6561bdc41d
2 changed files with 111 additions and 3 deletions
@@ -0,0 +1,40 @@
import path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import { resolveTsconfigPathAliasesForVite } from "../../vite.config.ts";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
function findStringAlias(key: string) {
return resolveTsconfigPathAliasesForVite().find((alias) => alias.find === key);
}
describe("Control UI Vite config", () => {
it("resolves root tsconfig package aliases for source imports", () => {
expect(findStringAlias("@openclaw/net-policy/ip")?.replacement).toBe(
path.join(repoRoot, "packages/net-policy/src/ip.ts"),
);
});
it("keeps specific tsconfig aliases ahead of broad package aliases", () => {
const aliases = resolveTsconfigPathAliasesForVite();
const netPolicyIpIndex = aliases.findIndex((alias) => alias.find === "@openclaw/net-policy/ip");
const netPolicyPackageIndex = aliases.findIndex(
(alias) => alias.find === "@openclaw/net-policy",
);
const netPolicyWildcardIndex = aliases.findIndex(
(alias) =>
alias.find instanceof RegExp && alias.replacement.includes("packages/net-policy/src/$1"),
);
const broadOpenClawWildcardIndex = aliases.findIndex(
(alias) => alias.find instanceof RegExp && alias.replacement.includes("extensions/$1"),
);
expect(netPolicyIpIndex).toBeGreaterThanOrEqual(0);
expect(netPolicyWildcardIndex).toBeGreaterThanOrEqual(0);
expect(netPolicyPackageIndex).toBeGreaterThanOrEqual(0);
expect(broadOpenClawWildcardIndex).toBeGreaterThanOrEqual(0);
expect(netPolicyIpIndex).toBeLessThan(netPolicyPackageIndex);
expect(netPolicyWildcardIndex).toBeLessThan(broadOpenClawWildcardIndex);
});
});
+71 -3
View File
@@ -11,6 +11,10 @@ const repoRoot = path.resolve(here, "..");
const outDir = path.resolve(here, "../dist/control-ui");
const require = createRequire(import.meta.url);
const json5EsmPath = require.resolve("json5/dist/index.mjs");
type ControlUiViteAlias = {
find: string | RegExp;
replacement: string;
};
const commonJsOptimizeDeps = [
"highlight.js/lib/core",
"highlight.js/lib/languages/bash",
@@ -83,6 +87,72 @@ function resolveControlUiBuildId(): string {
return normalizeBuildId(gitSha ? `${version}-${gitSha}` : version);
}
function escapeRegExp(input: string): string {
return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function sortTsconfigPathEntries(entries: Array<[string, unknown]>): Array<[string, unknown]> {
return entries.toSorted(([left], [right]) => {
const leftPrefixLength = left.includes("*") ? left.indexOf("*") : left.length;
const rightPrefixLength = right.includes("*") ? right.indexOf("*") : right.length;
if (leftPrefixLength !== rightPrefixLength) {
return rightPrefixLength - leftPrefixLength;
}
return right.length - left.length || left.localeCompare(right);
});
}
function resolveTsconfigTargetPath(target: string): string {
return path.resolve(repoRoot, target.replace(/^\.\//, ""));
}
function resolveTsconfigPathAlias(key: string, target: string): ControlUiViteAlias | null {
const keyWildcardIndex = key.indexOf("*");
const targetWildcardIndex = target.indexOf("*");
if (keyWildcardIndex === -1 || targetWildcardIndex === -1) {
if (keyWildcardIndex !== -1 || targetWildcardIndex !== -1) {
return null;
}
return {
find: key,
replacement: resolveTsconfigTargetPath(target),
};
}
if (
key.indexOf("*", keyWildcardIndex + 1) !== -1 ||
target.indexOf("*", targetWildcardIndex + 1) !== -1
) {
return null;
}
const prefix = key.slice(0, keyWildcardIndex);
const suffix = key.slice(keyWildcardIndex + 1);
return {
find: new RegExp(`^${escapeRegExp(prefix)}(.+)${escapeRegExp(suffix)}$`),
replacement: resolveTsconfigTargetPath(target).replace("*", "$1"),
};
}
export function resolveTsconfigPathAliasesForVite(): ControlUiViteAlias[] {
const raw = fs.readFileSync(path.join(repoRoot, "tsconfig.json"), "utf8");
const parsed = JSON.parse(raw) as {
compilerOptions?: { paths?: Record<string, unknown> };
};
const paths = parsed.compilerOptions?.paths;
if (!paths) {
return [];
}
return sortTsconfigPathEntries(Object.entries(paths)).flatMap(([key, targets]) => {
if (!Array.isArray(targets) || typeof targets[0] !== "string") {
return [];
}
const alias = resolveTsconfigPathAlias(key, targets[0]);
return alias ? [alias] : [];
});
}
function controlUiServiceWorkerBuildIdPlugin(buildId: string): Plugin {
return {
name: "control-ui-service-worker-build-id",
@@ -121,9 +191,7 @@ export default defineConfig(() => {
],
},
resolve: {
alias: {
json5: json5EsmPath,
},
alias: [{ find: "json5", replacement: json5EsmPath }, ...resolveTsconfigPathAliasesForVite()],
},
build: {
outDir,