dilanx / craco

Create React App Configuration Override, an easy and comprehensible configuration layer for Create React App.
https://craco.js.org
Apache License 2.0
7.42k stars 501 forks source link

[Support] - How to add less loader #5

Closed frankspin89 closed 5 years ago

frankspin89 commented 5 years ago

Thank you for this great plugin.

I'm trying to implement ant.design library with the latest version of create react app and craco. I manged to implement the babel import loader, but i'm currently stuck add implementing the less loader.

Ant design documentation on how to implement less loader https://ant.design/docs/react/customize-theme https://ant.design/docs/react/use-with-create-react-app

My implementation with craco:

module.exports = {
  style: {
    less: {
      loaderOptions: {
        modifyVars: {
          'primary-color': '#1DA57A',
          'link-color': '#1DA57A',
          'border-radius-base': '2px',
         },
       javascriptEnabled: true,
      }
    },
  },
  babel: {
    plugins: [
      ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": true}, "ant"],
    ],
  },
}

I think that I misunderstand the documentation on how to add less-loader support with craco. Any help is welcome!

jaredjj3 commented 5 years ago

It looks like the current implementation does not support a less loader yet.

jaredjj3 commented 5 years ago

I've Frankensteined together a craco.config.js that accomplishes what you want. This can be extracted into a plugin. I used a fresh install using react-scripts 2.1.1.

// https://github.com/timarney/react-app-rewired/blob/master/packages/react-app-rewire-less/index.js
const path = require('path');

const loaderNameMatches = function(rule, loaderName) {
  return rule && rule.loader && typeof rule.loader === 'string' &&
    (rule.loader.indexOf(`${path.sep}${loaderName}${path.sep}`) !== -1 ||
    rule.loader.indexOf(`@${loaderName}${path.sep}`) !== -1);
};

const getLoader = function(rules, matcher) {
  let loader;

  // Array.prototype.some is used to return early if a matcher is found
  rules.some(rule => {
    return (loader = matcher(rule)
      ? rule
      : getLoader(rule.use || rule.oneOf || (Array.isArray(rule.loader) && rule.loader) || [], matcher));
  });

  return loader;
};

module.exports = {
  babel: {
    plugins: [
      [
        'import',
        {
          'libraryName': 'antd',
          'libraryDirectory':
          'es',
          'style': true
        },
        'ant'
      ],
    ],
  },
  plugins: [
    {
      plugin: {
        overrideWebpackConfig: ({ webpackConfig }) => {
          const lessExtension = /\.less$/;

          const fileLoader = getLoader(
            webpackConfig.module.rules,
            rule => loaderNameMatches(rule, 'file-loader')
          );
          fileLoader.exclude.push(lessExtension);

          const lessRules = {
            oneOf: [{
              test: lessExtension,
              use: [
                {
                  loader: require.resolve('style-loader')
                }, {
                  loader: require.resolve('css-loader')
                }, {
                  loader: require.resolve('less-loader'),
                  options: {
                    modifyVars: {
                      '@primary-color': '#1DA57A',
                      '@link-color': '#1DA57A',
                      '@border-radius-base': '2px',
                    },
                    javascriptEnabled: true
                  }
                }
              ]
            }]
          }

          const oneOfRule = webpackConfig.module.rules.find(rule => (
            typeof rule.oneOf !== 'undefined'
          ));
          const appendTo = oneOfRule ? oneOfRule.oneOf : webpackConfig.module.rules;
          appendTo.push(lessRules);

          return webpackConfig;
        }
      }
    }
  ]
};
patricklafrance commented 5 years ago

@jaredjj3 is right, craco doesn't support Less. The idea is that the core of craco should only allow you to customize what is already supported by CRA.

Additional support should be added as plugins or custom configuration.

patricklafrance commented 5 years ago

@jaredjj3 for information purpose, craco also support a few utility functions to parse the webpack config: https://github.com/sharegate/craco/blob/master/packages/craco/README.md#develop-a-plugin

You could replace:

const fileLoader = getLoader(
    webpackConfig.module.rules,
    rule => loaderNameMatches(rule, 'file-loader')
);

with

const { getLoader, loaderByName } = require("craco");

const { isFound, match } = getLoader(webpackConfig, loaderByName("file-loader"));
if (isFound) {
    match.loader....
}
patricklafrance commented 5 years ago

I will close this issue since we dont support Less.

ndbroadbent commented 5 years ago

Thanks @jaredjj3! I was about to write the same plugin config for Less with Ant Design, so your code was really helpful! And thanks @patricklafrance for mentioning the utility functions. I have to say this is a really well designed tool!

I've modified @jaredjj3's code to use the getLoader / loaderByName utilities. I've also added some good error messages in case something changes when create-react-app or craco are updated. Here's a Gist with my craco.config.js that supports Ant Design, Less, and Preact: https://gist.github.com/ndbroadbent/f7d6a343c5f4976ef4f8fa10a8595bad

I've also opened PRs to add some recipes for Ant Design, Less (without the modifyVars option), and Preact. (It was really easy to get Preact working, but this recipe would have still been helpful.)

farhanmalhi-dbank commented 1 year ago

any update on this?

i am using this config

const { theme } = require('antd/lib');
const { convertLegacyToken } = require('@ant-design/compatible/lib');

const { defaultAlgorithm, defaultSeed } = theme;
const mapToken = defaultAlgorithm(defaultSeed);
const v4Token = convertLegacyToken(mapToken);

module.exports = {
  webpack: {
    configure: (webpackConfig, { env, paths }) => {
      const lessRegex = /\.less$/;
      const lessModuleRegex = /\.module\.less$/;

      const getStyleLoaders = (cssOptions, preProcessor) => {
        const loaders = [
          {
            loader: 'style-loader'
          },
          {
            loader: 'css-loader',
            options: cssOptions
          }
        ];

        if (preProcessor) {
          loaders.push(preProcessor);
        }
        return loaders;
      };

      webpackConfig.module.rules.push(
        {
          test: /\.mjs$/,
          include: /node_modules/,
          type: 'javascript/auto'
        },
        {
          test: /\.(ts|tsx)$/,
          exclude: /node_modules/,
          use: [
            {
              loader: 'babel-loader'
            },
            {
              loader: 'ts-loader',
              options: {
                transpileOnly: true
              }
            }
          ]
        },
        {
          test: lessRegex,
          exclude: lessModuleRegex,
          use: getStyleLoaders(
            {
              importLoaders: 2
            },
            {
              loader: 'less-loader',
              options: {
                lessOptions: {
                  modifyVars: v4Token,
                  javascriptEnabled: true
                }
              }
            }
          )
        },
        {
          test: lessModuleRegex,
          use: getStyleLoaders(
            {
              importLoaders: 2,
              modules: {
                localIdentName: '[local]_[hash:base64:5]'
              }
            },
            {
              loader: 'less-loader',
              options: {
                lessOptions: {
                  modifyVars: v4Token,
                  javascriptEnabled: true
                }
              }
            }
          )
        }
      );

      return webpackConfig;
    }
  }
};
farhanmalhi-dbank commented 1 year ago

reason for not using craco-less is latest version of craco-less supports craco 6 version which has webpack 4 i want to use webpack 5.