使用webpack从动态源输出UMD模块

时间:2018-01-05 19:42:16

标签: webpack umd

我有一个项目结构,其中包含一个文件夹,当我构建我的webpack配置并将每个文件作为输出版本中的子模块时,我想将其动态导入UMD模块。

例如,让我们说我的来源如下:

/src/snippets/red.js
/src/snippets/green.js
/src/snippets/blue.js

我希望webpack将这些源构建到一个模块中,允许我作为子模块访问每个模块,就像这样;

const red = require('snippets').red

我尝试遍历snippets目录并创建一个带有文件名和路径的对象,但是我无法确定哪个配置属性会指示webpack将它们捆绑到一个文件中并导出每个文件。这就是我到目前为止所拥有的:

const glob = require('glob')

module.exports = {
    entry: glob.sync('./src/snippets/*.js').reduce((files, file) => {
        files[path.basename(file,'.js')] = file

        return files
    }, {}),
    output: {
      path: __dirname + '/lib',
      filename: 'snippets.js',
      libraryTarget: 'umd'
    }
}

这出错:Conflict: Multiple assets emit to the same filename ./lib/snippets.js

关于如何实现我所寻找的目标的任何想法?

3 个答案:

答案 0 :(得分:3)

在生产中使用此解决方案的修改版本已有一段时间了,我没有遇到任何问题。 (我公司的设置有一个稍微复杂的过滤器,排除某些模拟模块,但在其他方面是相同的。)

我们使用构建脚本来抓取相关目录(在我们的设置中,它是src但在你的src/snippets中。对于以.js结尾的每个文件,我们导入它并重新在src/index.js中导出它。如果你需要更强大的东西,比如深入多层次,你需要修改它以递归遍历目录结构。

完成此操作后,我们将其输出到index.js,同时提醒您文件是自动生成的,以阻止人们手动将条目添加到文件中。

const fs = require("fs");
const { EOL } = require("os");
const path = require("path");

let modules = 0;
const buffer = [
  "// auto-generated file", "",
];

const emitModule = file => {
  const moduleName = file.replace(".js", "");
  modules += 1;
  buffer.push(`exports.${moduleName} = require("./snippets/${moduleName}");`);
};

const files = fs.readdirSync(__dirname + "/snippets");

files
.filter(fname => fname !== "index.js" && !fname.startsWith("."))
.forEach(f => {
  const stats = fs.statSync(path.join(__dirname, "snippets", f));
  if (stats.isFile()) {
    emitModule(f);
  }
});

fs.writeFileSync(path.join(__dirname, "index.js"), buffer.join(EOL)+EOL);

console.info(`Built 'src/index.js' with ${modules} modules`);

然后,在webpack.config.js中,我们将libraryTarget设置为umd,如下所示:

module.exports = {
  entry: path.resolve(__dirname, "src/index.js"),
  output: {
    path: path.resolve(__dirname, "build/"),
    filename: "mylib.js",
    libraryTarget: "umd"
  }
};

最后,为方便起见,在package.json中,我们使用以下命令在构建之前自动运行构建脚本(您也可以在启动webpack-dev-server之前使用它,或者运行mocha测试)。

这个设置感觉相当hacky,但它运作得很好。我遇到过的唯一问题是,偶尔会出现模块的顺序改变(可能是因为环境差异),文件的枚举会导致git中出现误报。

我已将整个软件包放在GitHub上:https://github.com/akatechis/webpack-lib-poc

更新: 如果您不想通过将构建脚本添加到package.json scripts来手动调用构建脚本,则可以始终将其包装为webpack插件。您可以阅读详细信息here

简而言之,插件只是一个带有apply方法的对象,它使用webpack编译器注册自己。来自文档:

  

作为一名聪明的JavaScript开发人员,您可能会记住Function.prototype.apply方法。由于这种方法,您可以将任何函数作为插件传递(this将指向编译器)。您可以使用此样式在配置中内联自定义插件。

以下是两种设置之间的变化:

更改webpack配置以导入构建脚本并将其添加到plugins阵列:

const path = require("path");
const buildPlugin = require("./src/index.build");

module.exports = {
  entry: path.resolve(__dirname, "src/index.js"),
  output: {
    path: path.resolve(__dirname, "build/"),
    filename: "mylib.js",
    libraryTarget: "umd"
  },
  plugins: [buildPlugin]
};

然后更改index.build.js以导出在编译器上注册回调的函数(它将其作为this接收)。从之前获取构建脚本的内容,将其放入build()函数,然后导出插件函数,如下所示:

module.exports = function () {
  this.plugin('run', function(compiler, callback) {
    console.log("Build script starting");
    build();
    callback();
  });
};

build-index中的任何脚本中删除package.json目标。 Webpack现在总是在运行之前调用您的构建脚本。

答案 1 :(得分:0)

试试这个:

我基本上以不同的方式略微更改条目,然后进一步使用NamedModulesPlugin。

module.exports = {
    entry: glob.sync('./snippets/*.js').reduce(function(entry, file){
        entry['./snippets'].push(file);
        return entry;
    }, { './snippets': []}),
    output: {
        path: path.resolve(__dirname, ''),
        filename: '[name].js'
        libraryTarget: 'umd'
    },
    module: {
        // only relevant portions shown
        plugins: [
            new webpack.NamedModulesPlugin()
        ]
    }
};

它应该有用。

答案 2 :(得分:-1)

看起来类似于conflict-multiple-assets-emit-to-the-same-filenamethis solved issue,所以它可能对您有所帮助。 我看到某处也可能是由于你的模块插件中的一个(或多个)不支持打包。