コンテンツにスキップ

プラグイン開発

プラグインの概要

Rollupプラグインは、以下に説明するプロパティビルドフック、および出力生成フックのうち1つ以上を持ち、規約に従うオブジェクトです。プラグインは、プラグイン固有のオプションを指定して呼び出すことができ、そのようなオブジェクトを返す関数をエクスポートするパッケージとして配布する必要があります。

プラグインを使用すると、バンドル前のコードのトランスパイルや、node_modulesフォルダー内のサードパーティ製モジュールの検索など、Rollupの動作をカスタマイズできます。プラグインの使用方法については、プラグインの使用を参照してください。

プラグインのリストはgithub.com/rollup/awesomeにあります。プラグインに関する提案がある場合は、プルリクエストを送信してください。

簡単な例

次のプラグインは、ファイルシステムにアクセスすることなく、virtual-moduleのインポートをインターセプトします。これは、たとえば、ブラウザでRollupを使用する場合に必要です。例に示すように、エントリポイントを置き換えるために使用することもできます。

js
// rollup-plugin-my-example.js
export default function myExample () {
  return {
    name: 'my-example', // this name will show up in logs and errors
    resolveId ( source ) {
      if (source === 'virtual-module') {
        // this signals that Rollup should not ask other plugins or check
        // the file system to find this id
        return source;
      }
      return null; // other ids should be handled as usually
    },
    load ( id ) {
      if (id === 'virtual-module') {
        // the source code for "virtual-module"
        return 'export default "This is virtual!"';
      }
      return null; // other ids should be handled as usually
    }
  };
}

// rollup.config.js
import myExample from './rollup-plugin-my-example.js';
export default ({
  input: 'virtual-module', // resolved by our plugin
  plugins: [myExample()],
  output: [{
    file: 'bundle.js',
    format: 'es'
  }]
});

規約

  • プラグインには、rollup-plugin-のプレフィックスが付いた明確な名前が必要です。
  • package.jsonrollup-pluginキーワードを含めます。
  • プラグインはテストする必要があります。Promiseをすぐにサポートするmochaまたはavaをお勧めします。
  • 可能な場合は、非同期メソッドを使用してください。例:fs.readFileSyncの代わりにfs.readFile
  • プラグインを英語でドキュメント化してください。
  • プラグインが適切な場合は、正しいソースマッピングを出力するようにしてください。
  • プラグインが「仮想モジュール」(例:ヘルパー関数用)を使用する場合は、モジュールIDの先頭に\0を付けます。これにより、他のプラグインがそれを処理しようとするのを防ぎます。

プロパティ

name

タイプstring

エラーメッセージやログで使用するプラグインの名前。

version

タイプstring

プラグイン間の通信シナリオで使用するプラグインのバージョン。

ビルドフック

ビルドプロセスと対話するために、プラグインオブジェクトには「フック」が含まれています。フックは、ビルドのさまざまな段階で呼び出される関数です。フックは、ビルドの実行方法に影響を与えたり、ビルドに関する情報を提供したり、完了したビルドを変更したりできます。さまざまな種類のフックがあります

  • async:フックは、同じタイプの値に解決されるPromiseを返すこともできます。それ以外の場合、フックはsyncとしてマークされます。
  • first:複数のプラグインがこのフックを実装している場合、フックは、フックがnullまたはundefined以外の値を返すまで順番に実行されます。
  • sequential:複数のプラグインがこのフックを実装している場合、それらはすべて指定されたプラグインの順序で実行されます。フックがasyncの場合、この種類の後続のフックは、現在のフックが解決されるまで待機します。
  • parallel:複数のプラグインがこのフックを実装している場合、それらはすべて指定されたプラグインの順序で実行されます。フックがasyncの場合、この種類の後続のフックは並行して実行され、現在のフックを待機しません。

関数の代わりに、フックをオブジェクトにすることもできます。その場合、実際のフック関数(またはbanner/footer/intro/outroの値)は、handlerとして指定する必要があります。これにより、フックの実行を変更する追加のオプションプロパティを提供できます。

  • order:「pre」|「post」| null
    このフックを実装しているプラグインが複数ある場合は、このプラグインを最初("pre")、最後("post")、またはユーザーが指定した位置(値なしまたはnull)で実行します。

    js
    export default function resolveFirst() {
    	return {
    		name: 'resolve-first',
    		resolveId: {
    			order: 'pre',
    			handler(source) {
    				if (source === 'external') {
    					return { id: source, external: true };
    				}
    				return null;
    			}
    		}
    	};
    }

    複数のプラグインが"pre"または"post"を使用している場合、Rollupはそれらをユーザーが指定した順序で実行します。このオプションは、すべてのプラグインフックに使用できます。並列フックの場合、フックの同期部分が実行される順序が変わります。

  • sequential:boolean
    他のプラグインの同じフックと並行してこのフックを実行しないでください。parallelフックにのみ使用できます。このオプションを使用すると、Rollupは以前のすべてのプラグインの結果を待ってから、プラグインフックを実行し、残りのプラグインを再び並行して実行します。たとえば、同じ並列フックを実装するプラグインABCDEがあり、中央のプラグインCsequential: trueがある場合、Rollupは最初にA + Bを並行して実行し、次にCを単独で実行し、次にD + Eを並行して実行します。

    これは、互いに依存する異なるwriteBundleフックで複数のコマンドラインツールを実行する必要がある場合に役立ちます(可能であれば、高速で、純粋なインメモリビルドで動作し、他のインメモリビルドプラグインがファイルを確認できるようにする、順次generateBundleフックでファイルを追加/削除することをお勧めします)。このオプションをorderと組み合わせて、追加の並べ替えを行うことができます。

    js
    import { resolve } from 'node:path';
    import { readdir } from 'node:fs/promises';
    
    export default function getFilesOnDisk() {
    	return {
    		name: 'getFilesOnDisk',
    		writeBundle: {
    			sequential: true,
    			order: 'post',
    			async handler({ dir }) {
    				const topLevelFiles = await readdir(resolve(dir));
    				console.log(topLevelFiles);
    			}
    		}
    	};
    }

ビルドフックは、rollup.rollup(inputOptions)によってトリガーされるビルドフェーズ中に実行されます。これらは主に、Rollupによって処理される前に入力ファイルを特定、提供、変換することに関係しています。ビルドフェーズの最初のフックはoptionsであり、最後のフックは常にbuildEndです。ビルドエラーが発生した場合、closeBundleがその後呼び出されます。

並列
順次
最初
非同期
同期

each entry
external
non-external
not cached
no imports
cached
false
true
each import()
non-external
each import
(cached)
each import
(not cached)
external
unresolved
watchChange
closeWatcher
buildEnd
buildStart
load
moduleParsed
options
resolveDynamicImport
resolveId
shouldTransformCachedModule
transform

さらに、ウォッチモードでは、watchChangeフックをいつでもトリガーして、現在の実行が出力を生成したら新しい実行がトリガーされることを通知できます。また、ウォッチャーが閉じると、closeWatcherフックがトリガーされます。

生成された出力を変更するための出力生成フェーズ中に実行されるフックについては、出力生成フックを参照してください。

buildEnd

タイプ(error?: Error) => void
種類非同期、並列
moduleParsedresolveId、またはresolveDynamicImport
これはビルドフェーズの最後のフックであるため、出力生成フェーズのoutputOptions

Rollupがバンドルを完了したが、generateまたはwriteが呼び出される前に呼び出されます。Promiseを返すこともできます。ビルド中にエラーが発生した場合、このフックに渡されます。

buildStart

タイプ(options: InputOptions) => void
種類非同期、並列
options
並列で各エントリポイントを解決するためのresolveId

rollup.rollupビルドで呼び出されます。これは、すべてのoptionsフックによる変換を考慮し、設定されていないオプションの正しいデフォルト値も含まれているため、rollup.rollup()に渡されたオプションへのアクセスが必要な場合に使用することをお勧めします。

closeWatcher

タイプ() => void
種類非同期、並列
前/次このフックは、ビルドフェーズと出力生成フェーズの両方でいつでもトリガーできます。その場合、現在のビルドは続行されますが、新しいwatchChangeイベントはトリガーされなくなります。

ウォッチャープロセスが閉じられるときに、プラグインに通知し、開いているリソースもすべて閉じられるようにします。Promiseが返された場合、RollupはPromiseが解決されるまでプロセスを閉じずに待機します。このフックは、出力プラグインでは使用できません。

load

タイプ(id: string) => LoadResult
種類async, first
resolveId または resolveDynamicImport でロードされたIDが解決された場所。さらに、このフックは、プラグインフックから this.load を呼び出して、IDに対応するモジュールをプリロードすることにより、いつでもトリガーできます。
キャッシュが使用されなかった場合、または同じ code を持つキャッシュされたコピーが存在しない場合は、ロードされたファイルを変換する transform。それ以外の場合は、shouldTransformCachedModule
typescript
type LoadResult = string | null | SourceDescription;

interface SourceDescription {
	code: string;
	map?: string | SourceMap;
	ast?: ESTree.Program;
	attributes?: { [key: string]: string } | null;
	meta?: { [plugin: string]: any } | null;
	moduleSideEffects?: boolean | 'no-treeshake' | null;
	syntheticNamedExports?: boolean | string | null;
}

カスタムローダーを定義します。null を返すと、他の load 関数(および最終的にはファイルシステムからのロードのデフォルトの動作)に処理が委ねられます。たとえば、このフックがすでに this.parse を使用して何らかの理由でASTを生成した場合などの追加の解析オーバーヘッドを防ぐために、このフックはオプションで { code, ast, map } オブジェクトを返すことができます。ast は、各ノードに start プロパティと end プロパティを持つ標準のESTree ASTである必要があります。変換がコードを移動しない場合は、mapnull に設定して既存のソースマップを保持できます。それ以外の場合は、ソースマップを生成する必要がある場合があります。ソースコード変換のセクションを参照してください。

moduleSideEffects に対して false が返され、他のモジュールがこのモジュールから何もインポートしない場合、モジュールに副作用があったとしても、このモジュールはバンドルに含まれません。true が返された場合、Rollupは、グローバル変数またはエクスポートされた変数の変更など、副作用のあるモジュール内のすべてのステートメントを含めるためのデフォルトのアルゴリズムを使用します。"no-treeshake" が返された場合、このモジュールではツリーシェイクが無効になり、空の場合でも、生成されたチャンクのいずれかに含まれます。null が返された場合、またはフラグが省略された場合、moduleSideEffects は、このモジュールを解決した最初の resolveId フック、treeshake.moduleSideEffects オプション、または最終的にはデフォルトで true になります。transform フックはこれをオーバーライドできます。

attributes には、このモジュールがインポートされたときに使用されたインポート属性が含まれています。現時点では、バンドルされたモジュールのレンダリングには影響しませんが、ドキュメントの目的に役立ちます。null が返された場合、またはフラグが省略された場合、attributes は、このモジュールを解決した最初の resolveId フック、またはこのモジュールの最初のインポートに存在する属性によって決定されます。transform フックはこれをオーバーライドできます。

syntheticNamedExports オプションの効果については、合成名前付きエクスポートを参照してください。null が返された場合、またはフラグが省略された場合、syntheticNamedExports は、このモジュールを解決した最初の resolveId フック、または最終的にはデフォルトで false になります。transform フックはこれをオーバーライドできます。

meta オプションの使用方法については、カスタムモジュールメタデータを参照してください。このフックによって meta オブジェクトが返された場合、resolveIdフックによって返された meta オブジェクトと浅くマージされます。フックが meta オブジェクトを返さない場合、空のオブジェクトがデフォルトになります。transform フックは、このオブジェクトのプロパティを追加または置換することもできます。

このフック内で attributesmetamoduleSideEffects、および syntheticNamedExports の以前の値を確認するには、this.getModuleInfo を使用できます。

moduleParsed

タイプ(moduleInfo: ModuleInfo) => void
種類非同期、並列
現在処理中のファイルが変換された場所の transform
存在する場合は、すべての検出された静的インポートと動的インポートを並行して解決するための resolveId および resolveDynamicImport、それ以外の場合は buildEnd

