Skip to content
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

Merged
merged 16 commits into from
Nov 12, 2020
Merged

Combine .css.proxy.js files into one #1407

merged 16 commits into from
Nov 12, 2020

Conversation

drwpow
Copy link
Collaborator

@drwpow drwpow commented Oct 26, 2020

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.

Screen Shot 2020-10-25 at 10 09 25 PM

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 files
  • 26 network requests saved (and no waterfall)
  • No runtime/no dynamic <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:

  1. chore: Organized HTML, JS, and CSS logic into separate files
  2. feat: A new __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)
  3. chore: hypertag was added, which is a much better way of scanning for <script type="module"> tags than we were using (and 0 deps! 🎉 )
  4. chore: node-html-inject (created by yours truly to solve this exact problem) was added as a better way to inject arbitrary HTML inside<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

@vercel
Copy link

vercel bot commented Oct 26, 2020

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: https://vercel.com/pikapkg/snowpack/qnz5hcc45
✅ Preview: https://snowpack-git-drwpow-optimize-css.pikapkg.vercel.app

const code = await fs.promises.readFile(file, 'utf-8');

// <link>
hypertag(code, 'link').forEach((link) => {
Copy link
Collaborator Author

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!

Copy link
Contributor

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

Copy link
Collaborator Author

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.

Copy link
Owner

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(
Copy link
Collaborator Author

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\\": {
Copy link
Collaborator Author

@drwpow drwpow Oct 26, 2020

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.

Copy link
Owner

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.


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}"`;
Copy link
Collaborator Author

@drwpow drwpow Oct 26, 2020

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.

<html>
<HEAD>
<link rel=\\"stylesheet\\" href=\\"/reset.css\\" />
<link type=\\"stylesheet\\" rel=\\"/imported-styles.css\\" />
Copy link
Collaborator Author

@drwpow drwpow Oct 26, 2020

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\\"};
Copy link
Collaborator Author

@drwpow drwpow Oct 26, 2020

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).

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;}"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSS Module support 👆🏻

Copy link
Contributor

@gr2m gr2m left a 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

@drwpow
Copy link
Collaborator Author

drwpow commented Oct 27, 2020

Don't forget the TODO about the debug logs

I’m not sure if I can fix that. By default, console.debug all pipes through, and I want it to only show when we specify the --verbose command. I think we may have to leave that until we provide a better log API for plugins, or we set a LOG_LEVEL=VERBOSE env var or something

Copy link
Owner

@FredKSchott FredKSchott left a 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)

| `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`) |
Copy link
Owner

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.

| `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`) |
Copy link
Owner

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

Copy link
Collaborator Author

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) {
Copy link
Owner

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 :)

@drwpow drwpow merged commit 88db5d0 into master Nov 12, 2020
@drwpow drwpow deleted the drwpow/optimize-css branch November 12, 2020 01:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants