Skip to content

Commit 0186e83

Browse files
authored
Merge pull request #192 from rollup/sync-743d0546
docs(en): merge rollup/master into rollup-docs-cn/master @ 743d054
2 parents 28f4681 + 7634f49 commit 0186e83

35 files changed

Lines changed: 596 additions & 78 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# rollup changelog
22

3+
## 4.57.0
4+
5+
_2026-01-27_
6+
7+
### Features
8+
9+
- Add import attributes to all plugin hooks that did not provide them yet (#5700)
10+
- Deprecate returning import attributes from `load` or `transform` hooks as that will no longer be supported with rollup 5 (#5700)
11+
12+
### Pull Requests
13+
14+
- [#5700](https://github.com/rollup/rollup/pull/5700): extend more hooks to include import attributes and add warnings (@TrickyPi, @lukastaegert)
15+
- [#6243](https://github.com/rollup/rollup/pull/6243): fix(deps): update swc monorepo (major) (@renovate[bot], @lukastaegert)
16+
- [#6244](https://github.com/rollup/rollup/pull/6244): fix(deps): lock file maintenance minor/patch updates (@renovate[bot], @lukastaegert)
17+
- [#6245](https://github.com/rollup/rollup/pull/6245): chore(deps): lock file maintenance (@renovate[bot])
18+
- [#6246](https://github.com/rollup/rollup/pull/6246): Refactor to reduce Rollup 5 upgrade diff (@lukastaegert)
19+
320
## 4.56.0
421

522
_2026-01-22_

browser/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rollup/browser",
3-
"version": "4.56.0",
3+
"version": "4.57.0",
44
"description": "Next-generation ES module bundler browser build",
55
"main": "dist/rollup.browser.js",
66
"module": "dist/es/rollup.browser.js",

docs/plugin-development/index.md

Lines changed: 117 additions & 17 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rollup",
3-
"version": "4.56.0",
3+
"version": "4.57.0",
44
"description": "Next-generation ES module bundler",
55
"main": "dist/rollup.js",
66
"module": "dist/es/rollup.js",

src/ModuleLoader.ts

Lines changed: 79 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ import {
3030
logUnresolvedEntry,
3131
logUnresolvedImplicitDependant,
3232
logUnresolvedImport,
33-
logUnresolvedImportTreatedAsExternal
33+
logUnresolvedImportTreatedAsExternal,
34+
warnDeprecation
3435
} from './utils/logs';
3536
import {
3637
doAttributesDiffer,
@@ -42,6 +43,7 @@ import relativeId from './utils/relativeId';
4243
import { resolveId } from './utils/resolveId';
4344
import stripBom from './utils/stripBom';
4445
import transform from './utils/transform';
46+
import { URL_LOAD } from './utils/urls';
4547

4648
export interface UnresolvedModule {
4749
fileName: string | null;
@@ -56,6 +58,7 @@ export type ModuleLoaderResolveId = (
5658
customOptions: CustomPluginOptions | undefined,
5759
isEntry: boolean | undefined,
5860
attributes: Record<string, string>,
61+
importerAttributes: Record<string, string> | undefined,
5962
skip?: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null
6063
) => Promise<ResolvedId | null>;
6164

@@ -107,7 +110,7 @@ export class ModuleLoader {
107110
const result = this.extendLoadModulesPromise(
108111
Promise.all(
109112
unresolvedModules.map(id =>
110-
this.loadEntryModule(id, false, undefined, null, isAddForManualChunks)
113+
this.loadEntryModule(id, false, undefined, null, isAddForManualChunks, undefined)
111114
)
112115
)
113116
);
@@ -130,7 +133,7 @@ export class ModuleLoader {
130133
const newEntryModules = await this.extendLoadModulesPromise(
131134
Promise.all(
132135
unresolvedEntryModules.map(({ id, importer }) =>
133-
this.loadEntryModule(id, true, importer, null)
136+
this.loadEntryModule(id, true, importer, null, undefined, undefined)
134137
)
135138
).then(entryModules => {
136139
for (const [index, entryModule] of entryModules.entries()) {
@@ -212,6 +215,7 @@ export class ModuleLoader {
212215
customOptions,
213216
isEntry,
214217
attributes,
218+
importerAttributes,
215219
skip = null
216220
) =>
217221
this.getResolvedIdWithDefaults(
@@ -228,6 +232,7 @@ export class ModuleLoader {
228232
customOptions,
229233
typeof isEntry === 'boolean' ? isEntry : !importer,
230234
attributes,
235+
importerAttributes,
231236
this.options.fs
232237
),
233238
importer,
@@ -242,32 +247,44 @@ export class ModuleLoader {
242247
): Promise<Module> {
243248
const chunkNamePriority = this.nextChunkNamePriority++;
244249
return this.extendLoadModulesPromise(
245-
this.loadEntryModule(unresolvedModule.id, false, unresolvedModule.importer, null).then(
246-
async entryModule => {
247-
addChunkNamesToModule(entryModule, unresolvedModule, false, chunkNamePriority);
248-
if (!entryModule.info.isEntry) {
249-
const implicitlyLoadedAfterModules = await Promise.all(
250-
implicitlyLoadedAfter.map(id =>
251-
this.loadEntryModule(id, false, unresolvedModule.importer, entryModule.id)
250+
this.loadEntryModule(
251+
unresolvedModule.id,
252+
false,
253+
unresolvedModule.importer,
254+
null,
255+
undefined,
256+
undefined
257+
).then(async entryModule => {
258+
addChunkNamesToModule(entryModule, unresolvedModule, false, chunkNamePriority);
259+
if (!entryModule.info.isEntry) {
260+
const implicitlyLoadedAfterModules = await Promise.all(
261+
implicitlyLoadedAfter.map(id =>
262+
this.loadEntryModule(
263+
id,
264+
false,
265+
unresolvedModule.importer,
266+
entryModule.id,
267+
undefined,
268+
undefined
252269
)
253-
);
254-
// We need to check again if this is still an entry module as these
255-
// changes need to be performed atomically to avoid race conditions
256-
// if the same module is re-emitted as an entry module.
257-
// The inverse changes happen in "handleExistingModule"
258-
if (!entryModule.info.isEntry) {
259-
this.implicitEntryModules.add(entryModule);
260-
for (const module of implicitlyLoadedAfterModules) {
261-
entryModule.implicitlyLoadedAfter.add(module);
262-
}
263-
for (const dependant of entryModule.implicitlyLoadedAfter) {
264-
dependant.implicitlyLoadedBefore.add(entryModule);
265-
}
270+
)
271+
);
272+
// We need to check again if this is still an entry module as these
273+
// changes need to be performed atomically to avoid race conditions
274+
// if the same module is re-emitted as an entry module.
275+
// The inverse changes happen in "handleExistingModule"
276+
if (!entryModule.info.isEntry) {
277+
this.implicitEntryModules.add(entryModule);
278+
for (const module of implicitlyLoadedAfterModules) {
279+
entryModule.implicitlyLoadedAfter.add(module);
280+
}
281+
for (const dependant of entryModule.implicitlyLoadedAfter) {
282+
dependant.implicitlyLoadedBefore.add(entryModule);
266283
}
267284
}
268-
return entryModule;
269285
}
270-
)
286+
return entryModule;
287+
})
271288
);
272289
}
273290

@@ -279,8 +296,21 @@ export class ModuleLoader {
279296
let source: LoadResult;
280297
try {
281298
source = await this.graph.fileOperationQueue.run(async () => {
282-
const content = await this.pluginDriver.hookFirst('load', [id]);
283-
if (content !== null) return content;
299+
const content = await this.pluginDriver.hookFirst('load', [
300+
id,
301+
{ attributes: module.info.attributes }
302+
]);
303+
if (content !== null) {
304+
if (typeof content === 'object' && content.attributes) {
305+
warnDeprecation(
306+
'Returning attributes from the "load" hook is forbidden.',
307+
URL_LOAD,
308+
false,
309+
this.options
310+
);
311+
}
312+
return content;
313+
}
284314
this.graph.watchFiles[id] = true;
285315
return (await this.options.fs.readFile(id, { encoding: 'utf8' })) as string;
286316
});
@@ -306,6 +336,7 @@ export class ModuleLoader {
306336
!(await this.pluginDriver.hookFirst('shouldTransformCachedModule', [
307337
{
308338
ast: cachedModule.ast,
339+
attributes: cachedModule.attributes,
309340
code: cachedModule.code,
310341
id: cachedModule.id,
311342
meta: cachedModule.meta,
@@ -323,7 +354,7 @@ export class ModuleLoader {
323354
} else {
324355
module.updateOptions(sourceDescription);
325356
await module.setSource(
326-
await transform(sourceDescription, module, this.pluginDriver, this.options.onLog)
357+
await transform(sourceDescription, module, this.pluginDriver, this.options)
327358
);
328359
}
329360
}
@@ -586,7 +617,14 @@ export class ModuleLoader {
586617
(module.resolvedIds[source] =
587618
module.resolvedIds[source] ||
588619
this.handleInvalidResolvedId(
589-
await this.resolveId(source, module.id, EMPTY_OBJECT, false, attributes),
620+
await this.resolveId(
621+
source,
622+
module.id,
623+
EMPTY_OBJECT,
624+
false,
625+
attributes,
626+
module.info.attributes
627+
),
590628
source,
591629
module.id,
592630
attributes
@@ -666,7 +704,8 @@ export class ModuleLoader {
666704
isEntry: boolean,
667705
importer: string | undefined,
668706
implicitlyLoadedBefore: string | null,
669-
isLoadForManualChunks = false
707+
isLoadForManualChunks = false,
708+
importerAttributes: Record<string, string> | undefined
670709
): Promise<Module> {
671710
const resolveIdResult = await resolveId(
672711
unresolvedId,
@@ -678,6 +717,7 @@ export class ModuleLoader {
678717
EMPTY_OBJECT,
679718
true,
680719
EMPTY_OBJECT,
720+
importerAttributes,
681721
this.options.fs
682722
);
683723
if (resolveIdResult == null) {
@@ -719,7 +759,7 @@ export class ModuleLoader {
719759
const resolution = await this.pluginDriver.hookFirst('resolveDynamicImport', [
720760
specifier,
721761
importer,
722-
{ attributes }
762+
{ attributes, importerAttributes: module.info.attributes }
723763
]);
724764
if (typeof specifier !== 'string') {
725765
if (typeof resolution === 'string') {
@@ -750,7 +790,14 @@ export class ModuleLoader {
750790
return existingResolution;
751791
}
752792
return (module.resolvedIds[specifier] = this.handleInvalidResolvedId(
753-
await this.resolveId(specifier, module.id, EMPTY_OBJECT, false, attributes),
793+
await this.resolveId(
794+
specifier,
795+
module.id,
796+
EMPTY_OBJECT,
797+
false,
798+
attributes,
799+
module.info.attributes
800+
),
754801
specifier,
755802
module.id,
756803
attributes

src/ast/nodes/ImportExpression.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@ export default class ImportExpression extends NodeBase {
328328
},
329329
moduleId: scope.context.module.id,
330330
targetChunk: targetChunk ? getChunkInfoWithPath(targetChunk) : null,
331+
targetModuleAttributes:
332+
resolution && typeof resolution !== 'string' ? resolution.info.attributes : {},
331333
targetModuleId: resolution && typeof resolution !== 'string' ? resolution.id : null
332334
}
333335
]);

src/ast/nodes/MetaProperty.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ export default class MetaProperty extends NodeBase {
8686
start,
8787
end
8888
} = this;
89-
const { id: moduleId } = module;
89+
const {
90+
id: moduleId,
91+
info: { attributes }
92+
} = module;
9093

9194
if (name !== IMPORT) return;
9295
const chunkId = preliminaryChunkId!;
@@ -97,7 +100,7 @@ export default class MetaProperty extends NodeBase {
97100
const isUrlObject = !!metaProperty?.startsWith(FILE_OBJ_PREFIX);
98101
const replacement =
99102
pluginDriver.hookFirstSync('resolveFileUrl', [
100-
{ chunkId, fileName, format, moduleId, referenceId, relativePath }
103+
{ attributes, chunkId, fileName, format, moduleId, referenceId, relativePath }
101104
]) || relativeUrlMechanisms[format](relativePath, isUrlObject);
102105

103106
code.overwrite(
@@ -111,7 +114,7 @@ export default class MetaProperty extends NodeBase {
111114

112115
let replacement = pluginDriver.hookFirstSync('resolveImportMeta', [
113116
metaProperty,
114-
{ chunkId, format, moduleId }
117+
{ attributes, chunkId, format, moduleId }
115118
]);
116119
if (!replacement) {
117120
replacement = importMetaMechanisms[format]?.(metaProperty, { chunkId, snippets });

0 commit comments

Comments
 (0)