Jack SZE TO
Jack SZE TO

Reputation: 41

Import React component from local npm package return empty object

I was building my first npm package of a react component.

After I build it successfully with webpack, I tried to test it with npm link.

It connected but the component didn't load. It gives me a bunch of errors like this

Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. Check the render method of `App`.

So I tried to console.log() the component. It returns an empty Object.

I'm struggling with this for 2 days and have no idea what went wrong.

Here's some of the configurations

webpack.config.js

var path = require("path");
const isDevelopment = process.env.NODE_ENV === 'development'
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
    mode: "production",
    entry: {
        index: path.join(__dirname, 'src', 'index.tsx')
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: isDevelopment ? '[name].css' : '[name].[hash].css',
            chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css'
        })
    ],
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                loader: 'ts-loader',
                exclude: /node_modules/,
                options: { allowTsInNodeModules: true }
            },
            {
                test: /\.css$/i,
                use: ["style-loader", "css-loader"],
            },
            {
                test: /\.module\.s[ac]ss$/,
                use: [
                    isDevelopment ? 'style-loader': MiniCssExtractPlugin.loader,
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true,
                            sourceMap: isDevelopment
                        }
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: isDevelopment
                        }
                    }
                ]
            },
            {
                test: /\.s[ac]ss$/i,
                exclude: /\.module.(s[ac]ss)$/,
                use: [
                    // Creates `style` nodes from JS strings
                    isDevelopment ? 'style-loader': MiniCssExtractPlugin.loader,
                    // to generate a .d.ts module
                    "css-modules-typescript-loader",
                    // Translates CSS into CommonJS
                    {loader: "css-loader", options: {modules: true}},
                    // Compiles Sass to CSS
                    {loader: "sass-loader", options: {sourceMap: isDevelopment}},
                ],
            },
        ]
    },
    resolve: {
        extensions: [".ts", ".tsx", ".js", ".css", ".scss"]
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
    },
}

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./dist/",
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "declaration": true,
    "declarationMap": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "removeComments": true,
    "noImplicitAny": true,
    "noEmit": false,
    "jsx": "react-jsx",
    "sourceMap": true,
    "plugins": [{"name": "typescript-plugin-css-modules"}]
  },
  "include": [
    "src"
  ]
}

package.json

{
  "name": "react-dissolve",
  "version": "1.0.0",
  "description": "A color and image animated dissolve effect.",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "files": [
    "dist",
    "src"
  ],
  "scripts": {
    "build": "webpack"
  },
  "author": "jack szeto",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.16.5",
    "@babel/preset-env": "^7.16.5",
    "@babel/preset-react": "^7.16.5",
    "@testing-library/jest-dom": "^5.16.1",
    "@testing-library/react": "^12.1.2",
    "@testing-library/user-event": "^13.5.0",
    "@types/color-string": "^1.5.2",
    "@types/jest": "^27.0.3",
    "@types/node": "^16.11.15",
    "@types/node-sass": "^4.11.2",
    "@types/react": "^17.0.37",
    "@types/react-dom": "^17.0.11",
    "color-string": "^1.9.0",
    "css-loader": "^6.5.1",
    "css-modules-typescript-loader": "^4.0.1",
    "mini-css-extract-plugin": "^2.4.5",
    "node-sass": "^7.0.1",
    "rc-slider": "^9.7.5",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-p5": "^1.3.24",
    "react-scripts": "5.0.0",
    "sass": "^1.45.1",
    "sass-loader": "^12.4.0",
    "simplex-noise": "^3.0.0",
    "style-loader": "^3.3.1",
    "ts-loader": "^9.2.6",
    "typescript": "^4.5.4",
    "web-vitals": "^2.1.2",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
  },
  "peerDependencies": {
    "react": "^17.0.2",
    "react-p5": "^1.3.24",
    "simplex-noise": "^3.0.0",
    "color-string": "^1.9.0",
    "rc-slider": "^9.7.5",
    "sass": "^1.45.1"
  },
  "dependencies": {}
}

The build was put in the dist folder and it seems to perfect success build in the dist.

Screenshot:

enter image description here

Minimal reproduction case:

by using the same configuration above. I created a test project(see structure below).

I ran npm run build to build the component with webpack.

Then npm link to link the test project to a new ts React project. Tried to import but it produced the same error.

use case

import Test from 'npm-test';
...
<Test />

file structure

npm-test/
  dist/
  node_modules/
  src/
    declarations.d.ts
    index.tsx
    style.module.scss
  .babelrc
  .gitgnore
  .npmignore
  package.json
  tsconfig.json
  webpack.config.js

index.tsx

import React, { CSSProperties } from 'react'
import styles from './style.module.scss';

interface TestProps {
    className?: string,
    style?: CSSProperties,
}
const Test = ({className, style}:TestProps) => {
    return (
        <div className={`${styles.test} ${className}`}
            style={{...style}}
        >
            Test
        </div>
    )
}
export default Test;

exported index.d.ts

import { CSSProperties } from 'react';
interface TestProps {
    className?: string;
    style?: CSSProperties;
}
declare const Test: ({ className, style }: TestProps) => JSX.Element;
export default Test;
//# sourceMappingURL=index.d.ts.map

Thanks for reading the long post!

Upvotes: 1

Views: 925

Answers (1)

Jack SZE TO
Jack SZE TO

Reputation: 41

I've solved the problem. Thanks @JaredSmith for helping me.

I messed around with webpack but finally, I decided to move on with rollup which is much easier to use.

Found the Rollup tutorial by Portexe and it's very helpful.

So I changed the file structure to this

npm-test-hellow-world/
  src/
    component/
      hello-world.tsx
    index.ts
  .babelrc
  .gitgnore
  .npmignore
  package.json
  tsconfig.json
  rollup.config.js

Since in hello-world.tsx the function component is exported as default

declare const HelloWorld: React.FC<HelloWorldProps>;
export default HelloWorld;

Somehow the compiler didn't export the component. So I put it in the src/component/ folder and export it from src/index.ts

export {default} from './component/hello-world';
// notice: 
// export * from './component/hello-world';
// didn't work

and it works! Not really sure why tho. If someone could explain it, I'll much appreciate it.

If you want to see more detail, you can check out the GitHub repo

Upvotes: 1

Related Questions