domingues / rollup-plugin-css-chunks

Rollup plugin to extract CSS into chunks
MIT License
14 stars 8 forks source link

Option to inject CSS #4

Open benmccann opened 4 years ago

benmccann commented 4 years ago

Right now it's a little tricky to get started using this plugin because you need to write code that loads the css. One thing I would really love is an option to update the JS so that it injects the CSS for you, which would make it so that you don't have to update your app in order to use this plugin besides installing the plugin itself.

This is how webpack's style-loader works. It has a few different options such as injecting either code to write a style tag with the CSS inlined or code to write a link tag linking the the .css file.

domingues commented 4 years ago

I like the idea, but we need to take in account the target of the compilation: browser/server side. In the browser side we can assume that de DOM is available and inject something like this:

{
    const link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = "${pluginOptions.publicPath}/${css_file_name}";
    document.getElementsByTagName("head")[0].appendChild(link);
}

but I don't know if in the SSR we can do a generic thing or if we need to inject code specific for each framework.

benmccann commented 4 years ago

I've been really thinking through how to do this for SSR. I think I've come to the conclusion that your existing bundle approach is probably the best approach. I still think that there's substantial value in injecting a link tag in the client side bundle though. It makes a lot of client side logic much easier to implement

benmccann commented 4 years ago

I've got a version of this working here: https://github.com/sveltejs/sapper/blob/ead10eebb7416971a390a0190f1df7c1f3327791/src/core/create_compilers/RollupCompiler.ts#L155

It runs your plugin and then another one I wrote to inject the link afterwards.

Please feel free to steal!

domingues commented 3 years ago

I'm been thinking about this, and I still don't know if this is the place to do this.

Another approach that I tried was to export the public URL of the CSS files (here) and then use it. For example with a (dumb) Svelte preprocessor we could load the stylesheet even when doing SSR:

const hash = content => crypto.createHmac('sha256', content).digest('hex').substr(0, 8);
const css_file_name = filename => path.resolve(filename.replace(new RegExp(`\\${path.extname(filename)}$`), '.css'));
const preprocessor = {
    markup: ({content, filename}) => {
        return {code: content + `<svelte:head><link rel='stylesheet' href='{css_${hash(css_file_name(filename))}}'></svelte:head>`}
    },
    script: ({content, attributes, filename}) => {
        if (!attributes.context) {
            const fname = css_file_name(filename)
            content += `\nimport css_${hash(fname)} from '${fname}';\n`;
        }
        return {code: content};
    },
}
benmccann commented 3 years ago

What happens if you have multiple instances of the same Svelte component? Will Svelte be smart enough not to duplicate the <link> elements in <svelte:head>?

domingues commented 3 years ago

In the DOM I think that there is no problem, but in SSR we will have duplicate <link>s with the same data-svelte, probably a Svelte bug/limitation: $$result.head +=.

const Src = create_ssr_component(($$result, $$props, $$bindings, slots) => {
    $$result.css.add(css);
    return `${($$result.head += `<link rel="${"stylesheet"}"${add_attribute("href", css_03132a47, 0)} data-svelte="svelte-aw03p5">`, "")}`;
});

But even if the later is fixed, my dumb preprocessor still have a problem: each component of the same chunk will load the same CSS one time each.

Maybe with a special component <LoadCSS href={} /> we can track which files have already been loaded.

benmccann commented 3 years ago

That's basically what I do as well: https://github.com/domingues/rollup-plugin-css-chunks/pull/8/files#diff-dcdc3e0b3362edb8fec2a51d3fa51f8fb8af8f70247e06d9887fa934834c9122R63

I think the trade-off between the approaches:

I personally think I still prefer https://github.com/domingues/rollup-plugin-css-chunks/pull/8 given those trade-offs because we can hide more setup from the user and we might get a larger user base since it'd be easier to use with non-svelte components. Not sure if I'm missing any other considerations though