このフックは、モジュールがRollupによって完全に解析されるたびに呼び出されます。このフックに渡される情報については、this.getModuleInfo を参照してください。

transform フックとは対照的に、このフックはキャッシュされず、キャッシュされたモジュールとその他のモジュールの両方に関する情報を取得するために使用できます。これには、meta プロパティ、code、および ast の最終的な形状が含まれます。

このフックは、moduleInfo.importedIdsmoduleInfo.dynamicallyImportedIdsmoduleInfo.importedIdResolutions、および moduleInfo.dynamicallyImportedIdResolutions の情報が完全かつ正確になるように、すべてのインポートが解決されるまで待機します。ただし、インポート側のモジュールに関する情報は、後で追加のインポーターが検出される可能性があるため、不完全である可能性があることに注意してください。この情報が必要な場合は、buildEnd フックを使用してください。

onLog

タイプ(level: LogLevel, log: RollupLog) => boolean | null
種類sync, sequential
前/次このフックは、いつでもトリガーできます。

使用可能な Loglevel 値と RollupLog タイプについては、onLog オプションを参照してください。

Rollupおよびプラグインによって生成されたログと警告を、onLog オプションに渡すか、コンソールに出力する前に受信およびフィルター処理する関数。

このフックから false が返された場合、ログはフィルター処理されます。それ以外の場合、ログは次のプラグインの onLog フック、onLog オプションに渡されるか、コンソールに出力されます。プラグインは、ログのログレベルを変更したり、ログを this.errorthis.warnthis.info、または this.debug に渡して false を返すことで、ログをエラーに変えることもできます。ログにプラグイン名などを追加する他のプラグインフックとは異なり、これらの関数はログのプロパティを追加または変更しないことに注意してください。さらに、onLog フックによって生成されたログは、同じプラグインの onLog フックに返されません。別のプラグインが独自の onLog フックでそのようなログに応じてログを生成した場合、このログも元の onLog フックに渡されません。

js
function plugin1() {
	return {
		name: 'plugin1',
		buildStart() {
			this.info({ message: 'Hey', pluginCode: 'SPECIAL_CODE' });
		},
		onLog(level, log) {
			if (log.plugin === 'plugin1' && log.pluginCode === 'SPECIAL_CODE') {
				// We turn logs into warnings based on their code. This warnings
				// will not be passed back to the same plugin to avoid an
				// infinite loop, but other plugins will still receive it.
				this.warn(log);
				return false;
			}
		}
	};
}

function plugin2() {
	return {
		name: 'plugin2',
		onLog(level, log) {
			if (log.plugin === 'plugin1' && log.pluginCode === 'SPECIAL_CODE') {
				// You can modify logs in this hooks as well
				log.meta = 'processed by plugin 2';
				// This turns the log back to "info". If this happens in
				// response to the first plugin, it will not be passed back to
				// either plugin to avoid an infinite loop. If both plugins are
				// active, the log will be an info log if the second plugin is
				// placed after the first one
				this.info(log);
				return false;
			}
		}
	};
}

options フックと同様に、このフックはRollupが完全に構成される前に実行される可能性があるため、ほとんどの プラグインコンテキストユーティリティ関数にアクセスできません。サポートされているプロパティは、this.meta と、ログとエラー用の this.errorthis.warnthis.info、および this.debug のみです。

options

タイプ(options: InputOptions) => InputOptions | null
種類async, sequential
これはビルドフェーズの最初のフックです
buildStart

rollup.rollup に渡される options オブジェクトを置換または操作します。null を返しても何も置換されません。オプションを読み取るだけの場合は、すべての options フックからの変換が考慮された後のオプションにアクセスできる buildStart フックを使用することをお勧めします。

onLog フックと同様に、このフックはRollupが完全に構成される前に実行されるため、ほとんどの プラグインコンテキストユーティリティ関数にアクセスできません。サポートされているプロパティは、this.meta と、ログとエラー用の this.errorthis.warnthis.info、および this.debug のみです。

resolveDynamicImport

タイプResolveDynamicImportHook
種類async, first
インポートファイル用の moduleParsed
フックがまだロードされていないIDで解決した場合の load、動的インポートに文字列が含まれていて、フックで解決されなかった場合の resolveId、それ以外の場合は buildEnd
typescript
type ResolveDynamicImportHook = (
	specifier: string | AstNode,
	importer: string,
	options: { attributes: Record<string, string> }
) => ResolveIdResult;

ヒント

戻り値の型であるResolveIdResultは、resolveId フックと同じです。

動的インポート用のカスタムリゾルバーを定義します。false を返すと、インポートはそのまま保持され、他のリゾルバーに渡されず、外部になることを示します。resolveId フックと同様に、オブジェクトを返してインポートを別のIDに解決しながら、同時に外部としてマークすることもできます。

attributes は、インポートにどのインポート属性が存在したかを通知します。つまり、import("foo", {assert: {type: "json"}}) は、attributes: {type: "json"} を渡します。

動的インポートに文字列が引数として渡された場合、このフックから返された文字列は既存のモジュールIDとして解釈され、null を返すと他のリゾルバーに処理が委ねられ、最終的には resolveId に委ねられます。

動的インポートに文字列が引数として渡されない場合、このフックは、分析のために生のASTノードにアクセスし、次の点でわずかに異なる動作をします

  • すべてのプラグインが null を返す場合、インポートは警告なしに external として扱われます。
  • 文字列が返された場合、この文字列はモジュールIDとして解釈されるのではなく、インポート引数の置換として使用されます。生成されたコードが有効であることを確認するのはプラグインの責任です。
  • このようなインポートを既存のモジュールに解決するには、オブジェクト {id, external} を返すこともできます。

このフックの戻り値は、後で resolveId に渡されないことに注意してください。静的解決アルゴリズムにアクセスする必要がある場合は、プラグインコンテキストで this.resolve(source, importer) を使用できます。

resolveId

タイプResolveIdHook
種類async, first
エントリポイントを解決している場合は buildStart、インポートを解決している場合は moduleParsed、または resolveDynamicImport のフォールバックとして。さらに、このフックは、this.emitFile を呼び出してエントリポイントを発行するか、this.resolve を呼び出して手動でIDを解決することにより、ビルドフェーズ中にプラグインフックからトリガーできます。
解決されたIDがまだロードされていない場合はload、それ以外の場合はbuildEnd
typescript
type ResolveIdHook = (
	source: string,
	importer: string | undefined,
	options: {
		attributes: Record<string, string>;
		custom?: { [plugin: string]: any };
		isEntry: boolean;
	}
) => ResolveIdResult;

type ResolveIdResult = string | null | false | PartialResolvedId;

interface PartialResolvedId {
	id: string;
	external?: boolean | 'absolute' | 'relative';
	attributes?: Record<string, string> | null;
	meta?: { [plugin: string]: any } | null;
	moduleSideEffects?: boolean | 'no-treeshake' | null;
	resolvedBy?: string | null;
	syntheticNamedExports?: boolean | string | null;
}

カスタムリゾルバーを定義します。リゾルバーは、例えば、サードパーティの依存関係を特定するのに役立ちます。ここで、sourceは、インポート文に書かれているとおりのインポート対象です。つまり、

js
import { foo } from '../bar.js';

の場合、sourceは"../bar.js"になります。

importerは、インポートしているモジュールの完全に解決されたIDです。エントリポイントを解決するとき、importerは通常undefinedになります。例外は、this.emitFileを介して生成されたエントリポイントで、ここではimporter引数を指定できます。

これらの場合、isEntryオプションは、ユーザー定義のエントリポイント、エミットされたチャンク、またはthis.resolveコンテキスト関数にisEntryパラメータが指定されたかどうかを教えてくれます。

これは、例えば、エントリポイントに対してカスタムプロキシモジュールを定義するメカニズムとして使用できます。次のプラグインは、すべてのエントリポイントをプロキシして、ポリフィルインポートを注入します。

js
// We prefix the polyfill id with \0 to tell other plugins not to try to load or
// transform it
const POLYFILL_ID = '\0polyfill';
const PROXY_SUFFIX = '?inject-polyfill-proxy';

function injectPolyfillPlugin() {
	return {
		name: 'inject-polyfill',
		async resolveId(source, importer, options) {
			if (source === POLYFILL_ID) {
				// It is important that side effects are always respected
				// for polyfills, otherwise using
				// "treeshake.moduleSideEffects: false" may prevent the
				// polyfill from being included.
				return { id: POLYFILL_ID, moduleSideEffects: true };
			}
			if (options.isEntry) {
				// Determine what the actual entry would have been.
				const resolution = await this.resolve(source, importer, options);
				// If it cannot be resolved or is external, just return it
				// so that Rollup can display an error
				if (!resolution || resolution.external) return resolution;
				// In the load hook of the proxy, we need to know if the
				// entry has a default export. There, however, we no longer
				// have the full "resolution" object that may contain
				// meta-data from other plugins that is only added on first
				// load. Therefore we trigger loading here.
				const moduleInfo = await this.load(resolution);
				// We need to make sure side effects in the original entry
				// point are respected even for
				// treeshake.moduleSideEffects: false. "moduleSideEffects"
				// is a writable property on ModuleInfo.
				moduleInfo.moduleSideEffects = true;
				// It is important that the new entry does not start with
				// \0 and has the same directory as the original one to not
				// mess up relative external import generation. Also
				// keeping the name and just adding a "?query" to the end
				// ensures that preserveModules will generate the original
				// entry name for this entry.
				return `${resolution.id}${PROXY_SUFFIX}`;
			}
			return null;
		},
		load(id) {
			if (id === POLYFILL_ID) {
				// Replace with actual polyfill
				return "console.log('polyfill');";
			}
			if (id.endsWith(PROXY_SUFFIX)) {
				const entryId = id.slice(0, -PROXY_SUFFIX.length);
				// We know ModuleInfo.hasDefaultExport is reliable because
				// we awaited this.load in resolveId
				const { hasDefaultExport } = this.getModuleInfo(entryId);
				let code =
					`import ${JSON.stringify(POLYFILL_ID)};` +
					`export * from ${JSON.stringify(entryId)};`;
				// Namespace reexports do not reexport default, so we need
				// special handling here
				if (hasDefaultExport) {
					code += `export { default } from ${JSON.stringify(entryId)};`;
				}
				return code;
			}
			return null;
		}
	};
}

attributesは、インポートに存在していたインポート属性を示します。例えば、import "foo" assert {type: "json"}は、attributes: {type: "json"}を渡します。

nullを返すと、他のresolveId関数と最終的なデフォルトの解決動作に委ねられます。falseを返すと、sourceは外部モジュールとして扱われ、バンドルに含めないことを示します。これが相対インポートで発生した場合、IDはexternalオプションが使用された場合と同じ方法で正規化されます。

オブジェクトを返す場合、インポートを異なるIDに解決しながら、同時にバンドルから除外することができます。これにより、ユーザーがexternalオプションを介して手動で「外部」とマークする必要なく、依存関係を外部依存関係に置き換えることができます。

js
function externalizeDependencyPlugin() {
	return {
		name: 'externalize-dependency',
		resolveId(source) {
			if (source === 'my-dependency') {
				return { id: 'my-dependency-develop', external: true };
			}
			return null;
		}
	};
}

externaltrueの場合、絶対IDは、ユーザーがmakeAbsoluteExternalsRelativeオプションで選択した内容に基づいて相対IDに変換されます。この選択は、絶対IDを常に相対IDに変換する場合はexternal: "relative"を、絶対IDのままにする場合はexternal: "absolute"を渡すことで上書きできます。オブジェクトを返す場合、相対外部ID、つまり./または../で始まるIDは、内部で絶対IDに変換されて出力で相対IDに変換されることはありません。代わりに、変更されずに出力に含まれます。代わりに相対IDを再正規化して重複排除する場合は、idとして絶対ファイルシステムロケーションを返し、external: "relative"を選択してください。

