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.44k stars 498 forks source link

CRACO with linaria #534

Open present-g opened 9 months ago

present-g commented 9 months ago

What's happening This documentation for Linaria is outdated. The proposed plugin craco-linaria is outdated and refers to an old version of the Linaria loader

What should happen Documentation must comply with new approaches to connecting Linaria by @wyw-in-js

To reproduce

  1. Connect craco-linaria as described in the documentation
  2. Run carco start

CRACO version 7.1.0

CRACO config

// craco.config.js

// Official documentation available at: https://github.com/jednano/craco-linaria
module.exports = {
  plugins: [{ plugin: require('craco-linaria') }],
};
jerrywithaz commented 3 weeks ago

Took me a while to figure out but here is what you have to do:

craco.config.ts

import path from "path";
import { loaderByName, addBeforeLoader } from "@craco/craco";
import { BaseContext, CracoConfig } from "@craco/types";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import webpack from "webpack";

const src = path.resolve(__dirname, "src");

function transformBabelLoader(
  loader: webpack.RuleSetRule,
  context: BaseContext
) {
  const options = (loader.options as {}) || {};
  const presets = ((options as any).presets) || [];

  const babelLoader = {
    loader: loader.loader,
    options: {...options, presets: [...presets, [require.resolve("@wyw-in-js/babel-preset")]]}
  };

  const linariaLoader = {
    loader: require.resolve("@wyw-in-js/webpack-loader"),
    options: {
      sourceMap: context.env !== "production",
      cacheDirectory: path.resolve(src, ".linaria_cache"),
    },
  };

  return {
    test: loader.test,
    include: loader.include,
    exclude: loader.exclude,
    use: [babelLoader, linariaLoader],
  };
}

const config: CracoConfig = {
  webpack: {
    alias: {
      "@app": src,
    },
    configure(config, context) {
      if (!config.plugins) config.plugins = [];
      if (!config.module) config.module = {};
      if (!config.module.rules) config.module.rules = [];

      const oneOfRules = config.module.rules.find(
        (rule) => typeof rule === "object" && rule !== null && rule?.oneOf
      ) as webpack.RuleSetRule;

      if (!oneOfRules) {
        throw new Error(
          `Can't find 'oneOf' rules under module.rules in the ${context.env} webpack config!`
        );
      }

      if (!oneOfRules.oneOf) {
        throw new Error("One of error");
      }

      oneOfRules.oneOf[3] = transformBabelLoader(
        oneOfRules.oneOf[3] as webpack.RuleSetRule,
        context
      );

      oneOfRules.oneOf[4] = transformBabelLoader(
        oneOfRules.oneOf[4] as webpack.RuleSetRule,
        context
      );

      addBeforeLoader(config, loaderByName("css-loader"), {
        loader: MiniCssExtractPlugin.loader,
      });

      config.plugins.push(
        context.env === "production"
          ? new MiniCssExtractPlugin({
              filename: "styles-[contenthash].css",
            })
          : new MiniCssExtractPlugin({
              filename: "styles.css",
            })
      );

      return config;
    },
  },
};

export default config;

wyw-in-js.config.js

module.exports = {
  evaluate: true,
  displayName: false,
  babelOptions: {
    presets: [
      [
        require.resolve('babel-preset-react-app'),
        { runtime: 'automatic' }
      ],
      [require.resolve("@wyw-in-js/babel-preset"), {}]
    ]
  }
};