diff --git a/internal/bundler/bundler_default_test.go b/internal/bundler/bundler_default_test.go index d89c6abee3..2dac20ba1a 100644 --- a/internal/bundler/bundler_default_test.go +++ b/internal/bundler/bundler_default_test.go @@ -3627,6 +3627,16 @@ func TestInject(t *testing.T) { return &js_ast.EIdentifier{Ref: args.FindSymbol(args.Loc, "replace")} }, }, + "obj.defined": { + DefineFunc: func(args config.DefineArgs) js_ast.E { + return &js_ast.EString{Value: js_lexer.StringToUTF16("defined")} + }, + }, + "injectedAndDefined": { + DefineFunc: func(args config.DefineArgs) js_ast.E { + return &js_ast.EString{Value: js_lexer.StringToUTF16("should be used")} + }, + }, }) default_suite.expectBundled(t, bundled{ files: map[string]string{ @@ -3634,6 +3644,8 @@ func TestInject(t *testing.T) { let sideEffects = console.log('this should be renamed') let collide = 123 console.log(obj.prop) + console.log(obj.defined) + console.log(injectedAndDefined) console.log(chain.prop.test) console.log(collide) console.log(re_export) @@ -3642,6 +3654,7 @@ func TestInject(t *testing.T) { export let obj = {} export let sideEffects = console.log('side effects') export let noSideEffects = /* @__PURE__ */ console.log('side effects') + export let injectedAndDefined = 'should not be used' `, "/node_modules/unused/index.js": ` console.log('This is unused but still has side effects') @@ -3694,6 +3707,16 @@ func TestInjectNoBundle(t *testing.T) { return &js_ast.EIdentifier{Ref: args.FindSymbol(args.Loc, "replace")} }, }, + "obj.defined": { + DefineFunc: func(args config.DefineArgs) js_ast.E { + return &js_ast.EString{Value: js_lexer.StringToUTF16("defined")} + }, + }, + "injectedAndDefined": { + DefineFunc: func(args config.DefineArgs) js_ast.E { + return &js_ast.EString{Value: js_lexer.StringToUTF16("should be used")} + }, + }, }) default_suite.expectBundled(t, bundled{ files: map[string]string{ @@ -3701,6 +3724,8 @@ func TestInjectNoBundle(t *testing.T) { let sideEffects = console.log('this should be renamed') let collide = 123 console.log(obj.prop) + console.log(obj.defined) + console.log(injectedAndDefined) console.log(chain.prop.test) console.log(collide) console.log(re_export) @@ -3709,6 +3734,7 @@ func TestInjectNoBundle(t *testing.T) { export let obj = {} export let sideEffects = console.log('side effects') export let noSideEffects = /* @__PURE__ */ console.log('side effects') + export let injectedAndDefined = 'should not be used' `, "/node_modules/unused/index.js": ` console.log('This is unused but still has side effects') diff --git a/internal/bundler/snapshots/snapshots_default.txt b/internal/bundler/snapshots/snapshots_default.txt index 70be39484c..aa9f7a5713 100644 --- a/internal/bundler/snapshots/snapshots_default.txt +++ b/internal/bundler/snapshots/snapshots_default.txt @@ -1195,6 +1195,8 @@ var import_external_pkg = __toModule(require("external-pkg")); var sideEffects2 = console.log("this should be renamed"); var collide = 123; console.log(obj.prop); +console.log("defined"); +console.log("should be used"); console.log(replace.test); console.log(collide); console.log(import_external_pkg.re_export); @@ -1244,6 +1246,8 @@ import {re_export} from "external-pkg"; let sideEffects2 = console.log("this should be renamed"); let collide = 123; console.log(obj.prop); +console.log("defined"); +console.log("should be used"); console.log(replace.test); console.log(collide); console.log(re_export); diff --git a/internal/js_ast/js_ast.go b/internal/js_ast/js_ast.go index e2344fb7a3..0e3ec56aef 100644 --- a/internal/js_ast/js_ast.go +++ b/internal/js_ast/js_ast.go @@ -1316,6 +1316,9 @@ const ( // Assigning to a "const" symbol will throw a TypeError at runtime SymbolConst + // Injected symbols can be overridden by provided defines + SymbolInjected + // This annotates all other symbols that don't have special behavior. SymbolOther ) @@ -1355,6 +1358,10 @@ func (kind SymbolKind) IsFunction() bool { return kind == SymbolHoistedFunction || kind == SymbolGeneratorOrAsyncFunction } +func (kind SymbolKind) IsUnboundOrInjected() bool { + return kind == SymbolUnbound || kind == SymbolInjected +} + var InvalidRef Ref = Ref{^uint32(0), ^uint32(0)} // Files are parsed in parallel for speed. We want to allow each parser to diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 5cd427e05c..54ca0c28e5 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -9508,8 +9508,10 @@ func (p *parser) isDotDefineMatch(expr js_ast.Expr, parts []string) bool { return false } - // The last symbol must be unbound - return p.symbols[result.ref.InnerIndex].Kind == js_ast.SymbolUnbound + p.ignoreUsage(result.ref) + + // The last symbol must be unbound or injected + return p.symbols[result.ref.InnerIndex].Kind.IsUnboundOrInjected() } } @@ -10312,8 +10314,8 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO } } - // Substitute user-specified defines for unbound symbols - if p.symbols[e.Ref.InnerIndex].Kind == js_ast.SymbolUnbound && !result.isInsideWithScope && e != p.deleteTarget { + // Substitute user-specified defines for unbound or injected symbols + if p.symbols[e.Ref.InnerIndex].Kind.IsUnboundOrInjected() && !result.isInsideWithScope && e != p.deleteTarget { if data, ok := p.options.defines.IdentifierDefines[name]; ok { if data.DefineFunc != nil { new := p.valueForDefine(expr.Loc, in.assignTarget, isDeleteTarget, data.DefineFunc) @@ -10321,6 +10323,7 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO // Don't substitute an identifier for a non-identifier if this is an // assignment target, since it'll cause a syntax error if _, ok := new.Data.(*js_ast.EIdentifier); in.assignTarget == js_ast.AssignTargetNone || ok { + p.ignoreUsage(e.Ref) return new, exprOut{} } } @@ -13193,7 +13196,8 @@ func Parse(log logger.Log, source logger.Source, options Options) (result js_ast exportsNoConflict := make([]string, 0, len(file.Exports)) symbols := make(map[string]js_ast.Ref) if file.IsDefine { - ref := p.newSymbol(js_ast.SymbolOther, js_ast.GenerateNonUniqueNameFromPath(file.Path)) + ref := p.newSymbol(js_ast.SymbolInjected, js_ast.GenerateNonUniqueNameFromPath(file.Path)) + p.moduleScope.Generated = append(p.moduleScope.Generated, ref) symbols["default"] = ref exportsNoConflict = append(exportsNoConflict, "default") @@ -13201,7 +13205,7 @@ func Parse(log logger.Log, source logger.Source, options Options) (result js_ast } else { for _, alias := range file.Exports { if _, ok := p.moduleScope.Members[alias]; !ok { - ref := p.newSymbol(js_ast.SymbolOther, alias) + ref := p.newSymbol(js_ast.SymbolInjected, alias) p.moduleScope.Members[alias] = js_ast.ScopeMember{Ref: ref} symbols[alias] = ref exportsNoConflict = append(exportsNoConflict, alias)