モジュールIDを解決する最初のフックでmoduleSideEffectsに対してfalseが返され、他のモジュールがこのモジュールから何もインポートしていない場合、このモジュールに副作用があったとしても、このモジュールは含まれません。trueが返された場合、Rollupはデフォルトのアルゴリズムを使用して、副作用のあるモジュール内のすべてのステートメント(グローバル変数やエクスポートされた変数の変更など)を含めます。"no-treeshake"が返された場合、このモジュールのツリーシェイキングが無効になり、空の場合でも生成されたチャンクの1つに含まれます。nullが返された場合、またはフラグが省略された場合、moduleSideEffectstreeshake.moduleSideEffectsオプションによって決定されるか、デフォルトでtrueになります。loadおよびtransformフックはこれを上書きできます。

resolvedByは、返されたオブジェクトで明示的に宣言できます。これは、this.resolveによって返される対応するフィールドを置き換えます。

外部モジュールのattributesに値を返す場合、これは"es"出力を生成するときにこのモジュールのインポートをどのようにレンダリングするかを決定します。例えば、{id: "foo", external: true, attributes: {type: "json"}}の場合、このモジュールのインポートはimport "foo" assert {type: "json"}として表示されます。値を渡さない場合、attributes入力パラメータの値が使用されます。属性を削除するには、空のオブジェクトを渡します。attributesはバンドルされたモジュールのレンダリングには影響しませんが、モジュールのすべてのインポート間で一貫性がある必要があり、そうでない場合は警告が発行されます。loadおよびtransformフックはこれを上書きできます。

syntheticNamedExportsオプションの効果については、合成名前付きエクスポートを参照してください。nullが返された場合、またはフラグが省略された場合、syntheticNamedExportsはデフォルトでfalseになります。loadおよびtransformフックはこれを上書きできます。

metaオプションの使用方法については、カスタムモジュールメタデータを参照してください。nullが返された場合、またはオプションが省略された場合、metaはデフォルトで空のオブジェクトになります。loadおよびtransformフックは、このオブジェクトのプロパティを追加または置き換えることができます。

resolveIdはモジュールの各インポートに対して呼び出されるため、同じidを何度も解決できますが、externalattributesmetamoduleSideEffects、またはsyntheticNamedExportsの値は、モジュールがロードされる前に一度だけ設定できることに注意してください。その理由は、この呼び出しの後、Rollupは、これらの値を上書きする可能性があり、そうする場合は優先されるべきモジュールのloadおよびtransformフックを続行するためです。

this.resolveを介してプラグインからこのフックをトリガーする場合、カスタムオプションオブジェクトをこのフックに渡すことができます。このオブジェクトは変更されずに渡されますが、プラグインは、オプションが意図されているプラグインの名前に対応するキーを持つオブジェクトを使用して、customプロパティを追加する規則に従う必要があります。詳細については、カスタムリゾルバーオプションを参照してください。

ウォッチモードの場合、またはキャッシュを明示的に使用している場合、キャッシュされたモジュールの解決済みインポートもキャッシュから取得され、resolveIdフックを介して再度決定されることはありません。これを防ぐには、そのモジュールに対してshouldTransformCachedModuleフックからtrueを返すことができます。これにより、モジュールとそのインポート解決がキャッシュから削除され、transformresolveIdが再度呼び出されます。

shouldTransformCachedModule

タイプShouldTransformCachedModuleHook
種類async, first
load。キャッシュされたファイルが、キャッシュされたバージョンとコードを比較するためにロードされた場所
プラグインがtrueを返さない場合はmoduleParsed、それ以外の場合はtransform
typescript
type ShouldTransformCachedModuleHook = (options: {
	ast: AstNode;
	code: string;
	id: string;
	meta: { [plugin: string]: any };
	moduleSideEffects: boolean | 'no-treeshake';
	syntheticNamedExports: boolean | string;
}) => boolean | NullValue;

Rollupキャッシュが使用されている場合(例えば、ウォッチモードまたはJavaScript APIを介して明示的に)、Rollupはtransformフックの後、ロードされたcodeがキャッシュされたコピーのコードと同一の場合、モジュールのtransformフックをスキップします。これを防ぐには、キャッシュされたコピーを破棄し、代わりにモジュールを変換するために、プラグインはこのフックを実装してtrueを返すことができます。

このフックを使用して、どのモジュールがキャッシュされたか、およびキャッシュされたメタ情報にアクセスすることもできます。

プラグインがブール値を返さない場合、Rollupはこのフックを他のプラグインに対してトリガーします。それ以外の場合は、残りのすべてのプラグインがスキップされます。

transform

タイプ(code: string, id: string) => TransformResult
種類async, sequential
現在処理中のファイルがロードされた場所のload。キャッシュが使用されていて、そのモジュールのキャッシュされたコピーがある場合は、shouldTransformCachedModule(プラグインがそのフックに対してtrueを返した場合)
ファイルが処理および解析された後のmoduleParsed
typescript
type TransformResult = string | null | Partial<SourceDescription>;

interface SourceDescription {
	code: string;
	map?: string | SourceMap;
	ast?: ESTree.Program;
	attributes?: { [key: string]: string } | null;
	meta?: { [plugin: string]: any } | null;
	moduleSideEffects?: boolean | 'no-treeshake' | null;
	syntheticNamedExports?: boolean | string | null;
}

個々のモジュールを変換するために使用できます。例えば、このフックが何らかの理由でthis.parseを使用してASTを生成した場合の追加の解析オーバーヘッドを防ぐために、このフックはオプションで{ code, ast, map }オブジェクトを返すことができます。astは、各ノードのstartプロパティとendプロパティを持つ標準のESTree ASTである必要があります。変換でコードが移動しない場合は、mapnullに設定することで既存のソースマップを保持できます。それ以外の場合は、ソースマップを生成する必要がある場合があります。ソースコード変換に関するセクションを参照してください。

ウォッチモードの場合、またはキャッシュを明示的に使用している場合、このフックの結果は再構築時にキャッシュされ、モジュールのcodeが変更された場合、またはこのモジュールに対して最後にフックがトリガーされたときにthis.addWatchFileを介して追加されたファイルが変更された場合にのみ、フックがモジュールidに対して再度トリガーされることに注意してください。

他のすべての場合、shouldTransformCachedModuleフックが代わりにトリガーされ、キャッシュされたモジュールへのアクセスが提供されます。shouldTransformCachedModuleからtrueを返すと、モジュールがキャッシュから削除され、代わりにtransformが再度呼び出されます。

戻り値のオブジェクト形式を使用して、モジュールの追加プロパティを構成することもできます。プロパティのみを返し、コード変換をしないことも可能です。

moduleSideEffectsに対してfalseが返され、他のモジュールがこのモジュールから何もインポートしていない場合、このモジュールに副作用があったとしても、このモジュールは含まれません。

trueが返された場合、Rollupはデフォルトのアルゴリズムを使用して、副作用のあるモジュール内のすべてのステートメント(グローバル変数やエクスポートされた変数の変更など)を含めます。

"no-treeshake"が返された場合、このモジュールのツリーシェイキングが無効になり、空の場合でも生成されたチャンクの1つに含まれます。

nullが返された場合、またはフラグが省略された場合、moduleSideEffectsは、このモジュールをロードしたloadフック、このモジュールを解決した最初のresolveIdフック、treeshake.moduleSideEffectsオプションによって決定されるか、最終的にデフォルトでtrueになります。

attributesには、このモジュールがインポートされたときに使用されたインポート属性が含まれています。現時点では、バンドルされたモジュールのレンダリングには影響しませんが、ドキュメントの目的を果たします。nullが返された場合、またはフラグが省略された場合、attributesは、このモジュールをロードしたloadフック、このモジュールを解決した最初のresolveIdフック、またはこのモジュールの最初のインポートに存在する属性によって決定されます。

syntheticNamedExportsオプションの効果については、合成名前付きエクスポートを参照してください。nullが返された場合、またはフラグが省略された場合、syntheticNamedExportsは、このモジュールをロードしたloadフック、このモジュールを解決した最初のresolveIdフック、treeshake.moduleSideEffectsオプションによって決定されるか、最終的にデフォルトでfalseになります。

metaオプションの使用方法については、カスタムモジュールメタデータを参照してください。nullが返された場合、またはオプションが省略された場合、metaは、このモジュールをロードしたloadフック、このモジュールを解決した最初のresolveIdフック、または最終的には空のオブジェクトによって決定されます。

このフック内で attributesmetamoduleSideEffects、および syntheticNamedExports の以前の値を確認するには、this.getModuleInfo を使用できます。

watchChange

タイプwatchChange: (id: string, change: {event: 'create' | 'update' | 'delete'}) => void
種類非同期、並列
前/次このフックは、ビルド中および出力生成フェーズの両方でいつでもトリガーできます。その場合、現在のビルドは引き続き進行しますが、現在のビルドが完了すると新しいビルドがスケジュールされ、optionsから再び開始されます。

Rollupが--watchモードで監視対象ファイルの変更を検出するたびに、プラグインに通知します。Promiseが返された場合、RollupはPromiseが解決されるまで待機してから、別のビルドをスケジュールします。このフックは、出力プラグインでは使用できません。2番目の引数には、変更イベントの詳細が含まれています。

出力生成フック

出力生成フックは、生成されたバンドルに関する情報を提供し、完了したビルドを変更できます。これらはビルドフックと同じように機能し、同じ型を持ちますが、bundle.generate(outputOptions)またはbundle.write(outputOptions)の各呼び出しに対して個別に呼び出されます。出力生成フックのみを使用するプラグインは、出力オプションを介して渡すこともできるため、特定の出力に対してのみ実行されます。

出力生成フェーズの最初のフックはoutputOptionsで、最後のフックは、出力がbundle.generate(...)で正常に生成された場合はgenerateBundle、出力がbundle.write(...)で正常に生成された場合はwriteBundle、または出力生成中にエラーが発生した場合はrenderErrorです。

並列
順次
最初
非同期
同期

each chunk
each chunk
next chunk
each import()
each import.meta.*
import.meta.url
other
next chunk
banner
footer
intro
outro
renderDynamicImport
resolveFileUrl
resolveImportMeta
augmentChunkHash
closeBundle
generateBundle
outputOptions
renderChunk
renderError
renderStart
writeBundle

さらに、closeBundleは最後のフックとして呼び出すことができますが、これをトリガーするには、ユーザーが手動でbundle.close()を呼び出す必要があります。CLIは、これが常に当てはまるようにします。

augmentChunkHash

タイプ(chunkInfo: ChunkInfo) => string
種類sync, sequential
renderChunk
まだ処理する必要のある他のチャンクがある場合はrenderChunk、それ以外の場合はgenerateBundle

個々のチャンクのハッシュを拡張するために使用できます。Rollupの出力チャンクごとに呼び出されます。偽の値を返すと、ハッシュは変更されません。真の値をhash.updateに渡します。chunkInfoは、generateBundleの縮小版であり、codemapがなく、ファイル名のハッシュにプレースホルダーを使用しています。

次のプラグインは、現在のタイムスタンプでチャンクfooのハッシュを無効にします。

js
function augmentWithDatePlugin() {
	return {
		name: 'augment-with-date',
		augmentChunkHash(chunkInfo) {
			if (chunkInfo.name === 'foo') {
				return Date.now().toString();
			}
		}
	};
}
タイプstring | ((chunk: ChunkInfo) => string)
種類async, sequential
import.meta.ROLLUP_FILE_URL_referenceIdの各使用にはresolveFileUrl、現在のチャンクでimport.metaへの他のすべてのアクセスにはresolveImportMeta
次のチャンクに動的インポート式がある場合は、各動的インポート式にrenderDynamicImport、それ以外の場合は最初のチャンクにrenderChunk

output.banner/output.footerを参照してください。

closeBundle

タイプcloseBundle: () => Promise<void> | void
種類非同期、並列
ビルドエラーがあった場合はbuildEnd、それ以外の場合はbundle.close()が呼び出されたとき。この場合、これがトリガーされる最後のフックになります。

実行中の可能性のある外部サービスをクリーンアップするために使用できます。RollupのCLIは、このフックが各実行後に呼び出されるようにしますが、バンドルの生成が完了したら、手動でbundle.close()を呼び出すのはJavaScript APIのユーザーの責任です。そのため、この機能に依存するプラグインは、ドキュメントでこのことを慎重に言及する必要があります。

プラグインがウォッチモードでビルド間でリソースを保持する場合は、このフックでthis.meta.watchModeを確認し、closeWatcherでウォッチモードに必要なクリーンアップを実行できます。

