Skip to content

Commit

Permalink
fix #2398: add the --drop-labels= option
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jul 15, 2023
1 parent c0f0380 commit 9b83e55
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 65 deletions.
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,38 @@

## Unreleased

* Add the `--drop-labels=` option ([#2398](https://github.com/evanw/esbuild/issues/2398))

If you want to conditionally disable some development-only code and have it not be present in the final production bundle, right now the most straightforward way of doing this is to use the `--define:` flag along with a specially-named global variable. For example, consider the following code:

```js
function main() {
DEV && doAnExpensiveCheck()
}
```

You can build this for development and production like this:

* Development: `esbuild --define:DEV=true`
* Production: `esbuild --define:DEV=false`

One drawback of this approach is that the resulting code crashes if you don't provide a value for `DEV` with `--define:`. In practice this isn't that big of a problem, and there are also various ways to work around this.

However, another approach that avoids this drawback is to use JavaScript label statements instead. That's what the `--drop-labels=` flag implements. For example, consider the following code:
```js
function main() {
DEV: doAnExpensiveCheck()
}
```
With this release, you can now build this for development and production like this:
* Development: `esbuild`
* Production: `esbuild --drop-labels=DEV`
This means that code containing optional development-only checks can now be written such that it's safe to run without any additional configuration. The `--drop-labels=` flag takes comma-separated list of multiple label names to drop.

* Avoid causing `unhandledRejection` during shutdown ([#3219](https://github.com/evanw/esbuild/issues/3219))

All pending esbuild JavaScript API calls are supposed to fail if esbuild's underlying child process is unexpectedly terminated. This can happen if `SIGINT` is sent to the parent `node` process with Ctrl+C, for example. Previously doing this could also cause an unhandled promise rejection when esbuild attempted to communicate this failure to its own child process that no longer exists. This release now swallows this communication failure, which should prevent this internal unhandled promise rejection. This change means that you can now use esbuild's JavaScript API with a custom `SIGINT` handler that extends the lifetime of the `node` process without esbuild's internals causing an early exit due to an unhandled promise rejection.
Expand Down
1 change: 1 addition & 0 deletions cmd/esbuild/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var helpText = func(colors logger.Colors) string {
(default "[name]-[hash]")
--color=... Force use of color terminal escapes (true | false)
--drop:... Remove certain constructs (console | debugger)
--drop-labels=... Remove labeled statements with these label names
--entry-names=... Path template to use for entry point output paths
(default "[dir]/[name]", can also use "[hash]")
--footer:T=... Text to be appended to each output file of type T
Expand Down
63 changes: 63 additions & 0 deletions internal/bundler_tests/bundler_dce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4447,3 +4447,66 @@ func TestDCEOfExprAfterKeepNamesIssue3195(t *testing.T) {
},
})
}

func TestDropLabels(t *testing.T) {
dce_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.js": `
keep_1: require('foo1')
DROP_1: require('bar1')
exports.bar = function() {
if (x) DROP_2: require('foo2')
if (y) keep_2: require('bar2')
}
`,
},
entryPaths: []string{"/entry.js"},
options: config.Options{
Mode: config.ModeBundle,
DropLabels: []string{
"DROP_1",
"DROP_2",
},
ExternalSettings: config.ExternalSettings{
PreResolve: config.ExternalMatchers{
Exact: map[string]bool{
"foo1": true,
"bar2": true,
},
},
},
AbsOutputFile: "/out.js",
OutputFormat: config.FormatCommonJS,
},
})
}

func TestRemoveCodeAfterLabelWithReturn(t *testing.T) {
dce_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.js": `
function earlyReturn() {
// This comes up when doing conditional compilation with "DropLabels"
keep: {
onlyWithKeep()
return
}
onlyWithoutKeep()
}
function loop() {
if (foo()) {
keep: {
bar()
return;
}
}
}
`,
},
entryPaths: []string{"/entry.js"},
options: config.Options{
AbsOutputFile: "/out.js",
MinifySyntax: true,
},
})
}
48 changes: 22 additions & 26 deletions internal/bundler_tests/bundler_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2797,38 +2797,34 @@ func TestMinifyNestedLabelsNoBundle(t *testing.T) {
default_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.js": `
L001:{L002:{L003:{L004:{L005:{L006:{L007:{L008:{L009:{L010:{L011:{L012:{L013:{L014:{L015:{L016:{nl('\n')
L017:{L018:{L019:{L020:{L021:{L022:{L023:{L024:{L025:{L026:{L027:{L028:{L029:{L030:{L031:{L032:{nl('\n')
L033:{L034:{L035:{L036:{L037:{L038:{L039:{L040:{L041:{L042:{L043:{L044:{L045:{L046:{L047:{L048:{nl('\n')
L049:{L050:{L051:{L052:{L053:{L054:{L055:{L056:{L057:{L058:{L059:{L060:{L061:{L062:{L063:{L064:{nl('\n')
L065:{L066:{L067:{L068:{L069:{L070:{L071:{L072:{L073:{L074:{L075:{L076:{L077:{L078:{L079:{L080:{nl('\n')
L081:{L082:{L083:{L084:{L085:{L086:{L087:{L088:{L089:{L090:{L091:{L092:{L093:{L094:{L095:{L096:{nl('\n')
L097:{L098:{L099:{L100:{L101:{L102:{L103:{L104:{L105:{L106:{L107:{L108:{L109:{L110:{L111:{L112:{nl('\n')
L113:{L114:{L115:{L116:{L117:{L118:{L119:{L120:{L121:{L122:{L123:{L124:{L125:{L126:{L127:{L128:{nl('\n')
L129:{L130:{L131:{L132:{L133:{L134:{L135:{L136:{L137:{L138:{L139:{L140:{L141:{L142:{L143:{L144:{nl('\n')
L145:{L146:{L147:{L148:{L149:{L150:{L151:{L152:{L153:{L154:{L155:{L156:{L157:{L158:{L159:{L160:{nl('\n')
L161:{L162:{L163:{L164:{L165:{L166:{L167:{L168:{L169:{L170:{L171:{L172:{L173:{L174:{L175:{L176:{nl('\n')
L177:{L178:{L179:{L180:{L181:{L182:{L183:{L184:{L185:{L186:{L187:{L188:{L189:{L190:{L191:{L192:{nl('\n')
L193:{L194:{L195:{L196:{L197:{L198:{L199:{L200:{L201:{L202:{L203:{L204:{L205:{L206:{L207:{L208:{nl('\n')
L209:{L210:{L211:{L212:{L213:{L214:{L215:{L216:{L217:{L218:{L219:{L220:{L221:{L222:{L223:{L224:{nl('\n')
L225:{L226:{L227:{L228:{L229:{L230:{L231:{L232:{L233:{L234:{L235:{L236:{L237:{L238:{L239:{L240:{nl('\n')
L241:{L242:{L243:{L244:{L245:{L246:{L247:{L248:{L249:{L250:{L251:{L252:{L253:{L254:{L255:{L256:{nl('\n')
L257:{L258:{L259:{L260:{L261:{L262:{L263:{L264:{L265:{L266:{L267:{L268:{L269:{L270:{L271:{L272:{nl('\n')
L273:{L274:{L275:{L276:{L277:{L278:{L279:{L280:{L281:{L282:{L283:{L284:{L285:{L286:{L287:{L288:{nl('\n')
L289:{L290:{L291:{L292:{L293:{L294:{L295:{L296:{L297:{L298:{L299:{L300:{L301:{L302:{L303:{L304:{nl('\n')
L305:{L306:{L307:{L308:{L309:{L310:{L311:{L312:{L313:{L314:{L315:{L316:{L317:{L318:{L319:{L320:{nl('\n')
L321:{L322:{L323:{L324:{L325:{L326:{L327:{L328:{L329:{L330:{L331:{L332:{L333:{}}}}}}}}}}}}}}}}}}nl('\n')
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}nl('\n')
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}nl('\n')
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}nl('\n')
}}}}}}}}}}}}}}}}}}}}}}}}}}}
L001:L002:L003:L004:L005:L006:L007:L008:L009:L010:L011:L012:L013:L014:L015:L016:{nl(` + "`\n`" + `)
L017:L018:L019:L020:L021:L022:L023:L024:L025:L026:L027:L028:L029:L030:L031:L032:{nl(` + "`\n`" + `)
L033:L034:L035:L036:L037:L038:L039:L040:L041:L042:L043:L044:L045:L046:L047:L048:{nl(` + "`\n`" + `)
L049:L050:L051:L052:L053:L054:L055:L056:L057:L058:L059:L060:L061:L062:L063:L064:{nl(` + "`\n`" + `)
L065:L066:L067:L068:L069:L070:L071:L072:L073:L074:L075:L076:L077:L078:L079:L080:{nl(` + "`\n`" + `)
L081:L082:L083:L084:L085:L086:L087:L088:L089:L090:L091:L092:L093:L094:L095:L096:{nl(` + "`\n`" + `)
L097:L098:L099:L100:L101:L102:L103:L104:L105:L106:L107:L108:L109:L110:L111:L112:{nl(` + "`\n`" + `)
L113:L114:L115:L116:L117:L118:L119:L120:L121:L122:L123:L124:L125:L126:L127:L128:{nl(` + "`\n`" + `)
L129:L130:L131:L132:L133:L134:L135:L136:L137:L138:L139:L140:L141:L142:L143:L144:{nl(` + "`\n`" + `)
L145:L146:L147:L148:L149:L150:L151:L152:L153:L154:L155:L156:L157:L158:L159:L160:{nl(` + "`\n`" + `)
L161:L162:L163:L164:L165:L166:L167:L168:L169:L170:L171:L172:L173:L174:L175:L176:{nl(` + "`\n`" + `)
L177:L178:L179:L180:L181:L182:L183:L184:L185:L186:L187:L188:L189:L190:L191:L192:{nl(` + "`\n`" + `)
L193:L194:L195:L196:L197:L198:L199:L200:L201:L202:L203:L204:L205:L206:L207:L208:{nl(` + "`\n`" + `)
L209:L210:L211:L212:L213:L214:L215:L216:L217:L218:L219:L220:L221:L222:L223:L224:{nl(` + "`\n`" + `)
L225:L226:L227:L228:L229:L230:L231:L232:L233:L234:L235:L236:L237:L238:L239:L240:{nl(` + "`\n`" + `)
L241:L242:L243:L244:L245:L246:L247:L248:L249:L250:L251:L252:L253:L254:L255:L256:{nl(` + "`\n`" + `)
L257:L258:L259:L260:L261:L262:L263:L264:L265:L266:L267:L268:L269:L270:L271:L272:{nl(` + "`\n`" + `)
L273:L274:L275:L276:L277:L278:L279:L280:L281:L282:L283:L284:L285:L286:L287:L288:{nl(` + "`\n`" + `)
L289:L290:L291:L292:L293:L294:L295:L296:L297:L298:L299:L300:L301:L302:L303:L304:{nl(` + "`\n`" + `)
L305:L306:L307:L308:L309:L310:L311:L312:L313:L314:L315:L316:L317:L318:L319:L320:{nl(` + "`\n`" + `)
L321:L322:L323:L324:L325:L326:L327:L328:L329:L330:L331:L332:L333:{}}}}}}}}}}}}}}}}}}nl(` + "`\n`" + `)
}}}
`,
},
entryPaths: []string{"/entry.js"},
options: config.Options{
MinifyWhitespace: true,
MinifyIdentifiers: true,
MinifySyntax: true,
AbsOutputFile: "/out.js",
},
})
Expand Down
31 changes: 28 additions & 3 deletions internal/bundler_tests/snapshots/snapshots_dce.txt
Original file line number Diff line number Diff line change
Expand Up @@ -778,9 +778,7 @@ function testStmts() {
var f;
if (!x)
var g;
var h;
x:
var i;
var h, i;
}
testReturn();
testThrow();
Expand All @@ -805,6 +803,20 @@ var keepMe4 = keepMe3();
var keepMe5 = pure();
var keepMe6 = some.fn();

================================================================================
TestDropLabels
---------- /out.js ----------
// entry.js
keep_1:
require("foo1");
exports.bar = function() {
if (x)
;
if (y)
keep_2:
require("bar2");
};

================================================================================
TestFileLoaderRemoveUnused
---------- /out.js ----------
Expand Down Expand Up @@ -2347,6 +2359,19 @@ TestPureCallsWithSpread
[...args];
[...args];

================================================================================
TestRemoveCodeAfterLabelWithReturn
---------- /out.js ----------
function earlyReturn() {
onlyWithKeep();
}
function loop() {
if (foo()) {
bar();
return;
}
}

================================================================================
TestRemoveTrailingReturn
---------- /out.js ----------
Expand Down
33 changes: 15 additions & 18 deletions internal/bundler_tests/snapshots/snapshots_default.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4536,31 +4536,28 @@ var i=r((t,e)=>{e.exports=123});var s=i();console.log(s,"no identifier in this f
================================================================================
TestMinifyNestedLabelsNoBundle
---------- /out.js ----------
n:l:a:b:c:d:e:f:g:h:i:j:k:m:o:p:{nl(`
`);q:r:s:t:u:v:w:x:y:z:A:{B:C:D:E:F:{nl(`
l:n:a:b:c:d:e:f:g:h:i:j:k:m:o:p:{nl(`
`);q:r:s:t:u:v:w:x:y:z:A:B:C:D:E:F:{nl(`
`);G:H:I:J:K:L:M:N:O:P:Q:R:S:T:U:V:{nl(`
`);W:X:Y:Z:_:$:nn:ln:an:bn:cn:dn:en:fn:gn:hn:{nl(`
`);jn:kn:mn:on:pn:qn:rn:sn:tn:un:vn:wn:xn:yn:zn:An:{nl(`
`);Bn:Cn:Dn:En:Fn:Gn:Hn:In:Jn:Kn:Ln:Mn:Nn:On:Pn:Qn:{nl(`
`);Rn:Sn:Tn:Un:Vn:Wn:Xn:Yn:Zn:_n:$n:nl:ll:al:bl:cl:{nl(`
`);dl:el:fl:gl:hl:il:jl:kl:ml:ol:pl:{ql:rl:sl:tl:ul:{nl(`
`);vl:wl:xl:yl:zl:Al:Bl:Cl:Dl:El:Fl:Gl:Hl:Il:Jl:Kl:{nl(`
`);Ll:Ml:Nl:Ol:Pl:Ql:Rl:Sl:Tl:Ul:Vl:Wl:Xl:Yl:Zl:_l:{nl(`
`);$l:na:la:aa:ba:ca:da:ea:fa:ga:ha:ia:ja:ka:ma:oa:{nl(`
`);W:X:Y:Z:_:$:ll:nl:al:bl:cl:dl:el:fl:gl:hl:{nl(`
`);il:jl:kl:ml:ol:pl:ql:rl:sl:tl:ul:vl:wl:xl:yl:zl:{nl(`
`);Al:Bl:Cl:Dl:El:Fl:Gl:Hl:Il:Jl:Kl:Ll:Ml:Nl:Ol:Pl:{nl(`
`);Ql:Rl:Sl:Tl:Ul:Vl:Wl:Xl:Yl:Zl:_l:$l:ln:nn:an:bn:{nl(`
`);cn:dn:en:fn:gn:hn:jn:kn:mn:on:pn:qn:rn:sn:tn:un:{nl(`
`);vn:wn:xn:yn:zn:An:Bn:Cn:Dn:En:Fn:Gn:Hn:In:Jn:Kn:{nl(`
`);Ln:Mn:Nn:On:Pn:Qn:Rn:Sn:Tn:Un:Vn:Wn:Xn:Yn:Zn:_n:{nl(`
`);$n:la:na:aa:ba:ca:da:ea:fa:ga:ha:ia:ja:ka:ma:oa:{nl(`
`);pa:qa:ra:sa:ta:ua:va:wa:xa:ya:za:Aa:Ba:Ca:Da:Ea:{nl(`
`);Fa:Ga:Ha:Ia:Ja:Ka:La:Ma:Na:Oa:Pa:Qa:Ra:Sa:Ta:Ua:{nl(`
`);Va:Wa:Xa:Ya:Za:_a:$a:nb:lb:ab:bb:{cb:db:eb:fb:gb:{nl(`
`);Va:Wa:Xa:Ya:Za:_a:$a:lb:nb:ab:bb:cb:db:eb:fb:gb:{nl(`
`);hb:ib:jb:kb:mb:ob:pb:qb:rb:sb:tb:ub:vb:wb:xb:yb:{nl(`
`);zb:Ab:Bb:Cb:Db:Eb:Fb:Gb:Hb:Ib:Jb:Kb:Lb:Mb:Nb:Ob:{nl(`
`);Pb:Qb:Rb:Sb:Tb:Ub:Vb:Wb:Xb:Yb:Zb:_b:$b:nc:lc:ac:{nl(`
`);Pb:Qb:Rb:Sb:Tb:Ub:Vb:Wb:Xb:Yb:Zb:_b:$b:lc:nc:ac:{nl(`
`);bc:cc:dc:ec:fc:gc:hc:ic:jc:kc:mc:oc:pc:qc:rc:sc:{nl(`
`);tc:uc:vc:wc:xc:yc:zc:Ac:Bc:Cc:Dc:Ec:Fc:Gc:Hc:Ic:{nl(`
`);Jc:Kc:Lc:Mc:Nc:Oc:Pc:Qc:Rc:Sc:Tc:{Uc:Vc:Wc:Xc:Yc:{nl(`
`);Zc:_c:$c:nd:ld:ad:bd:cd:dd:ed:fd:gd:hd:;}nl(`
`)}}}}}}}nl(`
`)}}}}}}}nl(`
`)}}}}}}}nl(`
`)}}
`);Jc:Kc:Lc:Mc:Nc:Oc:Pc:Qc:Rc:Sc:Tc:Uc:Vc:Wc:Xc:Yc:{nl(`
`);Zc:_c:$c:ld:nd:ad:bd:cd:dd:ed:fd:gd:hd:{}}}}}}}}}}}}}}}}}}nl(`
`)}}}

================================================================================
TestMinifyPrivateIdentifiersNoBundle
Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ type Options struct {
// unsupported feature sets above. It's used for error messages.
OriginalTargetEnv string

DropLabels []string
ExtensionOrder []string
MainFields []string
Conditions []string
Expand Down
Loading

0 comments on commit 9b83e55

Please sign in to comment.