Exposing a default variable to the browser when building a JS library with Webpack

Viewed 168

I have a JS class that I'd like to build in to a library with Webpack.

class MyCoolComponent {

  constructor(a, b, c) {
  }

  render() {
    // Do stuff
  }

}

export default MyCoolComponent;

The ultimate goal is to have end users source my script (with a <script> tag) and have the MyCoolComponent variable exposed as a variable globally in their browser.

According to the Webpack docs, I can expose the variable by configuring library and libraryTarget in my Webpack config file:

output: {
  // ...
  library: 'MyCoolComponent',
  libraryTarget: 'var',
  // ...
},

When I bundled the script, sourced it on the page, and tried accessing MyCoolComponent in my browser console, I received the following error:

var mcc = new MyCoolComponent('a', 'b', 'c');
=> TypeError: MyCoolComponent is not a constructor

I noticed that MyCoolComponent is actually an object with a default key, and it works if I access that key directly:

var mcc = new MyCoolComponent.default('a', 'b', 'c');
=> (No Error)
  1. Why does this occur? I specified export default MyCoolComponent in the JS file, so shouldn't the class be the default thing exported?

  2. How can I configure the export so that I don't have to access it with .default()?

Thanks!

EDIT - Webpack Config

var DEMO_BUILD_DIR = path.resolve(__dirname, 'docs');
var DEMO_SRC_DIR = path.resolve(__dirname, 'demo');
var DIST_DIR = path.resolve(__dirname, 'dist');
var SRC_DIR = path.resolve(__dirname, 'src');

var config = {
  entry: SRC_DIR + '/components/MyCoolComponent/MyCoolComponent.js',
  output: {
    path: DIST_DIR,
    filename: 'index.js',
    library: 'MyCoolComponent',
    libraryTarget: 'var'
  },
  externals: {},
  plugins: [
    new MiniCssExtractPlugin()
  ],
  devtool: 'source-map',
  mode: process.env.NODE_ENV,
  resolve: {
    extensions: ['.js', '.sass', '.scss', '.css'],
    modules: [
      SRC_DIR,
      'node_modules'
    ]
  },
  resolveLoader: {
    modules: ['node_modules']
  },
  module: {
    strictExportPresence: true,
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: { hmr: process.env.NODE_ENV == 'development' },
          },
          'css-loader',
        ],
      },
      {
        test: /\.(sass|scss)$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: { hmr: process.env.NODE_ENV == 'development' },
          },
          'css-loader',
          'sass-loader'
        ],
      },
      {
        test: /\.(js)$/,
        exclude: /node_modules/,
        use: [
          'babel-loader'
        ]
      }
    ]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
};
1 Answers

Looks like Webpack supports this case by specifying a libraryExport option

libraryExport: 'default' - The default export of your entry point will be assigned to the library target:

// if your entry has a default export of `MyDefaultModule`
var MyDefaultModule = _entry_return_.default;

So I just need to add the option and it worked great:

 output: {
   // ...
   library: 'MyCoolComponent',
   libraryTarget: 'var',
+  libraryExport: 'default'
   // ...
 },
Related