> For the complete documentation index, see [llms.txt](https://raozhanping.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://raozhanping.gitbook.io/notes/node/node-webpack/node-webpack-plugin.md).

# 编写插件

## 基础插件

### 编写示例

```javascript
class BasicPlugin {
    // 从构造函数中获取用户设置的配置
    constructor (options) {

    }
    // Webpack 会调用 BasicPlugin 实例的 apply 方法给插件实例传入 compiler 对象
    apply (compiler) {
        compiler.hooks.compilation.tap('BasicPlugin', compilation => {

        })
    }
}

// 导出 Plugin
module.exports = BasicPlugin
```

### 使用示例

```javascript
const BasicPlugin = require('./BasicPlugin.js')

module.exports = {
    plugins: [
        new BasicPlugin(options)
    ]
}
```

## Compiler 和 Compilation

* Compiler &#x20;

  Compiler 对象包含了 Webpack 环境所有的的配置信息，包含 options，loaders，plugins 这些信息，这个对象在 Webpack 启动时候被实例化，它是全局唯一的，可以简单地把它理解为 Webpack 实例 &#x20;
* Compilation &#x20;

  Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时，每当检测到一个文件变化，一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象 &#x20;
* 区别 &#x20;

  Compiler 代表了整个 Webpack 从启动到关闭的生命周期，而 Compilation 只是代表了一次新的编译

## 事件流

### 开发插件时注意事项

* 只要能拿到 Compiler 或 Compilation 对象，就能广播出新的事件，所以在新开发的插件中也能广播出事件，给其它插件监听使用
* 传给每个插件的 Compiler 和 Compilation 对象都是同一个引用。也就是说在一个插件中修改了 Compiler 或 Compilation 对象上的属性，会影响到后面的插件
* 有些事件是异步的，这些异步的事件会附带两个参数，第二个参数为回调函数，在插件处理完任务时需要调用回调函数通知 Webpack，才会进入下一处理流程(v3.8.1)

## 常用 API

### 读取输出资源、代码块、模块及其依赖

```javascript
class Plugin {
    apply (compiler) {
        compliler.hooks.emit('Plugin', compilation => {
            // 存放所有代码块，是一个数组
            compilation.chunks.forEach(function (chunk) {
                // chunk 代表一个代码块
                // 代码块有多个模块组成，通过 chunk.forEachModule 能读取组成代码块的每个模块
                chunk.forEachModule(module => {
                    // module 代表一个模块
                    // module.fileDependencies 存在当前模块的所有依赖的文件路径，是一个[]
                    module.fileDependenciew.forEach(filepath => {

                    })
                })
            })
        })

        // Webpack 会根据 Chunk 去生成输出的文件资源，每个 Chunk 都对应一个及其以上的输出文件
        // 例如在 Chunk 中包含了 CSS 模块并且使用了 ExtractTextPlugin 时，
        // 该 Chunk 就会生成 .js 和 .css 两个文件
        chunk.files.forEach(function (filename) {
          // compilation.assets 存放当前所有即将输出的资源
          // 调用一个输出资源的 source() 方法能获取到输出资源的内容
          let source = compilation.assets[filename].source();
        })
    }
}
```

* chunk 字段

```javascript
{
  id: 0,
  ids: [ 0 ],
  debugId: 1003,
  name: 'app',
  preventIntegration: false,
  entryModule: {
    // NormalModule
    {
      dependencies: [],
      blocks: [],
      variables: [],
      type: '',
      ...
    }
  },
  _modules: Set {
    // CssModule
    {
      dependencies: [],
      blocks: [],
      type: 'css/mini-extract'
    },
    // NormalModule
    {
      type: 'javascript/auto'
    }
    // ConcatenatedModule
    {
      type: 'javascript/esm'
    }
    ...
  },
  files: [],
  rendered: true,
  hash: 'hash 32',
  contentHash:
}
```

* module 字段

```javascript
{
  dependencies: [],
  blocks: [],
  variables: [],
  type: '',
  context: '' | null,
  debugId: 1041,
  hash: 'hash 32',
  renderedHash: 'hash 20',
  resolveOptions: {} ,
  factoryMeta: {},
  warnings: [],
  errors: [],
  buildMeta: {},
  buildInfo: {
    strict: true,
    cacheable: true,
    moduleArgument: undefined,
    exportsArgument: '__webpack_exports__',
    fileDependencies: Set {},
    contextDependencies: Set {},
    assets: undefined,
  },
  reasons: [
    {
      // ModuleReason
      module: [NormalModule],
      dependency: [],
      explanation: undefined,
      _chunks: null
    }
  ],
  _chunks: Set {
    {
      // Chunk
       id: 0,
       ids: [Array],
       debugId: 1003,
       name: 'app',
       preventIntegration: false,
       entryModule: [NormalModule],
       _modules: [SortableSet],
       filenameTemplate: undefined,
       _groups: [SortableSet],
       files: [Array],
       rendered: true,
       hash: '4be1571b7464f812b1b2e7cc00a6bc04',
       contentHash: [Object],
       renderedHash: '4be1571b7464f812b1b2',
       chunkReason: undefined,
       extraAsync: false,
       removedModules: undefined },
     _lastActiveSortFn: null,
     _sortFn: [Function: sortById],
     _cache: undefined,
     _cacheOrderIndependent: undefined
     }
  },
  id: 15,
  index: 21,
  index2: 28,
  depth: 1,
  issuer: null,
  profile: undefined,
  prefetched: false,
  built: true,
  used: true,
  usedExports: true,
  optimizationBailout: [],
  _rewriteChunkInReasons: undefined,
  useSourceMap: false,
  _source: null,
  rootModule: {
    // NormalModule
    {
      dependencies: [],
      ...
    }
  }

}
```

### 监听文件变化

```javascript
// // 当依赖的文件发生变化时会触发 watch-run 事件
compiler.hooks.watchRun.tap('Plugin', watching => {
     // 获取发生变化的文件列表
    const changedFiles = watching.compiler.watchFileSystem.watcher.mtimes
    // changedFiles 格式为键值对，键为发生变化的文件路径。
    if (changedFiles[filePath] !== undefined) {
      // filePath 对应的文件发生了变化
    }
})
```

```javascript
compiler.hooks.afterCompile.tap('Plugin', (compilation) => {
  // 把 HTML 文件添加到文件依赖列表，好让 Webpack 去监听 HTML 模块文件，在 HTML 模版文件发生变化时重新启动一次编译
  compilation.fileDependencies.push(filePath)
})
```

### 修改输出资源

```javascript
compiler.hooks.emit.tap('Plugin', (compilation) => {
  // 设置名称为 fileName 的输出资源
  compilation.assets[fileName] = {
    // 返回文件内容
    source: () => {
      // fileContent 既可以是代表文本文件的字符串，也可以是代表二进制文件的 Buffer
      return fileContent;
      },
    // 返回文件大小
      size: () => {
      return Buffer.byteLength(fileContent, 'utf8');
    }
  }

  // 读取 compilation.assets
  // 读取名称为 fileName 的输出资源
  const asset = compilation.assets[fileName]
  // 获取输出资源的内容
  asset.source()
  // 获取输出资源的文件大小
  asset.size()
})
```

### 判断 Webpack 使用了哪些插件

```javascript
// 判断当前配置使用使用了 ExtractTextPlugin，
// compiler 参数即为 Webpack 在 apply(compiler) 中传入的参数
function hasExtractTextPlugin(compiler) {
  // 当前配置所有使用的插件列表
  const plugins = compiler.options.plugins;
  // 去 plugins 中寻找有没有 ExtractTextPlugin 的实例
  return plugins.find(plugin=>plugin.__proto__.constructor === ExtractTextPlugin) != null;
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://raozhanping.gitbook.io/notes/node/node-webpack/node-webpack-plugin.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
