diff --git a/src/ast/nodes/Identifier.ts b/src/ast/nodes/Identifier.ts index 97a4f560092..d21db0dd645 100644 --- a/src/ast/nodes/Identifier.ts +++ b/src/ast/nodes/Identifier.ts @@ -14,7 +14,7 @@ import LocalVariable from '../variables/LocalVariable'; import Variable from '../variables/Variable'; import * as NodeType from './NodeType'; import SpreadElement from './SpreadElement'; -import { ExpressionEntity, LiteralValueOrUnknown } from './shared/Expression'; +import { ExpressionEntity, LiteralValueOrUnknown, UNKNOWN_EXPRESSION } from './shared/Expression'; import { ExpressionNode, NodeBase } from './shared/Node'; import { PatternNode } from './shared/Pattern'; @@ -45,9 +45,15 @@ export default class Identifier extends NodeBase implements PatternNode { declare(kind: string, init: ExpressionEntity): LocalVariable[] { let variable: LocalVariable; + const { treeshake } = this.context.options; switch (kind) { case 'var': - variable = this.scope.addDeclaration(this, this.context, init, true); + variable = this.scope.addDeclaration( + this, + this.context, + treeshake && treeshake.correctVarValueBeforeDeclaration ? UNKNOWN_EXPRESSION : init, + true + ); break; case 'function': // in strict mode, functions are only hoisted within a scope but not across block scopes diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index 8c3a17c3e05..a9a146a791a 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -478,6 +478,7 @@ type TreeshakingPreset = 'smallest' | 'safest' | 'recommended'; export interface TreeshakingOptions { annotations?: boolean; + correctVarValueBeforeDeclaration?: boolean; moduleSideEffects?: ModuleSideEffectsOption; preset?: TreeshakingPreset; propertyReadSideEffects?: boolean | 'always'; @@ -489,6 +490,7 @@ export interface TreeshakingOptions { export interface NormalizedTreeshakingOptions { annotations: boolean; + correctVarValueBeforeDeclaration: boolean; moduleSideEffects: HasModuleSideEffects; propertyReadSideEffects: boolean | 'always'; tryCatchDeoptimization: boolean; diff --git a/src/utils/options/normalizeInputOptions.ts b/src/utils/options/normalizeInputOptions.ts index 96cfba38885..d49bdecba28 100644 --- a/src/utils/options/normalizeInputOptions.ts +++ b/src/utils/options/normalizeInputOptions.ts @@ -7,6 +7,7 @@ import { PreserveEntrySignaturesOption, PureModulesOption, RollupBuild, + TreeshakingOptions, WarningHandler } from '../../rollup/types'; import { ensureArray } from '../ensureArray'; @@ -233,15 +234,21 @@ const getTreeshake = ( if (configTreeshake === false) { return false; } - if (!configTreeshake || configTreeshake === true) { - return { - annotations: true, - moduleSideEffects: () => true, - propertyReadSideEffects: true, - tryCatchDeoptimization: true, - unknownGlobalSideEffects: true - }; + if (typeof configTreeshake === 'string') { + const preset = treeshakePresets[configTreeshake]; + if (preset) { + return preset; + } + error( + errInvalidOption( + 'treeshake', + `valid values are false, true, ${printQuotedStringList( + Object.keys(treeshakePresets) + )}. You can also supply an object for more fine-grained control` + ) + ); } + let configWithPreset: TreeshakingOptions = {}; if (typeof configTreeshake === 'object') { if (typeof configTreeshake.pureExternalModules !== 'undefined') { warnDeprecationWithOptions( @@ -251,7 +258,7 @@ const getTreeshake = ( strictDeprecations ); } - let configWithPreset = configTreeshake; + configWithPreset = configTreeshake; const presetName = configTreeshake.preset; if (presetName) { const preset = treeshakePresets[presetName]; @@ -266,34 +273,24 @@ const getTreeshake = ( ); } } - return { - annotations: configWithPreset.annotations !== false, - moduleSideEffects: configTreeshake.pureExternalModules + } + return { + annotations: configWithPreset.annotations !== false, + correctVarValueBeforeDeclaration: configWithPreset.correctVarValueBeforeDeclaration === true, + moduleSideEffects: + typeof configTreeshake === 'object' && configTreeshake.pureExternalModules ? getHasModuleSideEffects( configTreeshake.moduleSideEffects, configTreeshake.pureExternalModules ) : getHasModuleSideEffects(configWithPreset.moduleSideEffects, undefined), - propertyReadSideEffects: - configWithPreset.propertyReadSideEffects === 'always' - ? 'always' - : configWithPreset.propertyReadSideEffects !== false, - tryCatchDeoptimization: configWithPreset.tryCatchDeoptimization !== false, - unknownGlobalSideEffects: configWithPreset.unknownGlobalSideEffects !== false - }; - } - const preset = treeshakePresets[configTreeshake]; - if (preset) { - return preset; - } - error( - errInvalidOption( - 'treeshake', - `valid values are false, true, ${printQuotedStringList( - Object.keys(treeshakePresets) - )}. You can also supply an object for more fine-grained control` - ) - ); + propertyReadSideEffects: + configWithPreset.propertyReadSideEffects === 'always' + ? 'always' + : configWithPreset.propertyReadSideEffects !== false, + tryCatchDeoptimization: configWithPreset.tryCatchDeoptimization !== false, + unknownGlobalSideEffects: configWithPreset.unknownGlobalSideEffects !== false + }; }; const getHasModuleSideEffects = ( diff --git a/src/utils/options/options.ts b/src/utils/options/options.ts index 22cf89d2923..8899b29476f 100644 --- a/src/utils/options/options.ts +++ b/src/utils/options/options.ts @@ -38,6 +38,7 @@ export const treeshakePresets: { } = { recommended: { annotations: true, + correctVarValueBeforeDeclaration: false, moduleSideEffects: () => true, propertyReadSideEffects: true, tryCatchDeoptimization: true, @@ -45,6 +46,7 @@ export const treeshakePresets: { }, safest: { annotations: true, + correctVarValueBeforeDeclaration: true, moduleSideEffects: () => true, propertyReadSideEffects: true, tryCatchDeoptimization: true, @@ -52,6 +54,7 @@ export const treeshakePresets: { }, smallest: { annotations: true, + correctVarValueBeforeDeclaration: false, moduleSideEffects: () => false, propertyReadSideEffects: false, tryCatchDeoptimization: false, diff --git a/test/cli/samples/treeshake-preset-override/main.js b/test/cli/samples/treeshake-preset-override/main.js index 2ef8f761fe7..46a542d6190 100644 --- a/test/cli/samples/treeshake-preset-override/main.js +++ b/test/cli/samples/treeshake-preset-override/main.js @@ -13,3 +13,6 @@ try { } catch {} unknownGlobal; + +if (!foo) console.log('effect'); +var foo = true; diff --git a/test/form/samples/treeshake-presets/preset-with-override/_config.js b/test/form/samples/treeshake-presets/preset-with-override/_config.js index 73bd2448ad1..cf5764aed70 100644 --- a/test/form/samples/treeshake-presets/preset-with-override/_config.js +++ b/test/form/samples/treeshake-presets/preset-with-override/_config.js @@ -11,6 +11,7 @@ module.exports = { plugins: [ { buildStart(options) { + assert.strictEqual(options.treeshake.correctVarValueBeforeDeclaration, false); assert.strictEqual(options.treeshake.propertyReadSideEffects, false); assert.strictEqual(options.treeshake.tryCatchDeoptimization, false); assert.strictEqual(options.treeshake.unknownGlobalSideEffects, true); diff --git a/test/form/samples/treeshake-presets/preset-with-override/main.js b/test/form/samples/treeshake-presets/preset-with-override/main.js index 2ef8f761fe7..46a542d6190 100644 --- a/test/form/samples/treeshake-presets/preset-with-override/main.js +++ b/test/form/samples/treeshake-presets/preset-with-override/main.js @@ -13,3 +13,6 @@ try { } catch {} unknownGlobal; + +if (!foo) console.log('effect'); +var foo = true; diff --git a/test/form/samples/treeshake-presets/recommended/_config.js b/test/form/samples/treeshake-presets/recommended/_config.js index e943af3e2aa..ada92c53ef7 100644 --- a/test/form/samples/treeshake-presets/recommended/_config.js +++ b/test/form/samples/treeshake-presets/recommended/_config.js @@ -8,6 +8,7 @@ module.exports = { plugins: [ { buildStart(options) { + assert.strictEqual(options.treeshake.correctVarValueBeforeDeclaration, false); assert.strictEqual(options.treeshake.propertyReadSideEffects, true); assert.strictEqual(options.treeshake.tryCatchDeoptimization, true); assert.strictEqual(options.treeshake.unknownGlobalSideEffects, false); diff --git a/test/form/samples/treeshake-presets/recommended/main.js b/test/form/samples/treeshake-presets/recommended/main.js index 2ef8f761fe7..46a542d6190 100644 --- a/test/form/samples/treeshake-presets/recommended/main.js +++ b/test/form/samples/treeshake-presets/recommended/main.js @@ -13,3 +13,6 @@ try { } catch {} unknownGlobal; + +if (!foo) console.log('effect'); +var foo = true; diff --git a/test/form/samples/treeshake-presets/safest/_config.js b/test/form/samples/treeshake-presets/safest/_config.js index 35b71dfd8a1..3e5353f5c7e 100644 --- a/test/form/samples/treeshake-presets/safest/_config.js +++ b/test/form/samples/treeshake-presets/safest/_config.js @@ -8,6 +8,7 @@ module.exports = { plugins: [ { buildStart(options) { + assert.strictEqual(options.treeshake.correctVarValueBeforeDeclaration, true); assert.strictEqual(options.treeshake.propertyReadSideEffects, true); assert.strictEqual(options.treeshake.tryCatchDeoptimization, true); assert.strictEqual(options.treeshake.unknownGlobalSideEffects, true); diff --git a/test/form/samples/treeshake-presets/safest/_expected.js b/test/form/samples/treeshake-presets/safest/_expected.js index 3df383b997e..a859fbd4964 100644 --- a/test/form/samples/treeshake-presets/safest/_expected.js +++ b/test/form/samples/treeshake-presets/safest/_expected.js @@ -13,3 +13,6 @@ try { } catch {} unknownGlobal; + +if (!foo) console.log('effect'); +var foo = true; diff --git a/test/form/samples/treeshake-presets/safest/main.js b/test/form/samples/treeshake-presets/safest/main.js index 2ef8f761fe7..46a542d6190 100644 --- a/test/form/samples/treeshake-presets/safest/main.js +++ b/test/form/samples/treeshake-presets/safest/main.js @@ -13,3 +13,6 @@ try { } catch {} unknownGlobal; + +if (!foo) console.log('effect'); +var foo = true; diff --git a/test/form/samples/treeshake-presets/smallest/_config.js b/test/form/samples/treeshake-presets/smallest/_config.js index ac49dae5a46..39e1162c861 100644 --- a/test/form/samples/treeshake-presets/smallest/_config.js +++ b/test/form/samples/treeshake-presets/smallest/_config.js @@ -8,6 +8,7 @@ module.exports = { plugins: [ { buildStart(options) { + assert.strictEqual(options.treeshake.correctVarValueBeforeDeclaration, false); assert.strictEqual(options.treeshake.propertyReadSideEffects, false); assert.strictEqual(options.treeshake.tryCatchDeoptimization, false); assert.strictEqual(options.treeshake.unknownGlobalSideEffects, false); diff --git a/test/form/samples/treeshake-presets/smallest/main.js b/test/form/samples/treeshake-presets/smallest/main.js index 2ef8f761fe7..46a542d6190 100644 --- a/test/form/samples/treeshake-presets/smallest/main.js +++ b/test/form/samples/treeshake-presets/smallest/main.js @@ -13,3 +13,6 @@ try { } catch {} unknownGlobal; + +if (!foo) console.log('effect'); +var foo = true; diff --git a/test/form/samples/treeshake-presets/true/_config.js b/test/form/samples/treeshake-presets/true/_config.js index 9a024352632..0445050db42 100644 --- a/test/form/samples/treeshake-presets/true/_config.js +++ b/test/form/samples/treeshake-presets/true/_config.js @@ -8,6 +8,7 @@ module.exports = { plugins: [ { buildStart(options) { + assert.strictEqual(options.treeshake.correctVarValueBeforeDeclaration, false); assert.strictEqual(options.treeshake.propertyReadSideEffects, true); assert.strictEqual(options.treeshake.tryCatchDeoptimization, true); assert.strictEqual(options.treeshake.unknownGlobalSideEffects, true); diff --git a/test/form/samples/treeshake-presets/true/main.js b/test/form/samples/treeshake-presets/true/main.js index 2ef8f761fe7..46a542d6190 100644 --- a/test/form/samples/treeshake-presets/true/main.js +++ b/test/form/samples/treeshake-presets/true/main.js @@ -13,3 +13,6 @@ try { } catch {} unknownGlobal; + +if (!foo) console.log('effect'); +var foo = true;