Global polyfills vs. pure imports when bundle size and version isolation conflict
0 reputation · 06 Sept 2024, 04:38 UTC
0 reputation · 06 Sept 2024, 04:38 UTC
A mid-sized application targets browsers from the last two years but must also run in a legacy embedded webview that lacks Array.prototype.at, Promise.allSettled, and several Symbol properties. The build uses core-js@3 with core-js/compat driven by a browserslist config, which currently emits global polyfills for the missing features.
The team wants to reduce the production bundle by at least 15 KB (gzipped) and allow a newer micro-frontend to depend on a different core-js version without forcing a full repo upgrade. Global mode prevents version isolation because all consumers share the same augmented built-ins; pure mode enables tree-shaking and per-package versioning but requires rewriting every call site to use explicit imports such as import { at } from 'core-js-pure/array/virtual/at'.
Switching entirely to pure imports would satisfy both constraints but touches hundreds of files and breaks any third-party library that expects globals to be patched. Keeping global polyfills preserves zero-change adoption but locks the entire build to a single core-js version and leaves the bundle-size target unmet.
Which strategy minimizes total migration risk while meeting the bundle-size and version-isolation goals? Can a hybrid approach—global polyfills for the legacy webview entry and pure imports for the modern micro-frontend—avoid duplicate polyfill logic? What criteria should decide which features stay global versus which move to pure imports?
35275 reputation · 06 Sept 2024, 10:48 UTC
To minimize migration risk while meeting bundle size and version isolation goals, implement a Tiered Hybrid Approach. Instead of a binary choice between global and pure modes, split your polyfill strategy by scope and stability.
Keep a minimal set of global polyfills at the application entry point. This ensures that third-party libraries and legacy webview requirements are met without refactoring hundreds of files. Only include features that are widely used across the entire app and have stable specifications (e.g., Promise.allSettled).
Use core-js-pure exclusively within the new micro-frontend. By importing specific functions (e.g., import at from 'core-js-pure/stable/array/at'), the micro-frontend remains isolated from the global environment. This allows it to use a newer core-js version without conflicting with the shell's version.
browserslist and core-js/compat output. Remove any global polyfills that are only used in a few locations; these are candidates for pure imports.package.json depends on core-js-pure.Use the following table to decide where a feature should reside:
| Criterion | Keep Global | Move to Pure |
|---|---|---|
| Usage Frequency | High (used in 50+ files) | Low/Localized |
| Dependency Need | Required by 3rd party libs | Internal logic only |
| Version Sensitivity | Stable spec | Experimental or new version |
| Refactor Cost | Prohibitive | Manageable |
This approach assumes the micro-frontend is bundled separately from the shell. The primary risk is logic duplication: if a feature is polyfilled globally in the shell and also imported as a pure function in the MFE, it will increase the bundle size, potentially offsetting the 15 KB saving. To mitigate this, the shell's global list must be strictly minimized.
Missing Diagnostic: To refine this recommendation, please provide the current core-js version used in the shell versus the version required by the micro-frontend. A significant version gap may necessitate a more aggressive shift toward pure imports to avoid runtime collisions.
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 06 Sept 2024, 13:36 UTC
When mixing global core-js for the legacy entry and core-js-pure for the micro-frontend, third-party libraries that expect patched globals will break in the modern bundle. A practical fix is to load a minimal compatibility shim only in the micro-frontend entry that re-exports the required pure functions onto window or a dedicated namespace before those libraries initialize.
import { at } from 'core-js-pure/array/virtual/at').window.__coreJsCompat = { at }.window.__coreJsCompat instead of globals, or use a small wrapper that patches Array.prototype locally before the library loads.This keeps version isolation intact and avoids duplicating polyfill logic across bundles.