-
Notifications
You must be signed in to change notification settings - Fork 319
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
Limit polyfill impact on bundle size to components that actually import them #2959
Conversation
If we'd want to be even more narrow, we could create functional wrappers1 of the polyfilled methods, similarly to what import '../vendor/polyfills/Element/prototype/closest.mjs
export function closest(element, selector) {
return element.closest(selector);
} import '../vendor/polyfills/String/prototype/trim.mjs
export function trim(value) {
return value.trim()
} This would give a very clear entry point for each polyfill that we'd have to use instead of calling the method on the object. Footnotes |
Thanks @romaricpascal this looks good Removes the "side effects" from Only thing I'd (probably) suggest is to rename "polyfilled" to "common" (or "utilities") and keep all our common utilities split up into separate files—but all together. Doesn't necessarily help to group polyfilled ones separately? src/govuk
├── assets
├── common
│ ├── closest-attribute-value.mjs
│ ├── closest-attribute-value.unit.test.mjs
│ ├── get-dataset.mjs
│ ├── get-dataset.unit.test.mjs
│ ├── index.mjs
│ ├── normalise-dataset.mjs
│ ├── normalise-dataset.unit.test.mjs
│ └── …etc…
├── common.mjs
└── …etc… For backwards compatibility I've left the original src/govuk/common.mjs export * from './common/index.mjs' Which re-exports from the common directory index: src/govuk/common/index.mjs export { closestAttributeValue } from './closest-attribute-value.mjs'
export { getDataset } from './get-dataset.mjs'
export { normaliseDataset } from './normalise-dataset.mjs' Think we should also stick to:
(Don't want to put too much thought into polyfill structure as we may nearly be ready to import what we need from |
Good shout on putting everything into a "commons" folder rather than my weird "polyfilled"! I'll update and tidy up the names of the files and tests. |
This'll allow the polyfill it requires to be tree-shaken properly when bundling components that require common.mjs
Allows the polyfill it requires to be tree-shaken properly when components that don't use it import `common.mjs`
1841673
to
2b47319
Compare
* @param {nodeListIterator} callback - Callback function to run for each node | ||
* @returns {undefined} | ||
*/ | ||
export function nodeListForEach (nodes, callback) { |
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.
Cheers for reorganising this 😊
Thoughts on making the index.mjs
file a namespace export? Typically we'd want index.mjs
to export everything from the directory
Currently two imports are needed:
import { closestAttributeValue } from './common/closest-attribute-value.mjs'
import { nodeListForEach } from './common/index.mjs'
Which we could turn into one?
import { closestAttributeValue, nodeListForEach } from './common/index.mjs'
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.
Cheers for chatting through this
Yep, we'd be bringing back "side effects" by importing all those polyfills again
Feels like we could import a "ready to run" polyfill but then we're back down the route of doing what core-js
will do for us:
import { detect, polyfill } '../../vendor/polyfills/Element/prototype/closest.mjs'
if (!detect()) {
polyfill()
}
I'm happy to leave this file as it is
src/govuk/common/get-dataset.mjs
Outdated
* This is a functional wrapper around `element.dataset` to centralise the | ||
* import of the dataset polyfill | ||
* | ||
* @param {HTMLElement|SVGElement} element - The element whose dataset to access |
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.
Nice to add SVGElement here but I have a feeling we might see conflicting types downstream, especially with document.activeElement
returning the base Element
Can I suggest we leave out getDataset()
from this PR and stick to reorganising only?
Leaves the `common.mjs` file to re-export it so that code importing it from the current release still find it
2b47319
to
0a87bbf
Compare
This PR extracts the functions that use polyfills in their own modules, allowing components to import only the polyfills that they will use:
normaliseDataset
which usesString.prototype.trim
closestAttributeValue
which usesElement.prototype.closest
getDataset
(introduced a function to make a central point for loading the polyfill forElement.prototype.dataset
)Currently, these methods are in
common.mjs
(or not exist at all forgetDataset
). This means that if some code imported individual components which importcommon.mjs
for other helpers (like the Checkboxes component), the bundled code would get the 3 aforementionned polyfills.[This spreasheet shows a comparion of file sizes before and after extraction] (
main
being506e4f051
at the time of the checks). It shows the difference in the modules bundled from imporing each component, as well as the weight of:stats
meant from webpack-bundle-analyzer)Methodology is explained in the footnotes1.
As a result of the extraction:
all.js
: aggregated size does increase, but gets minified to the same weight 🎉String.protopype.trim
. It's a small win (but still a win?).Closes #2907
Footnotes
Methodology
all.mjs
was bundled with Webpack (as it's the most popular bundler in the frontend survey), exporting the built file and bundling stats. This simulates a 3rd party project importing the module in their code and Webpack following the trail of imports to add whatever needs adding to the bundled output.This was automated by the following script, run on
main
(506e4f051
at the time) and the head of this branch.As a side note, I'd be keen to see if we could automate that kind of bundle content collection in a Github action as a safety for inadvertently adding polyfills 😄 ↩