diff --git a/changelog.md b/changelog.md index 4c03e4851a..9cf04f9252 100644 --- a/changelog.md +++ b/changelog.md @@ -2,12 +2,17 @@ ## Unreleased +### Features + +- [#4790](https://github.com/ignite/cli/pull/4790) Remove global vars and struct placeholders. + ## [`v29.3.1`](https://github.com/ignite/cli/releases/tag/v29.3.1) ### Fixes - [#4793](https://github.com/ignite/cli/pull/4793) Use latest `bytedance/sonic` version to support Go 1.25. + ## [`v29.3.0`](https://github.com/ignite/cli/releases/tag/v29.3.0) ### Features diff --git a/ignite/pkg/cosmosanalysis/app/testdata/modules/app_config/app_config.go b/ignite/pkg/cosmosanalysis/app/testdata/modules/app_config/app_config.go index 5f05c47eeb..85fe265f15 100644 --- a/ignite/pkg/cosmosanalysis/app/testdata/modules/app_config/app_config.go +++ b/ignite/pkg/cosmosanalysis/app/testdata/modules/app_config/app_config.go @@ -160,7 +160,6 @@ var ( {Account: ibctransfertypes.ModuleName, Permissions: []string{authtypes.Minter, authtypes.Burner}}, {Account: ibcfeetypes.ModuleName}, {Account: icatypes.ModuleName}, - // this line is used by starport scaffolding # stargate/app/maccPerms } // blocked account addresses diff --git a/ignite/pkg/cosmosanalysis/module/testdata/earth/app/app_config.go b/ignite/pkg/cosmosanalysis/module/testdata/earth/app/app_config.go index 9c02e0794b..cde71f58be 100644 --- a/ignite/pkg/cosmosanalysis/module/testdata/earth/app/app_config.go +++ b/ignite/pkg/cosmosanalysis/module/testdata/earth/app/app_config.go @@ -167,7 +167,6 @@ var ( {Account: ibctransfertypes.ModuleName, Permissions: []string{authtypes.Minter, authtypes.Burner}}, {Account: ibcfeetypes.ModuleName}, {Account: icatypes.ModuleName}, - // this line is used by starport scaffolding # stargate/app/maccPerms } // blocked account addresses diff --git a/ignite/pkg/xast/global.go b/ignite/pkg/xast/global.go index 6a5096fca6..15b11a8eda 100644 --- a/ignite/pkg/xast/global.go +++ b/ignite/pkg/xast/global.go @@ -192,7 +192,7 @@ type ( values []structValue } - // StructOpts configures code generation. + // StructOpts configures struct changes. StructOpts func(*structOpts) structValue struct { @@ -286,3 +286,118 @@ func ModifyStruct(fileContent, structName string, options ...StructOpts) (string // Return the modified content. return buf.String(), nil } + +type ( + // globalArrayOpts represent the options for globar array variables. + globalArrayOpts struct { + values []string + } + + // GlobalArrayOpts configures global array variable changes. + GlobalArrayOpts func(*globalArrayOpts) +) + +// AppendGlobalArrayValue add a new value inside a global array variable. For instances, +// the variable have only one field '[]]' and we want to add +// the `test2 int` the result will be 'test struct{ test1 string, test int }'. +func AppendGlobalArrayValue(value string) GlobalArrayOpts { + return func(c *globalArrayOpts) { + c.values = append(c.values, value) + } +} + +func newGlobalArrayOptions() globalArrayOpts { + return globalArrayOpts{ + values: make([]string, 0), + } +} + +// ModifyGlobalArrayVar modifies an array global array variable in the provided Go source code by appending new values. +func ModifyGlobalArrayVar(fileContent, globalName string, options ...GlobalArrayOpts) (string, error) { + opts := newGlobalArrayOptions() + for _, o := range options { + o(&opts) + } + + fileSet := token.NewFileSet() + + f, err := parser.ParseFile(fileSet, "", fileContent, parser.ParseComments) + if err != nil { + return "", err + } + cmap := ast.NewCommentMap(fileSet, f, f.Comments) + + var found bool + ast.Inspect(f, func(n ast.Node) bool { + genDecl, ok := n.(*ast.GenDecl) + if !ok || genDecl.Tok != token.VAR { + return true + } + + for _, spec := range genDecl.Specs { + valueSpec, ok := spec.(*ast.ValueSpec) + if !ok || len(valueSpec.Names) == 0 || valueSpec.Names[0].Name != globalName { + continue + } + + if len(valueSpec.Values) == 0 { + continue + } + + compLit, ok := valueSpec.Values[0].(*ast.CompositeLit) + if !ok { + continue + } + + file := fileSet.File(compLit.Pos()) + maxOffset := file.Offset(compLit.Rbrace) + for _, elt := range compLit.Elts { + if pos := elt.End(); pos.IsValid() { + offset := file.Offset(pos) + if offset > maxOffset { + maxOffset = offset + } + } + } + + for i, v := range opts.values { + // Advance position + insertOffset := maxOffset + i + insertPos := file.Pos(insertOffset) + + value := ast.NewIdent(v) + value.NamePos = insertPos + + compLit.Elts = append(compLit.Elts, value) + compLit.Rbrace += token.Pos(i + 1) + } + + // Ensure closing brace is on a new line and add comma after last element + if len(compLit.Elts) > 0 { + last := compLit.Elts[len(compLit.Elts)-1] + if file.Line(compLit.Rbrace) == file.Line(last.End())-1 { + // Add comma after last element + file.AddLine(file.Offset(compLit.Rbrace)) + compLit.Rbrace += token.Pos(1) + } + } + + found = true + return false // Stop searching once we modify the struct. + } + return true + }) + + if !found { + return "", errors.Errorf("global array %q not found in file content", globalName) + } + + f.Comments = cmap.Filter(f).Comments() + + var buf bytes.Buffer + if err := format.Node(&buf, fileSet, f); err != nil { + return "", err + } + + return buf.String(), nil +} diff --git a/ignite/pkg/xast/global_test.go b/ignite/pkg/xast/global_test.go index 2abaa97233..fdef462ac1 100644 --- a/ignite/pkg/xast/global_test.go +++ b/ignite/pkg/xast/global_test.go @@ -45,6 +45,30 @@ import ( var myIntVar int = 42 // This is a comment +`, + }, + { + name: "Insert global int var without type", + args: args{ + fileContent: `package main + +import ( + "fmt" +) + +`, + globalType: GlobalTypeVar, + globals: []GlobalOptions{ + WithGlobal("myIntVar", "", "42"), + }, + }, + want: `package main + +import ( + "fmt" +) + +var myIntVar = 42 `, }, { @@ -540,3 +564,107 @@ type MyStruct struct { }) } } + +func TestModifyGlobalArrayVar(t *testing.T) { + type args struct { + fileContent string + globalName string + options []GlobalArrayOpts + } + tests := []struct { + name string + args args + want string + err error + }{ + { + name: "Add field to custom variable array", + args: args{ + fileContent: `package app +var ( + moduleAccPerms = []*authmodulev1.ModuleAccountPermission{ + {Account: nft.ModuleName}, + {Account: ibctransfertypes.ModuleName, Permissions: []string{authtypes.Minter, authtypes.Burner}}, + } +) +`, + globalName: "moduleAccPerms", + options: []GlobalArrayOpts{AppendGlobalArrayValue("{Account: icatypes.ModuleName}")}, + }, + want: `package app + +var ( + moduleAccPerms = []*authmodulev1.ModuleAccountPermission{ + {Account: nft.ModuleName}, + {Account: ibctransfertypes.ModuleName, Permissions: []string{authtypes.Minter, authtypes.Burner}}, + {Account: icatypes.ModuleName}, + } +) +`, + }, + { + name: "Add field to string variable array", + args: args{ + fileContent: `package app + +var ( + blockAccAddrs = []string{ + authtypes.FeeCollectorName, + distrtypes.ModuleName, + minttypes.ModuleName, + stakingtypes.BondedPoolName, + stakingtypes.NotBondedPoolName, + } +) +`, + globalName: "blockAccAddrs", + options: []GlobalArrayOpts{AppendGlobalArrayValue("nft.ModuleName")}, + }, + want: `package app + +var ( + blockAccAddrs = []string{ + authtypes.FeeCollectorName, + distrtypes.ModuleName, + minttypes.ModuleName, + stakingtypes.BondedPoolName, + stakingtypes.NotBondedPoolName, + nft.ModuleName, + } +) +`, + }, + { + name: "name not found", + args: args{ + fileContent: `package app + +var ( + blockAccAddrs = []string{ + authtypes.FeeCollectorName, + distrtypes.ModuleName, + minttypes.ModuleName, + stakingtypes.BondedPoolName, + stakingtypes.NotBondedPoolName, + } +) +`, + globalName: "notFound", + options: []GlobalArrayOpts{AppendGlobalArrayValue("nft.ModuleName")}, + }, + err: errors.New("global array \"notFound\" not found in file content"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ModifyGlobalArrayVar(tt.args.fileContent, tt.args.globalName, tt.args.options...) + if tt.err != nil { + require.Error(t, err) + require.Equal(t, tt.err.Error(), err.Error()) + return + } + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/ignite/templates/app/files-minimal/app/app.go.plush b/ignite/templates/app/files-minimal/app/app.go.plush index 0918a5b3c2..1f616d595a 100644 --- a/ignite/templates/app/files-minimal/app/app.go.plush +++ b/ignite/templates/app/files-minimal/app/app.go.plush @@ -64,8 +64,6 @@ type App struct { StakingKeeper *stakingkeeper.Keeper DistrKeeper distrkeeper.Keeper - // this line is used by starport scaffolding # stargate/app/keeperDeclaration - // simulation manager sm *module.SimulationManager } diff --git a/ignite/templates/app/files-minimal/app/app_config.go.plush b/ignite/templates/app/files-minimal/app/app_config.go.plush index 715d3dca29..3cb419eb1a 100644 --- a/ignite/templates/app/files-minimal/app/app_config.go.plush +++ b/ignite/templates/app/files-minimal/app/app_config.go.plush @@ -35,7 +35,6 @@ var ( {Account: minttypes.ModuleName, Permissions: []string{authtypes.Minter}}, {Account: stakingtypes.BondedPoolName, Permissions: []string{authtypes.Burner, stakingtypes.ModuleName}}, {Account: stakingtypes.NotBondedPoolName, Permissions: []string{authtypes.Burner, stakingtypes.ModuleName}}, - // this line is used by starport scaffolding # stargate/app/maccPerms } // blocked account addresses diff --git a/ignite/templates/app/files/app/app.go.plush b/ignite/templates/app/files/app/app.go.plush index f81c94532f..def9015807 100644 --- a/ignite/templates/app/files/app/app.go.plush +++ b/ignite/templates/app/files/app/app.go.plush @@ -98,8 +98,6 @@ type App struct { ICAHostKeeper icahostkeeper.Keeper TransferKeeper ibctransferkeeper.Keeper - // this line is used by starport scaffolding # stargate/app/keeperDeclaration - // simulation manager sm *module.SimulationManager } diff --git a/ignite/templates/app/files/app/app_config.go.plush b/ignite/templates/app/files/app/app_config.go.plush index 3063914858..08879095dc 100644 --- a/ignite/templates/app/files/app/app_config.go.plush +++ b/ignite/templates/app/files/app/app_config.go.plush @@ -81,7 +81,6 @@ var ( {Account: nft.ModuleName}, {Account: ibctransfertypes.ModuleName, Permissions: []string{authtypes.Minter, authtypes.Burner}}, {Account: icatypes.ModuleName}, - // this line is used by starport scaffolding # stargate/app/maccPerms } // blocked account addresses diff --git a/ignite/templates/ibc/packet.go b/ignite/templates/ibc/packet.go index 373a500356..961236186a 100644 --- a/ignite/templates/ibc/packet.go +++ b/ignite/templates/ibc/packet.go @@ -64,7 +64,7 @@ func NewPacket(replacer placeholder.Replacer, opts *PacketOptions) (*genny.Gener g := genny.New() g.RunFn(moduleModify(replacer, opts)) g.RunFn(protoModify(opts)) - g.RunFn(eventModify(replacer, opts)) + g.RunFn(eventModify(opts)) if err := g.OnlyFS(subPacketComponent, nil, nil); err != nil { return g, err } @@ -261,7 +261,7 @@ func protoModify(opts *PacketOptions) genny.RunFn { } } -func eventModify(replacer placeholder.Replacer, opts *PacketOptions) genny.RunFn { +func eventModify(opts *PacketOptions) genny.RunFn { return func(r *genny.Runner) error { path := filepath.Join("x", opts.ModuleName, "types/events_ibc.go") f, err := r.Disk.Find(path) @@ -269,15 +269,19 @@ func eventModify(replacer placeholder.Replacer, opts *PacketOptions) genny.RunFn return err } - template := `EventType%[2]vPacket = "%[3]v_packet" -%[1]v` - replacement := fmt.Sprintf( - template, - PlaceholderIBCPacketEvent, - opts.PacketName.UpperCamel, - opts.PacketName.LowerCamel, + // Keeper declaration + content, err := xast.InsertGlobal( + f.String(), + xast.GlobalTypeConst, + xast.WithGlobal( + fmt.Sprintf("EventType%[1]vPacket", opts.PacketName.UpperCamel), + "", + fmt.Sprintf(`"%[1]v_packet"`, opts.PacketName.LowerCamel), + ), ) - content := replacer.Replace(f.String(), PlaceholderIBCPacketEvent, replacement) + if err != nil { + return err + } newFile := genny.NewFileS(path, content) return r.File(newFile) diff --git a/ignite/templates/ibc/placeholders.go b/ignite/templates/ibc/placeholders.go index d6f2be019c..106e27102c 100644 --- a/ignite/templates/ibc/placeholders.go +++ b/ignite/templates/ibc/placeholders.go @@ -3,7 +3,6 @@ package ibc //nolint:godot const ( // Placeholders IBC packets - PlaceholderIBCPacketEvent = "// this line is used by starport scaffolding # ibc/packet/event" PlaceholderIBCPacketModuleRecv = "// this line is used by starport scaffolding # ibc/packet/module/recv" PlaceholderIBCPacketModuleAck = "// this line is used by starport scaffolding # ibc/packet/module/ack" PlaceholderIBCPacketModuleTimeout = "// this line is used by starport scaffolding # ibc/packet/module/timeout" diff --git a/ignite/templates/module/create/base.go b/ignite/templates/module/create/base.go index d6d4353aae..ee413283c8 100644 --- a/ignite/templates/module/create/base.go +++ b/ignite/templates/module/create/base.go @@ -59,7 +59,7 @@ func NewGenerator(opts *CreateOptions) (*genny.Generator, error) { // NewAppModify returns generator with modifications required to register a module in the app. func NewAppModify(replacer placeholder.Replacer, opts *CreateOptions) *genny.Generator { g := genny.New() - g.RunFn(appModify(replacer, opts)) + g.RunFn(appModify(opts)) g.RunFn(appConfigModify(replacer, opts)) if opts.IsIBC { g.RunFn(appIBCModify(replacer, opts)) @@ -113,15 +113,17 @@ func appConfigModify(replacer placeholder.Replacer, opts *CreateOptions) genny.R for _, dep := range opts.Dependencies { // If bank is a dependency, add account permissions to the module if dep.Name == "Bank" { - template = `{Account: %[2]vmoduletypes.ModuleName, Permissions: []string{authtypes.Minter, authtypes.Burner, authtypes.Staking}}, -%[1]v` - replacement = fmt.Sprintf( - template, - module.PlaceholderSgAppMaccPerms, + "{Account: %[1]vmoduletypes.ModuleName, Permissions: []string{authtypes.Minter, authtypes.Burner, authtypes.Staking}}", opts.ModuleName, ) - content = replacer.Replace(content, module.PlaceholderSgAppMaccPerms, replacement) + + // Keeper definition + content, err = xast.ModifyGlobalArrayVar(content, "moduleAccPerms", xast.AppendGlobalArrayValue(replacement)) + if err != nil { + return err + } + } } @@ -132,7 +134,7 @@ func appConfigModify(replacer placeholder.Replacer, opts *CreateOptions) genny.R } // app.go modification when creating a module. -func appModify(replacer placeholder.Replacer, opts *CreateOptions) genny.RunFn { +func appModify(opts *CreateOptions) genny.RunFn { return func(r *genny.Runner) error { appPath := module.PathAppGo f, err := r.Disk.Find(appPath) @@ -153,15 +155,17 @@ func appModify(replacer placeholder.Replacer, opts *CreateOptions) genny.RunFn { } // Keeper declaration - template := `%[2]vKeeper %[3]vmodulekeeper.Keeper -%[1]v` - replacement := fmt.Sprintf( - template, - module.PlaceholderSgAppKeeperDeclaration, - xstrings.Title(opts.ModuleName), - opts.ModuleName, + content, err = xast.ModifyStruct( + content, + "App", + xast.AppendStructValue( + fmt.Sprintf("%[1]vKeeper", xstrings.Title(opts.ModuleName)), + fmt.Sprintf("%[1]vmodulekeeper.Keeper", opts.ModuleName), + ), ) - content = replacer.Replace(content, module.PlaceholderSgAppKeeperDeclaration, replacement) + if err != nil { + return err + } // Keeper definition content, err = xast.ModifyFunction( diff --git a/ignite/templates/module/create/files/ibc/x/{{moduleName}}/client/cli/tx.go.plush b/ignite/templates/module/create/files/ibc/x/{{moduleName}}/client/cli/tx.go.plush index 58b885e9db..a7911d3f25 100644 --- a/ignite/templates/module/create/files/ibc/x/{{moduleName}}/client/cli/tx.go.plush +++ b/ignite/templates/module/create/files/ibc/x/{{moduleName}}/client/cli/tx.go.plush @@ -25,7 +25,5 @@ func GetTxCmd() *cobra.Command { RunE: client.ValidateCmd, } - // this line is used by starport scaffolding # 1 - return cmd } diff --git a/ignite/templates/module/create/files/ibc/x/{{moduleName}}/types/events_ibc.go.plush b/ignite/templates/module/create/files/ibc/x/{{moduleName}}/types/events_ibc.go.plush index 3caff28893..10de7a07c3 100644 --- a/ignite/templates/module/create/files/ibc/x/{{moduleName}}/types/events_ibc.go.plush +++ b/ignite/templates/module/create/files/ibc/x/{{moduleName}}/types/events_ibc.go.plush @@ -3,7 +3,6 @@ package types // IBC events const ( EventTypeTimeout = "timeout" - // this line is used by starport scaffolding # ibc/packet/event AttributeKeyAckSuccess = "success" AttributeKeyAck = "acknowledgement" diff --git a/ignite/templates/module/placeholders.go b/ignite/templates/module/placeholders.go index 7bc358efb8..e5d4699e8b 100644 --- a/ignite/templates/module/placeholders.go +++ b/ignite/templates/module/placeholders.go @@ -3,12 +3,10 @@ package module //nolint:godot const ( // Placeholders in app.go - PlaceholderSgAppKeeperDeclaration = "// this line is used by starport scaffolding # stargate/app/keeperDeclaration" - PlaceholderSgAppInitGenesis = "// this line is used by starport scaffolding # stargate/app/initGenesis" - PlaceholderSgAppBeginBlockers = "// this line is used by starport scaffolding # stargate/app/beginBlockers" - PlaceholderSgAppEndBlockers = "// this line is used by starport scaffolding # stargate/app/endBlockers" - PlaceholderSgAppMaccPerms = "// this line is used by starport scaffolding # stargate/app/maccPerms" - PlaceholderSgAppModuleConfig = "// this line is used by starport scaffolding # stargate/app/moduleConfig" + PlaceholderSgAppInitGenesis = "// this line is used by starport scaffolding # stargate/app/initGenesis" + PlaceholderSgAppBeginBlockers = "// this line is used by starport scaffolding # stargate/app/beginBlockers" + PlaceholderSgAppEndBlockers = "// this line is used by starport scaffolding # stargate/app/endBlockers" + PlaceholderSgAppModuleConfig = "// this line is used by starport scaffolding # stargate/app/moduleConfig" // Placeholders IBC PlaceholderIBCKeysName = "// this line is used by starport scaffolding # ibc/keys/name"