タイプstring | ((chunk: ChunkInfo) => string)
種類async, sequential
import.meta.ROLLUP_FILE_URL_referenceIdの各使用にはresolveFileUrl、現在のチャンクでimport.metaへの他のすべてのアクセスにはresolveImportMeta
次のチャンクに動的インポート式がある場合は、各動的インポート式にrenderDynamicImport、それ以外の場合は最初のチャンクにrenderChunk

output.banner/output.footerを参照してください。

generateBundle

タイプ(options: OutputOptions, bundle: { [fileName: string]: OutputAsset | OutputChunk }, isWrite: boolean) => void
種類async, sequential
augmentChunkHash
出力がbundle.write(...)で生成された場合はwriteBundle、それ以外の場合は、これが出力生成フェーズの最後のフックであり、別の出力が生成された場合はoutputOptionsが続く場合があります。
typescript
interface OutputAsset {
	fileName: string;
	name?: string;
	needsCodeReference: boolean;
	source: string | Uint8Array;
	type: 'asset';
}

interface OutputChunk {
	code: string;
	dynamicImports: string[];
	exports: string[];
	facadeModuleId: string | null;
	fileName: string;
	implicitlyLoadedBefore: string[];
	imports: string[];
	importedBindings: { [imported: string]: string[] };
	isDynamicEntry: boolean;
	isEntry: boolean;
	isImplicitEntry: boolean;
	map: SourceMap | null;
	modules: {
		[id: string]: {
			renderedExports: string[];
			removedExports: string[];
			renderedLength: number;
			originalLength: number;
			code: string | null;
		};
	};
	moduleIds: string[];
	name: string;
	preliminaryFileName: string;
	referencedFiles: string[];
	sourcemapFileName: string | null;
	type: 'chunk';
}

bundle.generate()の最後に、またはbundle.write()でファイルが書き込まれる直前に呼び出されます。ファイルが書き込まれた後にファイルを変更するには、writeBundleフックを使用します。bundleは、書き込みまたは生成されるファイルの完全なリストを詳細とともに提供します。

このフックでバンドルオブジェクトから削除することで、ファイルが発行されないようにすることができます。追加のファイルを発行するには、this.emitFileプラグインコンテキスト関数を使用します。

危険

バンドルにアセットを直接追加しないでください。これは、Rollupがアセットを追跡するために持つ内部メカニズムを回避します。また、アセットがRollupが内部的に依存する重要なプロパティを見逃す可能性があり、プラグインがマイナーなRollupリリースで破損する可能性があります。

代わりに、常にthis.emitFileを使用してください。

intro

タイプstring | ((chunk: ChunkInfo) => string)
種類async, sequential
import.meta.ROLLUP_FILE_URL_referenceIdの各使用にはresolveFileUrl、現在のチャンクでimport.metaへの他のすべてのアクセスにはresolveImportMeta
次のチャンクに動的インポート式がある場合は、各動的インポート式にrenderDynamicImport、それ以外の場合は最初のチャンクにrenderChunk

output.intro/output.outroを参照してください。

outputOptions

タイプ(outputOptions: OutputOptions) => OutputOptions | null
種類sync, sequential
これが最初に出力を生成した場合はbuildEnd、それ以外の場合は、以前に生成された出力に応じてgenerateBundlewriteBundle、またはrenderError。これは、出力生成フェーズの最初のフックです。
renderStart

bundle.generate()またはbundle.write()に渡される出力オプションオブジェクトを置き換えるか、操作します。nullを返しても何も置き換えません。出力オプションを読み取るだけの場合は、すべてのoutputOptionsフックからの変換が考慮された後に出力オプションにアクセスできるため、renderStartフックを使用することをお勧めします。

outro

タイプstring | ((chunk: ChunkInfo) => string)
種類async, sequential
import.meta.ROLLUP_FILE_URL_referenceIdの各使用にはresolveFileUrl、現在のチャンクでimport.metaへの他のすべてのアクセスにはresolveImportMeta
次のチャンクに動的インポート式がある場合は、各動的インポート式にrenderDynamicImport、それ以外の場合は最初のチャンクにrenderChunk

output.intro/output.outroを参照してください。

renderChunk

タイプRenderChunkHook
種類async, sequential
最後のチャンクのbannerfooterintrooutro
augmentChunkHash
typescript
type RenderChunkHook = (
	code: string,
	chunk: RenderedChunk,
	options: NormalizedOutputOptions,
	meta: { chunks: Record<string, RenderedChunk> }
) => { code: string; map?: SourceMapInput } | string | null;

個々のチャンクを変換するために使用できます。Rollupの出力チャンクファイルごとに呼び出されます。nullを返すと、変換は適用されません。このフックでコードを変更し、ソースマップをサポートする場合は、変更を記述するmapを返す必要があります。詳細は、ソースコード変換に関するセクションを参照してください。

chunkには、generateBundleフックと同じChunkInfo型を使用するチャンクに関する追加情報が含まれています。ただし、次の違いがあります。

  • codemapは設定されていません。代わりに、このフックのcodeパラメータを使用してください。
  • ハッシュを含むすべての参照されるチャンクファイル名には、代わりにハッシュプレースホルダーが含まれます。これには、fileNameimportsimportedBindingsdynamicImports、およびimplicitlyLoadedBeforeが含まれます。このフックから返されたコードでこのようなプレースホルダーファイル名またはその一部を使用すると、RollupはgenerateBundleの前に、プレースホルダーを実際のハッシュで置き換えます。これにより、ハッシュは参照されるすべてのファイルハッシュを含む、最終的に生成されたチャンクの実際のコンテンツを反映するようにします。

chunkは可変であり、このフックで適用された変更は、他のプラグインおよび生成されたバンドルに伝播します。つまり、このフックでインポートまたはエクスポートを追加または削除する場合は、importsimportedBindings、および/またはexportsを更新する必要があります。

meta.chunksには、Rollupが生成するすべてのチャンクに関する情報が含まれており、ハッシュのプレースホルダーを使用して、それらのChunkInfoにアクセスできます。つまり、このフックでチャンクグラフ全体を探索できます。

renderDynamicImport

タイプrenderDynamicImportHook
種類同期、最初
これが最初のチャンクの場合はrenderStart、それ以外の場合は前のチャンクのbannerfooterintrooutro
import.meta.ROLLUP_FILE_URL_referenceIdの各使用にはresolveFileUrl、現在のチャンクでimport.metaへの他のすべてのアクセスにはresolveImportMeta
typescript
type renderDynamicImportHook = (options: {
	customResolution: string | null;
	format: string;
	moduleId: string;
	targetModuleId: string | null;
}) => { left: string; right: string } | null;

このフックは、インポート式の引数の左側(import()と右側())のコードの置換を提供することにより、動的インポートのレンダリング方法を細かく制御できます。nullを返すと、このタイプの他のフックに委ねられ、最終的には形式固有のデフォルトがレンダリングされます。

formatはレンダリングされた出力形式、moduleIdは動的インポートを実行するモジュールのIDです。インポートが内部または外部IDに解決できた場合、targetModuleIdはこのIDに設定されます。それ以外の場合は、nullになります。動的インポートに、resolveDynamicImportフックによって置換文字列に解決された非文字列式が含まれている場合、customResolutionにはその文字列が含まれます。

次のコードは、すべての動的インポートをカスタムハンドラーに置き換え、ハンドラーが相対インポートを正しく解決できるように、import.meta.urlを2番目の引数として追加します。

js
function dynamicImportPolyfillPlugin() {
	return {
		name: 'dynamic-import-polyfill',
		renderDynamicImport() {
			return {
				left: 'dynamicImportPolyfill(',
				right: ', import.meta.url)'
			};
		}
	};
}

// input
import('./lib.js');

// output
dynamicImportPolyfill('./lib.js', import.meta.url);

次のプラグインは、esm-libのすべての動的インポートが外部としてマークされ、インポート式として保持されるようにします。たとえば、CommonJSビルドがNode 13+でESモジュールをインポートできるようにします。詳細については、NodeドキュメントのCommonJSからのESモジュールのインポート方法を参照してください。

js
function retainImportExpressionPlugin() {
	return {
		name: 'retain-import-expression',
		resolveDynamicImport(specifier) {
			if (specifier === 'esm-lib') return false;
			return null;
		},
		renderDynamicImport({ targetModuleId }) {
			if (targetModuleId === 'esm-lib') {
				return {
					left: 'import(',
					right: ')'
				};
			}
		}
	};
}

このフックが非ES形式で動的インポートを書き換える場合、例えばデフォルトのエクスポートが.defaultとして利用可能になるようにするインターロップコードは生成されないことに注意してください。書き換えられた動的インポートが、適切な名前空間オブジェクトに解決されるPromiseを返すことを保証するのは、プラグインの責任です。

renderError

タイプ(error: Error) => void
種類非同期、並列
renderStartからrenderChunkまでの任意のフック
このフックが呼び出された場合、出力生成フェーズの最後のフックとなり、別の出力が生成される場合は、再びoutputOptionsが続く可能性があります。

bundle.generate()またはbundle.write()の実行中にRollupがエラーを検出したときに呼び出されます。エラーはこのフックに渡されます。生成が正常に完了したときに通知を受け取るには、generateBundleフックを使用します。

renderStart

タイプ(outputOptions: OutputOptions, inputOptions: InputOptions) => void
種類非同期、並列
outputOptions
最初のチャンク内の各動的インポート式に対してrenderDynamicImport

bundle.generate()またはbundle.write()が呼び出されるたびに最初に呼び出されます。生成が完了したときに通知を受け取るには、generateBundleおよびrenderErrorフックを使用します。これは、bundle.generate()またはbundle.write()に渡される出力オプションにアクセスする必要がある場合に推奨されるフックです。すべてのoutputOptionsフックによる変換を考慮し、設定されていないオプションの正しいデフォルト値も含まれています。また、rollup.rollup()に渡される入力オプションも受信するため、出力プラグインとして使用できるプラグイン(つまり、generateフェーズフックのみを使用するプラグイン)は、それらにアクセスできます。

resolveFileUrl

タイプResolveFileUrlHook
種類同期、最初
現在のチャンク内の各動的インポート式に対してrenderDynamicImport
現在のチャンクに対して並行してbannerfooterintrooutro
typescript
type ResolveFileUrlHook = (options: {
	chunkId: string;
	fileName: string;
	format: InternalModuleFormat;
	moduleId: string;
	referenceId: string;
	relativePath: string;
}) => string | NullValue;

プラグインがthis.emitFileを介して出力したファイルのURLをRollupがどのように解決するかをカスタマイズできます。デフォルトでは、Rollupはimport.meta.ROLLUP_FILE_URL_referenceIdのコードを生成します。これにより、出力形式とコードがデプロイされるホストシステムに関係なく、出力されたファイルの絶対URLを正しく生成する必要があります。

そのため、CommonJSとUMDを除くすべての形式は、URLdocumentが利用可能なブラウザ環境で実行されることを前提としています。これに失敗した場合、またはより最適化されたコードを生成するために、このフックを使用してこの動作をカスタマイズできます。そのためには、次の情報が利用可能です。

  • chunkId: このファイルが参照されているチャンクのID。チャンクファイル名にハッシュが含まれる場合、このIDには代わりにプレースホルダーが含まれます。Rollupは、生成されたコードに最終的に含まれる場合、このプレースホルダーを実際のファイル名に置き換えます。
  • fileName: 出力されたファイルのパスとファイル名。./を先頭に付けずにoutput.dirからの相対パス。これも名前内にハッシュを持つチャンクの場合、代わりにプレースホルダーが含まれます。
  • format: レンダリングされた出力形式。
  • moduleId: このファイルが参照されている元のモジュールのID。特定のアセットを条件付きで異なる方法で解決するのに役立ちます。
  • referenceId: ファイルの参照ID。
  • relativePath: ファイルが参照されているチャンクからの、出力されたファイルのパスとファイル名。このパスには先頭に./は含まれませんが、先頭に../が含まれる場合があります。

次のプラグインは常にすべてのファイルを現在のドキュメントからの相対パスで解決します。

