ensureExtensionDepsResolvable called symlinkSync without a type argument and without path normalization. On Windows without Developer Mode or admin rights, plain symlinkSync throws EPERM; the failure was silently swallowed, leaving extension-owned packages unresolvable from shared dist/ chunks and breaking gateway startup. Extract the link logic into electron/gateway/fs-link.ts: - linkDirSafe prefers junction on Windows (works without elevation), falls back to a plain dir symlink only if junction creation fails (e.g. cross-volume). - normalizeFsPath centralizes the \\?\ extended-length + UNC prefixing that was previously an inline helper in config-sync.ts. Also drop the now-redundant inline fsPath helper in config-sync.ts and replace the two bare symlinkSync calls with linkDirSafe. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
48 lines
1.7 KiB
TypeScript
48 lines
1.7 KiB
TypeScript
import { symlinkSync } from 'fs';
|
|
import path from 'path';
|
|
|
|
/**
|
|
* Normalize a filesystem path for the current platform. On Windows, convert
|
|
* forward slashes to backslashes and apply the `\\?\` extended-length prefix
|
|
* for absolute paths so long paths are handled correctly. On POSIX, return
|
|
* the path unchanged.
|
|
*/
|
|
export function normalizeFsPath(filePath: string): string {
|
|
if (process.platform !== 'win32') return filePath;
|
|
if (!filePath) return filePath;
|
|
if (filePath.startsWith('\\\\?\\')) return filePath;
|
|
const windowsPath = filePath.replace(/\//g, '\\');
|
|
if (!path.win32.isAbsolute(windowsPath)) return windowsPath;
|
|
if (windowsPath.startsWith('\\\\')) {
|
|
return `\\\\?\\UNC\\${windowsPath.slice(2)}`;
|
|
}
|
|
return `\\\\?\\${windowsPath}`;
|
|
}
|
|
|
|
/**
|
|
* Create a directory link from `src` to `dest`.
|
|
*
|
|
* On POSIX uses a regular symlink. On Windows prefers a junction (which does
|
|
* not require Developer Mode or administrator privileges) and falls back to
|
|
* a regular symlink only if the junction attempt fails.
|
|
*
|
|
* Throws on POSIX when symlink creation fails. On Windows, both attempts
|
|
* failing will throw the symlink error — callers guard with try/catch when
|
|
* link creation is non-fatal (e.g. optional extension dependency linking).
|
|
*/
|
|
export function linkDirSafe(src: string, dest: string): void {
|
|
const isWin = process.platform === 'win32';
|
|
const srcP = normalizeFsPath(src);
|
|
const destP = normalizeFsPath(dest);
|
|
if (!isWin) {
|
|
symlinkSync(srcP, destP, 'dir');
|
|
return;
|
|
}
|
|
try {
|
|
symlinkSync(srcP, destP, 'junction');
|
|
} catch {
|
|
// Junction failed (e.g. cross-volume). Try a symlink as a last resort.
|
|
symlinkSync(srcP, destP, 'dir');
|
|
}
|
|
}
|