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

13

我有一个项目结构,其中包含一个文件夹,我想在构建Webpack配置时动态导入到UMD模块中,并使每个文件成为输出构建中的子模块。

例如,假设我的源文件如下:

/src/snippets/red.js
/src/snippets/green.js
/src/snippets/blue.js
我希望使用webpack将这些源代码构建为一个单独的模块,使我可以像子模块一样访问每个源代码。
const red = require('snippets').red

我试图迭代遍历片段目录并创建一个带有文件名和路径的对象,但我无法弄清楚哪个配置属性将指示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个回答

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 插件进行包装。你可以在这里阅读详细信息:https://webpack.js.org/concepts/plugins/

简而言之,插件只是一个具有 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();
  });
};

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


这个最终确实实现了我的需求,但有没有办法让 webpack 执行额外的构建步骤? - Andy Baird
当然可以,你可以将构建脚本封装为一个webpack插件,并将其放入“plugins”配置选项中。我会在我的答案中提供更多信息。 - Alexandros Katechis

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

它应该可以工作。


这似乎编译成一个模块,该模块仅将代码片段目录中的最后一个文件作为其导出返回。它不会将所有文件作为子模块导出。 - Andy Baird
你使用的webpack版本是什么? - bhantol
Webpack版本3.8.1,我的配置要点:https://gist.github.com/ajbdev/4b3ecef76169bd6576c878d4a1d7b7e3 - Andy Baird
我们需要自己创建出口桶。为了让 const red = require('./snippets').red 在同一项目中工作,您需要 src/snippets/index.js,它将为每个文件创建出口桶以进行重新导出。例如:export * from './red';export * from './blue'; - bhantol
这是我所说的示例:https://github.com/yogeshgadge/webpack-folder-barrel.git。 - bhantol

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接