diff --git a/src/ast/nodes/AwaitExpression.ts b/src/ast/nodes/AwaitExpression.ts index 2240c4f1321..efa95f74f86 100644 --- a/src/ast/nodes/AwaitExpression.ts +++ b/src/ast/nodes/AwaitExpression.ts @@ -1,5 +1,3 @@ -import MagicString from 'magic-string'; -import { RenderOptions } from '../../utils/renderHelpers'; import { HasEffectsContext, InclusionContext } from '../ExecutionContext'; import ArrowFunctionExpression from './ArrowFunctionExpression'; import * as NodeType from './NodeType'; @@ -11,22 +9,21 @@ export default class AwaitExpression extends NodeBase { type!: NodeType.tAwaitExpression; hasEffects(context: HasEffectsContext) { - return super.hasEffects(context) || !context.ignore.returnAwaitYield; + return !context.ignore.returnAwaitYield || this.argument.hasEffects(context); } include(context: InclusionContext, includeChildrenRecursively: IncludeChildren) { - checkTopLevelAwait: if (!this.included && !this.context.usesTopLevelAwait) { - let parent = this.parent; - do { - if (parent instanceof FunctionNode || parent instanceof ArrowFunctionExpression) - break checkTopLevelAwait; - } while ((parent = (parent as Node).parent as Node)); - this.context.usesTopLevelAwait = true; + if (!this.included) { + this.included = true; + checkTopLevelAwait: if (!this.context.usesTopLevelAwait) { + let parent = this.parent; + do { + if (parent instanceof FunctionNode || parent instanceof ArrowFunctionExpression) + break checkTopLevelAwait; + } while ((parent = (parent as Node).parent as Node)); + this.context.usesTopLevelAwait = true; + } } - super.include(context, includeChildrenRecursively); - } - - render(code: MagicString, options: RenderOptions) { - super.render(code, options); + this.argument.include(context, includeChildrenRecursively); } } diff --git a/src/ast/nodes/ForInStatement.ts b/src/ast/nodes/ForInStatement.ts index 09dba4bb930..29613a2dd8a 100644 --- a/src/ast/nodes/ForInStatement.ts +++ b/src/ast/nodes/ForInStatement.ts @@ -60,6 +60,10 @@ export default class ForInStatement extends StatementBase { render(code: MagicString, options: RenderOptions) { this.left.render(code, options, NO_SEMICOLON); this.right.render(code, options, NO_SEMICOLON); + // handle no space between "in" and the right side + if (code.original.charCodeAt(this.right.start - 1) === 110 /* n */) { + code.prependLeft(this.right.start, ' '); + } this.body.render(code, options); } } diff --git a/src/ast/nodes/ForOfStatement.ts b/src/ast/nodes/ForOfStatement.ts index 29b0a081f1a..c5ca0d0f728 100644 --- a/src/ast/nodes/ForOfStatement.ts +++ b/src/ast/nodes/ForOfStatement.ts @@ -45,6 +45,10 @@ export default class ForOfStatement extends StatementBase { render(code: MagicString, options: RenderOptions) { this.left.render(code, options, NO_SEMICOLON); this.right.render(code, options, NO_SEMICOLON); + // handle no space between "of" and the right side + if (code.original.charCodeAt(this.right.start - 1) === 102 /* f */) { + code.prependLeft(this.right.start, ' '); + } this.body.render(code, options); } } diff --git a/src/ast/nodes/ThrowStatement.ts b/src/ast/nodes/ThrowStatement.ts index 3c6bbfffef5..bd52675707c 100644 --- a/src/ast/nodes/ThrowStatement.ts +++ b/src/ast/nodes/ThrowStatement.ts @@ -20,5 +20,8 @@ export default class ThrowStatement extends StatementBase { render(code: MagicString, options: RenderOptions) { this.argument.render(code, options, { preventASI: true }); + if (this.argument.start === this.start + 5 /* 'throw'.length */) { + code.prependLeft(this.start + 5, ' '); + } } } diff --git a/test/form/samples/side-effects-await/_config.js b/test/form/samples/side-effects-await/_config.js index 391e5d8e3e5..4f4ad0a1e63 100644 --- a/test/form/samples/side-effects-await/_config.js +++ b/test/form/samples/side-effects-await/_config.js @@ -1,5 +1,4 @@ module.exports = { description: - 'await statements should never be dropped if a function has other side-effects (#1584)', - options: { output: { name: 'myBundle' } } + 'await statements should never be dropped if a function has other side-effects (#1584)' }; diff --git a/test/form/samples/top-level-await/_expected/es.js b/test/form/samples/top-level-await/_expected/es.js index afb4a8fcd94..07c798efb2f 100644 --- a/test/form/samples/top-level-await/_expected/es.js +++ b/test/form/samples/top-level-await/_expected/es.js @@ -1 +1,3 @@ await operation(); + +await retainedOperation(); diff --git a/test/form/samples/top-level-await/_expected/system.js b/test/form/samples/top-level-await/_expected/system.js index c0417be381c..14c43dfc074 100644 --- a/test/form/samples/top-level-await/_expected/system.js +++ b/test/form/samples/top-level-await/_expected/system.js @@ -5,6 +5,8 @@ System.register([], function () { await operation(); + await retainedOperation(); + } }; }); diff --git a/test/form/samples/top-level-await/main.js b/test/form/samples/top-level-await/main.js index a9f75bdd1c0..94e77290f2b 100644 --- a/test/form/samples/top-level-await/main.js +++ b/test/form/samples/top-level-await/main.js @@ -1,4 +1,4 @@ await operation(); -if (false) - await treeshakenOperation(); \ No newline at end of file +if (false) await treeshakenOperation(); +else await retainedOperation(); diff --git a/test/function/samples/missing-spaces-after-simplification/_config.js b/test/function/samples/missing-spaces-after-simplification/_config.js new file mode 100644 index 00000000000..49540bde635 --- /dev/null +++ b/test/function/samples/missing-spaces-after-simplification/_config.js @@ -0,0 +1,5 @@ +module.exports = { + description: + 'handle situations where the simplification of an expression can lead to issues due to missing white-space', + options: { treeshake: { tryCatchDeoptimization: false } } +}; diff --git a/test/function/samples/missing-spaces-after-simplification/main.js b/test/function/samples/missing-spaces-after-simplification/main.js new file mode 100644 index 00000000000..0c8d6a3c1da --- /dev/null +++ b/test/function/samples/missing-spaces-after-simplification/main.js @@ -0,0 +1,28 @@ +let result = ''; + +const a = { a: true }; +for (const key in!0?a:a) result += key; + +const b = ['b']; +for (const value of!0?b:b) result += value; + +const c = 'c'; +function testReturn() { + return!0?c:c; +} +result += testReturn(); + +const d = 'd'; +function* testYield() { + yield!0?d:d; +} +for (const value of testYield()) result += value; + +const e = 'e'; +try { + throw!0?e:e; +} catch (err) { + result += err; +} + +assert.strictEqual(result, 'abcde');