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.41.1

_2025-05-24_

### Bug Fixes

- If a plugin calls `this.resolve` with `skipSelf: true`, subsequent calls when handling this by the same plugin with same parameters will return `null` to avoid infinite recursions (#5945)

### Pull Requests

- [#5945](https://github.com/rollup/rollup/pull/5945): Avoid recursively calling a plugin's resolveId hook with same id and importer (@younggglcy, @lukastaegert)
- [#5963](https://github.com/rollup/rollup/pull/5963): fix(deps): update swc monorepo (major) (@renovate[bot])
- [#5964](https://github.com/rollup/rollup/pull/5964): fix(deps): lock file maintenance minor/patch updates (@renovate[bot])

## 4.41.0

_2025-05-18_
Expand Down
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.41.0",
"version": "4.41.1",
"description": "Next-generation ES module bundler browser build",
"main": "dist/rollup.browser.js",
"module": "dist/es/rollup.browser.js",
Expand Down
4 changes: 2 additions & 2 deletions docs/plugin-development/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1901,7 +1901,7 @@ type Resolve = (
attributes?: { [key: string]: string };
custom?: { [plugin: string]: any };
}
) => ResolvedId;
) => Promise<ResolvedId | null>;
```

::: tip
Expand All @@ -1910,7 +1910,7 @@ type Resolve = (

:::

使用 Rollup 使用的相同插件解析导入到模块 id(即文件名),并确定导入是否应为外部。如果 Rollup 或任何插件无法解析导入,但用户未明确将其标记为外部,则返回 `null`。如果返回绝对外部 id,则应通过 [`makeAbsoluteExternalsRelative`](../configuration-options/index.md#makeabsoluteexternalsrelative) 选项或在 [`resolveId`](#resolveid) 钩子中进行显式插件选择,将其保持为绝对输出,`external` 将是 `"absolute"` 而不是 `true`。
使用 Rollup 使用的相同插件解析导入到模块 id(即文件名),并确定导入是否应为外部。如果 Rollup 或任何插件无法解析导入,但用户未明确将其标记为外部,则返回 `Promise<null>`。如果返回绝对外部 id,则应通过 [`makeAbsoluteExternalsRelative`](../configuration-options/index.md#makeabsoluteexternalsrelative) 选项或在 [`resolveId`](#resolveid) 钩子中进行显式插件选择,将其保持为绝对输出,`external` 将是 `"absolute"` 而不是 `true`。

`skipSelf` 的默认值是 `true`,因此在解析时将跳过调用 `this.resolve` 的插件的 `resolveId` 钩子。当其他插件在处理原始 `this.resolve` 调用时,也使用 _完全相同的 `source` 和 `importer`_ 在其 `resolveId` 钩子中调用 `this.resolve` 时,原始插件的 `resolveId` 钩子也将跳过这些调用。这里的原理是,插件已经声明它在此时点上“不知道”如何解析这个特定的 `source` 和 `importer` 组合。如果你不想要这种行为,将 `skipSelf` 设置为 `false`,并在必要时实现自己的无限循环预防机制。

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rollup",
"version": "4.41.0",
"version": "4.41.1",
"description": "Next-generation ES module bundler",
"main": "dist/rollup.js",
"module": "dist/es/rollup.js",
Expand Down
49 changes: 25 additions & 24 deletions rust/Cargo.lock

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

8 changes: 4 additions & 4 deletions rust/parse_ast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ edition = "2021"
[dependencies]
anyhow = "1.0.98"
swc_atoms = "5.0.0"
swc_compiler_base = "20.0.0"
swc_compiler_base = "22.0.0"
swc_config = "3.0.0"
swc_common = { version = "9.2.0", features = ["parking_lot"] }
swc_ecma_ast = "9.0.0"
swc_ecma_parser = "12.0.0"
swc_common = { version = "11.0.0", features = ["parking_lot"] }
swc_ecma_ast = "11.0.0"
swc_ecma_parser = "14.0.0"
parking_lot = "0.12.3"
14 changes: 14 additions & 0 deletions src/utils/resolveIdViaPlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ export function resolveIdViaPlugins(
...pluginContext,
resolve: (source, importer, { attributes, custom, isEntry, skipSelf } = BLANK) => {
skipSelf ??= true;
if (
skipSelf &&
skip.findIndex(skippedCall => {
return (
skippedCall.plugin === plugin &&
skippedCall.source === source &&
skippedCall.importer === importer
);
}) !== -1
) {
// This means that the plugin recursively called itself
// Thus returning Promise.resolve(null) in purpose of fallback to default behavior of `resolveId` plugin hook.
return Promise.resolve(null);
}
return moduleLoaderResolveId(
source,
importer,
Expand Down
27 changes: 27 additions & 0 deletions test/function/samples/resolveid-recursive-call/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module.exports = defineTest({
description:
'skipSelf: true option in resolveId hook option should skip the plugin if it has been called before with the same id and importer, see #5768 for more details',
options: {
plugins: [
{
name: 'r1',
async resolveId(id) {
const importer = 'foo';
return (await this.resolve(id, importer)) ?? 'success';
},
load(id) {
if (id === 'success') {
return { code: 'export default 1' };
}
}
},
{
name: 'r2',
resolveId(id) {
const importer = 'bar';
return this.resolve(id, importer);
}
}
]
}
});
3 changes: 3 additions & 0 deletions test/function/samples/resolveid-recursive-call/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import val from 'virtual'

assert.strictEqual(val, 1)
Loading