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

2

16

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.

Immotile answered 19/3, 2021 at 20:58 Comment(6)
what does import * as Something from [company-repo-name]; give you?Romanticize
Also an empty object @TiagoCoelhoImmotile
Did you tried with import {library} from [company-repo-name]?Exhibition
Yes @lissettdm, that returns undefined.Immotile
I'm trying to disable chunks right now based on this: floqast.com/engineering-blog/post/…, lets see how this goes.Immotile
I think setting the libraryTarget as commonjs2 is webpackConfig.output would fix the issue. Just try npm link to test out the pkg locally without having to deploy.Outplay
I
2

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).

Immotile answered 29/3, 2021 at 21:20 Comment(0)
N
0

This helped me:

webpackConfig.output = {
    ...webpackConfig.output,
    ...{
      library: 'xxxxx',
      libraryTarget: 'umd',
      jsonpFunction: `webpackJsonp_${name}`,
    },
  }
Ninette answered 22/9, 2021 at 12:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.