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(i18n): don't make virtual modules no-op #10662
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: 04a458f The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
// Split the Astro runtime into a separate chunk for readability | ||
if (id.includes('astro/dist/runtime')) { | ||
return 'astro'; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's NOT part of the fix, but having this is here felt very out of place, and I moved it inside plugin-chunks
0f58a9c
to
d0ce475
Compare
if (pageData.route.prerender && !pageData.hasSharedModules) { | ||
if ( | ||
pageData.route.prerender && | ||
!(pageData.hasSharedModules || pageData.hasAstroVirtualModule) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this part the actual fix of the issue? Can you explain why this is needed if so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes this is part of the fix. This is where we collect the chunks, few lines below we clean them up by making them no-op:
astro/packages/astro/src/core/build/static-build.ts
Lines 397 to 413 in 317d18e
await Promise.all( | |
files.map(async (filename) => { | |
if (!allStaticFiles.has(filename)) { | |
return; | |
} | |
const url = new URL(filename, out); | |
const text = await fs.promises.readFile(url, { encoding: 'utf8' }); | |
const [, exports] = eslexer.parse(text); | |
// Replace exports (only prerendered pages) with a noop | |
let value = 'const noop = () => {};'; | |
for (const e of exports) { | |
if (e.n === 'default') value += `\n export default noop;`; | |
else value += `\nexport const ${e.n} = noop;`; | |
} | |
await fs.promises.writeFile(url, value, { encoding: 'utf8' }); | |
}) | |
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok got it. It's a little confusing that this prerender specific logic is inlined into the build like this. What do you think about moving this to it's own function or something to improve readability?
@@ -70,6 +76,7 @@ function vitePluginPrerender(opts: StaticBuildOptions, internals: BuildInternals | |||
|
|||
opts.allPages; | |||
pageInfo.hasSharedModules = hasSharedModules; | |||
pageInfo.hasAstroVirtualModule = hasAstroVirtualModules; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this property is only set when there is prerendering, is that right? If so there might be a better name for this, so people don't think it can be used elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have some suggestions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure yet, what is the reason why prerendering needs special handling for virtual modules?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the other way around, virtual modules need special treatment for prerendered pages.
It seems that the static build takes the chunk that contains the virtual module and makes it a no-op.
I'm not very familiar with the specifics of business logic though.
I'm not really sure if this is the best fix for the issue. If there is I don't understand the issue well to suggest another fix, but I think there would be a different "bigger-picture" fix to prevent the no-op in the first place. |
All your concerns are valid, as they are my very own concerns. I was hoping that you guys had more context and knowledge about the prerendering logic and how the no-op strategy works. Not sure if you checked the source code of This issue is even worse because the functions that are made no-op are functions inside the Astro core. |
Great discussion @bluwy and @ematipico! Totally agree that this original prerendering logic has not held up as Astro's capabilities have grown. This was all written shortly after we initially added SSR support, so things like middleware, virtual modules, and our more complex bundling setup were never properly accounted for. If we're looking for a long-term refactor, I have some context to share on how and why things work as they currently do. Our build process is currently one single Rollup build (via Vite). For features like prerender and content collection caching, we are hitting the limits of manually mutating specific chunks before/after that build process. The prerender The main challenges I foresee with this multi-build approach are that:
Those tradeoffs and challenge still seem the most correct, given the number of edge cases we've had to fix with the current approach. As part of these refactors, we could even unify the divergent logic for static/hybrid/server modes since they're all essentially the same? That would definitely be a maintainability win. |
|
@lilnasy good questions! The manifest generation is a big open question. But yes, the gist of my recommendation would be to run pre-rendered pages in one bundler process and on-demand pages in another. The other idea would be to disable Rollup’s chunking behavior entirely for the first stage to avoid any shared chunks, then bundle just the code for the routes that should be rendered on-demand. As for “unifying” the logic between build modes, I only mean internally. The API would remain the same. We currently have quite a few conditional branches that use an |
Changes
Closes #10620
Closes PLT-1920
This was another tricky issue to fix. The
plugin-prerender
plugin was markingcomputePreferredLocale
as a no-op function.In order to prevent that, I decided to go for a more generic route, in case something like this happens in the future. I created a new constant that we can use to "mark" our virtual modules as "Astro internal virtual modules", and these virtual modules won't be transformed by the plugin.
As for now, I marked the i18n plugin as internal. Not sure if we need to do this also for others.
Testing
I tested this one locally, using the repository provided by OP. It works as expected now.
Docs
N/A