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 {}