HtmlRspackPlugin

Rspack 専用

rspack.HtmlRspackPluginはRustで実装された高性能なHTMLプラグインです。RspackプロジェクトでHTMLファイルを生成するために使用できます。

new rspack.HtmlRspackPlugin(options);

比較

rspack.HtmlRspackPluginを使用する前に、rspack.HtmlRspackPluginとコミュニティのhtml-webpack-pluginとの間にいくつかの違いがあることに注意してください。

パフォーマンス

rspack.HtmlRspackPluginはRustで実装されているため、特に多くのHTMLファイルをビルドするシナリオでは、html-webpack-pluginよりもビルドパフォーマンスが大幅に向上します。

機能

rspack.HtmlRspackPluginの機能はhtml-webpack-pluginのサブセットです。プラグインのパフォーマンスを確保するために、html-webpack-pluginが提供するすべての機能を実装していません。

オプションがニーズを満たしていない場合は、コミュニティのhtml-webpack-pluginを直接使用することもできます。

警告

rspack.HtmlRspackPluginは完全なejs構文をサポートしていません。ejs構文のサブセットのみをサポートしています。完全なejs構文のサポートが必要な場合は、html-webpack-pluginを直接使用してください。html-webpack-pluginのデフォルトのテンプレート構文に合わせるため、RspackはデフォルトのEJSのエスケープとエスケープ解除をhtml-webpack-pluginのデフォルト構文と同じに変更しました。

サポートされるEJS構文

次の基本的な補間式と一部の制御文のみがサポートされています。ここで、補間式は最も基本的な文字列型のみをサポートし、任意のJavaScript式はサポートしていません。その他のEJS構文は現在サポートされていません。

<%-: エスケープされた出力

補間内のコンテンツをエスケープします

ejs
<p>Hello, <%- name %>.</p>
<p>Hello, <%- 'the Most Honorable ' + name %>.</p>
locals
{
  "name": "Rspack<y>"
}
html
<p>Hello, Rspack&lt;y&gt;.</p>
<p>Hello, the Most Honorable Rspack&lt;y&gt;.</p>

<%=: エスケープされていない出力

補間内のコンテンツをエスケープしません

ejs
<p>Hello, <%- myHtml %>.</p>
<p>Hello, <%= myHtml %>.</p>

<p>Hello, <%- myMaliciousHtml %>.</p>
<p>Hello, <%= myMaliciousHtml %>.</p>
locals
{
  "myHtml": "<strong>Rspack</strong>",
  "myMaliciousHtml": "</p><script>document.write()</script><p>"
}
html
<p>Hello, &lt;strong&gt;Rspack&lt;/strong&gt;.</p>
<p>Hello, <strong>Rspack</strong>.</p>

<p>Hello, &lt;/p&gt;&lt;script&gt;document.write()&lt;/script&gt;&lt;p&gt;.</p>
<p>Hello,</p>
<script>
  document.write();
</script>
<p>.</p>

制御文

for in文を使用してリストのトラバースを実装し、if文を使用して条件付き判定を実装します

ejs
<% for tag in htmlRspackPlugin.tags.headTags { %>
  <% if tag.tagName=="script" { %>
    <%= toHtml(tag) %>
  <% } %>
<% } %>

使用方法

このプラグインは、<script>タグを使用してすべてのJS出力をheadに含むHTMLファイルを生成します。

プラグインをRspackの設定に追加するだけです。

rspack.config.js
const rspack = require('@rspack/core');

module.exports = {
  plugins: [new rspack.HtmlRspackPlugin()],
};

これにより、以下を含むdist/index.htmlファイルが生成されます。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>rspack</title>
    <script src="main.js" defer></script>
  </head>
  <body></body>
</html>

Rspackの設定に複数のエントリポイントがある場合、それらはすべて生成されたHTMLの<script>タグに含まれます。

ビルド出力にCSSアセットがある場合、それらはHTML headの<link>タグに含まれます。

オプション

いくつかの設定オプションをrspack.HtmlRspackPluginに渡すことができます。許可されるオプションは以下のとおりです。

type HtmlRspackPluginOptions = {
  title?: string;
  filename?: string | ((entry: string) => string);
  template?: string;
  templateContent?: string | ((params: Record<string, any>) => string | Promise<string>);
  templateParameters?: Record<string, string> | (oldParams: params: Record<string, any>) => Record<string, any> | Promise<Record<string, any>>;
  inject?: 'head' | 'body' | boolean;
  publicPath?: string;
  base?: string | {
    href?: string;
    target?: '_self' | '_blank' | '_parent' | '_top'
  };
  scriptLoading?: 'blocking' | 'defer' | 'module' | 'systemjs-module';
  chunks?: string[];
  excludeChunks?: string[];
  sri?: 'sha256' | 'sha384' | 'sha512';
  minify?: boolean;
  favicon?: string;
  meta?: Record<string, string | Record<string, string>>;
  hash?: boolean;
};
  • デフォルト: {}
