Skip to content

presets(vercel): hard link custom function dirs instead of copying#4373

Open
RihanArfan wants to merge 4 commits into
mainfrom
vercel-symlink-functions
Open

presets(vercel): hard link custom function dirs instead of copying#4373
RihanArfan wants to merge 4 commits into
mainfrom
vercel-symlink-functions

Conversation

@RihanArfan

Copy link
Copy Markdown
Member

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme, or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Instead of copying __server.func when there is modifications to .vc-config.json, we now hard link all files to it. We cannot symlink within a functions dir to another since on Vercel the builder cannot resolve the files, which is why we recursively hard link.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@RihanArfan RihanArfan requested a review from pi0 as a code owner June 23, 2026 03:19
@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nitro.build Ready Ready Preview, Comment Jun 30, 2026 3:00pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

In src/presets/vercel/utils.ts, fs.constants is imported and the fsp.cp call inside createFunctionDirWithCustomConfig is updated to use constants.COPYFILE_FICLONE copy mode and to filter out .vc-config.json during the copy, preventing it from overwriting the merged config written afterward.

Vercel preset copy mode change

Layer / File(s) Summary
COPYFILE_FICLONE copy with .vc-config.json filter
src/presets/vercel/utils.ts
Adds constants import from fs and updates the fsp.cp call to set mode: constants.COPYFILE_FICLONE and a filter that excludes .vc-config.json, so the subsequent merged config write is not clobbered.

🎯 2 (Simple) | ⏱️ ~5 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title uses a conventional commit-style format and accurately describes the Vercel function directory change.
Description check ✅ Passed The description is clearly related to the change, explaining the hard-linking approach and the Vercel builder limitation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vercel-symlink-functions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Jun 23, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/nitro@4373

commit: f54759d

Comment thread src/presets/_utils/fs.ts Outdated
*
* Entries listed in `skip` are ignored at the top level only.
*/
export async function hardLinkDir(src: string, dest: string, options: { skip?: Set<string> } = {}) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried fs.cp (https://nodejs.org/api/fs.html#fspromisescpsrc-dest-options) with COPYFILE_FICLONE flag? (https://nodejs.org/api/fs.html#fspromisescopyfilesrc-dest-mode) (it should have same CoW effect but hopefully faster since we don't do recursion ourselve.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use this however it's filesystem dependant. It works on macOS, Windows Dev Drives (although not NTFS), and several other Linux ones like ZFS, BTRFS but not ext4 which I'm guessing CI will use.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also while it might be faster since on disk it's not copying again on supported FS fools like du can't see that so people still may perceive output as larger than hard linking.

Building the test fixture and measuring with du -sh:

  • main 3.4M
  • CoW 3.4M
  • hard linking 1.0M

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in generals du in filesystems with cow is an estimation only. COPYFILE_FICLONE uses reflinks btw which is also supported usually in ext4.

image

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/presets/vercel/utils.ts`:
- Around line 640-643: The copy logic in the preset utility is using fsp.cp with
COPYFILE_FICLONE, which only gives CoW reflinks or falls back to regular copies
instead of preserving hard-link behavior. Update the server-to-function copy
path in the utility that handles the .vc-config.json filter to use the
hard-linking implementation instead of fsp.cp, while keeping the existing
exclusion for .vc-config.json.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5f8eedea-3546-44f0-ac5a-76e0833d5ef3

📥 Commits

Reviewing files that changed from the base of the PR and between d5bda43 and f54759d.

📒 Files selected for processing (1)
  • src/presets/vercel/utils.ts

Comment on lines +640 to +643
await fsp.cp(serverDir, funcDir, {
recursive: true,
mode: constants.COPYFILE_FICLONE,
filter: (src) => basename(src) !== ".vc-config.json",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== utils.ts around lines 600-700 ==\n'
sed -n '600,700p' src/presets/vercel/utils.ts | cat -n

printf '\n== search for hard-link / cp / COPYFILE_FICLONE / vc-config ==\n'
rg -n "COPYFILE_FICLONE|hard-?link|\.vc-config\.json|fsp\.cp|copyFile|link\(" src/presets/vercel -S

printf '\n== file list around vercel preset utils ==\n'
git ls-files src/presets/vercel

Repository: nitrojs/nitro

Length of output: 1978


🌐 Web query:

Node.js fs.cp COPYFILE_FICLONE fallback copyFile reflink hard link documentation

💡 Result:

In Node.js, the fs.cp and fs.copyFile functions provide support for copy-on-write (CoW) reflinks via specific constants in fs.constants [1][2][3]. When using fs.constants.COPYFILE_FICLONE as a mode, the operation attempts to create a copy-on-write reflink [1][3]. If the underlying platform or filesystem does not support copy-on-write, Node.js automatically falls back to a traditional copy mechanism (reading from the source and writing to the destination) [1][4][5]. Alternatively, fs.constants.COPYFILE_FICLONE_FORCE can be used if you require the operation to fail explicitly if copy-on-write is not supported [1][3]. It is important to distinguish this from "hard links." Reflinks (copy-on-write clones) share the same underlying data blocks on the storage media until one of the files is modified; they are distinct file entries. Hard links, by contrast, are multiple directory entries pointing to the same inode. COPYFILE_FICLONE specifically targets CoW functionality where available, not the creation of hard links [1][4]. Note that while fs.cp copies entire directory structures, its mode option passes the value directly to the underlying fs.copyFile operation for individual files [1][2][6]. The default value for mode is 0, meaning no special copy behavior (like CoW) is applied by default [1][7]. Top results: [1][4][3][5]

Citations:


Restore the hard-link copy path here

fsp.cp(..., { mode: constants.COPYFILE_FICLONE }) makes CoW reflinks when available and falls back to a regular copy when they aren’t, so it does not preserve the hard-link behavior this change is meant to add. Keep the .vc-config.json filter, but use the hard-linking path instead of cp here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/presets/vercel/utils.ts` around lines 640 - 643, The copy logic in the
preset utility is using fsp.cp with COPYFILE_FICLONE, which only gives CoW
reflinks or falls back to regular copies instead of preserving hard-link
behavior. Update the server-to-function copy path in the utility that handles
the .vc-config.json filter to use the hard-linking implementation instead of
fsp.cp, while keeping the existing exclusion for .vc-config.json.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants