Reputation: 41
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:
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
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