jaredpalmer / razzle

✨ Create server-rendered universal JavaScript applications with no configuration
https://razzlejs.org
MIT License
11.1k stars 866 forks source link

Razzle and CSS modules #428

Closed stereobooster closed 6 years ago

stereobooster commented 6 years ago

I'm trying to make razzle work with CSS Modules. No Luck yet. Any advice? As as I will manage to do it, can create PR to examples. Current code https://github.com/stereobooster/razzle-cssmodules

Thanks for awesome project.

rafaelderolez commented 6 years ago

Trying to set up the exact same thing!

Edit: not the exact same thing. We're trying to achieve scss + cssmodules.

I was using the config from the with-scss pull request, added a couple of options to it and it works on initial load, but breaks after refreshing due to the classNames not matching between server and client.

proxyConsole.js:54 Warning: Prop `className` did not match. Server: "null" Client: "Component__classNameHere___1AxrA"

I've added the following in the dev css-loader options:

modules: true,
localIdentName: '[name]__[local]___[hash:base64:5]'

And the following in the production css-loader options: modules: true

Not perfect but halfway there, I'd say. Here's the complete config so far:


const autoprefixer = require('autoprefixer');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  modify: (baseConfig, { target, dev }) => {
    const appConfig = Object.assign({}, baseConfig);
    const isServer = target !== 'web';

    const postCssLoader = {
      loader: 'postcss-loader',
      options: {
        ident: 'postcss', // https://webpack.js.org/guides/migrating/#complex-options
        sourceMap: dev,
        plugins: () => [
          autoprefixer({
            browsers: [
              '>1%',
              'last 4 versions',
              'Firefox ESR',
              'not ie < 9' // React doesn't support IE8 anyway
            ]
          })
        ]
      }
    };

    appConfig.module.rules.push({
      test: /.scss$/,
      // Handle scss imports on the server

      use: isServer
        ? ['css-loader', 'sass-loader']
        : // For development, include source map
          dev
          ? [
              'style-loader',
              {
                loader: 'css-loader',
                options: {
                  sourceMap: true,
                  modules: true,
                  localIdentName: '[name]__[local]___[hash:base64:5]'
                }
              },
              postCssLoader,
              {
                loader: 'sass-loader',
                options: {
                  sourceMap: true
                }
              }
            ]
          : // For production, extract CSS
            ExtractTextPlugin.extract({
              fallback: 'style-loader',
              use: [
                {
                  loader: 'css-loader',
                  options: {
                    importLoaders: 1,
                    modules: true
                  }
                },
                postCssLoader,
                'sass-loader'
              ]
            })
    });

    if (!isServer && !dev) {
      appConfig.plugins.push(
        new ExtractTextPlugin('static/css/[name].[contenthash:8].css')
      );
    }

    return appConfig;
  }
};
jaredpalmer commented 6 years ago

Razzle uses the same CSS setup as CRA. You would indeed need to replace the /.css/ module webpack rules in their entirety on the client. I'm pretty sure the server doesn't need to be touched at all.

stereobooster commented 6 years ago

You would indeed need to replace the /.css/

Yes this is what I'm doing.

I'm pretty sure the server doesn't need to be touched at all.

I hopped so too. But I get same error as @rafaelderolez. There are no CSS Modules on server, so css import returns object without classes, when I do styles.className I get undefined on server and proper class name on the client, hence mismatch

jaredpalmer commented 6 years ago

Interesting. Have to try this out myself

stereobooster commented 6 years ago

Ok I fixed it. Working code in the repo. @jaredpalmer do you want PR for that?

jaredpalmer commented 6 years ago

Yeah that’s be great to add as an example

stereobooster commented 6 years ago

I rushed to declare a victory. I fixed one problem with webpack, but CSS modules doesn't work :facepalm:

stereobooster commented 6 years ago

Opened PR. Closing issue #432

wagnerjsilva commented 6 years ago

Hi Guys,

Also trying to get CSS modules to work here, tried the example above but only getting an empty object in the front end.

Any ideas?

stereobooster commented 6 years ago

Did you try #432?

wagnerjsilva commented 6 years ago

I'll try it again now. Thanks for getting back :-)

wagnerjsilva commented 6 years ago

Yeah, same thing. Just an empty object. No errors.

Here are my files:

Navigation.css

