Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't import other project's component in create-react-app with craco

I'm having an issue importing a component from one React project into another. The problems seems very basic but I'm having a really hard time figuring out where the problem is and what is the exact craco configuration to achieve my goal.

Exporting project

I'm exporting App, which is a functional React component.

src/index.js

import App from "./App";
export default App;

I'm using craco mainly because of Tailwindcss and antd, this is the configuration file:

craco.config.js

const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
const WebpackBar = require("webpackbar");
const CracoAntDesignPlugin = require("craco-antd");

const path = require("path");

module.exports = {
  style: {
    postcss: {
      plugins: [require("tailwindcss"), require("autoprefixer")],
    },
  },
  webpack: {
    plugins: [
      new WebpackBar({ profile: true }),
      ...(process.env.NODE_ENV === "development"
        ? [new BundleAnalyzerPlugin({ openAnalyzer: false })]
        : []),
    ],
    configure: (webpackConfig, { env, paths }) => {
      paths.appBuild = webpackConfig.output.path = path.resolve("dist");

      webpackConfig.output = {
        ...webpackConfig.output,
        filename: "index.bundle.js",
        path: path.resolve(__dirname, "dist"),
        library: "library",
        libraryTarget: "umd",
      };

      webpackConfig.entry = path.join(__dirname, "./src/index.js");

      return webpackConfig;
    },
  },
  plugins: [
    {
      plugin: CracoAntDesignPlugin,
      options: {
        customizeThemeLessPath: path.join(
          __dirname,
          "./src/styles/variables.less"
        ),
        lessLoaderOptions: {
          lessOptions: {
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};

I'm using npm to publish my package and import it in the other project. This is the package.json start configuration (infos removed for privacy):

package.json

{
  "name": [company-repo-name],
  "version": "1.1.1-alpha16",
  "homepage": ".",
  "repository": [company-repo],
  "main": "dist/index.bundle.js",
  "publishConfig": {
    "registry": [company-registry]
  },
...

npm run build works as intended and generate a index.bundle.js inside the dist folder. I'm not sure if the problem lies here, but the file is full of minified functions and doesn't seem to export anything.

Consumer project

Installing the exporting project via npm works fine, and trying to import the App component gives me no result:

  • import App from [company-repo-name]; gives me {} (empty object)
  • import { App } from [company-repo-name]; gives me undefined

I currently don't know where the problem lies and I'm looking forward for suggestions to try out.

like image 222
Mateus Wolkmer Avatar asked Mar 19 '21 20:03

Mateus Wolkmer


People also ask

How do I import files outside SRC in React?

To import component outside src/ directory with React, we can declare a local dependency on package. json. { //... "dependencies": { "app-b-dashboard": "file:./packages/app-b-dashboard" //... } //... } to declare the app-b-dashboard dependency in the dependencies section of package.

What is craco?

CRACO stands for Create-React-App Configuration Override. It is implemented as an easy way to override create-react-app configuration without mastering Webpack or ejecting.


Video Answer


1 Answers

So after days fiddling with the webpack configurations I came across this life saver article. The thing I didn't try was the first thing the article says: "Output a single bundle instead of chunks". The lines that did the trick included changing the optimization webpack configuration:

config.optimization.splitChunks = {
    cacheGroups: {
      default: false,
    },
  };
config.optimization.runtimeChunk = false;

After following all the steps presented in the article, I ended up with the craco webpack configuration looking like this:

configure: (config, { paths }) => {
  paths.appBuild = config.output.path = path.resolve("build");
  if (process.env.REACT_APP_INJECTABLE === "true") {
    config = disableChunks(config);
    config = makeInjectable(config, { paths });
  }
  return config;
}

disableChunks() was imported from a secondary file that consolidated the build in a single bundle. I also disabled the minimization so I could have more descriptive errors in my consumer project. This is disable-chunks.js:

module.exports = function disableChunks(config) {
  // Consolidate bundle instead of chunk
  // https://webpack.js.org/plugins/split-chunks-plugin
  config.optimization.splitChunks = {
    cacheGroups: {
      default: false,
    },
  };

  // Move runtime into bundle instead of separate file
  // https://webpack.js.org/configuration/optimization
  config.optimization.runtimeChunk = false;

  config.optimization.minimize = false;

  // JS
  // https://webpack.js.org/configuration/output
  config.output.filename = "main.js";

  // CSS
  // https://webpack.js.org/plugins/mini-css-extract-plugin
  const cssPluginIdx = config.plugins
    .map((p) => p.constructor.name)
    .indexOf("MiniCssExtractPlugin");
  if (cssPluginIdx !== -1) {
    config.plugins[cssPluginIdx].options.filename = "main.css";
  }

  return config;
};

The last configuration file is make-injectable.js that sets the output configuration. I has this configured before but decided to move it to another file for organization reasons.

module.exports = function makeInjectable(config, { paths }) {
  // Output a UMD module and define its name via the library key.
  // This key will be what is referenced when the hub looks for
  // the correct module to dynamically load after the bundle is
  // injected into the DOM
  // https://webpack.js.org/configuration/output
  config.output.library = basename(process.env.npm_package_name);
  config.output.libraryTarget = "umd";

  // Set separate entry point when building the injectable lib
  // https://webpack.js.org/concepts/entry-points
  config.entry = `${paths.appSrc}/index.injectable.js`;

  // Exclude shared dependencies to reduce bundle size
  // https://webpack.js.org/configuration/externals
  config.externals = {
    react: "react",
    "react-router-dom": "react-router-dom",
  };

  return config;
};

Again, the code that worked for my was basically copied from the article Implementing a Micro-Frontend Architecture With React by J.C. Yamokoski (if you come across this question, thank you my man).

like image 168
Mateus Wolkmer Avatar answered Oct 22 '22 19:10

Mateus Wolkmer