Using SVG as React Component from Private React UI Library-(CRA) inside React app with Webpack 5 crash
Asked Answered
M

1

3

I created a UI Library with CRA. The library is published on npm. Components in the library use SVG, which I use with the syntax of

import {ReactCompoent as Icon} from 'assets / icon'

I want to use the library inside my main app that I create with react 17.0.2 and webpack 5. I add the loaders to Webpack and I can use svg as components inside Main app as -

import CalendarIcon from './Calendar.svg'

and it works.

After I install the library and import the component that use SVG as ReactComponent:

import {BadgeBox} from '@privatelib/ui/lib/src'

the app crashes. With an error of:

WARNING in ./node_modules/@privatelib/ui/lib/src/components/BadgeBox/index.js 107:1697-1707

export 'ReactComponent' (imported as 'CloseIcon') was not found in '../../assets/sm/xsmall-icon.svg' (possible exports: default)

My React UI Library:

components:

import { ReactComponent as CloseIcon } from '../../assets/sm/x-small-icon.svg';

tsconfig file:

{
  "compilerOptions": {
    "target": "es5",
    "baseUrl": "src",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext",
      "es5",
      "es2015",
      "es2016"
    ],
    "allowJs": false,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "react-jsx",
    "sourceMap": true,
    "declarationMap": true,
    "declaration": true,
    "outDir": "lib"
  },
  "types": ["cypress", "cypress-file-upload"],
  "include": [
    "src/**/*",
    "custom.d.ts",
  ],
}

My Main App:

webpack config:

const HtmlWebPackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

const deps = require('./package.json').dependencies;

module.exports = {
  output: {
    filename: '[name].[contenthash:20].esm.js',
    chunkFilename: '[name].[chunkhash:20].esm.js',
    hashFunction: 'xxhash64',
    pathinfo: false,
    crossOriginLoading: false,
    clean: true,
    publicPath: 'auto',
  },

  resolve: {
    extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
  },

  devServer: {
    port: 3011,
    historyApiFallback: true,
  },

  module: {
    rules: [
      {
        test: /\.m?js/,
        type: 'javascript/auto',
        resolve: {
          fullySpecified: false,
        },
      },
      {
        test: /\.(css|s[ac]ss)$/i,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
      {
        test: /\.(ts|tsx|js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.svg$/i,
        issuer: /\.[jt]sx?$/,
        use: ['@svgr/webpack'],
      },
      {
        test: /\.(?:ico|gif|png|jpg|jpeg|)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.(woff(2)?|eot|ttf|otf|)$/,
        type: 'asset/inline',
      },
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'sidebar',
      filename: 'remoteEntry.js',
      remotes: {},
      exposes: {
        './SideBar': './src/components/Sidebar/index.tsx',
      },
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        'react-dom': {
          singleton: true,
          requiredVersion: deps['react-dom'],
        },
      },
    }),
    new HtmlWebPackPlugin({
      template: './src/index.html',
    }),
  ],
};

babelrc:

{
  "presets": ["@babel/preset-typescript", "@babel/preset-react", "@babel/preset-env"],
  "plugins": [
    ["@babel/transform-runtime"],
    "babel-plugin-styled-components"
  ]
}
Mccall answered 11/4, 2022 at 9:31 Comment(0)
D
-1

TL;DR The right way of importing default exports in JavaScript is

import CloseIcon from '../../assets/sm/x-small-icon.svg';

In JavaScript, the import syntax is as follows:

import defaultExport from "module-name";
import { export1 } from "module-name";

which means:

  • "import the component of module-name that is exported as default and use it as defaultExport"
  • "import the component of module-name that is exported as export1 and use it as such"

According to the SVGR docs (1),

Please note that by default, @svgr/webpack will try to export the React Component via default export if there is no other loader handling svg files with default export.

Translated to plain JavaScript-English, this means "as long as you do not tell us otherwise, we will export the ReactComponent as the default". Since you are not configuring anything different in your webpack config and trying to import the ReactComponent, I assume it should be available as the default export. Therefore, the right way to importing it is

import CloseIcon from '../../assets/sm/x-small-icon.svg';

This is just a guess, of course you can proof me wrong. (2)


  1. I assume you are using @svgr/webpack again, as in your previous question.
  2. Since this proposal is deducted from the docs and your code, there might be other factors involved that I'm not aware of.
Deputize answered 11/4, 2022 at 19:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.