Skip to content

Commit

Permalink
Merge branch 'master' into fix/map-impure
Browse files Browse the repository at this point in the history
  • Loading branch information
TrickyPi committed Apr 29, 2023
2 parents 0f35bb0 + 703e88f commit 39def64
Show file tree
Hide file tree
Showing 15 changed files with 112 additions and 63 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,20 @@
# rollup changelog

## 3.21.1

_2023-04-29_

### Bug Fixes

- Make sure call arguments are properly deoptimized when a function uses the `arguments` variable (#4965)

### Pull Requests

- [#4957](https://github.com/rollup/rollup/pull/4957): Update dependencies (@lukastaegert)
- [#4964](https://github.com/rollup/rollup/pull/4964): Fix REPL in dev (@lukastaegert)
- [#4965](https://github.com/rollup/rollup/pull/4965): Ensure arguments are deoptimized when arguments variable is used (@lukastaegert)
- [#4967](https://github.com/rollup/rollup/pull/4967): Log REPL output to console (@lukastaegert)

## 3.21.0

_2023-04-23_
Expand Down
2 changes: 1 addition & 1 deletion browser/package.json
@@ -1,6 +1,6 @@
{
"name": "@rollup/browser",
"version": "3.21.0",
"version": "3.21.1",
"description": "Next-generation ES module bundler browser build",
"main": "dist/rollup.browser.js",
"module": "dist/es/rollup.browser.js",
Expand Down
13 changes: 10 additions & 3 deletions build-plugins/replace-browser-modules.ts
@@ -1,6 +1,6 @@
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Plugin } from 'rollup';
import type { Plugin } from 'vite';

const resolve = (path: string) => fileURLToPath(new URL(`../${path}`, import.meta.url));

Expand All @@ -14,10 +14,10 @@ const REPLACED_MODULES = [
'resolveId'
];

export const resolutions: ReadonlyMap<string, string> = new Map(
const resolutions: ReadonlyMap<string, string> = new Map(
REPLACED_MODULES.flatMap(module => {
const originalId = resolve(`src/utils/${module}`);
const replacementId = resolve(`browser/src//${module}.ts`);
const replacementId = resolve(`browser/src/${module}.ts`);
return [
[originalId, replacementId],
[`${originalId}.ts`, replacementId]
Expand All @@ -27,11 +27,18 @@ export const resolutions: ReadonlyMap<string, string> = new Map(

export default function replaceBrowserModules(): Plugin {
return {
apply: 'serve',
enforce: 'pre',
name: 'replace-browser-modules',
resolveId(source, importer) {
if (importer && source[0] === '.') {
return resolutions.get(join(dirname(importer), source));
}
},
transformIndexHtml(html) {
// Unfortunately, picomatch sneaks as a dedendency into the dev bundle.
// This fixes an error.
return html.replace('</head>', '<script>window.process={}</script></head>');
}
};
}
18 changes: 2 additions & 16 deletions docs/.vitepress/config.ts
@@ -1,7 +1,7 @@
import alias from '@rollup/plugin-alias';
import { defineConfig } from 'vitepress';
import { moduleAliases } from '../../build-plugins/aliases';
import { resolutions } from '../../build-plugins/replace-browser-modules';
import replaceBrowserModules from '../../build-plugins/replace-browser-modules';
import '../declarations.d';
import { examplesPlugin } from './create-examples';
import { renderMermaidGraphsPlugin } from './mermaid';
Expand Down Expand Up @@ -135,21 +135,7 @@ export default defineConfig({
vite: {
plugins: [
renderMermaidGraphsPlugin(),
{
apply: 'serve',
enforce: 'pre',
name: 'replace-browser-modules',
resolveId(source, importer) {
if (importer && source.startsWith('/@fs')) {
return resolutions.get(source.slice(4));
}
},
transformIndexHtml(html) {
// Unfortunately, picomatch sneaks as a dedendency into the dev bundle.
// This fixes an error.
return html.replace('</head>', '<script>window.process={}</script></head>');
}
},
replaceBrowserModules(),
{
apply: 'build',
enforce: 'pre',
Expand Down
14 changes: 7 additions & 7 deletions docs/repl/stores/options.ts
Expand Up @@ -136,7 +136,7 @@ export const useOptions = defineStore('options2', () => {
name: 'output.amd.define'
});
const optionOutputAmdForceJsExtensionForImports = getBoolean({
available: () => isAmdFormat.value && outputHasMultipleChunks.value,
available: isAmdFormat,
name: 'output.amd.forceJsExtensionForImports'
});
const optionOutputAmdId = getString({
Expand All @@ -149,7 +149,7 @@ export const useOptions = defineStore('options2', () => {
name: 'output.banner'
});
const optionOutputChunkFileNames = getString({
available: outputHasMultipleChunks,
available: alwaysTrue,
defaultValue: '[name]-[hash].js',
name: 'output.chunkFileNames'
});
Expand All @@ -162,7 +162,7 @@ export const useOptions = defineStore('options2', () => {
name: 'output.dynamicImportInCjs'
});
const optionOutputEntryFileNames = getString({
available: outputHasMultipleChunks,
available: alwaysTrue,
defaultValue: '[name].js',
name: 'output.entryFileNames'
});
Expand Down Expand Up @@ -240,7 +240,7 @@ export const useOptions = defineStore('options2', () => {
required: () => true
});
const optionOutputHoistTransitiveImports = getBoolean({
available: outputHasMultipleChunks,
available: alwaysTrue,
defaultValue: true,
name: 'output.hoistTransitiveImports'
});
Expand Down Expand Up @@ -279,7 +279,7 @@ export const useOptions = defineStore('options2', () => {
name: 'output.intro'
});
const optionOutputMinifyInternalExports = getBoolean({
available: outputHasMultipleChunks,
available: alwaysTrue,
name: 'output.minifyInternalExports'
});
const optionOutputNoConflict = getBoolean({
Expand Down Expand Up @@ -315,7 +315,7 @@ export const useOptions = defineStore('options2', () => {
name: 'output.sourcemap'
});
const optionOutputSanitizeFileName = getBoolean({
available: outputHasMultipleChunks,
available: alwaysTrue,
defaultValue: true,
name: 'output.sanitizeFileName'
});
Expand All @@ -337,7 +337,7 @@ export const useOptions = defineStore('options2', () => {
name: 'output.validate'
});
const optionPreserveEntrySignatures = getSelect({
available: outputHasMultipleChunks,
available: alwaysTrue,
defaultValue: 'exports-only',
name: 'preserveEntrySignatures',
options: () => ['strict', 'allow-extension', 'exports-only', false]
Expand Down
11 changes: 10 additions & 1 deletion docs/repl/stores/rollupOutput.ts
Expand Up @@ -48,7 +48,7 @@ async function bundle({ rollup: { instance }, modules, options, setOutput }: Bun
if (import.meta.env.PROD) {
console.clear();
}
console.log(`running Rollup version %c${instance.VERSION}`, 'font-weight: bold');
console.group(`running Rollup version ${instance.VERSION}`);

const modulesById = new Map<string, Module>();
for (const module of modules) {
Expand Down Expand Up @@ -96,20 +96,29 @@ async function bundle({ rollup: { instance }, modules, options, setOutput }: Bun
]
};

console.log('%coptions:', 'font-weight: bold; color: blue', rollupOptions);
try {
const generated = await (
await instance.rollup(rollupOptions)
).generate((rollupOptions as { output?: OutputOptions }).output || {});
console.log('%coutput:', 'font-weight: bold; color: green', generated.output);
setOutput({
error: null,
externalImports: [...externalImports].sort((a, b) => (a < b ? -1 : 1)),
output: generated.output,
warnings
});
} catch (error) {
console.log(
'%cerror:',
'font-weight: bold; color: red',
error,
JSON.parse(JSON.stringify(error))
);
setOutput({ error: error as Error, externalImports: [], output: [], warnings });
logWarning(error as Error);
}
console.groupEnd();
}

export const useRollupOutput = defineStore('rollupOutput', () => {
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
@@ -1,6 +1,6 @@
{
"name": "rollup",
"version": "3.21.0",
"version": "3.21.1",
"description": "Next-generation ES module bundler",
"main": "dist/rollup.js",
"module": "dist/es/rollup.js",
Expand Down
44 changes: 13 additions & 31 deletions scripts/release.js
Expand Up @@ -8,7 +8,7 @@ import inquirer from 'inquirer';
import semverInc from 'semver/functions/inc.js';
import semverParse from 'semver/functions/parse.js';
import semverPreRelease from 'semver/functions/prerelease.js';
import { cyan, red } from './colors.js';
import { cyan } from './colors.js';
import { runAndGetStdout, runWithEcho } from './helpers.js';

// We execute everything from the main directory
Expand Down Expand Up @@ -240,44 +240,26 @@ async function installDependenciesBuildAndTest() {
}

async function waitForChangelogUpdate(version) {
const { changelogUpdated } = await inquirer.prompt([
{
message: `Please confirm that the changelog has been updated to continue`,
name: 'changelogUpdated',
type: 'confirm'
}
]);

if (!changelogUpdated) {
console.log(red`Aborting release.`);
exit();
}

let changelogEntry = '';
while (true) {
await runWithEcho('npx', ['prettier', '--write', CHANGELOG]);
const changelog = await readFile(CHANGELOG, 'utf8');
const { text: newEntry } = getFirstChangelogEntry(changelog);
if (newEntry !== changelogEntry) {
changelogEntry = newEntry;
console.log(cyan('You generated the following changelog entry:\n') + changelogEntry);
const { changelogConfirmed } = await inquirer.prompt([
{
message: `Please edit the changelog again or confirm the changelog is acceptable to continue to release "${version}".`,
name: 'changelogConfirmed',
type: 'confirm'
}
]);
if (!changelogConfirmed) {
console.log(red`Aborting release.`);
exit();
}
continue;
if (newEntry === changelogEntry) {
console.log(cyan('No further changes, continuing release.'));
break;
}
break;
changelogEntry = newEntry;
console.log(cyan('You generated the following changelog entry:\n') + changelogEntry);
await inquirer.prompt([
{
choices: ['ok'],
message: `Please edit the changelog or confirm the changelog is acceptable to continue to release "${version}".`,
type: 'list'
}
]);
}

console.log(cyan('No further changes, continuing release.'));
return changelogEntry;
}

Expand Down
5 changes: 5 additions & 0 deletions src/ast/nodes/shared/FunctionBase.ts
Expand Up @@ -54,8 +54,11 @@ export default abstract class FunctionBase extends NodeBase {
argument.deoptimizePath(UNKNOWN_PATH);
} else if (parameter instanceof Identifier) {
parameters[position][0].addEntityToBeDeoptimized(argument);
this.addArgumentToBeDeoptimized(argument);
} else if (parameter) {
argument.deoptimizePath(UNKNOWN_PATH);
} else {
this.addArgumentToBeDeoptimized(argument);
}
}
} else {
Expand Down Expand Up @@ -181,6 +184,8 @@ export default abstract class FunctionBase extends NodeBase {
super.parseNode(esTreeNode);
}

protected addArgumentToBeDeoptimized(_argument: ExpressionEntity) {}

protected applyDeoptimizations() {}

protected abstract getObjectEntity(): ObjectEntity;
Expand Down
5 changes: 5 additions & 0 deletions src/ast/nodes/shared/FunctionNode.ts
Expand Up @@ -5,6 +5,7 @@ import FunctionScope from '../../scopes/FunctionScope';
import type { ObjectPath, PathTracker } from '../../utils/PathTracker';
import type BlockStatement from '../BlockStatement';
import Identifier, { type IdentifierWithVariable } from '../Identifier';
import type { ExpressionEntity } from './Expression';
import { UNKNOWN_EXPRESSION } from './Expression';
import FunctionBase from './FunctionBase';
import { type IncludeChildren } from './Node';
Expand Down Expand Up @@ -95,6 +96,10 @@ export default class FunctionNode extends FunctionBase {
this.id?.declare('function', this);
}

protected addArgumentToBeDeoptimized(argument: ExpressionEntity) {
this.scope.argumentsVariable.addArgumentToBeDeoptimized(argument);
}

protected getObjectEntity(): ObjectEntity {
if (this.objectEntity !== null) {
return this.objectEntity;
Expand Down
20 changes: 20 additions & 0 deletions src/ast/variables/ArgumentsVariable.ts
@@ -1,16 +1,36 @@
import type { AstContext } from '../../Module';
import type { NodeInteraction } from '../NodeInteractions';
import { INTERACTION_ACCESSED } from '../NodeInteractions';
import type { ExpressionEntity } from '../nodes/shared/Expression';
import { UNKNOWN_EXPRESSION } from '../nodes/shared/Expression';
import type { ObjectPath } from '../utils/PathTracker';
import { UNKNOWN_PATH } from '../utils/PathTracker';
import LocalVariable from './LocalVariable';

export default class ArgumentsVariable extends LocalVariable {
private deoptimizedArguments: ExpressionEntity[] = [];

constructor(context: AstContext) {
super('arguments', null, UNKNOWN_EXPRESSION, context);
}

addArgumentToBeDeoptimized(argument: ExpressionEntity): void {
if (this.included) {
argument.deoptimizePath(UNKNOWN_PATH);
} else {
this.deoptimizedArguments.push(argument);
}
}

hasEffectsOnInteractionAtPath(path: ObjectPath, { type }: NodeInteraction): boolean {
return type !== INTERACTION_ACCESSED || path.length > 1;
}

include() {
super.include();
for (const argument of this.deoptimizedArguments) {
argument.deoptimizePath(UNKNOWN_PATH);
}
this.deoptimizedArguments.length = 0;
}
}
3 changes: 3 additions & 0 deletions test/function/samples/deoptimize-via-arguments/_config.js
@@ -0,0 +1,3 @@
module.exports = {
description: 'deoptimize call parameters when arguments variable is used'
};
17 changes: 17 additions & 0 deletions test/function/samples/deoptimize-via-arguments/main.js
@@ -0,0 +1,17 @@
function mutate(a) {
arguments[0].x = true;
arguments[1].x = true;
}

var obj1 = {
x: false
};

var obj2 = {
x: false
};

mutate(obj1, obj2);

assert.ok(obj1.x ? true : false);
assert.ok(obj2.x ? true : false);
2 changes: 1 addition & 1 deletion test/watch/index.js
Expand Up @@ -1263,7 +1263,7 @@ describe('rollup.watch', () => {

it('rebuilds immediately by default', async () => {
await copy('test/watch/samples/basic', 'test/_tmp/input');
await wait(200);
await wait(300);
watcher = rollup.watch({
input: 'test/_tmp/input/main.js',
output: {
Expand Down

0 comments on commit 39def64

Please sign in to comment.