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

And*_*ird 11 webpack umd

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

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

/src/snippets/red.js
/src/snippets/green.js
/src/snippets/blue.js
Run Code Online (Sandbox Code Playgroud)

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

const red = require('snippets').red
Run Code Online (Sandbox Code Playgroud)

我尝试遍历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'
    }
}
Run Code Online (Sandbox Code Playgroud)

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

关于如何实现我正在寻找的东西的任何想法?

Ale*_*his 3

Been using a modified version of this solution in production for a while now, and I have not had any issues with it. (My company's set up has a slightly more elaborate filter, to exclude certain mock modules, but is otherwise the same).

We use a build script to crawl the directory in question (in our setup, it's src but in yours its src/snippets. For each file that ends in .js, we import it and re-export it in src/index.js. If you need something more robust, like going multiple levels deep, you'll need to modify this to recursively traverse the directory structure.

When this is done, we output it to index.js, along with a reminder that the file is auto-generated to dissuade people from manually adding entries to the file.

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`);
Run Code Online (Sandbox Code Playgroud)

Then, in webpack.config.js, we set the libraryTarget to be umd as follows:

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

Finally, for convenience, in package.json, we use the following to automatically run the build script before building (you can also use it before starting webpack-dev-server, or running mocha tests).

This set up feels rather hacky, but it works pretty well. The only issue I've ever come up against is that occasionally, the order of the modules will be changed around (presumably, because of environment differences), and the enumeration of the files causes a false positive in git.

I've put the whole package up on GitHub here: https://github.com/akatechis/webpack-lib-poc


Update: If you don't want to manually call the build script by adding it to your package.json scripts, you can always wrap it as a webpack plugin. You can read details here

In short, a plugin is just an object with an apply method that registers itself with the webpack compiler. From the docs:

As a clever JavaScript developer you may remember the Function.prototype.apply method. Because of this method you can pass any function as plugin (this will point to the compiler). You can use this style to inline custom plugins in your configuration.

Here are the changes between the two setups:

Change webpack config to import your build script and add it to the plugins array:

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]
};
Run Code Online (Sandbox Code Playgroud)

Then change index.build.js to export a function that registers a callback on the compiler (it receives it as this). Take the contents of the build script from before, put it into a build() function, and then export a plugin function as follows:

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

Remove the build-index target from any scripts in package.json. Webpack will now always call your build script before running.