Skip to content

Commit a3048c6

Browse files
bgwsokra
andauthored
Turbopack: Create junction points instead of symlinks on Windows (#87606)
Next 16.1 changed Turbopack's external packaging to use symlinks. Symlinks require elevated privileges on Windows if "Developer Mode" is not enabled. We can work around this by using junction points, which are mostly equivalent, but: - They only work with directories. - They can only point to absolute paths, not relative paths. Fixes #87334 Fixes #87607 Fixes #87603 Fixes #87605 Fixes #87559 Fixes #87604 Fixes #87601 Fixes #87599 Fixes #87598 Fixes #87596 Fixes #87595 Fixes #87592 Fixes #87590 Fixes #87589 Fixes #87473 Fixes #87586 Fixes #87585 Fixes #87584 Fixes #87583 Fixes #87582 Fixes #87576 Fixes #87575 Fixes #87574 Fixes #87573 Fixes #87572 Fixes #87571 Fixes #87568 ... and many more Also: Fixes #69858 (related issue about `output: 'standalone'` on Windows. ## Testing - Disable Developer Mode on Windows, reboot, and then verify that symlink creation fails. - Make a payload app with `pnpx create-payload-app` (just pick sqlite for the database when prompted), and then update the app with `pnpx @next/codemod upgrade 16.1.0`. - Add `output: 'standalone'` to the `next.config.js`. - Try running `pnpm build` --------- Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com>
1 parent 7fb2aa9 commit a3048c6

File tree

5 files changed

+177
-70
lines changed

5 files changed

+177
-70
lines changed

packages/next/src/build/utils.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,9 +1274,29 @@ export async function copyTracedFiles(
12741274
if (symlink) {
12751275
try {
12761276
await fs.symlink(symlink, fileOutputPath)
1277-
} catch (e: any) {
1278-
if (e.code !== 'EEXIST') {
1279-
throw e
1277+
} catch (err: any) {
1278+
// Windows doesn't support creating symlinks without elevated privileges, unless
1279+
// "Developer Mode" is turned on. If we failed to crate a symlink due to EPERM, try
1280+
// creating a junction point instead.
1281+
//
1282+
// Ideally we'd just preserve the input file type (junction point or symlink), but
1283+
// there's no API in node.js to differentiate between a junction point and a symlink,
1284+
// so we just try making a symlink first. Symlinks are preferred because they support
1285+
// relative paths and non-directory (file) targets.
1286+
if (
1287+
process.platform === 'win32' &&
1288+
err.code === 'EPERM' &&
1289+
path.isAbsolute(symlink)
1290+
) {
1291+
try {
1292+
await fs.symlink(symlink, fileOutputPath, 'junction')
1293+
} catch (junctionErr: any) {
1294+
if (junctionErr.code !== 'EEXIST') {
1295+
throw junctionErr
1296+
}
1297+
}
1298+
} else if (err.code !== 'EEXIST') {
1299+
throw err
12801300
}
12811301
}
12821302
} else {

turbopack/crates/turbo-tasks-fs/src/attach.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,10 @@ impl FileSystem for AttachedFileSystem {
130130
path: FileSystemPath,
131131
target: Vc<LinkContent>,
132132
) -> Result<Vc<()>> {
133-
Ok(self.get_inner_fs_path(path).await?.write_link(target))
133+
Ok(self
134+
.get_inner_fs_path(path)
135+
.await?
136+
.write_symbolic_link_dir(target))
134137
}
135138

136139
#[turbo_tasks::function]

0 commit comments

Comments
 (0)