Next.js: with-ant-design-less can't work together with cssModule

Created on 29 Jul 2019  Â·  6Comments  Â·  Source: vercel/next.js

Examples bug report

Example name

with-ant-design-less

Describe the bug

Using ant-design-less with cssModule makes all ant design style disappear.

To Reproduce

  1. Go to 'next.config.js'
  2. Add this code snippets into withLess option
cssModules: true,
cssLoaderOptions: {
  importLoaders: 1,
  localIdentName: '[local]___[hash:base64:5]',
},
  1. Save
  2. All ant design style is gone

Expected behavior

The example should apply ant design style when cssModule option is set to true.

Screenshots

Before apply cssModule
Screen Shot 2562-07-29 at 16 35 22

After apply cssModule
Screen Shot 2562-07-29 at 16 36 41

System information

  • OS: macOS
  • Browser: Any browser
  • Version of Next.js: 9.0.2

Most helpful comment

After taking the time to research I found that setting cssModule to true turn the whole project to use cssModule but some node_modules like antd doesn't implement by using cssModule.

So, I customized @zeit/next-less to support work like this.

next-antd-less.config.js

var cssLoaderConfig = require('@zeit/next-css/css-loader-config');

module.exports = (nextConfig = {}) => {
  return Object.assign({}, nextConfig, {
    webpack: (config, options) => {
      if (!options.defaultLoaders) {
        throw new Error(
          'This plugin is not compatible with Next.js versions below 5.0.0 https://err.sh/next-plugins/upgrade'
        );
      }

      const { dev, isServer } = options;
      const {
        cssModules,
        cssLoaderOptions,
        postcssLoaderOptions,
        lessLoaderOptions = {},
      } = nextConfig;

      options.defaultLoaders.less = cssLoaderConfig(config, {
        extensions: ['less'],
        cssModules,
        cssLoaderOptions,
        postcssLoaderOptions,
        dev,
        isServer,
        loaders: [
          {
            loader: 'less-loader',
            options: lessLoaderOptions,
          },
        ],
      });

      config.module.rules.push({
        test: /\.less$/,
        exclude: /node_modules/,
        use: options.defaultLoaders.less,
      });

      // disable antd css module
      config.module.rules.push({
        test: /\.less$/,
        include: /node_modules/,
        use: cssLoaderConfig(config, {
          extensions: ['less'],
          cssModules: false,
          cssLoaderOptions: {},
          dev,
          isServer,
          loaders: [
            {
              loader: 'less-loader',
              options: lessLoaderOptions,
            },
          ],
        }),
      });

      if (isServer) {
        const antStyles = /antd\/.*?\/style.*?/;
        const origExternals = [...config.externals];
        config.externals = [
          (context, request, callback) => {
            if (request.match(antStyles)) return callback();
            if (typeof origExternals[0] === 'function') {
              origExternals[0](context, request, callback);
            } else {
              callback();
            }
          },
          ...(typeof origExternals[0] === 'function' ? [] : origExternals),
        ];

        config.module.rules.unshift({
          test: antStyles,
          use: 'null-loader',
        });
      }

      if (typeof nextConfig.webpack === 'function') {
        return nextConfig.webpack(config, options);
      }

      return config;
    }
  });
};

then use it in next.config.js

const withNextAntdLess = require('./next-antd-less.config');
module.exports = withNextAntdLess({
    cssModules: true,
    cssLoaderOptions: {
      importLoaders: 1,
      localIdentName: '[local]___[hash:base64:5]',
    },
})

In my case, it usually works. Hopefully, It will help someone to solve the problem to work with antd together with cssModule.

Related example/issues/modules which I use to compose the solution. #8054 #7957 with-ant-design-less next-antd-aza-less

Anyway, if there is an official solution for this issue it will be very good. In my opinion, it will be a common issue.

All 6 comments

After taking the time to research I found that setting cssModule to true turn the whole project to use cssModule but some node_modules like antd doesn't implement by using cssModule.

So, I customized @zeit/next-less to support work like this.

next-antd-less.config.js

var cssLoaderConfig = require('@zeit/next-css/css-loader-config');

module.exports = (nextConfig = {}) => {
  return Object.assign({}, nextConfig, {
    webpack: (config, options) => {
      if (!options.defaultLoaders) {
        throw new Error(
          'This plugin is not compatible with Next.js versions below 5.0.0 https://err.sh/next-plugins/upgrade'
        );
      }

      const { dev, isServer } = options;
      const {
        cssModules,
        cssLoaderOptions,
        postcssLoaderOptions,
        lessLoaderOptions = {},
      } = nextConfig;

      options.defaultLoaders.less = cssLoaderConfig(config, {
        extensions: ['less'],
        cssModules,
        cssLoaderOptions,
        postcssLoaderOptions,
        dev,
        isServer,
        loaders: [
          {
            loader: 'less-loader',
            options: lessLoaderOptions,
          },
        ],
      });

      config.module.rules.push({
        test: /\.less$/,
        exclude: /node_modules/,
        use: options.defaultLoaders.less,
      });

      // disable antd css module
      config.module.rules.push({
        test: /\.less$/,
        include: /node_modules/,
        use: cssLoaderConfig(config, {
          extensions: ['less'],
          cssModules: false,
          cssLoaderOptions: {},
          dev,
          isServer,
          loaders: [
            {
              loader: 'less-loader',
              options: lessLoaderOptions,
            },
          ],
        }),
      });

      if (isServer) {
        const antStyles = /antd\/.*?\/style.*?/;
        const origExternals = [...config.externals];
        config.externals = [
          (context, request, callback) => {
            if (request.match(antStyles)) return callback();
            if (typeof origExternals[0] === 'function') {
              origExternals[0](context, request, callback);
            } else {
              callback();
            }
          },
          ...(typeof origExternals[0] === 'function' ? [] : origExternals),
        ];

        config.module.rules.unshift({
          test: antStyles,
          use: 'null-loader',
        });
      }

      if (typeof nextConfig.webpack === 'function') {
        return nextConfig.webpack(config, options);
      }

      return config;
    }
  });
};

