Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/rollup/rollup into sync-5…
Browse files Browse the repository at this point in the history
…e7a3631
  • Loading branch information
docschina-bot committed Sep 19, 2024
2 parents 50b362f + 5e7a363 commit 9b70472
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 18 deletions.
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# rollup changelog

## 4.22.0

_2024-09-19_

### Features

- Add additional known global values to avoid access side effects (#5651)

### Bug Fixes

- Ensure deterministic chunk hash generation despite async renderChunk hook (#5644)
- Improve side effect detection when using "smallest" treeshaking preset when imports are optimized away (#5658)

### Pull Requests

- [#5644](https://github.com/rollup/rollup/pull/5644): fix: apply final hashes deterministically with stable placeholders set (@mattkubej, @lukastaegert)
- [#5646](https://github.com/rollup/rollup/pull/5646): chore(deps): update dependency @mermaid-js/mermaid-cli to v11 (@renovate[bot])
- [#5647](https://github.com/rollup/rollup/pull/5647): chore(deps): update dependency concurrently to v9 (@renovate[bot])
- [#5648](https://github.com/rollup/rollup/pull/5648): chore(deps): lock file maintenance minor/patch updates (@renovate[bot])
- [#5651](https://github.com/rollup/rollup/pull/5651): feat: add `AggregateError`, `FinalizationRegistry`, `WeakRef` to knownGlobals (@re-taro)
- [#5653](https://github.com/rollup/rollup/pull/5653): Fix example selection in REPL (@lukastaegert)
- [#5657](https://github.com/rollup/rollup/pull/5657): chore(deps): update dependency vite to v5.4.6 [security] (@renovate[bot])
- [#5658](https://github.com/rollup/rollup/pull/5658): Detect variable reassignments in modules without side effects (@lukastaegert)

## 4.21.3

_2024-09-12_
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.21.3",
"version": "4.22.0",
"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 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.21.3",
"version": "4.22.0",
"description": "Next-generation ES module bundler",
"main": "dist/rollup.js",
"module": "dist/es/rollup.js",
Expand Down
1 change: 1 addition & 0 deletions src/Graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export default class Graph {
this.needsTreeshakingPass = false;
for (const module of this.modules) {
if (module.isExecuted) {
module.hasTreeShakingPassStarted = true;
if (module.info.moduleSideEffects === 'no-treeshake') {
module.includeAllInBundle();
} else {
Expand Down
9 changes: 5 additions & 4 deletions src/Module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import { extractAssignedNames } from '@rollup/pluginutils';
import { locate } from 'locate-character';
import MagicString from 'magic-string';
import { parseAsync } from '../native';
import ExternalModule from './ExternalModule';
import type Graph from './Graph';
import { createInclusionContext } from './ast/ExecutionContext';
import { convertProgram } from './ast/bufferParsers';
import { createInclusionContext } from './ast/ExecutionContext';
import { nodeConstructors } from './ast/nodes';
import ExportAllDeclaration from './ast/nodes/ExportAllDeclaration';
import ExportDefaultDeclaration from './ast/nodes/ExportDefaultDeclaration';
Expand All @@ -19,8 +17,8 @@ import Literal from './ast/nodes/Literal';
import type MetaProperty from './ast/nodes/MetaProperty';
import * as NodeType from './ast/nodes/NodeType';
import type Program from './ast/nodes/Program';
import VariableDeclaration from './ast/nodes/VariableDeclaration';
import type { NodeBase } from './ast/nodes/shared/Node';
import VariableDeclaration from './ast/nodes/VariableDeclaration';
import ModuleScope from './ast/scopes/ModuleScope';
import { type PathTracker, UNKNOWN_PATH } from './ast/utils/PathTracker';
import ExportDefaultVariable from './ast/variables/ExportDefaultVariable';
Expand All @@ -29,6 +27,8 @@ import ExternalVariable from './ast/variables/ExternalVariable';
import NamespaceVariable from './ast/variables/NamespaceVariable';
import SyntheticNamedExportVariable from './ast/variables/SyntheticNamedExportVariable';
import type Variable from './ast/variables/Variable';
import ExternalModule from './ExternalModule';
import type Graph from './Graph';
import type {
AstNode,
CustomPluginOptions,
Expand Down Expand Up @@ -220,6 +220,7 @@ export default class Module {
readonly dynamicImports: DynamicImport[] = [];
excludeFromSourcemap: boolean;
execIndex = Infinity;
hasTreeShakingPassStarted = false;
readonly implicitlyLoadedAfter = new Set<Module>();
readonly implicitlyLoadedBefore = new Set<Module>();
readonly importDescriptions = new Map<string, ImportDescription>();
Expand Down
17 changes: 10 additions & 7 deletions src/ast/nodes/Identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import GlobalVariable from '../variables/GlobalVariable';
import LocalVariable from '../variables/LocalVariable';
import type Variable from '../variables/Variable';
import * as NodeType from './NodeType';
import type SpreadElement from './SpreadElement';
import { Flag, isFlagSet, setFlag } from './shared/BitFlags';
import {
type ExpressionEntity,
Expand All @@ -30,6 +29,7 @@ import {
import { NodeBase } from './shared/Node';
import type { PatternNode } from './shared/Pattern';
import type { VariableKind } from './shared/VariableKinds';
import type SpreadElement from './SpreadElement';

export type IdentifierWithVariable = Identifier & { variable: Variable };

Expand Down Expand Up @@ -220,9 +220,10 @@ export default class Identifier extends NodeBase implements PatternNode {
this.variable instanceof LocalVariable &&
this.variable.kind &&
tdzVariableKinds.has(this.variable.kind) &&
// we ignore possible TDZs due to circular module dependencies as
// otherwise we get many false positives
this.variable.module === this.scope.context.module
// We ignore modules that did not receive a treeshaking pass yet as that
// causes many false positives due to circular dependencies or disabled
// moduleSideEffects.
this.variable.module.hasTreeShakingPassStarted
)
) {
return (this.isTDZAccess = false);
Expand All @@ -241,9 +242,7 @@ export default class Identifier extends NodeBase implements PatternNode {
return (this.isTDZAccess = true);
}

// We ignore the case where the module is not yet executed because
// moduleSideEffects are false.
if (!this.variable.initReached && this.scope.context.module.isExecuted) {
if (!this.variable.initReached) {
// Either a const/let TDZ violation or
// var use before declaration was encountered.
return (this.isTDZAccess = true);
Expand Down Expand Up @@ -294,6 +293,10 @@ export default class Identifier extends NodeBase implements PatternNode {
protected applyDeoptimizations(): void {
this.deoptimized = true;
if (this.variable instanceof LocalVariable) {
// When accessing a variable from a module without side effects, this
// means we use an export of that module and therefore need to potentially
// include it in the bundle.
this.variable.module.isExecuted = true;
this.variable.consolidateInitializers();
this.scope.context.requestTreeshakingPass();
}
Expand Down
11 changes: 8 additions & 3 deletions src/utils/renderChunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ export async function renderChunks(
const getHash = hasherByType[outputOptions.hashCharacters];
const chunkGraph = getChunkGraph(chunks);
const {
hashDependenciesByPlaceholder,
initialHashesByPlaceholder,
nonHashedChunksWithPlaceholders,
renderedChunksByPlaceholder,
hashDependenciesByPlaceholder
placeholders,
renderedChunksByPlaceholder
} = await transformChunksAndGenerateContentHashes(
renderedChunks,
chunkGraph,
Expand All @@ -71,6 +72,7 @@ export async function renderChunks(
renderedChunksByPlaceholder,
hashDependenciesByPlaceholder,
initialHashesByPlaceholder,
placeholders,
bundle,
getHash
);
Expand Down Expand Up @@ -283,6 +285,7 @@ async function transformChunksAndGenerateContentHashes(
hashDependenciesByPlaceholder,
initialHashesByPlaceholder,
nonHashedChunksWithPlaceholders,
placeholders,
renderedChunksByPlaceholder
};
}
Expand All @@ -291,11 +294,13 @@ function generateFinalHashes(
renderedChunksByPlaceholder: Map<string, RenderedChunkWithPlaceholders>,
hashDependenciesByPlaceholder: Map<string, HashResult>,
initialHashesByPlaceholder: Map<string, string>,
placeholders: Set<string>,
bundle: OutputBundleWithPlaceholders,
getHash: GetHash
) {
const hashesByPlaceholder = new Map<string, string>(initialHashesByPlaceholder);
for (const [placeholder, { fileName }] of renderedChunksByPlaceholder) {
for (const placeholder of placeholders) {
const { fileName } = renderedChunksByPlaceholder.get(placeholder)!;
let contentToHash = '';
const hashDependencyPlaceholders = new Set<string>([placeholder]);
for (const dependencyPlaceholder of hashDependencyPlaceholders) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = defineTest({
description: 'detects variable updates in modules without side effects (#5408)',
options: {
treeshake: {
moduleSideEffects: false
}
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export let direct = false;
direct = true;

export {indirect} from './dep2.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export let indirect = false;
(() => {
indirect = true;
})();
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { direct, indirect } from './dep.js';
assert.ok(direct ? true : false, 'direct');
assert.ok(indirect ? true : false, 'indirect');
58 changes: 58 additions & 0 deletions test/misc/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,64 @@ describe('misc', () => {
});
});

it('applies consistent hashes regardless of chunk transform order', async () => {
const FILES = {
main: `
import('folder1/dupe').then(({dupe}) => console.log(dupe));
import('folder2/dupe').then(({dupe}) => console.log(dupe));
`,
'folder1/dupe': `export const dupe = 'dupe content';`,
'folder2/dupe': `export const dupe = 'dupe content';`
};

async function buildBundle(delayedChunk) {
const bundle = await rollup.rollup({
input: 'main',
plugins: [
loader(FILES),
{
name: 'delay-chunk',
async renderChunk(_, chunk) {
if (chunk.facadeModuleId === delayedChunk) {
await new Promise(resolve => setTimeout(resolve, 100));
}
return null;
}
}
]
});
return bundle.generate({
format: 'es',
chunkFileNames: '[name]-[hash].js'
});
}

const { output: output1 } = await buildBundle('folder1/dupe');
const { output: output2 } = await buildBundle('folder2/dupe');

assert.strictEqual(
output1.length,
output2.length,
'Both outputs should have the same number of chunks'
);

const sortedOutput1 = output1.sort((a, b) => a.fileName.localeCompare(b.fileName));
const sortedOutput2 = output2.sort((a, b) => a.fileName.localeCompare(b.fileName));

for (let index = 0; index < sortedOutput1.length; index++) {
assert.strictEqual(
sortedOutput1[index].fileName,
sortedOutput2[index].fileName,
`Chunk ${index} should have the same filename in both outputs`
);
assert.strictEqual(
sortedOutput1[index].code,
sortedOutput2[index].code,
`Chunk ${index} should have the same code in both outputs`
);
}
});

it('ignores falsy plugins', () =>
rollup.rollup({
input: 'x',
Expand Down

0 comments on commit 9b70472

Please sign in to comment.