meteor-css-modules icon indicating copy to clipboard operation
meteor-css-modules copied to clipboard

Bundle css on server and return to client

Open TimoRuetten opened this issue 6 years ago • 11 comments

We are using Serverside rendering so we would like to send the generated css classes from the server to the client. Is there any way to solve this with your package?

If we do not send the css directly to the client from server the first render - which the user sees - does not have the styles. A second later the styles get appended to the elements which is a bit ugly on the first render.

TimoRuetten avatar Mar 02 '19 19:03 TimoRuetten

Currently this is not a feature of my package, but it may be possible to add it. How are you performing SSR and how do you envision sending the CSS? For example, we could add an additional export that allows you to access the CSS directly from your code: import styles, { css } from './foo.css';

akanix42 avatar Mar 04 '19 12:03 akanix42

There could be 2 ways in my opinion.

1 All css files which are imported could be bundled when the Meteor Server restarts and put all together in one file which can be added to the

2 Like in your example it could be possible to extract the css of the file and add it to a self written CSS <Provider /> which has the context of all CSS code on Serverside and can append a tag to the head - then only the needed CSS for the SSR will be returned to client.

I dont know which one would be the best and how to implement this because I have not rly a clue how css-modules works and how I am able to extract the generated css.

TimoRuetten avatar Mar 04 '19 14:03 TimoRuetten

The way Meteor handles CSS, we have a few options:

  1. Tell Meteor to only include the CSS file if it is imported somewhere. This plugin won't know if it's actually imported somewhere - Meteor handles that behind the scenes. When done this way, the CSS is not bundled into a single file and is instead only added to the page when it is imported. This is the behavior you are experiencing.
  2. Tell Meteor to always bundle the CSS file. When done this way, the CSS will be bundled into a single .css file and loaded by Meteor via <link rel="stylesheet"> in the <head>, so the styles will be loaded before your application is displayed. When done this way, all of the CSS in your application (regardless of whether or not you import it) will be included. This already automatically happens in Meteor <= 1.6 when not using the imports directory. We'd probably have to add a flag to allow you to force this behavior for use with the mainModule property introduced in Meteor 1.7.
  3. Modify the plugin to export the css so you can include it in the rendered content by manually adding a <style> tag.

There may be another way to do this by building a custom minifier plugin, but I'm not sure what all that would entail.

akanix42 avatar Mar 04 '19 20:03 akanix42

But is there a way to export the generated CSS when importing the file? If so it would be easy to append the exported css to the head on server side. I do not know much about css-modules so I am not sure how this could be solved.

Tomorrow I will do a little research

TimoRuetten avatar Mar 04 '19 23:03 TimoRuetten

Could you give me a hint how we could do this? Of course I would help when its possible for me to solve this case :)

TimoRuetten avatar Mar 05 '19 14:03 TimoRuetten

For option 3 (the export), I think it should be fairly straightforward (spoken as someone who is intimately familiar with the package, so our perspectives on straightforward may differ 🙂 ): The CSS is accessible as inputFile.contents and is added to the output here: https://github.com/nathantreid/meteor-css-modules/blob/master/css-modules-build-plugin.js#L239

If when shouldAddStylesheet is false we were to assign that to a variable in the output file and then export the variable, I think it would do the trick.

An example of exporting is here, where we do the export for the class names: https://github.com/nathantreid/meteor-css-modules/blob/master/css-modules-build-plugin.js#L247

akanix42 avatar Mar 06 '19 18:03 akanix42

Thanks for your input. Not 100% sure if I understand you correct that I should now change the plugin custom for my own case or if you are interested to implement something to solve it in general?

I think the best solution would be something like this:

  • control in the package.json settings if a server output should be generated
  • if so: when on serverside the classnames are generated I think the output css is also available - then we could maybe add the output css to the sink of meteor so you would be able to access the css right in your render method on serverside.

Would be this something which could be possible? If so and you are not interested in doing it I would try it by myself. Would need some time to understand your plugin in full but I would give it a try.

TimoRuetten avatar Mar 06 '19 19:03 TimoRuetten

I'm interested in doing it, but it may take me a little while to get around to doing so. If you'd like to, feel free to look into it. Currently server output is controlled in package.json via the specificArchitecture option: https://github.com/nathantreid/meteor-css-modules/wiki/Package-Options. I'm not sure we'd need anything more than that, as bundle sizes are less of a concern on the server, but we could add an exportCssOnServer option if necessary. I'm not quite sure what you mean by

the sink of meteor

but with the export change you'd be able to do something like the following:

import { css } from './foo.css';
import React from 'react';

export default class HelloWorld extends React.Component {
  render() {
    return (
      <style>{css}</style>
    );
  }
}

Is that what you had in mind?

akanix42 avatar Mar 07 '19 13:03 akanix42

Your example would be an option but then you need to add your css on any file you are including it. The only reason to do what I want is because of SSR. If you are using SSR with Meteor you are working with sink from meteor.

https://docs.meteor.com/packages/server-render.html

The sink object comes with the onPageLoad callback and holds some information of the request the user has made. So my idea would be to append the css information to the sink because this object is unique for every request. If it is possible to access the sink object on your package it would be easy to append the bundled css to the sink and then I can append it to the HTML on Server.

The other option would be to use it like in your comment. Then it would be more handy to create a React Provider which gets all the bundled css and outputting it. Maybe it could also be possible to "auto-fetch" on any file which is including a css file? But I dont think so.

I am not 100% sure which would be the best solution to solve this but in my case I would think that the sink method would be the easiest because then I am not in the duty to handle outputing the bunbled css on every file which is including style files.

TimoRuetten avatar Mar 07 '19 13:03 TimoRuetten

Hey @nathantreid - I just got your package wrong. I thought that on every request only the needed css will get generated on server - but thats not true. The css which is needed will be generated on the server start (which makes sense - of course).

I was able to clone your package to my local packages directory and will try to do a update which would be make it able to return the needed css on server. My goal would to be to make it possible to export the css like in your example but also to add the generated css to the sink on every request, so it would be possible to send only the needed css of every request back to the client.

Will let you know when I had success to do so.

TimoRuetten avatar Mar 12 '19 17:03 TimoRuetten

Probably related, as I was just on my way to ask @nathantreid if it's possible with the current implementation to merge these multiple stylesheets into one (?):

css-modules-issue

arggh avatar May 31 '19 12:05 arggh