.NavLink { color: #ff0000; }

Navigation.js

import React from 'react';
import Aux from '../../hoc/Aux/Aux';
import { Link } from 'react-router-dom';
import classes from './Navigation.css';

const Navigation = () => {

    console.log(classes.NavLink);

    //@todo use react nav links, fetch data from props - no need for state here
    return (
        <Aux>
            <ul>
                <li className={classes.NavLink}><Link to="/">Home</Link></li>
                <li><Link to="/critical-illness-insurance/what-is-the-best-critical-illness-cover">Best CIC</Link></li>
                <li><Link to="/income-protection-insurance/what-is-the-best-income-protection-cover">Best IP</Link></li>
            </ul>
        </Aux>
    );
};

export default Navigation;

razzle.config.js

"use strict";

const autoprefixer = require("autoprefixer");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const path = require("path");

module.exports = {
  modify(config, { target, dev }, webpack) {
    const appConfig = Object.assign({}, config);
    const isServer = target !== "web";
    const postCSSLoaderOptions = {
      ident: "postcss", // https://webpack.js.org/guides/migrating/#complex-options
      plugins: () => [
        require("postcss-flexbugs-fixes"),
        autoprefixer({
          browsers: [
            ">1%",
            "last 4 versions",
            "Firefox ESR",
            "not ie < 9" // React doesn't support IE8 anyway
          ],
          flexbox: "no-2009"
        })
      ]
    };

    const cssConfig = modules =>
      [
        {
          loader: require.resolve("css-loader"),
          options: {
            importLoaders: 1,
            minimize: !dev,
            sourceMap: !dev,
            modules: modules,
            localIdentName: modules ? "[path]__[name]___[local]" : undefined
          }
        },
        isServer && {
          loader: require.resolve("postcss-loader"),
          options: postCSSLoaderOptions
        }
      ].filter(x => !!x);
    const css = cssConfig(false);
    const cssModules = cssConfig(true);

    const i = appConfig.module.rules.findIndex(
      rule => rule.test && !!".css".match(rule.test)
    );

    if (!dev && !isServer) {
      appConfig.module.rules[i] = {
        test: /\.css$/,
        exclude: /\.module\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: {
            loader: require.resolve("style-loader"),
            options: {
              hmr: false
            }
          },
          use: css
        })
      };
      appConfig.module.rules.push({
        test: /\.module\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: {
            loader: require.resolve("style-loader"),
            options: {
              hmr: false
            }
          },
          use: cssModules
        })
      });
      appConfig.plugins.push(
        new ExtractTextPlugin("static/css/[name].[contenthash:8].css")
      );
    } else if (!dev && isServer) {
      appConfig.module.rules[i] = {
        test: /\.css$/,
        exclude: /\.module\.css$/,
        use: css
      };
      appConfig.module.rules.push({
        test: /\.module\.css$/,
        use: [
          isServer && require.resolve("isomorphic-style-loader"),
          ...cssModules
        ].filter(x => !!x)
      });
    } else {
      appConfig.module.rules[i] = {
        test: /\.css$/,
        exclude: /\.module\.css$/,
        use: [!isServer && require.resolve("style-loader"), ...css].filter(
          x => !!x
        )
      };
      appConfig.module.rules.push({
        test: /\.module\.css$/,
        use: [
          isServer
            ? require.resolve("isomorphic-style-loader")
            : require.resolve("style-loader"),
          ...cssModules
        ].filter(x => !!x)
      });
    }

    return appConfig;
  }
};`
wagnerjsilva commented 6 years ago

Switching to Navigation.module.css returns:

ERROR in ./src/components/Navigation/Navigation.module.css (./node_modules/css-loader??ref--9-1!./node_modules/postcss-loader/lib??postcss!./node_modules/style-loader!./node_modules/css-loader??ref--10-1!./src/components/Navigation/Navigation.module.css) Module build failed (from ./node_modules/postcss-loader/lib/index.js): Syntax Error

(2:1) Unknown word

stereobooster commented 6 years ago

It is import classes from './Navigation.module.css'; per c-r-a convention.

wagnerjsilva commented 6 years ago

Yeah, I've been trying that :-(

ERROR in ./src/components/Navigation/Navigation.module.css (./node_modules/css-loader??ref--9-1!./node_modules/postcss-loader/lib??postcss!./node_modules/style-loader!./node_modules/css-loader??ref--10-1!./src/components/Navigation/Navigation.module.css)
Module build failed (from ./node_modules/postcss-loader/lib/index.js):
Syntax Error 

(2:1) Unknown word

  1 | 
> 2 | var content = require("!!../../../node_modules/css-loader/index.js??ref--10-1!./Navigation.module.css");
stereobooster commented 6 years ago

Hm... have no idea, haven't see this before. Something with webpack config

wagnerjsilva commented 6 years ago

Yeah very strange. I'll update if I get around it.

wagnerjsilva commented 6 years ago

Ha! I'm kicking myself.

After banging my head against the keyboard a few times I noticed the module.css was being already processed, and when I added the duplicated rule things went bananas!

Thanks for the help though.

For prosperity it's worth noticing that css modules seem to be already being processed by Razzle/CRA without the need of a new razzle config. Unless I've done something really crazy with my local install :-)