Unexpected "Uncaught TypeError: XXX is not a constructor" errors with Babel and ES6

53,190

Solution 1

What could I be missing?

Babel assigns default exports to the default property. So if you use require to import ES6 modules, you need to access the default property:

const Button = require('./Components/Button.js').default;

Solution 2

I realize that you already have an answer. However I had a similar issue to which I found an answer. Starting my own question and answering it seems weird. So I'm just going to leave this here.

I had the same error as you got. However, I managed to solve it by changing my

export default {Class}

to

export default Class

I don't know why I wrapped the Class in an object but I remember having seen it somewhere so I just started using it.

So instead of the default returning a Class it returned an object like this {Class: Class}. This is completely valid yet it will break webpack+babel.

EDIT: I've since come to know why this probably breaks babel+webpack. The export default is meant to only have 1 export. A javascript-object can contain many properties. Which means it can have more than 1 export. (See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export).

For multiple exports use: export {definition1, definition2}.

Use-case: I've used this in a situation where I've created a library which exported different types of an editor (while the underlying code was the same, the appearance of the editor changes depending on which export you use).

Solution 3

You can just put export var __useDefault = true; just after exporting your Class.

export default class Header {
...
} 
export var __useDefault = true;

Solution 4

I was able to fix this by adding babel-plugin-add-module-exports to the .babelrc file

npm install babel-plugin-add-module-exports --save-dev

{
  "presets": ["@babel/env"],
  "plugins": ["add-module-exports"]
}

this adds

module.exports = exports.default;

to the last line when compiling the class with babel.

Share:
53,190
Silver Quettier
Author by

Silver Quettier

Computer enthusiast, working as web / Java developper at CGI Group. Also does some basic system administration from time to time, and runs some servers for a small gaming community.

Updated on February 05, 2022

Comments

  • Silver Quettier
    Silver Quettier over 2 years

    I am giving a try to Webpack, and am giving a try to the instructions in this tutorial, give or take a few custom things.

    This is simple code, really, but I'm quite puzzled about this error, and feel this is something silly that I missed.

    I defined two ES6 classes, each corresponding to a Handlebars template, and my app's entrypoint is supposed to replace the placeholder HTML in the index file by their contents:

    Entrypoint:

    import './bloj.less'
    
    // If we have a link, render the Button component on it
    if (document.querySelectorAll('a').length) {
        require.ensure([], () => {
            const Button = require('./Components/Button.js');
            const button = new Button('9gag.com');
    
            button.render('a');
        }, 'button');
    }
    
    // If we have a title, render the Header component on it
    if (document.querySelectorAll('h1').length) {
        require.ensure([], () => {
            const Header = require('./Components/Header.js');
    
            new Header().render('h1');
        }, 'header');
    }
    

    Index:

    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
        <h1>My title</h1>
        <a>Click me</a>
    
        <script src="build/bloj.js"></script>
    </body>
    </html>
    

    Button:

    import $ from 'jquery';
    import './Button.less';
    
    export default class Button {
    
        constructor(link) {
            this.link = link;
        }
    
        onClick(event) {
            event.preventDefault();
            alert(this.link);
        }
    
        render(node) {
            const text = $(node).text();
            var compiled = require('./Button.hbs');
    
            // Render our button
            $(node).html(
                compiled({"text": text, "link": this.link})
            );
    
            // Attach our listeners
            $('.button').click(this.onClick.bind(this));
        }
    }
    

    Header:

    import $ from 'jquery';
    import './Header.less';
    
    export default class Header {
        render(node) {
            const text = $(node).text();
            var compiled = require('./Header.hbs');
    
            // Render the header
            $(node).html(
                compiled({"text": text})
            );
        }
    }
    

    Sadly, it does not work, and I get both these errors when displaying the page:

    Uncaught TypeError: Header is not a constructor
    Uncaught TypeError: Button is not a constructor
    

    What could I be missing?

    Here is my webpack configuration:

    var path = require('path');
    var webpack = require('webpack');
    var CleanPlugin = require('clean-webpack-plugin');
    var ExtractPlugin = require('extract-text-webpack-plugin');
    
    var production = process.env.NODE_ENV === 'production';
    var appName = 'bloj';
    var entryPoint = './src/bloj.js';
    var outputDir =  './build/';
    var publicDir = './build/';
    
    // ************************************************************************** //
    
    var plugins = [
        //new ExtractPlugin(appName + '.css', {allChunks: true}),
        new CleanPlugin(outputDir),
        new webpack.optimize.CommonsChunkPlugin({
            name:      'main',
            children:  true,
            minChunks: 2
        })
    ];
    
    if (production) {
        plugins = plugins.concat([
            new webpack.optimize.DedupePlugin(),
            new webpack.optimize.OccurenceOrderPlugin(),
            new webpack.optimize.MinChunkSizePlugin({
                minChunkSize: 51200 // 50ko
            }),
            new webpack.optimize.UglifyJsPlugin({
                mangle:   true,
                compress: {
                    warnings: false // Suppress uglification warnings
                }
            }),
            new webpack.DefinePlugin({
                __SERVER__:      false,
                __DEVELOPMENT__: false,
                __DEVTOOLS__:    false,
                'process.env':   {
                    BABEL_ENV: JSON.stringify(process.env.NODE_ENV)
                }
            })
        ]);
    }
    
    module.exports = {
        entry:  entryPoint,
        output: {
            path:     outputDir,
            filename: appName + '.js',
            chunkFilename: '[name].js',
            publicPath: publicDir
        },
        debug:   !production,
        devtool: production ? false : 'eval',
        module: {
            loaders: [
                {
                    test: /\.js/,
                    loader: "babel",
                    include: path.resolve(__dirname, 'src'),
                    query: {
                        presets: ['es2015']
                    }
                },
                {
                    test: /\.less/,
                    //loader: ExtractPlugin.extract('style', 'css!less')
                    loader: "style!css!less"
                },
                {
                    test:   /\.html/,
                    loader: 'html'
                },
                {
                    test: /\.hbs/,
                    loader: "handlebars-template-loader"
                }
            ]
        },
        plugins: plugins,
        node: {
            fs: "empty" // Avoids Handlebars error messages
        }
    };