Skip to content

插件

插件通过在编译过程的不同阶段挂钩来扩展 mini-rspack 的功能。本页面记录了可用的插件以及如何创建自己的插件。

内置插件

mini-rspack 附带了一些内置插件:

EmitPlugin

EmitPlugin 生成一个包含所有资源列表的 assets.md 文件。

javascript
plugins: [
  'EmitPlugin'
]

示例插件

HtmlWebpackPlugin

HtmlWebpackPlugin 生成一个包含打包 JavaScript 文件的 HTML 文件。

javascript
plugins: [
  'HtmlWebpackPlugin'
]

BannerPlugin

BannerPlugin 在每个生成的文件顶部添加横幅。

javascript
plugins: [
  'BannerPlugin'
]

CleanPlugin

CleanPlugin 在编译前清理输出目录。

javascript
plugins: [
  'CleanPlugin'
]

创建自定义插件

您可以通过创建一个具有 apply 方法的 JavaScript 类来创建自己的插件,该方法接受一个编译器实例。

插件结构

javascript
class MyPlugin {
  constructor(options) {
    this.options = options || {};
  }

  apply(compiler) {
    // 挂钩到编译器钩子
    compiler.hooks.emit.tap('MyPlugin', (compilation) => {
      // 修改编译
      compilation.assets['my-file.txt'] = 'Generated by MyPlugin';
    });
  }
}

module.exports = MyPlugin;

可用钩子

mini-rspack 提供了几个插件可以挂钩的钩子:

  • run:在编译开始前调用
  • emit:在将资源发送到输出目录之前调用
  • done:在编译完成时调用

示例:BannerPlugin 实现

以下是 BannerPlugin 的实现示例:

javascript
class BannerPlugin {
  constructor(options) {
    if (typeof options === 'string') {
      this.banner = options;
    } else {
      this.options = {
        banner: '',
        entryOnly: false,
        ...options
      };
      this.banner = this.options.banner;
    }
  }

  apply(compiler) {
    // 挂钩到 emit 钩子
    compiler.hooks.emit.tap('BannerPlugin', (compilation) => {
      // 获取所有资源
      const assets = compilation.assets;
      
      // 遍历资源
      Object.keys(assets).forEach(filename => {
        // 跳过非 JavaScript 文件
        if (!filename.endsWith('.js')) {
          return;
        }
        
        // 如果 entryOnly 为 true 且这不是入口文件,则跳过
        if (this.options && this.options.entryOnly) {
          const isEntry = Object.keys(compilation.entries).some(entry => 
            filename.startsWith(entry) || filename === `${entry}.js`
          );
          
          if (!isEntry) {
            return;
          }
        }
        
        // 获取原始源
        const source = assets[filename];
        
        // 在顶部添加横幅
        const bannerComment = `/*!\n * ${this.banner}\n */\n`;
        
        // 用新内容替换资源
        compilation.assets[filename] = bannerComment + source;
      });
    });
  }
}

module.exports = BannerPlugin;

示例:HtmlWebpackPlugin 实现

以下是 HtmlWebpackPlugin 的实现示例:

javascript
class HtmlWebpackPlugin {
  constructor(options = {}) {
    this.options = {
      title: 'Mini Rspack App',
      template: null,
      filename: 'index.html',
      ...options
    };
  }

  apply(compiler) {
    // 挂钩到 emit 钩子
    compiler.hooks.emit.tap('HtmlWebpackPlugin', (compilation) => {
      // 生成 HTML 内容
      const html = this.generateHtml(compilation);
      
      // 将 HTML 文件添加到资源中
      compilation.assets[this.options.filename] = html;
    });
  }

  generateHtml(compilation) {
    // 如果提供了模板,则使用它
    if (this.options.template) {
      const fs = require('fs');
      let template = fs.readFileSync(this.options.template, 'utf8');
      
      // 替换占位符
      template = template.replace('<!-- title -->', this.options.title);
      
      // 为所有入口点添加脚本标签
      const scripts = Object.keys(compilation.assets)
        .filter(asset => asset.endsWith('.js'))
        .map(asset => `<script src="${asset}"></script>`)
        .join('\n    ');
      
      return template.replace('<!-- scripts -->', scripts);
    }
    
    // 否则,生成一个基本的 HTML 文件
    return `<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>${this.options.title}</title>
  </head>
  <body>
    <div id="app"></div>
    ${Object.keys(compilation.assets)
      .filter(asset => asset.endsWith('.js'))
      .map(asset => `<script src="${asset}"></script>`)
      .join('\n    ')}
  </body>
</html>`;
  }
}

module.exports = HtmlWebpackPlugin;

示例:CleanPlugin 实现

以下是 CleanPlugin 的实现示例:

javascript
class CleanPlugin {
  constructor(options = {}) {
    this.options = {
      paths: [],
      ...options
    };
  }

  apply(compiler) {
    // 挂钩到 beforeRun 钩子
    compiler.hooks.run.tap('CleanPlugin', () => {
      const fs = require('fs');
      const path = require('path');
      
      // 从编译器选项获取输出路径
      const outputPath = compiler.options.output.path;
      
      // 如果没有提供特定路径,则清理整个输出目录
      if (this.options.paths.length === 0) {
        this.cleanDirectory(outputPath);
      } else {
        // 否则,只清理指定的路径
        this.options.paths.forEach(relativePath => {
          const fullPath = path.join(outputPath, relativePath);
          this.cleanPath(fullPath);
        });
      }
    });
  }

  cleanDirectory(directory) {
    const fs = require('fs');
    const path = require('path');
    
    if (!fs.existsSync(directory)) {
      return;
    }
    
    const files = fs.readdirSync(directory);
    
    for (const file of files) {
      const fullPath = path.join(directory, file);
      this.cleanPath(fullPath);
    }
  }

  cleanPath(fullPath) {
    const fs = require('fs');
    
    if (fs.existsSync(fullPath)) {
      const stats = fs.statSync(fullPath);
      
      if (stats.isDirectory()) {
        // 递归清理子目录
        this.cleanDirectory(fullPath);
        
        // 删除目录本身
        fs.rmdirSync(fullPath);
      } else {
        // 删除文件
        fs.unlinkSync(fullPath);
      }
    }
  }
}

module.exports = CleanPlugin;

使用插件

要使用插件,请将其添加到配置中的 plugins 数组中:

javascript
const { createCompiler } = require('mini-rspack');
const compiler = createCompiler({
  // ...
  plugins: [
    'EmitPlugin',
    'HtmlWebpackPlugin',
    'BannerPlugin'
  ]
});

下一步

  • 加载器:了解可用的加载器
  • 配置:了解所有配置选项
  • 架构:了解 mini-rspack 的架构

基于 MIT 许可发布