如何修改webpack输出的内容?

webpack真是个妖艳货色,说它是个工作流,但是分明是个打包器,但是你说它是打包器,又感觉太小看它。我在输出umd格式的最终代码时,想兼容一下cmd,就是sea.js那套,只需要在define.amd后面加上define.cmd最为判断条件即可。

首先要解决的是怎样能够在webpack的打包流程中插入一脚,能够处理自己的逻辑。其次要解决的是,怎么在这一脚中修改要输出的代码内容。

第一个问题简单,就是使用插件。

webpack的插件就是在webpack的过程流中进行处理的代码。插件形式很简单,首先创建一个function类:

function WebpackPlugin() {}

其次给这个类添加一个apply方法:

WebpackPlugin.prototype.apply = function(compiler) {
    // ....
}

最后,在apply方法中通过compiler,在流程中的某些节点上挂载一个处理函数。

WebpackPlugin.prototype.apply = function(compiler) {
    compiler.plugin("emit", function(compilation, callback) {
        // 在这里使用compilation进行处理
    })
}

这里面要你自己去详细了解三个东西:1. compiler,2. compilation,3. 和emit同一个层面的其他节点名。

上面的代码基本描绘了本文要实现目标的这个插件的大致框架。如果你想写webpack的插件,可能还需要更深入的去了解一下。

第二个问题相对复杂一些,修改要输出的代码内容就是要在文件生成之前对webpack内存中的buffer进行修改。

我的处理办法,是直接通过compilation找到要输出的文件内容,通过replace进行替换,替换完之后,又把内容塞回去。

import RawSource from "webpack/lib/RawSource"

export function WebpackSupportCmdInUmd() {}

WebpackSupportCmdInUmd.prototype.apply = function(compiler) {

 compiler.plugin("emit", (compilation, callback) => {

  if(compilation.options.output.libraryTarget !== "umd") {
   return
  }

  let outputfile = compilation.options.output.filename
  let assets = compilation.assets
  let keys = Object.keys(assets) 
 
  keys.forEach(key => {
   if(outputfile !== key || outputfile.substr(outputfile.lastIndexOf('.')) !== ".js") {
    return
   }

   let asset = assets[key]
   let content = asset.source()

   content = content.replace("typeof define === 'function' && define.amd", "typeof define === 'function' && define.amd && define.cmd")
             .replace('"function"==typeof define&&define.amd', '"function"==typeof define&&define.amd&&define.cmd')
 
   assets[key] = new RawSource(content)
  })

  callback()

 })

}

上面的代码就是我的全部代码。compilation.assets获取了所有的assets,也就是每一个要处理的文件,它的key值是以options.output.filename命名的,所以你可以看到我用outputfile去和key进行对比。

asset.source()是直接获取到要输出的文件的内容。但是这个内容还是保存在内存中的,还没有生成文件。而通过new RawSource则创建了新的asset,再塞回去,这样输出来的文件内容就变了。

2016-12-29 |