From ec5cb5ec516a2129e08bbc20f3f6627341ef7b2d Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sun, 15 Nov 2026 23:13:14 +0800 Subject: [PATCH] fix(setup): read MCPorter's XDG folder in the client configuration-root owner (#11193) --- src/application/ClientConfigRoots.test.ts | 15 ++++++++++++++ src/application/ClientConfigRoots.ts | 25 +++++++++++++++++++++++ src/application/SupportedClients.ts | 19 ++++------------- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/application/ClientConfigRoots.test.ts b/src/application/ClientConfigRoots.test.ts index 03537fc9d9..4b74df5bf4 100644 --- a/src/application/ClientConfigRoots.test.ts +++ b/src/application/ClientConfigRoots.test.ts @@ -5,6 +5,7 @@ import { expect, it } from "vitest"; import { absoluteXdgConfigHome, + mcporterXdgConfigHome, xdgConfigHomeOrDefault, } from "./ClientConfigRoots.js"; @@ -24,6 +25,20 @@ it.each([ }, ); +it.each([ + [undefined, undefined], + ["", undefined], + ["relative", undefined], + [" ~/xdg ", "/home/user/xdg"], + ["~", "/home/user"], + [" /custom ", "/custom"], +])("reads XDG_CONFIG_HOME=%j as MCPorter's folder %j", (value, expected) => { + const env = value === undefined ? {} : { XDG_CONFIG_HOME: value }; + expect(mcporterXdgConfigHome({ home, platform: "linux", env })).toBe( + expected, + ); +}); + // Client root semantics differ per reader, so they are derived only here. it("keeps client configuration-root variables in one owner", async () => { const directory = fileURLToPath(new URL(".", import.meta.url)); diff --git a/src/application/ClientConfigRoots.ts b/src/application/ClientConfigRoots.ts index f4e009dae9..27e9c4d71e 100644 --- a/src/application/ClientConfigRoots.ts +++ b/src/application/ClientConfigRoots.ts @@ -43,6 +43,31 @@ export const absoluteXdgConfigHome = ({ : join(home, ".config"); }; +/** MCPorter's expandHome: a leading `~` path segment names the home folder. */ +export const mcporterExpandHome = (value: string, home: string): string => + value === "~" + ? home + : value.startsWith("~/") || value.startsWith("~\\") + ? join(home, value.slice(2)) + : value; + +/** + * MCPorter's XDG folder (`mcporterConfigCandidates`): `XDG_CONFIG_HOME` is + * trimmed and `~`-expanded, and only an absolute result counts. Otherwise + * MCPorter uses its legacy `~/.mcporter` folder rather than `~/.config`, so + * there is no XDG folder and this returns `undefined`. + */ +export const mcporterXdgConfigHome = ({ + home, + platform, + env, +}: ClientRootContext): string | undefined => { + const value = mcporterExpandHome(env.XDG_CONFIG_HOME?.trim() ?? "", home); + return (platform === "win32" ? win32 : posix).isAbsolute(value) + ? value + : undefined; +}; + /** * Code-OSS's `getUserDataPath` for one product's data folder (`Code` for VS * Code; `Cursor`, `Windsurf`, `VSCodium` or `Code - Insiders` for builds that diff --git a/src/application/SupportedClients.ts b/src/application/SupportedClients.ts index 4c703dedfe..01d7efc881 100644 --- a/src/application/SupportedClients.ts +++ b/src/application/SupportedClients.ts @@ -25,6 +25,8 @@ import { clientPathDependence, absoluteXdgConfigHome, type ClientRootFailure, + mcporterExpandHome, + mcporterXdgConfigHome, vscodeUserDirectory, xdgConfigHomeOrDefault, } from "./ClientConfigRoots.js"; @@ -744,14 +746,6 @@ const zedLocation = ({ }); }; -/** MCPorter's expandHome: a leading `~` path segment names the home folder. */ -const mcporterExpandHome = (value: string, home: string): string => - value === "~" - ? home - : value.startsWith("~/") || value.startsWith("~\\") - ? join(home, value.slice(2)) - : value; - const regularPathPresent = ( path: string, ): Result => { @@ -801,13 +795,8 @@ const mcporterLocation = ({ }) : absolute; } - const configHome = mcporterExpandHome( - env.XDG_CONFIG_HOME?.trim() ?? "", - home, - ); - const base = pathApi.isAbsolute(configHome) - ? join(configHome, "mcporter") - : legacy; + const configHome = mcporterXdgConfigHome({ home, platform, env }); + const base = configHome === undefined ? legacy : join(configHome, "mcporter"); const candidates = [base, ...(base === legacy ? [] : [legacy])].flatMap( (directory) => [ join(directory, "mcporter.json"),