Proposal: make the hydrate/ssr output tree-shakable for V5 #6743
Replies: 1 comment 3 replies
-
|
I guess I'd want to understand why first? 🙂 It's not something people have been asking for; it feels like a more work for a few less KBs (the difference between the minimal runtime and a full runtime with no DCE is ~15KB non gzip)
Again - this doesn't feel like a 'win' to me - atm it you drop it into your node backend and it works. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Proposal: make the
ssroutput tree-shakableThis is scoped to land alongside the V5
dist-hydrate-script→ssrrename, the Rolldown swap, and the move to@stencil/core/runtime/server. None of those changes on their own make the hydrate bundle tree-shakable — the closure architecture andtreeshake: falseare the actual blockers. Doing this in V5 piggybacks on breaks already happening so we don't need a second breaking release later.Why today's bundle isn't shakable
Every component, the runtime, and the platform are stringified into one
hydrateFactory($stencilWindow, ...) { ... }IIFE inhydrate-factory-closure.ts. The Rollup build ingenerate-hydrate-app.tsis forced totreeshake: falsewithinlineDynamicImports: true, and the emitted bundle is post-processed with regex string replacement inwrite-hydrate-outputs.tsandrelocate-hydrate-context.ts(e.g. injecting$stencilTagTransform, un-commentingmodeResolutionChain). Those regex passes break under any DCE/minification, which is why tree-shaking has to stay off. ESM-by-default (already in the V5 plan) is necessary but not sufficient — the.mjsfile still contains the same opaque closure today.Step-by-step plan
1. Replace the
hydrateFactoryclosure with a context-object patternDrop
HYDRATE_FACTORY_INTRO/HYDRATE_FACTORY_OUTROand thevar window = $stencilWindow; var Element = $stencilWindow.Element; ...shadowing. Plumb the mock window via an explicitsetHydrateContext(win)call (or a context object passed intohydrateApp). Platform code reads from this context instead of relying on closure-scoped vars. Remove thedeclare const $stencilTagTransformhack insrc/hydrate/platform/index.ts— tag transforms become a normal import.This is the keystone change. Everything else is gated on it.
2. Switch the inner factory build to ESM and remove
inlineDynamicImportsIn
bundle-hydrate-factory.tsandgenerate-hydrate-app.ts, dropformat: 'cjs',esModule: false,strict: false, andinlineDynamicImports: true. Component lazy chunks then survive as separate modules a downstream bundler can shake. CJS becomes a thin wrapper over ESM.3. Replace regex post-processing with Rolldown plugins
The string-injection passes in
write-hydrate-outputs.tsandrelocate-hydrate-context.tsmust become AST-aware Rolldown plugins. They'll otherwise break the moment DCE renames or removes the functions they're matching on. The Rollup → Rolldown swap is the right moment to rewrite these rather than porting the regex hacks forward. Hard prerequisite for step 4.4. Flip
treeshake: false→trueTrivial line change in
generate-hydrate-app.ts, valid only after steps 1–3.5. Remove module-level side effects in the server platform
In
src/hydrate/platform/index.ts:export const win = window;→ lazy accessor backed by the context object from step 1.cmpModules,styles,modeResolutionChain,pltinitializers → annotate with/*@__PURE__*/so unreferenced ones can be dropped.6. Mark outputs side-effect-free
sideEffects: falsetoscripts/esbuild/internal-platform-hydrate.ts'swritePkgJsoncall. All sibling internal packages already do this — hydrate is the lone exception.exportsfordist/ssr/hydrate.jsshould setsideEffects: false. Add astencil migratehint.7. Split the runner barrel
src/hydrate/runner/index.tsre-exportsrenderToString,ssrDocument,streamToString,serializeDocumentToString,createWindowFromHtml,serializeProperty/deserializeProperty, andsetTagTransformer/transformTagfrom one module that eagerly imports@hydrate-factory,@platform,mock-doc, and threecompiler/html/*helpers. Either:runtime/server/render,.../props,.../window,.../tag-transform) in the packageexportsmap; orrender.ts(removeUnusedStyles,relocateMetaCharset,updateCanonicalLink) so they only load on the code paths that use them.8. Audit
BUILD.*conditionalsOnce Rolldown is shaking,
getHydrateBuildConditionalsbecomes effective again —if (BUILD.shadowDom)/if (BUILD.scoped)branches can actually be dropped. Worth a pass to find conditions previously force-enabled because shaking didn't run.9. Drop the stub exports
After step 1, the
setTagTransformer = null as any/transformTag = null as anystubs insrc/hydrate/runner/hydrate-factory.ts(which existed only to be string-replaced by the post-processor) become honest re-exports from the platform module.Beta Was this translation helpful? Give feedback.
All reactions