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(prerender): ignore files already in public assets #3093

Open
wants to merge 4 commits into
base: v2
Choose a base branch
from

Conversation

danielroe
Copy link
Member

πŸ”— Linked issue

❓ 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

Currently there's no checking to see if a file already exists in public assets, and this has to be added manually to the ignore list. I think it's generally safe to ignore public assets paths as otherwise we have multiple possible files resolving to the same path.

Context: nuxt/nuxt#25378

πŸ“ Checklist

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

@danielroe danielroe requested a review from pi0 as a code owner February 13, 2025 23:43
@pi0
Copy link
Member

pi0 commented Feb 14, 2025

This makes sense as general fix (since in general we always check static assets before hitting routes, prerender should not at least override files).

But with current approach, i'm afraid it will at least add overhead (of two times scanning nested public dir) and also implicit behavior change of stopping crawler if there is an overlapping static asset file.

Maybe we can add a guard to canWriteToDisk to skip if file entry exists already?

Also perhaps Nuxt can explicitly ignore important files such as manifest.json from prerender?

@danielroe
Copy link
Member Author

We do ignore the Nuxt manifest:

https://github.com/nuxt/nuxt/blob/f56e29fb1caed4b502ec82baf6c3844767667767/packages/nuxt/src/core/nitro.ts#L266-L268

The linked issue is for a user manifest.json file, and the canWriteToDisk check would pass as subfolderIndex is on so it was actually generating manifest.json/index.html. It's a needless call to the SSR renderer (as static assets aren't reachable in prerenderer by default).

We could also add all public assets to ignore list but seemed better to fix in Nitro.

Perhaps we could cache scanned public asset files on the nitro instance so we don't scan it twice?

@pi0
Copy link
Member

pi0 commented Feb 14, 2025

Even if we say it is needless operation, it is behavior change. we do SSR during prerendering to both write staric pages but also crawl them all (even urls we don’t finally write like the ones with query params and this hidden case for crawling)

when subfolderIndex is on, we can check for existence of parrent without index.HTML?

Im more lean towards dynamic write ignore since gives more flexibility (for example if we support emitting static assets from prerender routes)

@danielroe
Copy link
Member Author

danielroe commented Feb 14, 2025

no worries, i can implement this in nuxt directly if you don’t think it’s worth a broader nitro change

i regard the crawling of the path as a bug even if we don’t write it to disk, so personally i wouldn’t worry about the fact that it is a behaviour change

let me know what you prefer nitro behaviour to be and i’ll update the pr πŸ‘

@pi0
Copy link
Member

pi0 commented Feb 14, 2025

not suggesting to diverge behavior (and main blast of behavior change is for nuxt users anyway)

Maybe this:

  • fs override safeguard by default
  • opt-in option for current PR effect nuxt can use

Also thinking we could indeed cache result of static writes (it happens before prerender, we just can keep it in nitro internal)

@danielroe
Copy link
Member Author

As it happens, copyPublicAssets is called by Nuxt after the prerender step (because Nuxt writes some files to public assets after prerendering based on the result) so caching the result may be tricky. We can work around in Nuxt by removing the cache but it seems ... not optimal. Do you have suggestions?

This also means FS protection step won't work (as it's order-dependent).

@@ -120,6 +128,11 @@ export async function prerender(nitro: Nitro) {
}
}

// do not prerender any files in the public assets
if (publicAssets.has(route)) {
Copy link
Member

Choose a reason for hiding this comment

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

One thing we can optimize (for almost free -- no cache && no glob), are nitro.options.publicAssets with dir (no fallthrugh) such as client assets and build manifest dir.

We can also do it by default since this dirs (prefixes) were already explicitly configured as static (not mixed prefixes)

Another benefit is that regardless of prerender/copy order it works same.

if (nitro.options.noPublicDir) {
return;
}

if (nitro._cachedPublicAssets) {
return nitro._cachedPublicAssets;
Copy link
Member

@pi0 pi0 Feb 14, 2025

Choose a reason for hiding this comment

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

I think this cache will be unreliable if nuxt emits assets after prerendering.

We can keep copyPublicAssets intact and introduce a util that scans only unprefixed public assets (usable for opt-in feature of prerenderer for nxut)

// Check if file already exists, for example copied across from public assets
const fileAlreadyExists =
existsSync(join(nitro.options.output.publicDir, route.fileName!)) ||
existsSync(join(nitro.options.output.publicDir, route.route));
Copy link
Member

Choose a reason for hiding this comment

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

We can optimize to reduce one stat if we check fileName ending with /index.html, then skip on parent existence (manifest.json/index.html)

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