@@ -15,23 +15,15 @@ import (
1515)
1616
1717var (
18- // Multi-line patterns for simple agent usage
19- clientBytesWithBodyPattern = regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*fiber\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\.(Body|BodyString)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.Bytes\(\)` )
20- clientBytesPattern = regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*fiber\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.Bytes\(\)` )
21- clientStringWithBodyPattern = regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*fiber\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\.(Body|BodyString)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.String\(\)` )
22- clientStringPattern = regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*fiber\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.String\(\)` )
23- clientStructWithBodyPattern = regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*fiber\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\.(Body|BodyString)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.Struct\(([^)]*)\)` )
24- clientStructPattern = regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*fiber\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.Struct\(([^)]*)\)` )
25- clientErrIfPattern = regexp .MustCompile (`if\s+len\(\s*errs\s*\)\s*>\s*0\s*{` )
26- clientErrLenPattern = regexp .MustCompile (`\blen\(errs\)` )
27- clientErrComparePattern = regexp .MustCompile (`err\s*!=\s*nil\s*>\s*0` )
28- clientErrMapPattern = regexp .MustCompile (`"errs"\s*:\s*errs` )
29- clientErrVarPattern = regexp .MustCompile (`\berrs\b` )
30- clientErrsDeclPattern = regexp .MustCompile (`\berrs\s+\[]error\b` )
31-
32- // AcquireAgent patterns
33- acquireAgentPattern = regexp .MustCompile (`(?m)^([ \t]*)(\w+)\s*:=\s*fiber\.AcquireAgent\(\)\s*$` )
34- releaseAgentPattern = regexp .MustCompile (`(?m)^([ \t]*)defer\s+fiber\.ReleaseAgent\((\w+)\)\s*$` )
18+ // Error handling patterns
19+ clientErrIfPattern = regexp .MustCompile (`if\s+len\(\s*errs\s*\)\s*>\s*0\s*{` )
20+ clientErrLenPattern = regexp .MustCompile (`\blen\(errs\)` )
21+ clientErrComparePattern = regexp .MustCompile (`err\s*!=\s*nil\s*>\s*0` )
22+ clientErrMapPattern = regexp .MustCompile (`"errs"\s*:\s*errs` )
23+ clientErrVarPattern = regexp .MustCompile (`\berrs\b` )
24+ clientErrsDeclPattern = regexp .MustCompile (`\berrs\s+\[]error\b` )
25+
26+ // Non-alias-dependent patterns
3527 requestFromAgent = regexp .MustCompile (`^([ \t]*)(\w+)\s*:=\s*(\w+)\.Request\(\)\s*$` )
3628 headerMethodPattern = regexp .MustCompile (`^([ \t]*)(\w+)\.Header\.SetMethod\(([^)]*)\)\s*$` )
3729 headerSetPattern = regexp .MustCompile (`^([ \t]*)(\w+)\.Header\.Set\(([^,]+),\s*([^)]*)\)\s*$` )
4335)
4436
4537var (
46- simpleAgentPattern = regexp . MustCompile ( `^([ \t]*)(\w+)\s*:=\s*fiber\.(Get|Head|Post|Put|Patch|Delete)\((.*)\)\s*$` )
38+ // Agent method patterns (non-alias-dependent )
4739 headerSetSimplePattern = regexp .MustCompile (`^([ \t]*)(\w+)\.Set\(([^,]+),\s*([^)]*)\)\s*$` )
4840 queryStringPattern = regexp .MustCompile (`^([ \t]*)(\w+)\.QueryString\(([^)]*)\)\s*$` )
4941 timeoutPattern = regexp .MustCompile (`^([ \t]*)(\w+)\.Timeout\(([^)]*)\)\s*$` )
@@ -58,13 +50,45 @@ var (
5850 agentStructCallPattern = regexp .MustCompile (`^([ \t]*)([^,]+),\s*([^,]+),\s*errs\s*(=|:=)\s*(\w+)\.Struct\((.+)\)\s*$` )
5951)
6052
53+ // buildAliasPatterns creates regex patterns for fiber package calls with given alias
54+ func buildAliasPatterns (alias string ) map [string ]* regexp.Regexp {
55+ escaped := regexp .QuoteMeta (alias )
56+ return map [string ]* regexp.Regexp {
57+ "simpleAgent" : regexp .MustCompile (`^([ \t]*)(\w+)\s*:=\s*` + escaped + `\.(Get|Head|Post|Put|Patch|Delete)\((.*)\)\s*$` ),
58+ "acquireAgent" : regexp .MustCompile (`(?m)^([ \t]*)(\w+)\s*:=\s*` + escaped + `\.AcquireAgent\(\)\s*$` ),
59+ "releaseAgent" : regexp .MustCompile (`(?m)^([ \t]*)defer\s+` + escaped + `\.ReleaseAgent\((\w+)\)\s*$` ),
60+ "bytesWithBody" : regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*` + escaped + `\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\.(Body|BodyString)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.Bytes\(\)` ),
61+ "bytes" : regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*` + escaped + `\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.Bytes\(\)` ),
62+ "stringWithBody" : regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*` + escaped + `\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\.(Body|BodyString)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.String\(\)` ),
63+ "string" : regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*` + escaped + `\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.String\(\)` ),
64+ "structWithBody" : regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*` + escaped + `\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\.(Body|BodyString)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.Struct\(([^)]*)\)` ),
65+ "struct" : regexp .MustCompile (`(?m)([ \t]*)(\w+)\s*:=\s*` + escaped + `\.(Get|Head|Post|Put|Patch|Delete)\(([^)]*)\)\s*\n([ \t]*)(\w+)\s*,\s*(\w+)\s*,\s*errs\s*:=\s*(\w+)\.Struct\(([^)]*)\)` ),
66+ }
67+ }
68+
6169const callTypeString = "string"
6270
6371func MigrateClientUsage (cmd * cobra.Command , cwd string , _ , _ * semver.Version ) error {
6472 changed , err := internal .ChangeFileContent (cwd , func (content string ) string {
65- updated , modified := rewriteClientExamples (content )
66- updated , agentChanged := rewriteAcquireAgentBlocks (updated )
67- modified = modified || agentChanged
73+ // Find all fiber import aliases used in this file
74+ aliases := findFiberImportAliases (content )
75+ if len (aliases ) == 0 {
76+ // Default to "fiber" if no import found (might be in another file)
77+ aliases = []string {"fiber" }
78+ }
79+
80+ modified := false
81+ updated := content
82+
83+ // Apply migrations for each alias
84+ for _ , alias := range aliases {
85+ var changed bool
86+ updated , changed = rewriteClientExamplesWithAlias (updated , alias )
87+ modified = modified || changed
88+ updated , changed = rewriteAcquireAgentBlocksWithAlias (updated , alias )
89+ modified = modified || changed
90+ }
91+
6892 if ! modified {
6993 return content
7094 }
@@ -84,7 +108,11 @@ func MigrateClientUsage(cmd *cobra.Command, cwd string, _, _ *semver.Version) er
84108 return nil
85109}
86110
87- func rewriteAcquireAgentBlocks (content string ) (string , bool ) {
111+ func rewriteAcquireAgentBlocksWithAlias (content string , alias string ) (string , bool ) {
112+ patterns := buildAliasPatterns (alias )
113+ acquireAgentPattern := patterns ["acquireAgent" ]
114+ releaseAgentPattern := patterns ["releaseAgent" ]
115+
88116 lines := strings .Split (content , "\n " )
89117 var out []string
90118 changed := false
@@ -239,14 +267,20 @@ func rewriteAcquireAgentBlocks(content string) (string, bool) {
239267 out = append (out , parseIndent + "if err != nil {" )
240268 out = append (out , parseBody [:len (parseBody )- 1 ]... )
241269 out = append (out , parseIndent + "}" )
242-
270+ // Declare variables and assign only if err == nil to avoid nil pointer dereference
243271 if statusVar != "" {
244- out = append (out , fmt .Sprintf ("%s%s %s resp.StatusCode() " , indent , statusVar , assignOp ))
272+ out = append (out , fmt .Sprintf ("%svar %s int " , indent , statusVar ))
245273 }
246274 if bodyVar != "" {
247- out = append (out , fmt .Sprintf ("%s%s %s resp.Body() " , indent , bodyVar , assignOp ))
275+ out = append (out , fmt .Sprintf ("%svar %s []byte " , indent , bodyVar ))
248276 }
249277 out = append (out , indent + "if err == nil {" )
278+ if statusVar != "" {
279+ out = append (out , fmt .Sprintf ("%s\t %s = resp.StatusCode()" , indent , statusVar ))
280+ }
281+ if bodyVar != "" {
282+ out = append (out , fmt .Sprintf ("%s\t %s = resp.Body()" , indent , bodyVar ))
283+ }
250284 out = append (out , fmt .Sprintf ("%s\t err = resp.JSON(%s)" , indent , structTarget ))
251285 out = append (out , indent + "}" )
252286 out = append (out , structMatch [1 ]+ "if err != nil {" )
@@ -269,27 +303,33 @@ func rewriteAcquireAgentBlocks(content string) (string, bool) {
269303 out = append (out , parseIndent + "if err != nil {" )
270304 out = append (out , parseBody [:len (parseBody )- 1 ]... )
271305 out = append (out , parseIndent + "}" )
272- out = append (out , fmt .Sprintf ("%s%s %s resp.StatusCode()" , indent , statusVar , assignOp ))
273- out = append (out , fmt .Sprintf ("%s%s %s resp.Body()" , indent , bodyVar , assignOp ))
306+ // Declare variables and assign only if err == nil to avoid nil pointer dereference
307+ out = append (out , fmt .Sprintf ("%svar %s int" , indent , statusVar ))
308+ out = append (out , fmt .Sprintf ("%svar %s []byte" , indent , bodyVar ))
309+ out = append (out , indent + "if err == nil {" )
310+ out = append (out , fmt .Sprintf ("%s\t %s = resp.StatusCode()" , indent , statusVar ))
311+ out = append (out , fmt .Sprintf ("%s\t %s = resp.Body()" , indent , bodyVar ))
312+ out = append (out , indent + "}" )
274313
275314 i = structStart
276315 changed = true
277316 continue
278317 case len (stringMatch ) > 0 && stringMatch [5 ] == agentVar :
279318 statusVar := strings .TrimSpace (stringMatch [2 ])
280319 bodyVar := strings .TrimSpace (stringMatch [3 ])
281- assignOp := stringMatch [4 ]
282- if assignOp == "" {
283- assignOp = "="
284- }
285320
286321 respLine := fmt .Sprintf ("%sresp, err := client.%s(%s%s)" , indent , methodName , uriExpr , configLine )
287322 out = append (out , respLine )
288323 out = append (out , parseIndent + "if err != nil {" )
289324 out = append (out , parseBody [:len (parseBody )- 1 ]... )
290325 out = append (out , parseIndent + "}" )
291- out = append (out , fmt .Sprintf ("%s%s %s resp.StatusCode()" , indent , statusVar , assignOp ))
292- out = append (out , fmt .Sprintf ("%s%s %s resp.String()" , indent , bodyVar , assignOp ))
326+ // Declare variables and assign only if err == nil to avoid nil pointer dereference
327+ out = append (out , fmt .Sprintf ("%svar %s int" , indent , statusVar ))
328+ out = append (out , fmt .Sprintf ("%svar %s string" , indent , bodyVar ))
329+ out = append (out , indent + "if err == nil {" )
330+ out = append (out , fmt .Sprintf ("%s\t %s = resp.StatusCode()" , indent , statusVar ))
331+ out = append (out , fmt .Sprintf ("%s\t %s = resp.String()" , indent , bodyVar ))
332+ out = append (out , indent + "}" )
293333
294334 i = structStart
295335 changed = true
@@ -342,20 +382,22 @@ func buildConfig(headers map[string]string) string {
342382 return fmt .Sprintf (", client.Config{Header: map[string]string{%s}}" , strings .Join (parts , ", " ))
343383}
344384
345- func rewriteClientExamples (content string ) (string , bool ) {
346- updated , changedSimple := rewriteSimpleAgentBlocks (content )
385+ func rewriteClientExamplesWithAlias (content string , alias string ) (string , bool ) {
386+ patterns := buildAliasPatterns (alias )
387+
388+ updated , changedSimple := rewriteSimpleAgentBlocksWithAlias (content , alias )
347389 changed := changedSimple
348390
349391 for _ , replace := range []struct {
350392 pattern * regexp.Regexp
351393 build func (parts []string ) (string , bool )
352394 }{
353- {pattern : clientBytesWithBodyPattern , build : buildBytesWithBodyReplacement },
354- {pattern : clientBytesPattern , build : buildBytesReplacement },
355- {pattern : clientStringWithBodyPattern , build : buildStringWithBodyReplacement },
356- {pattern : clientStringPattern , build : buildStringReplacement },
357- {pattern : clientStructWithBodyPattern , build : buildStructWithBodyReplacement },
358- {pattern : clientStructPattern , build : buildStructReplacement },
395+ {pattern : patterns [ "bytesWithBody" ] , build : buildBytesWithBodyReplacement },
396+ {pattern : patterns [ "bytes" ] , build : buildBytesReplacement },
397+ {pattern : patterns [ "stringWithBody" ] , build : buildStringWithBodyReplacement },
398+ {pattern : patterns [ "string" ] , build : buildStringReplacement },
399+ {pattern : patterns [ "structWithBody" ] , build : buildStructWithBodyReplacement },
400+ {pattern : patterns [ "struct" ] , build : buildStructReplacement },
359401 } {
360402 updated = replace .pattern .ReplaceAllStringFunc (updated , func (match string ) string {
361403 parts := replace .pattern .FindStringSubmatch (match )
@@ -381,7 +423,6 @@ type simpleAgentConfig struct {
381423 body string
382424 timeout string
383425 tlsConfig string
384- debug bool
385426 config bool
386427}
387428
@@ -390,7 +431,10 @@ type headerValue struct {
390431 raw bool
391432}
392433
393- func rewriteSimpleAgentBlocks (content string ) (string , bool ) {
434+ func rewriteSimpleAgentBlocksWithAlias (content string , alias string ) (string , bool ) {
435+ patterns := buildAliasPatterns (alias )
436+ simpleAgentPattern := patterns ["simpleAgent" ]
437+
394438 lines := strings .Split (content , "\n " )
395439 var out []string
396440 changed := false
@@ -480,9 +524,8 @@ func rewriteSimpleAgentBlocks(content string) (string, bool) {
480524 cfg .config = true
481525 continue
482526 }
483- // Handle Debug() - will be removed as v3 uses hooks instead
527+ // Handle Debug() - not supported in v3, just skip ( v3 uses hooks instead)
484528 if m := debugPattern .FindStringSubmatch (l ); len (m ) > 0 && m [2 ] == varName {
485- cfg .debug = true
486529 continue
487530 }
488531 // Handle Reuse() - not needed in v3, just skip
@@ -783,28 +826,95 @@ func rewriteClientErrorHandling(content string) string {
783826}
784827
785828// removeUnusedFiberImport removes unused fiber/v2 or fiber/v3 imports
786- // when they are no longer needed after migration to the client package
829+ // when they are no longer needed after migration to the client package.
830+ // Handles: single-line imports, import blocks, and aliased imports.
787831func removeUnusedFiberImport (content string ) string {
788- // Check if fiber is still used somewhere in the code (excluding imports)
789- fiberUsagePattern := regexp .MustCompile (`\bfiber\.(Get|Head|Post|Put|Patch|Delete|AcquireAgent|ReleaseAgent)\b` )
790- if fiberUsagePattern .MatchString (content ) {
832+ // Extract all import aliases for fiber
833+ aliases := findFiberImportAliases (content )
834+ if len (aliases ) == 0 {
835+ // No fiber import found
791836 return content
792837 }
793838
794- // Remove import line for fiber/v2 or fiber/v3 if no longer used
795- // But only remove if there's no other usage of 'fiber.' in the code
796- fiberAnyUsage := regexp .MustCompile (`\bfiber\.` )
839+ // Check if any alias is still used in the code (excluding imports)
797840 importContent := extractImportSection (content )
798-
799- // Check if fiber is used outside of imports
800841 contentWithoutImports := strings .Replace (content , importContent , "" , 1 )
801- if fiberAnyUsage .MatchString (contentWithoutImports ) {
802- return content
842+
843+ for _ , alias := range aliases {
844+ // Check for usage like "alias." in code
845+ usagePattern := regexp .MustCompile (`\b` + regexp .QuoteMeta (alias ) + `\.` )
846+ if usagePattern .MatchString (contentWithoutImports ) {
847+ return content
848+ }
803849 }
804850
805- // Remove the fiber import line
806- fiberImportLine := regexp .MustCompile (`(?m)^\s*"github\.com/gofiber/fiber/v[23]"\s*\n?` )
807- return fiberImportLine .ReplaceAllString (content , "" )
851+ // No alias is used, remove the fiber import
852+ updated := removeFiberImportLine (content )
853+ updated = cleanupEmptyImportBlock (updated )
854+ return updated
855+ }
856+
857+ // findFiberImportAliases finds all import aliases for fiber/v2 or fiber/v3
858+ // Returns slice of aliases (e.g., ["fiber"] for `import "..."` or ["f"] for `import f "..."`)
859+ func findFiberImportAliases (content string ) []string {
860+ var aliases []string
861+
862+ // Pattern for aliased import in block: `alias "github.com/gofiber/fiber/v3"`
863+ aliasedBlockPattern := regexp .MustCompile (`(?m)^\s*(\w+)\s+"github\.com/gofiber/fiber/v[23]"\s*$` )
864+ if matches := aliasedBlockPattern .FindAllStringSubmatch (content , - 1 ); matches != nil {
865+ for _ , m := range matches {
866+ aliases = append (aliases , m [1 ])
867+ }
868+ }
869+
870+ // Pattern for non-aliased import in block: `"github.com/gofiber/fiber/v3"`
871+ nonAliasedBlockPattern := regexp .MustCompile (`(?m)^\s*"github\.com/gofiber/fiber/v[23]"\s*$` )
872+ if nonAliasedBlockPattern .MatchString (content ) {
873+ aliases = append (aliases , "fiber" )
874+ }
875+
876+ // Pattern for single-line aliased import: `import alias "github.com/gofiber/fiber/v3"`
877+ singleAliasedPattern := regexp .MustCompile (`(?m)^import\s+(\w+)\s+"github\.com/gofiber/fiber/v[23]"\s*$` )
878+ if matches := singleAliasedPattern .FindAllStringSubmatch (content , - 1 ); matches != nil {
879+ for _ , m := range matches {
880+ aliases = append (aliases , m [1 ])
881+ }
882+ }
883+
884+ // Pattern for single-line non-aliased import: `import "github.com/gofiber/fiber/v3"`
885+ singleNonAliasedPattern := regexp .MustCompile (`(?m)^import\s+"github\.com/gofiber/fiber/v[23]"\s*$` )
886+ if singleNonAliasedPattern .MatchString (content ) {
887+ aliases = append (aliases , "fiber" )
888+ }
889+
890+ return aliases
891+ }
892+
893+ // removeFiberImportLine removes the fiber import line from content
894+ // Handles both single-line imports and imports within a block, with or without aliases
895+ func removeFiberImportLine (content string ) string {
896+ // Remove single-line import (with or without alias)
897+ singleLinePattern := regexp .MustCompile (`(?m)^import\s+(\w+\s+)?"github\.com/gofiber/fiber/v[23]"\s*\n?` )
898+ content = singleLinePattern .ReplaceAllString (content , "" )
899+
900+ // Remove import line from block (with or without alias)
901+ blockLinePattern := regexp .MustCompile (`(?m)^\s*(\w+\s+)?"github\.com/gofiber/fiber/v[23]"\s*\n?` )
902+ content = blockLinePattern .ReplaceAllString (content , "" )
903+
904+ return content
905+ }
906+
907+ // cleanupEmptyImportBlock removes empty import blocks like `import (\n)`
908+ func cleanupEmptyImportBlock (content string ) string {
909+ // Remove import blocks that only contain whitespace/newlines
910+ emptyBlockPattern := regexp .MustCompile (`(?m)^import\s*\(\s*\)\s*\n?` )
911+ content = emptyBlockPattern .ReplaceAllString (content , "" )
912+
913+ // Remove import blocks with only whitespace between parentheses
914+ emptyBlockWithWhitespace := regexp .MustCompile (`(?ms)^import\s*\(\s*\)\s*\n?` )
915+ content = emptyBlockWithWhitespace .ReplaceAllString (content , "" )
916+
917+ return content
808918}
809919
810920func extractImportSection (content string ) string {
@@ -813,7 +923,8 @@ func extractImportSection(content string) string {
813923 return match
814924 }
815925
816- singleImport := regexp .MustCompile (`(?m)^import\s+"[^"]+"\s*$` )
926+ // Single-line import with optional alias
927+ singleImport := regexp .MustCompile (`(?m)^import\s+(\w+\s+)?"[^"]+"\s*$` )
817928 if match := singleImport .FindString (content ); match != "" {
818929 return match
819930 }
0 commit comments