next
Version:
The React Framework
72 lines (71 loc) • 2.9 kB
JavaScript
import fs from 'fs';
import path from 'path';
/**
* When passing cross-root symlinks from Turbopack's additional roots feature to
* the adapter, we may need to rewrite the symlink target's relative path.
*
* Adapters receive a map of `{"destination": "source"}` file paths, and they
* copy symlink paths verbatim.
*
* We must write a "synthetic" symlink at a source path (inside
* `.next/adapter/synthetic_symlinks`) for the adapter to copy to its output
* artifact (e.g. a Lambda zip file).
*
* As a future optimization, we could pass symlink information directly to the
* adapter, if the adapter signals that it supports accepting that information.
* That would avoid a lot of small filesystem operations.
*/ export class SyntheticSymlinkManager {
constructor(stagingRoot){
this.stagingRoot = stagingRoot;
this.stagedLinkNames = new Set();
}
createLink(source, linkTarget, targetHash) {
let targetType = 'file';
// Keep 128 bits of entropy while shortening the path to avoid Windows'
// 260-character path limit.
let stagedName = targetHash.slice(0, 32);
if (process.platform === 'win32') {
try {
targetType = fs.statSync(source).isDirectory() ? 'dir' : 'file';
} catch (error) {
const code = error.code;
// We cannot determine the target type, so just create a file symlink
// ENOENT: Dangling link, preserve the dangling link as a file link
// ELOOP: Unresolvable link cycle, preserve any part of the cycle that
// was traced
if (code !== 'ENOENT' && code !== 'ELOOP') {
throw error;
}
}
stagedName += `_${targetType}`;
}
const stagedPath = path.join(this.stagingRoot, stagedName);
if (!this.stagedLinkNames.has(stagedName)) {
try {
fs.symlinkSync(linkTarget, stagedPath, targetType);
} catch (error) {
// This link may exist if `rmSync` (with `force: true`) failed to delete
// some files (can happen on Windows), but it's content-addressed, so
// we can safely ignore EEXIST.
if (error.code !== 'EEXIST') {
throw error;
}
}
this.stagedLinkNames.add(stagedName);
}
return stagedPath;
}
}
export function createAdapterSyntheticSymlinkDirectory(distDir) {
const stagingRoot = path.join(distDir, 'adapter', 'synthetic_symlinks');
fs.rmSync(stagingRoot, {
recursive: true,
force: true,
maxRetries: 3
});
fs.mkdirSync(stagingRoot, {
recursive: true
});
return new SyntheticSymlinkManager(stagingRoot);
}
//# sourceMappingURL=synthetic-symlinks.js.map