Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Svelte init #343

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion .github/workflows/generate-linter-advanced.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
framework: [chi, gin, fiber, gorilla/mux, httprouter, standard-library, echo]
driver: [postgres]
git: [commit]
advanced: [htmx, githubaction, websocket, tailwind, docker, react]
advanced: [htmx, githubaction, websocket, tailwind, docker, react, svelte]

runs-on: ubuntu-latest
steps:
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,9 @@ You can now use the `--advanced` flag when running the `create` command to get a
- [Tailwind](https://tailwindcss.com/) css framework
- Docker configuration for go project
- [React](https://react.dev/) frontend written in TypeScript, including an example fetch request to the backend
- [Svelte](https://svelte.dev/) frontend written in TypeScript, including an example fetch request to the backend

Note: Selecting Tailwind option will automatically select HTMX unless React is explicitly selected
Note: Selecting Tailwind option will automatically select HTMX unless React or Svelte is explicitly selected

<a id="blueprint-ui"></a>

Expand Down Expand Up @@ -191,6 +192,10 @@ React:
go-blueprint create --advanced --feature react
```

```bash
go-blueprint create --advanced --feature svelte
```

Or all features at once:

```bash
Expand Down
15 changes: 8 additions & 7 deletions cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ import (

const logo = `

____ _ _ _
| _ \| | (_) | |
| |_) | |_ _ ___ _ __ _ __ _ _ __ | |_
____ _ _ _
| _ \| | (_) | |
| |_) | |_ _ ___ _ __ _ __ _ _ __ | |_
| _ <| | | | |/ _ \ '_ \| '__| | '_ \| __|
| |_) | | |_| | __/ |_) | | | | | | | |_
| |_) | | |_| | __/ |_) | | | | | | | |_
|____/|_|\__,_|\___| .__/|_| |_|_| |_|\__|
| |
|_|
| |
|_|

`

Expand Down Expand Up @@ -280,7 +280,7 @@ var createCmd = &cobra.Command{
fmt.Println(endingMsgStyle.Render("\nNext steps:"))
fmt.Println(endingMsgStyle.Render(fmt.Sprintf("• cd into the newly created project with: `cd %s`\n", utils.GetRootDir(project.ProjectName))))

if options.Advanced.Choices["React"] {
if options.Advanced.Choices["React"] || options.Advanced.Choices["Svelte"] {
options.Advanced.Choices["Htmx"] = false
options.Advanced.Choices["Tailwind"] = false
fmt.Println(endingMsgStyle.Render("• cd into frontend\n"))
Expand All @@ -296,6 +296,7 @@ var createCmd = &cobra.Command{

if options.Advanced.Choices["Htmx"] {
options.Advanced.Choices["react"] = false
options.Advanced.Choices["svelte"] = false
fmt.Println(endingMsgStyle.Render("• Install the templ cli if you haven't already by running `go install github.com/a-h/templ/cmd/templ@latest`\n"))
fmt.Println(endingMsgStyle.Render("• Generate templ function files by running `templ generate`\n"))
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/flags/advancedFeatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ const (
Websocket string = "websocket"
Tailwind string = "tailwind"
React string = "react"
Svelte string = "svelte"
Docker string = "docker"
)

var AllowedAdvancedFeatures = []string{string(React), string(Htmx), string(GoProjectWorkflow), string(Websocket), string(Tailwind), string(Docker)}
var AllowedAdvancedFeatures = []string{string(React), string(Svelte), string(Htmx), string(GoProjectWorkflow), string(Websocket), string(Tailwind), string(Docker)}

func (f AdvancedFeatures) String() string {
return strings.Join(f, ",")
Expand Down
100 changes: 72 additions & 28 deletions cmd/program/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,11 +406,12 @@ func (p *Project) CreateMainFile() error {
return err
}

if p.AdvancedOptions[string(flags.React)] {
// deselect htmx option automatically since react is selected
if p.AdvancedOptions[string(flags.React)] || p.AdvancedOptions[string(flags.Svelte)] {
// deselect htmx option automatically since react or svelte is selected
p.AdvancedOptions[string(flags.Htmx)] = false
if err := p.CreateViteReactProject(projectPath); err != nil {
return fmt.Errorf("failed to set up React project: %w", err)
if err := p.CreateViteProject(projectPath); err != nil {
// if there was an error creating the vite project, return the error
return fmt.Errorf("failed to set up Vite project: %w", err)
}

// if everything went smoothly, remove tailwing flag option
Expand Down Expand Up @@ -802,7 +803,7 @@ func (p *Project) CreateFileWithInjection(pathToCreate string, projectPath strin
return nil
}

func (p *Project) CreateViteReactProject(projectPath string) error {
func (p *Project) CreateViteProject(projectPath string) error {
if err := checkNpmInstalled(); err != nil {
return err
}
Expand All @@ -825,7 +826,19 @@ func (p *Project) CreateViteReactProject(projectPath string) error {

// the interactive vite command will not work as we can't interact with it
fmt.Println("Installing create-vite...")
cmd := exec.Command("npm", "create", "vite@latest", "frontend", "--", "--template", "react-ts")

// for any future changes, we can use the advanced options to determine the template
var templateOption string

if p.AdvancedOptions[string(flags.React)] {
templateOption = "react-ts"
} else if p.AdvancedOptions[string(flags.Svelte)] {
templateOption = "svelte-ts"
} else {
return fmt.Errorf("unsupported template option")
}

cmd := exec.Command("npm", "create", "vite@latest", "frontend", "--", "--template", templateOption)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
Expand All @@ -846,13 +859,15 @@ func (p *Project) CreateViteReactProject(projectPath string) error {
return fmt.Errorf("failed to create src directory: %w", err)
}

if err := os.WriteFile(filepath.Join(srcDir, "App.tsx"), advanced.ReactAppfile(), 0644); err != nil {
return fmt.Errorf("failed to write App.tsx template: %w", err)
}

// Handle Tailwind configuration if selected
if p.AdvancedOptions[string(flags.Tailwind)] {
fmt.Println("Tailwind selected. Configuring with React...")

if p.AdvancedOptions[string(flags.React)] {
fmt.Println("Tailwind selected. Configuring with React...")
} else if p.AdvancedOptions[string(flags.Svelte)] {
fmt.Println("Tailwind selected. Configuring with Svelte...")
}

cmd := exec.Command("npm", "install", "-D", "tailwindcss", "postcss", "autoprefixer")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
Expand All @@ -866,35 +881,64 @@ func (p *Project) CreateViteReactProject(projectPath string) error {
return fmt.Errorf("failed to initialize Tailwind: %w", err)
}

// use the tailwind config file
err = os.WriteFile("tailwind.config.js", advanced.ReactTailwindConfigTemplate(), 0644)
if err != nil {
return fmt.Errorf("failed to write tailwind config: %w", err)
}

srcDir := filepath.Join(frontendPath, "src")
if err := os.MkdirAll(srcDir, 0755); err != nil {
return fmt.Errorf("failed to create src directory: %w", err)
}

err = os.WriteFile(filepath.Join(srcDir, "index.css"), advanced.InputCssTemplateReact(), 0644)
if err != nil {
return fmt.Errorf("failed to update index.css: %w", err)
}
if p.AdvancedOptions[string(flags.React)] {
// use the tailwind config file for React
err = os.WriteFile("tailwind.config.js", advanced.ReactTailwindConfigTemplate(), 0644)
if err != nil {
return fmt.Errorf("failed to write tailwind config: %w", err)
}
// update index.css for React
err = os.WriteFile(filepath.Join(srcDir, "index.css"), advanced.InputCssTemplateReact(), 0644)
if err != nil {
return fmt.Errorf("failed to update index.css: %w", err)
}

if err := os.WriteFile(filepath.Join(srcDir, "App.tsx"), advanced.ReactTailwindAppfile(), 0644); err != nil {
return fmt.Errorf("failed to write App.tsx template: %w", err)
}
if err := os.WriteFile(filepath.Join(srcDir, "App.tsx"), advanced.ReactTailwindAppfile(), 0644); err != nil {
return fmt.Errorf("failed to write App.tsx template: %w", err)
}

if err := os.Remove(filepath.Join(srcDir, "App.css")); err != nil {
// Don't return error if file doesn't exist
if !os.IsNotExist(err) {
return fmt.Errorf("failed to remove App.css: %w", err)
// this only exists in the React template
if err := os.Remove(filepath.Join(srcDir, "App.css")); err != nil {
// Don't return error if file doesn't exist
if !os.IsNotExist(err) {
return fmt.Errorf("failed to remove App.css: %w", err)
}
}
} else if p.AdvancedOptions[string(flags.Svelte)] {
// use the tailwind config file for svelte
err = os.WriteFile("tailwind.config.js", advanced.SvelteTailwindConfigTemplate(), 0644)
if err != nil {
return fmt.Errorf("failed to write tailwind config: %w", err)
}
// update app.css for Svelte
err = os.WriteFile(filepath.Join(srcDir, "app.css"), advanced.InputCssTemplateSvelte(), 0644)
if err != nil {
return fmt.Errorf("failed to update app.css: %w", err)
}

if err := os.WriteFile(filepath.Join(srcDir, "App.svelte"), advanced.SvelteTailwindAppfile(), 0644); err != nil {
return fmt.Errorf("failed to write App.svelte template: %w", err)
}
}

// set to false to not re-do in next step
p.AdvancedOptions[string(flags.Tailwind)] = false
} else {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • moved the copying of template file in else block of tailwind as currently the app file was written once and then again it was overwritten if the tailwind one needed to be copied so now it is only written once.

// if tailwind is not selected, use the default App file with server call example
if p.AdvancedOptions[string(flags.React)] {
if err := os.WriteFile(filepath.Join(srcDir, "App.tsx"), advanced.ReactAppfile(), 0644); err != nil {
return fmt.Errorf("failed to write App.tsx template: %w", err)
}
} else if p.AdvancedOptions[string(flags.Svelte)] {
if err := os.WriteFile(filepath.Join(srcDir, "App.svelte"), advanced.SvelteAppfile(), 0644); err != nil {
return fmt.Errorf("failed to write App.svelte template: %w", err)
}
}
}

return nil
Expand Down
9 changes: 7 additions & 2 deletions cmd/steps/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,15 @@ func InitSteps(projectType flags.Framework, databaseType flags.Database) *Steps
Title: "React",
Desc: "Use Vite to spin up a React project in TypeScript. This disables selecting HTMX/Templ",
},
{
Flag: "Svelte",
Title: "Svelte",
Desc: "Use Vite to spin up a Svelte project in TypeScript. This disables selecting HTMX/Templ",
},
{
Flag: "Htmx",
Title: "HTMX/Templ",
Desc: "Add starter HTMX and Templ files. This disables selecting React",
Desc: "Add starter HTMX and Templ files. This disables selecting React or Svelte",
},
{
Flag: "GitHubAction",
Expand All @@ -119,7 +124,7 @@ func InitSteps(projectType flags.Framework, databaseType flags.Database) *Steps
{
Flag: "Tailwind",
Title: "TailwindCSS",
Desc: "A utility-first CSS framework (selecting this will automatically add HTMX unless React is specified)",
Desc: "A utility-first CSS framework (selecting this will automatically add HTMX unless React or Svelte is specified)",
},
{
Flag: "Docker",
Expand Down
1 change: 0 additions & 1 deletion cmd/template/advanced/files/react/tailwind/app.tsx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ function App() {
>
Count is {count}
</button>

<button
onClick={fetchData}
className="block w-full bg-green-500 hover:bg-green-600 text-white font-semibold py-2 px-4 rounded-md transition-colors"
Expand Down
73 changes: 73 additions & 0 deletions cmd/template/advanced/files/svelte/App.svelte.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script lang="ts">
import svelteLogo from './assets/svelte.svg'
import viteLogo from '/vite.svg'
import Counter from './lib/Counter.svelte'
let message: string = $state('');

async function fetchData() {
try {
const response = await fetch('http://localhost:8080/');
message = await response.text();
} catch (error) {
console.error('Error fetching data:', error);
}
}
</script>

<main>
<div>
<a href="https://vite.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} class="logo" alt="Vite Logo" />
</a>
<a href="https://svelte.dev" target="_blank" rel="noreferrer">
<img src={svelteLogo} class="logo svelte" alt="Svelte Logo" />
</a>
</div>
<h1>Vite + Svelte</h1>

<p class="read-the-docs">
Click on the Vite and Svelte logos to learn more
</p>

<div class="card">
<Counter />
<p>
Edit <code>src/App.svelte</code> and save to test HMR
</p>
</div>

<button class="btn-fetch" onclick={fetchData}>
Click to fetch from Go server
</button>
{#if message}
<div>
<h2>Server Response:</h2>
<p>{message}</p>
</div>
{/if}

<p>
Check out <a href="https://svelte.dev/docs/svelte/overview" target="_blank" rel="noreferrer">Svelte</a>, the official Svelte app powered by Vite!
</p>
</main>

<style>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.svelte:hover {
filter: drop-shadow(0 0 2em #ff3e00aa);
}
.read-the-docs {
color: #888;
}
.btn-fetch {
background-color: #36b962;
}
</style>
Loading
Loading