名前デフォルト説明
titlestring|undefinedundefined生成されたHTMLドキュメントに使用するタイトル。
filenamestring|undefined|((entry: string) => string)'index.html'HTMLを書き込むファイル。デフォルトはindex.htmlです。ここではサブディレクトリも指定できます(例:pages/index.html)。
templatestring|undefinedundefinedテンプレートファイルパス
templateContentstring|undefined|((params: Record<string, any>) => string | Promise<string>)undefinedテンプレートファイルの内容。templateよりも優先順位が高いです。関数を用いる場合、テンプレートパラメータを渡し、返された文字列をテンプレートの内容として使用します。
templateParametersRecord<string, string>|(oldParams: params: Record<string, any>) => Record<string, any> | Promise<Record<string, any>>{}テンプレートで使用されるパラメータを上書きできます。関数を用いる場合、元のテンプレートパラメータを渡し、返されたオブジェクトを最終的なテンプレートパラメータとして使用します。
inject'head' | 'body' | boolean | undefinedundefinedtemplateにおけるscriptタグとlinkタグの挿入位置。falseを指定すると挿入されません。指定しない場合、scriptLoadingに基づいて自動的に決定されます。
publicPathstring''scriptタグとlinkタグで使用されるpublicPath。
scriptLoading'blocking'|'defer'|'module'|'systemjs-module'|undefined'defer'最新のブラウザは非ブロッキングJavaScript読み込み('defer')をサポートしており、ページの起動パフォーマンスを向上させます。'module'に設定すると、属性type='module'が追加されます。これは、モジュールは自動的に遅延されるため、'defer'も意味します。
chunksstring[]|undefinedundefined一部のチャンクのみを追加できます。
excludeChunksstring[]|undefinedundefined一部のチャンクをスキップできます。
sri'sha256'|'sha384'|'sha512'|undefinedundefinedsriハッシュアルゴリズム。デフォルトで無効。
minifybooleanfalse出力を縮小するかどうかを制御します。
faviconstring|undefinedundefined指定されたfaviconパスを出力HTMLに追加します。
metaRecord<string, string|Record<string, string>>{}メタタグを挿入できます。
hashbooleanfalsetrueの場合、含まれるすべてのスクリプトとCSSファイルに一意のrspackコンパイルハッシュを追加します。これはキャッシュバストに役立ちます。
basestring|object|undefinedundefinedbaseタグを挿入します。

カスタムHTMLテンプレート

デフォルトで生成されたHTMLがニーズを満たしていない場合は、独自のテンプレートを使用できます。

テンプレートファイルを使用する

最も簡単な方法は、templateオプションを使用してカスタムHTMLファイルを渡すことです。rspack.HtmlRspackPluginは、必要なすべてのJS、CSS、faviconファイルをHTMLに自動的に挿入します。

templateを使用してHTMLテンプレートファイルを指定します。

index.html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title><%= htmlRspackPlugin.options.title %></title>
  </head>
  <body></body>
</html>
rspack.config.js
const rspack = require('@rspack/core');

module.exports = {
  plugins: [
    new rspack.HtmlRspackPlugin({
      template: 'index.html',
    }),
  ],
};

テンプレート文字列を使用する

templateContentを使用してHTMLテンプレートの内容を指定します。

rspack.config.js
const rspack = require('@rspack/core');

module.exports = {
  plugins: [
    new rspack.HtmlRspackPlugin({
      title: "My HTML Template"
      templateContent: `
        <!DOCTYPE html>
        <html>
          <head>
            <title><%= htmlRspackPlugin.options.title %></title>
          </head>
          <body></body>
        </html>
      `,
    }),
  ],
};

テンプレート関数を使用する

関数を使用してHTMLテンプレートの内容を生成します。

  • templateContentに関数を渡します。
rspack.config.js
const rspack = require('@rspack/core');

module.exports = {
  plugins: [
    new rspack.HtmlRspackPlugin({
      title: "My HTML Template"
      templateContent: ({ htmlRspackPlugin }) => `
        <!DOCTYPE html>
        <html>
          <head>
            <title>${htmlRspackPlugin.options.title}</title>
          </head>
          <body></body>
        </html>
      `,
    }),
  ],
};
  • または、template.jsまたは.cjsで終わるファイルパスを渡します。
