diff --git a/docs/999-big-list-of-options.md b/docs/999-big-list-of-options.md
index 5b105f9a8af..a0863c232bc 100755
--- a/docs/999-big-list-of-options.md
+++ b/docs/999-big-list-of-options.md
@@ -802,7 +802,7 @@ Default: `false`
If this option is provided, bundling will not fail if bindings are imported from a file that does not define these bindings. Instead, new variables will be created for these bindings with the value `undefined`.
#### treeshake
-Type: `boolean | { annotations?: boolean, moduleSideEffects?: ModuleSideEffectsOption, propertyReadSideEffects?: boolean }`
+Type: `boolean | { annotations?: boolean, moduleSideEffects?: ModuleSideEffectsOption, propertyReadSideEffects?: boolean, tryCatchDeoptimization?: boolean, unknownGlobalSideEffects?: boolean }`
CLI: `--treeshake`/`--no-treeshake`
Default: `true`
@@ -938,6 +938,29 @@ test(otherFn);
```
+**treeshake.unknownGlobalSideEffects**
+Type: `boolean`
+CLI: `--treeshake.unknownGlobalSideEffects`/`--no-treeshake.unknownGlobalSideEffects`
+Default: `true`
+
+Since accessing a non-existing global variable will throw an error, Rollup does by default retain any accesses to non-builtin global variables. Set this option to `false` to avoid this check. This is probably safe for most code-bases.
+
+```js
+// input
+const jQuery = $;
+const requestTimeout = setTimeout;
+const element = angular.element;
+
+// output with unknownGlobalSideEffects == true
+const jQuery = $;
+const element = angular.element;
+
+// output with unknownGlobalSideEffects == false
+const element = angular.element;
+```
+
+In the example, the last line is always retained as accessing the `element` property could also throw an error if `angular` is e.g. `null`. To avoid this check, set `treeshake.propertyReadSideEffects` to `false` as well.
+
### Experimental options
These options reflect new features that have not yet been fully finalized. Availability, behaviour and usage may therefore be subject to change between minor versions.
diff --git a/src/Graph.ts b/src/Graph.ts
index b210a13ba92..632e4aa82cd 100644
--- a/src/Graph.ts
+++ b/src/Graph.ts
@@ -116,13 +116,16 @@ export default class Graph {
(options.treeshake as TreeshakingOptions).propertyReadSideEffects !== false,
pureExternalModules: (options.treeshake as TreeshakingOptions).pureExternalModules,
tryCatchDeoptimization:
- (options.treeshake as TreeshakingOptions).tryCatchDeoptimization !== false
+ (options.treeshake as TreeshakingOptions).tryCatchDeoptimization !== false,
+ unknownGlobalSideEffects:
+ (options.treeshake as TreeshakingOptions).unknownGlobalSideEffects !== false
}
: {
annotations: true,
moduleSideEffects: true,
propertyReadSideEffects: true,
- tryCatchDeoptimization: true
+ tryCatchDeoptimization: true,
+ unknownGlobalSideEffects: true
};
if (typeof this.treeshakingOptions.pureExternalModules !== 'undefined') {
this.warnDeprecation(
diff --git a/src/Module.ts b/src/Module.ts
index ea045ffffc4..0920e33a295 100644
--- a/src/Module.ts
+++ b/src/Module.ts
@@ -110,6 +110,7 @@ export interface AstContext {
traceVariable: (name: string) => Variable | null;
treeshake: boolean;
tryCatchDeoptimization: boolean;
+ unknownGlobalSideEffects: boolean;
usesTopLevelAwait: boolean;
warn: (warning: RollupWarning, pos: number) => void;
warnDeprecation: (deprecation: string | RollupWarning, activeDeprecation: boolean) => void;
@@ -605,6 +606,8 @@ export default class Module {
treeshake: !!this.graph.treeshakingOptions,
tryCatchDeoptimization: (!this.graph.treeshakingOptions ||
this.graph.treeshakingOptions.tryCatchDeoptimization) as boolean,
+ unknownGlobalSideEffects: (!this.graph.treeshakingOptions ||
+ this.graph.treeshakingOptions.unknownGlobalSideEffects) as boolean,
usesTopLevelAwait: false,
warn: this.warn.bind(this),
warnDeprecation: this.graph.warnDeprecation.bind(this.graph)
diff --git a/src/ast/nodes/Identifier.ts b/src/ast/nodes/Identifier.ts
index 1b0937baf46..056e8338d15 100644
--- a/src/ast/nodes/Identifier.ts
+++ b/src/ast/nodes/Identifier.ts
@@ -102,8 +102,8 @@ export default class Identifier extends NodeBase implements PatternNode {
}
hasEffects(): boolean {
- // TODO Lukas observe the option here
return (
+ this.context.unknownGlobalSideEffects &&
this.variable instanceof GlobalVariable &&
this.variable.hasEffectsWhenAccessedAtPath(EMPTY_PATH)
);
diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts
index f24e305b4d7..22e06a53580 100644
--- a/src/rollup/types.d.ts
+++ b/src/rollup/types.d.ts
@@ -385,6 +385,7 @@ export interface TreeshakingOptions {
/** @deprecated Use `moduleSideEffects` instead */
pureExternalModules?: PureModulesOption;
tryCatchDeoptimization?: boolean;
+ unknownGlobalSideEffects?: boolean;
}
export type GetManualChunk = (id: string) => string | null | undefined;
diff --git a/test/form/samples/ignore-unknown-global-side-effects/_config.js b/test/form/samples/ignore-unknown-global-side-effects/_config.js
new file mode 100644
index 00000000000..08ae295d520
--- /dev/null
+++ b/test/form/samples/ignore-unknown-global-side-effects/_config.js
@@ -0,0 +1,10 @@
+module.exports = {
+ description:
+ 'ignore side-effects when accessing unknown globals if treeshake.unknownGlobalSideEffects is false',
+ expectedWarnings: ['EMPTY_BUNDLE'],
+ options: {
+ treeshake: {
+ unknownGlobalSideEffects: false
+ }
+ }
+};
diff --git a/test/form/samples/ignore-unknown-global-side-effects/_expected.js b/test/form/samples/ignore-unknown-global-side-effects/_expected.js
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/test/form/samples/ignore-unknown-global-side-effects/_expected.js
@@ -0,0 +1 @@
+
diff --git a/test/form/samples/ignore-unknown-global-side-effects/main.js b/test/form/samples/ignore-unknown-global-side-effects/main.js
new file mode 100644
index 00000000000..5efa5034a33
--- /dev/null
+++ b/test/form/samples/ignore-unknown-global-side-effects/main.js
@@ -0,0 +1,2 @@
+foo;
+class bar extends baz {}