js
function resolveToDocumentPlugin() {
	return {
		name: 'resolve-to-document',
		resolveFileUrl({ fileName }) {
			return `new URL('${fileName}', document.baseURI).href`;
		}
	};
}

resolveImportMeta

タイプ(property: string | null, {chunkId: string, moduleId: string, format: string}) => string | null
種類同期、最初
現在のチャンク内の各動的インポート式に対してrenderDynamicImport
現在のチャンクに対して並行してbannerfooterintrooutro

Rollupがimport.metaおよびimport.meta.someProperty、特にimport.meta.urlをどのように処理するかをカスタマイズできます。ESモジュールでは、import.metaはオブジェクトであり、import.meta.urlには現在のモジュールのURLが含まれます。例えば、ブラウザの場合はhttp://server.net/bundle.js、Nodeの場合はfile:///path/to/bundle.jsなどです。

デフォルトでは、ESモジュール以外の形式の場合、Rollupはimport.meta.urlを、現在のチャンクの動的なURLを返すことでこの動作に一致させようとするコードに置き換えます。CommonJSとUMDを除くすべての形式は、URLdocumentが利用可能なブラウザ環境で実行されることを前提としていることに注意してください。他のプロパティについては、import.meta.somePropertyundefinedに置き換えられ、import.metaurlプロパティを含むオブジェクトに置き換えられます。

この動作は、このフックを使用して、ESモジュールについても変更できます。import.meta<.someProperty>が発生するたびに、このフックはプロパティの名前、またはimport.metaに直接アクセスする場合はnullを指定して呼び出されます。例えば、次のコードは、元のモジュールの相対パスを使用してimport.meta.urlを現在の作業ディレクトリに解決し、実行時にこのパスを現在のドキュメントのベースURLに対して再度解決します。

js
function importMetaUrlCurrentModulePlugin() {
	return {
		name: 'import-meta-url-current-module',
		resolveImportMeta(property, { moduleId }) {
			if (property === 'url') {
				return `new URL('${path.relative(
					process.cwd(),
					moduleId
				)}', document.baseURI).href`;
			}
			return null;
		}
	};
}

chunkIdにハッシュが含まれる場合、代わりにプレースホルダーが含まれます。このプレースホルダーが生成されたコードに含まれる場合、Rollupはそれを実際のチャンクハッシュに置き換えます。

writeBundle

タイプ(options: OutputOptions, bundle: { [fileName: string]: AssetInfo | ChunkInfo }) => void
種類非同期、並列
generateBundle
このフックが呼び出された場合、出力生成フェーズの最後のフックとなり、別の出力が生成される場合は、再びoutputOptionsが続く可能性があります。

すべてのファイルの書き込みが完了した後、bundle.write()の最後にのみ呼び出されます。generateBundleフックと同様に、bundleは書き込まれているファイルの完全なリストと詳細を提供します。

プラグインコンテキスト

多くのユーティリティ関数と情報ビットは、thisを介してほとんどのフック内からアクセスできます。

this.addWatchFile

タイプ(id: string) => void

ウォッチモードで監視する追加のファイルを追加して、これらのファイルへの変更が再構築をトリガーするようにします。idは、ファイルまたはディレクトリへの絶対パス、または現在の作業ディレクトリからの相対パスにできます。このコンテキスト関数は、closeBundleを除くすべてのプラグインフックで使用できます。ただし、watch.skipWritetrueに設定されている場合、出力生成フックで使用しても効果はありません。

注意: 通常、ウォッチモードでは、再構築の速度を向上させるために、transformフックは、指定されたモジュールの内容が実際に変更された場合にのみトリガーされます。transformフック内からthis.addWatchFileを使用すると、監視対象のファイルが変更された場合にも、このモジュールのtransformフックが再評価されるようになります。

一般に、監視対象のファイルに依存するフック内からthis.addWatchFileを使用することをお勧めします。

this.debug

タイプ(log: string | RollupLog | (() => RollupLog | string), position?: number | { column: number; line: number }) => void

"debug"ログを生成します。詳細については、this.warnを参照してください。デバッグログには、Rollupによって常にcode: "PLUGIN_LOG"が追加されます。フィルタリングを容易にするために、これらのログに特徴的なpluginCodeを追加してください。

これらのログは、logLevelオプションが明示的に"debug"に設定されている場合にのみ処理され、それ以外の場合は何も行いません。したがって、プラグインに役立つデバッグログを追加することをお勧めします。これにより、問題を発見するのに役立つ一方で、デフォルトでは効率的にミュートされます。ログを生成するためにコストのかかる計算を実行する必要がある場合は、ログが実際に処理される場合にのみこれらの計算が実行されるように、関数形式を使用してください。

js
function plugin() {
	return {
		name: 'test',
		transform(code, id) {
			this.debug(
				() =>
					`transforming ${id},\n` +
					`module contains, ${code.split('\n').length} lines`
			);
		}
	};
}

this.emitFile

タイプ(emittedFile: EmittedChunk | EmittedPrebuiltChunk | EmittedAsset) => string
typescript
interface EmittedChunk {
	type: 'chunk';
	id: string;
	name?: string;
	fileName?: string;
	implicitlyLoadedAfterOneOf?: string[];
	importer?: string;
	preserveSignature?: 'strict' | 'allow-extension' | 'exports-only' | false;
}

interface EmittedPrebuiltChunk {
	type: 'prebuilt-chunk';
	fileName: string;
	code: string;
	exports?: string[];
	map?: SourceMap;
}

interface EmittedAsset {
	type: 'asset';
	name?: string;
	needsCodeReference?: boolean;
	fileName?: string;
	source?: string | Uint8Array;
}

ビルド出力に含まれる新しいファイルを出力し、出力されたファイルを参照するためにさまざまな場所で使用できるreferenceIdを返します。チャンク、事前構築済みチャンク、またはアセットを出力できます。

チャンクまたはアセットを出力する場合、nameまたはfileNameのいずれかを指定できます。fileNameが指定されている場合、これは生成されたファイルの名前として変更せずに使用され、これにより競合が発生するとエラーがスローされます。それ以外の場合、nameが指定されている場合、これは対応するoutput.chunkFileNamesまたはoutput.assetFileNamesパターンの[name]の代替として使用され、競合を回避するためにファイル名の末尾に一意の番号が追加される場合があります。namefileNameも指定されていない場合、デフォルトの名前が使用されます。事前構築済みチャンクには、常にfileNameが必要です。

loadまたはtransformプラグインフックによって返される任意のコードで、import.meta.ROLLUP_FILE_URL_referenceIdを介して出力されたファイルのURLを参照できます。詳細と例については、ファイルURLを参照してください。

import.meta.ROLLUP_FILE_URL_referenceIdを置き換える生成されたコードは、resolveFileUrlプラグインフックを使用してカスタマイズできます。this.getFileName(referenceId)を使用して、利用可能になったらすぐにファイル名を判断することもできます。ファイル名が明示的に設定されていない場合、

  • アセットファイル名は、renderStartフックから利用可能です。後で出力されるアセットの場合、ファイル名はアセットを出力した直後に利用可能になります。
  • ハッシュを含まないチャンクファイル名は、renderStartフックの後にチャンクが作成されるとすぐに利用可能になります。
  • チャンクファイル名にハッシュが含まれる場合、generateBundle より前の任意のフックで getFileName を使用すると、実際の名前ではなくプレースホルダーを含む名前が返されます。このファイル名またはその一部を renderChunk で変換するチャンクで使用すると、Rollup は generateBundle の前にプレースホルダーを実際のハッシュに置き換え、ハッシュが、参照されているすべてのファイルハッシュを含む、最終的に生成されたチャンクの実際のコンテンツを反映するようにします。

typechunk の場合、これは指定されたモジュール id をエントリポイントとして新しいチャンクを生成します。これを解決するために、idresolveId から始まる通常の開始点と同じように、ビルドフックを通過します。importer が提供されている場合、これは resolveId の 2 番目のパラメーターとして機能し、相対パスを適切に解決するために重要です。提供されていない場合、パスは現在の作業ディレクトリを基準に解決されます。preserveSignature の値が提供されている場合、これはこの特定のチャンクの preserveEntrySignatures をオーバーライドします。

これにより、グラフ内に重複したモジュールは生成されません。代わりに、必要に応じて、既存のチャンクが分割されるか、再エクスポートを含むファサードチャンクが作成されます。指定された fileName を持つチャンクは常に個別のチャンクを生成しますが、他の出力されたチャンクは、name が一致しなくても、同じソースを持つ場合は既存のチャンクと重複排除される可能性があります。このようなチャンクが重複排除されない場合、output.chunkFileNames 名前パターンが使用されます。

デフォルトでは、Rollup は、出力されたチャンクが他のエントリポイントとは独立して実行され、場合によっては他のコードが実行される前であっても実行されると想定します。これは、出力されたチャンクが既存のエントリポイントと依存関係を共有している場合、Rollup はこれらのエントリポイント間で共有される依存関係のために追加のチャンクを作成することを意味します。implicitlyLoadedAfterOneOf にモジュール ID の空でない配列を提供すると、Rollup に追加の情報を提供して、場合によってはこれを防ぐことで、その動作が変更されます。これらの ID は、id プロパティと同じ方法で解決され、importer プロパティが提供されている場合はそれを尊重します。Rollup は、出力されたチャンクが、implicitlyLoadedAfterOneOf にある ID のいずれかにつながるエントリポイントの少なくとも1つがロード済みである場合にのみ実行されると想定するようになり、新しく出力されたチャンクが implicitlyLoadedAfterOneOf のモジュールからの動的インポートを介してのみ到達可能である場合と同じチャンクを作成します。以下は、これを活用して、実行順序を尊重するように最適化されたチャンクを作成する、複数のスクリプトを含む単純な HTML ファイルを作成する例です。

js
// rollup.config.js
function generateHtmlPlugin() {
	let ref1, ref2, ref3;
	return {
		name: 'generate-html',
		buildStart() {
			ref1 = this.emitFile({
				type: 'chunk',
				id: 'src/entry1'
			});
			ref2 = this.emitFile({
				type: 'chunk',
				id: 'src/entry2',
				implicitlyLoadedAfterOneOf: ['src/entry1']
			});
			ref3 = this.emitFile({
				type: 'chunk',
				id: 'src/entry3',
				implicitlyLoadedAfterOneOf: ['src/entry2']
			});
		},
		generateBundle() {
			this.emitFile({
				type: 'asset',
				fileName: 'index.html',
				source: `
        <!DOCTYPE html>
        <html>
        <head>
          <meta charset="UTF-8">
          <title>Title</title>
         </head>
        <body>
          <script src="${this.getFileName(ref1)}" type="module"></script>
          <script src="${this.getFileName(ref2)}" type="module"></script>
          <script src="${this.getFileName(ref3)}" type="module"></script>
        </body>
        </html>`
			});
		}
	};
}

export default {
	input: [],
	preserveEntrySignatures: false,
	plugins: [generateHtmlPlugin()],
	output: {
		format: 'es',
		dir: 'dist'
	}
};

動的インポートがない場合、これは正確に3つのチャンクを作成し、最初のチャンクには src/entry1 のすべての依存関係が含まれ、2番目のチャンクには最初のチャンクに含まれていない src/entry2 の依存関係のみが含まれ、それらを最初のチャンクからインポートし、3番目のチャンクも同様になります。

任意のモジュール ID を implicitlyLoadedAfterOneOf で使用できますが、その ID がチャンクと一意に関連付けることができない場合 (たとえば、既存の静的エントリポイントから暗黙的または明示的に id に到達できない場合や、ファイルが完全にツリーシェイクされている場合)、Rollup はエラーをスローすることに注意してください。ユーザーによって定義されたエントリポイント、または以前に出力されたチャンクのエントリポイントのみを使用すると、常に機能します。

typeprebuilt-chunk の場合、code パラメーターによって提供された固定コンテンツを持つチャンクを出力します。現時点では、チャンクの名前を提供するために fileName も必要です。いくつかの変数をエクスポートする場合は、オプションの exports を介してこれらをリストする必要があります。map を介して、code に対応するソースマップを提供できます。