template.js
module.exports = ({ htmlRspackPlugin }) => `
  <!DOCTYPE html>
  <html>
    <head>
      <title>${htmlRspackPlugin.options.title}</title>
    </head>
    <body></body>
  </html>
`;
rspack.config.js
const rspack = require('@rspack/core');

module.exports = {
  plugins: [
    new rspack.HtmlRspackPlugin({
      title: "My HTML Template"
      template: "template.js",
    }),
  ],
};

テンプレートパラメータ

templateParametersを使用してHTMLテンプレートレンダリングパラメータを拡張できます。デフォルトで次の変数が使用できます。

  • htmlRspackPlugin: プラグインのデータ
    • htmlRspackPlugin.options: プラグインの設定オブジェクト
    • htmlRspackPlugin.tags: テンプレートへの挿入用に準備されたタグ情報
      • htmlRspackPlugin.tags.headTags: <head>への挿入のための<base><meta><link><script>タグのリスト
      • htmlRspackPlugin.tags.bodyTags: <body>への挿入のための<script>タグのリスト
    • htmlRspackPlugin.files: このコンパイルで生成されたアセットファイル
      • htmlRspackPlugin.files.js: このコンパイルで生成されたJSアセットのパス一覧
      • htmlRspackPlugin.files.css: このコンパイルで生成されたCSSアセットのパス一覧
      • htmlRspackPlugin.files.favicon: favicon が設定されている場合、計算された最終的なfaviconアセットパスがここにあります。
      • htmlRspackPlugin.files.publicPath: アセットファイルのpublicPath
  • rspackConfig: このコンパイルで使用されたRspack設定オブジェクト
  • compilation: このコンパイルのCompilationオブジェクト
警告

テンプレートレンダリング中にタグを挿入するためにhtmlRspackPlugin.tagsを使用する場合は、injectfalseに設定してください。設定しないと、タグが2回挿入されます。

相違点

HtmlWebpackPluginとのいくつかの違いがあります。

  • !を使用してローダーを追加してテンプレートファイルを処理することはサポートされていません。
  • rspackConfigオブジェクトは現在、modeoutput.publicPathoutput.crossOriginLoadingのみサポートしています。
  • compilationオブジェクトは、現在テンプレート関数を使用する場合のみサポートされています。
  • テンプレート内でタグリスト(例:htmlRspackPlugin.tags.headTags)または単一のタグ(例:htmlRspackPlugin.tags.headTags[0])をレンダリングする場合は、HTMLコードを生成するためにtoHtml()関数が必要です。

チャンクのフィルタリング

挿入する必要があるチャンクは、次の設定で指定できます。

rspack.config.js
const rspack = require('@rspack/core');

module.exports = {
  plugins: [
    new HtmlRspackPlugin({
      chunks: ['app'],
    }),
  ],
};

特定のチャンクは、次の設定で除外することもできます。

rspack.config.js
const rspack = require('@rspack/core');

module.exports = {
  plugins: [
    new HtmlRspackPlugin({
      excludeChunks: ['app'],
    }),
  ],
};

メタタグ

metaが設定されている場合、HtmlRspackPluginは<meta>タグを挿入します。

利用可能なほぼすべてのメタタグの、よく管理されたリストをご覧ください。

次の設定でキーバリューペアを追加して<meta>タグを生成します。

rspack.config.js
const rspack = require('@rspack/core');

module.exports = {
  plugins: [
    new HtmlRspackPlugin({
      meta: {
        // Will generate: <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no',
        // Will generate: <meta name="theme-color" content="#4285f4">
        'theme-color': '#4285f4',
        // Will generate:  <meta http-equiv="Content-Security-Policy" content="default-src https:">
        'Content-Security-Policy': {
          'http-equiv': 'Content-Security-Policy',
          content: 'default-src https:',
        },
      },
    }),
  ],
};

ベースタグ

baseが設定されている場合、HtmlRspackPluginは<base>タグを挿入します。

<base>タグの詳細については、ドキュメントを参照してください。

<base>タグは、次の設定で生成できます。

rspack.config.js
new HtmlWebpackPlugin({
  // Will generate: <base href="http://example.com/some/page.html">
  base: 'http://example.com/some/page.html',
});

new HtmlWebpackPlugin({
  // Will generate: <base href="http://example.com/some/page.html" target="_blank">
  base: {
    href: 'http://example.com/some/page.html',
    target: '_blank',
  },
});

複数のHTMLファイルの生成

