Robert Harbison
Robert Harbison

Reputation: 81

Webpack + Typscript library import is undefined in React project

I am trying to make a React library using Typescript, Webpack and Babel however I am running into a problem. If I build then import the library into a React project then my import is 'undefined' (See the below error). I think this would be because in the bundle.js there is no module.exports for the variable that would represent my class there is only a __webpack_exports__["default"] = (ExampleComponent); (However I am unsure of what this does in practice so I could be wrong.)

I specifically got this error:

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

What I have tried:

Versions:

Code:

React Project:

import React from "react";
import { ExampleComponent } from "test-lib";

// This is always undefined
console.log(ExampleComponent);

function App() {
    return <ExampleComponent />;
}

export default App;

Library Project:

index.ts:

import ExampleComponent from './ExampleComponent'

export { ExampleComponent }

ExampleComponent.tsx

import * as React from 'react'
import './ExampleComponent.css'

interface Props {
    text: string
}

// prettier-ignore
const ExampleComponent: React.FC<Props> = ({ text }) => (
    <h1 className="example-text">{text}</h1>
)

export default ExampleComponent

Library Configs:

tsconfig.json:

{
    "compilerOptions": {
        "outDir": "dist",
        "module": "esnext",
        "lib": ["dom", "esnext"],
        "moduleResolution": "node",
        "jsx": "react",
        "sourceMap": true,
        "declaration": true,
        "esModuleInterop": true,
        "noImplicitReturns": true,
        "noImplicitThis": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "suppressImplicitAnyIndexErrors": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "allowSyntheticDefaultImports": true,
        "target": "es5",
        "allowJs": true,
        "skipLibCheck": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "resolveJsonModule": true
    },
    "include": ["src", "tests"],
    "exclude": ["node_modules", "dist", "example"]
}

.babelrc:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "debug": true,
                "useBuiltIns": "usage",
                "corejs": 3
            }
        ],
        "@babel/preset-react",
        "@babel/preset-typescript"
    ]
}

Webpack Config:

const path = require('path')
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

module.exports = {
    entry: {
        bundle: './src/index.ts',
    },

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js',
    },

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

    devtool: 'source-map',

    module: {
        rules: [
            {
                test: /\.tsx?$/,
                exclude: /node_modules/,
                use: [{ loader: 'babel-loader' }, { loader: 'ts-loader' }],
            },

            {
                test: /\.css$/,
                loaders: ['style-loader', 'css-loader'],
            },

            {
                test: /\.(gif|png|jpe?g|svg)$/,
                use: [
                    'file-loader',
                    {
                        loader: 'image-webpack-loader',
                        options: {
                            disable: true,
                        },
                    },
                ],
            },

            {
                test: /\.js$/,
                enforce: 'pre',
                loader: 'source-map-loader',
            },
        ],
    },

    plugins: [new ForkTsCheckerWebpackPlugin()],
}

If you want to see the full code here is a link to the Github Repo.

Upvotes: 0

Views: 3764

Answers (1)

Robert Harbison
Robert Harbison

Reputation: 81

Based on the comment from Scovy I was able to get this working by using the output.libraryTarget and output.globalObject output options.

Now my output entry in my webpack.base.config.js looks like this:

output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    libraryTarget: 'umd',
    globalObject: 'this',
},

Update:

The above change did not seam to work 100% of the time so I found a library called esm-webpack-plugin which ended up working perfectly.

So the final code for the output entry in the webpack config is:

output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    library: 'LIB',
    libraryTarget: 'var',
},

and I also added the plugin:

plugins: [new ForkTsCheckerWebpackPlugin(), new EsmWebpackPlugin()],

Upvotes: 5

Related Questions