├── .github
└── workflows
│ └── build.yaml
├── .gitignore
├── .templates
├── jsbase
│ ├── .env
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ └── src
│ │ ├── app.js
│ │ └── server.js
└── tsbase
│ ├── .env
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ └── src
│ ├── app.ts
│ └── server.ts
├── LICENSE.md
├── Makefile
├── README.md
├── assets
└── expressify-logo.png
├── cmd
└── expressify
│ └── main.go
├── go.mod
├── go.sum
└── internal
├── cli_model
└── cli_model.go
├── coding_styles
└── coding_styles.go
├── configs
└── configs.go
├── databases
└── databases.go
├── languages
└── languages.go
├── loggers
└── loggers.go
├── orms
└── orms.go
├── package_managers
└── package_managers.go
├── selector
└── selector.go
├── structure
└── structure.go
└── test_frameworks
└── test_frameworks.go
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | # This workflow will build a golang project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
3 |
4 | name: Go
5 |
6 | on:
7 | push:
8 | branches: [ "main" ]
9 | pull_request:
10 | branches: [ "main" ]
11 |
12 | jobs:
13 |
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v3
18 |
19 | - name: Set up Go
20 | uses: actions/setup-go@v4
21 | with:
22 | go-version: '1.21.3'
23 |
24 | - name: Build
25 | run: go build -o bin/expressify cmd/expressify/main.go
26 |
27 | - name: Test
28 | run: go test -v ./...
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin
2 | .DS_Store
3 | .vscode
4 | .expressify
--------------------------------------------------------------------------------
/.templates/jsbase/.env:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/jsbase/.env
--------------------------------------------------------------------------------
/.templates/jsbase/.eslintrc.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/jsbase/.eslintrc.js
--------------------------------------------------------------------------------
/.templates/jsbase/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/jsbase/.gitignore
--------------------------------------------------------------------------------
/.templates/jsbase/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/jsbase/README.md
--------------------------------------------------------------------------------
/.templates/jsbase/package.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/jsbase/package.json
--------------------------------------------------------------------------------
/.templates/jsbase/src/app.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/jsbase/src/app.js
--------------------------------------------------------------------------------
/.templates/jsbase/src/server.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/jsbase/src/server.js
--------------------------------------------------------------------------------
/.templates/tsbase/.env:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/tsbase/.env
--------------------------------------------------------------------------------
/.templates/tsbase/.eslintrc.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/tsbase/.eslintrc.js
--------------------------------------------------------------------------------
/.templates/tsbase/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/tsbase/.gitignore
--------------------------------------------------------------------------------
/.templates/tsbase/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/tsbase/README.md
--------------------------------------------------------------------------------
/.templates/tsbase/package.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/tsbase/package.json
--------------------------------------------------------------------------------
/.templates/tsbase/src/app.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/tsbase/src/app.ts
--------------------------------------------------------------------------------
/.templates/tsbase/src/server.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/.templates/tsbase/src/server.ts
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Coder's Gyan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | BINARY_NAME=expressify
2 | ENTRY_POINT=cmd/expressify/main.go
3 |
4 | all: test build
5 |
6 | build:
7 | @go build -o bin/$(BINARY_NAME) $(ENTRY_POINT)
8 |
9 | run:
10 | @go run $(ENTRY_POINT)
11 |
12 | test:
13 | @go test ./...
14 |
15 | clean:
16 | @go clean
17 | @rm -f bin/$(BINARY_NAME)
18 |
19 | .PHONY: all build run test clean
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Expressify-CLI 🚀
2 |
3 | 
4 |
5 | **Expressify-CLI** is a command-line tool designed to generate a **production-grade** scaffold for Express applications in a single command. This tool streamlines the setup process for developing Express applications with JavaScript/TypeScript, making it faster and more efficient.
6 |
7 | #### Inspiration 💡
8 |
9 | Setting up a new Express project can often feel like reinventing the wheel. Developers frequently encounter the challenge of integrating a myriad of components such as TypeScript configurations, testing frameworks, linters, Docker configurations, and more. This not only consumes valuable time but also poses a risk of inconsistency and configuration errors.
10 |
11 | #### How Expressify-CLI Solves the Problem 🛠️
12 |
13 | Expressify-CLI tackles these challenges head-on by automating the creation of a comprehensive and robust scaffold for Express applications. With just one command, developers can now generate a project structure that includes:
14 |
15 | - TypeScript (TS) configuration for robust typing. 📐
16 | - Test configurations for reliable code testing. ✅
17 | - Linters for maintaining code quality and consistency. 🔍
18 | - Docker files for easy containerization. 🐳
19 | - Logger setup for effective logging and monitoring. 📝
20 | - Graceful shutdowns for better resource management. 🧘
21 | - Error handling middleware for improved error management. 🚫
22 | - Optional authentication module with JWT for secure access. 🔒
23 | - Pre-configured Git setup with README, license file, and other production-level settings. 📄
24 |
25 | #### Run Locally (Development) 🚀
26 |
27 | Running Expressify-CLI locally is straightforward with the help of the provided Makefile.
28 | Follow these steps to set up and run the project in a development environment:
29 |
30 |
31 | ##### Prerequisites:
32 |
33 | Ensure that Go is installed on your system. Verify this with `go version`.
34 | Familiarity with basic make commands can be helpful.
35 |
36 | **_Notes:_**
37 | The Makefile simplifies common tasks but can be modified if your workflow requires it.
38 | Additional configuration or steps might be necessary depending on the specific setup of your project.
39 |
40 | ##### Clone the Repository
41 |
42 | Start with cloning the repository to your local machine using Git:
43 |
44 | ```bash
45 | git clone https://github.com/codersgyan/expressify.git
46 | cd expressify
47 | ```
48 |
49 | ##### Using the Makefile
50 |
51 | The Makefile includes commands to simplify the build and test process. Here's how you can use it:
52 |
53 | ###### Build the Project:
54 |
55 | Compiles the project and creates an executable.
56 |
57 | ```bash
58 | make build
59 | ```
60 |
61 | ###### Run the Tool:
62 |
63 | Executes the compiled binary.
64 |
65 | ```bash
66 | make run
67 | ```
68 |
69 | ###### Run Tests:
70 |
71 | Runs the automated tests.
72 |
73 | ```bash
74 | make test
75 | ```
76 |
77 | ###### Clean Up:
78 |
79 | Removes the generated binary and any other temporary files.
80 |
81 | ```bash
82 | make clean
83 | ```
84 |
85 | #### Open Source Contribution 🤝
86 |
87 | Expressify-CLI is an open source project, and contributions are greatly appreciated! If you have ideas for improvements or have found a bug, feel free to open an issue. We also warmly welcome pull requests. Let's build a stronger tool together! 🌍🛠️
88 |
--------------------------------------------------------------------------------
/assets/expressify-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersgyan/expressify/6c939f6e2b12090e9b9ea252ebd63396b9ea9aae/assets/expressify-logo.png
--------------------------------------------------------------------------------
/cmd/expressify/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | tea "github.com/charmbracelet/bubbletea"
8 | "github.com/codersgyan/expressify/internal/cli_model"
9 | )
10 |
11 | func main() {
12 |
13 | // cwd, err := os.Getwd()
14 | // if err != nil {
15 | // fmt.Printf("unable to get current working directory: %w", err)
16 | // os.Exit(1)
17 | // }
18 | // srcPath := cwd + "/.templates/jsbase"
19 | // dstPath := cwd + "/.expressify/auth-service"
20 |
21 | // cpErr := structure.CopyDir(srcPath, dstPath)
22 | // if cpErr != nil {
23 | // fmt.Printf("Error copying directory: %s\n", cpErr)
24 | // } else {
25 | // fmt.Println("Directory copied successfully.")
26 | // }
27 | // return n
28 |
29 | p := tea.NewProgram(cli_model.InitialModel())
30 | if _, err := p.Run(); err != nil {
31 | fmt.Printf("Alas, there's been an error: %v", err)
32 | os.Exit(1)
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/codersgyan/expressify
2 |
3 | go 1.21.3
4 |
5 | require (
6 | github.com/charmbracelet/bubbles v0.17.1
7 | github.com/charmbracelet/bubbletea v0.25.0
8 | )
9 |
10 | require (
11 | github.com/atotto/clipboard v0.1.4 // indirect
12 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
13 | github.com/charmbracelet/lipgloss v0.9.1 // indirect
14 | github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
15 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
16 | github.com/mattn/go-isatty v0.0.18 // indirect
17 | github.com/mattn/go-localereader v0.0.1 // indirect
18 | github.com/mattn/go-runewidth v0.0.15 // indirect
19 | github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
20 | github.com/muesli/cancelreader v0.2.2 // indirect
21 | github.com/muesli/reflow v0.3.0 // indirect
22 | github.com/muesli/termenv v0.15.2 // indirect
23 | github.com/rivo/uniseg v0.2.0 // indirect
24 | github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect
25 | golang.org/x/sync v0.1.0 // indirect
26 | golang.org/x/sys v0.12.0 // indirect
27 | golang.org/x/term v0.6.0 // indirect
28 | golang.org/x/text v0.3.8 // indirect
29 | )
30 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
2 | github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
3 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
4 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
5 | github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4=
6 | github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o=
7 | github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
8 | github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
9 | github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
10 | github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
11 | github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
12 | github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
13 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
14 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
15 | github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
16 | github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
17 | github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
18 | github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
19 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
20 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
21 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
22 | github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
23 | github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
24 | github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
25 | github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
26 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
27 | github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
28 | github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
29 | github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
30 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
31 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
32 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
33 | github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
34 | github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
35 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
36 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
37 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
38 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
39 | golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
40 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
41 | golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
42 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
43 | golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
44 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
45 |
--------------------------------------------------------------------------------
/internal/cli_model/cli_model.go:
--------------------------------------------------------------------------------
1 | package cli_model
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/charmbracelet/bubbles/list"
7 | "github.com/charmbracelet/bubbles/textinput"
8 | tea "github.com/charmbracelet/bubbletea"
9 | "github.com/charmbracelet/lipgloss"
10 | "github.com/codersgyan/expressify/internal/coding_styles"
11 | "github.com/codersgyan/expressify/internal/configs"
12 | "github.com/codersgyan/expressify/internal/databases"
13 | "github.com/codersgyan/expressify/internal/languages"
14 | "github.com/codersgyan/expressify/internal/loggers"
15 | "github.com/codersgyan/expressify/internal/orms"
16 | "github.com/codersgyan/expressify/internal/package_managers"
17 | "github.com/codersgyan/expressify/internal/selector"
18 | "github.com/codersgyan/expressify/internal/structure"
19 | "github.com/codersgyan/expressify/internal/test_frameworks"
20 | )
21 |
22 | var quitTextStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4)
23 |
24 | type AppState int
25 |
26 | const (
27 | StateWelcome AppState = iota
28 | StateProjectName
29 | StateLanguage
30 | StatePackageManager
31 | StateTestFramework
32 | StateLoggerLibrary
33 | StateDatabase
34 | StateORM
35 | StateConfig
36 | StateCodingStyle
37 | StateFolderStructure
38 | // StateAuth
39 | // StateMainWork
40 | // StateDone
41 | )
42 |
43 | type CliModel struct {
44 | CurrentState AppState
45 | ProjectNameInput textinput.Model
46 | WelcomeMessage string
47 | LanguageList list.Model
48 | SelectedLanguage string
49 | PackageManagerList list.Model
50 | SelectedPackageManager string
51 | TestFrameworkList list.Model
52 | SelectedTestFramework string
53 | LoggerLibraryList list.Model
54 | SelectedLoggerLibrary string
55 | DatabaseList list.Model
56 | SelectedDatabase string
57 | ORMList list.Model
58 | SelectedORM string
59 | ConfigList list.Model
60 | SelectedConfig string
61 | CodingStyleList list.Model
62 | SelectedCodingStyle string
63 | FolderStructureCreated bool
64 | Error error
65 | }
66 |
67 | func (m CliModel) Init() tea.Cmd {
68 | return nil
69 | }
70 |
71 | type (
72 | errMsg error
73 | )
74 |
75 | func (m CliModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
76 | var cmd tea.Cmd
77 |
78 | switch msg := msg.(type) {
79 | case tea.WindowSizeMsg:
80 | m.LanguageList.SetWidth(msg.Width)
81 | m.ProjectNameInput.Width = msg.Width
82 | return m, nil
83 | case tea.KeyMsg:
84 | switch msg.Type {
85 | case tea.KeyEnter:
86 | // here, only changing the state
87 | if m.CurrentState == StateWelcome {
88 | m.CurrentState = StateProjectName
89 | m.ProjectNameInput.Focus()
90 | return m, nil
91 | }
92 | if m.CurrentState == StateProjectName {
93 | m.CurrentState = StateLanguage
94 | return m, nil
95 | }
96 |
97 | if m.CurrentState == StateLanguage {
98 | i, ok := m.LanguageList.SelectedItem().(selector.Item)
99 | if ok {
100 | m.SelectedLanguage = string(i)
101 | }
102 | m.CurrentState = StatePackageManager
103 | return m, nil
104 | }
105 |
106 | if m.CurrentState == StatePackageManager {
107 | i, ok := m.PackageManagerList.SelectedItem().(selector.Item)
108 | if ok {
109 | m.SelectedPackageManager = string(i)
110 | }
111 | m.CurrentState = StateTestFramework
112 | return m, nil
113 | }
114 |
115 | if m.CurrentState == StateTestFramework {
116 | i, ok := m.TestFrameworkList.SelectedItem().(selector.Item)
117 | if ok {
118 | m.SelectedTestFramework = string(i)
119 | }
120 | m.CurrentState = StateLoggerLibrary
121 | return m, nil
122 | }
123 |
124 | if m.CurrentState == StateLoggerLibrary {
125 | i, ok := m.LoggerLibraryList.SelectedItem().(selector.Item)
126 | if ok {
127 | m.SelectedLoggerLibrary = string(i)
128 | }
129 | m.CurrentState = StateDatabase
130 | return m, nil
131 | }
132 |
133 | if m.CurrentState == StateDatabase {
134 | i, ok := m.DatabaseList.SelectedItem().(selector.Item)
135 | if ok {
136 | m.SelectedDatabase = string(i)
137 | }
138 | m.CurrentState = StateORM
139 | return m, nil
140 | }
141 |
142 | if m.CurrentState == StateORM {
143 | i, ok := m.ORMList.SelectedItem().(selector.Item)
144 | if ok {
145 | m.SelectedORM = string(i)
146 | }
147 | m.CurrentState = StateConfig
148 | return m, nil
149 | }
150 |
151 | if m.CurrentState == StateConfig {
152 | i, ok := m.ConfigList.SelectedItem().(selector.Item)
153 | if ok {
154 | m.SelectedConfig = string(i)
155 | }
156 | m.CurrentState = StateCodingStyle
157 | return m, nil
158 | }
159 |
160 | if m.CurrentState == StateCodingStyle {
161 | i, ok := m.CodingStyleList.SelectedItem().(selector.Item)
162 | if ok {
163 | m.SelectedCodingStyle = string(i)
164 | }
165 | m.CurrentState = StateFolderStructure
166 | return m, nil
167 | }
168 |
169 | if m.CurrentState == StateFolderStructure {
170 | // Create folder structure
171 | err := structure.CreateBaseFileStructure(m.ProjectNameInput.Value(), m.SelectedLanguage)
172 | if err != nil {
173 | fmt.Printf("error creating folder structure: %v", err)
174 | return m, tea.Quit
175 | }
176 | // todo: transition to next state
177 | // m.CurrentState = StateFolderStructure
178 | return m, nil
179 | }
180 |
181 | case tea.KeyEsc, tea.KeyCtrlC:
182 | return m, tea.Quit
183 | }
184 | case errMsg:
185 | m.Error = msg
186 | return m, tea.Quit
187 | }
188 |
189 | if m.CurrentState == StateProjectName {
190 | m.ProjectNameInput, cmd = m.ProjectNameInput.Update(msg)
191 | return m, cmd
192 | }
193 |
194 | if m.CurrentState == StateLanguage {
195 | m.LanguageList, cmd = m.LanguageList.Update(msg)
196 | return m, cmd
197 | }
198 |
199 | if m.CurrentState == StatePackageManager {
200 | m.PackageManagerList, cmd = m.PackageManagerList.Update(msg)
201 | return m, cmd
202 | }
203 |
204 | if m.CurrentState == StateTestFramework {
205 | m.TestFrameworkList, cmd = m.TestFrameworkList.Update(msg)
206 | return m, cmd
207 | }
208 |
209 | if m.CurrentState == StateLoggerLibrary {
210 | m.LoggerLibraryList, cmd = m.LoggerLibraryList.Update(msg)
211 | return m, cmd
212 | }
213 |
214 | if m.CurrentState == StateDatabase {
215 | m.DatabaseList, cmd = m.DatabaseList.Update(msg)
216 | return m, cmd
217 | }
218 |
219 | if m.CurrentState == StateORM {
220 | m.ORMList, cmd = m.ORMList.Update(msg)
221 | return m, cmd
222 | }
223 |
224 | if m.CurrentState == StateConfig {
225 | m.ConfigList, cmd = m.ConfigList.Update(msg)
226 | return m, cmd
227 | }
228 |
229 | if m.CurrentState == StateCodingStyle {
230 | m.CodingStyleList, cmd = m.CodingStyleList.Update(msg)
231 | return m, cmd
232 | }
233 |
234 | return m, nil
235 | }
236 |
237 | func (m CliModel) View() string {
238 | var s string
239 |
240 | switch m.CurrentState {
241 | case StateWelcome:
242 | s = m.WelcomeMessage
243 | case StateProjectName:
244 | s = "\n📗 Enter your project name:\n\n" + m.ProjectNameInput.View()
245 | case StateLanguage:
246 | if m.SelectedLanguage != "" {
247 | var str string
248 | if m.SelectedLanguage == string(languages.JavaScript) {
249 | str = "🎉 Awesome choice! JavaScript brings flexibility and dynamism to your project. Let's get coding! 🚀"
250 | } else if m.SelectedLanguage == string(languages.TypeScript) {
251 | str = "👍 Great pick! TypeScript adds type safety and robustness to your application. Time to build! 🏗️"
252 | }
253 | return quitTextStyle.Render(fmt.Sprintf(str))
254 | }
255 | return m.LanguageList.View()
256 | case StatePackageManager:
257 | if m.SelectedPackageManager != "" {
258 | var str string
259 | if m.SelectedPackageManager == string(package_managers.NPM) {
260 | str = "🎉 Awesome choice! NPM is the world's most popular package manager. Let's get coding! 🚀"
261 | } else if m.SelectedPackageManager == string(package_managers.PNPM) {
262 | str = "👍 Great pick! PNPM is a fast, disk space efficient package manager. Time to build! 🏗️"
263 | }
264 | return quitTextStyle.Render(fmt.Sprintf(str))
265 | }
266 | return m.PackageManagerList.View()
267 | case StateTestFramework:
268 | if m.SelectedTestFramework != "" {
269 | var str string
270 | if m.SelectedTestFramework == string(test_frameworks.SuperTestWithJest) {
271 | str = "🎉 Awesome choice! Supertest is best framework 🚀"
272 | } else if m.SelectedTestFramework == string(test_frameworks.MochaWithChaiHTTP) {
273 | str = "👍 Great pick! Mocha with Chai is powerful framework"
274 | }
275 | return quitTextStyle.Render(fmt.Sprintf(str))
276 | }
277 | return m.TestFrameworkList.View()
278 |
279 | case StateLoggerLibrary:
280 | if m.SelectedLoggerLibrary != "" {
281 | var str string
282 | if m.SelectedLoggerLibrary == string(loggers.Winston) {
283 | str = "🎉 Awesome choice! Winston is best logger out there 🚀"
284 | } else if m.SelectedLoggerLibrary == string(loggers.Bunyan) {
285 | str = "👍 Great pick! Bunyan is powerful logger"
286 | } else if m.SelectedLoggerLibrary == string(loggers.Pino) {
287 | str = "👍 Great pick! Pino is powerful logger"
288 | }
289 | return quitTextStyle.Render(fmt.Sprintf(str))
290 | }
291 | return m.LoggerLibraryList.View()
292 |
293 | case StateDatabase:
294 | if m.SelectedDatabase != "" {
295 | var str string
296 | if m.SelectedDatabase == string(databases.MongoDB) {
297 | str = "🎉 Awesome choice! MongoDB is powerful NoSQL database 🚀"
298 | } else if m.SelectedDatabase == string(databases.PostgreSQL) {
299 | str = "👍 Great pick! PostgreSQL is powerful relational database"
300 | } else if m.SelectedDatabase == string(databases.MySQL) {
301 | str = "👍 Great pick! MySQL is powerful database"
302 | }
303 | return quitTextStyle.Render(fmt.Sprintf(str))
304 | }
305 | return m.DatabaseList.View()
306 |
307 | case StateORM:
308 | if m.SelectedORM != "" {
309 | var str string
310 | if m.SelectedORM == string(orms.Mongoose) {
311 | str = "🎉 Awesome choice! 🚀"
312 | } else if m.SelectedORM == string(orms.Sequelize) {
313 | str = "👍 Great pick!"
314 | } else if m.SelectedORM == string(orms.TypeORM) {
315 | str = "👍 Great pick!"
316 | } else if m.SelectedORM == string(orms.Prisma) {
317 | str = "👍 Great pick!"
318 | } else if m.SelectedORM == string(orms.None) {
319 | str = "🔥 Loving hardcore, yeah 💪"
320 | }
321 | return quitTextStyle.Render(fmt.Sprintf(str))
322 | }
323 | return m.ORMList.View()
324 |
325 | case StateConfig:
326 | if m.SelectedConfig != "" {
327 | var str string
328 | if m.SelectedConfig == string(configs.ENV) {
329 | str = "🎉 Awesome choice! 🚀"
330 | } else if m.SelectedConfig == string(configs.JSON) {
331 | str = "👍 Great pick!"
332 | } else if m.SelectedConfig == string(configs.YAML) {
333 | str = "👍 Great pick!"
334 | }
335 | return quitTextStyle.Render(fmt.Sprintf(str))
336 | }
337 | return m.ConfigList.View()
338 |
339 | case StateCodingStyle:
340 | if m.SelectedCodingStyle != "" {
341 | var str string
342 | if m.SelectedCodingStyle == string(coding_styles.Functional) {
343 | str = "🎉 Awesome choice! 🚀"
344 | } else if m.SelectedCodingStyle == string(coding_styles.ObjectOriented) {
345 | str = "👍 Great pick!"
346 | }
347 | return quitTextStyle.Render(fmt.Sprintf(str))
348 | }
349 | return m.CodingStyleList.View()
350 | }
351 | return s
352 | }
353 |
354 | func InitialModel() CliModel {
355 | projectNameInput := textinput.New()
356 | projectNameInput.Placeholder = "my-expressify-app"
357 | return CliModel{
358 | CurrentState: StateWelcome,
359 | ProjectNameInput: projectNameInput,
360 | WelcomeMessage: `
361 | 🌟🚀 Welcome to Expressify! 🚀🌟
362 | We're thrilled to have you on board for a seamless and efficient Express.js application setup.
363 | Get ready to supercharge your development process with our intuitive CLI tool.
364 |
365 | Let's create something amazing together! 🎉👨💻👩💻
366 |
367 | Press Enter to begin... (or ESC to quit)
368 | `,
369 | LanguageList: languages.NewLanguageSelector().List,
370 | PackageManagerList: package_managers.NewPManagerSelector().List,
371 | TestFrameworkList: test_frameworks.NewTestFrameworkSelector().List,
372 | LoggerLibraryList: loggers.NewLoggerSelector().List,
373 | DatabaseList: databases.NewDatabaseSelector().List,
374 | ORMList: orms.NewORMSelector().List,
375 | ConfigList: configs.NewConfigSelector().List,
376 | CodingStyleList: coding_styles.NewCodingStyleSelector().List,
377 | }
378 | }
379 |
--------------------------------------------------------------------------------
/internal/coding_styles/coding_styles.go:
--------------------------------------------------------------------------------
1 | package coding_styles
2 |
3 | import (
4 | "github.com/charmbracelet/bubbles/list"
5 | "github.com/codersgyan/expressify/internal/selector"
6 | )
7 |
8 | type CodingStyle string
9 |
10 | const (
11 | Functional CodingStyle = "Functional"
12 | ObjectOriented CodingStyle = "Object Oriented"
13 | )
14 |
15 | var codingStyles = []selector.Item{
16 | selector.Item(Functional),
17 | selector.Item(ObjectOriented),
18 | }
19 |
20 | func NewCodingStyleSelector() *selector.Selector {
21 | var items []list.Item
22 | for _, item := range codingStyles {
23 | items = append(items, list.Item(item))
24 | }
25 | return selector.NewSelector("\n😎 Choose a coding style", items)
26 | }
27 |
--------------------------------------------------------------------------------
/internal/configs/configs.go:
--------------------------------------------------------------------------------
1 | package configs
2 |
3 | import (
4 | "github.com/charmbracelet/bubbles/list"
5 | "github.com/codersgyan/expressify/internal/selector"
6 | )
7 |
8 | type Config string
9 |
10 | const (
11 | ENV Config = "ENV based"
12 | JSON Config = "File based (JSON)"
13 | YAML Config = "File based (YAML)"
14 | )
15 |
16 | var configs = []selector.Item{
17 | selector.Item(ENV),
18 | selector.Item(JSON),
19 | selector.Item(YAML),
20 | }
21 |
22 | func NewConfigSelector() *selector.Selector {
23 | var items []list.Item
24 | for _, item := range configs {
25 | items = append(items, list.Item(item))
26 | }
27 | return selector.NewSelector("\n😎 Choose a config style", items)
28 | }
29 |
--------------------------------------------------------------------------------
/internal/databases/databases.go:
--------------------------------------------------------------------------------
1 | package databases
2 |
3 | import (
4 | "github.com/charmbracelet/bubbles/list"
5 | "github.com/codersgyan/expressify/internal/selector"
6 | )
7 |
8 | type Database string
9 |
10 | const (
11 | MongoDB Database = "MongoDB"
12 | PostgreSQL Database = "PostgreSQL"
13 | MySQL Database = "MySQL"
14 | )
15 |
16 | var databases = []selector.Item{
17 | selector.Item(MongoDB),
18 | selector.Item(PostgreSQL),
19 | selector.Item(MySQL),
20 | }
21 |
22 | func NewDatabaseSelector() *selector.Selector {
23 | var items []list.Item
24 | for _, item := range databases {
25 | items = append(items, list.Item(item))
26 | }
27 | return selector.NewSelector("\n😎 Choose a database", items)
28 | }
29 |
--------------------------------------------------------------------------------
/internal/languages/languages.go:
--------------------------------------------------------------------------------
1 | package languages
2 |
3 | import (
4 | "github.com/charmbracelet/bubbles/list"
5 | "github.com/codersgyan/expressify/internal/selector"
6 | )
7 |
8 | type Language string
9 |
10 | const (
11 | JavaScript Language = "JavaScript"
12 | TypeScript Language = "TypeScript"
13 | )
14 |
15 | var languageList = []selector.Item{
16 | selector.Item(JavaScript),
17 | selector.Item(TypeScript),
18 | // selector.Item("JavaScript"),
19 | // selector.Item("TypeScript"),
20 | }
21 |
22 | func NewLanguageSelector() *selector.Selector {
23 | var items []list.Item
24 | for _, item := range languageList {
25 | items = append(items, list.Item(item))
26 | }
27 | return selector.NewSelector("\n😎 Choose a language", items)
28 | }
29 |
--------------------------------------------------------------------------------
/internal/loggers/loggers.go:
--------------------------------------------------------------------------------
1 | package loggers
2 |
3 | import (
4 | "github.com/charmbracelet/bubbles/list"
5 | "github.com/codersgyan/expressify/internal/selector"
6 | )
7 |
8 | type Logger string
9 |
10 | const (
11 | Winston Logger = "Winston"
12 | Bunyan Logger = "Bunyan"
13 | Pino Logger = "Pino"
14 | )
15 |
16 | var loggers = []selector.Item{
17 | selector.Item(Winston),
18 | selector.Item(Bunyan),
19 | selector.Item(Pino),
20 | }
21 |
22 | func NewLoggerSelector() *selector.Selector {
23 | var items []list.Item
24 | for _, item := range loggers {
25 | items = append(items, list.Item(item))
26 | }
27 | return selector.NewSelector("\n😎 Choose a logger", items)
28 | }
29 |
--------------------------------------------------------------------------------
/internal/orms/orms.go:
--------------------------------------------------------------------------------
1 | package orms
2 |
3 | import (
4 | "github.com/charmbracelet/bubbles/list"
5 | "github.com/codersgyan/expressify/internal/selector"
6 | )
7 |
8 | type ORM string
9 |
10 | const (
11 | Mongoose ORM = "Mongoose"
12 | Sequelize ORM = "Sequelize"
13 | TypeORM ORM = "TypeORM"
14 | Prisma ORM = "Prisma"
15 | None ORM = "None"
16 | )
17 |
18 | var orms = []selector.Item{
19 | selector.Item(Mongoose),
20 | selector.Item(Sequelize),
21 | selector.Item(TypeORM),
22 | selector.Item(Prisma),
23 | selector.Item(None),
24 | }
25 |
26 | func NewORMSelector() *selector.Selector {
27 | var items []list.Item
28 | for _, item := range orms {
29 | items = append(items, list.Item(item))
30 | }
31 | return selector.NewSelector("\n😎 Choose an ORM", items)
32 | }
33 |
--------------------------------------------------------------------------------
/internal/package_managers/package_managers.go:
--------------------------------------------------------------------------------
1 | package package_managers
2 |
3 | import (
4 | "github.com/charmbracelet/bubbles/list"
5 | "github.com/codersgyan/expressify/internal/selector"
6 | )
7 |
8 | type PackageManager string
9 |
10 | const (
11 | NPM PackageManager = "NPM"
12 | PNPM PackageManager = "PNPM"
13 | )
14 |
15 | var packageManagers = []selector.Item{
16 | selector.Item(NPM),
17 | selector.Item(PNPM),
18 | }
19 |
20 | func NewPManagerSelector() *selector.Selector {
21 | var items []list.Item
22 | for _, item := range packageManagers {
23 | items = append(items, list.Item(item))
24 | }
25 | return selector.NewSelector("\n😎 Choose a package manager", items)
26 | }
27 |
--------------------------------------------------------------------------------
/internal/selector/selector.go:
--------------------------------------------------------------------------------
1 | package selector
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "strings"
7 |
8 | "github.com/charmbracelet/bubbles/list"
9 | tea "github.com/charmbracelet/bubbletea"
10 | "github.com/charmbracelet/lipgloss"
11 | )
12 |
13 | type Item string
14 |
15 | func (i Item) FilterValue() string { return "" }
16 |
17 | type Selector struct {
18 | List list.Model
19 | }
20 |
21 | func NewSelector(title string, items []list.Item) *Selector {
22 | titleStyle := lipgloss.NewStyle().MarginLeft(2)
23 | paginationStyle := list.DefaultStyles().PaginationStyle.PaddingLeft(4)
24 | helpStyle := list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1)
25 |
26 | l := list.New(items, itemDelegate{}, defaultWidth, listHeight)
27 | l.Title = title
28 | l.SetShowStatusBar(false)
29 | l.SetFilteringEnabled(false)
30 | l.Styles.Title = titleStyle
31 | l.Styles.PaginationStyle = paginationStyle
32 | l.Styles.HelpStyle = helpStyle
33 |
34 | return &Selector{
35 | List: l,
36 | }
37 | }
38 |
39 | const listHeight = 14
40 | const defaultWidth = 100
41 |
42 | var (
43 | titleStyle = lipgloss.NewStyle().MarginLeft(2)
44 | itemStyle = lipgloss.NewStyle().PaddingLeft(4)
45 | selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
46 | paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
47 | helpStyle = list.DefaultStyles().HelpStyle.PaddingLeft(4).PaddingBottom(1)
48 | )
49 |
50 | type itemDelegate struct{}
51 |
52 | func (d itemDelegate) Height() int { return 1 }
53 | func (d itemDelegate) Spacing() int { return 0 }
54 | func (d itemDelegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { return nil }
55 | func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {
56 | i, ok := listItem.(Item)
57 | if !ok {
58 | return
59 | }
60 |
61 | str := fmt.Sprintf(" %d. %s", index+1, i)
62 |
63 | fn := itemStyle.Render
64 | if index == m.Index() {
65 | fn = func(s ...string) string {
66 | return selectedItemStyle.Render("👉" + strings.Join(s, " "))
67 | }
68 | }
69 |
70 | fmt.Fprint(w, fn(str))
71 | }
72 |
--------------------------------------------------------------------------------
/internal/structure/structure.go:
--------------------------------------------------------------------------------
1 | package structure
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "os"
7 | "path/filepath"
8 |
9 | "github.com/codersgyan/expressify/internal/languages"
10 | )
11 |
12 | func CreateBaseFileStructure(projectName string, language string) error {
13 | cwd, err := os.Getwd()
14 | fmt.Println("Current Working Directory:", cwd)
15 |
16 | if err != nil {
17 | return fmt.Errorf("unable to get current working directory: %w", err)
18 | }
19 |
20 | projectPath := filepath.Join(cwd, ".expressify", projectName)
21 | if _, err := os.Stat(projectPath); !os.IsNotExist(err) {
22 | return fmt.Errorf("folder \"%s\" already exists or cannot check existence", projectPath)
23 | }
24 | mkdirProjectDirErr := os.Mkdir(projectPath, 0755) // 0755 is commonly used permissions
25 | if mkdirProjectDirErr != nil {
26 | return fmt.Errorf("unable to create project folder: %w", err)
27 | }
28 |
29 | var languageWisePath string
30 | if language == string(languages.JavaScript) {
31 | languageWisePath = "jsbase"
32 | } else {
33 | languageWisePath = "tsbase"
34 | }
35 |
36 | srcPath := filepath.Join(cwd, ".templates", languageWisePath)
37 | dstPath := filepath.Join(projectPath)
38 |
39 | cpErr := CopyDir(srcPath, dstPath)
40 | if cpErr != nil {
41 | fmt.Printf("Error copying directory: %s\n", cpErr)
42 | } else {
43 | fmt.Println("Directory copied successfully.")
44 | }
45 | return nil
46 | }
47 |
48 | func CopyFile(src, dst string) error {
49 | sourceFile, err := os.Open(src)
50 | if err != nil {
51 | return err
52 | }
53 | defer sourceFile.Close()
54 |
55 | destFile, err := os.Create(dst)
56 | if err != nil {
57 | return err
58 | }
59 | defer destFile.Close()
60 |
61 | _, err = io.Copy(destFile, sourceFile)
62 | return err
63 | }
64 |
65 | // copyDir recursively copies a directory from src to dst.
66 | func CopyDir(src, dst string) error {
67 | // Get properties of source dir
68 | info, err := os.Stat(src)
69 | if err != nil {
70 | return err
71 | }
72 |
73 | // Create the destination directory
74 | err = os.MkdirAll(dst, info.Mode())
75 | if err != nil {
76 | return err
77 | }
78 |
79 | // Read directory contents
80 | entries, err := os.ReadDir(src)
81 | if err != nil {
82 | return err
83 | }
84 |
85 | for _, entry := range entries {
86 | srcPath := filepath.Join(src, entry.Name())
87 | dstPath := filepath.Join(dst, entry.Name())
88 |
89 | if entry.IsDir() {
90 | // Recursive call for directories
91 | err = CopyDir(srcPath, dstPath)
92 | if err != nil {
93 | return err
94 | }
95 | } else {
96 | // Copy files
97 | err = CopyFile(srcPath, dstPath)
98 | if err != nil {
99 | return err
100 | }
101 | }
102 | }
103 | return nil
104 | }
105 |
--------------------------------------------------------------------------------
/internal/test_frameworks/test_frameworks.go:
--------------------------------------------------------------------------------
1 | package test_frameworks
2 |
3 | import (
4 | "github.com/charmbracelet/bubbles/list"
5 | "github.com/codersgyan/expressify/internal/selector"
6 | )
7 |
8 | type TestFramwork string
9 |
10 | const (
11 | SuperTestWithJest TestFramwork = "SuperTest with Jest"
12 | MochaWithChaiHTTP TestFramwork = "Mocha with Chai HTTP"
13 | )
14 |
15 | var testFrameworks = []selector.Item{
16 | selector.Item(SuperTestWithJest),
17 | selector.Item(MochaWithChaiHTTP),
18 | }
19 |
20 | func NewTestFrameworkSelector() *selector.Selector {
21 | var items []list.Item
22 | for _, item := range testFrameworks {
23 | items = append(items, list.Item(item))
24 | }
25 | return selector.NewSelector("\n😎 Choose a test framework", items)
26 | }
27 |
--------------------------------------------------------------------------------