then use it in next.config.js

const withNextAntdLess = require('./next-antd-less.config');
module.exports = withNextAntdLess({
    cssModules: true,
    cssLoaderOptions: {
      importLoaders: 1,
      localIdentName: '[local]___[hash:base64:5]',
    },
})

In my case, it usually works. Hopefully, It will help someone to solve the problem to work with antd together with cssModule.

Related example/issues/modules which I use to compose the solution. #8054 #7957 with-ant-design-less next-antd-aza-less

Anyway, if there is an official solution for this issue it will be very good. In my opinion, it will be a common issue.

Thanks for sharing! Your code build fine for me too except that it showed the following error when I actually added my own .css file

圖片

I need to add the following module rule to the above next-antd-less.config.js to make it work:

        ......
                    loader: 'less-loader',
                    options: lessLoaderOptions
                }
            ]
        })

        config.module.rules.push({
            test: /\.css$/,
            exclude: /node_modules/,
            use: options.defaultLoaders.less
        })

        config.module.rules.push({
            test: /\.less$/,
            exclude: /node_modules/,
        ......

In my opinion, the proper way to solve your error is wrap the next-antd-less.config.js with @zeit/next-css

eg.

const withNextAntdLess = require(‘./next-antd-less.config.js
const withCss = require(‘@zeit/next-css’)

export.modules = withCss(withNextAntdLess({
  ...youtOptions,
})

Notice that you are using less-loader to test the css file. I’m not sure that it will make any side effect or not.

After taking the time to research I found that setting cssModule to true turn the whole project to use cssModule but some node_modules like antd doesn't implement by using cssModule.

So, I customized @zeit/next-less to support work like this.

next-antd-less.config.js

var cssLoaderConfig = require('@zeit/next-css/css-loader-config');

module.exports = (nextConfig = {}) => {
  return Object.assign({}, nextConfig, {
    webpack: (config, options) => {
      if (!options.defaultLoaders) {
        throw new Error(
          'This plugin is not compatible with Next.js versions below 5.0.0 https://err.sh/next-plugins/upgrade'
        );
      }

      const { dev, isServer } = options;
      const {
        cssModules,
        cssLoaderOptions,
        postcssLoaderOptions,
        lessLoaderOptions = {},
      } = nextConfig;

      options.defaultLoaders.less = cssLoaderConfig(config, {
        extensions: ['less'],
        cssModules,
        cssLoaderOptions,
        postcssLoaderOptions,
        dev,
        isServer,
        loaders: [
          {
            loader: 'less-loader',
            options: lessLoaderOptions,
          },
        ],
      });

      config.module.rules.push({
        test: /\.less$/,
        exclude: /node_modules/,
        use: options.defaultLoaders.less,
      });

      // disable antd css module
      config.module.rules.push({
        test: /\.less$/,
        include: /node_modules/,
        use: cssLoaderConfig(config, {
          extensions: ['less'],
          cssModules: false,
          cssLoaderOptions: {},
          dev,
          isServer,
          loaders: [
            {
              loader: 'less-loader',
              options: lessLoaderOptions,
            },
          ],
        }),
      });

      if (isServer) {
        const antStyles = /antd\/.*?\/style.*?/;
        const origExternals = [...config.externals];
        config.externals = [
          (context, request, callback) => {
            if (request.match(antStyles)) return callback();
            if (typeof origExternals[0] === 'function') {
              origExternals[0](context, request, callback);
            } else {
              callback();
            }
          },
          ...(typeof origExternals[0] === 'function' ? [] : origExternals),
        ];

        config.module.rules.unshift({
          test: antStyles,
          use: 'null-loader',
        });
      }

      if (typeof nextConfig.webpack === 'function') {
        return nextConfig.webpack(config, options);
      }

      return config;
    }
  });
};

then use it in next.config.js

const withNextAntdLess = require('./next-antd-less.config');
module.exports = withNextAntdLess({
    cssModules: true,
    cssLoaderOptions: {
      importLoaders: 1,
      localIdentName: '[local]___[hash:base64:5]',
    },
})

In my case, it usually works. Hopefully, It will help someone to solve the problem to work with antd together with cssModule.

Related example/issues/modules which I use to compose the solution. #8054 #7957 with-ant-design-less next-antd-aza-less

Anyway, if there is an official solution for this issue it will be very good. In my opinion, it will be a common issue.

how to add withSass in config

Closing as this example does not showcase CSS Modules itself. Feel free to create a new example combining the two, if you'd like.

If you use Next.js > 9.3, you can try next-plugin-antd-less Use Antd (with Less) with Next.js, Zero Dependency on other Next-Plugins.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dunika picture dunika  Â·  58Comments

poyiding picture poyiding  Â·  73Comments

Vista1nik picture Vista1nik  Â·  55Comments

Knaackee picture Knaackee  Â·  122Comments

nickredmark picture nickredmark  Â·  60Comments