Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
module github.com/anthdm/slick

go 1.21.0
go 1.21

require (
github.com/a-h/templ v0.2.476
github.com/julienschmidt/httprouter v1.3.0
github.com/joho/godotenv v1.5.1
github.com/spf13/cobra v1.8.0
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
)

require github.com/joho/godotenv v1.5.1
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
github.com/a-h/templ v0.2.476 h1:+H4hP4CwK4kfJwXsE6kHeFWMGtcVOVoOm/I64uzARBk=
github.com/a-h/templ v0.2.476/go.mod h1:zQ95mSyadNTGHv6k5Fm+wQU8zkBMMbHCHg7eAvUZKNM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
35 changes: 35 additions & 0 deletions slick/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"github.com/spf13/cobra"
)

type commandFunc func() *cobra.Command

type cli struct {
cmds []commandFunc
}

func NewCommand() *cli {
return &cli{
cmds: make([]commandFunc, 0),
}
}

func (c *cli) Register(cmds ...commandFunc) {
c.cmds = append(c.cmds, cmds...)
}

func (c *cli) Execute() {
rootCmd := &cobra.Command{
Use: "slick",
Short: "Slick CLI",
Long: "Slick web framework",
}

for _, command := range c.cmds {
rootCmd.AddCommand(command())
}

rootCmd.Execute()
}
248 changes: 160 additions & 88 deletions slick/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,111 +6,182 @@ import (
"os/exec"
"strings"
"time"
)

func usage() {
fmt.Println("help....")
os.Exit(0)
}
"github.com/spf13/cobra"
)

func main() {
args := os.Args
if len(args) < 2 {
usage()
}
cmd := os.Args[1]

switch cmd {
case "run":
if _, err := os.Stat("cmd/main.go"); err != nil {
fmt.Println("not in slick app root: cmd/main.go not found")
os.Exit(1)
}
if err := exec.Command("templ", "generate").Run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
exec.Command("go", "run", "cmd/main.go").Run()
case "install":
if err := installProject(); err != nil {
fmt.Println(err)
}
case "new":
if len(os.Args) != 3 {
usage()
}
name := os.Args[2]
if err := generateProject(name); err != nil {
fmt.Println(err)
}
}
type File struct {
Path string
Content []byte
}

func generateProject(name string) error {
fmt.Println("creating new slick project:", name)
if err := os.Mkdir(name, os.ModePerm); err != nil {
func writeFileWithCheck(file File) error {
if err := os.WriteFile(file.Path, file.Content, os.ModePerm); err != nil {
return err
}

folders := []string{"model", "handler", "view", "cmd", "public"}
return nil
}

for _, folder := range folders {
if err := os.Mkdir(name+"/"+folder, os.ModePerm); err != nil {
return err
}
}
func main() {
cmd := NewCommand()
cmd.Register(
runProject,
installProject,
generateProject,
generateModel,
generateView,
generateHandler,
)

cmd.Execute()
}

if err := os.WriteFile(name+"/go.mod", writeGoModContents(name), os.ModePerm); err != nil {
return err
}
if err := os.WriteFile(name+"/.air.toml", writeAirTomlContents(), os.ModePerm); err != nil {
return err
}
if err := os.WriteFile(name+"/.env", writeEnvFileContents(), os.ModePerm); err != nil {
return err
}
if err := os.WriteFile(name+"/.gitignore", writeGitignore(), os.ModePerm); err != nil {
return err
}
if err := os.WriteFile(name+"/public/app.css", []byte(""), os.ModePerm); err != nil {
return err
}
if err := os.WriteFile(name+"/cmd/main.go", writeMainContents(name), os.ModePerm); err != nil {
return err
}
if err := os.WriteFile(name+"/handler/hello.go", writeHandlerContent(name), os.ModePerm); err != nil {
return err
}
if err := os.Mkdir(name+"/view/hello", os.ModePerm); err != nil {
return err
}
if err := os.Mkdir(name+"/view/layout", os.ModePerm); err != nil {
return err
}
if err := os.WriteFile(name+"/view/layout/base.templ", writeBaseLayoutContent(), os.ModePerm); err != nil {
return err
func runProject() *cobra.Command {
return &cobra.Command{
Use: "run",
Example: "slick run",
Short: "Run slick development server",
Run: func(cmd *cobra.Command, args []string) {
if _, err := os.Stat("cmd/main.go"); err != nil {
fmt.Println("not in slick app root: cmd/main.go not found")
return
}
if err := exec.Command("templ", "generate").Run(); err != nil {
fmt.Println(err)
return
}

if err := exec.Command("go", "run", "cmd/main.go").Run(); err != nil {
fmt.Println(err)
}
},
}
if err := os.WriteFile(name+"/view/hello/hello.templ", writeViewContent(name), os.ModePerm); err != nil {
return err
}

func installProject() *cobra.Command {
return &cobra.Command{
Use: "install",
Aliases: []string{"i"},
Example: "slick install",
Short: "Install project's dependency",
Run: func(cmd *cobra.Command, args []string) {
start := time.Now()
fmt.Println("installing project...")
if err := exec.Command("go", "get", "github.com/anthdm/slick@latest").Run(); err != nil {
fmt.Println(err)
return
}

if err := exec.Command("go", "get", "github.com/a-h/templ").Run(); err != nil {
fmt.Println(err)
return
}
if err := exec.Command("templ", "generate").Run(); err != nil {
fmt.Println(err)
return
}

fmt.Printf("done installing project in %v\n", time.Since(start))
},
}
}

return nil
func generateProject() *cobra.Command {
return &cobra.Command{
Use: "new",
Example: "slick new hello-world",
Short: "Create new slick project",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
fmt.Println("invalid arguments")
return
}

name := args[0]

fmt.Println("creating new slick project:", name)
if err := os.Mkdir(name, os.ModePerm); err != nil {
fmt.Println(err)
return
}

files := []File{
// setup directory
{Path: name + "/model", Content: nil},
{Path: name + "/handler", Content: nil},
{Path: name + "/view", Content: nil},
{Path: name + "/cmd", Content: nil},
{Path: name + "/public", Content: nil},
{Path: name + "/view/hello", Content: nil},
{Path: name + "/view/layout", Content: nil},

// setup files
{Path: name + "/go.mod", Content: writeGoModContents(name)},
{Path: name + "/.air.toml", Content: writeAirTomlContents()},
{Path: name + "/.env", Content: writeEnvFileContents()},
{Path: name + "/.gitignore", Content: writeGitignore()},
{Path: name + "/public/app.css", Content: []byte("")},
{Path: name + "/cmd/main.go", Content: writeMainContents(name)},
{Path: name + "/handler/hello.go", Content: writeHandlerContent(name)},
{Path: name + "/view/layout/base.templ", Content: writeBaseLayoutContent()},
{Path: name + "/view/hello/hello.templ", Content: writeViewContent(name)},
}

errors := []error{}
for _, file := range files {
if file.Content == nil {
if err := os.Mkdir(file.Path, os.ModePerm); err != nil {
errors = append(errors, err)
}
} else {
if err := writeFileWithCheck(file); err != nil {
errors = append(errors, err)
}
}
}

if len(errors) != 0 {
fmt.Println("slick encountered errors during file initialization:", errors)
return
}
},
}
}

func installProject() error {
start := time.Now()
fmt.Println("installing project...")
if err := exec.Command("go", "get", "github.com/anthdm/slick@latest").Run(); err != nil {
return err
func generateModel() *cobra.Command {
return &cobra.Command{
Use: "model",
Example: "slick model user",
Short: "Generate new model",
Run: func(cmd *cobra.Command, args []string) {
// TODO:
},
}
if err := exec.Command("go", "get", "github.com/a-h/templ").Run(); err != nil {
return err
}

func generateView() *cobra.Command {
return &cobra.Command{
Use: "view",
Example: "slick view user",
Short: "Generate new view",
Run: func(cmd *cobra.Command, args []string) {
// TODO:

},
}
if err := exec.Command("templ", "generate").Run(); err != nil {
return err
}

func generateHandler() *cobra.Command {
return &cobra.Command{
Use: "handler",
Example: "slick handler home",
Short: "Generate new handler",
Run: func(cmd *cobra.Command, args []string) {
// TODO:

},
}
fmt.Printf("done installing project in %v\n", time.Since(start))
return nil
}

func writeEnvFileContents() []byte {
Expand All @@ -130,6 +201,7 @@ func writeMainContents(mod string) []byte {
package main

import (
"log"
"github.com/anthdm/slick"
"%s/handler"
)
Expand Down