Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make webpack's library output compatible with babel6

Babel's 6th version changes the functioning of export default and in particular its relation with commonjs require.

To summarise, while until babel5, require('module') where giving the default export of the module, it now always returns the module object containing all of the exports of the module. If one only wants the default, he/she must use require('module').default. As explained here, there is very good reasons behind this and the aim of this question is not to break or hack this behaviour.

However, if one is building a library, he/she usually does not want to distribute a module but the export value of his library (e.g. a function, whatever module system is used internally). This is well dealt with by webpack and the output.library configuration when using commonjs or AMD. Because prior babel's versions allowed the default export to be required with commonjs, babel was also compatible with this mechanism. However it is not the case anymore: the library now always provides an es6 module object.

Here is an example.

src/main.js

export default "my lib content";

webpack.config.js

var path = require("path");
var webpack = require("webpack");

module.exports = {
  entry: {
    lib: [ path.resolve(__dirname, "src/main.js") ],
  },
  output: {
    path: path.join(__dirname, "dist"),
    filename: "mylib-build.js",
    library: 'myLib'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: "babel",
        include: path.join(__dirname, "src"),
        query: { presets: ['es2015'] }
      }
    ]
  }
};

test.html

<html>
<head></head>
<body>
<script src="dist/mylib-build.js"></script>
<!-- `myLib` will be attached to `window` -->
<script>
  console.log(JSON.stringify(myLib)); // { default: "my lib content" }
</script>
</body>
</html>

This is a very simple example but I obviously want the export of mylib to be the string "my lib content" instead of { default: "my lib content" }.

One solution could be to create an export source file in commonjs to perform the transformation:

module.exports = require('./main').default;

However I find this solution quite poor. One should be able to solve it at the compilation level, without changing the source code. Any idea?

like image 408
Quentin Roy Avatar asked Nov 12 '15 18:11

Quentin Roy


People also ask

Does webpack use Babel by default?

Webpack does not contain Babel or uglify by default. These are contained within the loaders. These are seperate npm packages you need to install used in the configuration.

Does webpack use CommonJS?

Webpack supports the following module types natively: ECMAScript modules. CommonJS modules.


2 Answers

Was just going at this my self. Whether one like to call it a workaround or solution, there seem to be a Babel plugin that "solve it".

Using the plugin babel-plugin-add-module-exports as referenced in https://stackoverflow.com/a/34778391/1592572

Example config

var webpackOptions = {
    entry: {
        Lib1: './src/Lib1.js',
        Lib2: './src/Lib2.js'
    },
    output: {
        filename: "Master.[name].js",
        library: ["Master","[name]"],
        libraryTarget: "var"
    },
    module: {
        loaders: [
            {
                loader: 'babel',
                query: {
                    presets: ['es2015'],
                    plugins: ["add-module-exports"]
                }
            }
        ]
    }
};

This yields Master.Lib1 to be lib1 instead of Master.Lib1.default.

like image 131
Victor Häggqvist Avatar answered Sep 17 '22 20:09

Victor Häggqvist


Webpack 2 now supports es6 modules which partially solves this issue. Migrating from webpack 1 to webpack 2 is relatively painless. One just need to remember to disable babel's es6 module to commonjs conversion to make this work:

.babelrc

{
  "presets": [
    ["es2015", {"modules": false}]
  ]
}

However, unfortunately, it does not work properly with export default (but an issue is opened, hopefully a solution will be released eventually).

EDIT

Good news! Webpack 3 supports the output.libraryExport option that can be used to directly expose the default export:

var path = require("path");
var webpack = require("webpack");

module.exports = {
  entry: {
    lib: [ path.resolve(__dirname, "src/main.js") ],
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "mylib-build.js",
    library: "myLib",
    // Expose the default export.
    libraryExport: "default"
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: "babel",
        include: path.resolve(__dirname, "src")
      }
    ]
  }
};
like image 24
Quentin Roy Avatar answered Sep 16 '22 20:09

Quentin Roy