インポートでプリビルドチャンクを参照するには、プリビルドチャンクはモジュールグラフの一部ではないため、resolveId フックで「モジュール」を外部としてマークする必要があります。代わりに、チャンクメタデータを持つアセットのように動作します。

js
function emitPrebuiltChunkPlugin() {
	return {
		name: 'emit-prebuilt-chunk',
		resolveId(source) {
			if (source === './my-prebuilt-chunk.js') {
				return {
					id: source,
					external: true
				};
			}
		},
		buildStart() {
			this.emitFile({
				type: 'prebuilt-chunk',
				fileName: 'my-prebuilt-chunk.js',
				code: 'export const foo = "foo"',
				exports: ['foo']
			});
		}
	};
}

これで、コードでプリビルドチャンクを参照できます。

js
import { foo } from './my-prebuilt-chunk.js';

現在、プリビルドチャンクの出力は基本的な機能です。フィードバックをお待ちしております。

typeasset の場合、これは指定された source をコンテンツとして持つ任意の新しいファイルを出力します。ビルドフェーズ中にファイルを参照できるようにするために、this.setAssetSource(referenceId, source) を介して source の設定を後で延期し、生成フェーズ中に各出力に対して個別にソースを設定することができます。指定された fileName を持つアセットは常に個別のファイルを生成しますが、name が一致しなくても、他の出力されたアセットは、同じソースを持つ場合は既存のアセットと重複排除される可能性があります。fileName を持たないアセットが重複排除されない場合、output.assetFileNames 名前パターンが使用されます。needsCodeReferencetrue に設定されており、このアセットが import.meta.ROLLUP_FILE_URL_referenceId を介して出力内のどのコードからも参照されていない場合、Rollup はそれを出力しません。これは、ツリーシェイクによって削除された参照も尊重します。つまり、対応する import.meta.ROLLUP_FILE_URL_referenceId がソースコードの一部であっても、実際には使用されておらず、参照がツリーシェイクによって削除された場合、アセットは出力されません。

this.error

タイプ(error: string | RollupLog | Error, position?: number | { column: number; line: number }) => never

this.warn と構造的に同等ですが、エラーが発生するとバンドルプロセスも中断されます。RollupLog 型の詳細については、onLog オプションを参照してください。

Error インスタンスが渡された場合、それはそのまま使用されます。それ以外の場合は、指定されたエラーメッセージと、提供されたすべての追加プロパティを使用して、新しい Error インスタンスが作成されます。

onLog フックを除くすべてのフックでは、エラーは code: "PLUGIN_ERROR" および plugin: plugin.name プロパティで拡張されます。code プロパティがすでに存在し、コードが PLUGIN_ で始まらない場合、pluginCode に名前が変更されます。

onLog フックでは、この関数は、警告のすべての追加プロパティを保持しながら、警告をエラーに変える簡単な方法です。

js
function myPlugin() {
	return {
		name: 'my-plugin',
		onLog(level, log) {
			if (level === 'warn' && log.code === 'THIS_IS_NOT_OK') {
				return this.error(log);
			}
		}
	};
}

transform フックで使用する場合、現在のモジュールの id も追加され、position を指定できます。これは、posloc (標準の { file, line, column } オブジェクト)、および frame (場所を示すコードスニペット) でログを拡張するために使用される文字インデックスまたはファイル位置です。

this.getCombinedSourcemap

タイプ() => SourceMap

以前のすべてのプラグインの結合されたソースマップを取得します。このコンテキスト関数は、transform プラグインフックでのみ使用できます。

this.getFileName

タイプ(referenceId: string) => string

this.emitFile を介して出力されたチャンクまたはアセットのファイル名を取得します。ファイル名は outputOptions.dir を基準にした相対パスになります。

this.getModuleIds

タイプ() => IterableIterator<string>

現在のグラフ内のすべてのモジュール ID にアクセスできる Iterator を返します。次のように反復処理できます

js
for (const moduleId of this.getModuleIds()) {
	/* ... */
}

または、Array.from(this.getModuleIds()) を使用して配列に変換できます。

this.getModuleInfo

タイプ(moduleId: string) => (ModuleInfo | null)
typescript
interface ModuleInfo {
	id: string; // the id of the module, for convenience
	code: string | null; // the source code of the module, `null` if external or not yet available
	ast: ESTree.Program; // the parsed abstract syntax tree if available
	hasDefaultExport: boolean | null; // is there a default export, `null` if external or not yet available
	isEntry: boolean; // is this a user- or plugin-defined entry point
	isExternal: boolean; // for external modules that are referenced but not included in the graph
	isIncluded: boolean | null; // is the module included after tree-shaking, `null` if external or not yet available
	importedIds: string[]; // the module ids statically imported by this module
	importedIdResolutions: ResolvedId[]; // how statically imported ids were resolved, for use with this.load
	importers: string[]; // the ids of all modules that statically import this module
	exportedBindings: Record<string, string[]> | null; // contains all exported variables associated with the path of `from`, `null` if external
	exports: string[] | null; // all exported variables, `null` if external
	dynamicallyImportedIds: string[]; // the module ids imported by this module via dynamic import()
	dynamicallyImportedIdResolutions: ResolvedId[]; // how ids imported via dynamic import() were resolved
	dynamicImporters: string[]; // the ids of all modules that import this module via dynamic import()
	implicitlyLoadedAfterOneOf: string[]; // implicit relationships, declared via this.emitFile
	implicitlyLoadedBefore: string[]; // implicit relationships, declared via this.emitFile
	attributes: { [key: string]: string }; // import attributes for this module
	meta: { [plugin: string]: any }; // custom module meta-data
	moduleSideEffects: boolean | 'no-treeshake'; // are imports of this module included if nothing is imported from it
	syntheticNamedExports: boolean | string; // final value of synthetic named exports
}

interface ResolvedId {
	id: string; // the id of the imported module
	external: boolean | 'absolute'; // is this module external, "absolute" means it will not be rendered as relative in the module
	attributes: { [key: string]: string }; // import attributes for this import
	meta: { [plugin: string]: any }; // custom module meta-data when resolving the module
	moduleSideEffects: boolean | 'no-treeshake'; // are side effects of the module observed, is tree-shaking enabled
	resolvedBy: string; // which plugin resolved this module, "rollup" if resolved by Rollup itself
	syntheticNamedExports: boolean | string; // does the module allow importing non-existing named exports
}

問題のモジュールに関する追加情報を返します。

ビルド中、このオブジェクトは、buildEnd フックの前には不正確になる可能性のある、モジュールに関する現在利用可能な情報を表します。

  • idisExternal は決して変わりません。
  • codeasthasDefaultExportexports、および exportedBindings は、解析後、つまり moduleParsed フックまたは this.load を待機した後にのみ使用できます。その時点で、それらは変更されなくなります。
  • isEntrytrue の場合、それは変更されなくなります。ただし、モジュールは、this.emitFile を介して、またはプラグインがエントリポイントを解決するときに resolveId フックで this.load を介して潜在的なエントリポイントを検査するために、解析後にエントリポイントになる可能性があります。したがって、transform フックでこのフラグに依存することはお勧めしません。buildEnd の後は変更されなくなります。
  • 同様に、implicitlyLoadedAfterOneOf は、this.emitFile を介して buildEnd 前にいつでも追加のエントリを受け取ることができます。
  • importersdynamicImporters、および implicitlyLoadedBefore は、新しいインポーターと暗黙的な依存関係が検出されると追加のエントリを受け取る空の配列として開始されます。buildEnd の後は変更されなくなります。
  • isIncludedbuildEnd 後にのみ使用でき、その時点で変更されなくなります。
  • importedIdsimportedIdResolutionsdynamicallyImportedIds、および dynamicallyImportedIdResolutions は、モジュールが解析され、その依存関係が解決されたときに使用できます。これは、moduleParsed フック、または resolveDependencies フラグを使用して this.load を待機したときの場合です。その時点で、それらは変更されなくなります。
  • attributesmetamoduleSideEffects、および syntheticNamedExports は、load および transform フックによって変更できます。さらに、ほとんどのプロパティは読み取り専用ですが、これらのプロパティは書き込み可能であり、buildEnd フックがトリガーされる前に発生した場合、変更が反映されます。meta 自体は上書きしないでください。ただし、モジュールに関するメタ情報を保存するために、いつでもそのプロパティを変更しても問題ありません。プラグインで状態を維持する代わりにこれを行う利点は、キャッシュが使用されている場合 (たとえば、CLI からウォッチモードを使用する場合) に meta がキャッシュに永続化され、キャッシュから復元されることです。

モジュール ID が見つからない場合は null を返します。

this.getWatchFiles

タイプ() => string[]

以前に監視されていたファイルの ID を取得します。this.addWatchFile を使用してプラグインによって追加されたファイルと、ビルド中に Rollup によって暗黙的に追加されたファイルの両方を含みます。

this.info

タイプ(log: string | RollupLog | (() => RollupLog | string), position?: number | { column: number; line: number }) => void

"info" ログを生成します。詳細については、this.warn を参照してください。情報ログには常に Rollup によって追加された code: "PLUGIN_LOG" が含まれます。これらのログはデフォルトで表示されるため、警告ではないが、すべてのユーザーにすべてのビルドで表示することが理にかなっている情報に使用してください。

logLevel オプションが "warn" または "silent" に設定されている場合、このメソッドは何もしません。

this.load

タイプロード
typescript
type Load = (options: {
	id: string;
	resolveDependencies?: boolean;
	attributes?: Record<string, string> | null;
	meta?: CustomPluginOptions | null;
	moduleSideEffects?: boolean | 'no-treeshake' | null;
	syntheticNamedExports?: boolean | string | null;
}) => Promise<ModuleInfo>;

指定された ID に対応するモジュールをロードして解析し、追加のメタ情報があればモジュールに付与します。これにより、モジュールが別のモジュールによってインポートされた場合と同様の loadtransformmoduleParsed フックがトリガーされます。

これにより、resolveId フックでモジュールをどのように解決するかを決定する前に、モジュールの最終的なコンテンツを調べることができ、例えば、代わりにプロキシモジュールを解決することができます。モジュールが後でグラフの一部になった場合、このコンテキスト関数を使用しても、モジュールが再び解析されることはないため、追加のオーバーヘッドは発生しません。シグネチャでは、this.resolve の戻り値を、nullでも外部モジュールでもない限り、この関数に直接渡すことができます。

返される Promise は、モジュールが完全に変換および解析された後、インポートが解決される前に解決されます。つまり、結果の ModuleInfo には、空の importedIdsdynamicallyImportedIdsimportedIdResolutions、および dynamicallyImportedIdResolutions が含まれます。これにより、resolveId フックで this.load を待機する際のデッドロック状態を回避するのに役立ちます。importedIds および dynamicallyImportedIds に興味がある場合は、moduleParsed フックを実装するか、resolveDependencies フラグを渡すことができます。これにより、this.load によって返される Promise は、すべての依存関係 ID が解決されるまで待機します。

attributesmetamoduleSideEffects、および syntheticNamedExports オプションに関しては、resolveId フックと同じ制限が適用されることに注意してください。これらの値は、モジュールがまだロードされていない場合にのみ有効です。したがって、まず this.resolve を使用して、プラグインが resolveId フックでこれらのオプションに特別な値を設定するかどうかを確認し、必要に応じてこれらのオプションを this.load に渡すことが非常に重要です。以下の例は、特別なコードコメントを含むモジュールにプロキシモジュールを追加する方法を示しています。デフォルトのエクスポートの再エクスポートに関する特別な処理に注意してください。

js
export default function addProxyPlugin() {
	return {
		async resolveId(source, importer, options) {
			if (importer?.endsWith('?proxy')) {
				// Do not proxy ids used in proxies
				return null;
			}
			// We make sure to pass on any resolveId options to
			// this.resolve to get the module id
			const resolution = await this.resolve(source, importer, options);
			// We can only pre-load existing and non-external ids
			if (resolution && !resolution.external) {
				// we pass on the entire resolution information
				const moduleInfo = await this.load(resolution);
				if (moduleInfo.code.includes('/* use proxy */')) {
					return `${resolution.id}?proxy`;
				}
			}
			// As we already fully resolved the module, there is no reason
			// to resolve it again
			return resolution;
		},
		load(id) {
			if (id.endsWith('?proxy')) {
				const importee = id.slice(0, -'?proxy'.length);
				// Note that namespace reexports do not reexport default
				// exports
				let code = `console.log('proxy for ${importee}'); export * from ${JSON.stringify(
					importee
				)};`;
				// We know that while resolving the proxy, importee was
				// already fully loaded and parsed, so we can rely on
				// hasDefaultExport
				if (this.getModuleInfo(importee).hasDefaultExport) {
					code += `export { default } from ${JSON.stringify(importee)};`;
				}
				return code;
			}
			return null;
		}
	};
}

