-
Notifications
You must be signed in to change notification settings - Fork 916
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
Combine .css.proxy.js files into one #1407
Conversation
This pull request is being automatically deployed with Vercel (learn more). 🔍 Inspect: https://vercel.com/pikapkg/snowpack/qnz5hcc45 |
const code = await fs.promises.readFile(file, 'utf-8'); | ||
|
||
// <link> | ||
hypertag(code, 'link').forEach((link) => { |
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.
❤️ Are you kidding me? Can’t believe we weren’t using this before!
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! I'm a bit concerned about the 0.0.3 version number, but the API and performance of hypertag sure looks great
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.
Yeah, I feel you. I took a look at the source, and it’s basically just really, really good RegEx-ing that was doing a better job than ours. It’s a simple, 0-dep library that I feel confident in its simplicity. But if we discovered a critical bug and the author didn’t respond, it’s also simple enough to fork & patch if need be.
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.
Yea, agreed on the feeling of this not being a super well used library that hasn't seen an update in 1+ years. BUT as long as you've given it a look inside and it looks good, then I'm +1
@@ -188,26 +118,65 @@ exports.default = function plugin(config, userDefinedOptions) { | |||
}) | |||
.map((file) => path.join(buildDirectory, file)); // resolve to root dir | |||
|
|||
// 2. optimize all files in parallel | |||
// 2. scan imports | |||
const manifest = await scanHTML( |
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.
🆕 Import manifest. This was needed because we first needed to see if any .css.proxy.js
files were imported in the entire project before we injected a <link />
tag in the HTML. This looks like…
exports[`snowpack build preload-css: build/__snowpack__/optimize-manifest.json 1`] = ` | ||
"{ | ||
\\"imports\\": { | ||
\\"/index.html\\": { |
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.
…this. This optimize-manifest.json
first creates an import map, from all HTML files -> CSS and JS.
It also contains a generatedFiles: []
array, so that if you’re generating your own HTML server-side, you could scan this file to know about the new CSS file generated.
We should probably also expand this to preloadModules: true
support—tagging the imports that need to be preloaded—but that’d be in a followup PR.
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.
+1, this is something that I really want to build on in follow up PRs. We do a lot of work to scan and analyze your application, we should be able to share that result with other optimize steps.
test/build/preload-css/__snapshots__
Outdated
|
||
exports[`snowpack build preload-css: build/_dist_/vanilla.js 1`] = `""`; | ||
|
||
exports[`snowpack build preload-css: build/imported-styles.css 1`] = `":root{--background-body:#fff;--background:#efefef;--background-alt:#f7f7f7;--selection:#9e9e9e;--text-main:#363636;--text-bright:#000;--text-muted:#70777f;--links:#0076d1;--focus:rgba(0,150,191,0.67);--border:#dbdbdb;--code:#000;--animation-duration:0.1s;--button-hover:#ddd;--scrollbar-thumb:#d5d5d5;--scrollbar-thumb-hover:#c4c4c4;--form-placeholder:#949494;--form-text:#000;--variable:#39a33c;--highlight:#ff0;--select-arrow:url(\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='63' width='117' fill='%23161f27'%3E%3Cpath d='M115 2c-1-2-4-2-5 0L59 53 7 2a4 4 0 00-5 5l54 54 2 2 3-2 54-54c2-1 2-4 0-5z'/%3E%3C/svg%3E\\")}@media (prefers-color-scheme:dark){:root{--background-body:#202b38;--background:#161f27;--background-alt:#1a242f;--selection:#1c76c5;--text-main:#dbdbdb;--text-bright:#fff;--text-muted:#a9b1ba;--links:#41adff;--focus:rgba(0,150,191,0.67);--border:#526980;--code:#ffbe85;--animation-duration:0.1s;--button-hover:#324759;--scrollbar-thumb:var(--button-hover);--scrollbar-thumb-hover:#141414;--form-placeholder:#a9a9a9;--form-text:#fff;--variable:#d941e2;--highlight:#efdb43;--select-arrow:url(\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='63' width='117' fill='%23efefef'%3E%3Cpath d='M115 2c-1-2-4-2-5 0L59 53 7 2a4 4 0 00-5 5l54 54 2 2 3-2 54-54c2-1 2-4 0-5z'/%3E%3C/svg%3E\\")}}html{scrollbar-color:#d5d5d5 #fff;scrollbar-color:var(--scrollbar-thumb) var(--background-body);scrollbar-width:thin}@media (prefers-color-scheme:dark){html{scrollbar-color:#324759 #202b38;scrollbar-color:var(--scrollbar-thumb) var(--background-body)}}body{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Segoe UI Emoji,Apple Color Emoji,Noto Color Emoji,sans-serif;max-width:800px;margin:20px auto;padding:0 10px;word-wrap:break-word;color:#363636;color:var(--text-main);background:#fff;background:var(--background-body);text-rendering:optimizeLegibility}@media (prefers-color-scheme:dark){body{background:#202b38;background:var(--background-body);color:#dbdbdb;color:var(--text-main)}}button,input,textarea{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease;transition:background-color var(--animation-duration) linear,border-color var(--animation-duration) linear,color var(--animation-duration) linear,box-shadow var(--animation-duration) linear,transform var(--animation-duration) ease}@media (prefers-color-scheme:dark){button,input,textarea{transition:background-color .1s linear,border-color .1s linear,color .1s linear,box-shadow .1s linear,transform .1s ease;transition:background-color var(--animation-duration) linear,border-color var(--animation-duration) linear,color var(--animation-duration) linear,box-shadow var(--animation-duration) linear,transform var(--animation-duration) ease}}h1,mark{color:#000}h1{font-size:2.2em;color:var(--text-bright)}h1,h2,h3,h4,h5,h6{margin-bottom:12px;margin-top:24px}h2,h3,h4,h5,h6,strong{color:#000;color:var(--text-bright)}@media (prefers-color-scheme:dark){h1,h2,h3,h4,h5,h6,strong{color:#fff;color:var(--text-bright)}}b,h1,h2,h3,h4,h5,h6,strong,th{font-weight:600}q:after,q:before{content:none}blockquote,q{border-left:4px solid rgba(0,150,191,.67);border-left:4px solid var(--focus);margin:1.5em 0;padding:.5em 1em;font-style:italic}@media (prefers-color-scheme:dark){blockquote,q{border-left:4px solid rgba(0,150,191,.67);border-left:4px solid var(--focus)}}blockquote>footer{font-style:normal;border:0}address,blockquote cite{font-style:normal}a[href^=mailto\\\\:]:before{content:\\"📧 \\"}a[href^=tel\\\\:]:before{content:\\"📞 \\"}a[href^=sms\\\\:]:before{content:\\"💬 \\"}mark{background-color:#ff0;background-color:var(--highlight);border-radius:2px;padding:0 2px}@media (prefers-color-scheme:dark){mark{background-color:#efdb43;background-color:var(--highlight)}}button,input[type=button],input[type=checkbox],input[type=radio],input[type=range],input[type=submit],select{cursor:pointer}input:not([type=checkbox]):not([type=radio]),select{display:block}button,input,select{margin-right:6px}button,input,select,textarea{color:#000;color:var(--form-text);background-color:#efefef;background-color:var(--background);font-family:inherit;font-size:inherit;margin-bottom:6px;padding:10px;border:0;border-radius:6px;outline:0}@media (prefers-color-scheme:dark){button,input,select,textarea{background-color:#161f27;background-color:var(--background);color:#fff;color:var(--form-text)}}input[type=checkbox],input[type=radio]{height:1em;width:1em}input[type=radio]{border-radius:100%}input{vertical-align:top}label{vertical-align:middle;margin-bottom:4px;display:inline-block}button,input:not([type=checkbox]):not([type=radio]),input[type=range],select,textarea{-webkit-appearance:none}textarea{display:block;margin-right:0;box-sizing:border-box;resize:vertical}textarea:not([cols]){width:100%}textarea:not([rows]){min-height:40px;height:140px}select{background:#efefef url(\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='63' width='117' fill='%23161f27'%3E%3Cpath d='M115 2c-1-2-4-2-5 0L59 53 7 2a4 4 0 00-5 5l54 54 2 2 3-2 54-54c2-1 2-4 0-5z'/%3E%3C/svg%3E\\") calc(100% - 12px) 50%/12px no-repeat;background:var(--background) var(--select-arrow) calc(100% - 12px) 50%/12px no-repeat;padding-right:35px}@media (prefers-color-scheme:dark){select{background:#161f27 url(\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='63' width='117' fill='%23efefef'%3E%3Cpath d='M115 2c-1-2-4-2-5 0L59 53 7 2a4 4 0 00-5 5l54 54 2 2 3-2 54-54c2-1 2-4 0-5z'/%3E%3C/svg%3E\\") calc(100% - 12px) 50%/12px no-repeat;background:var(--background) var(--select-arrow) calc(100% - 12px) 50%/12px no-repeat}}select::-ms-expand{display:none}select[multiple]{padding-right:10px;background-image:none;overflow-y:auto}button,input[type=button],input[type=submit]{padding-right:30px;padding-left:30px}button:hover,input[type=submit]:hover{background:#ddd;background:var(--button-hover)}@media (prefers-color-scheme:dark){button:hover,input[type=submit]:hover{background:#324759;background:var(--button-hover)}}input[type=button]:hover{background:#ddd;background:var(--button-hover)}@media (prefers-color-scheme:dark){input[type=button]:hover{background:#324759;background:var(--button-hover)}}button:focus,input:focus,select:focus,textarea:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67);box-shadow:0 0 0 2px var(--focus)}@media (prefers-color-scheme:dark){button:focus,input:focus,select:focus,textarea:focus{box-shadow:0 0 0 2px rgba(0,150,191,.67);box-shadow:0 0 0 2px var(--focus)}}button:active,input[type=button]:active,input[type=checkbox]:active,input[type=radio]:active,input[type=range]:active,input[type=submit]:active{transform:translateY(2px)}button:disabled,input:disabled,select:disabled,textarea:disabled{cursor:not-allowed;opacity:.5}::-moz-placeholder{color:#949494;color:var(--form-placeholder)}:-ms-input-placeholder,::-ms-input-placeholder{color:#949494;color:var(--form-placeholder)}::placeholder{color:#949494;color:var(--form-placeholder)}@media (prefers-color-scheme:dark){::-moz-placeholder{color:#a9a9a9;color:var(--form-placeholder)}:-ms-input-placeholder,::-ms-input-placeholder{color:#a9a9a9;color:var(--form-placeholder)}::placeholder{color:#a9a9a9;color:var(--form-placeholder)}}fieldset{border:1px solid rgba(0,150,191,.67);border:1px solid var(--focus);border-radius:6px;margin:0 0 12px;padding:10px}@media (prefers-color-scheme:dark){fieldset{border:1px solid rgba(0,150,191,.67);border:1px solid var(--focus)}}legend{font-size:.9em;font-weight:600}input[type=range]{margin:10px 0;padding:10px 0;background:0 0}input[type=range]:focus{outline:0}input[type=range]::-webkit-slider-runnable-track{width:100%;height:9.5px;-webkit-transition:.2s;transition:.2s;background:#efefef;background:var(--background);border-radius:3px}@media (prefers-color-scheme:dark){input[type=range]::-webkit-slider-runnable-track{background:#161f27;background:var(--background)}}input[type=range]::-webkit-slider-thumb{box-shadow:0 1px 1px #000,0 0 1px #0d0d0d;height:20px;width:20px;border-radius:50%;background:#dbdbdb;background:var(--border);-webkit-appearance:none;margin-top:-7px}@media (prefers-color-scheme:dark){input[type=range]::-webkit-slider-thumb{background:#526980;background:var(--border)}}input[type=range]:focus::-webkit-slider-runnable-track{background:#efefef;background:var(--background)}@media (prefers-color-scheme:dark){input[type=range]:focus::-webkit-slider-runnable-track{background:#161f27;background:var(--background)}}input[type=range]::-moz-range-track{width:100%;height:9.5px;-moz-transition:.2s;transition:.2s;background:#efefef;background:var(--background);border-radius:3px}@media (prefers-color-scheme:dark){input[type=range]::-moz-range-track{background:#161f27;background:var(--background)}}input[type=range]::-moz-range-thumb{box-shadow:1px 1px 1px #000,0 0 1px #0d0d0d;height:20px;width:20px;border-radius:50%;background:#dbdbdb;background:var(--border)}@media (prefers-color-scheme:dark){input[type=range]::-moz-range-thumb{background:#526980;background:var(--border)}}input[type=range]::-ms-track{width:100%;height:9.5px;background:0 0;border-color:transparent;border-width:16px 0;color:transparent}input[type=range]::-ms-fill-lower{background:#efefef;background:var(--background);border:.2px solid #010101;border-radius:3px;box-shadow:1px 1px 1px #000,0 0 1px #0d0d0d}@media (prefers-color-scheme:dark){input[type=range]::-ms-fill-lower{background:#161f27;background:var(--background)}}input[type=range]::-ms-fill-upper{background:#efefef;background:var(--background);border:.2px solid #010101;border-radius:3px;box-shadow:1px 1px 1px #000,0 0 1px #0d0d0d}@media (prefers-color-scheme:dark){input[type=range]::-ms-fill-upper{background:#161f27;background:var(--background)}}input[type=range]::-ms-thumb{box-shadow:1px 1px 1px #000,0 0 1px #0d0d0d;border:1px solid #000;height:20px;width:20px;border-radius:50%;background:#dbdbdb;background:var(--border)}@media (prefers-color-scheme:dark){input[type=range]::-ms-thumb{background:#526980;background:var(--border)}}input[type=range]:focus::-ms-fill-lower{background:#efefef;background:var(--background)}@media (prefers-color-scheme:dark){input[type=range]:focus::-ms-fill-lower{background:#161f27;background:var(--background)}}input[type=range]:focus::-ms-fill-upper{background:#efefef;background:var(--background)}@media (prefers-color-scheme:dark){input[type=range]:focus::-ms-fill-upper{background:#161f27;background:var(--background)}}a{text-decoration:none;color:var(--links)}@media (prefers-color-scheme:dark){a{color:#41adff;color:var(--links)}}a:hover{text-decoration:underline}code,kbd,samp,time{background:#efefef;background:var(--background)}code,samp,time{color:#000;color:var(--code);padding:2.5px 5px;border-radius:6px;font-size:1em}@media (prefers-color-scheme:dark){code,samp,time{color:#ffbe85;color:var(--code);background:#161f27;background:var(--background)}}pre>code{padding:10px;display:block;overflow-x:auto}var{color:#39a33c;color:var(--variable);font-style:normal;font-family:monospace}@media (prefers-color-scheme:dark){var{color:#d941e2;color:var(--variable)}}kbd{border:1px solid #dbdbdb;border:1px solid var(--border);border-radius:2px;color:#363636;color:var(--text-main);padding:2px 4px}@media (prefers-color-scheme:dark){kbd{color:#dbdbdb;color:var(--text-main);border:1px solid #526980;border:1px solid var(--border);background:#161f27;background:var(--background)}}img,video{max-width:100%;height:auto}hr{border:0;border-top:1px solid #dbdbdb;border-top:1px solid var(--border)}@media (prefers-color-scheme:dark){hr{border-top:1px solid #526980;border-top:1px solid var(--border)}}table{border-collapse:collapse;margin-bottom:10px;width:100%;table-layout:fixed}table caption,td,th{text-align:left}td,th{padding:6px;vertical-align:top;word-wrap:break-word}thead{border-bottom:1px solid #dbdbdb;border-bottom:1px solid var(--border)}@media (prefers-color-scheme:dark){thead{border-bottom:1px solid #526980;border-bottom:1px solid var(--border)}}tfoot{border-top:1px solid #dbdbdb;border-top:1px solid var(--border)}@media (prefers-color-scheme:dark){tfoot{border-top:1px solid #526980;border-top:1px solid var(--border)}}tbody tr:nth-child(2n){background-color:#f7f7f7;background-color:var(--background-alt)}@media (prefers-color-scheme:dark){tbody tr:nth-child(2n){background-color:#1a242f;background-color:var(--background-alt)}}::-webkit-scrollbar{height:10px;width:10px}::-webkit-scrollbar-track{background:#efefef;background:var(--background);border-radius:6px}@media (prefers-color-scheme:dark){::-webkit-scrollbar-track{background:#161f27;background:var(--background)}}::-webkit-scrollbar-thumb{background:#d5d5d5;background:var(--scrollbar-thumb);border-radius:6px}@media (prefers-color-scheme:dark){::-webkit-scrollbar-thumb{background:#324759;background:var(--scrollbar-thumb)}}::-webkit-scrollbar-thumb:hover{background:#c4c4c4;background:var(--scrollbar-thumb-hover)}@media (prefers-color-scheme:dark){::-webkit-scrollbar-thumb:hover{background:#141414;background:var(--scrollbar-thumb-hover)}}::-moz-selection{background-color:#9e9e9e;background-color:var(--selection);color:#000;color:var(--text-bright)}::selection{background-color:#9e9e9e;background-color:var(--selection);color:#000;color:var(--text-bright)}@media (prefers-color-scheme:dark){::-moz-selection{color:#fff;color:var(--text-bright)}::selection{color:#fff;color:var(--text-bright)}::-moz-selection{background-color:#1c76c5;background-color:var(--selection)}::selection{background-color:#1c76c5;background-color:var(--selection)}}details{display:flex;flex-direction:column;align-items:flex-start;background-color:#f7f7f7;background-color:var(--background-alt);padding:10px 10px 0;margin:1em 0;border-radius:6px;overflow:hidden}@media (prefers-color-scheme:dark){details{background-color:#1a242f;background-color:var(--background-alt)}}details[open]{padding:10px}details>:last-child{margin-bottom:0}details[open] summary{margin-bottom:10px}summary{display:list-item;background-color:#efefef;background-color:var(--background);padding:10px;margin:-10px -10px 0;cursor:pointer;outline:0}@media (prefers-color-scheme:dark){summary{background-color:#161f27;background-color:var(--background)}}summary:focus,summary:hover{text-decoration:underline}details>:not(summary){margin-top:0}summary::-webkit-details-marker{color:#363636;color:var(--text-main)}@media (prefers-color-scheme:dark){summary::-webkit-details-marker{color:#dbdbdb;color:var(--text-main)}}footer{border-top:1px solid #dbdbdb;border-top:1px solid var(--border);padding-top:10px;color:#70777f;color:var(--text-muted)}@media (prefers-color-scheme:dark){footer{color:#a9b1ba;color:var(--text-muted);border-top:1px solid #526980;border-top:1px solid var(--border)}}body>footer{margin-top:40px}@media print{body,button,code,details,input,pre,summary,textarea{background-color:#fff}button,input,textarea{border:1px solid #000}body,button,code,footer,h1,h2,h3,h4,h5,h6,input,pre,strong,summary,textarea{color:#000}summary::marker{color:#000}summary::-webkit-details-marker{color:#000}tbody tr:nth-child(2n){background-color:#f2f2f2}a{color:#00f;text-decoration:underline}}body{font-size:16px;line-height:1.5}a{color:#6495ed}.one{color:#eee8aa}.two{color:khaki}.three{color:#f8f8ff}.four{color:#fffacd}"`; |
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.
This new imported-styles.css
file only captures CSS loaded via import *
, not all CSS in the project.
test/build/preload-css/__snapshots__
Outdated
<html> | ||
<HEAD> | ||
<link rel=\\"stylesheet\\" href=\\"/reset.css\\" /> | ||
<link type=\\"stylesheet\\" rel=\\"/imported-styles.css\\" /> |
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.
CSS tag automatically injected at the end of <head>
import(\\"./dynamic-css.css.proxy.js\\"); | ||
// CSS Modules | ||
const {one, two} = {\\"one\\":\\"_dist_scoped_module__one\\",\\"two\\":\\"_dist_scoped_module__two\\"}; | ||
const styles = {\\"three\\":\\"_dist_scoped_scss_module__three\\",\\"four\\":\\"_dist_scoped_scss_module__four\\"}; |
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.
This is where you can see the transforms happening. Before, this one file had 4 CSS imports. Now, it only has one—the dynamic import()
. The rest were inlined, saving network requests and main thread time (i.e. we let the browser handle the CSS part, and we leave only the bare minimum needed in JS).
test/build/preload-css/__snapshots__
Outdated
color: cornflowerblue; | ||
} | ||
._dist_scoped_module__one { color: palegoldenrod;}._dist_scoped_module__two { color: khaki;} | ||
._dist_scoped_scss_module__three { color: ghostwhite;}._dist_scoped_scss_module__four { color: lemonchiffon;}" |
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.
CSS Module support 👆🏻
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.
changes look good to me 👍🏼 Good tests, too! Don't forget the TODO about the debug logs
I’m not sure if I can fix that. By default, |
d8bb6bb
to
4a5b7d5
Compare
4a5b7d5
to
b0bbdb1
Compare
b0bbdb1
to
2b47979
Compare
26c935f
to
5ecf595
Compare
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.
This is great, and I love that you've already tried it out in your Vue application. +1, my main comments are around naming before we merge.
Also would love a walk through some of the trickier scanning code, the code looks fine but some rubber ducky debugging and I'm curious to learn more.
Awesome work on this! Sorry it's taken so long to review! Lets work to get this merged, released, and then we can start to talk through the right order of operations to get from here to a built-in Snowpack optimization config. (#1276)
plugins/plugin-optimize/README.md
Outdated
| `minifyJS` | `boolean` | Enable JS minification (default: `true`) | | ||
| `minifyCSS` | `boolean` | Enable CSS minification (default: `true`) | | ||
| `minifyHTML` | `boolean` | Enable HTML minification (default: `true`) | | ||
| `preloadModules` | `boolean` | Experimental: Add deep, optimized [`<link rel="modulepreload">`](https://developers.google.com/web/updates/2017/12/modulepreload) tags into your HTML. (default: `false`) | |
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.
to match above naming, should this be preloadJS
? Since there's no such thing as a non-module JS script "import"/"dependency", I don't think you need to worry about that distinction.
plugins/plugin-optimize/README.md
Outdated
| `minifyHTML` | `boolean` | Enable HTML minification (default: `true`) | | ||
| `preloadModules` | `boolean` | Experimental: Add deep, optimized [`<link rel="modulepreload">`](https://developers.google.com/web/updates/2017/12/modulepreload) tags into your HTML. (default: `false`) | | ||
| `preloadCSS` | `boolean` | Experimental: Eliminate `.css.proxy.js` files and combine imported CSS into one file for better network performance (default: `false`) | | ||
| `preloadedCSSName` | `string` | If preloading CSS, change the name of the generated CSS file. Only used in conjunction with `preloadCSS: true` (default: `/imported-styles.css`) | |
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.
nit: Name
makes me think some sort of CSS class or variable name. We use FileName
and FileLoc
in other plugins, that may be a better fit here
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.
trying out preloadCSSFileName
* import url from 'global.css' -> const url = 'global.css' | ||
* import {foo, bar} from 'local.module.css' -> const {foo, bar} = 'local.module.css' | ||
*/ | ||
function transformCSSProxy(file, originalCode) { |
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.
lets walk through this code together, I want to make sure that I understand it and I'm not sure I do at the moment :)
748c0fe
to
a717c27
Compare
a717c27
to
59c7d59
Compare
3d23f9d
to
2ba32a6
Compare
Changes
Background
Background: #1334
For every
.css
file imported into JS, Snowpack creates one.css.proxy.js
file. This requires an additional network request as well as a tiny amount of main thread blocking time to add the styles to the page.As you can see, navigating through a Snowpack site can mean extra network requests, as well as delay in rendering while styles download.
Preloading CSS
Instead, a user can opt to concatenate all the CSS imported by JS, and only that, into one file. This can be achieved by passing
{ preloadCSS: true }
to '@snowpack/plugin-optimize'. This serves all the CSS as one request, and injects it into the<head>
element (or if Snowpack isn’t controlling your HTML, there’s now a__snowpack__/optimize-manifest.json
file where this generated file can be pulled into an SSR setup).In this way, you get the best of both worlds: all the CSS-in-JS features you’d expect with a modern setup, but compiled away into zero runtime, and in a way optimized for browsers.
Tested on
skypack.dev
:48 kB
saved between no longer needing to inline<style>
tags in the payload + no more wrapper code needed around all the JS files26
network requests saved (and no waterfall)<style>
injection/no main thread blocking (though main thread time was only ~4ms
on a fast machine)Why is this PR so big?
Very little of the PR is the feature itself; the bulk of the PR comes from a few other changes which made the work of preloading CSS a heck of a lot easier. This is marked a draft PR for review because all 4 of the following could be split out as one or multiple prereqs if needed:
__snowpack__/optimize-manifest.json
was created so that this plugin can better optimize, as well as giving people generating their own HTML a hook to see generated files from Snowpack (inspired by a discussion from Getting rid of `.css.proxy.js` files for production builds #1334)<script type="module">
tags than we were using (and 0 deps! 🎉 )<head>
and<body>
. While I found many HTML parsers and HTML AST libraries for Node, I couldn’t find any that easily let you mutate the HTML or AST. But even of the few that could, they were either very heavy and cumbersome or they mangled the original HTML in some way (because if you mutate something, everything has to be rebuilt). So I came up with a lightweight solution that’s basically just pure string injection, but in a better way than we were doing.And of course, all these made the process of preloading CSS easier, which is the final change on top of these.
Testing
Tests were added to test this behavior, but it was also manually tested in a vanilla JS setup, Svelte, and Vue, and all work as expected.
If you want to profile a live Vue app using this feature, go here (code here)
Docs
Plugin docs were updated