From 68573abff50db8a0c3be0c1f6ec5209dab5663f9 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Thu, 19 Dec 2024 15:34:12 -0500 Subject: [PATCH] internal: factor out key format for define map --- pkg/api/api_impl.go | 87 +++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/pkg/api/api_impl.go b/pkg/api/api_impl.go index c628341bdd..69757d2cc3 100644 --- a/pkg/api/api_impl.go +++ b/pkg/api/api_impl.go @@ -524,6 +524,18 @@ func validateJSXExpr(log logger.Log, text string, name string) config.DefineExpr return config.DefineExpr{} } +// This returns an arbitrary but unique key for each unique array of strings +func mapKeyForDefine(parts []string) string { + var sb strings.Builder + var n [4]byte + for _, part := range parts { + binary.LittleEndian.PutUint32(n[:], uint32(len(part))) + sb.Write(n[:]) + sb.WriteString(part) + } + return sb.String() +} + func validateDefines( log logger.Log, defines map[string]string, @@ -533,13 +545,26 @@ func validateDefines( minify bool, drop Drop, ) (*config.ProcessedDefines, []config.InjectedDefine) { + // Sort injected defines for determinism, since the imports will be injected + // into every file in the order that we return them from this function + sortedKeys := make([]string, 0, len(defines)) + for key := range defines { + sortedKeys = append(sortedKeys, key) + } + sort.Strings(sortedKeys) + rawDefines := make(map[string]config.DefineData) - var valueToInject map[string]config.InjectedDefine - var definesToInject []string + nodeEnvParts := []string{"process", "env", "NODE_ENV"} + nodeEnvMapKey := mapKeyForDefine(nodeEnvParts) + var injectedDefines []config.InjectedDefine + + for _, key := range sortedKeys { + value := defines[key] + keyParts := strings.Split(key, ".") + mapKey := mapKeyForDefine(keyParts) - for key, value := range defines { // The key must be a dot-separated identifier list - for _, part := range strings.Split(key, ".") { + for _, part := range keyParts { if !js_ast.IsIdentifier(part) { if part == key { log.AddError(nil, logger.Range{}, fmt.Sprintf("The define key %q must be a valid identifier", key)) @@ -555,10 +580,10 @@ func validateDefines( // Define simple expressions if defineExpr.Constant != nil || len(defineExpr.Parts) > 0 { - rawDefines[key] = config.DefineData{DefineExpr: &defineExpr} + rawDefines[mapKey] = config.DefineData{KeyParts: keyParts, DefineExpr: &defineExpr} // Try to be helpful for common mistakes - if len(defineExpr.Parts) == 1 && key == "process.env.NODE_ENV" { + if len(defineExpr.Parts) == 1 && mapKey == nodeEnvMapKey { data := logger.MsgData{ Text: fmt.Sprintf("%q is defined as an identifier instead of a string (surround %q with quotes to get a string)", key, value), } @@ -606,15 +631,13 @@ func validateDefines( // Inject complex expressions if injectExpr != nil { - definesToInject = append(definesToInject, key) - if valueToInject == nil { - valueToInject = make(map[string]config.InjectedDefine) - } - valueToInject[key] = config.InjectedDefine{ + index := ast.MakeIndex32(uint32(len(injectedDefines))) + injectedDefines = append(injectedDefines, config.InjectedDefine{ Source: logger.Source{Contents: value}, Data: injectExpr, Name: key, - } + }) + rawDefines[mapKey] = config.DefineData{KeyParts: keyParts, DefineExpr: &config.DefineExpr{InjectedDefineIndex: index}} continue } @@ -622,18 +645,6 @@ func validateDefines( log.AddError(nil, logger.Range{}, fmt.Sprintf("Invalid define value (must be an entity name or valid JSON syntax): %s", value)) } - // Sort injected defines for determinism, since the imports will be injected - // into every file in the order that we return them from this function - var injectedDefines []config.InjectedDefine - if len(definesToInject) > 0 { - injectedDefines = make([]config.InjectedDefine, len(definesToInject)) - sort.Strings(definesToInject) - for i, key := range definesToInject { - injectedDefines[i] = valueToInject[key] - rawDefines[key] = config.DefineData{DefineExpr: &config.DefineExpr{InjectedDefineIndex: ast.MakeIndex32(uint32(i))}} - } - } - // If we're bundling for the browser, add a special-cased define for // "process.env.NODE_ENV" that is "development" when not minifying and // "production" when minifying. This is a convention from the React world @@ -641,16 +652,16 @@ func validateDefines( // is only done if it's not already defined so that you can override it if // necessary. if isBuildAPI && platform == config.PlatformBrowser { - if _, process := rawDefines["process"]; !process { - if _, processEnv := rawDefines["process.env"]; !processEnv { - if _, processEnvNodeEnv := rawDefines["process.env.NODE_ENV"]; !processEnvNodeEnv { + if _, process := rawDefines[mapKeyForDefine([]string{"process"})]; !process { + if _, processEnv := rawDefines[mapKeyForDefine([]string{"process.env"})]; !processEnv { + if _, processEnvNodeEnv := rawDefines[nodeEnvMapKey]; !processEnvNodeEnv { var value []uint16 if minify { value = helpers.StringToUTF16("production") } else { value = helpers.StringToUTF16("development") } - rawDefines["process.env.NODE_ENV"] = config.DefineData{DefineExpr: &config.DefineExpr{Constant: &js_ast.EString{Value: value}}} + rawDefines[nodeEnvMapKey] = config.DefineData{KeyParts: nodeEnvParts, DefineExpr: &config.DefineExpr{Constant: &js_ast.EString{Value: value}}} } } } @@ -658,14 +669,20 @@ func validateDefines( // If we're dropping all console API calls, replace each one with undefined if (drop & DropConsole) != 0 { - define := rawDefines["console"] + consoleParts := []string{"console"} + consoleMapKey := mapKeyForDefine(consoleParts) + define := rawDefines[consoleMapKey] + define.KeyParts = consoleParts define.Flags |= config.MethodCallsMustBeReplacedWithUndefined - rawDefines["console"] = define + rawDefines[consoleMapKey] = define } for _, key := range pureFns { + keyParts := strings.Split(key, ".") + mapKey := mapKeyForDefine(keyParts) + // The key must be a dot-separated identifier list - for _, part := range strings.Split(key, ".") { + for _, part := range keyParts { if !js_ast.IsIdentifier(part) { log.AddError(nil, logger.Range{}, fmt.Sprintf("Invalid pure function: %q", key)) continue @@ -673,16 +690,16 @@ func validateDefines( } // Merge with any previously-specified defines - define := rawDefines[key] + define := rawDefines[mapKey] + define.KeyParts = keyParts define.Flags |= config.CallCanBeUnwrappedIfUnused - rawDefines[key] = define + rawDefines[mapKey] = define } // Processing defines is expensive. Process them once here so the same object // can be shared between all parsers we create using these arguments. definesArray := make([]config.DefineData, 0, len(rawDefines)) - for key, define := range rawDefines { - define.KeyParts = strings.Split(key, ".") + for _, define := range rawDefines { definesArray = append(definesArray, define) } processed := config.ProcessDefines(definesArray)