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

add a svelte.extractCss function #409

Closed
Rich-Harris opened this issue Mar 26, 2017 · 5 comments
Closed

add a svelte.extractCss function #409

Rich-Harris opened this issue Mar 26, 2017 · 5 comments

Comments

@Rich-Harris
Copy link
Member

Mentioned this in Gitter earlier but it deserves an issue. Right now, it's a bit too hard to do the right thing when it comes to CSS (where 'the right thing' is pre-rendering the CSS and adding it via a <link> or — for critical path CSS — an inline <style>. This is much better for performance and prevents CSP violations). You have to use the ssr compiler, and do something like the following:

require( 'svelte/ssr/register' ); // or use a bundler with `generate: 'ssr'`
var App = require( './App.html' );

var { css } = App.renderCss();

fs.writeFileSync( 'public/main.css', css );

This can be finicky if your app isn't Node-friendly (e.g. it imports libraries that expect window to exist, etc). It would be much easier if you could do something like

var css = svelte.extractCss( 'App.html', {
  children: true // or `false` if you *don't* want child component CSS included
});

With the accompanying command line interface:

svelte css App.html > public/main.css

Because Svelte components are easy to statically analyse, it ought to be straightforward to trace imported child components, at least if they can be resolved as relative paths or with the standard Node resolution algorithm. (It will be even easier to only extract CSS for one component at a time, but you might end up with some duplication that way.)

Everything is up for bikeshedding at this point (is extractCss the right name? Should this live in a separate project, rather than in Svelte core? How does this interact with #181 and #65?).

Note: Doing the right thing also means remembering to add css: false to the compiler options in production. (How do we make it more likely that this happens?)

@Rich-Harris
Copy link
Member Author

Another thought: bundler integrations would be well placed to do this work as well:

// rollup.config.js
import svelte from 'rollup-plugin-svelte';

export default {
  entry: 'src/main.js',
  dest: 'public/bundle.js',
  format: 'iife',
  plugins: [
    svelte({
      extractCss: 'public/main.css'
    })
  ]
};

@Conduitry
Copy link
Member

Unscientific ideas of "what a nice API is" are telling me that this feature should live in a separate project, and that the core Svelte compiler should continue to be synchronous and not have any dealings with the filesystem. Bundler integrations sound like a nice place to do this, but that sounds like a bit of code duplication across different integrations unless we put some common code in Svelte core or in another library.

I'm having trouble seeing what sort of avoidable duplication might occur if you traced through the component definitions, extracted the CSS from each one separately, and then combined them. Having the compiler also expose the extracted CSS for a single component (perhaps an additional value in the output alongside code and map), and then having the bundler integrations be able to follow dependencies and combine these sounds like a tidy way to do things - but do you see a cost associated with that, or missed savings?

@PaulBGD
Copy link
Member

PaulBGD commented Mar 27, 2017

So currently in one of my websites with svelte, the easiest thing to do was create a server bundle for CSS since I'm already doing server side rendering. However if I wasn't using SSR already, a extractCss function would be nice.

I do suppose something like this could go in a different package though, we might start running into the issue of having too much stuff in the core library/compiler then having to provide support for it forever.

@Rich-Harris
Copy link
Member Author

Having the compiler also expose the extracted CSS for a single component (perhaps an additional value in the output alongside code and map)

Ooohhh, I really like that. I think that's the right move — it would make the Rollup example above fairly straightforward to implement, at the very least. And it would work the same way with both the dom and ssr compilers.

I share your feelings about keeping the core hygienic. Just trying to balance that against the desire to minimise the amount of things someone has to install to get the right behaviour, as I've gone too far in the other direction before! I think it becomes a non-issue with the {code, map, css} suggestion though. (I guess really it'll need to be {code, map, css, cssMap}...)

I'm having trouble seeing what sort of avoidable duplication might occur if you traced through the component definitions

Ha, I think I just confused myself is all. I was thinking about Foo.html and Bar.html both having @import './baz.css' statements, and imagining a world where we'd already figured out how to deduplicate that in a component that imports both Foo and Bar.

@PaulBGD That's basically my workflow too — in fact that's how svelte.technology is built (component, build script). Nice to have some external validation :)

Rich-Harris added a commit that referenced this issue Mar 28, 2017
include css in compiler output
@Rich-Harris
Copy link
Member Author

This can be closed now — #417

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants