ツリーシェイキング

Rspack は、JavaScript エコシステムで広く使用されている用語であるツリーシェイキングをサポートしています。これは、使用されていないコード(一般的に「デッドコード」と呼ばれる)の削除です。デッドコードは、モジュールの特定のエクスポートが使用されず、副作用がない場合に発生し、最終的な出力サイズを削減するために、安全に削除できます。

modeproduction に設定すると、Rspack はデフォルトでツリーシェイキングに関連する一連の最適化を有効にします。

  • usedExports:モジュールエクスポートが使用されているかどうかをチェックし、使用されていないエクスポートを削除できるようにします。
  • sideEffects:モジュールの副作用を評価します。副作用のないモジュールは、再エクスポートによってさらに最適化できます。
  • providedExports:すべてのエクスポートとその再エクスポートのソースを分析します。
  • innerGraph:変数の伝達を追跡し、エクスポートが実際に使用されているかどうかを判断する精度を高めます。

これらの設定オプションがどのように機能するかを示す例を以下に示します。

情報

Rspack はデッドコードを直接削除するのではなく、使用されていないエクスポートを潜在的な「デッドコード」としてラベル付けすることに注意してください。これらのラベルは、後続の圧縮ツールによって認識され、処理されます。そのため、圧縮機能をオフにすると、実際のコード削除は観察できません。可読性を高めるために、疑似コードを使用してコード削除の効果を示す場合があります。

プロジェクトのエントリポイントとして src/main.js を仮定して、このメカニズムを例を通して理解しましょう。

src/main.js
import { foo } from './util.js';

console.log(foo);
// `bar` is not used
src/util.js
export const foo = 1;
export const bar = 2;

この例では、util.jsbar は使用されていません。production モードでは、Rspack はデフォルトで usedExports 最適化を有効にして、どのエクスポートがアクティブに使用されているかを検出します。bar のような使用されていないエクスポートは安全に削除されます。最終的な出力は次のようになります。

dist/main.js
const foo = 1;

console.log(foo);

副作用分析

production モードでは、Rspack は通常、モジュールの副作用の存在についても分析します。モジュールのすべてのエクスポートが使用されておらず、モジュールに副作用がない場合、モジュール全体を削除できます。前の例を少し変更してみましょう。

src/main.js
import { foo } from './util.js';

- console.log(foo);
// `bar` is not used

この場合、util.js のエクスポートはどれも使用されておらず、副作用がないと分析されるため、util.js 全体削除が許可されます。

package.json または module.rules を使用して、モジュールが副作用を保持するかどうかを手動で示すことができます。その方法については、sideEffects を参照してください。

再エクスポート分析

再エクスポートは開発では一般的です。しかし、モジュールは多くの他のモジュールを取り込む一方で、通常はそれらのほんの一部しか必要ありません。Rspack は、参照側が実際のエクスポートされたモジュールに直接アクセスできるようにすることで、この状況を最適化します。再エクスポートを含むこの例を考えてみましょう。

src/main.js
import { value } from './re-exports.js';
console.log(value);
src/re-exports.js
export * from './value.js';
export * from './other.js'; // this can be removed if `other.js` does not have any side effects
src/value.js
export const value = 42;
export const foo = 42; // not used

Rspack はデフォルトで providedExports を有効にします。これにより、再エクスポートモジュールからのすべてのエクスポートとその元の場所を特定できます。

src/re-exports.js に副作用がない場合、Rspack は src/main.js のインポートを src/re-exports.js から src/value.js からのインポートに直接変換し、効果的に

src/main.js
- import { value } from './re-exports.js';
+ import { value } from './value.js';
console.log(value);

このアプローチは、src/re-exports.js モジュールを完全に無視することで利点があります。

src/re-exports.js のすべての再エクスポートを分析することで、src/value.jsfoo は使用されず、最終的な出力から削除されることがわかります。

変数の伝達

場合によっては、エクスポートにアクセスしても、実際には使用されない場合があります。たとえば

src/main.js
import { foo } from './value.js';

function log() {
  console.log(foo);
} // `log` is not used

const bar = foo; // `foo` is not used

上記のシナリオでは、log 関数と変数 barfoo に依存していても、どちらも使用されていないため、foo はデッドコードと見なされ、削除できます。

innerGraph 最適化(production モードでデフォルトで有効)を有効にすると、複雑なモジュール間の状況でも、Rspack は変数の使用状況を追跡し、正確なコード最適化を実現できます。

src/main.js
import { value } from './bar.js';
console.log(value);
src/bar.js
import { foo } from './foo.js';
const bar = foo;
export const value = bar;
src/foo.js
export const foo = 42;

このコンテキストでは、value が最終的に使用されるため、それが依存する foo は保持されます。