モジュールが既にロードされている場合、this.load は解析が完了するのを待ってから、そのモジュール情報を返します。モジュールが別のモジュールによってまだインポートされていない場合、このモジュールによってインポートされた他のモジュールのロードが自動的にトリガーされることはありません。代わりに、静的および動的な依存関係は、このモジュールが少なくとも一度実際にインポートされた場合にのみロードされます。

resolveId フックで this.load を使用するのは安全ですが、load または transform フックで待機する場合は非常に注意する必要があります。モジュールグラフに循環依存関係がある場合、これは簡単にデッドロックにつながる可能性があるため、プラグインは、ロードされたモジュールと循環関係にあるモジュールの load または transform 内で this.load を待機しないように手動で注意する必要があります。

resolveDependencies オプションと this.load の繰り返し呼び出しを介して、依存関係サブグラフ全体をスキャンする、より詳細な例を次に示します。循環依存関係を処理するために、処理されたモジュール ID の Set を使用します。プラグインの目標は、チャンク内のすべてのモジュールをリストするだけで、動的にインポートされた各チャンクにログを追加することです。これは単なるおもちゃの例ですが、この手法を使用して、例えばサブグラフでインポートされたすべての CSS 用の単一のスタイルタグを作成することができます。

js
// The leading \0 instructs other plugins not to try to resolve, load or
// transform our proxy modules
const DYNAMIC_IMPORT_PROXY_PREFIX = '\0dynamic-import:';

export default function dynamicChunkLogsPlugin() {
	return {
		name: 'dynamic-chunk-logs',
		async resolveDynamicImport(specifier, importer) {
			// Ignore non-static targets
			if (!(typeof specifier === 'string')) return;
			// Get the id and initial meta information of the import target
			const resolved = await this.resolve(specifier, importer);
			// Ignore external targets. Explicit externals have the
			// "external" property while unresolved imports are "null".
			if (resolved && !resolved.external) {
				// We trigger loading the module without waiting for it
				// here because meta information attached by resolveId
				// hooks, that may be contained in "resolved" and that
				// plugins like "commonjs" may depend upon, is only
				// attached to a module the first time it is loaded. This
				// ensures that this meta information is not lost when we
				// later use "this.load" again in the load hook with just
				// the module id.
				this.load(resolved);
				return `${DYNAMIC_IMPORT_PROXY_PREFIX}${resolved.id}`;
			}
		},
		async load(id) {
			// Ignore all files except our dynamic import proxies
			if (!id.startsWith('\0dynamic-import:')) return null;
			const actualId = id.slice(DYNAMIC_IMPORT_PROXY_PREFIX.length);
			// To allow loading modules in parallel while keeping
			// complexity low, we do not directly await each "this.load"
			// call but put their promises into an array where we await
			// them via an async for loop.
			const moduleInfoPromises = [
				this.load({ id: actualId, resolveDependencies: true })
			];
			// We track each loaded dependency here so that we do not load
			// a file twice and also do  not get stuck when there are
			// circular dependencies.
			const dependencies = new Set([actualId]);
			// "importedIdResolutions" tracks the objects created by
			// resolveId hooks. We are using those instead of "importedIds"
			// so that again, important meta information is not lost.
			for await (const { importedIdResolutions } of moduleInfoPromises) {
				for (const resolved of importedIdResolutions) {
					if (!dependencies.has(resolved.id)) {
						dependencies.add(resolved.id);
						moduleInfoPromises.push(
							this.load({ ...resolved, resolveDependencies: true })
						);
					}
				}
			}
			// We log all modules in a dynamic chunk when it is loaded.
			let code = `console.log([${[...dependencies]
				.map(JSON.stringify)
				.join(', ')}]); export * from ${JSON.stringify(actualId)};`;
			// Namespace reexports do not reexport default exports, which
			// is why we reexport it manually if it exists
			if (this.getModuleInfo(actualId).hasDefaultExport) {
				code += `export { default } from ${JSON.stringify(actualId)};`;
			}
			return code;
		}
	};
}

this.meta

タイプ{rollupVersion: string, watchMode: boolean}

潜在的に役立つ Rollup メタデータを含むオブジェクト

  • rollupVersion: package.json で定義されている、現在実行中の Rollup のバージョン。
  • watchMode: Rollup が rollup.watch(...) またはコマンドラインから --watch で起動された場合は true、それ以外の場合は false

meta は、options フックからアクセスできる唯一のコンテキストプロパティです。

this.parse

タイプ(code: string, options?: ParseOptions) => ESTree.Program
typescript
interface ParseOptions {
	allowReturnOutsideFunction?: boolean;
}

Rollup の内部 SWC ベースのパーサーを使用して、コードを ESTree 互換の AST に解析します。

  • allowReturnOutsideFunction: true の場合、関数外での return ステートメントを許可して、例えば CommonJS コードの解析をサポートします。

this.resolve

タイプ解決
typescript
type Resolve = (
	source: string,
	importer?: string,
	options?: {
		skipSelf?: boolean;
		isEntry?: boolean;
		attributes?: { [key: string]: string };
		custom?: { [plugin: string]: any };
	}
) => ResolvedId;

ヒント

このフックの戻り値の型である ResolvedId は、this.getModuleInfo で定義されています。

Rollup が使用するのと同じプラグインを使用して、インポートをモジュール ID (つまり、ファイル名) に解決し、インポートが外部であるかどうかを判断します。null が返された場合、インポートは Rollup またはプラグインで解決できませんでしたが、ユーザーによって明示的に外部としてマークされていませんでした。makeAbsoluteExternalsRelative オプションを使用するか、resolveId フックで明示的にプラグインを選択することにより、出力で絶対のままにする必要がある絶対外部 ID が返された場合、externaltrue の代わりに "absolute" になります。

skipSelf のデフォルトは true です。したがって、this.resolve が呼び出されたプラグインの resolveId フックは、解決時にスキップされます。他のプラグイン自体が、元の this.resolve 呼び出しを処理中にまったく同じ sourceimporter を使用して resolveId フックで this.resolve を呼び出すと、元のプラグインの resolveId フックもこれらの呼び出しでスキップされます。ここでの理由は、プラグインがこの時点で特定の sourceimporter の組み合わせを解決する方法を「知らない」と既に述べているためです。この動作を望まない場合は、skipSelffalse に設定し、必要に応じて独自の無限ループ防止メカニズムを実装してください。

custom オプションを使用してプラグイン固有のオプションのオブジェクトを渡すこともできます。詳細については、カスタムリゾルバーオプション を参照してください。

ここで渡す isEntry の値は、この呼び出しを処理する resolveId フックに渡されます。それ以外の場合、インポーターがある場合は false が渡され、ない場合は true が渡されます。

attributes にオブジェクトを渡すと、アサーション付きでインポートを解決するシミュレーションが実行されます。例えば、attributes: {type: "json"} は、import "foo" assert {type: "json"} を解決するシミュレーションを実行します。これは、この呼び出しを処理する resolveId フックに渡され、最終的に返されるオブジェクトの一部になる可能性があります。

resolveId フックからこの関数を呼び出す場合は、isEntrycustom、および attributes オプションを渡すことが理にかなっているかどうかを常に確認する必要があります。

resolvedBy の値は、このソースを解決したプラグインを示します。Rollup 自体によって解決された場合、値は "rollup" になります。プラグインの resolveId フックがこのソースを解決する場合、resolvedBy の明示的な値を返さない限り、値はプラグインの名前になります。このフラグはデバッグとドキュメント作成のみを目的としており、Rollup ではこれ以上処理されません。

this.setAssetSource

タイプ(referenceId: string, source: string | Uint8Array) => void

アセットの遅延ソースを設定します。source として Node の Buffer を渡すこともできます。これは Uint8Array のサブクラスであるためです。

this.warn

タイプ(log: string | RollupLog | (() => RollupLog | string), position?: number | { column: number; line: number }) => void

このメソッドを使用すると、ログレベル "warn" のログであるビルドの警告が生成されます。RollupLog 型の詳細については、onLog オプションを参照してください。他のログを生成するには、this.info および this.debug も参照してください。エラーを生成するには、this.error を参照してください。

内部で生成された警告と同様に、これらのログは、カスタムの onLog または onwarn ハンドラーに転送されるか、コンソールに出力される前に、まずプラグインの onLog フックに渡されてフィルター処理されます。

warning 引数は、string または (少なくとも) message プロパティを持つオブジェクトにすることができます。

js
this.warn('hmm...');
// is equivalent to
this.warn({
	message: 'hmm...',
	pluginCode: 'CODE_TO_IDENTIFY_LOG',
	meta: 'Additional plugin specific information'
});

onLog ハンドラーでユーザーがそれらのログを簡単にフィルター処理できるようにするために、pluginCode プロパティを持つオブジェクトを使用することをお勧めします。追加の情報を追加する必要がある場合は、meta プロパティを使用できます。ログに code が含まれており、まだ pluginCode プロパティがない場合、プラグインの警告には常に Rollup によって追加される PLUGIN_WARNINGcode が付与されるため、pluginCode に名前が変更されます。この動作を防ぐために、プラグインは代わりに、buildStart フックに渡される正規化された onLog オプションを使用できます。プラグインからこのオプションを呼び出すと、ログをプラグインの onLog ハンドラーおよび onLog または onwarn ハンドラーに渡すときにプロパティは変更されません。

ログを生成するためにコストのかかる計算を行う必要がある場合は、string または RollupLog オブジェクトのいずれかを返す関数を渡すこともできます。この関数は、logLevel オプションでログがフィルタリングされない場合にのみ呼び出されます。

js
// This will only run if the logLevel is set to "debug"
this.debug(() => generateExpensiveDebugLog());

transform フックで使用する場合、現在のモジュールの id も追加され、position を指定できます。これは、posloc (標準の { file, line, column } オブジェクト)、および frame (場所を示すコードスニペット) でログを拡張するために使用される文字インデックスまたはファイル位置です。

logLevel オプションが "silent" に設定されている場合、このメソッドは何もしません。

ファイル URL

JS コード内からファイル URL 参照を参照するには、import.meta.ROLLUP_FILE_URL_referenceId の置換を使用します。これにより、出力形式に依存するコードが生成され、ターゲット環境で出力されたファイルを指す URL が生成されます。CommonJS および UMD を除くすべての形式は、URL および document が利用可能なブラウザー環境で実行されることを前提としていることに注意してください。

次の例では、.svg ファイルのインポートを検出し、インポートされたファイルをアセットとして出力し、例えば img タグの src 属性として使用される URL を返します。

js
function svgResolverPlugin() {
	return {
		name: 'svg-resolver',
		resolveId(source, importer) {
			if (source.endsWith('.svg')) {
				return path.resolve(path.dirname(importer), source);
			}
		},
		load(id) {
			if (id.endsWith('.svg')) {
				const referenceId = this.emitFile({
					type: 'asset',
					name: path.basename(id),
					source: fs.readFileSync(id)
				});
				return `export default import.meta.ROLLUP_FILE_URL_${referenceId};`;
			}
		}
	};
}

使用方法

js
import logo from '../images/logo.svg';
const image = document.createElement('img');
image.src = logo;
document.body.appendChild(image);

場合によっては、このアセットを参照したコードは、次の例のように条件付きでのみ使用されます。

js
import logo from '../images/logo.svg';
if (COMPILER_FLAG) {
	const image = document.createElement('img');
	image.src = logo;
	document.body.appendChild(image);
}

プラグインが COMPILER_FLAGfalse に置き換えた場合、予期しない結果が発生します。参照されていないアセットは引き続き出力されますが、使用されません。この問題を解決するには、次のコードのように、this.emitFile を呼び出すときに needsCodeReference を true に設定します。

