Closed20

[キャッチアップ] Rspack

shingo.sasakishingo.sasaki

Introduction

https://www.rspack.dev/guide/introduction.html

  • Rspack の発音記号的にはアーレスペックに近い
  • Rust 製の高速モジュールバンドラで、 webpack エコシステムとの互換性に力を入れている

なぜ Rspack を作ったのか

元々は社内のビルドシステムの問題解決のために始まった。

多くのバンドラツールに求められる要件を調査した結果が以下だった。

  • 開発環境の起動が高速であること
  • CI/CD でのビルドが高速であること
  • 柔軟に設定可能であること
  • 本番環境用の最適化能力があること

上記を満たせるものがなかった (例えば webpack は低速ではあるが柔軟なカスタマイズがウリである)

Rspack の現状

  • 2022/04 から開発を始め、2023/03 に OSS 公開したばかりでアーリーステージ

  • Webpack 互換の機能もまだまだ不足している

  • とはいえパレートの原則に基づけば、既に多くのニーズを満たせて入る

  • 社内プロジェクトでは既に webpack から移行しているものもあり、5〜10倍の高速化も出来ている

  • Rspack は webpack のスキーマ、ローダーといったアーキテクチャと互換性を持っている

  • babel-loader lessloader sass-loader` といったおなじみのローダーをすぐに使える

  • 将来的には vue-loader といった主要なローダーをすべてサポートする

  • 現状はオンメモリのシンプルなキャッシュ機構を持っている

  • 将来的には永続キャッシュを導入して、クラウドキャッシュを再利用するなども実現可能にしたい

Rspack のこれから

Rspack は webpack との完全な互換性を目指して、コミュニティからのフィードバックをベースに開発優先順位を付けている。

− コミュニティパートナーとの協働

  • プラグインの互換性の強化
    • 既に基本的な loader インタフェースと、いくつかのプラグインの実装は完了済み
    • 100% 互換を目指すわけであはないが、基本的な開発に用いられているものについてはフィードバックを元に対応する
  • 継続的なパフォーマンスの向上
    • Rspack はパフォーマンスを最大のウリとして開発している
    • 並列化アルゴリズムやキャッシュ戦略を強化してさらなるパフォーマンスを求める
  • テストカバレッジの向上
    • 既に主要なwebpack 互換に関するテストを対応済み
    • リリース間の互換性を保つためのテストスイートも広げていく

Webpack との違い

webpack はおそらく最も普及したバンドラで、活発なエコシステム、柔軟なカスタマイズ性、そして機能性がウリである。

webpack と rspack の違いは以下の通り。

  • Rust により圧倒的なパフォーマンス
    • そもそも JS よりも速い言語を採用
  • 並列実行に特化したアーキテクチャ
    • JS では言語の制約上不可能だった、マルチコアを活用したアーキテクチャを Rust で実現している
  • 主要機能をビルトインで揃えている
    • webpack はそこについてもサードパーティに頼らざるを得なく、それがボトルネックになりやすい
  • 最適化された HMR
    • インクリメンタルコンパイル戦略によって、プロジェクトの規模に関わらないスピードを出せる

Vite との違い

Vite は開発体験としては良いが、プロダクションビルドには Rollup を使用しており、JavaScript の制約による限界がある。

esbuild との違い

esbuild は golang を用いてパフォーマンスの向上を行ったが、webpack のような機能は持ち合わせていない。

Turbopack との違い

Turbopack は Rspack と同じで Rust を採用してパフォーマンスの向上を図っているが、webpack との互換性を持たない再設計を行っているため、マイグレーションが困難である。

shingo.sasakishingo.sasaki

FAQ

https://www.rspack.dev/misc/FAQ.html

Rspack と Webpack の関係は?

  • webpack チームとはパートナーシップを結んでいる
  • Rspack の目的は webpack のパフォーマンスを Rust を用いて解決することと
  • webpack チームと協力して、さらなる webpack の可能性も探求する

Webpack ないし JavaScript エコシステムとの互換性を保つとパフォーマンス劣化しない?

  • 劣化はするが、ビジネスの到達点を考えると妥協できる劣化に過ぎない
  • ベンチマークを追いすぎてマイグレーションコストを無視しない

babel-loader を使わずに babel-loader 互換をどうやって保つ?

  • Rspack では SWC を使ってコードのトランスパイルをしている

Webpack に対して 100% 互換する?

  • しないし 100% は目指していない
  • 多くの場面で使われている主要な機能を優先して対応している

Vue をサポートする?

  • 現在作業中
  • 4月から6月 での対応を目指してる

Webpack + SWC-loader を使うのと比べるメリットは?

  • babel-loader に関わるパフォーマンスの問題は解決できるが、webpack 自体がボトルネックとして残る
  • webpack がシングルスレッドで動いている問題を Rspack ではマルチコアで対応できる

カスタムプラグインやカスタムローダは Rust で実装する?

  • webpack と同様に、JavaScript を用いて実装する
  • Rust を使って実装できる方法も模索している

React Server Component をサポートする?

  • 現在のところは予定はない
  • コミュニティの動向次第

プロダクションで利用できる?

  • できる
  • 社内のいくつかのプロジェクトでは既に利用済み
  • ランタイムターゲットは概ね webpack と揃えてはいるが、まだ完全ではない
shingo.sasakishingo.sasaki

Glossary

https://www.rspack.dev/guide/glossary.html

Rspack と webpack でよく使われる用語集

asset

  • アプリケーション内で利用される、画像や動画、フォントなどのリソースのこと
  • base64に変換されコード内にインラインで展開されたり、出力ディレクトリに配置されたりする

asset module

  • 画像、動画、フォントなどのアセットをモジュールとして扱ったもの

bundle

  • 従来はソースコードを一つのファイルに束ねたものをそう呼んでいた
  • 現代ではバンドルは小さなファイル(チャンク) に分解され、オンデマンドで読み込めるようになっているため一つとは限らない

bundle splitting

  • ソースコードを分割統合し、複数のバンドルにするテクニック
  • 並列リクエストやブラウザキャッシュの効率化に役立つ

chunk

  • 複数のモジュールを一つのグループに束ねたファイル

chunk graph

  • チャンク同士の関連を表現したデータ構造
  • グラフは有向グラフっで、チャンク間の依存関係を示す

code splitting

  • コードを複数のチャンクに分割するテクニック
  • アプリケーションを動かすのに必要な最低限のコードのみ最初に読み込み、以降は必要に応じてチャンクを取得する

first class module type

  • RSpack において、追加のローダーやプラグインなしで最初から扱えるモジュールの種類
  • JS, CSS, JSON などがファーストクラスモジュール

loader

  • モジュールを変換することに特化したプラグインのようなもの
  • TypeScript を JavaScript に変換するのもローダー
  • CSSモジュールを JavaScript モジュールにするのもローダー

module

  • import / export を通じてコードの再利用をできるようにした仕組み

module type

  • そのモジュールをバンドラがどのように扱うかで分類された種別
  • 例えばモジュールが CSS であれば、バンドラは CSS パーサを使用する

module resolution

  • import のようなモジュール指定しによって示されるファイルパスを計算するプロセス

module graph

  • モジュール間の依存関係を表現したデータ構造
  • グラフは有向グラフっで、チャンク間の依存関係を示す

NAPI_RS

  • Rustでコンパイル済みのNode.jsアドオンを構築するためのフレームワーク

plugin

  • Rspack の機能を拡張するプログラム
  • 各プロセスにフックして任意のプロセスを追加できる

tree shaking

  • バンドルから未使用コードを除外するテクニック
  • Rspack では自動でデッドコードを検出して削除する
shingo.sasakishingo.sasaki

Language support

https://www.rspack.dev/guide/language-support.html

TypeScript

  • TS はビルトインでサポートされている
  • トランスパイルには SWC を使用している
    • よって webpack から移行する場合は、 babel-loader ts-loader の設定は除外できる
    • 型チェックは行わないので、必要に応じて tsc などを組み合わせる必要がある
  • 並列実行を最大限に活かすために、 isolatedModules を設定する必要がある
    • よって enum や namespace を使用することはできない

JSX and TSX

  • JSX/TSX はビルトインでサポートされている
  • Node に対するポリフィルは自動で挿入しないので、手動で@rspack/plugin-node-polyfill を設定する

Static resource handling

  • 画像、動画、フォントなどの静的リソースのバンドルもビルトインでサポートしている
  • 以下は .png を静的リソースとしてバンドルするための設定
module.exports = {
  module: {
    rules: [
      {
        test: /\.png$/,
        type: 'asset',
      },
    ],
  },
};

CSS

  • CSS はビルトインでサポートされている
    • webpack から移行する場合、 css-loader style-loader は不要になる
  • .css ファイルは CSS モジュールタイプとして扱われ、 .module.css は CSS Modules モジュールタイプとして扱われる
  • ややこし!
  • 前者は css-loader に当たるものが適用され、後者は CSS Modules による JavaScript モジュールとなる
  • postcss-loader 実装済み
  • less-loader 実装済み
  • sass-loader 実装済み
  • Tailwind CSS は postcss と組み合わせて利用可能

JSON

  • JSONはビルトインでサポートされている
shingo.sasakishingo.sasaki

Asset modules

画像や動画、フォントといった静的ファイルをモジュールとして扱うアセットモジュールは、Rspack ではびつロインでサポートされており、追加のローダーなどを必要とせずにそのまま利用することができる。

https://www.rspack.dev/guide/asset-module.html

Supported Asset Module types

アセットモジュールはいくつかのモジュールタイプに分類できる

  • asset/inline: アセットを Base64 エンコードした DataURI 形式に変換してエクスポートする
  • asset/resource: アセットを単体のファイルとして扱い、ファイルのURLとしてエクスポートする
  • asset: サイズに応じて asset/inline asset/resource のいずれかになる
    • デフォルトでは8KBまでは asset/inline になる
  • asset/source: アセットファイルの内容をそのまま文字列としてエクスポートする
shingo.sasakishingo.sasaki

Loader

ローダーはファイルの変換に特化したプラグインのようなもので、Rspack では多くのローダーがビルトインで提供される。

https://www.rspack.dev/guide/loader.html

  • 例えば less-loader などもすぐに使える
  • 複数のローダーを組み合わせるといった使い方も、webpack と同じように設定を書ける
  • カスタムローダーを JavaScript で簡単に実装できる

以下はバナーをコンテントの先頭に付与するローダーの実装

const BANNER = `/**
 * MIT Licensed
 * Copyright (c) 2022-present ByteDance, Inc. and its affiliates.
 * https://github.com/web-infra-dev/rspack/blob/main/LICENSE
 */`;

module.exports = function (content) {
  return `${BANNER}\n${content}`;
};

ローダーモジュールを取り込んで、すべてのJSファイルに適用できる

rspack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: require.resolve('./banner-loader.js'),
      },
    ],
  },
};
shingo.sasakishingo.sasaki

Plugins

プラグインはローダーと違い、ビルドプロセス全体に対する拡張を行える。

https://www.rspack.dev/guide/plugin.html

プラグインは JavaScript で記述し、Rust のプロセス内で実行される。実装する場合は apply メソッドを持つクラスを作成し、apply メソッドではコンパイラインスタンスに対して任意の操作ができる。

const PLUGIN_NAME = 'MyPlugin';

class MyPlugin {
  apply(compiler) {
    compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
      console.log('The Rspack build process is starting!');
    });
  }
}

module.exports = MyPlugin;

commonjs 形式で公開されたクラスを、設定ファイルから読み込めばOK

rspack.config.js
const BundleAnalyzerPlugin =
  require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [new BundleAnalyzerPlugin()],
};
shingo.sasakishingo.sasaki

Module resolution

モジュール解決は、 import 文などに現れるモジュール識別子から、実際のファイルパスを解決するプロセスのこと。

Rspack では Node における解決アルゴリズムを拡張した nodejs_resolver を使用している。

https://www.rspack.dev/guide/module-resolution.html

モジュール解決は概ね以下の3パターンサポートされている

絶対パス

import '/home/me/file';

既に参照可能なファイルパスになっているのでそのままでOK

相対パス

import './src/answer';

そのファイルが属しているディレクトリからみた相対パスとして解決される

モジュールパス

import 'lodash';

./ / などが使用されず、パッケージ名のみ指定したケース。 Node.js のアルゴリズムに基づいて、 node_modules ディレクトリなどから解決される

shingo.sasakishingo.sasaki

Optimization: Production

https://www.rspack.dev/guide/production.html

ソースマップ

  • ソースマップ機能を有しており、デバッグのためにもプロダクションでの有効化を推奨している
  • プロジェクトが大きく、ソースマップ生成のパフォーマンスが気になる場合は、 source-map オプションで調整することが可能

ミニフィケーション

  • ビルトインで JS や CSS をミニファイする機能を有している
  • もちろん無効化や設定変更も可能
shingo.sasakishingo.sasaki

Optimization: Code splitting

コード分割は、コードをいくつかのチャンクに分割することで、初期読み込みされるリソースを削減し、パフォーマンスを向上させる機能。

分割には3つの方法がある

  • Entry Points
  • Prevent Duplication
  • Dynamic imports

https://www.rspack.dev/guide/code-splitting.html

Entry point

entry オプションにて、手動でバンドル生成のエントリーファイルを複数指定する手法。

rspack.config.js
/**
 * @type {import('@rspack/core').Configuration}
 */
const config = {
  mode: 'development',
  entry: {
    index: './src/index.js',
    another: './src/another-module.js',
  },
  output: {
    filename: '[name].bundle.js',
  },
};

module.exports = config;

上記では少なくとも indexanother 2つのバンドルを生成している。

この手法では直感的に分割できるが、以下の問題が発生しやすい。

  • index another それぞれで共通のモジュールへの依存があっても、それぞれで依存解決を行い、同じコードが両バンドルに含まれてしまう
  • 手動設定なので柔軟性に欠け、コアロジックを動的に分割する手法が使えなくなる

SplitChunksPlugin

SplitChunksPlugin は、エントリー間で共通するモジュールを抜き出して新たにチャンクを生成するプラグイン。

Dynamic import

import() シンタックスを利用した、ESM による動的インポート機能を用いてインポートすることで、依存モジュールをチャンクとして切り出すことができる。

index.js
import('./shared')
console.log('index.js')

この場合、 sharedindex のバンドルに含まれずに、非同期で読み込めるようになる。

shingo.sasakishingo.sasaki

Optimization: Tree shaking

https://www.rspack.dev/guide/tree-shaking.html

  • ツリーシェイキングは、コード内の到達不能コード(デッドコード) を検出してバンドルから除外してくれる機能
  • ESM による import/export での依存グラフを用いて、モジュール内の不使用関数などを除外できる
  • ツリーシェイキングは modeproduction の場合に自動で有効化される
  • モジュールが副作用を持っている可能性がある場合、パッケージの sideEffects オプションに応じて、副作用がツリーシェイキングに影響するかを判断する
shingo.sasakishingo.sasaki

Framework support: Vue

Vue の完全なサポートを提供予定(≒今は不完全)

https://www.rspack.dev/guide/vue.html

vue-loader

vue-loader の完全なサポートを目指しているが、現状 Rspack が提供する API の制限の都合まだ出来ていない。

代わりに vue-loader 相当の機能を自分で実装することは可能
https://github.com/web-infra-dev/rspack/blob/main/examples/vue/vue-loader.js

Vue 3 JSX

@vue/babel-plugin-jsx を直接使用することはできるので、JSX シンタックスは扱える

shingo.sasakishingo.sasaki

Migration from webpack

Rspack は Webpack との高い互換性を目指してい入るが、実装の違いによりいくつかのマイグレーションプロセスが必要になる

https://www.rspack.dev/guide/migrate-from-webpack.html

設定ファイルのマイグレーション

設定ファイルにも互換性があったりなかったりするので、フィールドごとの互換性を確認し、書き換える必要がある。

https://www.rspack.dev/guide/config-diff.html

ローダーの置き換え

  • babel-loader は多くの場合不要に
    • コードダウングレードはビルトインでサポート
    • TypeScript/JSX もビルトインでサポート
    • 他の理由で babel-loader を使用している場合は継続的に利用可能
  • css-loader style-loader mini-css-extract-plugin も不要に
  • file-loader url-loader も不要
    • (webpack 5 では既に不要になってる)

プラグインの置き換え

  • html-webpack-plugin はビルトイン機能に移動
このスクラップは2023/03/13にクローズされました