複数のエントリーポイントがあり、各エントリーごとにHTMLファイルを生成する場合は、複数のrspack.HtmlRspackPluginを登録できます。

  • filenameを使用して、各HTMLファイルの名前を指定します。
  • chunksを使用して、各HTMLファイルに含めるJSバンドルを指定します。

たとえば、次の設定ではfoo.htmlとbar.htmlが生成され、foo.htmlにはfoo.jsによって生成されたJSバンドルのみが含まれます。

rspack.config.js
const rspack = require('@rspack/core');

module.exports = {
  entry: {
    foo: './foo.js',
    bar: './bar.js',
  },
  plugins: [
    new rspack.HtmlRspackPlugin({
      filename: 'foo.html',
      chunks: ['foo'],
    }),
    new rspack.HtmlRspackPlugin({
      filename: 'bar.html',
      chunks: ['bar'],
    }),
  ],
};

フック

HtmlRspackPluginは、タグや生成されたHTMLコードを変更できるいくつかのフックを提供します。フックオブジェクトは、HtmlRspackPlugin.getCompilationHooksから取得できます。

rspack.config.js
const HtmlModifyPlugin = {
  apply: function (compiler) {
    compiler.hooks.compilation.tap('HtmlModifyPlugin', compilation => {
      const hooks = HtmlRspackPlugin.getCompilationHooks(compilation);
      // hooks.beforeAssetTagGeneration.tapPromise()
      // hooks.alterAssetTags.tapPromise()
      // hooks.alterAssetTagGroups.tapPromise()
      // hooks.afterTemplateExecution.tapPromise()
      // hooks.beforeEmit.tapPromise()
      // hooks.afterEmit.tapPromise()
    });
  },
};

module.exports = {
  //...
  plugins: [new HtmlRspackPlugin(), HtmlModifyPlugin],
};

beforeAssetTagGeneration

このフックは、コンパイルからアセットを収集してロードパスを生成した後、タグを生成する前に呼び出されます。

ここでassetsを変更して、カスタムJSおよびCSSアセットファイルを追加できます。

  • : AsyncSeriesWaterfallHook<[BeforeAssetTagGenerationData]>
  • パラメータ:
    type BeforeAssetTagGenerationData = {
      assets: {
        publicPath: string;
        js: Array<string>;
        css: Array<string>;
        favicon?: string;
      };
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };
警告

assets.jsassets.cssassets.faviconのみ変更できます。他の項目の変更は有効になりません。

次のコードは、追加のextra-script.jsを追加し、最終的なhtmlコンテンツに<script defer src="extra-script.js"></script>タグを生成します。

rspack.config.js
const AddScriptPlugin = {
  apply: function (compiler) {
    compiler.hooks.compilation.tap('AddScriptPlugin', compilation => {
      HtmlRspackPlugin.getCompilationHooks(
        compilation,
      ).beforeAssetTagGeneration.tapPromise('AddScriptPlugin', async data => {
        data.assets.js.push('extra-script.js');
      });
    });
  },
};

module.exports = {
  //...
  plugins: [new HtmlRspackPlugin(), AddScriptPlugin],
};

alterAssetTags

このフックは、アセットファイルに基づいてアセットタグを生成した後、タグの挿入位置を決定する前に呼び出されます。

ここでタグを調整できます。

  • : AsyncSeriesWaterfallHook<[AlterAssetTagsData]>

  • パラメータ:

    type HtmlTag = {
      tagName: string;
      attributes: Record<string, string | boolean | undefined | null>;
      voidTag: boolean;
      innerHTML?: string;
      asset?: string;
    };
    
    type AlterAssetTagsData = {
      assetTags: {
        scripts: Array<HtmlTag>;
        styles: Array<HtmlTag>;
        meta: Array<HtmlTag>;
      };
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };
警告

assetTagsのみ変更できます。他の項目の変更は有効になりません。

  • 属性値をtrueに設定すると、値のない属性が追加され、<script defer specialattribute src="main.js"></script>が生成されます。
  • 属性値をstringに設定すると、値のある属性が追加され、<script defer specialattribute="some value" src="main.js"></script>が生成されます。
  • 属性値をfalseに設定すると、属性が削除されます。

次のコードは、すべてのscriptタイプのタグにspecialAttributeプロパティを追加します。

rspack.config.js
const AddAttributePlugin = {
  apply: function (compiler) {
    compiler.hooks.compilation.tap('AddAttributePlugin', compilation => {
      HtmlRspackPlugin.getCompilationHooks(
        compilation,
      ).alterAssetTags.tapPromise('AddAttributePlugin', async data => {
        data.assetTags.scripts = data.assetTags.scripts.map(tag => {
          if (tag.tagName === 'script') {
            tag.attributes.specialAttribute = true;
          }
          return tag;
        });
      });
    });
  },
};

