Merge pull request #4159 from jeremymcs/fix/4158-windows-junction-regression

fix(cli): use junction symlinks in merged node_modules path
This commit is contained in:
Jeremy McSpadden 2026-04-13 20:56:23 -05:00 committed by GitHub
commit 13be3c58fe
2 changed files with 20 additions and 3 deletions

View file

@ -393,7 +393,7 @@ function reconcileMergedNodeModules(
// Skip the gsd-pi package itself and dotfiles
if (entry.name === basename(packageRoot)) continue
if (entry.name.startsWith('.')) continue
try { symlinkSync(join(hoisted, entry.name), join(agentNodeModules, entry.name)); linkedCount++ } catch { /* skip individual */ }
try { symlinkSync(join(hoisted, entry.name), join(agentNodeModules, entry.name), 'junction'); linkedCount++ } catch { /* skip individual */ }
}
} catch (err) {
console.error(`[gsd] WARN: Failed to read hoisted node_modules at ${hoisted}: ${err instanceof Error ? err.message : err}`)
@ -408,7 +408,7 @@ function reconcileMergedNodeModules(
const link = join(agentNodeModules, entry.name)
// Replace hoisted symlink with internal version (internal takes precedence)
try { lstatSync(link); unlinkSync(link) } catch { /* didn't exist — will create below */ }
try { symlinkSync(join(internal, entry.name), link); linkedCount++ } catch { /* skip individual */ }
try { symlinkSync(join(internal, entry.name), link, 'junction'); linkedCount++ } catch { /* skip individual */ }
}
} catch (err) {
console.error(`[gsd] WARN: Failed to read internal node_modules at ${internal}: ${err instanceof Error ? err.message : err}`)

View file

@ -5,8 +5,9 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, readdirSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { dirname, join } from "node:path";
import { tmpdir } from "node:os";
import { fileURLToPath } from "node:url";
// --- Integration tests via initResources (source/monorepo path) ---
@ -284,3 +285,19 @@ test("merged node_modules marker uses fingerprint including directory entries",
const fingerprint2 = `${fakePackageRoot}\n${h2}\n${i}`;
assert.notEqual(fingerprint, fingerprint2, "fingerprint should change when deps change");
});
test("reconcileMergedNodeModules uses junction symlinks for Windows compatibility", () => {
const testDir = dirname(fileURLToPath(import.meta.url));
const source = readFileSync(join(testDir, "..", "resource-loader.ts"), "utf-8");
assert.match(
source,
/symlinkSync\(join\(hoisted,\s*entry\.name\),\s*join\(agentNodeModules,\s*entry\.name\),\s*'junction'\)/,
"hoisted merged symlink must use 'junction'",
);
assert.match(
source,
/symlinkSync\(join\(internal,\s*entry\.name\),\s*link,\s*'junction'\)/,
"internal merged symlink must use 'junction'",
);
});