js
function svgResolverPlugin() {
	return {
		/* ... */
		load(id) {
			if (id.endsWith('.svg')) {
				const referenceId = this.emitFile({
					type: 'asset',
					name: path.basename(id),
					needsCodeReference: true,
					source: fs.readFileSync(id)
				});
				return `export default import.meta.ROLLUP_FILE_URL_${referenceId};`;
			}
		}
	};
}

これで、アセットは、参照 `import.meta.ROLLUP_FILE_URL_referenceId` がコード内で実際に使用されている場合にのみバンドルに追加されるようになります。

アセットと同様に、出力されたチャンクも、`import.meta.ROLLUP_FILE_URL_referenceId` を介してJSコード内から参照できます。

次の例では、`register-paint-worklet:` で始まるインポートを検出し、CSSペイントワークレットを生成するために必要なコードと別のチャンクを生成します。これは最新のブラウザでのみ機能し、出力形式が `es` に設定されている場合にのみ機能することに注意してください。

js
const REGISTER_WORKLET = 'register-paint-worklet:';

function registerPaintWorkletPlugin() {
	return {
		name: 'register-paint-worklet',
		load(id) {
			if (id.startsWith(REGISTER_WORKLET)) {
				return `CSS.paintWorklet.addModule(import.meta.ROLLUP_FILE_URL_${this.emitFile(
					{
						type: 'chunk',
						id: id.slice(REGISTER_WORKLET.length)
					}
				)});`;
			}
		},
		resolveId(source, importer) {
			// We remove the prefix, resolve everything to absolute ids and
			// add the prefix again. This makes sure that you can use
			// relative imports to define worklets
			if (source.startsWith(REGISTER_WORKLET)) {
				return this.resolve(
					source.slice(REGISTER_WORKLET.length),
					importer
				).then(resolvedId => REGISTER_WORKLET + resolvedId.id);
			}
			return null;
		}
	};
}

使用方法

js
// main.js
import 'register-paint-worklet:./worklet.js';
import { color, size } from './config.js';
document.body.innerHTML += `<h1 style="background-image: paint(vertical-lines);">color: ${color}, size: ${size}</h1>`;

// worklet.js
import { color, size } from './config.js';
registerPaint(
	'vertical-lines',
	class {
		paint(ctx, geom) {
			for (let x = 0; x < geom.width / size; x++) {
				ctx.beginPath();
				ctx.fillStyle = color;
				ctx.rect(x * size, 0, 2, geom.height);
				ctx.fill();
			}
		}
	}
);

// config.js
export const color = 'greenyellow';
export const size = 6;

このコードをビルドすると、メインチャンクとワークレットの両方が、共有チャンクを介して `config.js` のコードを共有します。これにより、ブラウザのキャッシュを利用して、送信されるデータを削減し、ワークレットの読み込みを高速化できます。

トランスフォーマー

トランスフォーマープラグイン(例えば、非JSファイルをトランスパイルするために `transform` 関数を返すもの)は、 `options.include` および `options.exclude` をサポートする必要があります。どちらも、ミニマッチパターンまたはミニマッチパターンの配列にできます。 `options.include` が省略されているか、長さがゼロの場合、ファイルはデフォルトで含まれる必要があります。それ以外の場合は、IDがパターンのいずれかに一致する場合にのみ含まれる必要があります。

`transform` フックは、オブジェクトを返す場合、`ast` プロパティを含めることもできます。この機能は、何をしているかを理解している場合にのみ使用してください。トランスフォームのチェーンで最後のASTのみが使用されることに注意してください(そして、トランスフォームがある場合、`load` フックによって生成されたASTは、トランスフォームされたモジュールに対して破棄されます)。

トランスフォーマーの例

(一般的な機能や推奨される方法でトランスフォーマーを実装するには、@rollup/pluginutils を使用してください。)

js
import { createFilter } from '@rollup/pluginutils';

function transformCodePlugin(options = {}) {
	const filter = createFilter(options.include, options.exclude);

	return {
		name: 'transform-code',
		transform(code, id) {
			if (!filter(id)) return;

			// proceed with the transformation...
			return {
				code: generatedCode,
				map: generatedSourceMap
			};
		}
	};
}

ソースコードの変換

プラグインがソースコードを変換する場合、特定の `sourceMap: false` オプションがない限り、自動的にソースマップを生成する必要があります。Rollupは `mappings` プロパティのみに関心があります(その他はすべて自動的に処理されます)。 magic-string は、コードスニペットの追加や削除などの基本的な変換のために、そのようなマップを生成する簡単な方法を提供します。

ソースマップを生成することが意味をなさない場合は、(例: rollup-plugin-string)空のソースマップを返します。

js
return {
	code: transformedCode,
	map: { mappings: '' }
};

変換がコードを移動しない場合は、`null` を返すことで既存のソースマップを保持できます。

js
return {
	code: transformedCode,
	map: null
};

他の人に役立つと思われるプラグインを作成した場合は、NPMに公開し、github.com/rollup/awesome に送信してください!

合成名前付きエクスポート

`resolveId``load`、または`transform`フックでモジュールの `syntheticNamedExports` オプションを設定することにより、欠落しているエクスポートのフォールバックエクスポートを指定できます。 `syntheticNamedExports` に文字列値が使用されている場合、このモジュールは、指定された名前の名前付きエクスポートのプロパティに、欠落している名前付きエクスポートの解決をフォールバックします。

dep.js:(`{syntheticNamedExports: '__synthetic'}`)

js
export const foo = 'explicit';
export const __synthetic = {
	foo: 'foo',
	bar: 'bar'
};

main.js

js
import { foo, bar, baz, __synthetic } from './dep.js';

// logs "explicit" as non-synthetic exports take precedence
console.log(foo);

// logs "bar", picking the property from __synthetic
console.log(bar);

// logs "undefined"
console.log(baz);

// logs "{foo:'foo',bar:'bar'}"
console.log(__synthetic);

エントリポイントとして使用する場合、明示的なエクスポートのみが公開されます。例の合成フォールバックエクスポート、つまり`__synthetic`は、`syntheticNamedExports`の文字列値に対して公開されません。ただし、値が`true`の場合、デフォルトのエクスポートが公開されます。これが `syntheticNamedExports: true` と `syntheticNamedExports: 'default'` の唯一の顕著な違いです。

プラグイン間の通信

多くの専用プラグインを使用している場合、ビルド中に無関係なプラグインが情報を交換する必要がある場合があります。Rollupがこれを可能にするいくつかのメカニズムがあります。

カスタムリゾルバーオプション

あるプラグインが、別のプラグインによってインポートがどのように生成されたかに応じて、インポートを異なるIDに解決する必要があると仮定します。これを実現する方法の1つは、特別なプロキシIDを使用するようにインポートを書き換えることです。例えば、CommonJSファイルの`require("foo")`によるトランスパイルされたインポートは、特別なID `import "foo?require=true"`を持つ通常のインポートになる可能性があります。これにより、リゾルバープラグインはこれを認識します。

ただし、ここでの問題は、このプロキシIDが、実際にはファイルに対応しないため、他のリゾルバーに渡されたときに意図しない副作用を引き起こす可能性があることです。さらに、IDがプラグイン `A` によって作成され、解決がプラグイン `B` で行われる場合、プラグイン `A` は `B` なしでは使用できないという依存関係がこれらのプラグイン間に作成されます。

カスタムリゾルバーオプションは、`this resolve`を介してモジュールを手動で解決するときに、プラグインの追加オプションを渡すことを可能にすることで、ここで解決策を提供します。これは、IDを変更せずに、意図されたターゲットプラグインが存在しない場合に、他のプラグインがモジュールを正しく解決する能力を損なうことなく行われます。

js
function requestingPlugin() {
	return {
		name: 'requesting',
		async buildStart() {
			const resolution = await this.resolve('foo', undefined, {
				custom: { resolving: { specialResolution: true } }
			});
			console.log(resolution.id); // "special"
		}
	};
}

function resolvingPlugin() {
	return {
		name: 'resolving',
		resolveId(id, importer, { custom }) {
			if (custom.resolving?.specialResolution) {
				return 'special';
			}
			return null;
		}
	};
}

カスタムオプションは、解決プラグインのプラグイン名に対応するプロパティを使用して追加する必要があるという規則に注意してください。どのオプションを尊重するかを指定するのは、解決プラグインの責任です。

カスタムモジュールメタデータ

プラグインは、`resolveId``load`、および`transform`フックを介して自身および他のプラグインによって設定でき、`this.getModuleInfo``this.load`、および`moduleParsed`フックを介してアクセスできるカスタムメタデータでモジュールを注釈できます。このメタデータは常にJSON.stringify可能である必要があり、ウォッチモードなどでキャッシュに保持されます。

js
function annotatingPlugin() {
	return {
		name: 'annotating',
		transform(code, id) {
			if (thisModuleIsSpecial(code, id)) {
				return { meta: { annotating: { special: true } } };
			}
		}
	};
}

function readingPlugin() {
	let parentApi;
	return {
		name: 'reading',
		buildEnd() {
			const specialModules = Array.from(this.getModuleIds()).filter(
				id => this.getModuleInfo(id).meta.annotating?.special
			);
			// do something with this list
		}
	};
}

データを追加または変更するプラグインは、この場合は `annotating` であるプラグイン名に対応するプロパティを使用する必要があるという規則に注意してください。一方、どのプラグインも `this.getModuleInfo` を介して他のプラグインからすべてのメタデータを読み取ることができます。

複数のプラグインがメタデータを追加したり、メタデータが異なるフックで追加されたりする場合、これらの `meta` オブジェクトは浅くマージされます。つまり、プラグイン `first` がresolveIdフックで`{meta: {first: {resolved: "first"}}}`を追加し、loadフックで`{meta: {first: {loaded: "first"}}}`を追加し、プラグイン `second` が`transform`フックで`{meta: {second: {transformed: "second"}}}`を追加した場合、結果の `meta` オブジェクトは`{first: {loaded: "first"}, second: {transformed: "second"}}`になります。ここで、`resolveId`フックの結果は、プラグインが両方ともトップレベルのプロパティ `first` の下に格納していたため、`load`フックの結果によって上書きされます。一方、他のプラグインの `transform` データは、その横に配置されます。

モジュールの `meta` オブジェクトは、Rollupがモジュールのロードを開始するとすぐに作成され、モジュールのライフサイクルフックごとに更新されます。このオブジェクトへの参照を保存すると、手動で更新することもできます。まだロードされていないモジュールのメタオブジェクトにアクセスするには、`this.load` を介してモジュールの作成とロードをトリガーできます。

js
function plugin() {
	return {
		name: 'test',
		buildStart() {
			// trigger loading a module. We could also pass an initial
			// "meta" object here, but it would be ignored if the module
			// was already loaded via other means
			this.load({ id: 'my-id' });
			// the module info is now available, we do not need to await
			// this.load
			const meta = this.getModuleInfo('my-id').meta;
			// we can also modify meta manually now
			meta.test = { some: 'data' };
		}
	};
}

プラグイン間の直接通信

その他の種類のプラグイン間通信には、以下のパターンをお勧めします。`api` が今後のプラグインフックと競合することはないことに注意してください。

js
function parentPlugin() {
	return {
		name: 'parent',
		api: {
			//...methods and properties exposed for other plugins
			doSomething(...args) {
				// do something interesting
			}
		}
		// ...plugin hooks
	};
}

function dependentPlugin() {
	let parentApi;
	return {
		name: 'dependent',
		buildStart({ plugins }) {
			const parentName = 'parent';
			const parentPlugin = plugins.find(
				plugin => plugin.name === parentName
			);
			if (!parentPlugin) {
				// or handle this silently if it is optional
				throw new Error(
					`This plugin depends on the "${parentName}" plugin.`
				);
			}
			// now you can access the API methods in subsequent hooks
			parentApi = parentPlugin.api;
		},
		transform(code, id) {
			if (thereIsAReasonToDoSomething(id)) {
				parentApi.doSomething(id);
			}
		}
	};
}

MITライセンスの下でリリースされています。