Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(kernel): fast module loading fails on Windows (EPERM) #4212

Merged
merged 1 commit into from
Aug 8, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 39 additions & 19 deletions packages/@jsii/kernel/src/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
statSync,
symlinkSync,
} from 'fs';
import * as os from 'os';
import { dirname, join } from 'path';

/**
Expand All @@ -16,31 +17,50 @@ import { dirname, join } from 'path';
const PRESERVE_SYMLINKS = process.execArgv.includes('--preserve-symlinks');

/**
* Creates directories containing hard links if possible, and falls back on
* copy otherwise.
* Link existing to destination directory
*
* @param existing is the original file or directory to link.
* @param destination is the new file or directory to create.
* - If Node has been started with a module resolution strategy that does not
* resolve symlinks (so peerDependencies can be found), use symlinking.
* Symlinking may fail on Windows for non-Admin users.
* - If not symlinking the entire directory, crawl the directory tree and
* hardlink all files (if possible), copying them if not.
*
* @param existingRoot is the original file or directory to link.
* @param destinationRoot is the new file or directory to create.
*/
export function link(existing: string, destination: string): void {
if (PRESERVE_SYMLINKS) {
mkdirSync(dirname(destination), { recursive: true });
symlinkSync(existing, destination);
return;
}
export function link(existingRoot: string, destinationRoot: string): void {
mkdirSync(dirname(destinationRoot), { recursive: true });

const stat = statSync(existing);
if (!stat.isDirectory()) {
if (PRESERVE_SYMLINKS) {
try {
linkSync(existing, destination);
} catch {
copyFileSync(existing, destination);
symlinkSync(existingRoot, destinationRoot);
return;
} catch (e: any) {
// On Windows, non-Admin users aren't allowed to create symlinks. In that case, fall back to the copying workflow.
const winNoSymlink = e.code === 'EPERM' && os.platform() === 'win32';

if (!winNoSymlink) {
throw e;
}
}
return;
}
// Fall back to the slow method
recurse(existingRoot, destinationRoot);

function recurse(existing: string, destination: string): void {
const stat = statSync(existing);
if (!stat.isDirectory()) {
try {
linkSync(existing, destination);
} catch {
copyFileSync(existing, destination);
}
return;
}

mkdirSync(destination, { recursive: true });
for (const file of readdirSync(existing)) {
link(join(existing, file), join(destination, file));
mkdirSync(destination, { recursive: true });
for (const file of readdirSync(existing)) {
recurse(join(existing, file), join(destination, file));
}
}
}
Loading