diff --git a/changelog.md b/changelog.md index 86fc19bb74..3f53c3c879 100644 --- a/changelog.md +++ b/changelog.md @@ -13,7 +13,7 @@ - [#4686](https://github.com/ignite/cli/pull/4686) Filter discovered protos to only messages. - [#4691](https://github.com/ignite/cli/pull/4691), [#4706](https://github.com/ignite/cli/pull/4706), [#4725](https://github.com/ignite/cli/pull/4725), [#4737](https://github.com/ignite/cli/pull/4737) Fix ts-client query template and solely Go template for `ts-client` generation. -- [#4742](https://github.com/ignite/cli/pull/4742) Updates Vue composables template for new ts-client and tanstack/vue-query v5 +- [#4744](https://github.com/ignite/cli/pull/4744) Remove `react` frontend generation via `s react` command. Use the [Ignite CCA App](https://github.com/ignite/apps) instead. ## [`v28.10.0`](https://github.com/ignite/cli/releases/tag/v28.10.0) diff --git a/docs/docs/03-clients/03-vue.md b/docs/docs/03-clients/03-vue.md index 3619cec6e5..91f8c67c43 100644 --- a/docs/docs/03-clients/03-vue.md +++ b/docs/docs/03-clients/03-vue.md @@ -94,20 +94,22 @@ purposes, but you should not do this in production. In the `example` directory run the following command to start your blockchain: -``` +```bash ignite chain serve ``` To start your Vue application, go to the `vue` directory and run the following command in a separate terminal window: -``` -npm install && npm run dev +:::note +Make sure you have [pnpm](https://pnpm.io/) installed. +::: + +```bash +pnpm install && pnpm dev ``` -It is recommended to run `npm install` before starting your app with `npm run -dev` to ensure that all dependencies are installed (including the ones that the -API client has, see `vue/postinstall.js`). +It is recommended to run `pnpm install` before starting your app with `pnpm dev` to ensure that all dependencies are installed (including the ones that the API client has, see `vue/postinstall.js`). Open your browser and navigate to [http://localhost:5173/](http://localhost:5173/). diff --git a/go.mod b/go.mod index 1c1aa05384..e1c0730262 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( github.com/hashicorp/go-plugin v1.6.0 github.com/iancoleman/strcase v0.3.0 github.com/ignite/ignite-files/protoc v0.0.1 - github.com/ignite/web v0.6.1 + github.com/ignite/web v1.0.8 github.com/imdario/mergo v0.3.13 github.com/jpillora/chisel v1.9.1 github.com/lib/pq v1.10.9 diff --git a/go.sum b/go.sum index edc878357f..e20193a90c 100644 --- a/go.sum +++ b/go.sum @@ -835,8 +835,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/ignite/ignite-files/protoc v0.0.1 h1:wXxU1dzruUgSVl1diAuAOA+xv0NQKXJFsDWht2+tAP8= github.com/ignite/ignite-files/protoc v0.0.1/go.mod h1:cVCHJbEHPIeKHMPk3ZoPS0Xw4XQfUc76BAMAPU9Fwjg= -github.com/ignite/web v0.6.1 h1:kHG+T7NnR8cCPjAGxEFQD+njVYM08toeG57iYRXzpwo= -github.com/ignite/web v0.6.1/go.mod h1:WZWBaBYF8RazN7dE462BLpvXDY8ScacxcJ07BKwX/jY= +github.com/ignite/web v1.0.8 h1:St3L6UJj70+h16+No5em8Vn2Hx93tS2G1MyWO/Kt1cc= +github.com/ignite/web v1.0.8/go.mod h1:WZWBaBYF8RazN7dE462BLpvXDY8ScacxcJ07BKwX/jY= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= diff --git a/ignite/cmd/generate.go b/ignite/cmd/generate.go index 4e3ad4d602..36f853e01b 100644 --- a/ignite/cmd/generate.go +++ b/ignite/cmd/generate.go @@ -36,7 +36,6 @@ meant to be edited by hand. c.AddCommand(NewGenerateTSClient()) c.AddCommand(NewGenerateVuex()) c.AddCommand(NewGenerateComposables()) - c.AddCommand(NewGenerateHooks()) c.AddCommand(NewGenerateOpenAPI()) return c diff --git a/ignite/cmd/generate_hooks.go b/ignite/cmd/generate_hooks.go deleted file mode 100644 index 84f570faef..0000000000 --- a/ignite/cmd/generate_hooks.go +++ /dev/null @@ -1,58 +0,0 @@ -package ignitecmd - -import ( - "github.com/spf13/cobra" - - "github.com/ignite/cli/v28/ignite/pkg/cliui" - "github.com/ignite/cli/v28/ignite/pkg/cliui/icons" - "github.com/ignite/cli/v28/ignite/services/chain" -) - -func NewGenerateHooks() *cobra.Command { - c := &cobra.Command{ - Use: "hooks", - Short: "TypeScript frontend client and React hooks", - RunE: generateHooksHandler, - } - - c.Flags().AddFlagSet(flagSetYes()) - c.Flags().StringP(flagOutput, "o", "", "React hooks output path") - - return c -} - -func generateHooksHandler(cmd *cobra.Command, _ []string) error { - session := cliui.New(cliui.StartSpinnerWithText(statusGenerating)) - defer session.End() - - c, err := chain.NewWithHomeFlags( - cmd, - chain.WithOutputer(session), - chain.CollectEvents(session.EventBus()), - chain.PrintGeneratedPaths()) - if err != nil { - return err - } - - cacheStorage, err := newCache(cmd) - if err != nil { - return err - } - - output, err := cmd.Flags().GetString(flagOutput) - if err != nil { - return err - } - - var opts []chain.GenerateTarget - if flagGetEnableProtoVendor(cmd) { - opts = append(opts, chain.GenerateProtoVendor()) - } - - err = c.Generate(cmd.Context(), cacheStorage, chain.GenerateHooks(output), opts...) - if err != nil { - return err - } - - return session.Println(icons.OK, "Generated Typescript Client and React hooks") -} diff --git a/ignite/cmd/scaffold_react.go b/ignite/cmd/scaffold_react.go index 3c04eb9ac1..60a9dfd47d 100644 --- a/ignite/cmd/scaffold_react.go +++ b/ignite/cmd/scaffold_react.go @@ -2,37 +2,14 @@ package ignitecmd import ( "github.com/spf13/cobra" - - chainconfig "github.com/ignite/cli/v28/ignite/config/chain" - "github.com/ignite/cli/v28/ignite/pkg/cliui" - "github.com/ignite/cli/v28/ignite/pkg/cosmosgen" ) // NewScaffoldReact scaffolds a React app for a chain. func NewScaffoldReact() *cobra.Command { c := &cobra.Command{ - Hidden: true, // hidden util we have a better ts-client. - Use: "react", - Short: "React web app template", - Args: cobra.NoArgs, - PreRunE: migrationPreRunHandler, - RunE: scaffoldReactHandler, + Use: "react", + Deprecated: "the React scaffolding feature is removed from Ignite CLI.\nPlease use the Ignite CCA app to create a React app.\nFor more information, visit: https://ignite.com/marketplace/CCA", } - c.Flags().AddFlagSet(flagSetYes()) - c.Flags().StringP(flagPath, "p", "./"+chainconfig.DefaultReactPath, "path to scaffold content of the React app") - return c } - -func scaffoldReactHandler(cmd *cobra.Command, _ []string) error { - session := cliui.New(cliui.StartSpinnerWithText(statusScaffolding)) - defer session.End() - - path := flagGetPath(cmd) - if err := cosmosgen.React(path); err != nil { - return err - } - - return session.Printf("\nšŸŽ‰ Scaffolded a React app in %s.\n\n", path) -} diff --git a/ignite/cmd/scaffold_vue.go b/ignite/cmd/scaffold_vue.go index 7c095ba506..fd29dbde3c 100644 --- a/ignite/cmd/scaffold_vue.go +++ b/ignite/cmd/scaffold_vue.go @@ -1,6 +1,8 @@ package ignitecmd import ( + "path/filepath" + "github.com/spf13/cobra" chainconfig "github.com/ignite/cli/v28/ignite/config/chain" @@ -11,7 +13,7 @@ import ( // NewScaffoldVue scaffolds a Vue.js app for a chain. func NewScaffoldVue() *cobra.Command { c := &cobra.Command{ - Hidden: true, // hidden util we have a better ts-client. + Hidden: true, Use: "vue", Short: "Vue 3 web app template", Args: cobra.NoArgs, @@ -20,7 +22,6 @@ func NewScaffoldVue() *cobra.Command { } c.Flags().AddFlagSet(flagSetYes()) - c.Flags().StringP(flagPath, "p", "./"+chainconfig.DefaultVuePath, "path to scaffold content of the Vue.js app") return c } @@ -29,7 +30,7 @@ func scaffoldVueHandler(cmd *cobra.Command, _ []string) error { session := cliui.New(cliui.StartSpinnerWithText(statusScaffolding)) defer session.End() - path := flagGetPath(cmd) + path := filepath.Join(".", chainconfig.DefaultVuePath) if err := cosmosgen.Vue(path); err != nil { return err } diff --git a/ignite/config/chain/base/config.go b/ignite/config/chain/base/config.go index 3ce3de2d41..f5b71b5b73 100644 --- a/ignite/config/chain/base/config.go +++ b/ignite/config/chain/base/config.go @@ -63,9 +63,6 @@ type Client struct { // Composables configures code generation for Vue 3 composables. Composables Composables `yaml:"composables,omitempty"` - // Hooks configures code generation for React hooks. - Hooks Hooks `yaml:"hooks,omitempty"` - // OpenAPI configures OpenAPI spec generation for API. OpenAPI OpenAPI `yaml:"openapi,omitempty"` } @@ -90,12 +87,6 @@ type Composables struct { Path string `yaml:"path"` } -// Hooks configures code generation for react-query hooks. -type Hooks struct { - // Path configures out location for generated vue-query hooks. - Path string `yaml:"path"` -} - // OpenAPI configures OpenAPI spec generation for API. type OpenAPI struct { Path string `yaml:"path"` diff --git a/ignite/config/chain/config.go b/ignite/config/chain/config.go index 189f7b9765..c3407faca9 100644 --- a/ignite/config/chain/config.go +++ b/ignite/config/chain/config.go @@ -26,10 +26,6 @@ var ( // The path is relative to the app's directory. DefaultVuePath = "vue" - // DefaultReactPath defines the default relative path to use when scaffolding a React app. - // The path is relative to the app's directory. - DefaultReactPath = "react" - // DefaultVuexPath defines the default relative path to use when generating Vuex stores for a Vue app. // The path is relative to the app's directory. DefaultVuexPath = "vue/src/store" @@ -38,9 +34,8 @@ var ( // The path is relative to the app's directory. DefaultComposablesPath = "vue/src/composables" - // DefaultHooksPath defines the default relative path to use when generating useQuery hooks for a React app. - // The path is relative to the app's directory. - DefaultHooksPath = "react/src/hooks" + // DefaultVueTypesPath defines the default vue types path. + DefaultVueTypesPath = "vue/src/views/Types.vue" // DefaultOpenAPIPath defines the default relative path to use when generating an OpenAPI schema. // The path is relative to the app's directory. @@ -112,16 +107,6 @@ func ComposablesPath(conf *Config) string { return DefaultComposablesPath } -// HooksPath returns the relative path to the React useQuery hooks directory. -// Path is relative to the app's directory. -func HooksPath(conf *Config) string { - if path := strings.TrimSpace(conf.Client.Hooks.Path); path != "" { - return filepath.Clean(path) - } - - return DefaultHooksPath -} - // LocateDefault locates the default path for the config file. // Returns ErrConfigNotFound when no config file found. func LocateDefault(root string) (path string, err error) { diff --git a/ignite/pkg/cosmosgen/cosmosgen.go b/ignite/pkg/cosmosgen/cosmosgen.go index 7f3917d3d7..92dc8b74a3 100644 --- a/ignite/pkg/cosmosgen/cosmosgen.go +++ b/ignite/pkg/cosmosgen/cosmosgen.go @@ -32,9 +32,6 @@ type generateOptions struct { composablesOut func(module.Module) string composablesRootPath string - hooksOut func(module.Module) string - hooksRootPath string - specOut string } @@ -68,14 +65,7 @@ func WithComposablesGeneration(out ModulePathFunc, composablesRootPath string) O } } -func WithHooksGeneration(out ModulePathFunc, hooksRootPath string) Option { - return func(o *generateOptions) { - o.hooksOut = out - o.hooksRootPath = hooksRootPath - } -} - -// WithGoGeneration adds protobuf (gogoproto and pulsar) code generation. +// WithGoGeneration adds protobuf (gogoproto) code generation. func WithGoGeneration() Option { return func(o *generateOptions) { o.generateProtobuf = true @@ -112,6 +102,7 @@ type generator struct { appPath string protoDir string goModPath string + frontendPath string opts *generateOptions sdkImport string sdkDir string @@ -132,7 +123,7 @@ func (g *generator) cleanup() { // Generate generates code from protoDir of an SDK app residing at appPath with given options. // protoDir must be relative to the projectPath. -func Generate(ctx context.Context, cacheStorage cache.Storage, appPath, protoDir, goModPath string, options ...Option) error { +func Generate(ctx context.Context, cacheStorage cache.Storage, appPath, protoDir, goModPath string, frontendPath string, options ...Option) error { b, err := cosmosbuf.New(cacheStorage, goModPath) if err != nil { return err @@ -143,6 +134,7 @@ func Generate(ctx context.Context, cacheStorage cache.Storage, appPath, protoDir appPath: appPath, protoDir: protoDir, goModPath: goModPath, + frontendPath: frontendPath, opts: &generateOptions{}, thirdModules: make(map[string][]module.Module), thirdModuleIncludes: make(map[string]protoIncludes), @@ -215,26 +207,14 @@ func Generate(ctx context.Context, cacheStorage cache.Storage, appPath, protoDir } if g.opts.composablesRootPath != "" { - if err := g.generateComposables("vue"); err != nil { + if err := g.generateComposables(); err != nil { return err } // Update Vue app dependencies when Vue composables are generated. // This update is required to link the "ts-client" folder so the // package is available during development before publishing it. - if err := g.updateComposableDependencies("vue"); err != nil { - return err - } - } - if g.opts.hooksRootPath != "" { - if err := g.generateComposables("react"); err != nil { - return err - } - - // Update React app dependencies when React hooks are generated. - // This update is required to link the "ts-client" folder so the - // package is available during development before publishing it. - if err := g.updateComposableDependencies("react"); err != nil { + if err := g.updateComposableDependencies(); err != nil { return err } } diff --git a/ignite/pkg/cosmosgen/generate_composables.go b/ignite/pkg/cosmosgen/generate_composables.go index 75b3f89713..fd8f251d60 100644 --- a/ignite/pkg/cosmosgen/generate_composables.go +++ b/ignite/pkg/cosmosgen/generate_composables.go @@ -15,31 +15,30 @@ import ( "github.com/ignite/cli/v28/ignite/pkg/gomodulepath" ) -type composablesGenerator struct { - g *generator - frontendType string -} +func (g *generator) checkVueExists() error { + _, err := os.Stat(filepath.Join(g.appPath, g.frontendPath)) + if errors.Is(err, os.ErrNotExist) { + return errors.New("frontend does not exist, please run `ignite scaffold vue` first") + } -func newComposablesGenerator(g *generator, frontendType string) *composablesGenerator { - return &composablesGenerator{g, frontendType} + return err } -func (g *generator) updateComposableDependencies(frontendType string) error { - // Init the path to the appropriate frontend folder inside the app - frontendPath := filepath.Join(g.appPath, frontendType) - packagesPath := filepath.Join(frontendPath, "package.json") - if _, err := os.Stat(packagesPath); errors.Is(err, os.ErrNotExist) { - return nil +func (g *generator) updateComposableDependencies() error { + if err := g.checkVueExists(); err != nil { + return err } - // Read the Vue app package file + // Init the path to the appropriate frontend folder inside the app + frontendPath := filepath.Join(g.appPath, g.frontendPath) + packagesPath := filepath.Join(g.appPath, g.frontendPath, "package.json") + b, err := os.ReadFile(packagesPath) if err != nil { return err } - var pkg map[string]interface{} - + var pkg map[string]any if err := json.Unmarshal(b, &pkg); err != nil { return errors.Errorf("error parsing %s: %w", packagesPath, err) } @@ -90,7 +89,11 @@ func (g *generator) updateComposableDependencies(frontendType string) error { return nil } -func (g *generator) generateComposables(frontendType string) error { +func (g *generator) generateComposables() error { + if err := g.checkVueExists(); err != nil { + return err + } + chainPath, _, err := gomodulepath.Find(g.appPath) if err != nil { return err @@ -106,7 +109,7 @@ func (g *generator) generateComposables(frontendType string) error { data.Modules = append(data.Modules, modules...) } - vsg := newComposablesGenerator(g, frontendType) + vsg := newComposablesGenerator(g) if err := vsg.generateComposableTemplates(data); err != nil { return err } @@ -114,6 +117,14 @@ func (g *generator) generateComposables(frontendType string) error { return vsg.generateRootTemplates(data) } +type composablesGenerator struct { + g *generator +} + +func newComposablesGenerator(g *generator) *composablesGenerator { + return &composablesGenerator{g} +} + func (g *composablesGenerator) generateComposableTemplates(p generatePayload) error { gg := &errgroup.Group{} @@ -129,35 +140,22 @@ func (g *composablesGenerator) generateComposableTemplates(p generatePayload) er } func (g *composablesGenerator) generateComposableTemplate(m module.Module, p generatePayload) error { - var outDir string - if g.frontendType == "vue" { - outDir = g.g.opts.composablesOut(m) - } else { - outDir = g.g.opts.hooksOut(m) - } - + outDir := g.g.opts.composablesOut(m) if err := os.MkdirAll(outDir, 0o766); err != nil { return err } return templateTSClientComposable.Write(outDir, "", struct { - Module module.Module - PackageNS string - FrontendType string + Module module.Module + PackageNS string }{ - Module: m, - PackageNS: p.PackageNS, - FrontendType: g.frontendType, + Module: m, + PackageNS: p.PackageNS, }) } func (g *composablesGenerator) generateRootTemplates(p generatePayload) error { - var outDir string - if g.frontendType == "vue" { - outDir = g.g.opts.composablesRootPath - } else { - outDir = g.g.opts.hooksRootPath - } + outDir := g.g.opts.composablesRootPath if err := os.MkdirAll(outDir, 0o766); err != nil { return err } diff --git a/ignite/pkg/cosmosgen/templates/composable/index.ts.tpl b/ignite/pkg/cosmosgen/templates/composable/index.ts.tpl index a1c4d9205b..35f238123f 100644 --- a/ignite/pkg/cosmosgen/templates/composable/index.ts.tpl +++ b/ignite/pkg/cosmosgen/templates/composable/index.ts.tpl @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { useQuery, type UseQueryOptions, useInfiniteQuery, type UseInfiniteQueryOptions, type InfiniteData } from "@tanstack/{{- .FrontendType -}}-query"; +import { useQuery, type UseQueryOptions, useInfiniteQuery, type UseInfiniteQueryOptions, type InfiniteData } from "@tanstack/vue-query"; import { useClient } from '../useClient'; export default function use{{ camelCaseUpperSta $.Module.Pkg.Name }}() { diff --git a/ignite/pkg/cosmosgen/webtemplates.go b/ignite/pkg/cosmosgen/webtemplates.go index ba60111f0f..9f194d42e7 100644 --- a/ignite/pkg/cosmosgen/webtemplates.go +++ b/ignite/pkg/cosmosgen/webtemplates.go @@ -6,11 +6,6 @@ import ( "github.com/ignite/cli/v28/ignite/pkg/localfs" ) -// React scaffolds a React app for a chain. -func React(path string) error { - return localfs.Save(webtemplates.ReactBoilerplate(), path) -} - // Vue scaffolds a Vue.js app for a chain. func Vue(path string) error { return localfs.Save(webtemplates.VueBoilerplate(), path) diff --git a/ignite/services/chain/generate.go b/ignite/services/chain/generate.go index 8178660ecd..43623f09e8 100644 --- a/ignite/services/chain/generate.go +++ b/ignite/services/chain/generate.go @@ -21,13 +21,11 @@ type generateOptions struct { isGoEnabled bool isTSClientEnabled bool isComposablesEnabled bool - isHooksEnabled bool isVuexEnabled bool isOpenAPIEnabled bool tsClientPath string vuexPath string composablesPath string - hooksPath string } // GenerateTarget is a target to generate code for from proto files. @@ -72,16 +70,6 @@ func GenerateComposables(path string) GenerateTarget { } } -// GenerateHooks enables generating proto based Typescript Client and React composables. -func GenerateHooks(path string) GenerateTarget { - return func(o *generateOptions) { - o.isOpenAPIEnabled = true - o.isTSClientEnabled = true - o.isHooksEnabled = true - o.hooksPath = path - } -} - // GenerateOpenAPI enables generating OpenAPI spec for your chain. func GenerateOpenAPI() GenerateTarget { return func(o *generateOptions) { @@ -128,10 +116,6 @@ func (c *Chain) generateFromConfig(ctx context.Context, cacheStorage cache.Stora if p := conf.Client.Composables.Path; p != "" { targets = append(targets, GenerateComposables(p)) } - - if p := conf.Client.Hooks.Path; p != "" { - targets = append(targets, GenerateHooks(p)) - } } // Generate proto based code for Go and optionally for any optional targets @@ -173,8 +157,8 @@ func (c *Chain) Generate( } var ( - openAPIPath, tsClientPath, vuexPath, composablesPath, hooksPath string - updateConfig bool + openAPIPath, tsClientPath, vuexPath, composablesPath string + updateConfig bool ) if targetOptions.isOpenAPIEnabled { @@ -268,36 +252,13 @@ func (c *Chain) Generate( ) } - if targetOptions.isHooksEnabled { - hooksPath = targetOptions.hooksPath - if hooksPath == "" { - hooksPath = chainconfig.HooksPath(conf) - - if conf.Client.Hooks.Path == "" { - conf.Client.Hooks.Path = hooksPath - updateConfig = true - } - } - - // Non-absolute Hooks output paths must be treated as relative to the app directory - if !filepath.IsAbs(hooksPath) { - hooksPath = filepath.Join(c.app.Path, hooksPath) - } - - options = append(options, - cosmosgen.WithHooksGeneration( - cosmosgen.ComposableModulePath(hooksPath), - hooksPath, - ), - ) - } - if err := cosmosgen.Generate( ctx, cacheStorage, c.app.Path, conf.Build.Proto.Path, c.app.ImportPath, + chainconfig.DefaultVuePath, options..., ); err != nil { return &CannotBuildAppError{err} @@ -327,14 +288,6 @@ func (c *Chain) Generate( ) } - if targetOptions.isHooksEnabled { - c.ev.Send( - fmt.Sprintf("React hooks path: %s", hooksPath), - events.Icon(icons.Bullet), - events.ProgressFinish(), - ) - } - if targetOptions.isVuexEnabled { c.ev.Send( fmt.Sprintf("Vuex stores path: %s", vuexPath), diff --git a/ignite/services/scaffolder/scaffolder.go b/ignite/services/scaffolder/scaffolder.go index 1968d9b381..d9a9a71994 100644 --- a/ignite/services/scaffolder/scaffolder.go +++ b/ignite/services/scaffolder/scaffolder.go @@ -134,5 +134,5 @@ func protoc(ctx context.Context, cacheStorage cache.Storage, projectPath, gomodP options = append(options, cosmosgen.WithOpenAPIGeneration(openAPIPath)) } - return cosmosgen.Generate(ctx, cacheStorage, projectPath, conf.Build.Proto.Path, gomodPath, options...) + return cosmosgen.Generate(ctx, cacheStorage, projectPath, conf.Build.Proto.Path, gomodPath, chainconfig.DefaultVuePath, options...) } diff --git a/ignite/templates/app/files/readme.md.plush b/ignite/templates/app/files/readme.md.plush index c480ea2870..c0e2c15248 100644 --- a/ignite/templates/app/files/readme.md.plush +++ b/ignite/templates/app/files/readme.md.plush @@ -15,11 +15,10 @@ Your blockchain in development can be configured with `config.yml`. To learn mor ### Web Frontend -Additionally, Ignite CLI offers both Vue and React options for frontend scaffolding: +Additionally, Ignite CLI offers a frontend scaffolding feature (based on Vue) to help you quickly build a web frontend for your blockchain: -For a Vue frontend, use: `ignite scaffold vue` -For a React frontend, use: `ignite scaffold react` -These commands can be run within your scaffolded blockchain project. +Use: `ignite scaffold vue` +This command can be run within your scaffolded blockchain project. For more information see the [monorepo for Ignite front-end development](https://github.com/ignite/web). diff --git a/ignite/templates/typed/list/list.go b/ignite/templates/typed/list/list.go index 251008fb8e..c9ff702f69 100644 --- a/ignite/templates/typed/list/list.go +++ b/ignite/templates/typed/list/list.go @@ -10,6 +10,7 @@ import ( "github.com/emicklei/proto" "github.com/gobuffalo/genny/v2" + chaincfg "github.com/ignite/cli/v28/ignite/config/chain" "github.com/ignite/cli/v28/ignite/pkg/errors" "github.com/ignite/cli/v28/ignite/pkg/gomodulepath" "github.com/ignite/cli/v28/ignite/pkg/placeholder" @@ -413,7 +414,7 @@ func clientCliQueryModify(replacer placeholder.Replacer, opts *typed.Options) ge func frontendSrcStoreAppModify(replacer placeholder.Replacer, opts *typed.Options) genny.RunFn { return func(r *genny.Runner) error { - path := filepath.Join(opts.AppPath, "vue/src/views/Types.vue") + path := filepath.Join(opts.AppPath, chaincfg.DefaultVueTypesPath) f, err := r.Disk.Find(path) if os.IsNotExist(err) { // Skip modification if the app doesn't contain front-end diff --git a/integration/client.go b/integration/client.go deleted file mode 100644 index 9e075f2158..0000000000 --- a/integration/client.go +++ /dev/null @@ -1,157 +0,0 @@ -package envtest - -import ( - "bytes" - "os" - "os/exec" - "path/filepath" - "runtime" - - "github.com/stretchr/testify/require" - - chainconfig "github.com/ignite/cli/v28/ignite/config/chain" - "github.com/ignite/cli/v28/ignite/pkg/cmdrunner" - "github.com/ignite/cli/v28/ignite/pkg/cmdrunner/step" -) - -type clientOptions struct { - env map[string]string - testName, testFilePath string -} - -// ClientOption defines options for the TS client test runner. -type ClientOption func(*clientOptions) - -// ClientEnv option defines environment values for the tests. -func ClientEnv(env map[string]string) ClientOption { - return func(o *clientOptions) { - for k, v := range env { - o.env[k] = v - } - } -} - -// ClientTestName option defines a pattern to match the test names that should be run. -func ClientTestName(pattern string) ClientOption { - return func(o *clientOptions) { - o.testName = pattern - } -} - -// ClientTestFile option defines the name of the file where to look for tests. -func ClientTestFile(filePath string) ClientOption { - return func(o *clientOptions) { - o.testFilePath = filePath - } -} - -// RunClientTests runs the Typescript client tests. -func (a App) RunClientTests(options ...ClientOption) bool { - npm, err := exec.LookPath("npm") - require.NoError(a.env.t, err, "npm binary not found") - - // The root dir for the tests must be an absolute path. - // It is used as the start search point to find test files. - rootDir, err := os.Getwd() - require.NoError(a.env.t, err) - - // The filename of this module is required to be able to define the location - // of the TS client test runner package to be used as working directory when - // running the tests. - _, filename, _, ok := runtime.Caller(0) - if !ok { - a.env.t.Fatal("failed to read file name") - } - - opts := clientOptions{ - env: map[string]string{ - // Absolute path to the blockchain app directory - "TEST_CHAIN_PATH": a.path, - // Absolute path to the TS client directory - "TEST_TSCLIENT_DIR": filepath.Join(a.path, chainconfig.DefaultTSClientPath), - }, - } - for _, o := range options { - o(&opts) - } - - var ( - output bytes.Buffer - env []string - ) - - // Install the dependencies needed to run TS client tests - ok = a.env.Exec("install client dependencies", step.NewSteps( - step.New( - step.Workdir(filepath.Join(a.path, chainconfig.DefaultTSClientPath)), - step.Stdout(&output), - step.Exec(npm, "install"), - step.PostExec(func(err error) error { - // Print the npm output when there is an error - if err != nil { - a.env.t.Log("\n", output.String()) - } - - return err - }), - ), - )) - if !ok { - return false - } - - output.Reset() - - args := []string{"run", "test", "--", "--dir", rootDir} - if opts.testName != "" { - args = append(args, "-t", opts.testName) - } - - if opts.testFilePath != "" { - args = append(args, opts.testFilePath) - } - - for k, v := range opts.env { - env = append(env, cmdrunner.Env(k, v)) - } - - // The tests are run from the TS client test runner package directory - runnerDir := filepath.Join(filepath.Dir(filename), "testdata/tstestrunner") - - // TODO: Ignore stderr ? Errors are already displayed with traceback in the stdout - return a.env.Exec("run client tests", step.NewSteps( - // Make sure the test runner dependencies are installed - step.New( - step.Workdir(runnerDir), - step.Stdout(&output), - step.Exec(npm, "install"), - step.PostExec(func(err error) error { - // Print the npm output when there is an error - if err != nil { - a.env.t.Log("\n", output.String()) - } - - return err - }), - ), - // Run the TS client tests - step.New( - step.Workdir(runnerDir), - step.Stdout(&output), - step.Env(env...), - step.PreExec(func() error { - // Clear the output from the previous step - output.Reset() - - return nil - }), - step.Exec(npm, args...), - step.PostExec(func(err error) error { - // Always print tests output to be available on errors or when verbose is enabled - a.env.t.Log("\n", output.String()) - - return err - }), - ), - )) -} diff --git a/integration/cosmosgen/bank_module_test.go b/integration/cosmosgen/bank_module_test.go deleted file mode 100644 index 61ebe1ebcd..0000000000 --- a/integration/cosmosgen/bank_module_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package cosmosgen_test - -import ( - "context" - "encoding/json" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - chainconfig "github.com/ignite/cli/v28/ignite/config/chain" - "github.com/ignite/cli/v28/ignite/config/chain/base" - "github.com/ignite/cli/v28/ignite/pkg/xurl" - envtest "github.com/ignite/cli/v28/integration" -) - -func TestBankModule(t *testing.T) { - t.Skip() - - var ( - env = envtest.New(t) - app = env.Scaffold("chain", "--no-module") - servers = app.RandomizeServerPorts() - ) - - queryAPI, err := xurl.HTTP(servers.API) - require.NoError(t, err) - - txAPI, err := xurl.HTTP(servers.RPC) - require.NoError(t, err) - - // Accounts to be included in the genesis - accounts := []base.Account{ - { - Name: "account1", - Address: "cosmos1j8hw8283hj80hhq8urxaj40syrzqp77dt8qwhm", - Mnemonic: fmt.Sprint( - "toe mail light plug pact length excess predict real artwork laundry when steel ", - "online adapt clutch debate vehicle dash alter rifle virtual season almost", - ), - Coins: []string{"10000token", "10000stake"}, - }, - { - Name: "account2", - Address: "cosmos19yy9sf00k00cjcwh532haeq8s63uhdy7qs5m2n", - Mnemonic: fmt.Sprint( - "someone major rule wrestle forget want job record coil table enter gold bracket ", - "zone tent music grow shiver width index radio matter asset when", - ), - Coins: []string{"100token", "100stake"}, - }, - { - Name: "account3", - Address: "cosmos10957ee377t2xpwyt4mlpedjldp592h0ylt8uz7", - Mnemonic: fmt.Sprint( - "edit effort own cat chuckle rookie mechanic side tool sausage other fade math ", - "joy midnight cabin act plastic spawn loud chest invest budget rebel", - ), - Coins: []string{"100token", "100stake"}, - }, - } - - app.EditConfig(func(cfg *chainconfig.Config) { - cfg.Accounts = append(cfg.Accounts, accounts...) - }) - - env.Must(app.GenerateTSClient()) - - ctx, cancel := context.WithTimeout(env.Ctx(), envtest.ServeTimeout) - defer cancel() - - go func() { - app.Serve("should serve app", envtest.ExecCtx(ctx)) - }() - - // Wait for the server to be up before running the client tests - err = env.IsAppServed(ctx, servers.API) - require.NoError(t, err) - - testAccounts, err := json.Marshal(accounts) - require.NoError(t, err) - - env.Must(app.RunClientTests( - envtest.ClientTestFile("bank_module_test.ts"), - envtest.ClientEnv(map[string]string{ - "TEST_QUERY_API": queryAPI, - "TEST_TX_API": txAPI, - "TEST_ACCOUNTS": string(testAccounts), - }), - )) -} diff --git a/integration/cosmosgen/bank_module_test.ts b/integration/cosmosgen/bank_module_test.ts deleted file mode 100644 index 8a6090ac97..0000000000 --- a/integration/cosmosgen/bank_module_test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; -import { isDeliverTxSuccess } from "@cosmjs/stargate"; - -describe("bank module", async () => { - const { Client } = await import("client"); - - it("should transfer to two different addresses", async () => { - const { account1, account2, account3 } = globalThis.accounts; - - const mnemonic = account1["Mnemonic"]; - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic); - const [account] = await wallet.getAccounts(); - - const denom = "token"; - const env = { - denom, - rpcURL: globalThis.txApi, - apiURL: globalThis.queryApi, - }; - const client = new Client(env, wallet); - - const toAddresses = [account2["Address"], account3["Address"]]; - - // Both accounts start with 100token before the transfer - const result = await client.signAndBroadcast([ - client.CosmosBankV1Beta1.tx.msgSend({ - value: { - fromAddress: account.address, - toAddress: toAddresses[0], - amount: [{ denom, amount: "100" }], - }, - }), - client.CosmosBankV1Beta1.tx.msgSend({ - value: { - fromAddress: account.address, - toAddress: toAddresses[1], - amount: [{ denom, amount: "200" }], - }, - }), - ]); - - expect(isDeliverTxSuccess(result)).toEqual(true); - - // Check that the transfers were successful - const cases = [ - { address: toAddresses[0], wantAmount: "200" }, - { address: toAddresses[1], wantAmount: "300" }, - ]; - - for (let tc of cases) { - let response = await client.CosmosBankV1Beta1.query.queryBalance( - tc.address, - { denom } - ); - - expect(response.statusText).toEqual("OK"); - expect(response.data.balance.amount).toEqual(tc.wantAmount); - } - }); -}); diff --git a/integration/cosmosgen/cosmosgen_composables_test.go b/integration/cosmosgen/cosmosgen_composables_test.go index 0c65326fae..94240521fc 100644 --- a/integration/cosmosgen/cosmosgen_composables_test.go +++ b/integration/cosmosgen/cosmosgen_composables_test.go @@ -13,7 +13,9 @@ import ( ) func TestCosmosGenScaffoldComposables(t *testing.T) { - t.Skip() + if envtest.IsCI { + t.Skip("Skipping TestCosmosGenScaffoldComposables test in CI environment") + } var ( env = envtest.New(t) @@ -103,6 +105,16 @@ func TestCosmosGenScaffoldComposables(t *testing.T) { composablesDirGenerated := filepath.Join(app.SourcePath(), "vue/src/composables") require.NoError(t, os.RemoveAll(composablesDirGenerated)) + env.Must(env.Exec("scaffold vue", + step.NewSteps(step.New( + step.Exec( + envtest.IgniteApp, + "s", + "vue", + ), + step.Workdir(app.SourcePath()), + )), + )) env.Must(env.Exec("generate composables", step.NewSteps(step.New( step.Exec( @@ -137,16 +149,25 @@ func TestCosmosGenScaffoldComposables(t *testing.T) { "useCosmosUpgradeV1Beta1", "useCosmosVestingV1Beta1", // custom modules - "useTestBlogBlog", - "useTestBlogWithmsg", - "useTestBlogWithoutmsg", + "useBlogBlogV1", + "useBlogWithmsgV1", + "useBlogWithoutmsgV1", } for _, mod := range expectedQueryModules { - _, err := os.Stat(filepath.Join(composablesDirGenerated, mod)) if assert.False(t, os.IsNotExist(err), "missing composable %q in %s", mod, composablesDirGenerated) { assert.NoError(t, err) } } + + if t.Failed() { + // list composables files + composablesFiles, err := os.ReadDir(composablesDirGenerated) + require.NoError(t, err) + t.Log("Composables files:", len(composablesFiles)) + for _, file := range composablesFiles { + t.Logf(" - %s", file.Name()) + } + } } diff --git a/integration/cosmosgen/cosmosgen_hooks_test.go b/integration/cosmosgen/cosmosgen_hooks_test.go deleted file mode 100644 index 10b96fc588..0000000000 --- a/integration/cosmosgen/cosmosgen_hooks_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package cosmosgen_test - -import ( - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/ignite/cli/v28/ignite/pkg/cmdrunner/step" - envtest "github.com/ignite/cli/v28/integration" -) - -func TestCosmosGenScaffoldHooks(t *testing.T) { - t.Skip() - - var ( - env = envtest.New(t) - app = env.Scaffold("github.com/test/blog") - ) - - const ( - withMsgModuleName = "withmsg" - withoutMsgModuleName = "withoutmsg" - ) - - env.Must(env.Exec("add custom module with message", - step.NewSteps(step.New( - step.Exec( - envtest.IgniteApp, - "s", - "module", - "--yes", - withMsgModuleName, - ), - step.Workdir(app.SourcePath()), - )), - )) - - env.Must(env.Exec("create a message", - step.NewSteps(step.New( - step.Exec( - envtest.IgniteApp, - "s", - "message", - "--yes", - "mymessage", - "myfield1", - "myfield2:bool", - "--module", - withMsgModuleName, - ), - step.Workdir(app.SourcePath()), - )), - )) - - env.Must(env.Exec("add custom module without message", - step.NewSteps(step.New( - step.Exec( - envtest.IgniteApp, - "s", - "module", - "--yes", - withoutMsgModuleName, - ), - step.Workdir(app.SourcePath()), - )), - )) - - env.Must(env.Exec("create a type", - step.NewSteps(step.New( - step.Exec( - envtest.IgniteApp, - "s", - "type", - "--yes", - "mytype", - "mytypefield", - "--module", - withoutMsgModuleName, - ), - step.Workdir(app.SourcePath()), - )), - )) - - env.Must(env.Exec("create a query", - step.NewSteps(step.New( - step.Exec( - envtest.IgniteApp, - "s", - "query", - "--yes", - "myQuery", - "mytypefield", - "--module", - withoutMsgModuleName, - ), - step.Workdir(app.SourcePath()), - )), - )) - - hooksDireGenerated := filepath.Join(app.SourcePath(), "react/src/hooks") - require.NoError(t, os.RemoveAll(hooksDireGenerated)) - - env.Must(env.Exec("generate hooks", - step.NewSteps(step.New( - step.Exec( - envtest.IgniteApp, - "g", - "hooks", - "--yes", - "--clear-cache", - ), - step.Workdir(app.SourcePath()), - )), - )) - - expectedQueryModules := []string{ - "useCosmosAuthV1Beta1", - "useCosmosAuthzV1Beta1", - "useCosmosBankV1Beta1", - "useCosmosBaseTendermintV1Beta1", - "useCosmosCrisisV1Beta1", - "useCosmosDistributionV1Beta1", - "useCosmosEvidenceV1Beta1", - "useCosmosFeegrantV1Beta1", - "useCosmosGovV1Beta1", - "useCosmosGovV1", - "useCosmosGroupV1", - "useCosmosMintV1Beta1", - "useCosmosNftV1Beta1", - "useCosmosParamsV1Beta1", - "useCosmosSlashingV1Beta1", - "useCosmosStakingV1Beta1", - "useCosmosTxV1Beta1", - "useCosmosUpgradeV1Beta1", - "useCosmosVestingV1Beta1", - // custom modules - "useTestBlogBlog", - "useTestBlogWithmsg", - "useTestBlogWithoutmsg", - } - - for _, mod := range expectedQueryModules { - - _, err := os.Stat(filepath.Join(hooksDireGenerated, mod)) - if assert.False(t, os.IsNotExist(err), "missing hook %q in %s", mod, hooksDireGenerated) { - assert.NoError(t, err) - } - } -} diff --git a/integration/cosmosgen/cosmosgen_test.go b/integration/cosmosgen/cosmosgen_test.go index 0b14be3d17..ab7cc1f77a 100644 --- a/integration/cosmosgen/cosmosgen_test.go +++ b/integration/cosmosgen/cosmosgen_test.go @@ -13,7 +13,9 @@ import ( ) func TestCosmosGenScaffold(t *testing.T) { - t.Skip("skip till we add a buf token into the CI") + if envtest.IsCI { + t.Skip("Skipping CosmosGenScaffold test in CI environment") + } var ( env = envtest.New(t) @@ -148,4 +150,14 @@ func TestCosmosGenScaffold(t *testing.T) { assert.NoError(t, err) } } + + if t.Failed() { + // list ts-client files + tsFiles, err := os.ReadDir(tsDirGenerated) + require.NoError(t, err) + t.Log("TS files:", len(tsFiles)) + for _, file := range tsFiles { + t.Logf(" - %s", file.Name()) + } + } } diff --git a/integration/cosmosgen/custom_module_test.go b/integration/cosmosgen/custom_module_test.go deleted file mode 100644 index 50ac36d6c1..0000000000 --- a/integration/cosmosgen/custom_module_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package cosmosgen_test - -import ( - "context" - "encoding/json" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - chainconfig "github.com/ignite/cli/v28/ignite/config/chain" - "github.com/ignite/cli/v28/ignite/config/chain/base" - "github.com/ignite/cli/v28/ignite/pkg/cmdrunner/step" - "github.com/ignite/cli/v28/ignite/pkg/xurl" - envtest "github.com/ignite/cli/v28/integration" -) - -func TestCustomModule(t *testing.T) { - t.Skip() - - var ( - env = envtest.New(t) - app = env.Scaffold("chain", "--no-module") - servers = app.RandomizeServerPorts() - ) - - queryAPI, err := xurl.HTTP(servers.API) - require.NoError(t, err) - - txAPI, err := xurl.HTTP(servers.RPC) - require.NoError(t, err) - - // Accounts to be included in the genesis - accounts := []base.Account{ - { - Name: "account1", - Address: "cosmos1j8hw8283hj80hhq8urxaj40syrzqp77dt8qwhm", - Mnemonic: fmt.Sprint( - "toe mail light plug pact length excess predict real artwork laundry when ", - "steel online adapt clutch debate vehicle dash alter rifle virtual season almost", - ), - Coins: []string{"10000token", "10000stake"}, - }, - } - - app.EditConfig(func(cfg *chainconfig.Config) { - cfg.Accounts = append(cfg.Accounts, accounts...) - }) - - path := app.SourcePath() - - env.Must(env.Exec("create a module", - step.NewSteps(step.New( - step.Exec(envtest.IgniteApp, "s", "module", "disco", "--require-registration", "--yes"), - step.Workdir(path), - )), - )) - - env.Must(env.Exec("create a list type", - step.NewSteps(step.New( - step.Exec(envtest.IgniteApp, "s", "list", "entry", "name", "--module", "disco", "--yes"), - step.Workdir(path), - )), - )) - - env.Must(app.GenerateTSClient()) - - ctx, cancel := context.WithTimeout(env.Ctx(), envtest.ServeTimeout) - defer cancel() - - go func() { - app.Serve("serve app", envtest.ExecCtx(ctx)) - }() - - // Wait for the server to be up before running the client tests - err = env.IsAppServed(ctx, servers.API) - require.NoError(t, err) - - testAccounts, err := json.Marshal(accounts) - require.NoError(t, err) - - env.Must(app.RunClientTests( - envtest.ClientTestFile("custom_module_test.ts"), - envtest.ClientEnv(map[string]string{ - "TEST_QUERY_API": queryAPI, - "TEST_TX_API": txAPI, - "TEST_ACCOUNTS": string(testAccounts), - }), - )) -} diff --git a/integration/cosmosgen/custom_module_test.ts b/integration/cosmosgen/custom_module_test.ts deleted file mode 100644 index 4d51795238..0000000000 --- a/integration/cosmosgen/custom_module_test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing' -import { isDeliverTxSuccess } from '@cosmjs/stargate' - -describe('custom module', async () => { - const { Client } = await import('client') - - it('should create a list entry', async () => { - const { account1 } = globalThis.accounts - - const mnemonic = account1['Mnemonic'] - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic) - const [account] = await wallet.getAccounts(); - - const denom = 'token' - const env = { - denom, - rpcURL: globalThis.txApi, - apiURL: globalThis.queryApi, - } - const client = new Client(env, wallet) - - const entry = { - id: '0', - creator: account.address, - name: "test", - } - - // Create a new list entry - const result = await client.ChainDisco.tx.sendMsgCreateEntry({ value: entry }) - - expect(isDeliverTxSuccess(result)).toEqual(true) - - // Check that the list entry is created - const response = await client.ChainDisco.query.queryEntryAll() - - expect(response.statusText).toEqual('OK') - expect(response.data['Entry']).toHaveLength(1) - expect(response.data['Entry'][0]).toEqual(entry) - }) -}) diff --git a/integration/env.go b/integration/env.go index ef2cc5ae56..35aff818e4 100644 --- a/integration/env.go +++ b/integration/env.go @@ -33,7 +33,7 @@ var ( // invoked. IgniteApp = path.Join(os.TempDir(), "ignite-tests", "ignite") - isCI, _ = strconv.ParseBool(os.Getenv("CI")) + IsCI, _ = strconv.ParseBool(os.Getenv("CI")) compileBinaryOnce sync.Once ) diff --git a/integration/exec.go b/integration/exec.go index 87e441cfde..4bbbe8cf38 100644 --- a/integration/exec.go +++ b/integration/exec.go @@ -81,7 +81,7 @@ func (e Env) Exec(msg string, steps step.Steps, options ...ExecOption) (ok bool) fmt.Printf("Executing %d step(s) for %q\n", len(steps), msg) copts = append(copts, cmdrunner.EnableDebug()) } - if isCI { + if IsCI { copts = append(copts, cmdrunner.EndSignal(os.Kill)) } err := cmdrunner.