Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# rollup changelog

## 4.38.0

_2025-03-29_

### Features

- Support `.filter` option in `resolveId`, `load` and `transform` hooks (#5882)

### Pull Requests

- [#5882](https://github.com/rollup/rollup/pull/5882): Add support for hook filters (@sapphi-red)
- [#5894](https://github.com/rollup/rollup/pull/5894): fix(deps): lock file maintenance minor/patch updates (@renovate[bot])
- [#5895](https://github.com/rollup/rollup/pull/5895): chore(deps): update dependency eslint-plugin-unicorn to v58 (@renovate[bot])

## 4.37.0

_2025-03-23_
Expand Down
29 changes: 29 additions & 0 deletions browser/LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,32 @@ Repository: https://github.com/rich-harris/magic-string
> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

---------------------------------------

## picomatch
License: MIT
By: Jon Schlinkert
Repository: micromatch/picomatch

> The MIT License (MIT)
>
> Copyright (c) 2017-present, Jon Schlinkert.
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.
2 changes: 1 addition & 1 deletion browser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rollup/browser",
"version": "4.37.0",
"version": "4.38.0",
"description": "Next-generation ES module bundler browser build",
"main": "dist/rollup.browser.js",
"module": "dist/es/rollup.browser.js",
Expand Down
38 changes: 37 additions & 1 deletion docs/plugin-development/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default ({
- `sequential`:如果有多个插件实现此钩子,则所有这些钩子将按指定的插件顺序运行。如果钩子是 `async`,则此类后续钩子将等待当前钩子解决后再运行。
- `parallel`:如果有多个插件实现此钩子,则所有这些钩子将按指定的插件顺序运行。如果钩子是 `async`,则此类后续钩子将并行运行,而不是等待当前钩子。

除了函数之外,钩子也可以是对象。在这种情况下,实际的钩子函数(或 `banner/footer/intro/outro` 的值)必须指定为 `handler`。这允许你提供更多的可选属性,以改变钩子的执行
除了函数之外,钩子也可以是对象。在这种情况下,实际的钩子函数(或 `banner/footer/intro/outro` 的值)必须指定为 `handler`。这允许你提供更多的可选属性,以改变或跳过钩子的执行

- `order: "pre" | "post" | null`<br> 如果有多个插件实现此钩子,则可以先运行此插件(`"pre"`),最后运行此插件(`"post"`),或在用户指定的位置运行(没有值或 `null`)。

Expand Down Expand Up @@ -145,6 +145,42 @@ export default ({
}
```

- `filter`<br> 仅当指定的过滤器返回真值时,才运行此插件钩子。这个属性只对 `resolveId`,`load`,`transform` 可用。`code` 过滤器只对 `transform` 钩子可用。`id` 过滤器,除了 `resolveId` 钩子,支持 [picomatch 模式](https://github.com/micromatch/picomatch#globbing-features)。

```ts
type StringOrRegExp = string | RegExp;
type StringFilter<Value = StringOrRegExp> =
| MaybeArray<Value>
| {
include?: MaybeArray<Value>;
exclude?: MaybeArray<Value>;
};

interface HookFilter {
id?: StringFilter;
code?: StringFilter;
}
```

```js twoslash
/** @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`](#options),最后一个钩子始终是 [`buildEnd`](#buildend)。如果有构建错误,则在此之后将调用 [`closeBundle`](#closebundle)。

<style>
Expand Down
70 changes: 27 additions & 43 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rollup",
"version": "4.37.0",
"version": "4.38.0",
"description": "Next-generation ES module bundler",
"main": "dist/rollup.js",
"module": "dist/es/rollup.js",
Expand Down Expand Up @@ -142,6 +142,7 @@
"@shikijs/vitepress-twoslash": "^3.2.1",
"@types/mocha": "^10.0.10",
"@types/node": "^18.19.83",
"@types/picomatch": "^3.0.2",
"@types/semver": "^7.5.8",
"@types/yargs-parser": "^21.0.3",
"@vue/language-server": "^2.2.8",
Expand All @@ -160,7 +161,7 @@
"eslint": "^9.23.0",
"eslint-config-prettier": "^10.1.1",
"eslint-plugin-prettier": "^5.2.4",
"eslint-plugin-unicorn": "^57.0.0",
"eslint-plugin-unicorn": "^58.0.0",
"eslint-plugin-vue": "^10.0.0",
"fixturify": "^3.0.0",
"flru": "^1.0.2",
Expand All @@ -177,6 +178,7 @@
"npm-audit-resolver": "^3.0.0-RC.0",
"nyc": "^17.1.0",
"picocolors": "^1.1.1",
"picomatch": "^4.0.2",
"pinia": "^3.0.1",
"prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.1.0",
Expand Down
25 changes: 24 additions & 1 deletion src/rollup/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,20 @@ export interface PluginContextMeta {
watchMode: boolean;
}

export type StringOrRegExp = string | RegExp;

export type StringFilter<Value = StringOrRegExp> =
| MaybeArray<Value>
| {
include?: MaybeArray<Value>;
exclude?: MaybeArray<Value>;
};

export interface HookFilter {
id?: StringFilter;
code?: StringFilter;
}

export interface ResolvedId extends ModuleOptions {
external: boolean | 'absolute';
id: string;
Expand Down Expand Up @@ -526,11 +540,20 @@ type MakeAsync<Function_> = Function_ extends (
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export type ObjectHook<T, O = {}> = T | ({ handler: T; order?: 'pre' | 'post' | null } & O);

export type HookFilterExtension<K extends keyof FunctionPluginHooks> = K extends 'transform'
? { filter?: HookFilter }
: K extends 'load'
? { filter?: Pick<HookFilter, 'id'> }
: K extends 'resolveId'
? { filter?: { id: StringFilter<RegExp> } }
: // eslint-disable-next-line @typescript-eslint/no-empty-object-type
{};

export type PluginHooks = {
[K in keyof FunctionPluginHooks]: ObjectHook<
K extends AsyncPluginHooks ? MakeAsync<FunctionPluginHooks[K]> : FunctionPluginHooks[K],
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
K extends ParallelPluginHooks ? { sequential?: boolean } : {}
HookFilterExtension<K> & (K extends ParallelPluginHooks ? { sequential?: boolean } : {})
>;
};

Expand Down
33 changes: 33 additions & 0 deletions src/utils/PluginDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
EmitFile,
FirstPluginHooks,
FunctionPluginHooks,
HookFilter,
InputPluginHooks,
NormalizedInputOptions,
NormalizedOutputOptions,
Expand All @@ -30,6 +31,12 @@ import {
logPluginError
} from './logs';
import type { OutputBundleWithPlaceholders } from './outputBundle';
import {
createFilterForId,
createFilterForTransform,
type PluginFilter,
type TransformHookFilter
} from './pluginFilter';

/**
* Coerce a promise union to always be a promise.
Expand Down Expand Up @@ -79,6 +86,10 @@ export class PluginDriver {
private readonly plugins: readonly Plugin[];
private readonly sortedPlugins = new Map<AsyncPluginHooks, Plugin[]>();
private readonly unfulfilledActions = new Set<HookAction>();
private readonly compiledPluginFilters = {
idOnlyFilter: new WeakMap<Pick<HookFilter, 'id'>, PluginFilter | undefined>(),
transformFilter: new WeakMap<HookFilter, TransformHookFilter | undefined>()
};

constructor(
private readonly graph: Graph,
Expand Down Expand Up @@ -319,6 +330,28 @@ export class PluginDriver {
const hook = plugin[hookName];
const handler = typeof hook === 'object' ? hook.handler : hook;

if (typeof hook === 'object' && 'filter' in hook && hook.filter) {
if (hookName === 'transform') {
const filter = hook.filter as HookFilter;
const hookParameters = parameters as Parameters<FunctionPluginHooks['transform']>;
const compiledFilter = getOrCreate(this.compiledPluginFilters.transformFilter, filter, () =>
createFilterForTransform(filter.id, filter.code)
);
if (compiledFilter && !compiledFilter(hookParameters[1], hookParameters[0])) {
return Promise.resolve();
}
} else if (hookName === 'resolveId' || hookName === 'load') {
const filter = hook.filter;
const hookParameters = parameters as Parameters<FunctionPluginHooks['load' | 'resolveId']>;
const compiledFilter = getOrCreate(this.compiledPluginFilters.idOnlyFilter, filter, () =>
createFilterForId(filter.id)
);
if (compiledFilter && !compiledFilter(hookParameters[0])) {
return Promise.resolve();
}
}
}

let context = this.pluginContexts.get(plugin)!;
if (replaceContext) {
context = replaceContext(context, plugin);
Expand Down
Loading