プラグイン開発
プラグインの概要
Rollupプラグインは、以下に説明するプロパティ、ビルドフック、および出力生成フックのうち1つ以上を持ち、規約に従うオブジェクトです。プラグインは、プラグイン固有のオプションを指定して呼び出すことができ、そのようなオブジェクトを返す関数をエクスポートするパッケージとして配布する必要があります。
プラグインを使用すると、バンドル前のコードのトランスパイルや、node_modules
フォルダー内のサードパーティ製モジュールの検索など、Rollupの動作をカスタマイズできます。プラグインの使用方法については、プラグインの使用を参照してください。
プラグインのリストはgithub.com/rollup/awesomeにあります。プラグインに関する提案がある場合は、プルリクエストを送信してください。
簡単な例
次のプラグインは、ファイルシステムにアクセスすることなく、virtual-module
のインポートをインターセプトします。これは、たとえば、ブラウザでRollupを使用する場合に必要です。例に示すように、エントリポイントを置き換えるために使用することもできます。
// 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.json
にrollup-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
)で実行します。jsexport 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は以前のすべてのプラグインの結果を待ってから、プラグインフックを実行し、残りのプラグインを再び並行して実行します。たとえば、同じ並列フックを実装するプラグインA
、B
、C
、D
、E
があり、中央のプラグインC
にsequential: true
がある場合、Rollupは最初にA + B
を並行して実行し、次にC
を単独で実行し、次にD + E
を並行して実行します。これは、互いに依存する異なる
writeBundle
フックで複数のコマンドラインツールを実行する必要がある場合に役立ちます(可能であれば、高速で、純粋なインメモリビルドで動作し、他のインメモリビルドプラグインがファイルを確認できるようにする、順次generateBundle
フックでファイルを追加/削除することをお勧めします)。このオプションをorder
と組み合わせて、追加の並べ替えを行うことができます。jsimport { 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
がその後呼び出されます。
さらに、ウォッチモードでは、watchChange
フックをいつでもトリガーして、現在の実行が出力を生成したら新しい実行がトリガーされることを通知できます。また、ウォッチャーが閉じると、closeWatcher
フックがトリガーされます。
生成された出力を変更するための出力生成フェーズ中に実行されるフックについては、出力生成フックを参照してください。
buildEnd
タイプ | (error?: Error) => void |
---|---|
種類 | 非同期、並列 |
前 | moduleParsed 、resolveId 、または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 。 |
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である必要があります。変換がコードを移動しない場合は、map
を null
に設定して既存のソースマップを保持できます。それ以外の場合は、ソースマップを生成する必要がある場合があります。ソースコード変換のセクションを参照してください。
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
フックは、このオブジェクトのプロパティを追加または置換することもできます。
このフック内で attributes
、meta
、moduleSideEffects
、および syntheticNamedExports
の以前の値を確認するには、this.getModuleInfo
を使用できます。
moduleParsed
タイプ | (moduleInfo: ModuleInfo) => void |
---|---|
種類 | 非同期、並列 |
前 | 現在処理中のファイルが変換された場所の transform |
次 | 存在する場合は、すべての検出された静的インポートと動的インポートを並行して解決するための resolveId および resolveDynamicImport 、それ以外の場合は buildEnd |
このフックは、モジュールがRollupによって完全に解析されるたびに呼び出されます。このフックに渡される情報については、this.getModuleInfo
を参照してください。
transform
フックとは対照的に、このフックはキャッシュされず、キャッシュされたモジュールとその他のモジュールの両方に関する情報を取得するために使用できます。これには、meta
プロパティ、code
、および ast
の最終的な形状が含まれます。
このフックは、moduleInfo.importedIds
、moduleInfo.dynamicallyImportedIds
、moduleInfo.importedIdResolutions
、および moduleInfo.dynamicallyImportedIdResolutions
の情報が完全かつ正確になるように、すべてのインポートが解決されるまで待機します。ただし、インポート側のモジュールに関する情報は、後で追加のインポーターが検出される可能性があるため、不完全である可能性があることに注意してください。この情報が必要な場合は、buildEnd
フックを使用してください。
onLog
タイプ | (level: LogLevel, log: RollupLog) => boolean | null |
---|---|
種類 | sync, sequential |
前/次 | このフックは、いつでもトリガーできます。 |
使用可能な Loglevel
値と RollupLog
タイプについては、onLog
オプションを参照してください。
Rollupおよびプラグインによって生成されたログと警告を、onLog
オプションに渡すか、コンソールに出力する前に受信およびフィルター処理する関数。
このフックから false
が返された場合、ログはフィルター処理されます。それ以外の場合、ログは次のプラグインの onLog
フック、onLog
オプションに渡されるか、コンソールに出力されます。プラグインは、ログのログレベルを変更したり、ログを this.error
、this.warn
、this.info
、または this.debug
に渡して false
を返すことで、ログをエラーに変えることもできます。ログにプラグイン名などを追加する他のプラグインフックとは異なり、これらの関数はログのプロパティを追加または変更しないことに注意してください。さらに、onLog
フックによって生成されたログは、同じプラグインの onLog
フックに返されません。別のプラグインが独自の onLog
フックでそのようなログに応じてログを生成した場合、このログも元の onLog
フックに渡されません。
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.error
、this.warn
、this.info
、および this.debug
のみです。
options
タイプ | (options: InputOptions) => InputOptions | null |
---|---|
種類 | async, sequential |
前 | これはビルドフェーズの最初のフックです |
次 | buildStart |
rollup.rollup
に渡される options オブジェクトを置換または操作します。null
を返しても何も置換されません。オプションを読み取るだけの場合は、すべての options
フックからの変換が考慮された後のオプションにアクセスできる buildStart
フックを使用することをお勧めします。
onLog
フックと同様に、このフックはRollupが完全に構成される前に実行されるため、ほとんどの プラグインコンテキストユーティリティ関数にアクセスできません。サポートされているプロパティは、this.meta
と、ログとエラー用の this.error
、this.warn
、this.info
、および this.debug
のみです。
resolveDynamicImport
タイプ | ResolveDynamicImportHook |
---|---|
種類 | async, first |
前 | インポートファイル用の moduleParsed |
次 | フックがまだロードされていないIDで解決した場合の load 、動的インポートに文字列が含まれていて、フックで解決されなかった場合の resolveId 、それ以外の場合は buildEnd |
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 |
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
は、インポート文に書かれているとおりのインポート対象です。つまり、
import { foo } from '../bar.js';
の場合、sourceは"../bar.js"
になります。
importer
は、インポートしているモジュールの完全に解決されたIDです。エントリポイントを解決するとき、importerは通常undefined
になります。例外は、this.emitFile
を介して生成されたエントリポイントで、ここではimporter
引数を指定できます。
これらの場合、isEntry
オプションは、ユーザー定義のエントリポイント、エミットされたチャンク、またはthis.resolve
コンテキスト関数にisEntry
パラメータが指定されたかどうかを教えてくれます。
これは、例えば、エントリポイントに対してカスタムプロキシモジュールを定義するメカニズムとして使用できます。次のプラグインは、すべてのエントリポイントをプロキシして、ポリフィルインポートを注入します。
// 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
オプションを介して手動で「外部」とマークする必要なく、依存関係を外部依存関係に置き換えることができます。
function externalizeDependencyPlugin() {
return {
name: 'externalize-dependency',
resolveId(source) {
if (source === 'my-dependency') {
return { id: 'my-dependency-develop', external: true };
}
return null;
}
};
}
external
がtrue
の場合、絶対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
が返された場合、またはフラグが省略された場合、moduleSideEffects
はtreeshake.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
を何度も解決できますが、external
、attributes
、meta
、moduleSideEffects
、またはsyntheticNamedExports
の値は、モジュールがロードされる前に一度だけ設定できることに注意してください。その理由は、この呼び出しの後、Rollupは、これらの値を上書きする可能性があり、そうする場合は優先されるべきモジュールのload
およびtransform
フックを続行するためです。
this.resolve
を介してプラグインからこのフックをトリガーする場合、カスタムオプションオブジェクトをこのフックに渡すことができます。このオブジェクトは変更されずに渡されますが、プラグインは、オプションが意図されているプラグインの名前に対応するキーを持つオブジェクトを使用して、custom
プロパティを追加する規則に従う必要があります。詳細については、カスタムリゾルバーオプションを参照してください。
ウォッチモードの場合、またはキャッシュを明示的に使用している場合、キャッシュされたモジュールの解決済みインポートもキャッシュから取得され、resolveId
フックを介して再度決定されることはありません。これを防ぐには、そのモジュールに対してshouldTransformCachedModule
フックからtrue
を返すことができます。これにより、モジュールとそのインポート解決がキャッシュから削除され、transform
とresolveId
が再度呼び出されます。
shouldTransformCachedModule
タイプ | ShouldTransformCachedModuleHook |
---|---|
種類 | async, first |
前 | load 。キャッシュされたファイルが、キャッシュされたバージョンとコードを比較するためにロードされた場所 |
次 | プラグインがtrue を返さない場合はmoduleParsed 、それ以外の場合はtransform |
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 |
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である必要があります。変換でコードが移動しない場合は、map
をnull
に設定することで既存のソースマップを保持できます。それ以外の場合は、ソースマップを生成する必要がある場合があります。ソースコード変換に関するセクションを参照してください。
ウォッチモードの場合、またはキャッシュを明示的に使用している場合、このフックの結果は再構築時にキャッシュされ、モジュールの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
フック、または最終的には空のオブジェクトによって決定されます。
このフック内で attributes
、meta
、moduleSideEffects
、および 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
です。
さらに、closeBundle
は最後のフックとして呼び出すことができますが、これをトリガーするには、ユーザーが手動でbundle.close()
を呼び出す必要があります。CLIは、これが常に当てはまるようにします。
augmentChunkHash
タイプ | (chunkInfo: ChunkInfo) => string |
---|---|
種類 | sync, sequential |
前 | renderChunk |
次 | まだ処理する必要のある他のチャンクがある場合はrenderChunk 、それ以外の場合はgenerateBundle |
個々のチャンクのハッシュを拡張するために使用できます。Rollupの出力チャンクごとに呼び出されます。偽の値を返すと、ハッシュは変更されません。真の値をhash.update
に渡します。chunkInfo
は、generateBundle
の縮小版であり、code
とmap
がなく、ファイル名のハッシュにプレースホルダーを使用しています。
次のプラグインは、現在のタイムスタンプでチャンクfoo
のハッシュを無効にします。
function augmentWithDatePlugin() {
return {
name: 'augment-with-date',
augmentChunkHash(chunkInfo) {
if (chunkInfo.name === 'foo') {
return Date.now().toString();
}
}
};
}
banner
タイプ | 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
でウォッチモードに必要なクリーンアップを実行できます。
footer
タイプ | 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 が続く場合があります。 |
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 、それ以外の場合は、以前に生成された出力に応じてgenerateBundle 、writeBundle 、または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 |
前 | 最後のチャンクのbanner 、footer 、intro 、outro |
次 | augmentChunkHash |
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
型を使用するチャンクに関する追加情報が含まれています。ただし、次の違いがあります。
code
とmap
は設定されていません。代わりに、このフックのcode
パラメータを使用してください。- ハッシュを含むすべての参照されるチャンクファイル名には、代わりにハッシュプレースホルダーが含まれます。これには、
fileName
、imports
、importedBindings
、dynamicImports
、およびimplicitlyLoadedBefore
が含まれます。このフックから返されたコードでこのようなプレースホルダーファイル名またはその一部を使用すると、RollupはgenerateBundle
の前に、プレースホルダーを実際のハッシュで置き換えます。これにより、ハッシュは参照されるすべてのファイルハッシュを含む、最終的に生成されたチャンクの実際のコンテンツを反映するようにします。
chunk
は可変であり、このフックで適用された変更は、他のプラグインおよび生成されたバンドルに伝播します。つまり、このフックでインポートまたはエクスポートを追加または削除する場合は、imports
、importedBindings
、および/またはexports
を更新する必要があります。
meta.chunks
には、Rollupが生成するすべてのチャンクに関する情報が含まれており、ハッシュのプレースホルダーを使用して、それらのChunkInfo
にアクセスできます。つまり、このフックでチャンクグラフ全体を探索できます。
renderDynamicImport
タイプ | renderDynamicImportHook |
---|---|
種類 | 同期、最初 |
前 | これが最初のチャンクの場合はrenderStart 、それ以外の場合は前のチャンクのbanner 、footer 、intro 、outro |
次 | import.meta.ROLLUP_FILE_URL_referenceId の各使用にはresolveFileUrl 、現在のチャンクでimport.meta への他のすべてのアクセスにはresolveImportMeta |
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番目の引数として追加します。
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モジュールのインポート方法を参照してください。
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 |
次 | 現在のチャンクに対して並行してbanner 、footer 、intro 、outro |
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を除くすべての形式は、URL
とdocument
が利用可能なブラウザ環境で実行されることを前提としています。これに失敗した場合、またはより最適化されたコードを生成するために、このフックを使用してこの動作をカスタマイズできます。そのためには、次の情報が利用可能です。
chunkId
: このファイルが参照されているチャンクのID。チャンクファイル名にハッシュが含まれる場合、このIDには代わりにプレースホルダーが含まれます。Rollupは、生成されたコードに最終的に含まれる場合、このプレースホルダーを実際のファイル名に置き換えます。fileName
: 出力されたファイルのパスとファイル名。./
を先頭に付けずにoutput.dir
からの相対パス。これも名前内にハッシュを持つチャンクの場合、代わりにプレースホルダーが含まれます。format
: レンダリングされた出力形式。moduleId
: このファイルが参照されている元のモジュールのID。特定のアセットを条件付きで異なる方法で解決するのに役立ちます。referenceId
: ファイルの参照ID。relativePath
: ファイルが参照されているチャンクからの、出力されたファイルのパスとファイル名。このパスには先頭に./
は含まれませんが、先頭に../
が含まれる場合があります。
次のプラグインは常にすべてのファイルを現在のドキュメントからの相対パスで解決します。
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 |
次 | 現在のチャンクに対して並行してbanner 、footer 、intro 、outro |
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を除くすべての形式は、URL
とdocument
が利用可能なブラウザ環境で実行されることを前提としていることに注意してください。他のプロパティについては、import.meta.someProperty
はundefined
に置き換えられ、import.meta
はurl
プロパティを含むオブジェクトに置き換えられます。
この動作は、このフックを使用して、ESモジュールについても変更できます。import.meta<.someProperty>
が発生するたびに、このフックはプロパティの名前、またはimport.meta
に直接アクセスする場合はnull
を指定して呼び出されます。例えば、次のコードは、元のモジュールの相対パスを使用してimport.meta.url
を現在の作業ディレクトリに解決し、実行時にこのパスを現在のドキュメントのベースURLに対して再度解決します。
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.skipWrite
がtrue
に設定されている場合、出力生成フックで使用しても効果はありません。
注意: 通常、ウォッチモードでは、再構築の速度を向上させるために、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"
に設定されている場合にのみ処理され、それ以外の場合は何も行いません。したがって、プラグインに役立つデバッグログを追加することをお勧めします。これにより、問題を発見するのに役立つ一方で、デフォルトでは効率的にミュートされます。ログを生成するためにコストのかかる計算を実行する必要がある場合は、ログが実際に処理される場合にのみこれらの計算が実行されるように、関数形式を使用してください。
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 |
---|
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]
の代替として使用され、競合を回避するためにファイル名の末尾に一意の番号が追加される場合があります。name
もfileName
も指定されていない場合、デフォルトの名前が使用されます。事前構築済みチャンクには、常に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
の前にプレースホルダーを実際のハッシュに置き換え、ハッシュが、参照されているすべてのファイルハッシュを含む、最終的に生成されたチャンクの実際のコンテンツを反映するようにします。
type
が chunk
の場合、これは指定されたモジュール id
をエントリポイントとして新しいチャンクを生成します。これを解決するために、id
は resolveId
から始まる通常の開始点と同じように、ビルドフックを通過します。importer
が提供されている場合、これは resolveId
の 2 番目のパラメーターとして機能し、相対パスを適切に解決するために重要です。提供されていない場合、パスは現在の作業ディレクトリを基準に解決されます。preserveSignature
の値が提供されている場合、これはこの特定のチャンクの preserveEntrySignatures
をオーバーライドします。
これにより、グラフ内に重複したモジュールは生成されません。代わりに、必要に応じて、既存のチャンクが分割されるか、再エクスポートを含むファサードチャンクが作成されます。指定された fileName
を持つチャンクは常に個別のチャンクを生成しますが、他の出力されたチャンクは、name
が一致しなくても、同じソースを持つ場合は既存のチャンクと重複排除される可能性があります。このようなチャンクが重複排除されない場合、output.chunkFileNames
名前パターンが使用されます。
デフォルトでは、Rollup は、出力されたチャンクが他のエントリポイントとは独立して実行され、場合によっては他のコードが実行される前であっても実行されると想定します。これは、出力されたチャンクが既存のエントリポイントと依存関係を共有している場合、Rollup はこれらのエントリポイント間で共有される依存関係のために追加のチャンクを作成することを意味します。implicitlyLoadedAfterOneOf
にモジュール ID の空でない配列を提供すると、Rollup に追加の情報を提供して、場合によってはこれを防ぐことで、その動作が変更されます。これらの ID は、id
プロパティと同じ方法で解決され、importer
プロパティが提供されている場合はそれを尊重します。Rollup は、出力されたチャンクが、implicitlyLoadedAfterOneOf
にある ID のいずれかにつながるエントリポイントの少なくとも1つがロード済みである場合にのみ実行されると想定するようになり、新しく出力されたチャンクが implicitlyLoadedAfterOneOf
のモジュールからの動的インポートを介してのみ到達可能である場合と同じチャンクを作成します。以下は、これを活用して、実行順序を尊重するように最適化されたチャンクを作成する、複数のスクリプトを含む単純な HTML ファイルを作成する例です。
// 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 はエラーをスローすることに注意してください。ユーザーによって定義されたエントリポイント、または以前に出力されたチャンクのエントリポイントのみを使用すると、常に機能します。
type
が prebuilt-chunk
の場合、code
パラメーターによって提供された固定コンテンツを持つチャンクを出力します。現時点では、チャンクの名前を提供するために fileName
も必要です。いくつかの変数をエクスポートする場合は、オプションの exports
を介してこれらをリストする必要があります。map
を介して、code
に対応するソースマップを提供できます。
インポートでプリビルドチャンクを参照するには、プリビルドチャンクはモジュールグラフの一部ではないため、resolveId
フックで「モジュール」を外部としてマークする必要があります。代わりに、チャンクメタデータを持つアセットのように動作します。
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']
});
}
};
}
これで、コードでプリビルドチャンクを参照できます。
import { foo } from './my-prebuilt-chunk.js';
現在、プリビルドチャンクの出力は基本的な機能です。フィードバックをお待ちしております。
type
が asset
の場合、これは指定された source
をコンテンツとして持つ任意の新しいファイルを出力します。ビルドフェーズ中にファイルを参照できるようにするために、this.setAssetSource(referenceId, source)
を介して source
の設定を後で延期し、生成フェーズ中に各出力に対して個別にソースを設定することができます。指定された fileName
を持つアセットは常に個別のファイルを生成しますが、name
が一致しなくても、他の出力されたアセットは、同じソースを持つ場合は既存のアセットと重複排除される可能性があります。fileName
を持たないアセットが重複排除されない場合、output.assetFileNames
名前パターンが使用されます。needsCodeReference
が true
に設定されており、このアセットが 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
フックでは、この関数は、警告のすべての追加プロパティを保持しながら、警告をエラーに変える簡単な方法です。
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
を指定できます。これは、pos
、loc
(標準の { file, line, column }
オブジェクト)、および frame
(場所を示すコードスニペット) でログを拡張するために使用される文字インデックスまたはファイル位置です。
this.getCombinedSourcemap
タイプ | () => SourceMap |
---|
以前のすべてのプラグインの結合されたソースマップを取得します。このコンテキスト関数は、transform
プラグインフックでのみ使用できます。
this.getFileName
タイプ | (referenceId: string) => string |
---|
this.emitFile
を介して出力されたチャンクまたはアセットのファイル名を取得します。ファイル名は outputOptions.dir
を基準にした相対パスになります。
this.getModuleIds
タイプ | () => IterableIterator<string> |
---|
現在のグラフ内のすべてのモジュール ID にアクセスできる Iterator
を返します。次のように反復処理できます
for (const moduleId of this.getModuleIds()) {
/* ... */
}
または、Array.from(this.getModuleIds())
を使用して配列に変換できます。
this.getModuleInfo
タイプ | (moduleId: string) => (ModuleInfo | null) |
---|
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
フックの前には不正確になる可能性のある、モジュールに関する現在利用可能な情報を表します。
id
とisExternal
は決して変わりません。code
、ast
、hasDefaultExport
、exports
、およびexportedBindings
は、解析後、つまりmoduleParsed
フックまたはthis.load
を待機した後にのみ使用できます。その時点で、それらは変更されなくなります。isEntry
がtrue
の場合、それは変更されなくなります。ただし、モジュールは、this.emitFile
を介して、またはプラグインがエントリポイントを解決するときにresolveId
フックでthis.load
を介して潜在的なエントリポイントを検査するために、解析後にエントリポイントになる可能性があります。したがって、transform
フックでこのフラグに依存することはお勧めしません。buildEnd
の後は変更されなくなります。- 同様に、
implicitlyLoadedAfterOneOf
は、this.emitFile
を介してbuildEnd
前にいつでも追加のエントリを受け取ることができます。 importers
、dynamicImporters
、およびimplicitlyLoadedBefore
は、新しいインポーターと暗黙的な依存関係が検出されると追加のエントリを受け取る空の配列として開始されます。buildEnd
の後は変更されなくなります。isIncluded
はbuildEnd
後にのみ使用でき、その時点で変更されなくなります。importedIds
、importedIdResolutions
、dynamicallyImportedIds
、およびdynamicallyImportedIdResolutions
は、モジュールが解析され、その依存関係が解決されたときに使用できます。これは、moduleParsed
フック、またはresolveDependencies
フラグを使用してthis.load
を待機したときの場合です。その時点で、それらは変更されなくなります。attributes
、meta
、moduleSideEffects
、および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
タイプ | ロード |
---|
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 に対応するモジュールをロードして解析し、追加のメタ情報があればモジュールに付与します。これにより、モジュールが別のモジュールによってインポートされた場合と同様の load
、transform
、moduleParsed
フックがトリガーされます。
これにより、resolveId
フックでモジュールをどのように解決するかを決定する前に、モジュールの最終的なコンテンツを調べることができ、例えば、代わりにプロキシモジュールを解決することができます。モジュールが後でグラフの一部になった場合、このコンテキスト関数を使用しても、モジュールが再び解析されることはないため、追加のオーバーヘッドは発生しません。シグネチャでは、this.resolve
の戻り値を、null
でも外部モジュールでもない限り、この関数に直接渡すことができます。
返される Promise は、モジュールが完全に変換および解析された後、インポートが解決される前に解決されます。つまり、結果の ModuleInfo
には、空の importedIds
、dynamicallyImportedIds
、importedIdResolutions
、および dynamicallyImportedIdResolutions
が含まれます。これにより、resolveId
フックで this.load
を待機する際のデッドロック状態を回避するのに役立ちます。importedIds
および dynamicallyImportedIds
に興味がある場合は、moduleParsed
フックを実装するか、resolveDependencies
フラグを渡すことができます。これにより、this.load
によって返される Promise は、すべての依存関係 ID が解決されるまで待機します。
attributes
、meta
、moduleSideEffects
、および syntheticNamedExports
オプションに関しては、resolveId
フックと同じ制限が適用されることに注意してください。これらの値は、モジュールがまだロードされていない場合にのみ有効です。したがって、まず this.resolve
を使用して、プラグインが resolveId
フックでこれらのオプションに特別な値を設定するかどうかを確認し、必要に応じてこれらのオプションを this.load
に渡すことが非常に重要です。以下の例は、特別なコードコメントを含むモジュールにプロキシモジュールを追加する方法を示しています。デフォルトのエクスポートの再エクスポートに関する特別な処理に注意してください。
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 用の単一のスタイルタグを作成することができます。
// 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 |
---|
interface ParseOptions {
allowReturnOutsideFunction?: boolean;
}
Rollup の内部 SWC ベースのパーサーを使用して、コードを ESTree 互換の AST に解析します。
allowReturnOutsideFunction
:true
の場合、関数外での return ステートメントを許可して、例えば CommonJS コードの解析をサポートします。
this.resolve
タイプ | 解決 |
---|
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 が返された場合、external
は true
の代わりに "absolute"
になります。
skipSelf
のデフォルトは true
です。したがって、this.resolve
が呼び出されたプラグインの resolveId
フックは、解決時にスキップされます。他のプラグイン自体が、元の this.resolve
呼び出しを処理中にまったく同じ source
と importer
を使用して resolveId
フックで this.resolve
を呼び出すと、元のプラグインの resolveId
フックもこれらの呼び出しでスキップされます。ここでの理由は、プラグインがこの時点で特定の source
と importer
の組み合わせを解決する方法を「知らない」と既に述べているためです。この動作を望まない場合は、skipSelf
を false
に設定し、必要に応じて独自の無限ループ防止メカニズムを実装してください。
custom
オプションを使用してプラグイン固有のオプションのオブジェクトを渡すこともできます。詳細については、カスタムリゾルバーオプション を参照してください。
ここで渡す isEntry
の値は、この呼び出しを処理する resolveId
フックに渡されます。それ以外の場合、インポーターがある場合は false
が渡され、ない場合は true
が渡されます。
attributes
にオブジェクトを渡すと、アサーション付きでインポートを解決するシミュレーションが実行されます。例えば、attributes: {type: "json"}
は、import "foo" assert {type: "json"}
を解決するシミュレーションを実行します。これは、この呼び出しを処理する resolveId
フックに渡され、最終的に返されるオブジェクトの一部になる可能性があります。
resolveId
フックからこの関数を呼び出す場合は、isEntry
、custom
、および 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
プロパティを持つオブジェクトにすることができます。
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_WARNING
の code
が付与されるため、pluginCode
に名前が変更されます。この動作を防ぐために、プラグインは代わりに、buildStart
フックに渡される正規化された onLog
オプションを使用できます。プラグインからこのオプションを呼び出すと、ログをプラグインの onLog
ハンドラーおよび onLog
または onwarn
ハンドラーに渡すときにプロパティは変更されません。
ログを生成するためにコストのかかる計算を行う必要がある場合は、string
または RollupLog
オブジェクトのいずれかを返す関数を渡すこともできます。この関数は、logLevel
オプションでログがフィルタリングされない場合にのみ呼び出されます。
// This will only run if the logLevel is set to "debug"
this.debug(() => generateExpensiveDebugLog());
transform
フックで使用する場合、現在のモジュールの id
も追加され、position
を指定できます。これは、pos
、loc
(標準の { file, line, column }
オブジェクト)、および frame
(場所を示すコードスニペット) でログを拡張するために使用される文字インデックスまたはファイル位置です。
logLevel
オプションが "silent"
に設定されている場合、このメソッドは何もしません。
ファイル URL
JS コード内からファイル URL 参照を参照するには、import.meta.ROLLUP_FILE_URL_referenceId
の置換を使用します。これにより、出力形式に依存するコードが生成され、ターゲット環境で出力されたファイルを指す URL が生成されます。CommonJS および UMD を除くすべての形式は、URL
および document
が利用可能なブラウザー環境で実行されることを前提としていることに注意してください。
次の例では、.svg
ファイルのインポートを検出し、インポートされたファイルをアセットとして出力し、例えば img
タグの src
属性として使用される URL を返します。
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};`;
}
}
};
}
使用方法
import logo from '../images/logo.svg';
const image = document.createElement('img');
image.src = logo;
document.body.appendChild(image);
場合によっては、このアセットを参照したコードは、次の例のように条件付きでのみ使用されます。
import logo from '../images/logo.svg';
if (COMPILER_FLAG) {
const image = document.createElement('img');
image.src = logo;
document.body.appendChild(image);
}
プラグインが COMPILER_FLAG
を false
に置き換えた場合、予期しない結果が発生します。参照されていないアセットは引き続き出力されますが、使用されません。この問題を解決するには、次のコードのように、this.emitFile
を呼び出すときに needsCodeReference
を true に設定します。
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` に設定されている場合にのみ機能することに注意してください。
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;
}
};
}
使用方法
// 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 を使用してください。)
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)空のソースマップを返します。
return {
code: transformedCode,
map: { mappings: '' }
};
変換がコードを移動しない場合は、`null` を返すことで既存のソースマップを保持できます。
return {
code: transformedCode,
map: null
};
他の人に役立つと思われるプラグインを作成した場合は、NPMに公開し、github.com/rollup/awesome に送信してください!
合成名前付きエクスポート
`resolveId`、`load`、または`transform`フックでモジュールの `syntheticNamedExports` オプションを設定することにより、欠落しているエクスポートのフォールバックエクスポートを指定できます。 `syntheticNamedExports` に文字列値が使用されている場合、このモジュールは、指定された名前の名前付きエクスポートのプロパティに、欠落している名前付きエクスポートの解決をフォールバックします。
dep.js:(`{syntheticNamedExports: '__synthetic'}`)
export const foo = 'explicit';
export const __synthetic = {
foo: 'foo',
bar: 'bar'
};
main.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を変更せずに、意図されたターゲットプラグインが存在しない場合に、他のプラグインがモジュールを正しく解決する能力を損なうことなく行われます。
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可能である必要があり、ウォッチモードなどでキャッシュに保持されます。
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` を介してモジュールの作成とロードをトリガーできます。
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` が今後のプラグインフックと競合することはないことに注意してください。
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);
}
}
};
}