【问题标题】:Output a UMD module from dynamic sources with webpack使用 webpack 从动态源输出 UMD 模块
【发布时间】:2018-06-15 15:49:14
【问题描述】:

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

例如,假设我的来源如下所示:

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

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

const red = require('snippets').red

我尝试遍历 sn-ps 目录并使用文件名和路径创建一个对象,但我不知道哪个配置属性会指示 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

知道如何完成我正在寻找的东西吗?

【问题讨论】:

    标签: webpack umd


    【解决方案1】:

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

    我们使用构建脚本来抓取有问题的目录(在我们的设置中,它是 src,但在您的设置中是 src/snippets。对于每个以 .js 结尾的文件,我们将其导入并以 @ 重新导出987654325@。如果你需要更健壮的东西,比如深入多层,你需要修改它以递归遍历目录结构。

    完成后,我们将其输出到 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测试)。

    这个设置感觉很老套,但效果很好。我遇到的唯一问题是,模块的顺序偶尔会发生变化(可能是因为环境差异),并且文件的枚举会导致 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();
      });
    };
    

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

    【讨论】:

    • 这最终完全符合我的要求,但是有没有办法让 webpack 执行额外的构建步骤?
    • 当然,您可以将构建脚本包装为 webpack 插件,并将其放入 plugins 配置选项中。我会用更多关于这方面的信息来更新我的答案。
    【解决方案2】:

    试试这个:

    我基本上是在稍微不同地更改条目,然后进一步使用 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()
            ]
        }
    };
    

    它应该可以工作。

    【讨论】:

    • 这似乎编译成一个模块,它在导出时只返回 sn-ps 目录中的最后一个文件。它不会将所有文件导出为子模块。
    • 你用的是什么版本的 webpack?
    • Webpack 版本 3.8.1 我的配置要点:gist.github.com/ajbdev/4b3ecef76169bd6576c878d4a1d7b7e3
    • 我们需要自己创建出口桶。要使const red = require('./snippets').red 在同一个项目中工作,您将需要src/snippets/index.js,它会创建用于从每个文件重新导出的导出桶。例如export * from './red';export * from './blue';
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-12
      • 1970-01-01
      • 2021-09-17
      • 1970-01-01
      • 1970-01-01
      • 2018-07-06
      • 2017-05-08
      • 2021-09-24
      相关资源
      最近更新 更多