module.exports = {
  //...
  plugins: [new HtmlRspackPlugin(), AddAttributePlugin],
};

alterAssetTagGroups

このフックは、headbodyのタググループを生成した後、テンプレートが関数またはテンプレートエンジンによってレンダリングされる前に呼び出されます。

ここでタグの挿入位置を調整できます。

  • : AsyncSeriesWaterfallHook<[AlterAssetTagGroupsData]>
  • パラメータ:
    type AlterAssetTagGroupsData = {
      headTags: Array<HtmlTag>;
      bodyTags: Array<HtmlTag>;
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };
警告

headTagsbodyTagsのみ変更できます。他の項目の変更は有効になりません。

次のコードは、async scriptタグをbodyからheadに移動します。

rspack.config.js
const MoveTagsPlugin = {
  apply: function (compiler) {
    compiler.hooks.compilation.tap('MoveTagsPlugin', compilation => {
      HtmlWebpackPlugin.getCompilationHooks(
        compilation,
      ).alterAssetTagGroups.tapPromise('MoveTagsPlugin', async data => {
        data.headTags.push(data.headTags.bodyTags.filter(i => i.async));
        data.bodyTags = data.bodyTags.filter(i => !i.async);
      });
    });
  },
};

module.exports = {
  //...
  plugins: [
    new HtmlRspackPlugin({
      inject: 'body',
    }),
    AllHeadTagsPlugin,
  ],
};

afterTemplateExecution

このフックは、テンプレートのレンダリングが完了した後、タグが挿入される前に呼び出されます。

ここでHTMLコンテンツと挿入するタグを変更できます。

  • 関数templateContentまたは.js/.cjsで終わるtemplateを使用し、この関数を使用してテンプレートをレンダリングする場合、htmlは関数によって返される結果です。

  • 他のシナリオでは、HTMLテンプレートは内部のテンプレートエンジンによってコンパイルされ、htmlはコンパイルされた結果になります。

  • : AsyncSeriesWaterfallHook<[AfterTemplateExecutionData]>

  • パラメータ:

    type AfterTemplateExecutionData = {
      html: string;
      headTags: Array<HtmlTag>;
      bodyTags: Array<HtmlTag>;
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };

    :::warning 警告 htmlheadTagsbodyTagsのみ変更できます。他の項目の変更は有効になりません。::

次のコードは、本文の最後にInjected by pluginを追加します。その後、このテキストの後にタグが挿入されます。そのため、最終的なHTMLコンテンツでは<Injected by plugin<script defer src="main.js"></script></body>となります。

rspack.config.js
const InjectContentPlugin = {
  apply: function (compiler) {
    compiler.hooks.compilation.tap('InjectContentPlugin', compilation => {
      HtmlWebpackPlugin.getCompilationHooks(
        compilation,
      ).afterTemplateExecution.tapPromise('InjectContentPlugin', async data => {
        data.html = data.html.replace('</body>', 'Injected by plugin</body>');
      });
    });
  },
};

module.exports = {
  //...
  plugins: [
    new HtmlRspackPlugin({
      inject: 'body',
    }),
    InjectContentPlugin,
  ],
};

beforeEmit

このフックは、HTMLアセットファイルを生成する前に呼び出され、HTMLコンテンツを変更する最後の機会です。

  • : SyncHook<[BeforeEmitData]>
  • パラメータ:
    type BeforeEmitData = {
      html: string;
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };
警告

htmlのみ変更できます。他の項目の変更は有効になりません。

次のコードは、本文の最後にInjected by pluginを追加します。最終的なHTMLコンテンツでは<script defer src="main.js"></script>Injected by plugin</body>となります。

rspack.config.js
const InjectContentPlugin = {
  apply: function (compiler) {
    compiler.hooks.compilation.tap('InjectContentPlugin', compilation => {
      HtmlWebpackPlugin.getCompilationHooks(compilation).beforeEmit.tapPromise(
        'InjectContentPlugin',
        async data => {
          data.html = data.html.replace('</body>', 'Injected by plugin</body>');
        },
      );
    });
  },
};

module.exports = {
  //...
  plugins: [
    new HtmlRspackPlugin({
      inject: 'body',
    }),
    InjectContentPlugin,
  ],
};

afterEmit

このフックは、HTMLアセットファイルの生成後に呼び出され、通知のみに使用されます。

  • : SyncHook<[AfterEmitData]>
  • パラメータ:
    type AfterEmitData = {
      outputName: string;
      plugin: {
        options: HtmlRspackPluginOptions;
      };
    };