-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve if statement rendering #2146
Changes from all commits
3d4fda0
c1bc6ca
6c002d1
8e3495f
d21b935
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,27 +14,32 @@ export default class IfStatement extends StatementBase { | |
private hasUnknownTestValue: boolean; | ||
|
||
hasEffects(options: ExecutionPathOptions): boolean { | ||
return ( | ||
this.test.hasEffects(options) || | ||
(this.hasUnknownTestValue | ||
? this.consequent.hasEffects(options) || | ||
(this.alternate !== null && this.alternate.hasEffects(options)) | ||
: this.someRelevantBranch(node => node.hasEffects(options))) | ||
); | ||
if (this.test.hasEffects(options)) return true; | ||
const testValue = this.hasUnknownTestValue ? UNKNOWN_VALUE : this.getTestValue(); | ||
if (testValue === UNKNOWN_VALUE) { | ||
return ( | ||
this.consequent.hasEffects(options) || | ||
(this.alternate !== null && this.alternate.hasEffects(options)) | ||
); | ||
} | ||
return testValue | ||
? this.consequent.hasEffects(options) | ||
: this.alternate !== null && this.alternate.hasEffects(options); | ||
} | ||
|
||
include() { | ||
this.included = true; | ||
const testValue = this.test.getValue(); | ||
const testValue = this.hasUnknownTestValue ? UNKNOWN_VALUE : this.getTestValue(); | ||
if (testValue === UNKNOWN_VALUE || this.test.shouldBeIncluded()) { | ||
this.test.include(); | ||
} | ||
if (testValue === UNKNOWN_VALUE) { | ||
this.consequent.include(); | ||
if (this.alternate !== null) this.alternate.include(); | ||
} else if (testValue) { | ||
if ((testValue === UNKNOWN_VALUE || testValue) && this.consequent.shouldBeIncluded()) { | ||
this.consequent.include(); | ||
} else if (this.alternate !== null) { | ||
} | ||
if ( | ||
this.alternate !== null && | ||
((testValue === UNKNOWN_VALUE || !testValue) && this.alternate.shouldBeIncluded()) | ||
) { | ||
this.alternate.include(); | ||
} | ||
} | ||
|
@@ -45,33 +50,43 @@ export default class IfStatement extends StatementBase { | |
} | ||
|
||
render(code: MagicString, options: RenderOptions) { | ||
const testValue = this.test.getValue(); | ||
// Note that unknown test values are always included | ||
const testValue = this.hasUnknownTestValue ? UNKNOWN_VALUE : this.test.getValue(); | ||
if ( | ||
!this.context.treeshake || | ||
this.test.included || | ||
(testValue ? this.alternate !== null && this.alternate.included : this.consequent.included) | ||
!this.test.included && | ||
(testValue ? this.alternate === null || !this.alternate.included : !this.consequent.included) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps put brackets around the (A || B) clause here for clarity (although I usually like to skip brackets for && though out of brevity provided those trained by C compilers can get past their reservations... :P) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try this and you might learn something new about prettier 😜 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well there goes that debate...! |
||
) { | ||
super.render(code, options); | ||
const singleRetainedBranch = testValue ? this.consequent : this.alternate; | ||
code.remove(this.start, singleRetainedBranch.start); | ||
code.remove(singleRetainedBranch.end, this.end); | ||
singleRetainedBranch.render(code, options); | ||
} else { | ||
// if test is not included, it is impossible that alternate===null even though it is the retained branch | ||
const branchToRetain = testValue ? this.consequent : this.alternate; | ||
code.remove(this.start, branchToRetain.start); | ||
code.remove(branchToRetain.end, this.end); | ||
branchToRetain.render(code, options); | ||
if (this.test.included) { | ||
this.test.render(code, options); | ||
} else { | ||
code.overwrite(this.test.start, this.test.end, testValue ? 'true' : 'false'); | ||
} | ||
if (this.consequent.included) { | ||
this.consequent.render(code, options); | ||
} else { | ||
code.overwrite(this.consequent.start, this.consequent.end, ';'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not expecting to get this in on this PR, but how about optimizing this a little further? if (this.alternate === null || !this.alternate.included) {
code.overwrite(this.start, this.start + 3, '');
code.overwrite(this.consequent.start - 1, this.consequent.end, ';');
} else {
code.insertLeft(this.test.start, '!');
code.overwrite(this.consequent.start, this.consequent.end + 'false'.length, '');
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So if I see this correctly, if you have both the test and only one branch included, you want to render them as just two separate statements? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was actually meant in the cases of the test not being included to have: if (true) effect(); become if (false) ;
else
effect(); become There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can certainly look at a separate PR though! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh I see - the two statement variation would also be worthwhile here yes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the test is not included, it already works this way! This is the first branch with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So missing (for a future PR?) are:
|
||
} | ||
if (this.alternate !== null) { | ||
if (this.alternate.included) { | ||
this.alternate.render(code, options); | ||
} else { | ||
code.remove(this.consequent.end, this.alternate.end); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private someRelevantBranch(predicateFunction: (node: StatementNode) => boolean): boolean { | ||
const testValue = this.test.getValue(); | ||
if (testValue === UNKNOWN_VALUE) { | ||
private getTestValue() { | ||
if (this.hasUnknownTestValue) return UNKNOWN_VALUE; | ||
const value = this.test.getValue(); | ||
if (value === UNKNOWN_VALUE) { | ||
this.hasUnknownTestValue = true; | ||
return ( | ||
predicateFunction(this.consequent) || | ||
(this.alternate !== null && predicateFunction(this.alternate)) | ||
); | ||
} | ||
return testValue | ||
? predicateFunction(this.consequent) | ||
: this.alternate !== null && predicateFunction(this.alternate); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice simplification to remove this! |
||
return value; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've always wondered what this means - is it referring to
obj.path[c ? d : e]
? Or something else?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it means you access e.g.
(c ? d : e).the.path
. In this example, the path['the', 'path']
ofc ? d : e
is accessed.For
obj.path[c ? d : e]
, we would just be accessing the path[UNKNOWN_KEY]
ofobj.path
. The "unknown" is because non-literal computed keys are never evaluated at the moment (we might extendgetValueAtPath
to be used here as well, though).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks that helps a lot... I may start PR'ing some comments if I work on these parts to help those who follow.