插件开发
插件概述
Rollup 插件是一个对象,具有 属性、构建钩子 和 输出生成钩子 中的一个或多个,并遵循我们的 约定。插件应作为一个导出一个函数的包进行发布,该函数可以使用插件特定的选项进行调用并返回此类对象。
插件允许你通过例如在打包之前进行转译代码或在node_modules文件夹中查找第三方模块来自定义 Rollup 的行为。有关如何使用它们的示例,请参见 使用插件。
插件列表可以在 github.com/rollup/awesome 上找到。如果你想给某个插件提交建议,请提交一个 Pull Request。
一个简单的示例
以下插件将拦截任何不通过访问文件系统的 virtual-module 导入。例如,如果你想在浏览器中使用 Rollup,则需要这样做。它甚至可以用来替换入口点,如示例所示。
// @filename: rollup-plugin-my-example.js
// ---cut-start---
/** @returns {import('rollup').Plugin} */
// ---cut-end---
export default function myExample () {
return {
name: 'my-example', // 此名称将出现在警告和错误中
resolveId ( source ) {
if (source === 'virtual-module') {
// 这表示 rollup 不应询问其他插件或
// 从文件系统检查以找到此 ID
return source;
}
return null; // 其他ID应按通常方式处理
},
load ( id ) {
if (id === 'virtual-module') {
// "virtual-module"的源代码
return 'export default "This is virtual!"';
}
return null; // 其他ID应按通常方式处理
}
};
}
// @filename: rollup.config.js
import myExample from './rollup-plugin-my-example.js';
export default ({
input: 'virtual-module', // 由我们的插件解析
plugins: [myExample()],
output: [{
file: 'bundle.js',
format: 'es'
}]
});
约定
- 插件应该有一个明确的名称,并以
rollup-plugin-作为前缀。 - 在
package.json中包含rollup-plugin关键字。 - 插件应该被测试,我们推荐 mocha 或 ava,它们支持 Promise。
- 可能的话,使用异步方法,例如
fs.readFile而不是fs.readFileSync - 用英文文档描述你的插件。
- 确保如果适当,你的插件输出正确的源映射。
- 如果插件使用“虚拟模块”(例如用于辅助函数),请使用
\0前缀模块 ID。这可以防止其他插件尝试处理它。
属性
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)。// ---cut-start---
/** @returns {import('rollup').Plugin} */
// ---cut-end---
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 等待所有先前插件的结果,然后执行插件钩子,然后再次并行运行剩余的插件。例如,当你有插件A、B、C、D、E,它们都实现了相同的并行钩子,并且中间插件C具有sequential: true时,Rollup 将首先并行运行A + B,然后单独运行C,然后再次并行运行D + E。当你需要在不同的
writeBundle钩子中运行多个命令行工具并相互依赖时,这可能很有用(请注意,如果可能,建议在顺序generateBundle钩子中添加/删除文件,这样更快,适用于纯内存构建,并允许其他内存构建插件查看文件)。你可以将此选项与order结合使用进行排序。import path from 'node:path';
import { readdir } from 'node:fs/promises';
// ---cut-start---
/** @returns {import('rollup').Plugin} */
// ---cut-end---
export default function getFilesOnDisk() {
return {
name: 'getFilesOnDisk',
writeBundle: {
sequential: true,
order: 'post',
async handler({ dir }) {
const topLevelFiles = await readdir(path.resolve(dir));
console.log(topLevelFiles);
}
}
};
} -
filter
仅当指定的过滤器返回真值时,才运行此插件钩子。这个属性只对resolveId,load,transform可用。code过滤器只对transform钩子可用。id过滤器,除了resolveId钩子,支持 picomatch 模式。type StringOrRegExp = string | RegExp;
type StringFilter<Value = StringOrRegExp> =
| MaybeArray<Value>
| {
include?: MaybeArray<Value>;
exclude?: MaybeArray<Value>;
};
interface HookFilter {
id?: StringFilter;
code?: StringFilter;
}/** @returns {import('rollup').Plugin} */
// ---cut---
export default function jsxAdditionalTransform() {
return {
name: 'jsxAdditionalTransform',
transform: {
filter: {
id: '*.jsx',
code: '<Custom'
},
handler(code) {
// transform <Custom /> here
}
}
};
}
构建钩子在构建阶段运行,该阶段由 rollup.rollup(inputOptions) 触发。它们主要涉及在 Rollup 处理输入文件之前定位、提供和转换输入文件。构建阶段的第一个钩子是 options,最后一个钩子始终是 buildEnd。如果有构建错误,则在此之后将调用 closeBundle。
parallel
sequential
first
async
sync
此外,在监视模式下,watchChange 钩子可以在任何时候触发,以通知当前运行生成输出后将触发新的运行。另外,当监视器关闭时,closeWatcher 钩子将被触发。
有关在输出生成阶段运行以修改生成的输出的钩子,请参见 输出生成钩子。
buildEnd
| 类型: | (error?: Error) => void |
| 类别: | async, parallel |
| 上一个钩子: | moduleParsed、resolveId 或 resolveDynamicImport |
| 下一个钩子: | 输出生成阶段的 outputOptions,因为这是构建阶段的最后一个钩子 |
在 Rollup 完成产物但尚未调用 generate 或 write 之前调用;也可以返回一个 Promise。如 果在构建过程中发生错误,则将其传递给此钩子。
buildStart
| 类型: | (options: InputOptions) => void |
| 类别: | async, parallel |
| 上一个钩子: | options |
| 下一个钩子: | 并行解析每个入口点的 resolveId |
在每个 rollup.rollup 构建上调用。当你需要访问传递给 rollup.rollup() 的选项时,建议使用此钩子,因为它考虑了所有 options 钩子的转换,并且还包含未设置选项的正确默认值。
closeWatcher
| 类型: | () => void |
| 类别: | async, parallel |
| 上一个/下一个钩子: | 此钩子可以在构建和输出生成阶段的任何时候触发。如果是这种情况,则当前构建仍将继续,但永远不会触发新的 watchChange 事件 |
在观察器进程即将关闭时通知插件,以便可以关闭所有打开的资源。如果返回一个 Promise,则 Rollup 将在关闭进程之前等待 Promise 解决。输出插件无法使用此钩子。
load
| 类型: | (id: string) => LoadResult |
| 类别: | async, first |
| 上一个钩子: | 已解析加载的 id 的 resolveId 或 resolveDynamicImport。此外,此钩子可以通过调用 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 必须是一个标准的 ESTree AST,每个节点都有 start 和 end 属性。如果转换不移动代码,则可以通过将 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 钩子可以进一步添加或替换该对象的属性。
你可以使用 this.getModuleInfo 在此钩子中查找 attributes、meta、moduleSideEffects 和 syntheticNamedExports 的先前值。
moduleParsed
| 类型: | (moduleInfo: ModuleInfo) => void |
| 类别: | async,parallel |
| 上一个钩子: | 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 |
| 上一个/下一个钩子: | 此钩子可以在任何时候触发。 |
请参阅 onLog 选项以了解可用的 Loglevel 值和 RollupLog 类型。
此钩子是一个函数,它接收并过滤由 Rollup 和插件生成的日志和警告,然后将它们传递给 onLog 选项或打印到控制台。
如果此钩子返回 false,日志将会被过滤。否则,日志将会传递给下一个插件的 onLog 钩子、onLog 选项或打印到控制台。插件还可以通过将日志传递给 this.error、this.warn、this.info 或 this.debug 并返回 false 来改变日志的级别或将日志转换为报错。请注意,与其他插件钩子不同,这些函数不会添加或更改日志的属性,例如插件名称。另外,由 onLog 钩子生成的日志不会再次传递给同一个插件的 onLog 钩子。如果另一个插件在其自己的 onLog 钩子中对这样的日志做出响应并生成了另一个日志,那么这个日志也不会传递给原始的 onLog 钩子。
// ---cut-start---
/** @returns {import('rollup').Plugin} */
// ---cut-end---
function plugin1() {
return {
name: 'plugin1',
buildStart() {
this.info({ message: 'Hey', pluginCode: 'SPECIAL_CODE' });
},
onLog(level, log) {
if (log.plugin === 'plugin1' && log.pluginCode === 'SPECIAL_CODE') {
// 我们根据 code 将日志转换为警告。这些警告
// 不会被传递回同一个插件,以避免无限循环,
// 但其他插件仍然会接收到。
this.warn(log);
return false;
}
}
};
}
// ---cut-start---
/** @returns {import('rollup').Plugin} */
// ---cut-end---
function plugin2() {
return {
name: 'plugin2',
onLog(level, log) {
if (log.plugin === 'plugin1' && log.pluginCode === 'SPECIAL_CODE') {
// 你也可以在这个钩子中修改日志
log.meta = 'processed by plugin 2';
// 这会把日志转换回“info”级别。如果这是对
// 第一个插件的响应,为了避免无限循环,它将
// 不会被传递回之前的插件。如果两个插件都处于
// 激活状态,并且第二个插件位于第一个插件之后,
// 则该日志将成为一个 info 级别的日志
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 的选项对象。返回 null 不会替换任何内容。如果只需要读取选项,则建议使用 buildStart 钩子,因为该钩子可以访问所有 options 钩子的转换考虑后的选项。
如 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;
::: tip
返回类型 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 参数。
例如,你可以将其用作为入口点定义自定义代理模块的机制。以下插件将所有入口点代理到注入 polyfill 导入的模块中。
// 我们在 polyfill id 前面加上 \0,
// 以告诉其他插件不要尝试加载或转换它
const POLYFILL_ID = '\0polyfill';
const PROXY_SUFFIX = '?inject-polyfill-proxy';
// ---cut-start---
/** @returns {import('rollup').Plugin} */
// ---cut-end---
function injectPolyfillPlugin() {
return {
name: 'inject-polyfill',
async resolveId(source, importer, options) {
if (source === POLYFILL_ID) {
// 对于polyfill,必须始终考虑副作用
// 否则使用
// "treeshake.moduleSideEffects: false"
// 这样可能会阻止包含polyfill
return { id: POLYFILL_ID, moduleSideEffects: true };
}
if (options.isEntry) {
// 决定实际的入口点是什么。
const resolution = await this.resolve(source, importer, options);
// 如果它不能被解析或是外部的,就返回它
// 这样 Rollup 就可以显示一个错误
if (!resolution || resolution.external) return resolution;
// 在代理的加载钩子中,我们需要知道入口点是否有默认导出。
// 然而,我们不再拥有完整的“resolution”对象,
// 该对象可能包含来自其他插件的元数据,
// 这些元数据仅在第一次加载时添加。
// 因此,我们在此处触发加载
const moduleInfo = await this.load(resolution);
// 我们需要确保原始入口点中的副作用
// 即使对于 treeshake.moduleSideEffects: false
// 也要被考虑。
// “moduleSideEffects”是 ModuleInfo 上的可写属性。
moduleInfo.moduleSideEffects = true;
// 重要的是,新的入口点不以 \0 开头
// 并且与原始入口点具有相同的目录
// 以避免破坏相对外部引用的生成。
// 同时保留名称并仅在末尾添加一个“?query”
// 确保 preserveModules
// 为此入口点生成原始入口点名称。
return `${resolution.id}${PROXY_SUFFIX}`;
}
return null;
},
load(id) {
if (id === POLYFILL_ID) {
// 用实际的 polyfill 替换
return "console.log('polyfill');";
}
if (id.endsWith(PROXY_SUFFIX)) {
const entryId = id.slice(0, -PROXY_SUFFIX.length);
// 我们知道 ModuleInfo.hasDefaultExport 是可靠的
// 因为我们在 resolveId 中等待了 this.load
const { hasDefaultExport } = this.getModuleInfo(entryId);
let code =
`import ${JSON.stringify(POLYFILL_ID)};` +
`export * from ${JSON.stringify(entryId)};`;
// 命名空间重新导出不会重新导出默认值
// 因此我们需要特殊处理
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 应被视为外部模块,不包含在产物中。如果这发生在相对导入中,则会像使用 external 选项时一样重新规范化 id。
如果返回一个对象,则可以将导入解析为不同的 id,同时将其从产物中排除。这使你可以将依赖项替换为外部依赖项,而无需用户手动通过 external 选项将它们挪到产物“之外” :
// ---cut-start---
/** @returns {import('rollup').Plugin} */
// ---cut-end---
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。可以通过传递 external: "relative" 来覆盖此选择,以始终将绝对 id 转换为相对 id,或者传递 external: "absolute" 来保持其为绝对 id。当返回一个对象时,相对的外部 id,即以 ./ 或 ../ 开头的 id,将 不会 被内部转换为绝对 id 并在输出中转换回相对 id,而是不变地包含在输出中。如果你希望相对 id 被重新规范化和去重,请将绝对文件系统位置作为 id 返回,并选择 external: "relative"。
如果在解析模块 id 的第一个钩子中返回 false 且没有其他模块从此模块导入任何内容,则即使该模块具有副作用,该模块也不会被包含。如果返回 true,Rollup 将使用其默认算法来包含模块中具有副作用的所有语句(例如修改全局或导出变量)。如果返回 "no-treeshake",则将关闭此模块的除屑,并且即使它为空,它也将包含在生成的块之一中。如果返回 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 选项的影响,请参见 synthetic named exports。如果返回 null 或省略标志,则 syntheticNamedExports 将默认为 false。load 和 transform 钩子可以覆盖此选项。
有关如何使用 meta 选项,请参见 custom module meta-data。如果返回 null 或省略选项,则 meta 将默认为空对象。load 和 transform 钩子可以添加或替换此对象的属性。
注意,虽然 resolveId 将为模块的每个导入调用一次,并且因此可 以多次解析为相同的 id,但是 external、attributes、meta、moduleSideEffects 或 syntheticNamedExports 的值只能在加载模块之前设置一次。原因是在此调用之后,Rollup 将继续使用该模块的 load 和 transform 钩子,这些钩子可能会覆盖这些值,并且如果它们这样做,则应优先考虑。
当通过 this.resolve 从插件触发此钩子时,可以向此钩子传递自定义选项对象。虽然此对象将不被修改,但插件应遵循添加具有对象的 custom 属性的约定,其中键对应于选项所针对的插件的名称。有关详细信息,请参见 custom resolver options。
在观察模式下或者明确使用缓存时,缓存模块的已解析导入也会从缓存中获取,而不是再次通过 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 明确使用),如果在 load 钩子之后,加载的 code 与缓存副本的代码相同,则 Rollup 将跳过模块的 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 必须是一个标准的 ESTree AST,每个节点都有 start 和 end 属性。如果转换不移动代码,你可以通过将 map 设置为 null 来保留现有的 sourcemaps。否则,你可能需要生成源映射。请参阅 源代码转换 部分。
请注意,在观察模式下或明确使用缓存时,当重新构建时,此钩子的结果会被缓存,仅当模块的 code 发生更改或上次触发此钩子时添加了通过 this.addWatchFile 或 this.emitFile 添加的文件时,才会再次触发该模块的钩子。
在所有其他情况下,将触发 shouldTransformCachedModule 钩子,该钩子可以访问缓存的模块。从 shouldTransformCachedModule 返回 true 将从缓存中删除该模块,并再次调用 transform。
你还可以使用返回值的对象形式来配置模块的其他属性。请注意,可能只返回属性而没有代码转换。
如果对于 moduleSideEffects 返回 false,并且没有其他模块从该模块导入任何内容,则即使该模块具有副作用,该模块也不会被包含。
如果返回 true,则 Rollup 将使用其默认算法,包括模块中具有副作用的所有语句(例如修改全局或导出变量)。
如果返回 "no-treeshake",则将关闭此模块的除屑优化,并且即使为空,它也将包含在生成的块之一中。
如果返回 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 钩子或最终默认为空对象确定。
你可以使用 this.getModuleInfo 在此钩子中查找 assertions、meta、moduleSideEffects 和 syntheticNamedExports 的先前值。
watchChange
| 类型: | watchChange: (id: string, change: {event: 'create' | 'update' | 'delete'}) => void |
| 类别: | async, parallel |
| 上一个/下一个钩子: | 此钩子可以在构建和输出生成阶段的任何时候触发。如果是这种情况,则当前构建仍将继续,但会安排一个新的构建在当前构建完成后开始,从 options 开始 |
每当 Rollup 在 --watch 模式下检测到监控文件发生变化时,会通知插件。如果当前正在运行构建,则在构建完成后调用此挂钩。每次有文件变化时都会被调用一次。如果返回一个 Promise,Rollup 将等待 Promise 解决后再安排另一次构建。此挂钩不能由输出插件使用。第二个参数包含变更事件的额外细节。如果您需要在文件发生变化时立即收到通知,可以使用配置选项 watch.onInvalidate。
输出生成钩子
输出生成钩子可以提供有关生成的产物的信息并在构建完成后修改构建。它们的工作方式和类型与 构建钩子 相同,但是对于每个调用 bundle.generate(outputOptions) 或 bundle.write(outputOptions),它 们都会单独调用。仅使用输出生成钩子的插件也可以通过输出选项传递,并且因此仅针对某些输出运行。
输出生成阶段的第一个钩子是 outputOptions,最后一个钩子是 generateBundle(如果通过 bundle.generate(...) 成功生成输出),writeBundle(如果通过 bundle.write(...) 成功生成输出),或 renderError(如果在输出生成期间的任何时候发生错误)。
parallel
sequential
first
async
sync
此外,closeBundle 可以作为最后一个钩子调用,但是用户有责任手动调用 bundle.close() 来触发此操作。CLI 将始终确保这一点。
augmentChunkHash
| 类型: | (chunkInfo: RenderedChunk) => string |
| 类别: | sync, sequential |
| 上一个钩子: | renderChunk |
| 下一个钩子: | 如果还有其他需要处理的块,则为 renderChunk,否则为 generateBundle |
查看 renderChunk 钩子以了解 RenderedChunk 类型。
可用于增加单个块的哈希值。为每个 Rollup 输出块调用。返回假值不会修改哈希值。如果返回真值,就会被传递给 hash.update。RenderedChunk 类型是 generateBundle 中 OutputChunk 类型的简化版本,它不包括 code 和 map,并在文件名中使用哈希的占位符。
以下插件将使用当前时间戳使块 foo 的哈希无效:
// ---cut-start---
/** @returns {import('rollup').Plugin} */
// ---cut-end---
function augmentWithDatePlugin() {
return {
name: 'augment-with-date',
augmentChunkHash(chunkInfo) {
if (chunkInfo.name === 'foo') {
return Date.now().toString();
}
}
};
}
banner
| 类型: | string | ((chunk: RenderedChunk) => string) |
| 类别: | async, sequential |
| 上一个钩子: | resolveFileUrl 用于每个 import.meta.ROLLUP_FILE_URL_referenceId 的使用和 resolveImportMeta 用于当前块中所有其他 import.meta 访问 |
| 下一个钩子: | 如果有下一个块中的动态导入表达式,则为 renderDynamicImport,否则为第一个块的 renderChunk |
参见 output.banner/output.footer。
closeBundle
| 类型: | closeBundle: (error?: Error) => Promise<void> | void |
| 类别: | async,parallel |
| 上一个钩子: | 如果有构建错误,则为 buildEnd,否则为调用 bundle.close(),在这种情况下,这将是最后一个触发的钩子 |
可用于清理可能正在运行的任何外部服务。Rollup 的 CLI 将确保在每次运行后调用此钩子,但是 JavaScript API 的用户有责任在生成产物后手动调用 bundle.close()。因此,任何依赖此功能的插件都应在其文档中仔细提到这一点。
如果插件想要在监视模式下保留资源,则可以在此钩子中检查 this.meta.watchMode 并在 closeWatcher 中执行必要的监视模式清理。
如果在构建过程或执行 buildEnd 钩子时发生错误,那么这个错误会作为第一个参数传递给这个钩子。
footer
| 类型: | string | ((chunk: RenderedChunk) => string) |
| 类别: | async, sequential |
| 上一个钩子: | resolveFileUrl 用于每个 import.meta.ROLLUP_FILE_URL_referenceId 的使用和 resolveImportMeta 用于当前块中所有其他 import.meta 访问 |
| 下一个钩子: | 如果有下一个块中的动态导入表达式,则为 renderDynamicImport,否则为第一个块的 renderChunk |
参见 output.banner/output.footer。
generateBundle
| 类型: | (options: OutputOptions, bundle: { [fileName: string]: OutputAsset | OutputChunk }, isWrite: boolean) => void |
| 类别: | async, sequential |
| 上一个钩子: | augmentChunkHash |
| 下一个钩子: | writeBundle 如果输出是通过 bundle.write(...) 生成的,则为,否则这是输出生成阶段的最后一个钩子,并且可能再次跟随 outputOptions,如果生成了另一个输出。 |
interface OutputAsset {
fileName: string;
names: string[];
needsCodeReference: boolean;
originalFileNames: string[];
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 插件上下文函数。
::: danger
不要直接将静态资源添加到产物中。这会绕过 Rollup 用于跟踪静态资源的内部机制。它还可能导致你的静态资源缺少 Rollup 在内部依赖的重要属性,因此你的插件可能会因为 Rollup 的小版本更新而出现问题。
相反,请始终使用 this.emitFile。
:::
intro
| 类型: | string | ((chunk: RenderedChunk) => string) |
| 类别: | async, sequential |
| 上一个钩子: | resolveFileUrl 用于每个 import.meta.ROLLUP_FILE_URL_referenceId 的使用和 resolveImportMeta 用于当前块中所有其他 import.meta 访问 |
| 下一个钩子: | 如果有下一个块中的动态导入表达式,则为 renderDynamicImport,否则为第一个块的 renderChunk |