Skip to content

Commit 889bab1

Browse files
committed
fix #2292: allow entity names as define values
1 parent 9860ee3 commit 889bab1

File tree

9 files changed

+217
-227
lines changed

9 files changed

+217
-227
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040

4141
This fix was contributed by [@nkeynes](https://github.com/nkeynes).
4242

43+
* Allow entity names as define values ([#2292](https://github.com/evanw/esbuild/issues/2292))
44+
45+
The "define" feature allows you to replace certain expressions with certain other expressions at compile time. For example, you might want to replace the global identifier `IS_PRODUCTION` with the boolean value `true` when building for production. Previously the only expressions you could substitute in were either identifier expressions or anything that is valid JSON syntax. This limitation exists because supporting more complex expressions is more complex (for example, substituting in a `require()` call could potentially pull in additional files, which would need to be handled). With this release, you can now also now define something as a member expression chain of the form `foo.abc.xyz`.
46+
4347
* Implement package self-references ([#2312](https://github.com/evanw/esbuild/issues/2312))
4448

4549
This release implements a rarely-used feature in node where a package can import itself by name instead of using relative imports. You can read more about this feature here: https://nodejs.org/api/packages.html#self-referencing-a-package-using-its-name. For example, assuming the `package.json` in a given package looks like this:

internal/bundler/bundler.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,10 +1189,10 @@ func (s *scanner) maybeParseFile(
11891189

11901190
// Allow certain properties to be overridden
11911191
if len(resolveResult.JSXFactory) > 0 {
1192-
optionsClone.JSX.Factory = config.JSXExpr{Parts: resolveResult.JSXFactory}
1192+
optionsClone.JSX.Factory = config.DefineExpr{Parts: resolveResult.JSXFactory}
11931193
}
11941194
if len(resolveResult.JSXFragment) > 0 {
1195-
optionsClone.JSX.Fragment = config.JSXExpr{Parts: resolveResult.JSXFragment}
1195+
optionsClone.JSX.Fragment = config.DefineExpr{Parts: resolveResult.JSXFragment}
11961196
}
11971197
if resolveResult.UseDefineForClassFieldsTS != config.Unspecified {
11981198
optionsClone.UseDefineForClassFields = resolveResult.UseDefineForClassFieldsTS

internal/bundler/bundler_default_test.go

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,8 @@ func TestJSXImportsCommonJS(t *testing.T) {
449449
options: config.Options{
450450
Mode: config.ModeBundle,
451451
JSX: config.JSXOptions{
452-
Factory: config.JSXExpr{Parts: []string{"elem"}},
453-
Fragment: config.JSXExpr{Parts: []string{"frag"}},
452+
Factory: config.DefineExpr{Parts: []string{"elem"}},
453+
Fragment: config.DefineExpr{Parts: []string{"frag"}},
454454
},
455455
AbsOutputFile: "/out.js",
456456
},
@@ -473,8 +473,8 @@ func TestJSXImportsES6(t *testing.T) {
473473
options: config.Options{
474474
Mode: config.ModeBundle,
475475
JSX: config.JSXOptions{
476-
Factory: config.JSXExpr{Parts: []string{"elem"}},
477-
Fragment: config.JSXExpr{Parts: []string{"frag"}},
476+
Factory: config.DefineExpr{Parts: []string{"elem"}},
477+
Fragment: config.DefineExpr{Parts: []string{"frag"}},
478478
},
479479
AbsOutputFile: "/out.js",
480480
},
@@ -529,7 +529,7 @@ func TestJSXConstantFragments(t *testing.T) {
529529
Mode: config.ModeBundle,
530530
AbsOutputFile: "/out.js",
531531
JSX: config.JSXOptions{
532-
Fragment: config.JSXExpr{
532+
Fragment: config.DefineExpr{
533533
Constant: &js_ast.EString{Value: helpers.StringToUTF16("]")},
534534
},
535535
},
@@ -2090,7 +2090,7 @@ func TestImportReExportES6Issue149(t *testing.T) {
20902090
options: config.Options{
20912091
Mode: config.ModeBundle,
20922092
JSX: config.JSXOptions{
2093-
Factory: config.JSXExpr{Parts: []string{"h"}},
2093+
Factory: config.DefineExpr{Parts: []string{"h"}},
20942094
},
20952095
AbsOutputFile: "/out.js",
20962096
ExternalSettings: config.ExternalSettings{
@@ -3979,18 +3979,18 @@ func TestInjectDuplicate(t *testing.T) {
39793979
func TestInject(t *testing.T) {
39803980
defines := config.ProcessDefines(map[string]config.DefineData{
39813981
"chain.prop": {
3982-
DefineFunc: func(args config.DefineArgs) js_ast.E {
3983-
return &js_ast.EIdentifier{Ref: args.FindSymbol(args.Loc, "replace")}
3982+
DefineExpr: &config.DefineExpr{
3983+
Parts: []string{"replace"},
39843984
},
39853985
},
39863986
"obj.defined": {
3987-
DefineFunc: func(args config.DefineArgs) js_ast.E {
3988-
return &js_ast.EString{Value: helpers.StringToUTF16("defined")}
3987+
DefineExpr: &config.DefineExpr{
3988+
Constant: &js_ast.EString{Value: helpers.StringToUTF16("defined")},
39893989
},
39903990
},
39913991
"injectedAndDefined": {
3992-
DefineFunc: func(args config.DefineArgs) js_ast.E {
3993-
return &js_ast.EString{Value: helpers.StringToUTF16("should be used")}
3992+
DefineExpr: &config.DefineExpr{
3993+
Constant: &js_ast.EString{Value: helpers.StringToUTF16("should be used")},
39943994
},
39953995
},
39963996
})
@@ -4059,18 +4059,18 @@ func TestInject(t *testing.T) {
40594059
func TestInjectNoBundle(t *testing.T) {
40604060
defines := config.ProcessDefines(map[string]config.DefineData{
40614061
"chain.prop": {
4062-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4063-
return &js_ast.EIdentifier{Ref: args.FindSymbol(args.Loc, "replace")}
4062+
DefineExpr: &config.DefineExpr{
4063+
Parts: []string{"replace"},
40644064
},
40654065
},
40664066
"obj.defined": {
4067-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4068-
return &js_ast.EString{Value: helpers.StringToUTF16("defined")}
4067+
DefineExpr: &config.DefineExpr{
4068+
Constant: &js_ast.EString{Value: helpers.StringToUTF16("defined")},
40694069
},
40704070
},
40714071
"injectedAndDefined": {
4072-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4073-
return &js_ast.EString{Value: helpers.StringToUTF16("should be used")}
4072+
DefineExpr: &config.DefineExpr{
4073+
Constant: &js_ast.EString{Value: helpers.StringToUTF16("should be used")},
40744074
},
40754075
},
40764076
})
@@ -4134,8 +4134,8 @@ func TestInjectNoBundle(t *testing.T) {
41344134
func TestInjectJSX(t *testing.T) {
41354135
defines := config.ProcessDefines(map[string]config.DefineData{
41364136
"React.createElement": {
4137-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4138-
return &js_ast.EIdentifier{Ref: args.FindSymbol(args.Loc, "el")}
4137+
DefineExpr: &config.DefineExpr{
4138+
Parts: []string{"el"},
41394139
},
41404140
},
41414141
})
@@ -4310,18 +4310,18 @@ func TestAvoidTDZNoBundle(t *testing.T) {
43104310
func TestDefineImportMeta(t *testing.T) {
43114311
defines := config.ProcessDefines(map[string]config.DefineData{
43124312
"import.meta": {
4313-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4314-
return &js_ast.ENumber{Value: 1}
4313+
DefineExpr: &config.DefineExpr{
4314+
Constant: &js_ast.ENumber{Value: 1},
43154315
},
43164316
},
43174317
"import.meta.foo": {
4318-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4319-
return &js_ast.ENumber{Value: 2}
4318+
DefineExpr: &config.DefineExpr{
4319+
Constant: &js_ast.ENumber{Value: 2},
43204320
},
43214321
},
43224322
"import.meta.foo.bar": {
4323-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4324-
return &js_ast.ENumber{Value: 3}
4323+
DefineExpr: &config.DefineExpr{
4324+
Constant: &js_ast.ENumber{Value: 3},
43254325
},
43264326
},
43274327
})
@@ -4354,8 +4354,8 @@ func TestDefineImportMeta(t *testing.T) {
43544354
func TestDefineImportMetaES5(t *testing.T) {
43554355
defines := config.ProcessDefines(map[string]config.DefineData{
43564356
"import.meta.x": {
4357-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4358-
return &js_ast.ENumber{Value: 1}
4357+
DefineExpr: &config.DefineExpr{
4358+
Constant: &js_ast.ENumber{Value: 1},
43594359
},
43604360
},
43614361
})
@@ -4391,18 +4391,18 @@ kept.js: WARNING: "import.meta" is not available in the configured target enviro
43914391
func TestDefineThis(t *testing.T) {
43924392
defines := config.ProcessDefines(map[string]config.DefineData{
43934393
"this": {
4394-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4395-
return &js_ast.ENumber{Value: 1}
4394+
DefineExpr: &config.DefineExpr{
4395+
Constant: &js_ast.ENumber{Value: 1},
43964396
},
43974397
},
43984398
"this.foo": {
4399-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4400-
return &js_ast.ENumber{Value: 2}
4399+
DefineExpr: &config.DefineExpr{
4400+
Constant: &js_ast.ENumber{Value: 2},
44014401
},
44024402
},
44034403
"this.foo.bar": {
4404-
DefineFunc: func(args config.DefineArgs) js_ast.E {
4405-
return &js_ast.ENumber{Value: 3}
4404+
DefineExpr: &config.DefineExpr{
4405+
Constant: &js_ast.ENumber{Value: 3},
44064406
},
44074407
},
44084408
})
@@ -4791,8 +4791,8 @@ func TestJSXThisValueCommonJS(t *testing.T) {
47914791
options: config.Options{
47924792
Mode: config.ModeBundle,
47934793
JSX: config.JSXOptions{
4794-
Factory: config.JSXExpr{Parts: []string{"this"}},
4795-
Fragment: config.JSXExpr{Parts: []string{"this"}},
4794+
Factory: config.DefineExpr{Parts: []string{"this"}},
4795+
Fragment: config.DefineExpr{Parts: []string{"this"}},
47964796
},
47974797
AbsOutputDir: "/out",
47984798
},
@@ -4833,8 +4833,8 @@ func TestJSXThisValueESM(t *testing.T) {
48334833
options: config.Options{
48344834
Mode: config.ModeBundle,
48354835
JSX: config.JSXOptions{
4836-
Factory: config.JSXExpr{Parts: []string{"this"}},
4837-
Fragment: config.JSXExpr{Parts: []string{"this"}},
4836+
Factory: config.DefineExpr{Parts: []string{"this"}},
4837+
Fragment: config.DefineExpr{Parts: []string{"this"}},
48384838
},
48394839
AbsOutputDir: "/out",
48404840
},
@@ -4878,8 +4878,8 @@ func TestJSXThisPropertyCommonJS(t *testing.T) {
48784878
options: config.Options{
48794879
Mode: config.ModeBundle,
48804880
JSX: config.JSXOptions{
4881-
Factory: config.JSXExpr{Parts: []string{"this", "factory"}},
4882-
Fragment: config.JSXExpr{Parts: []string{"this", "fragment"}},
4881+
Factory: config.DefineExpr{Parts: []string{"this", "factory"}},
4882+
Fragment: config.DefineExpr{Parts: []string{"this", "fragment"}},
48834883
},
48844884
AbsOutputDir: "/out",
48854885
},
@@ -4920,8 +4920,8 @@ func TestJSXThisPropertyESM(t *testing.T) {
49204920
options: config.Options{
49214921
Mode: config.ModeBundle,
49224922
JSX: config.JSXOptions{
4923-
Factory: config.JSXExpr{Parts: []string{"this", "factory"}},
4924-
Fragment: config.JSXExpr{Parts: []string{"this", "fragment"}},
4923+
Factory: config.DefineExpr{Parts: []string{"this", "factory"}},
4924+
Fragment: config.DefineExpr{Parts: []string{"this", "fragment"}},
49254925
},
49264926
AbsOutputDir: "/out",
49274927
},
@@ -4968,8 +4968,8 @@ func TestJSXImportMetaValue(t *testing.T) {
49684968
Mode: config.ModeBundle,
49694969
UnsupportedJSFeatures: compat.ImportMeta,
49704970
JSX: config.JSXOptions{
4971-
Factory: config.JSXExpr{Parts: []string{"import", "meta"}},
4972-
Fragment: config.JSXExpr{Parts: []string{"import", "meta"}},
4971+
Factory: config.DefineExpr{Parts: []string{"import", "meta"}},
4972+
Fragment: config.DefineExpr{Parts: []string{"import", "meta"}},
49734973
},
49744974
AbsOutputDir: "/out",
49754975
},
@@ -5018,8 +5018,8 @@ func TestJSXImportMetaProperty(t *testing.T) {
50185018
Mode: config.ModeBundle,
50195019
UnsupportedJSFeatures: compat.ImportMeta,
50205020
JSX: config.JSXOptions{
5021-
Factory: config.JSXExpr{Parts: []string{"import", "meta", "factory"}},
5022-
Fragment: config.JSXExpr{Parts: []string{"import", "meta", "fragment"}},
5021+
Factory: config.DefineExpr{Parts: []string{"import", "meta", "factory"}},
5022+
Fragment: config.DefineExpr{Parts: []string{"import", "meta", "fragment"}},
50235023
},
50245024
AbsOutputDir: "/out",
50255025
},
@@ -5978,8 +5978,8 @@ func TestManglePropsJSXTransform(t *testing.T) {
59785978
AbsOutputFile: "/out.js",
59795979
MangleProps: regexp.MustCompile("_$"),
59805980
JSX: config.JSXOptions{
5981-
Factory: config.JSXExpr{Parts: []string{"Foo", "createElement_"}},
5982-
Fragment: config.JSXExpr{Parts: []string{"Foo", "Fragment_"}},
5981+
Factory: config.DefineExpr{Parts: []string{"Foo", "createElement_"}},
5982+
Fragment: config.DefineExpr{Parts: []string{"Foo", "Fragment_"}},
59835983
},
59845984
},
59855985
})

internal/bundler/bundler_ts_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55

66
"github.com/evanw/esbuild/internal/compat"
77
"github.com/evanw/esbuild/internal/config"
8-
"github.com/evanw/esbuild/internal/js_ast"
98
)
109

1110
var ts_suite = suite{
@@ -1719,8 +1718,8 @@ func TestTSEnumDefine(t *testing.T) {
17191718
Defines: &config.ProcessedDefines{
17201719
IdentifierDefines: map[string]config.DefineData{
17211720
"d": {
1722-
DefineFunc: func(args config.DefineArgs) js_ast.E {
1723-
return &js_ast.EIdentifier{Ref: args.FindSymbol(args.Loc, "b")}
1721+
DefineExpr: &config.DefineExpr{
1722+
Parts: []string{"b"},
17241723
},
17251724
},
17261725
},

internal/config/config.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,12 @@ import (
1313
)
1414

1515
type JSXOptions struct {
16-
Factory JSXExpr
17-
Fragment JSXExpr
16+
Factory DefineExpr
17+
Fragment DefineExpr
1818
Parse bool
1919
Preserve bool
2020
}
2121

22-
type JSXExpr struct {
23-
Constant js_ast.E
24-
Parts []string
25-
}
26-
2722
type TSOptions struct {
2823
Parse bool
2924
NoAmbiguousLessThan bool

internal/config/globals.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import (
55
"strings"
66
"sync"
77

8+
"github.com/evanw/esbuild/internal/ast"
89
"github.com/evanw/esbuild/internal/helpers"
910
"github.com/evanw/esbuild/internal/js_ast"
10-
"github.com/evanw/esbuild/internal/logger"
1111
)
1212

1313
var processedGlobalsMutex sync.Mutex
@@ -842,16 +842,26 @@ var knownGlobals = [][]string{
842842
{"window"},
843843
}
844844

845-
type DefineArgs struct {
846-
FindSymbol func(logger.Loc, string) js_ast.Ref
847-
SymbolForDefine func(int) js_ast.Ref
848-
Loc logger.Loc
845+
// We currently only support compile-time replacement with certain expressions:
846+
//
847+
// - Primitive literals
848+
// - Identifiers
849+
// - "Entity names" which are identifiers followed by property accesses
850+
//
851+
// We don't support arbitrary expressions because arbitrary expressions may
852+
// require the full AST. For example, there could be "import()" or "require()"
853+
// expressions that need an import record. We also need to re-generate some
854+
// nodes such as identifiers within the injected context so that they can
855+
// bind to symbols in that context. Other expressions such as "this" may
856+
// also be contextual.
857+
type DefineExpr struct {
858+
Constant js_ast.E
859+
Parts []string
860+
InjectedDefineIndex ast.Index32
849861
}
850862

851-
type DefineFunc func(DefineArgs) js_ast.E
852-
853863
type DefineData struct {
854-
DefineFunc DefineFunc
864+
DefineExpr *DefineExpr
855865

856866
// True if accessing this value is known to not have any side effects. For
857867
// example, a bare reference to "Object.create" can be removed because it
@@ -928,13 +938,13 @@ func ProcessDefines(userDefines map[string]DefineData) ProcessedDefines {
928938

929939
// Swap in certain literal values because those can be constant folded
930940
result.IdentifierDefines["undefined"] = DefineData{
931-
DefineFunc: func(DefineArgs) js_ast.E { return js_ast.EUndefinedShared },
941+
DefineExpr: &DefineExpr{Constant: js_ast.EUndefinedShared},
932942
}
933943
result.IdentifierDefines["NaN"] = DefineData{
934-
DefineFunc: func(DefineArgs) js_ast.E { return &js_ast.ENumber{Value: math.NaN()} },
944+
DefineExpr: &DefineExpr{Constant: &js_ast.ENumber{Value: math.NaN()}},
935945
}
936946
result.IdentifierDefines["Infinity"] = DefineData{
937-
DefineFunc: func(DefineArgs) js_ast.E { return &js_ast.ENumber{Value: math.Inf(1)} },
947+
DefineExpr: &DefineExpr{Constant: &js_ast.ENumber{Value: math.Inf(1)}},
938948
}
939949

940950
// Then copy the user-specified defines in afterwards, which will overwrite

0 commit comments

Comments
 (0)