Сделать вывод библиотеки webpack совместимым с babel6

Babel 6-я версия изменяет работу export default и, в частности, ее связь с commonjs require.

Подводя итог, пока до babel5, require('module'), где предоставляется экспорт по умолчанию модуля, он теперь всегда возвращает объект модуля, содержащий все экспортные данные модуля. Если требуется только по умолчанию, он должен использовать require('module').default. Как объясняется здесь, для этого есть очень веские причины, и цель этого вопроса заключается не в том, чтобы сломать или взломать это поведение.

Однако, если вы создаете библиотеку, он обычно не хочет распространять модуль, а значение экспорта его библиотеки (например, функция, независимо от того, какая система модулей используется внутри). Это хорошо рассмотрено webpack и конфигурацией output.library при использовании commonjs или AMD. Поскольку предыдущие версии babel разрешали экспорт по умолчанию с помощью commonjs, babel также был совместим с этим механизмом. Однако это уже не так: теперь библиотека всегда предоставляет объект модуля es6.

Вот пример.

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>

Это очень простой пример, но я, очевидно, хочу, чтобы экспорт mylib был строкой "my lib content" вместо { default: "my lib content" }.

Одним из решений может быть создание исходного файла экспорта в commonjs для выполнения преобразования:

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

Однако я нахожу это решение довольно плохим. Нужно уметь решать его на уровне компиляции без изменения исходного кода. Любая идея?

Ответы

Ответ 1

Webpack 2 теперь поддерживает модули es6, которые частично решают эту проблему. Миграция из webpack 1 в webpack 2 относительно безболезненна. Нужно просто запомнить отключить модуль babel es6 для преобразования commonjs, чтобы сделать эту работу:

.babelrc

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

Однако, к сожалению, он не работает должным образом с экспортом по умолчанию (но проблема открыта, надеюсь, что решение будет выпущено в конце концов).

ИЗМЕНИТЬ

Хорошие новости! Webpack 3 поддерживает параметр output.libraryExport, который может использоваться для прямого экспорта экспорта по умолчанию:

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")
      }
    ]
  }
};

Ответ 2

Я просто занимался этим. Можно ли назвать это обходным решением или решением, похоже, есть плагин Babel, который "решает его".

Используя плагин babel-plugin-add-module-exports, как указано в fooobar.com/questions/357721/...

Пример конфигурации

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"]
                }
            }
        ]
    }
};

Это дает Master.Lib1 как lib1 вместо Master.Lib1.default.

Ответ 3

Вы можете использовать это решение (это больше похоже на обходной путь, но это позволяет вам не изменять ваши источники):

Существует загрузчик callback-loader. Это позволяет вам изменять источники в течение времени сборки, вызывая обратный вызов и вместо этого ставьте результат. Другими словами, вы можете превратить все require('module') в require('module').default автоматически во время сборки.

Вот ваш конфиг для этого:

var webpackConfig = {
    module: {
        loaders: [
            { test: /\.js$/, exclude: /node_modules/, loader: 'callback' },
            ...
        ]
    },
    ...
    callbackLoader: {
        require: function() {
            return 'require("' + Array.prototype.join.call(arguments, ',') + '").default';
        }
    }
};