From f402c46b9763f88d5876e2a5b57fe88761dff2a4 Mon Sep 17 00:00:00 2001 From: arafays Date: Wed, 29 Jan 2025 05:18:27 +0500 Subject: [PATCH 01/34] WIP: tailwindcss v4 --- cmd/program/program.go | 20 +------------------ .../advanced/files/docker/dockerfile.tmpl | 2 +- .../files/react/tailwind/index.css.tmpl | 4 +--- .../react/tailwind/tailwind.config.js.tmpl | 11 ---------- .../advanced/files/tailwind/input.css.tmpl | 4 +--- .../files/tailwind/tailwind.config.js.tmpl | 11 ---------- cmd/template/advanced/routes.go | 14 ------------- cmd/template/framework/files/makefile.tmpl | 6 +++--- 8 files changed, 7 insertions(+), 65 deletions(-) delete mode 100644 cmd/template/advanced/files/react/tailwind/tailwind.config.js.tmpl delete mode 100644 cmd/template/advanced/files/tailwind/tailwind.config.js.tmpl diff --git a/cmd/program/program.go b/cmd/program/program.go index b5a8465bf..ef71b5b5d 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -421,18 +421,6 @@ func (p *Project) CreateMainFile() error { // select htmx option automatically since tailwind is selected p.AdvancedOptions[string(flags.Htmx)] = true - tailwindConfigFile, err := os.Create(fmt.Sprintf("%s/tailwind.config.js", projectPath)) - if err != nil { - return err - } - defer tailwindConfigFile.Close() - - tailwindConfigTemplate := advanced.TailwindConfigTemplate() - err = os.WriteFile(fmt.Sprintf("%s/tailwind.config.js", projectPath), tailwindConfigTemplate, 0o644) - if err != nil { - return err - } - err = os.MkdirAll(fmt.Sprintf("%s/%s/assets/css", projectPath, cmdWebPath), 0o755) if err != nil { return err @@ -891,7 +879,7 @@ func (p *Project) CreateViteReactProject(projectPath string) error { cmd := exec.Command("npm", "install", "--prefer-offline", "--no-fund", - "tailwindcss", "postcss", "autoprefixer") + "tailwindcss") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { @@ -906,12 +894,6 @@ 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) diff --git a/cmd/template/advanced/files/docker/dockerfile.tmpl b/cmd/template/advanced/files/docker/dockerfile.tmpl index c2042ed6b..da6fd9fe1 100644 --- a/cmd/template/advanced/files/docker/dockerfile.tmpl +++ b/cmd/template/advanced/files/docker/dockerfile.tmpl @@ -16,7 +16,7 @@ RUN go install github.com/a-h/templ/cmd/templ@latest && \ {{- end}} {{- if .AdvancedOptions.tailwind}} - curl -sL https://github.com/tailwindlabs/tailwindcss/releases/download/v3.4.10/tailwindcss-linux-x64 -o tailwindcss && \ + curl -sL https://github.com/tailwindlabs/tailwindcss/releases/download/latest/tailwindcss-linux-x64 -o tailwindcss && \ chmod +x tailwindcss && \ ./tailwindcss -i cmd/web/styles/input.css -o cmd/web/assets/css/output.css {{- end }} diff --git a/cmd/template/advanced/files/react/tailwind/index.css.tmpl b/cmd/template/advanced/files/react/tailwind/index.css.tmpl index b5c61c956..a461c505f 100644 --- a/cmd/template/advanced/files/react/tailwind/index.css.tmpl +++ b/cmd/template/advanced/files/react/tailwind/index.css.tmpl @@ -1,3 +1 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss"; \ No newline at end of file diff --git a/cmd/template/advanced/files/react/tailwind/tailwind.config.js.tmpl b/cmd/template/advanced/files/react/tailwind/tailwind.config.js.tmpl deleted file mode 100644 index 89a305e02..000000000 --- a/cmd/template/advanced/files/react/tailwind/tailwind.config.js.tmpl +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - content: [ - "./index.html", - "./src/**/*.{js,ts,jsx,tsx}", - ], - theme: { - extend: {}, - }, - plugins: [], -} \ No newline at end of file diff --git a/cmd/template/advanced/files/tailwind/input.css.tmpl b/cmd/template/advanced/files/tailwind/input.css.tmpl index b5c61c956..73a943cd0 100644 --- a/cmd/template/advanced/files/tailwind/input.css.tmpl +++ b/cmd/template/advanced/files/tailwind/input.css.tmpl @@ -1,3 +1 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss" diff --git a/cmd/template/advanced/files/tailwind/tailwind.config.js.tmpl b/cmd/template/advanced/files/tailwind/tailwind.config.js.tmpl deleted file mode 100644 index 4ec345194..000000000 --- a/cmd/template/advanced/files/tailwind/tailwind.config.js.tmpl +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - "./cmd/web/**/*.html", "./cmd/web/**/*.templ", - ], - theme: { - extend: {}, - }, - plugins: [], -} - diff --git a/cmd/template/advanced/routes.go b/cmd/template/advanced/routes.go index 3ed424f66..041eb5fcb 100644 --- a/cmd/template/advanced/routes.go +++ b/cmd/template/advanced/routes.go @@ -10,12 +10,6 @@ var helloTemplTemplate []byte //go:embed files/htmx/base.templ.tmpl var baseTemplTemplate []byte -//go:embed files/tailwind/tailwind.config.js.tmpl -var tailwindConfigTemplate []byte - -//go:embed files/react/tailwind/tailwind.config.js.tmpl -var reactTailwindConfigTemplate []byte - //go:embed files/react/tailwind/index.css.tmpl var inputCssTemplateReact []byte @@ -119,14 +113,6 @@ func BaseTemplTemplate() []byte { return baseTemplTemplate } -func TailwindConfigTemplate() []byte { - return tailwindConfigTemplate -} - -func ReactTailwindConfigTemplate() []byte { - return reactTailwindConfigTemplate -} - func ReactTailwindAppfile() []byte { return reactTailwindAppFile } diff --git a/cmd/template/framework/files/makefile.tmpl b/cmd/template/framework/files/makefile.tmpl index 27386e870..7e10b2548 100644 --- a/cmd/template/framework/files/makefile.tmpl +++ b/cmd/template/framework/files/makefile.tmpl @@ -39,12 +39,12 @@ templ-install: {{- if and .AdvancedOptions.tailwind (not .AdvancedOptions.react) }} {{- if .OSCheck.UnixBased}} tailwind-install: - {{ if .OSCheck.linux }}@if [ ! -f tailwindcss ]; then curl -sL https://github.com/tailwindlabs/tailwindcss/releases/download/v3.4.10/tailwindcss-linux-x64 -o tailwindcss; fi{{- end }} - {{ if .OSCheck.darwin }}@if [ ! -f tailwindcss ]; then curl -sL https://github.com/tailwindlabs/tailwindcss/releases/download/v3.4.10/tailwindcss-macos-x64 -o tailwindcss; fi{{- end }} + {{ if .OSCheck.linux }}@if [ ! -f tailwindcss ]; then curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 -o tailwindcss; fi{{- end }} + {{ if .OSCheck.darwin }}@if [ ! -f tailwindcss ]; then curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-macos-x64 -o tailwindcss; fi{{- end }} @chmod +x tailwindcss {{- else }} tailwind-install: - @if not exist tailwindcss.exe powershell -ExecutionPolicy Bypass -Command "Invoke-WebRequest -Uri 'https://github.com/tailwindlabs/tailwindcss/releases/download/v3.4.10/tailwindcss-windows-x64.exe' -OutFile 'tailwindcss.exe'"{{- end }} + @if not exist tailwindcss.exe powershell -ExecutionPolicy Bypass -Command "Invoke-WebRequest -Uri 'https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-windows-x64.exe' -OutFile 'tailwindcss.exe'"{{- end }} {{- end }} build:{{- if and .AdvancedOptions.tailwind (not .AdvancedOptions.react) }} tailwind-install{{- end }}{{- if and (or .AdvancedOptions.htmx .AdvancedOptions.tailwind) (not .AdvancedOptions.react) }} templ-install{{- end }} From 5aec066d29f48c47d10546415233d6616f3253c7 Mon Sep 17 00:00:00 2001 From: arafays Date: Wed, 29 Jan 2025 05:56:42 +0500 Subject: [PATCH 02/34] WIP: react + tailwindcss done --- cmd/program/program.go | 11 ++++------- .../advanced/files/react/tailwind/vite.config.ts.tmpl | 8 ++++++++ cmd/template/advanced/routes.go | 7 +++++++ 3 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 cmd/template/advanced/files/react/tailwind/vite.config.ts.tmpl diff --git a/cmd/program/program.go b/cmd/program/program.go index ef71b5b5d..a5473a3aa 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -879,19 +879,16 @@ func (p *Project) CreateViteReactProject(projectPath string) error { cmd := exec.Command("npm", "install", "--prefer-offline", "--no-fund", - "tailwindcss") + "tailwindcss", "@tailwindcss/vite") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return fmt.Errorf("failed to install Tailwind: %w", err) } - fmt.Println("Initializing Tailwind...") - cmd = exec.Command("npx", "--prefer-offline", "tailwindcss", "init", "-p") - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return fmt.Errorf("failed to initialize Tailwind: %w", err) + // Create the vite + react + Tailwind v4 configuration + if err := os.WriteFile(filepath.Join(frontendPath, "vite.config.ts"), advanced.ViteTailwindConfigFile(), 0644); err != nil { + return fmt.Errorf("failed to write vite.config.ts: %w", err) } srcDir := filepath.Join(frontendPath, "src") diff --git a/cmd/template/advanced/files/react/tailwind/vite.config.ts.tmpl b/cmd/template/advanced/files/react/tailwind/vite.config.ts.tmpl new file mode 100644 index 000000000..6370c075b --- /dev/null +++ b/cmd/template/advanced/files/react/tailwind/vite.config.ts.tmpl @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(),tailwindcss()], +}) \ No newline at end of file diff --git a/cmd/template/advanced/routes.go b/cmd/template/advanced/routes.go index 041eb5fcb..e7860f58f 100644 --- a/cmd/template/advanced/routes.go +++ b/cmd/template/advanced/routes.go @@ -13,6 +13,9 @@ var baseTemplTemplate []byte //go:embed files/react/tailwind/index.css.tmpl var inputCssTemplateReact []byte +//go:embed files/react/tailwind/vite.config.ts.tmpl +var viteTailwindConfigFile []byte + //go:embed files/react/tailwind/app.tsx.tmpl var reactTailwindAppFile []byte @@ -125,6 +128,10 @@ func InputCssTemplateReact() []byte { return inputCssTemplateReact } +func ViteTailwindConfigFile() []byte { + return viteTailwindConfigFile +} + func InputCssTemplate() []byte { return inputCssTemplate } From a9997b5811450919fb24ad155244bf2a662aa159 Mon Sep 17 00:00:00 2001 From: arafays Date: Wed, 29 Jan 2025 21:27:48 +0500 Subject: [PATCH 03/34] fix: correct path --- cmd/template/advanced/files/docker/dockerfile.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/template/advanced/files/docker/dockerfile.tmpl b/cmd/template/advanced/files/docker/dockerfile.tmpl index da6fd9fe1..961b5a56d 100644 --- a/cmd/template/advanced/files/docker/dockerfile.tmpl +++ b/cmd/template/advanced/files/docker/dockerfile.tmpl @@ -16,7 +16,7 @@ RUN go install github.com/a-h/templ/cmd/templ@latest && \ {{- end}} {{- if .AdvancedOptions.tailwind}} - curl -sL https://github.com/tailwindlabs/tailwindcss/releases/download/latest/tailwindcss-linux-x64 -o tailwindcss && \ + curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 -o tailwindcss && \ chmod +x tailwindcss && \ ./tailwindcss -i cmd/web/styles/input.css -o cmd/web/assets/css/output.css {{- end }} From a1f55e4724df590440ee4b46fceaba759c13b2de Mon Sep 17 00:00:00 2001 From: arafays Date: Wed, 29 Jan 2025 21:27:56 +0500 Subject: [PATCH 04/34] update docs --- docs/docs/advanced-flag/tailwind.md | 7 +++---- docs/docs/index.md | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/docs/advanced-flag/tailwind.md b/docs/docs/advanced-flag/tailwind.md index 0d2e4b666..4b869b090 100644 --- a/docs/docs/advanced-flag/tailwind.md +++ b/docs/docs/advanced-flag/tailwind.md @@ -3,6 +3,7 @@ Tailwind is closely coupled with the advanced HTMX flag, and HTMX will be automa We do not introduce outside dependencies automatically, and you need compile output.css (file is empty by default) with the Tailwind CLI tool. The project tree would look like this: + ```bash / (Root) ├── cmd/ @@ -30,8 +31,7 @@ The project tree would look like this: ├── go.mod ├── go.sum ├── Makefile -├── README.md -└── tailwind.config.js +└── README.md ``` ## Standalone Tailwind CLI @@ -41,6 +41,7 @@ The The idea is to avoid using Node.js and npm to build output.css. The Makefile will have entries for downloading and compiling CSS. It will automatically detect the OS and download the latest release from the [official repository](https://github.com/tailwindlabs/tailwindcss/releases). ## Linux Makefile Example + ```bash all: build templ-install: @@ -75,5 +76,3 @@ By default, simple CSS examples are included in the codebase. Update base.templ and hello.templ, then rerun templ generate to see the changes at the `localhost:PORT/web` endpoint. ![Tailwind](../public/tailwind.png) - - diff --git a/docs/docs/index.md b/docs/docs/index.md index 39e0fd00e..3219be683 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -2,11 +2,11 @@ hide: - toc --- + ## Go Blueprint - Ultimate Golang Blueprint Library ![logo](./public/logo.png) - Powerful CLI tool designed to streamline the process of creating Go projects with a robust and standardized structure. Not only does Go Blueprint facilitate project initialization, but it also offers seamless integration with popular Go frameworks, allowing you to focus on your application's code from the very beginning. ## Why Choose Go Blueprint? @@ -87,7 +87,6 @@ Here's an overview of the project structure created by Go Blueprint when all opt ├── .goreleaser.yml # Configuration file for GoReleaser, a tool for building and releasing binaries. ├── go.sum # Go module file containing checksums for dependencies. ├── Makefile # Makefile for defining and running commands. -├── tailwind.config.js # Tailwind CSS configuration file for HTMX. └── README.md # Project's README file containing essential information about the project. ``` From 829a14d9f0a6b06c593caf54c0726864610783b2 Mon Sep 17 00:00:00 2001 From: Jon Calhoun Date: Thu, 27 Feb 2025 11:53:51 -0500 Subject: [PATCH 05/34] Fixed a bug where the interupt signal was still being captured and not allowing for a forced shutdown during graceful shutdown. --- cmd/template/framework/files/main/fiber_main.go.tmpl | 1 + cmd/template/framework/files/main/main.go.tmpl | 1 + 2 files changed, 2 insertions(+) diff --git a/cmd/template/framework/files/main/fiber_main.go.tmpl b/cmd/template/framework/files/main/fiber_main.go.tmpl index a86d876f4..52d253e49 100644 --- a/cmd/template/framework/files/main/fiber_main.go.tmpl +++ b/cmd/template/framework/files/main/fiber_main.go.tmpl @@ -23,6 +23,7 @@ func gracefulShutdown(fiberServer *server.FiberServer, done chan bool) { <-ctx.Done() log.Println("shutting down gracefully, press Ctrl+C again to force") + stop() // Allow Ctrl+C to force shutdown // The context is used to inform the server it has 5 seconds to finish // the request it is currently handling diff --git a/cmd/template/framework/files/main/main.go.tmpl b/cmd/template/framework/files/main/main.go.tmpl index ddcb061e4..dbe97e31f 100644 --- a/cmd/template/framework/files/main/main.go.tmpl +++ b/cmd/template/framework/files/main/main.go.tmpl @@ -21,6 +21,7 @@ func gracefulShutdown(apiServer *http.Server, done chan bool) { <-ctx.Done() log.Println("shutting down gracefully, press Ctrl+C again to force") + stop() // Allow Ctrl+C to force shutdown // The context is used to inform the server it has 5 seconds to finish // the request it is currently handling From 1651f2cac41714f474ba331c1ee514c630073459 Mon Sep 17 00:00:00 2001 From: DLTADragonHawk Date: Tue, 4 Mar 2025 20:01:30 -0600 Subject: [PATCH 06/34] Fix Melkeydev#115 Pull request missing pq driver replacement --- cmd/program/program.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/program/program.go b/cmd/program/program.go index a5473a3aa..a375d38a7 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -100,7 +100,7 @@ var ( echoPackage = []string{"github.com/labstack/echo/v4", "github.com/labstack/echo/v4/middleware"} mysqlDriver = []string{"github.com/go-sql-driver/mysql"} - postgresDriver = []string{"github.com/lib/pq"} + postgresDriver = []string{"github.com/jackc/pgx/v5/stdlib"} sqliteDriver = []string{"github.com/mattn/go-sqlite3"} redisDriver = []string{"github.com/redis/go-redis/v9"} mongoDriver = []string{"go.mongodb.org/mongo-driver"} From e478069b7bd506e409791a07b57fc44f7074047f Mon Sep 17 00:00:00 2001 From: agungfir98 Date: Thu, 13 Mar 2025 13:40:02 +0700 Subject: [PATCH 07/34] more transparent error message when executing command --- cmd/create.go | 2 +- cmd/program/program.go | 10 +++++----- cmd/utils/utils.go | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 8a46bc095..908e045ba 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -273,7 +273,7 @@ var createCmd = &cobra.Command{ if releaseErr := spinner.ReleaseTerminal(); releaseErr != nil { log.Printf("Problem releasing terminal: %v", releaseErr) } - log.Printf("Problem creating files for project. %v", err) + log.Printf("Problem creating files for project.") cobra.CheckErr(textinput.CreateErrorInputModel(err).Err()) } diff --git a/cmd/program/program.go b/cmd/program/program.go index a375d38a7..5537017d8 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -293,7 +293,7 @@ func (p *Project) CreateMainFile() error { if p.ProjectType != flags.StandardLibrary { err = utils.GoGetPackage(projectPath, p.FrameworkMap[p.ProjectType].packageName) if err != nil { - log.Printf("Could not install go dependency for the chosen framework %v\n", err) + log.Println("Could not install go dependency for the chosen framework") return err } } @@ -303,7 +303,7 @@ func (p *Project) CreateMainFile() error { p.createDBDriverMap() err = utils.GoGetPackage(projectPath, p.DBDriverMap[p.DBDriver].packageName) if err != nil { - log.Printf("Could not install go dependency for chosen driver %v\n", err) + log.Println("Could not install go dependency for chosen driver") return err } @@ -348,7 +348,7 @@ func (p *Project) CreateMainFile() error { err = utils.GoGetPackage(projectPath, godotenvPackage) if err != nil { - log.Printf("Could not install go dependency %v\n", err) + log.Println("Could not install go dependency") return err } @@ -517,7 +517,7 @@ func (p *Project) CreateMainFile() error { } err = utils.GoGetPackage(projectPath, templPackage) if err != nil { - log.Printf("Could not install go dependency %v\n", err) + log.Println("Could not install go dependency") return err } @@ -535,7 +535,7 @@ func (p *Project) CreateMainFile() error { } err = utils.GoGetPackage(projectPath, []string{"github.com/gofiber/fiber/v2/middleware/adaptor"}) if err != nil { - log.Printf("Could not install go dependency %v\n", err) + log.Println("Could not install go dependency") return err } } else { diff --git a/cmd/utils/utils.go b/cmd/utils/utils.go index 7ba4dd341..72c71456c 100644 --- a/cmd/utils/utils.go +++ b/cmd/utils/utils.go @@ -52,9 +52,11 @@ func ExecuteCmd(name string, args []string, dir string) error { command := exec.Command(name, args...) command.Dir = dir var out bytes.Buffer + var stdErr bytes.Buffer command.Stdout = &out + command.Stderr = &stdErr if err := command.Run(); err != nil { - return err + return fmt.Errorf("%v\n%v", err, stdErr.String()) } return nil } From 0625a01133814458004f7478a220811ba5b19635 Mon Sep 17 00:00:00 2001 From: ujstor <116409846+Ujstor@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:22:14 +0200 Subject: [PATCH 08/34] docs actions update (#389) --- .github/workflows/docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ff04ca1b9..1cf620a6b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,10 +16,10 @@ jobs: uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('docs/requirements.txt') }} From e7af940492101a111c8404c18465a90aa85d50f5 Mon Sep 17 00:00:00 2001 From: ujstor <116409846+Ujstor@users.noreply.github.com> Date: Fri, 18 Apr 2025 20:22:38 +0200 Subject: [PATCH 09/34] go 1.23.0 (#390) --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 88501fc45..200962c44 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/melkeydev/go-blueprint -go 1.22.4 +go 1.23.0 require ( github.com/charmbracelet/bubbles v0.16.1 From 99288d3216ab9fe214854b0c6c5de1a14f70c908 Mon Sep 17 00:00:00 2001 From: Abdul Rafay Shaikh Date: Thu, 24 Apr 2025 22:22:38 +0500 Subject: [PATCH 10/34] fix: dont install less than v4 (#391) As the @tailwindcss/vite is installed and only supports v4 we are using --prefer-offline flag that can install tailwindcss v3 from cache with this we force to use cache only is tailwindcss version is greater than v4 --- cmd/program/program.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/program/program.go b/cmd/program/program.go index 5537017d8..4970b3492 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -346,7 +346,6 @@ func (p *Project) CreateMainFile() error { // Install the godotenv package err = utils.GoGetPackage(projectPath, godotenvPackage) - if err != nil { log.Println("Could not install go dependency") @@ -879,7 +878,7 @@ func (p *Project) CreateViteReactProject(projectPath string) error { cmd := exec.Command("npm", "install", "--prefer-offline", "--no-fund", - "tailwindcss", "@tailwindcss/vite") + "tailwindcss@^4", "@tailwindcss/vite") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { @@ -918,6 +917,7 @@ func (p *Project) CreateViteReactProject(projectPath string) error { return nil } + func (p *Project) CreateHtmxTemplates() { routesPlaceHolder := "" importsPlaceHolder := "" From d68f777eb2a694c6f6051010bf3214354566a28a Mon Sep 17 00:00:00 2001 From: Mussar <67082011+0x4D5352@users.noreply.github.com> Date: Sat, 3 May 2025 12:19:58 -0500 Subject: [PATCH 11/34] fix typos/grammar and remove duplicate text (#393) --- docs/docs/advanced-flag/react-vite.md | 4 ++-- docs/docs/advanced-flag/websocket.md | 2 +- docs/docs/blueprint-core/db-drivers.md | 6 +++--- docs/docs/blueprint-core/frameworks.md | 2 +- docs/docs/creating-project/project-init.md | 2 +- docs/docs/endpoints-test/mongo.md | 2 +- docs/docs/index.md | 6 +++--- docs/docs/installation.md | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/docs/advanced-flag/react-vite.md b/docs/docs/advanced-flag/react-vite.md index 01de8afe8..f9c7bdff6 100644 --- a/docs/docs/advanced-flag/react-vite.md +++ b/docs/docs/advanced-flag/react-vite.md @@ -7,14 +7,14 @@ The React advanced flag can be combined with the Tailwind flag for enhanced styl ```bash / (Root) ├── frontend/ # React advanced flag. Excludes HTMX. -│ ├── .env # Frontend environment configuration +│ ├── .env # Frontend environment configuration. │ ├── node_modules/ # Node dependencies. │ ├── public/ │ │ ├── index.html │ │ └── favicon.ico │ ├── src/ # React source files. │ │ ├── App.tsx # Main React component. -│ │ ├── assets/ # React assets directory +│ │ ├── assets/ # React assets directory. │ │ │ └── logo.svg │ │ ├── components/ # React components directory. │ │ │ ├── Header.tsx diff --git a/docs/docs/advanced-flag/websocket.md b/docs/docs/advanced-flag/websocket.md index c7bea3ad6..aaafb4252 100644 --- a/docs/docs/advanced-flag/websocket.md +++ b/docs/docs/advanced-flag/websocket.md @@ -1,4 +1,4 @@ -A `/websocket` endpoint is added in `routes.go` to facilitate websocket connections. Upon accessing this endpoint, the server establishes a websocket connection and begins transmitting timestamp messages at 2-second intervals. WS is utilized across all Go-blueprint supported frameworks. This simple implementation showcases how flexible project is. +A `/websocket` endpoint is added in `routes.go` to facilitate websocket connections. Upon accessing this endpoint, the server establishes a websocket connection and begins transmitting timestamp messages at 2-second intervals. WS is utilized across all Go-blueprint supported frameworks. This simple implementation showcases how flexible a project is. ### Code Implementation diff --git a/docs/docs/blueprint-core/db-drivers.md b/docs/docs/blueprint-core/db-drivers.md index f34c72d0a..e4d4b62ed 100644 --- a/docs/docs/blueprint-core/db-drivers.md +++ b/docs/docs/blueprint-core/db-drivers.md @@ -4,7 +4,7 @@ To extend the project with database functionality, users can choose from a varie 2. [Mysql](https://github.com/go-sql-driver/mysql): Enables seamless integration with MySQL databases. 3. [Postgres](https://github.com/jackc/pgx/): Facilitates connectivity to PostgreSQL databases. 4. [Redis](https://github.com/redis/go-redis): Provides tools for connecting and interacting with Redis. -5. [Sqlite](https://github.com/mattn/go-sqlite3): Suitable for projects requiring a lightweight, self-contained database. and interacting with Redis +5. [Sqlite](https://github.com/mattn/go-sqlite3): Suitable for projects requiring a lightweight, self-contained database. 6. [ScyllaDB](https://github.com/scylladb/gocql): Facilitates connectivity to ScyllaDB databases. ## Updated Project Structure @@ -36,7 +36,7 @@ Users can select the desired database driver based on their project's specific n ## Integration Tests for Database Operations -For all the database drivers but the `Sqlite`, integration tests are automatically generated to ensure that the database connection is working correctly. It uses [Testcontainers for Go](https://golang.testcontainers.org/) to spin up a containerized instance of the database server, run the tests, and then tear down the container. +For all the database drivers but `Sqlite`, integration tests are automatically generated to ensure that the database connection is working correctly. It uses [Testcontainers for Go](https://golang.testcontainers.org/) to spin up a containerized instance of the database server, run the tests, and then tear down the container. [Testcontainers for Go](https://golang.testcontainers.org/) is a Go package that makes it simple to create and clean up container-based dependencies for automated integration/smoke tests. The clean, easy-to-use API enables developers to programmatically define containers that should be run as part of a test and clean up those resources when the test is done. @@ -59,7 +59,7 @@ Go to the `internal/database` directory and run the following command: go test -v ``` -or just run the following command from the root directory: +Or, just run the following command from the root directory: ```bash make itest diff --git a/docs/docs/blueprint-core/frameworks.md b/docs/docs/blueprint-core/frameworks.md index 00aa84df6..4d86e82b1 100644 --- a/docs/docs/blueprint-core/frameworks.md +++ b/docs/docs/blueprint-core/frameworks.md @@ -1,4 +1,4 @@ -Created project can utilizes several Go web frameworks to handle HTTP routing and server functionality. The chosen frameworks are: +Created projects can utilize several Go web frameworks to handle HTTP routing and server functionality. The chosen frameworks are: 1. [**Chi**](https://github.com/go-chi/chi): Lightweight and flexible router for building Go HTTP services. 2. [**Echo**](https://github.com/labstack/echo): High-performance, extensible, minimalist Go web framework. diff --git a/docs/docs/creating-project/project-init.md b/docs/docs/creating-project/project-init.md index 9eae177c5..6ad0b23c6 100644 --- a/docs/docs/creating-project/project-init.md +++ b/docs/docs/creating-project/project-init.md @@ -39,7 +39,7 @@ To recreate the project using the same configuration semi-interactively, use the ```bash go-blueprint create --name my-project --framework chi --driver mysql --git commit --advanced ``` -This approach opens interactive mode only for advanced features, which allow you to choose the one or combination of available features. +This approach opens interactive mode only for advanced features, which allows you to choose the one or combination of available features. ![AdvancedFlag](../public/blueprint_advanced.png) diff --git a/docs/docs/endpoints-test/mongo.md b/docs/docs/endpoints-test/mongo.md index 15557f30c..1c81aa9c7 100644 --- a/docs/docs/endpoints-test/mongo.md +++ b/docs/docs/endpoints-test/mongo.md @@ -44,4 +44,4 @@ func (s *service) Health() map[string]string { ## Note -MongoDB does not support advanced health check functions like SQL databases or Redis. Implementation is basic, providing only a simple ping response to indicate if the server is reachable and DB connection healthy. +MongoDB does not support advanced health check functions like SQL databases or Redis. The implementation is basic, providing only a simple ping response to indicate if the server is reachable and DB connection healthy. diff --git a/docs/docs/index.md b/docs/docs/index.md index 3219be683..1e0d44f2a 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -33,8 +33,8 @@ Here's an overview of the project structure created by Go Blueprint when all opt │ ├── api/ │ │ └── main.go # Main file for starting the server. │ └── web/ -│ ├── styles/ # only for generating css will not be served public -│ │ └── input.css # Tailwind input file for compiling output.css with CLI when HTMX is used +│ ├── styles/ # Only for generating css will not be served public. +│ │ └── input.css # Tailwind input file for compiling output.css with CLI when HTMX is used. │ ├── assets/ │ │ ├── css/ │ │ │ └── output.css # Generated CSS file. @@ -53,7 +53,7 @@ Here's an overview of the project structure created by Go Blueprint when all opt │ │ └── favicon.ico │ ├── src/ # React source files. │ │ ├── App.tsx # Main React component. -│ │ ├── assets/ # React assets directory +│ │ ├── assets/ # React assets directory. │ │ │ └── logo.svg │ │ ├── components/ # React components directory. │ │ │ ├── Header.tsx diff --git a/docs/docs/installation.md b/docs/docs/installation.md index 852a103f6..de058c824 100644 --- a/docs/docs/installation.md +++ b/docs/docs/installation.md @@ -7,7 +7,7 @@ Go-Blueprint provides a convenient CLI tool to effortlessly set up your Go proje ## Binary Installation -To install the Go-Blueprint CLI tool as a binary, Run the following command: +To install the Go-Blueprint CLI tool as a binary, run the following command: ```sh go install github.com/melkeydev/go-blueprint@latest @@ -15,7 +15,7 @@ go install github.com/melkeydev/go-blueprint@latest This command installs the Go-Blueprint binary, automatically binding it to your `$GOPATH`. -> if you’re using Zsh, you’ll need to add it manually to `~/.zshrc`. +> If you’re using Zsh, you’ll need to add it manually to `~/.zshrc`. > After running the installation command, you need to update your `PATH` environment variable. To do this, you need to find out the correct `GOPATH` for your system. You can do this by running the following command: > Check your `GOPATH` From 83092ca46db49259fbb3348e3b0352aa3ce97a33 Mon Sep 17 00:00:00 2001 From: Ethan Rouimi Date: Sat, 3 May 2025 19:20:31 +0200 Subject: [PATCH 12/34] feat: add flag completions (#395) --- cmd/create.go | 5 +++++ cmd/utils/utils.go | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/cmd/create.go b/cmd/create.go index 908e045ba..fd2fe065f 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -52,6 +52,11 @@ func init() { createCmd.Flags().BoolP("advanced", "a", false, "Get prompts for advanced features") createCmd.Flags().Var(&advancedFeatures, "feature", fmt.Sprintf("Advanced feature to use. Allowed values: %s", strings.Join(flags.AllowedAdvancedFeatures, ", "))) createCmd.Flags().VarP(&flagGit, "git", "g", fmt.Sprintf("Git to use. Allowed values: %s", strings.Join(flags.AllowedGitsOptions, ", "))) + + utils.RegisterStaticCompletions(createCmd, "framework", flags.AllowedProjectTypes) + utils.RegisterStaticCompletions(createCmd, "driver", flags.AllowedDBDrivers) + utils.RegisterStaticCompletions(createCmd, "feature", flags.AllowedAdvancedFeatures) + utils.RegisterStaticCompletions(createCmd, "git", flags.AllowedGitsOptions) } type Options struct { diff --git a/cmd/utils/utils.go b/cmd/utils/utils.go index 72c71456c..6266b62d0 100644 --- a/cmd/utils/utils.go +++ b/cmd/utils/utils.go @@ -5,10 +5,12 @@ package utils import ( "bytes" "fmt" + "log" "os/exec" "regexp" "strings" + "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -47,6 +49,16 @@ func NonInteractiveCommand(use string, flagSet *pflag.FlagSet) string { return nonInteractiveCommand } +func RegisterStaticCompletions(cmd *cobra.Command, flag string, options []string) { + err := cmd.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return options, cobra.ShellCompDirectiveNoFileComp + }) + + if err != nil { + log.Printf("warning: could not register completion for --%s: %v", flag, err) + } +} + // ExecuteCmd provides a shorthand way to run a shell command func ExecuteCmd(name string, args []string, dir string) error { command := exec.Command(name, args...) From 99ce129aa52c01010aea8563c9356ed31db2b916 Mon Sep 17 00:00:00 2001 From: ScaryFrogg <45082384+ScaryFrogg@users.noreply.github.com> Date: Sun, 18 May 2025 00:01:32 +0200 Subject: [PATCH 13/34] Fixed no git on system panic even if git flag is skip (#396) --- cmd/program/program.go | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/cmd/program/program.go b/cmd/program/program.go index 4970b3492..e32bf272e 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -253,15 +253,17 @@ func (p *Project) CreateMainFile() error { } // Check if user.email is set. - emailSet, err := utils.CheckGitConfig("user.email") - if err != nil { - return err - } + if p.GitOptions.String() != flags.Skip { - if !emailSet && p.GitOptions.String() != flags.Skip { - fmt.Println("user.email is not set in git config.") - fmt.Println("Please set up git config before trying again.") - panic("\nGIT CONFIG ISSUE: user.email is not set in git config.\n") + emailSet, err := utils.CheckGitConfig("user.email") + if err != nil { + return err + } + if !emailSet { + fmt.Println("user.email is not set in git config.") + fmt.Println("Please set up git config before trying again.") + panic("\nGIT CONFIG ISSUE: user.email is not set in git config.\n") + } } p.ProjectName = strings.TrimSpace(p.ProjectName) @@ -283,7 +285,7 @@ func (p *Project) CreateMainFile() error { p.createFrameworkMap() // Create go.mod - err = utils.InitGoMod(p.ProjectName, projectPath) + err := utils.InitGoMod(p.ProjectName, projectPath) if err != nil { log.Printf("Could not initialize go.mod in new project %v\n", err) return err @@ -678,12 +680,12 @@ func (p *Project) CreateMainFile() error { return err } - nameSet, err := utils.CheckGitConfig("user.name") - if err != nil { - return err - } - if p.GitOptions != flags.Skip { + nameSet, err := utils.CheckGitConfig("user.name") + if err != nil { + return err + } + if !nameSet { fmt.Println("user.name is not set in git config.") fmt.Println("Please set up git config before trying again.") From 66561771ccfedbd7df15bde29048e610a21fbe59 Mon Sep 17 00:00:00 2001 From: eggplannt Date: Tue, 27 May 2025 00:05:38 -0600 Subject: [PATCH 14/34] initial implimentation --- cmd/create.go | 15 +++-- cmd/flags/advancedFeatures.go | 3 +- cmd/program/program.go | 63 ++++++++++++++++--- cmd/steps/steps.go | 5 ++ .../advanced/files/sqlc/query.sql.tmpl | 36 +++++++++++ .../advanced/files/sqlc/schema.sql.tmpl | 9 +++ .../advanced/files/sqlc/sqlc.yaml.tmpl | 14 +++++ cmd/template/advanced/routes.go | 21 +++++++ .../dbdriver/files/service/mysql.tmpl | 13 ++++ .../dbdriver/files/service/postgres.tmpl | 13 ++++ .../dbdriver/files/service/sqlite.tmpl | 13 ++++ cmd/template/framework/files/air.toml.tmpl | 2 +- cmd/template/framework/files/makefile.tmpl | 38 ++++++++++- sqlc.yaml | 14 +++++ 14 files changed, 241 insertions(+), 18 deletions(-) create mode 100644 cmd/template/advanced/files/sqlc/query.sql.tmpl create mode 100644 cmd/template/advanced/files/sqlc/schema.sql.tmpl create mode 100644 cmd/template/advanced/files/sqlc/sqlc.yaml.tmpl create mode 100644 sqlc.yaml diff --git a/cmd/create.go b/cmd/create.go index 908e045ba..280165ded 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -18,6 +18,7 @@ import ( "github.com/melkeydev/go-blueprint/cmd/ui/textinput" "github.com/melkeydev/go-blueprint/cmd/utils" "github.com/spf13/cobra" + "slices" ) const logo = ` @@ -113,7 +114,7 @@ var createCmd = &cobra.Command{ GitOptions: flagGit, } - steps := steps.InitSteps(flagFramework, flagDBDriver) + optionSteps := steps.InitSteps(flagFramework, flagDBDriver) fmt.Printf("%s\n", logoStyle.Render(logo)) // Advanced option steps: @@ -155,7 +156,7 @@ var createCmd = &cobra.Command{ if project.ProjectType == "" { isInteractive = true - step := steps.Steps["framework"] + step := optionSteps.Steps["framework"] tprogram = tea.NewProgram(multiInput.InitialModelMulti(step.Options, options.ProjectType, step.Headers, project)) if _, err := tprogram.Run(); err != nil { cobra.CheckErr(textinput.CreateErrorInputModel(err).Err()) @@ -175,7 +176,7 @@ var createCmd = &cobra.Command{ if project.DBDriver == "" { isInteractive = true - step := steps.Steps["driver"] + step := optionSteps.Steps["driver"] tprogram = tea.NewProgram(multiInput.InitialModelMulti(step.Options, options.DBDriver, step.Headers, project)) if _, err := tprogram.Run(); err != nil { cobra.CheckErr(textinput.CreateErrorInputModel(err).Err()) @@ -202,7 +203,11 @@ var createCmd = &cobra.Command{ } } else { isInteractive = true - step := steps.Steps["advanced"] + step := optionSteps.Steps["advanced"] + if project.DBDriver != flags.Postgres && project.DBDriver != flags.MySql && project.DBDriver != flags.Sqlite { + step.Options = slices.DeleteFunc(step.Options, func(s steps.Item) bool { return s.Flag == "Sqlc" }) + + } tprogram = tea.NewProgram((multiSelect.InitialModelMultiSelect(step.Options, options.Advanced, step.Headers, project))) if _, err := tprogram.Run(); err != nil { cobra.CheckErr(textinput.CreateErrorInputModel(err).Err()) @@ -224,7 +229,7 @@ var createCmd = &cobra.Command{ if project.GitOptions == "" { isInteractive = true - step := steps.Steps["git"] + step := optionSteps.Steps["git"] tprogram = tea.NewProgram(multiInput.InitialModelMulti(step.Options, options.Git, step.Headers, project)) if _, err := tprogram.Run(); err != nil { cobra.CheckErr(textinput.CreateErrorInputModel(err).Err()) diff --git a/cmd/flags/advancedFeatures.go b/cmd/flags/advancedFeatures.go index f97d629e3..333fa5ece 100644 --- a/cmd/flags/advancedFeatures.go +++ b/cmd/flags/advancedFeatures.go @@ -14,9 +14,10 @@ const ( Tailwind string = "tailwind" React string = "react" Docker string = "docker" + Sqlc string = "sqlc" ) -var AllowedAdvancedFeatures = []string{string(React), string(Htmx), string(GoProjectWorkflow), string(Websocket), string(Tailwind), string(Docker)} +var AllowedAdvancedFeatures = []string{string(React), string(Htmx), string(GoProjectWorkflow), string(Websocket), string(Tailwind), string(Docker), string(Sqlc)} func (f AdvancedFeatures) String() string { return strings.Join(f, ",") diff --git a/cmd/program/program.go b/cmd/program/program.go index 5537017d8..249ef50a5 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -54,7 +54,7 @@ type Framework struct { } type Driver struct { - packageName []string + PackageName []string templater DBDriverTemplater } @@ -189,28 +189,28 @@ func (p *Project) createFrameworkMap() { func (p *Project) createDBDriverMap() { p.DBDriverMap[flags.MySql] = Driver{ - packageName: mysqlDriver, + PackageName: mysqlDriver, templater: dbdriver.MysqlTemplate{}, } p.DBDriverMap[flags.Postgres] = Driver{ - packageName: postgresDriver, + PackageName: postgresDriver, templater: dbdriver.PostgresTemplate{}, } p.DBDriverMap[flags.Sqlite] = Driver{ - packageName: sqliteDriver, + PackageName: sqliteDriver, templater: dbdriver.SqliteTemplate{}, } p.DBDriverMap[flags.Mongo] = Driver{ - packageName: mongoDriver, + PackageName: mongoDriver, templater: dbdriver.MongoTemplate{}, } p.DBDriverMap[flags.Redis] = Driver{ - packageName: redisDriver, + PackageName: redisDriver, templater: dbdriver.RedisTemplate{}, } p.DBDriverMap[flags.Scylla] = Driver{ - packageName: gocqlDriver, + PackageName: gocqlDriver, templater: dbdriver.ScyllaTemplate{}, } } @@ -301,7 +301,10 @@ func (p *Project) CreateMainFile() error { // Install the correct package for the selected driver if p.DBDriver != "none" { p.createDBDriverMap() - err = utils.GoGetPackage(projectPath, p.DBDriverMap[p.DBDriver].packageName) + if p.AdvancedOptions[string(flags.Sqlc)] && (p.DBDriver != flags.Postgres && p.DBDriver != flags.MySql && p.DBDriver != flags.Sqlite) { + p.AdvancedOptions[string(flags.Sqlc)] = false + } + err = utils.GoGetPackage(projectPath, p.DBDriverMap[p.DBDriver].PackageName) if err != nil { log.Println("Could not install go dependency for chosen driver") return err @@ -456,6 +459,50 @@ func (p *Project) CreateMainFile() error { } } + if p.AdvancedOptions[string(flags.Sqlc)] { + yamlFile, err := os.Create(fmt.Sprintf("%s/sqlc.yaml", projectPath)) + if err != nil { + return err + } + defer yamlFile.Close() + + yamlTemplate := template.Must(template.New("sqlcyaml").Parse((string(advanced.SqlcYamlTemplate())))) + err = yamlTemplate.Execute(yamlFile, p) + if err != nil { + return err + } + + err = os.MkdirAll(fmt.Sprintf("%s/%s/sql", projectPath, internalDatabasePath), 0o755) + if err != nil { + return err + } + + schemaFile, err := os.Create(fmt.Sprintf("%s/%s/sql/schema.sql", projectPath, internalDatabasePath)) + if err != nil { + return err + } + defer schemaFile.Close() + + queryFile, err := os.Create(fmt.Sprintf("%s/%s/sql/query.sql", projectPath, internalDatabasePath)) + if err != nil { + return err + } + defer queryFile.Close() + + schamaTemplate := advanced.SqlcSchemaTemplate() + _, err = schemaFile.Write(schamaTemplate) + if err != nil { + return err + } + + queryTemplate := advanced.SqlcQueryTemplate() + _, err = queryFile.Write(queryTemplate) + if err != nil { + return err + } + + } + if p.AdvancedOptions[string(flags.Htmx)] { // create folders and hello world file err = p.CreatePath(cmdWebPath, projectPath) diff --git a/cmd/steps/steps.go b/cmd/steps/steps.go index eacb874bc..dd6d60e85 100644 --- a/cmd/steps/steps.go +++ b/cmd/steps/steps.go @@ -96,6 +96,11 @@ func InitSteps(projectType flags.Framework, databaseType flags.Database) *Steps StepName: "Advanced Features", Headers: "Which advanced features do you want?", Options: []Item{ + { + Flag: "Sqlc", + Title: "Sqlc", + Desc: "Generates fully type-safe idiomatic Go code from Postgres, MySql, and SQlite", + }, { Flag: "React", Title: "React", diff --git a/cmd/template/advanced/files/sqlc/query.sql.tmpl b/cmd/template/advanced/files/sqlc/query.sql.tmpl new file mode 100644 index 000000000..9a1fd14b4 --- /dev/null +++ b/cmd/template/advanced/files/sqlc/query.sql.tmpl @@ -0,0 +1,36 @@ +-- Insert a new user +-- name: CreateUser :one +INSERT INTO users ( + id, + name, + email +) VALUES ( + $1, $2, $3 +) RETURNING *; + +-- Get a user by ID +-- name: GetUserByID :one +SELECT * FROM users +WHERE id = $1 LIMIT 1; + +-- Get a user by email +-- name: GetUserByEmail :one +SELECT * FROM users +WHERE email = $1 LIMIT 1; + +-- List all users +-- name: ListUsers :many +SELECT * FROM users +ORDER BY name; + +-- Update a user's name +-- name: UpdateUserName :one +UPDATE users +SET name = $2 +WHERE id = $1 +RETURNING *; + +-- Delete a user +-- name: DeleteUser :exec +DELETE FROM users +WHERE id = $1; diff --git a/cmd/template/advanced/files/sqlc/schema.sql.tmpl b/cmd/template/advanced/files/sqlc/schema.sql.tmpl new file mode 100644 index 000000000..1d0c418ba --- /dev/null +++ b/cmd/template/advanced/files/sqlc/schema.sql.tmpl @@ -0,0 +1,9 @@ +-- Create a simple users table +CREATE TABLE users ( + id UUID PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) UNIQUE NOT NULL +); + +-- Add an index for faster lookups on email +CREATE INDEX users_email_idx ON users (email); diff --git a/cmd/template/advanced/files/sqlc/sqlc.yaml.tmpl b/cmd/template/advanced/files/sqlc/sqlc.yaml.tmpl new file mode 100644 index 000000000..c19075a2b --- /dev/null +++ b/cmd/template/advanced/files/sqlc/sqlc.yaml.tmpl @@ -0,0 +1,14 @@ +version: "2" +sql: + - engine: "{{ .DBDriver }}{{- if eq .DBDriver "postgres" }}ql{{- end}}" + queries: "internal/database/sql/query.sql" + schema: "internal/database/sql/schema.sql" + gen: + go: + package: "repository" + out: "internal/database/repository" + sql_package: "{{ index (index .DBDriverMap .DBDriver).PackageName 0 }}" + emit_json_tags: true + emit_prepared_queries: false + emit_interface: true + emit_empty_slices: true diff --git a/cmd/template/advanced/routes.go b/cmd/template/advanced/routes.go index e7860f58f..cd3efd964 100644 --- a/cmd/template/advanced/routes.go +++ b/cmd/template/advanced/routes.go @@ -76,6 +76,15 @@ var fiberHtmxTemplImports []byte //go:embed files/websocket/imports/fiber.tmpl var fiberWebsocketTemplImports []byte +//go:embed files/sqlc/sqlc.yaml.tmpl +var sqlcYamlTemplate []byte + +//go:embed files/sqlc/schema.sql.tmpl +var sqlcSchemaTemplate []byte + +//go:embed files/sqlc/query.sql.tmpl +var sqlcQueryTemplate []byte + func EchoHtmxTemplRoutesTemplate() []byte { return echoHtmxTemplRoutes } @@ -171,3 +180,15 @@ func FiberWebsocketTemplImportsTemplate() []byte { func GinHtmxTemplImportsTemplate() []byte { return ginHtmxTemplImports } + +func SqlcYamlTemplate() []byte { + return sqlcYamlTemplate +} + +func SqlcSchemaTemplate() []byte { + return sqlcSchemaTemplate +} + +func SqlcQueryTemplate() []byte { + return sqlcQueryTemplate +} diff --git a/cmd/template/dbdriver/files/service/mysql.tmpl b/cmd/template/dbdriver/files/service/mysql.tmpl index bc68a5ff8..d4364c01f 100644 --- a/cmd/template/dbdriver/files/service/mysql.tmpl +++ b/cmd/template/dbdriver/files/service/mysql.tmpl @@ -9,6 +9,10 @@ import ( "strconv" "time" + {{- if .AdvancedOptions.sqlc}} + "{{.ProjectName}}/internal/database/repository" + {{- end}} + _ "github.com/go-sql-driver/mysql" _ "github.com/joho/godotenv/autoload" ) @@ -26,6 +30,9 @@ type Service interface { type service struct { db *sql.DB + {{- if .AdvancedOptions.sqlc}} + Queries *repository.Queries + {{- end}} } var ( @@ -54,8 +61,14 @@ func New() Service { db.SetMaxIdleConns(50) db.SetMaxOpenConns(50) + {{- if .AdvancedOptions.sqlc}} + queries := repository.New(db) + {{- end}} dbInstance = &service{ db: db, + {{- if .AdvancedOptions.sqlc}} + Queries: queries + {{- end}} } return dbInstance } diff --git a/cmd/template/dbdriver/files/service/postgres.tmpl b/cmd/template/dbdriver/files/service/postgres.tmpl index 0fececd06..4662897b9 100644 --- a/cmd/template/dbdriver/files/service/postgres.tmpl +++ b/cmd/template/dbdriver/files/service/postgres.tmpl @@ -9,6 +9,10 @@ import ( "strconv" "time" + {{- if .AdvancedOptions.sqlc}} + "{{.ProjectName}}/internal/database/repository" + {{- end}} + _ "github.com/jackc/pgx/v5/stdlib" _ "github.com/joho/godotenv/autoload" ) @@ -26,6 +30,9 @@ type Service interface { type service struct { db *sql.DB + {{- if .AdvancedOptions.sqlc}} + Queries *repository.Queries + {{- end}} } var ( @@ -48,8 +55,14 @@ func New() Service { if err != nil { log.Fatal(err) } + {{- if .AdvancedOptions.sqlc}} + queries := repository.New(db) + {{- end}} dbInstance = &service{ db: db, + {{- if .AdvancedOptions.sqlc}} + Queries: queries, + {{- end}} } return dbInstance } diff --git a/cmd/template/dbdriver/files/service/sqlite.tmpl b/cmd/template/dbdriver/files/service/sqlite.tmpl index 4f65e760b..852a61aac 100644 --- a/cmd/template/dbdriver/files/service/sqlite.tmpl +++ b/cmd/template/dbdriver/files/service/sqlite.tmpl @@ -9,6 +9,10 @@ import ( "strconv" "time" + {{- if .AdvancedOptions.sqlc}} + "{{.ProjectName}}/internal/database/repository" + {{- end}} + _ "github.com/mattn/go-sqlite3" _ "github.com/joho/godotenv/autoload" ) @@ -26,6 +30,9 @@ type Service interface { type service struct { db *sql.DB + {{- if .AdvancedOptions.sqlc}} + Queries *repository.Queries + {{- end}} } var ( @@ -46,8 +53,14 @@ func New() Service { log.Fatal(err) } + {{- if .AdvancedOptions.sqlc}} + queries := repository.New(db) + {{- end}} dbInstance = &service{ db: db, + {{- if .AdvancedOptions.sqlc}} + Queries: queries, + {{- end}} } return dbInstance } diff --git a/cmd/template/framework/files/air.toml.tmpl b/cmd/template/framework/files/air.toml.tmpl index 271c01c22..202b01fea 100644 --- a/cmd/template/framework/files/air.toml.tmpl +++ b/cmd/template/framework/files/air.toml.tmpl @@ -14,7 +14,7 @@ tmp_dir = "tmp" follow_symlink = false full_bin = "" include_dir = [] - include_ext = ["go", "tpl", "tmpl", "html"{{if .AdvancedOptions.htmx}}, "templ"{{end}}] + include_ext = ["go", "tpl", "tmpl", "html"{{if .AdvancedOptions.htmx}}, "templ"{{end}}{{if .AdvancedOptions.sqlc}}, "sql", "yaml"{{end}}] include_file = [] kill_delay = "0s" log = "build-errors.log" diff --git a/cmd/template/framework/files/makefile.tmpl b/cmd/template/framework/files/makefile.tmpl index 7e10b2548..027272097 100644 --- a/cmd/template/framework/files/makefile.tmpl +++ b/cmd/template/framework/files/makefile.tmpl @@ -46,11 +46,43 @@ tailwind-install: tailwind-install: @if not exist tailwindcss.exe powershell -ExecutionPolicy Bypass -Command "Invoke-WebRequest -Uri 'https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-windows-x64.exe' -OutFile 'tailwindcss.exe'"{{- end }} {{- end }} - -build:{{- if and .AdvancedOptions.tailwind (not .AdvancedOptions.react) }} tailwind-install{{- end }}{{- if and (or .AdvancedOptions.htmx .AdvancedOptions.tailwind) (not .AdvancedOptions.react) }} templ-install{{- end }} +{{- if .AdvancedOptions.sqlc }} +{{- if .OSCheck.UnixBased }} +sqlc-install: + @if ! command -v sqlc > /dev/null; then \ + read -p "Go's 'sqlc' is not installed on your machine. Do you want to install it? [Y/n] " choice; \ + if [ "$$choice" != "n" ] && [ "$$choice" != "N" ]; then \ + go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest; \ + if [ ! -x "$$(command -v sqlc)" ]; then \ + echo "sqlc installation failed. Exiting..."; \ + exit 1; \ + fi; \ + else \ + echo "You chose not to install sqlc. Exiting..."; \ + exit 1; \ + fi; \ + fi +{{- else }} +sqlc-install: + @powershell -ExecutionPolicy Bypass -Command "if (Get-Command sqlc -ErrorAction SilentlyContinue) { \ + ; \ + } else { \ + Write-Output 'Installing sqlc...'; \ + go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest; \ + if (-not (Get-Command sqlc -ErrorAction SilentlyContinue)) { \ + Write-Output 'sqlc installation failed. Exiting...'; \ + exit 1; \ + } else { \ + Write-Output 'sqlc installed successfully.'; \ + } \ + }" +{{- end }} +{{- end }} +build:{{- if and .AdvancedOptions.tailwind (not .AdvancedOptions.react) }} tailwind-install{{- end }}{{- if and (or .AdvancedOptions.htmx .AdvancedOptions.tailwind) (not .AdvancedOptions.react) }} templ-install{{- end }}{{- if .AdvancedOptions.sqlc}} sqlc-install {{- end }} @echo "Building..." {{ if and (or .AdvancedOptions.htmx .AdvancedOptions.tailwind) (not .AdvancedOptions.react) }}@templ generate{{- end }} {{ if and .AdvancedOptions.tailwind (not .AdvancedOptions.react) }}@{{ if .OSCheck.UnixBased }}./tailwindcss{{ else }}.\tailwindcss.exe{{ end }} -i cmd/web/styles/input.css -o cmd/web/assets/css/output.css{{ end }} + {{- if .AdvancedOptions.sqlc }}@sqlc generate {{- end}} {{ if .OSCheck.UnixBased }}@{{- if and (.AdvancedOptions.docker) (eq .DBDriver "sqlite") }}CGO_ENABLED=1 GOOS=linux {{ end }}go build -o main cmd/api/main.go{{- else }}@go build -o main.exe cmd/api/main.go{{- end }} # Run the application @@ -138,4 +170,4 @@ watch: }" {{- end }} -.PHONY: all build run test clean watch{{- if and (not .AdvancedOptions.react) .AdvancedOptions.tailwind }} tailwind-install{{- end }}{{- if and (ne .DBDriver "none") (ne .DBDriver "sqlite") }} docker-run docker-down itest{{- end }}{{- if and (or .AdvancedOptions.htmx .AdvancedOptions.tailwind) (not .AdvancedOptions.react) }} templ-install{{- end }} +.PHONY: all build run test clean watch{{- if and (not .AdvancedOptions.react) .AdvancedOptions.tailwind }} tailwind-install{{- end }}{{- if and (ne .DBDriver "none") (ne .DBDriver "sqlite") }} docker-run docker-down itest{{- end }}{{- if and (or .AdvancedOptions.htmx .AdvancedOptions.tailwind) (not .AdvancedOptions.react) }} templ-install{{- end }}{{- if .AdvancedOptions.sql }} sqlc-generate {{- end}} diff --git a/sqlc.yaml b/sqlc.yaml new file mode 100644 index 000000000..9110a760f --- /dev/null +++ b/sqlc.yaml @@ -0,0 +1,14 @@ +version: "2" +sql: + - engine: "sqlite" + queries: "internal/database/sql/query.sql" + schema: "internal/database/sql/schema.sql" + gen: + go: + package: "repository" + out: "internal/database/repository" + sql_package: "github.com/mattn/go-sqlite3" + emit_json_tags: true + emit_prepared_queries: false + emit_interface: true + emit_empty_slices: true From 241bb13e4af84a8fdf41817bd153435824c1fc14 Mon Sep 17 00:00:00 2001 From: eggplannt Date: Tue, 27 May 2025 00:10:44 -0600 Subject: [PATCH 15/34] undo make packageName public --- cmd/program/program.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/program/program.go b/cmd/program/program.go index 249ef50a5..18491af5c 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -54,7 +54,7 @@ type Framework struct { } type Driver struct { - PackageName []string + packageName []string templater DBDriverTemplater } @@ -189,28 +189,28 @@ func (p *Project) createFrameworkMap() { func (p *Project) createDBDriverMap() { p.DBDriverMap[flags.MySql] = Driver{ - PackageName: mysqlDriver, + packageName: mysqlDriver, templater: dbdriver.MysqlTemplate{}, } p.DBDriverMap[flags.Postgres] = Driver{ - PackageName: postgresDriver, + packageName: postgresDriver, templater: dbdriver.PostgresTemplate{}, } p.DBDriverMap[flags.Sqlite] = Driver{ - PackageName: sqliteDriver, + packageName: sqliteDriver, templater: dbdriver.SqliteTemplate{}, } p.DBDriverMap[flags.Mongo] = Driver{ - PackageName: mongoDriver, + packageName: mongoDriver, templater: dbdriver.MongoTemplate{}, } p.DBDriverMap[flags.Redis] = Driver{ - PackageName: redisDriver, + packageName: redisDriver, templater: dbdriver.RedisTemplate{}, } p.DBDriverMap[flags.Scylla] = Driver{ - PackageName: gocqlDriver, + packageName: gocqlDriver, templater: dbdriver.ScyllaTemplate{}, } } @@ -304,7 +304,7 @@ func (p *Project) CreateMainFile() error { if p.AdvancedOptions[string(flags.Sqlc)] && (p.DBDriver != flags.Postgres && p.DBDriver != flags.MySql && p.DBDriver != flags.Sqlite) { p.AdvancedOptions[string(flags.Sqlc)] = false } - err = utils.GoGetPackage(projectPath, p.DBDriverMap[p.DBDriver].PackageName) + err = utils.GoGetPackage(projectPath, p.DBDriverMap[p.DBDriver].packageName) if err != nil { log.Println("Could not install go dependency for chosen driver") return err From fd7ef8aefe804697df4332b616608c4380fa2bb1 Mon Sep 17 00:00:00 2001 From: eggplannt Date: Tue, 27 May 2025 00:36:34 -0600 Subject: [PATCH 16/34] hotfix --- cmd/program/program.go | 6 --- .../advanced/files/sqlc/query.sql.tmpl | 38 +------------------ .../advanced/files/sqlc/schema.sql.tmpl | 9 ----- .../advanced/files/sqlc/sqlc.yaml.tmpl | 1 - cmd/template/advanced/routes.go | 7 ---- .../dbdriver/files/service/mysql.tmpl | 7 +++- .../dbdriver/files/service/postgres.tmpl | 7 +++- .../dbdriver/files/service/sqlite.tmpl | 7 +++- 8 files changed, 17 insertions(+), 65 deletions(-) delete mode 100644 cmd/template/advanced/files/sqlc/schema.sql.tmpl diff --git a/cmd/program/program.go b/cmd/program/program.go index 18491af5c..4cff55646 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -489,12 +489,6 @@ func (p *Project) CreateMainFile() error { } defer queryFile.Close() - schamaTemplate := advanced.SqlcSchemaTemplate() - _, err = schemaFile.Write(schamaTemplate) - if err != nil { - return err - } - queryTemplate := advanced.SqlcQueryTemplate() _, err = queryFile.Write(queryTemplate) if err != nil { diff --git a/cmd/template/advanced/files/sqlc/query.sql.tmpl b/cmd/template/advanced/files/sqlc/query.sql.tmpl index 9a1fd14b4..5e40b6e21 100644 --- a/cmd/template/advanced/files/sqlc/query.sql.tmpl +++ b/cmd/template/advanced/files/sqlc/query.sql.tmpl @@ -1,36 +1,2 @@ --- Insert a new user --- name: CreateUser :one -INSERT INTO users ( - id, - name, - email -) VALUES ( - $1, $2, $3 -) RETURNING *; - --- Get a user by ID --- name: GetUserByID :one -SELECT * FROM users -WHERE id = $1 LIMIT 1; - --- Get a user by email --- name: GetUserByEmail :one -SELECT * FROM users -WHERE email = $1 LIMIT 1; - --- List all users --- name: ListUsers :many -SELECT * FROM users -ORDER BY name; - --- Update a user's name --- name: UpdateUserName :one -UPDATE users -SET name = $2 -WHERE id = $1 -RETURNING *; - --- Delete a user --- name: DeleteUser :exec -DELETE FROM users -WHERE id = $1; +-- name: Placeholder :one +SELECT 1; diff --git a/cmd/template/advanced/files/sqlc/schema.sql.tmpl b/cmd/template/advanced/files/sqlc/schema.sql.tmpl deleted file mode 100644 index 1d0c418ba..000000000 --- a/cmd/template/advanced/files/sqlc/schema.sql.tmpl +++ /dev/null @@ -1,9 +0,0 @@ --- Create a simple users table -CREATE TABLE users ( - id UUID PRIMARY KEY, - name VARCHAR(255) NOT NULL, - email VARCHAR(255) UNIQUE NOT NULL -); - --- Add an index for faster lookups on email -CREATE INDEX users_email_idx ON users (email); diff --git a/cmd/template/advanced/files/sqlc/sqlc.yaml.tmpl b/cmd/template/advanced/files/sqlc/sqlc.yaml.tmpl index c19075a2b..91fd93ab4 100644 --- a/cmd/template/advanced/files/sqlc/sqlc.yaml.tmpl +++ b/cmd/template/advanced/files/sqlc/sqlc.yaml.tmpl @@ -7,7 +7,6 @@ sql: go: package: "repository" out: "internal/database/repository" - sql_package: "{{ index (index .DBDriverMap .DBDriver).PackageName 0 }}" emit_json_tags: true emit_prepared_queries: false emit_interface: true diff --git a/cmd/template/advanced/routes.go b/cmd/template/advanced/routes.go index cd3efd964..8733f894e 100644 --- a/cmd/template/advanced/routes.go +++ b/cmd/template/advanced/routes.go @@ -79,9 +79,6 @@ var fiberWebsocketTemplImports []byte //go:embed files/sqlc/sqlc.yaml.tmpl var sqlcYamlTemplate []byte -//go:embed files/sqlc/schema.sql.tmpl -var sqlcSchemaTemplate []byte - //go:embed files/sqlc/query.sql.tmpl var sqlcQueryTemplate []byte @@ -185,10 +182,6 @@ func SqlcYamlTemplate() []byte { return sqlcYamlTemplate } -func SqlcSchemaTemplate() []byte { - return sqlcSchemaTemplate -} - func SqlcQueryTemplate() []byte { return sqlcQueryTemplate } diff --git a/cmd/template/dbdriver/files/service/mysql.tmpl b/cmd/template/dbdriver/files/service/mysql.tmpl index d4364c01f..7d95a9c2a 100644 --- a/cmd/template/dbdriver/files/service/mysql.tmpl +++ b/cmd/template/dbdriver/files/service/mysql.tmpl @@ -31,7 +31,7 @@ type Service interface { type service struct { db *sql.DB {{- if .AdvancedOptions.sqlc}} - Queries *repository.Queries + queries *repository.Queries {{- end}} } @@ -67,7 +67,7 @@ func New() Service { dbInstance = &service{ db: db, {{- if .AdvancedOptions.sqlc}} - Queries: queries + queries: queries {{- end}} } return dbInstance @@ -131,3 +131,6 @@ func (s *service) Close() error { log.Printf("Disconnected from database: %s", dbname) return s.db.Close() } +func (s *service) Quiries() *repository.Queries { + return s.queries +} diff --git a/cmd/template/dbdriver/files/service/postgres.tmpl b/cmd/template/dbdriver/files/service/postgres.tmpl index 4662897b9..02788e6ee 100644 --- a/cmd/template/dbdriver/files/service/postgres.tmpl +++ b/cmd/template/dbdriver/files/service/postgres.tmpl @@ -31,7 +31,7 @@ type Service interface { type service struct { db *sql.DB {{- if .AdvancedOptions.sqlc}} - Queries *repository.Queries + queries *repository.Queries {{- end}} } @@ -61,7 +61,7 @@ func New() Service { dbInstance = &service{ db: db, {{- if .AdvancedOptions.sqlc}} - Queries: queries, + queries: queries, {{- end}} } return dbInstance @@ -126,3 +126,6 @@ func (s *service) Close() error { log.Printf("Disconnected from database: %s", database) return s.db.Close() } +func (s *service) Quiries() *repository.Queries { + return s.queries +} diff --git a/cmd/template/dbdriver/files/service/sqlite.tmpl b/cmd/template/dbdriver/files/service/sqlite.tmpl index 852a61aac..a4984cfd4 100644 --- a/cmd/template/dbdriver/files/service/sqlite.tmpl +++ b/cmd/template/dbdriver/files/service/sqlite.tmpl @@ -31,7 +31,7 @@ type Service interface { type service struct { db *sql.DB {{- if .AdvancedOptions.sqlc}} - Queries *repository.Queries + queries *repository.Queries {{- end}} } @@ -59,7 +59,7 @@ func New() Service { dbInstance = &service{ db: db, {{- if .AdvancedOptions.sqlc}} - Queries: queries, + queries: queries, {{- end}} } return dbInstance @@ -124,3 +124,6 @@ func (s *service) Close() error { log.Printf("Disconnected from database: %s", dburl) return s.db.Close() } +func (s *service) Quiries() *repository.Queries { + return s.queries +} From 6b34dfd3eba3cc8a1cc99c812cda660f7c8836a6 Mon Sep 17 00:00:00 2001 From: eggplannt Date: Tue, 27 May 2025 00:42:25 -0600 Subject: [PATCH 17/34] edited linter --- .github/workflows/generate-linter-advanced.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/generate-linter-advanced.yml b/.github/workflows/generate-linter-advanced.yml index cc673d52e..159e0394f 100644 --- a/.github/workflows/generate-linter-advanced.yml +++ b/.github/workflows/generate-linter-advanced.yml @@ -8,10 +8,11 @@ jobs: framework_matrix: strategy: matrix: - framework: [chi, gin, fiber, gorilla/mux, httprouter, standard-library, echo] + 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, sqlc] runs-on: ubuntu-latest steps: @@ -20,7 +21,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '1.23.x' + go-version: "1.23.x" - name: Install golangci-lint run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.63.4 @@ -39,12 +40,18 @@ jobs: - if: ${{ matrix.advanced == 'htmx' || matrix.advanced == 'tailwind' }} name: Install Templ & gen templates - run: | + run: | go install github.com/a-h/templ/cmd/templ@latest /home/runner/go/bin/templ generate -path ${{ env.PROJECT_DIRECTORY }} + - if: ${{ matrix.advanced == 'sqlc'}} + name: Install Sqlc & gen code + run: | + go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest + /home/runner/go/bin/sqlc generate -f ${{ env.PROJECT_DIRECTORY }}/sqlc.yaml + - name: golangci-lint - run: | + run: | cd ${{ env.PROJECT_DIRECTORY }} golangci-lint run From 4294896c6822496d5d42f48245b49f8b2b993eba Mon Sep 17 00:00:00 2001 From: eggplannt Date: Tue, 27 May 2025 00:55:29 -0600 Subject: [PATCH 18/34] hotfix --- cmd/template/dbdriver/files/service/mysql.tmpl | 9 +++++++-- cmd/template/dbdriver/files/service/postgres.tmpl | 7 ++++++- cmd/template/dbdriver/files/service/sqlite.tmpl | 7 ++++++- sqlc.yaml | 14 -------------- 4 files changed, 19 insertions(+), 18 deletions(-) delete mode 100644 sqlc.yaml diff --git a/cmd/template/dbdriver/files/service/mysql.tmpl b/cmd/template/dbdriver/files/service/mysql.tmpl index 7d95a9c2a..3922ea38d 100644 --- a/cmd/template/dbdriver/files/service/mysql.tmpl +++ b/cmd/template/dbdriver/files/service/mysql.tmpl @@ -26,6 +26,11 @@ type Service interface { // Close terminates the database connection. // It returns an error if the connection cannot be closed. Close() error + {{- if .AdvancedOptions.sqlc}} + + // Returns the query used to interact with sqlc + Queries() *repository.Queries + {{- end}} } type service struct { @@ -67,7 +72,7 @@ func New() Service { dbInstance = &service{ db: db, {{- if .AdvancedOptions.sqlc}} - queries: queries + queries: queries, {{- end}} } return dbInstance @@ -131,6 +136,6 @@ func (s *service) Close() error { log.Printf("Disconnected from database: %s", dbname) return s.db.Close() } -func (s *service) Quiries() *repository.Queries { +func (s *service) Queries() *repository.Queries { return s.queries } diff --git a/cmd/template/dbdriver/files/service/postgres.tmpl b/cmd/template/dbdriver/files/service/postgres.tmpl index 02788e6ee..1879583f2 100644 --- a/cmd/template/dbdriver/files/service/postgres.tmpl +++ b/cmd/template/dbdriver/files/service/postgres.tmpl @@ -26,6 +26,11 @@ type Service interface { // Close terminates the database connection. // It returns an error if the connection cannot be closed. Close() error + {{- if .AdvancedOptions.sqlc}} + + // Returns the query used to interact with sqlc + Queries() *repository.Queries + {{- end}} } type service struct { @@ -126,6 +131,6 @@ func (s *service) Close() error { log.Printf("Disconnected from database: %s", database) return s.db.Close() } -func (s *service) Quiries() *repository.Queries { +func (s *service) Queries() *repository.Queries { return s.queries } diff --git a/cmd/template/dbdriver/files/service/sqlite.tmpl b/cmd/template/dbdriver/files/service/sqlite.tmpl index a4984cfd4..cca72a61c 100644 --- a/cmd/template/dbdriver/files/service/sqlite.tmpl +++ b/cmd/template/dbdriver/files/service/sqlite.tmpl @@ -26,6 +26,11 @@ type Service interface { // Close terminates the database connection. // It returns an error if the connection cannot be closed. Close() error + {{- if .AdvancedOptions.sqlc}} + + // Returns the query used to interact with sqlc + Queries() *repository.Queries + {{- end}} } type service struct { @@ -124,6 +129,6 @@ func (s *service) Close() error { log.Printf("Disconnected from database: %s", dburl) return s.db.Close() } -func (s *service) Quiries() *repository.Queries { +func (s *service) Queries() *repository.Queries { return s.queries } diff --git a/sqlc.yaml b/sqlc.yaml deleted file mode 100644 index 9110a760f..000000000 --- a/sqlc.yaml +++ /dev/null @@ -1,14 +0,0 @@ -version: "2" -sql: - - engine: "sqlite" - queries: "internal/database/sql/query.sql" - schema: "internal/database/sql/schema.sql" - gen: - go: - package: "repository" - out: "internal/database/repository" - sql_package: "github.com/mattn/go-sqlite3" - emit_json_tags: true - emit_prepared_queries: false - emit_interface: true - emit_empty_slices: true From 5e61afec72c8a5d85aa15dd32512ad00f36fefd5 Mon Sep 17 00:00:00 2001 From: eggplannt Date: Tue, 27 May 2025 01:09:19 -0600 Subject: [PATCH 19/34] forgot to check if sqlc enabled --- cmd/template/dbdriver/files/service/mysql.tmpl | 2 ++ cmd/template/dbdriver/files/service/postgres.tmpl | 2 ++ cmd/template/dbdriver/files/service/sqlite.tmpl | 2 ++ 3 files changed, 6 insertions(+) diff --git a/cmd/template/dbdriver/files/service/mysql.tmpl b/cmd/template/dbdriver/files/service/mysql.tmpl index 3922ea38d..2b15eec63 100644 --- a/cmd/template/dbdriver/files/service/mysql.tmpl +++ b/cmd/template/dbdriver/files/service/mysql.tmpl @@ -136,6 +136,8 @@ func (s *service) Close() error { log.Printf("Disconnected from database: %s", dbname) return s.db.Close() } +{{- if .AdvancedOptions.sqlc}} func (s *service) Queries() *repository.Queries { return s.queries } +{{- end}} diff --git a/cmd/template/dbdriver/files/service/postgres.tmpl b/cmd/template/dbdriver/files/service/postgres.tmpl index 1879583f2..385f1dcc9 100644 --- a/cmd/template/dbdriver/files/service/postgres.tmpl +++ b/cmd/template/dbdriver/files/service/postgres.tmpl @@ -131,6 +131,8 @@ func (s *service) Close() error { log.Printf("Disconnected from database: %s", database) return s.db.Close() } +{{- if .AdvancedOptions.sqlc}} func (s *service) Queries() *repository.Queries { return s.queries } +{{- end}} diff --git a/cmd/template/dbdriver/files/service/sqlite.tmpl b/cmd/template/dbdriver/files/service/sqlite.tmpl index cca72a61c..a3bc47e7f 100644 --- a/cmd/template/dbdriver/files/service/sqlite.tmpl +++ b/cmd/template/dbdriver/files/service/sqlite.tmpl @@ -129,6 +129,8 @@ func (s *service) Close() error { log.Printf("Disconnected from database: %s", dburl) return s.db.Close() } +{{- if .AdvancedOptions.sqlc}} func (s *service) Queries() *repository.Queries { return s.queries } +{{- end}} From 51fbd7a9d16810b78b67a83ec55795dfcc83e4cd Mon Sep 17 00:00:00 2001 From: FabricSoul <114606360+FabricSoul@users.noreply.github.com> Date: Mon, 23 Jun 2025 05:51:35 -0400 Subject: [PATCH 20/34] Fix docker build process (#403) --- cmd/template/advanced/files/docker/dockerfile.tmpl | 2 +- docs/docs/advanced-flag/docker.md | 9 +++++---- docs/docs/advanced-flag/react-vite.md | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cmd/template/advanced/files/docker/dockerfile.tmpl b/cmd/template/advanced/files/docker/dockerfile.tmpl index 961b5a56d..fd1467900 100644 --- a/cmd/template/advanced/files/docker/dockerfile.tmpl +++ b/cmd/template/advanced/files/docker/dockerfile.tmpl @@ -1,4 +1,4 @@ -FROM golang:1.23-alpine AS build +FROM golang:1.24.4-alpine AS build {{- if or (.AdvancedOptions.tailwind) (eq .DBDriver "sqlite") }} RUN apk add --no-cache{{- if .AdvancedOptions.tailwind }} curl{{ end }}{{- if (eq .DBDriver "sqlite") }} alpine-sdk{{ end }} {{- end }} diff --git a/docs/docs/advanced-flag/docker.md b/docs/docs/advanced-flag/docker.md index 3f250ef54..1b2ef94a6 100644 --- a/docs/docs/advanced-flag/docker.md +++ b/docs/docs/advanced-flag/docker.md @@ -4,7 +4,7 @@ The Dockerfile includes a two-stage build, and the final config depends on the u ## Dockerfile ```dockerfile -FROM golang:1.23-alpine AS build +FROM golang:1.24.4-alpine AS build RUN apk add --no-cache curl @@ -15,9 +15,10 @@ RUN go mod download COPY . . -RUN go install github.com/a-h/templ/cmd/templ@latest && \ +RUN apk add --no-cache libstdc++ libgcc && \ + go install github.com/a-h/templ/cmd/templ@latest && \ templ generate && \ - curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 -o tailwindcss && \ + curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64-musl -o tailwindcss && \ chmod +x tailwindcss && \ ./tailwindcss -i cmd/web/styles/input.css -o cmd/web/assets/css/output.css @@ -33,7 +34,7 @@ CMD ["./main"] Docker config if React flag is used: ```dockerfile -FROM golang:1.23-alpine AS build +FROM golang:1.24.4-alpine AS build WORKDIR /app diff --git a/docs/docs/advanced-flag/react-vite.md b/docs/docs/advanced-flag/react-vite.md index f9c7bdff6..ae29ac221 100644 --- a/docs/docs/advanced-flag/react-vite.md +++ b/docs/docs/advanced-flag/react-vite.md @@ -87,7 +87,7 @@ make docker-run ### Dockerfile ```dockerfile -FROM golang:1.23-alpine AS build +FROM golang:1.24.4-alpine AS build WORKDIR /app From 89715fbfb0aeee3f7ea88b6a578ec551a465bb5e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 13:36:48 +0200 Subject: [PATCH 21/34] update htmx version v2.0.5 (#406) --- .../advanced/files/htmx/htmx.min.js.tmpl | 2352 ++++++++--------- 1 file changed, 1174 insertions(+), 1178 deletions(-) diff --git a/cmd/template/advanced/files/htmx/htmx.min.js.tmpl b/cmd/template/advanced/files/htmx/htmx.min.js.tmpl index e29aba6b0..937d3c466 100644 --- a/cmd/template/advanced/files/htmx/htmx.min.js.tmpl +++ b/cmd/template/advanced/files/htmx/htmx.min.js.tmpl @@ -11,7 +11,7 @@ var htmx = (function () { findAll: null, closest: null, values: function (e, t) { - const n = cn(e, t || "post"); + const n = dn(e, t || "post"); return n.values; }, remove: null, @@ -64,18 +64,20 @@ var htmx = (function () { { code: "[45]..", swap: false, error: true }, ], allowNestedOobSwaps: true, + historyRestoreAsHxRequest: true, }, parseInterval: null, + location: location, _: null, - version: "2.0.4", + version: "2.0.5", }; Q.onLoad = j; - Q.process = kt; - Q.on = ye; + Q.process = Ft; + Q.on = xe; Q.off = be; - Q.trigger = he; - Q.ajax = Rn; - Q.find = u; + Q.trigger = ae; + Q.ajax = Ln; + Q.find = f; Q.findAll = x; Q.closest = g; Q.remove = z; @@ -84,43 +86,43 @@ var htmx = (function () { Q.toggleClass = W; Q.takeClass = Z; Q.swap = $e; - Q.defineExtension = Fn; - Q.removeExtension = Bn; + Q.defineExtension = zn; + Q.removeExtension = $n; Q.logAll = V; Q.logNone = _; Q.parseInterval = d; Q._ = e; const n = { addTriggerHandler: St, - bodyContains: le, + bodyContains: se, canAccessLocalStorage: B, findThisElement: Se, - filterValues: hn, + filterValues: yn, swap: $e, hasAttribute: s, - getAttributeValue: te, - getClosestAttributeValue: re, - getClosestMatch: o, - getExpressionVars: En, - getHeaders: fn, - getInputValues: cn, - getInternalData: ie, - getSwapSpecification: gn, + getAttributeValue: a, + getClosestAttributeValue: ne, + getClosestMatch: q, + getExpressionVars: Tn, + getHeaders: mn, + getInputValues: dn, + getInternalData: oe, + getSwapSpecification: bn, getTriggerSpecs: st, getTarget: Ee, makeFragment: P, - mergeObjects: ce, - makeSettleInfo: xn, - oobSwap: He, - querySelectorExt: ae, - settleImmediately: Kt, + mergeObjects: le, + makeSettleInfo: Sn, + oobSwap: Re, + querySelectorExt: ue, + settleImmediately: Yt, shouldCancel: ht, - triggerEvent: he, + triggerEvent: ae, triggerErrorEvent: fe, - withExtensions: Ft, + withExtensions: jt, }; - const r = ["get", "post", "put", "delete", "patch"]; - const H = r + const de = ["get", "post", "put", "delete", "patch"]; + const T = de .map(function (e) { return "[hx-" + e + "], [data-hx-" + e + "]"; }) @@ -149,30 +151,30 @@ var htmx = (function () { !!e.hasAttribute && (e.hasAttribute(t) || e.hasAttribute("data-" + t)) ); } - function te(e, t) { + function a(e, t) { return ee(e, t) || ee(e, "data-" + t); } - function c(e) { + function u(e) { const t = e.parentElement; if (!t && e.parentNode instanceof ShadowRoot) return e.parentNode; return t; } - function ne() { + function te() { return document; } - function m(e, t) { - return e.getRootNode ? e.getRootNode({ composed: t }) : ne(); + function y(e, t) { + return e.getRootNode ? e.getRootNode({ composed: t }) : te(); } - function o(e, t) { + function q(e, t) { while (e && !t(e)) { - e = c(e); + e = u(e); } return e || null; } - function i(e, t, n) { - const r = te(t, n); - const o = te(t, "hx-disinherit"); - var i = te(t, "hx-inherit"); + function o(e, t, n) { + const r = a(t, n); + const o = a(t, "hx-disinherit"); + var i = a(t, "hx-inherit"); if (e !== t) { if (Q.config.disableInheritance) { if (i && (i === "*" || i.split(" ").indexOf(n) >= 0)) { @@ -187,27 +189,19 @@ var htmx = (function () { } return r; } - function re(t, n) { + function ne(t, n) { let r = null; - o(t, function (e) { - return !!(r = i(t, ue(e), n)); + q(t, function (e) { + return !!(r = o(t, ce(e), n)); }); if (r !== "unset") { return r; } } function h(e, t) { - const n = - e instanceof Element && - (e.matches || - e.matchesSelector || - e.msMatchesSelector || - e.mozMatchesSelector || - e.webkitMatchesSelector || - e.oMatchesSelector); - return !!n && n.call(e, t); - } - function T(e) { + return e instanceof Element && e.matches(t); + } + function A(e) { const t = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i; const n = t.exec(e); if (n) { @@ -216,18 +210,18 @@ var htmx = (function () { return ""; } } - function q(e) { + function L(e) { const t = new DOMParser(); return t.parseFromString(e, "text/html"); } - function L(e, t) { + function N(e, t) { while (t.childNodes.length > 0) { e.append(t.childNodes[0]); } } - function A(e) { - const t = ne().createElement("script"); - se(e.attributes, function (e) { + function r(e) { + const t = te().createElement("script"); + ie(e.attributes, function (e) { t.setAttribute(e.name, e.value); }); t.textContent = e.textContent; @@ -237,7 +231,7 @@ var htmx = (function () { } return t; } - function N(e) { + function i(e) { return ( e.matches("script") && (e.type === "text/javascript" || e.type === "module" || e.type === "") @@ -245,13 +239,13 @@ var htmx = (function () { } function I(e) { Array.from(e.querySelectorAll("script")).forEach((e) => { - if (N(e)) { - const t = A(e); + if (i(e)) { + const t = r(e); const n = e.parentNode; try { n.insertBefore(t, e); } catch (e) { - O(e); + H(e); } finally { e.remove(); } @@ -260,20 +254,20 @@ var htmx = (function () { } function P(e) { const t = e.replace(/]*)?>[\s\S]*?<\/head>/i, ""); - const n = T(t); + const n = A(t); let r; if (n === "html") { r = new DocumentFragment(); - const i = q(e); - L(r, i.body); + const i = L(e); + N(r, i.body); r.title = i.title; } else if (n === "body") { r = new DocumentFragment(); - const i = q(t); - L(r, i.body); + const i = L(t); + N(r, i.body); r.title = i.title; } else { - const i = q( + const i = L( '", @@ -295,7 +289,7 @@ var htmx = (function () { } return r; } - function oe(e) { + function re(e) { if (e) { e(); } @@ -303,13 +297,13 @@ var htmx = (function () { function t(e, t) { return Object.prototype.toString.call(e) === "[object " + t + "]"; } - function k(e) { + function D(e) { return typeof e === "function"; } - function D(e) { + function k(e) { return t(e, "Object"); } - function ie(e) { + function oe(e) { const t = "htmx-internal-data"; let n = e[t]; if (!n) { @@ -326,26 +320,26 @@ var htmx = (function () { } return n; } - function se(t, n) { + function ie(t, n) { if (t) { for (let e = 0; e < t.length; e++) { n(t[e]); } } } - function X(e) { + function F(e) { const t = e.getBoundingClientRect(); const n = t.top; const r = t.bottom; return n < window.innerHeight && r >= 0; } - function le(e) { + function se(e) { return e.getRootNode({ composed: true }) === document; } - function F(e) { + function X(e) { return e.trim().split(/\s+/); } - function ce(e, t) { + function le(e, t) { for (const n in t) { if (t.hasOwnProperty(n)) { e[n] = t[n]; @@ -353,40 +347,36 @@ var htmx = (function () { } return e; } - function S(e) { + function v(e) { try { return JSON.parse(e); } catch (e) { - O(e); + H(e); return null; } } function B() { - const e = "htmx:localStorageTest"; + const e = "htmx:sessionStorageTest"; try { - localStorage.setItem(e, e); - localStorage.removeItem(e); + sessionStorage.setItem(e, e); + sessionStorage.removeItem(e); return true; } catch (e) { return false; } } - function U(t) { - try { - const e = new URL(t); - if (e) { - t = e.pathname + e.search; - } - if (!/^\/$/.test(t)) { - t = t.replace(/\/+$/, ""); - } - return t; - } catch (e) { - return t; + function U(e) { + const t = new URL(e, "http://x"); + if (t) { + e = t.pathname + t.search; } + if (e != "/") { + e = e.replace(/\/+$/, ""); + } + return e; } function e(e) { - return vn(ne().body, function () { + return On(te().body, function () { return eval(e); }); } @@ -406,35 +396,35 @@ var htmx = (function () { function _() { Q.logger = null; } - function u(e, t) { + function f(e, t) { if (typeof e !== "string") { return e.querySelector(t); } else { - return u(ne(), e); + return f(te(), e); } } function x(e, t) { if (typeof e !== "string") { return e.querySelectorAll(t); } else { - return x(ne(), e); + return x(te(), e); } } - function E() { + function b() { return window; } function z(e, t) { - e = y(e); + e = w(e); if (t) { - E().setTimeout(function () { + b().setTimeout(function () { z(e); e = null; }, t); } else { - c(e).removeChild(e); + u(e).removeChild(e); } } - function ue(e) { + function ce(e) { return e instanceof Element ? e : null; } function $(e) { @@ -443,7 +433,7 @@ var htmx = (function () { function J(e) { return typeof e === "string" ? e : null; } - function f(e) { + function p(e) { return e instanceof Element || e instanceof Document || e instanceof DocumentFragment @@ -451,12 +441,12 @@ var htmx = (function () { : null; } function K(e, t, n) { - e = ue(y(e)); + e = ce(w(e)); if (!e) { return; } if (n) { - E().setTimeout(function () { + b().setTimeout(function () { K(e, t); e = null; }, n); @@ -465,12 +455,12 @@ var htmx = (function () { } } function G(e, t, n) { - let r = ue(y(e)); + let r = ce(w(e)); if (!r) { return; } if (n) { - E().setTimeout(function () { + b().setTimeout(function () { G(r, t); r = null; }, n); @@ -484,28 +474,22 @@ var htmx = (function () { } } function W(e, t) { - e = y(e); + e = w(e); e.classList.toggle(t); } function Z(e, t) { - e = y(e); - se(e.parentElement.children, function (e) { + e = w(e); + ie(e.parentElement.children, function (e) { G(e, t); }); - K(ue(e), t); + K(ce(e), t); } function g(e, t) { - e = ue(y(e)); - if (e && e.closest) { + e = ce(w(e)); + if (e) { return e.closest(t); - } else { - do { - if (e == null || h(e, t)) { - return e; - } - } while ((e = e && ue(c(e)))); - return null; } + return null; } function l(e, t) { return e.substring(0, t.length) === t; @@ -513,7 +497,7 @@ var htmx = (function () { function Y(e, t) { return e.substring(e.length - t.length) === t; } - function ge(e) { + function pe(e) { const t = e.trim(); if (l(t, "<") && Y(t, "/>")) { return t.substring(1, t.length - 2); @@ -521,11 +505,11 @@ var htmx = (function () { return t; } } - function p(t, r, n) { + function m(t, r, n) { if (r.indexOf("global ") === 0) { - return p(t, r.slice(7), true); + return m(t, r.slice(7), true); } - t = y(t); + t = w(t); const o = []; { let t = 0; @@ -550,20 +534,20 @@ var htmx = (function () { const i = []; const s = []; while (o.length > 0) { - const r = ge(o.shift()); + const r = pe(o.shift()); let e; if (r.indexOf("closest ") === 0) { - e = g(ue(t), ge(r.substr(8))); + e = g(ce(t), pe(r.slice(8))); } else if (r.indexOf("find ") === 0) { - e = u(f(t), ge(r.substr(5))); + e = f(p(t), pe(r.slice(5))); } else if (r === "next" || r === "nextElementSibling") { - e = ue(t).nextElementSibling; + e = ce(t).nextElementSibling; } else if (r.indexOf("next ") === 0) { - e = pe(t, ge(r.substr(5)), !!n); + e = ge(t, pe(r.slice(5)), !!n); } else if (r === "previous" || r === "previousElementSibling") { - e = ue(t).previousElementSibling; + e = ce(t).previousElementSibling; } else if (r.indexOf("previous ") === 0) { - e = me(t, ge(r.substr(9)), !!n); + e = me(t, pe(r.slice(9)), !!n); } else if (r === "document") { e = document; } else if (r === "window") { @@ -571,7 +555,7 @@ var htmx = (function () { } else if (r === "body") { e = document.body; } else if (r === "root") { - e = m(t, !!n); + e = y(t, !!n); } else if (r === "host") { e = t.getRootNode().host; } else { @@ -583,13 +567,13 @@ var htmx = (function () { } if (s.length > 0) { const e = s.join(","); - const c = f(m(t, !!n)); + const c = p(y(t, !!n)); i.push(...M(c.querySelectorAll(e))); } return i; } - var pe = function (t, e, n) { - const r = f(m(t, n)).querySelectorAll(e); + var ge = function (t, e, n) { + const r = p(y(t, n)).querySelectorAll(e); for (let e = 0; e < r.length; e++) { const o = r[e]; if (o.compareDocumentPosition(t) === Node.DOCUMENT_POSITION_PRECEDING) { @@ -598,7 +582,7 @@ var htmx = (function () { } }; var me = function (t, e, n) { - const r = f(m(t, n)).querySelectorAll(e); + const r = p(y(t, n)).querySelectorAll(e); for (let e = r.length - 1; e >= 0; e--) { const o = r[e]; if (o.compareDocumentPosition(t) === Node.DOCUMENT_POSITION_FOLLOWING) { @@ -606,52 +590,63 @@ var htmx = (function () { } } }; - function ae(e, t) { + function ue(e, t) { if (typeof e !== "string") { - return p(e, t)[0]; + return m(e, t)[0]; } else { - return p(ne().body, e)[0]; + return m(te().body, e)[0]; } } - function y(e, t) { + function w(e, t) { if (typeof e === "string") { - return u(f(t) || document, e); + return f(p(t) || document, e); } else { return e; } } - function xe(e, t, n, r) { - if (k(t)) { - return { target: ne().body, event: J(e), listener: t, options: n }; + function ye(e, t, n, r) { + if (D(t)) { + return { target: te().body, event: J(e), listener: t, options: n }; } else { - return { target: y(e), event: J(t), listener: n, options: r }; + return { target: w(e), event: J(t), listener: n, options: r }; } } - function ye(t, n, r, o) { - Vn(function () { - const e = xe(t, n, r, o); + function xe(t, n, r, o) { + Gn(function () { + const e = ye(t, n, r, o); e.target.addEventListener(e.event, e.listener, e.options); }); - const e = k(n); + const e = D(n); return e ? n : r; } function be(t, n, r) { - Vn(function () { - const e = xe(t, n, r); + Gn(function () { + const e = ye(t, n, r); e.target.removeEventListener(e.event, e.listener); }); - return k(n) ? n : r; + return D(n) ? n : r; } - const ve = ne().createElement("output"); - function we(e, t) { - const n = re(e, t); - if (n) { - if (n === "this") { - return [Se(e, t)]; + const ve = te().createElement("output"); + function we(t, n) { + const e = ne(t, n); + if (e) { + if (e === "this") { + return [Se(t, n)]; } else { - const r = p(e, n); + const r = m(t, e); + const o = /(^|,)(\s*)inherit(\s*)($|,)/.test(e); + if (o) { + const i = ce( + q(t, function (e) { + return e !== t && s(ce(e), n); + }), + ); + if (i) { + r.push(...we(i, n)); + } + } if (r.length === 0) { - O('The selector "' + n + '" on ' + t + " returned no matches!"); + H('The selector "' + e + '" on ' + n + " returned no matches!"); return [ve]; } else { return r; @@ -660,52 +655,46 @@ var htmx = (function () { } } function Se(e, t) { - return ue( - o(e, function (e) { - return te(ue(e), t) != null; + return ce( + q(e, function (e) { + return a(ce(e), t) != null; }), ); } function Ee(e) { - const t = re(e, "hx-target"); + const t = ne(e, "hx-target"); if (t) { if (t === "this") { return Se(e, "hx-target"); } else { - return ae(e, t); + return ue(e, t); } } else { - const n = ie(e); + const n = oe(e); if (n.boosted) { - return ne().body; + return te().body; } else { return e; } } } - function Ce(t) { - const n = Q.config.attributesToSettle; - for (let e = 0; e < n.length; e++) { - if (t === n[e]) { - return true; - } - } - return false; + function Ce(e) { + return Q.config.attributesToSettle.includes(e); } function Oe(t, n) { - se(t.attributes, function (e) { + ie(t.attributes, function (e) { if (!n.hasAttribute(e.name) && Ce(e.name)) { t.removeAttribute(e.name); } }); - se(n.attributes, function (e) { + ie(n.attributes, function (e) { if (Ce(e.name)) { t.setAttribute(e.name, e.value); } }); } - function Re(t, e) { - const n = Un(e); + function He(t, e) { + const n = Jn(e); for (let e = 0; e < n.length; e++) { const r = n[e]; try { @@ -713,14 +702,14 @@ var htmx = (function () { return true; } } catch (e) { - O(e); + H(e); } } return t === "outerHTML"; } - function He(e, o, i, t) { - t = t || ne(); - let n = "#" + ee(o, "id"); + function Re(e, o, i, t) { + t = t || te(); + let n = "#" + CSS.escape(ee(o, "id")); let s = "outerHTML"; if (e === "true") { } else if (e.indexOf(":") > 0) { @@ -731,40 +720,40 @@ var htmx = (function () { } o.removeAttribute("hx-swap-oob"); o.removeAttribute("data-hx-swap-oob"); - const r = p(t, n, false); - if (r) { - se(r, function (e) { + const r = m(t, n, false); + if (r.length) { + ie(r, function (e) { let t; const n = o.cloneNode(true); - t = ne().createDocumentFragment(); + t = te().createDocumentFragment(); t.appendChild(n); - if (!Re(s, e)) { - t = f(n); + if (!He(s, e)) { + t = p(n); } const r = { shouldSwap: true, target: e, fragment: t }; - if (!he(e, "htmx:oobBeforeSwap", r)) return; + if (!ae(e, "htmx:oobBeforeSwap", r)) return; e = r.target; if (r.shouldSwap) { qe(t); _e(s, e, e, t, i); Te(); } - se(i.elts, function (e) { - he(e, "htmx:oobAfterSwap", r); + ie(i.elts, function (e) { + ae(e, "htmx:oobAfterSwap", r); }); }); o.parentNode.removeChild(o); } else { o.parentNode.removeChild(o); - fe(ne().body, "htmx:oobErrorNoTarget", { content: o }); + fe(te().body, "htmx:oobErrorNoTarget", { content: o }); } return e; } function Te() { - const e = u("#--htmx-preserve-pantry--"); + const e = f("#--htmx-preserve-pantry--"); if (e) { for (const t of [...e.children]) { - const n = u("#" + t.id); + const n = f("#" + t.id); n.parentNode.moveBefore(t, n); n.remove(); } @@ -772,18 +761,18 @@ var htmx = (function () { } } function qe(e) { - se(x(e, "[hx-preserve], [data-hx-preserve]"), function (e) { - const t = te(e, "id"); - const n = ne().getElementById(t); + ie(x(e, "[hx-preserve], [data-hx-preserve]"), function (e) { + const t = a(e, "id"); + const n = te().getElementById(t); if (n != null) { if (e.moveBefore) { - let e = u("#--htmx-preserve-pantry--"); + let e = f("#--htmx-preserve-pantry--"); if (e == null) { - ne().body.insertAdjacentHTML( + te().body.insertAdjacentHTML( "afterend", "
", ); - e = u("#--htmx-preserve-pantry--"); + e = f("#--htmx-preserve-pantry--"); } e.moveBefore(n, null); } else { @@ -792,13 +781,13 @@ var htmx = (function () { } }); } - function Le(l, e, c) { - se(e.querySelectorAll("[id]"), function (t) { + function Ae(l, e, c) { + ie(e.querySelectorAll("[id]"), function (t) { const n = ee(t, "id"); if (n && n.length > 0) { const r = n.replace("'", "\\'"); const o = t.tagName.replace(":", "\\:"); - const e = f(l); + const e = p(l); const i = e && e.querySelector(o + "[id='" + r + "']"); if (i && i !== e) { const s = t.cloneNode(); @@ -810,12 +799,12 @@ var htmx = (function () { } }); } - function Ae(e) { + function Le(e) { return function () { G(e, Q.config.addedClass); - kt(ue(e)); - Ne(f(e)); - he(e, "htmx:load"); + Ft(ce(e)); + Ne(p(e)); + ae(e, "htmx:load"); }; } function Ne(e) { @@ -825,14 +814,14 @@ var htmx = (function () { n.focus(); } } - function a(e, t, n, r) { - Le(e, n, r); + function c(e, t, n, r) { + Ae(e, n, r); while (n.childNodes.length > 0) { const o = n.firstChild; - K(ue(o), Q.config.addedClass); + K(ce(o), Q.config.addedClass); e.insertBefore(o, t); if (o.nodeType !== Node.TEXT_NODE && o.nodeType !== Node.COMMENT_NODE) { - r.tasks.push(Ae(o)); + r.tasks.push(Le(o)); } } } @@ -845,19 +834,17 @@ var htmx = (function () { } function Pe(t) { let n = 0; - if (t.attributes) { - for (let e = 0; e < t.attributes.length; e++) { - const r = t.attributes[e]; - if (r.value) { - n = Ie(r.name, n); - n = Ie(r.value, n); - } + for (let e = 0; e < t.attributes.length; e++) { + const r = t.attributes[e]; + if (r.value) { + n = Ie(r.name, n); + n = Ie(r.value, n); } } return n; } - function ke(t) { - const n = ie(t); + function De(t) { + const n = oe(t); if (n.onHandlers) { for (let e = 0; e < n.onHandlers.length; e++) { const r = n.onHandlers[e]; @@ -866,43 +853,41 @@ var htmx = (function () { delete n.onHandlers; } } - function De(e) { - const t = ie(e); + function ke(e) { + const t = oe(e); if (t.timeout) { clearTimeout(t.timeout); } if (t.listenerInfos) { - se(t.listenerInfos, function (e) { + ie(t.listenerInfos, function (e) { if (e.on) { be(e.on, e.trigger, e.listener); } }); } - ke(e); - se(Object.keys(t), function (e) { + De(e); + ie(Object.keys(t), function (e) { if (e !== "firstInitCompleted") delete t[e]; }); } - function b(e) { - he(e, "htmx:beforeCleanupElement"); - De(e); - if (e.children) { - se(e.children, function (e) { - b(e); - }); - } + function S(e) { + ae(e, "htmx:beforeCleanupElement"); + ke(e); + ie(e.children, function (e) { + S(e); + }); } function Me(t, e, n) { - if (t instanceof Element && t.tagName === "BODY") { + if (t.tagName === "BODY") { return Ve(t, e, n); } let r; const o = t.previousSibling; - const i = c(t); + const i = u(t); if (!i) { return; } - a(i, t, e, n); + c(i, t, e, n); if (o == null) { r = i.firstChild; } else { @@ -917,41 +902,37 @@ var htmx = (function () { } r = r.nextSibling; } - b(t); - if (t instanceof Element) { - t.remove(); - } else { - t.parentNode.removeChild(t); - } - } - function Xe(e, t, n) { - return a(e, e.firstChild, t, n); + S(t); + t.remove(); } function Fe(e, t, n) { - return a(c(e), e, t, n); + return c(e, e.firstChild, t, n); + } + function Xe(e, t, n) { + return c(u(e), e, t, n); } function Be(e, t, n) { - return a(e, null, t, n); + return c(e, null, t, n); } function Ue(e, t, n) { - return a(c(e), e.nextSibling, t, n); + return c(u(e), e.nextSibling, t, n); } function je(e) { - b(e); - const t = c(e); + S(e); + const t = u(e); if (t) { return t.removeChild(e); } } function Ve(e, t, n) { const r = e.firstChild; - a(e, r, t, n); + c(e, r, t, n); if (r) { while (r.nextSibling) { - b(r.nextSibling); + S(r.nextSibling); e.removeChild(r.nextSibling); } - b(r); + S(r); e.removeChild(r); } } @@ -963,10 +944,10 @@ var htmx = (function () { Me(n, r, o); return; case "afterbegin": - Xe(n, r, o); + Fe(n, r, o); return; case "beforebegin": - Fe(n, r, o); + Xe(n, r, o); return; case "beforeend": Be(n, r, o); @@ -978,7 +959,7 @@ var htmx = (function () { je(n); return; default: - var i = Un(e); + var i = Jn(e); for (let e = 0; e < i.length; e++) { const s = i[e]; try { @@ -991,14 +972,14 @@ var htmx = (function () { c.nodeType !== Node.TEXT_NODE && c.nodeType !== Node.COMMENT_NODE ) { - o.tasks.push(Ae(c)); + o.tasks.push(Le(c)); } } } return; } } catch (e) { - O(e); + H(e); } } if (t === "innerHTML") { @@ -1010,11 +991,11 @@ var htmx = (function () { } function ze(e, n, r) { var t = x(e, "[hx-swap-oob], [data-hx-swap-oob]"); - se(t, function (e) { + ie(t, function (e) { if (Q.config.allowNestedOobSwaps || e.parentElement === null) { - const t = te(e, "hx-swap-oob"); + const t = a(e, "hx-swap-oob"); if (t != null) { - He(t, e, n, r); + Re(t, e, n, r); } } else { e.removeAttribute("hx-swap-oob"); @@ -1023,144 +1004,181 @@ var htmx = (function () { }); return t.length > 0; } - function $e(e, t, r, o) { - if (!o) { - o = {}; - } - e = y(e); - const i = o.contextElement ? m(o.contextElement, false) : ne(); - const n = document.activeElement; - let s = {}; - try { - s = { - elt: n, - start: n ? n.selectionStart : null, - end: n ? n.selectionEnd : null, + function $e(h, d, p, g) { + if (!g) { + g = {}; + } + let m = null; + let n = null; + let e = function () { + re(g.beforeSwapCallback); + h = w(h); + const r = g.contextElement ? y(g.contextElement, false) : te(); + const e = document.activeElement; + let t = {}; + t = { + elt: e, + start: e ? e.selectionStart : null, + end: e ? e.selectionEnd : null, }; - } catch (e) {} - const l = xn(e); - if (r.swapStyle === "textContent") { - e.textContent = t; - } else { - let n = P(t); - l.title = n.title; - if (o.selectOOB) { - const u = o.selectOOB.split(","); - for (let t = 0; t < u.length; t++) { - const a = u[t].split(":", 2); - let e = a[0].trim(); - if (e.indexOf("#") === 0) { - e = e.substring(1); - } - const f = a[1] || "true"; - const h = n.querySelector("#" + e); - if (h) { - He(f, h, l, i); + const o = Sn(h); + if (p.swapStyle === "textContent") { + h.textContent = d; + } else { + let n = P(d); + o.title = g.title || n.title; + if (g.historyRequest) { + n = n.querySelector("[hx-history-elt],[data-hx-history-elt]") || n; + } + if (g.selectOOB) { + const i = g.selectOOB.split(","); + for (let t = 0; t < i.length; t++) { + const s = i[t].split(":", 2); + let e = s[0].trim(); + if (e.indexOf("#") === 0) { + e = e.substring(1); + } + const l = s[1] || "true"; + const c = n.querySelector("#" + e); + if (c) { + Re(l, c, o, r); + } } } - } - ze(n, l, i); - se(x(n, "template"), function (e) { - if (e.content && ze(e.content, l, i)) { - e.remove(); - } - }); - if (o.select) { - const d = ne().createDocumentFragment(); - se(n.querySelectorAll(o.select), function (e) { - d.appendChild(e); + ze(n, o, r); + ie(x(n, "template"), function (e) { + if (e.content && ze(e.content, o, r)) { + e.remove(); + } }); - n = d; - } - qe(n); - _e(r.swapStyle, o.contextElement, e, n, l); - Te(); - } - if (s.elt && !le(s.elt) && ee(s.elt, "id")) { - const g = document.getElementById(ee(s.elt, "id")); - const p = { - preventScroll: - r.focusScroll !== undefined - ? !r.focusScroll - : !Q.config.defaultFocusScroll, - }; - if (g) { - if (s.start && g.setSelectionRange) { - try { - g.setSelectionRange(s.start, s.end); - } catch (e) {} + if (g.select) { + const u = te().createDocumentFragment(); + ie(n.querySelectorAll(g.select), function (e) { + u.appendChild(e); + }); + n = u; } - g.focus(p); + qe(n); + _e(p.swapStyle, g.contextElement, h, n, o); + Te(); } - } - e.classList.remove(Q.config.swappingClass); - se(l.elts, function (e) { - if (e.classList) { - e.classList.add(Q.config.settlingClass); + if (t.elt && !se(t.elt) && ee(t.elt, "id")) { + const f = document.getElementById(ee(t.elt, "id")); + const a = { + preventScroll: + p.focusScroll !== undefined + ? !p.focusScroll + : !Q.config.defaultFocusScroll, + }; + if (f) { + if (t.start && f.setSelectionRange) { + try { + f.setSelectionRange(t.start, t.end); + } catch (e) {} + } + f.focus(a); + } } - he(e, "htmx:afterSwap", o.eventInfo); - }); - if (o.afterSwapCallback) { - o.afterSwapCallback(); - } - if (!r.ignoreTitle) { - kn(l.title); - } - const c = function () { - se(l.tasks, function (e) { - e.call(); - }); - se(l.elts, function (e) { + h.classList.remove(Q.config.swappingClass); + ie(o.elts, function (e) { if (e.classList) { - e.classList.remove(Q.config.settlingClass); + e.classList.add(Q.config.settlingClass); } - he(e, "htmx:afterSettle", o.eventInfo); + ae(e, "htmx:afterSwap", g.eventInfo); }); - if (o.anchor) { - const e = ue(y("#" + o.anchor)); - if (e) { - e.scrollIntoView({ block: "start", behavior: "auto" }); - } + re(g.afterSwapCallback); + if (!p.ignoreTitle) { + Bn(o.title); } - yn(l.elts, r); - if (o.afterSettleCallback) { - o.afterSettleCallback(); + const n = function () { + ie(o.tasks, function (e) { + e.call(); + }); + ie(o.elts, function (e) { + if (e.classList) { + e.classList.remove(Q.config.settlingClass); + } + ae(e, "htmx:afterSettle", g.eventInfo); + }); + if (g.anchor) { + const e = ce(w("#" + g.anchor)); + if (e) { + e.scrollIntoView({ block: "start", behavior: "auto" }); + } + } + En(o.elts, p); + re(g.afterSettleCallback); + re(m); + }; + if (p.settleDelay > 0) { + b().setTimeout(n, p.settleDelay); + } else { + n(); } }; - if (r.settleDelay > 0) { - E().setTimeout(c, r.settleDelay); - } else { - c(); + let t = Q.config.globalViewTransitions; + if (p.hasOwnProperty("transition")) { + t = p.transition; + } + const r = g.contextElement || te(); + if ( + t && + ae(r, "htmx:beforeTransition", g.eventInfo) && + typeof Promise !== "undefined" && + document.startViewTransition + ) { + const o = new Promise(function (e, t) { + m = e; + n = t; + }); + const i = e; + e = function () { + document.startViewTransition(function () { + i(); + return o; + }); + }; + } + try { + if (p?.swapDelay && p.swapDelay > 0) { + b().setTimeout(e, p.swapDelay); + } else { + e(); + } + } catch (e) { + fe(r, "htmx:swapError", g.eventInfo); + re(n); + throw e; } } function Je(e, t, n) { const r = e.getResponseHeader(t); if (r.indexOf("{") === 0) { - const o = S(r); + const o = v(r); for (const i in o) { if (o.hasOwnProperty(i)) { let e = o[i]; - if (D(e)) { + if (k(e)) { n = e.target !== undefined ? e.target : n; } else { e = { value: e }; } - he(n, i, e); + ae(n, i, e); } } } else { const s = r.split(","); for (let e = 0; e < s.length; e++) { - he(n, s[e].trim(), []); + ae(n, s[e].trim(), []); } } } const Ke = /\s/; - const v = /[\s,]/; + const E = /[\s,]/; const Ge = /[_$a-zA-Z]/; const We = /[_$a-zA-Z0-9]/; const Ze = ['"', "'", "/"]; - const w = /[^\s]/; + const C = /[^\s]/; const Ye = /[{(]/; const Qe = /[})]/; function et(e) { @@ -1219,7 +1237,7 @@ var htmx = (function () { o.shift(); t += ")})"; try { - const l = vn( + const l = On( r, function () { return Function(t)(); @@ -1231,7 +1249,7 @@ var htmx = (function () { l.source = t; return l; } catch (e) { - fe(ne().body, "htmx:syntax:error", { error: e, source: t }); + fe(te().body, "htmx:syntax:error", { error: e, source: t }); return null; } } @@ -1258,7 +1276,7 @@ var htmx = (function () { } } } - function C(e, t) { + function O(e, t) { let n = ""; while (e.length > 0 && !t.test(e[0])) { n += e.shift(); @@ -1269,10 +1287,10 @@ var htmx = (function () { let t; if (e.length > 0 && Ye.test(e[0])) { e.shift(); - t = C(e, Qe).trim(); + t = O(e, Qe).trim(); e.shift(); } else { - t = C(e, v); + t = O(e, E); } return t; } @@ -1281,44 +1299,44 @@ var htmx = (function () { const r = []; const o = et(t); do { - C(o, w); + O(o, C); const l = o.length; - const c = C(o, /[,\[\s]/); + const c = O(o, /[,\[\s]/); if (c !== "") { if (c === "every") { const u = { trigger: "every" }; - C(o, w); - u.pollInterval = d(C(o, /[,\[\s]/)); - C(o, w); + O(o, C); + u.pollInterval = d(O(o, /[,\[\s]/)); + O(o, C); var i = nt(e, o, "event"); if (i) { u.eventFilter = i; } r.push(u); } else { - const a = { trigger: c }; + const f = { trigger: c }; var i = nt(e, o, "event"); if (i) { - a.eventFilter = i; + f.eventFilter = i; } - C(o, w); + O(o, C); while (o.length > 0 && o[0] !== ",") { - const f = o.shift(); - if (f === "changed") { - a.changed = true; - } else if (f === "once") { - a.once = true; - } else if (f === "consume") { - a.consume = true; - } else if (f === "delay" && o[0] === ":") { + const a = o.shift(); + if (a === "changed") { + f.changed = true; + } else if (a === "once") { + f.once = true; + } else if (a === "consume") { + f.consume = true; + } else if (a === "delay" && o[0] === ":") { o.shift(); - a.delay = d(C(o, v)); - } else if (f === "from" && o[0] === ":") { + f.delay = d(O(o, E)); + } else if (a === "from" && o[0] === ":") { o.shift(); if (Ye.test(o[0])) { var s = rt(o); } else { - var s = C(o, v); + var s = O(o, E); if ( s === "closest" || s === "find" || @@ -1332,34 +1350,34 @@ var htmx = (function () { } } } - a.from = s; - } else if (f === "target" && o[0] === ":") { + f.from = s; + } else if (a === "target" && o[0] === ":") { o.shift(); - a.target = rt(o); - } else if (f === "throttle" && o[0] === ":") { + f.target = rt(o); + } else if (a === "throttle" && o[0] === ":") { o.shift(); - a.throttle = d(C(o, v)); - } else if (f === "queue" && o[0] === ":") { + f.throttle = d(O(o, E)); + } else if (a === "queue" && o[0] === ":") { o.shift(); - a.queue = C(o, v); - } else if (f === "root" && o[0] === ":") { + f.queue = O(o, E); + } else if (a === "root" && o[0] === ":") { o.shift(); - a[f] = rt(o); - } else if (f === "threshold" && o[0] === ":") { + f[a] = rt(o); + } else if (a === "threshold" && o[0] === ":") { o.shift(); - a[f] = C(o, v); + f[a] = O(o, E); } else { fe(e, "htmx:syntax:error", { token: o.shift() }); } - C(o, w); + O(o, C); } - r.push(a); + r.push(f); } } if (o.length === l) { fe(e, "htmx:syntax:error", { token: o.shift() }); } - C(o, w); + O(o, C); } while (o[0] === "," && o.shift()); if (n) { n[t] = r; @@ -1367,7 +1385,7 @@ var htmx = (function () { return r; } function st(e) { - const t = te(e, "hx-trigger"); + const t = a(e, "hx-trigger"); let n = []; if (t) { const r = Q.config.triggerSpecsCache; @@ -1386,13 +1404,13 @@ var htmx = (function () { } } function lt(e) { - ie(e).cancelled = true; + oe(e).cancelled = true; } function ct(e, t, n) { - const r = ie(e); - r.timeout = E().setTimeout(function () { - if (le(e) && r.cancelled !== true) { - if (!gt(n, e, Mt("hx:poll:trigger", { triggerSpec: n, target: e }))) { + const r = oe(e); + r.timeout = b().setTimeout(function () { + if (se(e) && r.cancelled !== true) { + if (!pt(n, e, Bt("hx:poll:trigger", { triggerSpec: n, target: e }))) { t(e); } ct(e, t, n); @@ -1406,10 +1424,10 @@ var htmx = (function () { ee(e, "href").indexOf("#") !== 0 ); } - function at(e) { + function ft(e) { return g(e, Q.config.disableSelector); } - function ft(t, n, e) { + function at(t, n, e) { if ( (t instanceof HTMLAnchorElement && ut(t) && @@ -1427,22 +1445,22 @@ var htmx = (function () { r = i ? i.toLowerCase() : "get"; o = ee(t, "action"); if (o == null || o === "") { - o = ne().location.href; + o = location.href; } if (r === "get" && o.includes("?")) { o = o.replace(/\?[^#]+/, ""); } } e.forEach(function (e) { - pt( + gt( t, function (e, t) { - const n = ue(e); - if (at(n)) { - b(n); + const n = ce(e); + if (ft(n)) { + S(n); return; } - de(r, o, n, t); + he(r, o, n, t); }, n, e, @@ -1452,25 +1470,19 @@ var htmx = (function () { } } function ht(e, t) { - const n = ue(t); - if (!n) { - return false; - } if (e.type === "submit" || e.type === "click") { - if (n.tagName === "FORM") { + t = ce(e.target) || t; + if (t.tagName === "FORM") { return true; } - if ( - h(n, 'input[type="submit"], button') && - (h(n, "[form]") || g(n, "form") !== null) - ) { + if (t.form && t.type === "submit") { return true; } if ( - n instanceof HTMLAnchorElement && - n.href && - (n.getAttribute("href") === "#" || - n.getAttribute("href").indexOf("#") !== 0) + t instanceof HTMLAnchorElement && + t.href && + (t.getAttribute("href") === "#" || + t.getAttribute("href").indexOf("#") !== 0) ) { return true; } @@ -1479,60 +1491,60 @@ var htmx = (function () { } function dt(e, t) { return ( - ie(e).boosted && + oe(e).boosted && e instanceof HTMLAnchorElement && t.type === "click" && (t.ctrlKey || t.metaKey) ); } - function gt(e, t, n) { + function pt(e, t, n) { const r = e.eventFilter; if (r) { try { return r.call(t, n) !== true; } catch (e) { const o = r.source; - fe(ne().body, "htmx:eventFilter:error", { error: e, source: o }); + fe(te().body, "htmx:eventFilter:error", { error: e, source: o }); return true; } } return false; } - function pt(l, c, e, u, a) { - const f = ie(l); + function gt(l, c, e, u, f) { + const a = oe(l); let t; if (u.from) { - t = p(l, u.from); + t = m(l, u.from); } else { t = [l]; } if (u.changed) { - if (!("lastValue" in f)) { - f.lastValue = new WeakMap(); + if (!("lastValue" in a)) { + a.lastValue = new WeakMap(); } t.forEach(function (e) { - if (!f.lastValue.has(u)) { - f.lastValue.set(u, new WeakMap()); + if (!a.lastValue.has(u)) { + a.lastValue.set(u, new WeakMap()); } - f.lastValue.get(u).set(e, e.value); + a.lastValue.get(u).set(e, e.value); }); } - se(t, function (i) { + ie(t, function (i) { const s = function (e) { - if (!le(l)) { + if (!se(l)) { i.removeEventListener(u.trigger, s); return; } if (dt(l, e)) { return; } - if (a || ht(e, l)) { + if (f || ht(e, l)) { e.preventDefault(); } - if (gt(u, l, e)) { + if (pt(u, l, e)) { return; } - const t = ie(e); + const t = oe(e); t.triggerSpec = u; if (t.handledFor == null) { t.handledFor = []; @@ -1543,47 +1555,47 @@ var htmx = (function () { e.stopPropagation(); } if (u.target && e.target) { - if (!h(ue(e.target), u.target)) { + if (!h(ce(e.target), u.target)) { return; } } if (u.once) { - if (f.triggeredOnce) { + if (a.triggeredOnce) { return; } else { - f.triggeredOnce = true; + a.triggeredOnce = true; } } if (u.changed) { - const n = event.target; + const n = e.target; const r = n.value; - const o = f.lastValue.get(u); + const o = a.lastValue.get(u); if (o.has(n) && o.get(n) === r) { return; } o.set(n, r); } - if (f.delayed) { - clearTimeout(f.delayed); + if (a.delayed) { + clearTimeout(a.delayed); } - if (f.throttle) { + if (a.throttle) { return; } if (u.throttle > 0) { - if (!f.throttle) { - he(l, "htmx:trigger"); + if (!a.throttle) { + ae(l, "htmx:trigger"); c(l, e); - f.throttle = E().setTimeout(function () { - f.throttle = null; + a.throttle = b().setTimeout(function () { + a.throttle = null; }, u.throttle); } } else if (u.delay > 0) { - f.delayed = E().setTimeout(function () { - he(l, "htmx:trigger"); + a.delayed = b().setTimeout(function () { + ae(l, "htmx:trigger"); c(l, e); }, u.delay); } else { - he(l, "htmx:trigger"); + ae(l, "htmx:trigger"); c(l, e); } } @@ -1596,19 +1608,19 @@ var htmx = (function () { }); } let mt = false; - let xt = null; - function yt() { - if (!xt) { - xt = function () { + let yt = null; + function xt() { + if (!yt) { + yt = function () { mt = true; }; - window.addEventListener("scroll", xt); - window.addEventListener("resize", xt); + window.addEventListener("scroll", yt); + window.addEventListener("resize", yt); setInterval(function () { if (mt) { mt = false; - se( - ne().querySelectorAll( + ie( + te().querySelectorAll( "[hx-trigger*='revealed'],[data-hx-trigger*='revealed']", ), function (e) { @@ -1620,16 +1632,16 @@ var htmx = (function () { } } function bt(e) { - if (!s(e, "data-hx-revealed") && X(e)) { + if (!s(e, "data-hx-revealed") && F(e)) { e.setAttribute("data-hx-revealed", "true"); - const t = ie(e); + const t = oe(e); if (t.initHash) { - he(e, "revealed"); + ae(e, "revealed"); } else { e.addEventListener( "htmx:afterProcessNode", function () { - he(e, "revealed"); + ae(e, "revealed"); }, { once: true }, ); @@ -1640,32 +1652,32 @@ var htmx = (function () { const o = function () { if (!n.loaded) { n.loaded = true; - he(e, "htmx:trigger"); + ae(e, "htmx:trigger"); t(e); } }; if (r > 0) { - E().setTimeout(o, r); + b().setTimeout(o, r); } else { o(); } } function wt(t, n, e) { let i = false; - se(r, function (r) { + ie(de, function (r) { if (s(t, "hx-" + r)) { - const o = te(t, "hx-" + r); + const o = a(t, "hx-" + r); i = true; n.path = o; n.verb = r; e.forEach(function (e) { St(t, e, n, function (e, t) { - const n = ue(e); - if (g(n, Q.config.disableSelector)) { - b(n); + const n = ce(e); + if (ft(n)) { + S(n); return; } - de(r, o, n, t); + he(r, o, n, t); }); }); } @@ -1674,13 +1686,13 @@ var htmx = (function () { } function St(r, e, t, n) { if (e.trigger === "revealed") { - yt(); - pt(r, n, t, e); - bt(ue(r)); + xt(); + gt(r, n, t, e); + bt(ce(r)); } else if (e.trigger === "intersect") { const o = {}; if (e.root) { - o.root = ae(r, e.root); + o.root = ue(r, e.root); } if (e.threshold) { o.threshold = parseFloat(e.threshold); @@ -1689,26 +1701,26 @@ var htmx = (function () { for (let e = 0; e < t.length; e++) { const n = t[e]; if (n.isIntersecting) { - he(r, "intersect"); + ae(r, "intersect"); break; } } }, o); - i.observe(ue(r)); - pt(ue(r), n, t, e); + i.observe(ce(r)); + gt(ce(r), n, t, e); } else if (!t.firstInitCompleted && e.trigger === "load") { - if (!gt(e, r, Mt("load", { elt: r }))) { - vt(ue(r), n, t, e.delay); + if (!pt(e, r, Bt("load", { elt: r }))) { + vt(ce(r), n, t, e.delay); } } else if (e.pollInterval > 0) { t.polling = true; - ct(ue(r), n, e); + ct(ce(r), n, e); } else { - pt(r, n, t, e); + gt(r, n, t, e); } } function Et(e) { - const t = ue(e); + const t = ce(e); if (!t) { return false; } @@ -1732,13 +1744,13 @@ var htmx = (function () { ); function Ot(e, t) { if (Et(e)) { - t.push(ue(e)); + t.push(ce(e)); } const n = Ct.evaluate(e); let r = null; - while ((r = n.iterateNext())) t.push(ue(r)); + while ((r = n.iterateNext())) t.push(ce(r)); } - function Rt(e) { + function Ht(e) { const t = []; if (e instanceof DocumentFragment) { for (const n of e.childNodes) { @@ -1749,13 +1761,13 @@ var htmx = (function () { } return t; } - function Ht(e) { + function Rt(e) { if (e.querySelectorAll) { const n = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; const r = []; - for (const i in Mn) { - const s = Mn[i]; + for (const i in Vn) { + const s = Vn[i]; if (s.getSelectors) { var t = s.getSelectors(); if (t) { @@ -1764,7 +1776,7 @@ var htmx = (function () { } } const o = e.querySelectorAll( - H + + T + n + ", form, [type='submit']," + " [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger]" + @@ -1779,43 +1791,46 @@ var htmx = (function () { } } function Tt(e) { - const t = g(ue(e.target), "button, input[type='submit']"); - const n = Lt(e); + const t = At(e.target); + const n = Nt(e); if (n) { n.lastButtonClicked = t; } } function qt(e) { - const t = Lt(e); + const t = Nt(e); if (t) { t.lastButtonClicked = null; } } + function At(e) { + return g(ce(e), "button, input[type='submit']"); + } function Lt(e) { - const t = g(ue(e.target), "button, input[type='submit']"); + return e.form || g(e, "form"); + } + function Nt(e) { + const t = At(e.target); if (!t) { return; } - const n = y("#" + ee(t, "form"), t.getRootNode()) || g(t, "form"); - if (!n) { - return; - } - return ie(n); + const n = Lt(t); + return oe(n); } - function At(e) { + function It(e) { e.addEventListener("click", Tt); e.addEventListener("focusin", Tt); e.addEventListener("focusout", qt); } - function Nt(t, e, n) { - const r = ie(t); + function Pt(t, e, n) { + const r = oe(t); if (!Array.isArray(r.onHandlers)) { r.onHandlers = []; } let o; const i = function (e) { - vn(t, function () { - if (at(t)) { + On(t, function () { + if (ft(t)) { return; } if (!o) { @@ -1827,8 +1842,8 @@ var htmx = (function () { t.addEventListener(e, i); r.onHandlers.push({ event: e, listener: i }); } - function It(t) { - ke(t); + function Dt(t) { + De(t); for (let e = 0; e < t.attributes.length; e++) { const n = t.attributes[e].name; const r = t.attributes[e].value; @@ -1844,138 +1859,144 @@ var htmx = (function () { } else if (l(e, "htmx-")) { e = "htmx:" + e.slice(5); } - Nt(t, e, r); + Pt(t, e, r); } } } } - function Pt(t) { - if (g(t, Q.config.disableSelector)) { - b(t); - return; - } - const n = ie(t); - const e = Pe(t); - if (n.initHash !== e) { - De(t); - n.initHash = e; - he(t, "htmx:beforeProcessNode"); - const r = st(t); - const o = wt(t, n, r); - if (!o) { - if (re(t, "hx-boost") === "true") { - ft(t, n, r); - } else if (s(t, "hx-trigger")) { - r.forEach(function (e) { - St(t, e, n, function () {}); - }); - } - } - if ( - t.tagName === "FORM" || - (ee(t, "type") === "submit" && s(t, "form")) - ) { - At(t); + function kt(t) { + ae(t, "htmx:beforeProcessNode"); + const n = oe(t); + const e = st(t); + const r = wt(t, n, e); + if (!r) { + if (ne(t, "hx-boost") === "true") { + at(t, n, e); + } else if (s(t, "hx-trigger")) { + e.forEach(function (e) { + St(t, e, n, function () {}); + }); } - n.firstInitCompleted = true; - he(t, "htmx:afterProcessNode"); } + if (t.tagName === "FORM" || (ee(t, "type") === "submit" && s(t, "form"))) { + It(t); + } + n.firstInitCompleted = true; + ae(t, "htmx:afterProcessNode"); } - function kt(e) { - e = y(e); - if (g(e, Q.config.disableSelector)) { - b(e); + function Mt(e) { + if (!(e instanceof Element)) { + return false; + } + const t = oe(e); + const n = Pe(e); + if (t.initHash !== n) { + ke(e); + t.initHash = n; + return true; + } + return false; + } + function Ft(e) { + e = w(e); + if (ft(e)) { + S(e); return; } - Pt(e); - se(Ht(e), function (e) { - Pt(e); + const t = []; + if (Mt(e)) { + t.push(e); + } + ie(Rt(e), function (e) { + if (ft(e)) { + S(e); + return; + } + if (Mt(e)) { + t.push(e); + } }); - se(Rt(e), It); + ie(Ht(e), Dt); + ie(t, kt); } - function Dt(e) { + function Xt(e) { return e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); } - function Mt(e, t) { - let n; - if (window.CustomEvent && typeof window.CustomEvent === "function") { - n = new CustomEvent(e, { - bubbles: true, - cancelable: true, - composed: true, - detail: t, - }); - } else { - n = ne().createEvent("CustomEvent"); - n.initCustomEvent(e, true, true, t); - } - return n; + function Bt(e, t) { + return new CustomEvent(e, { + bubbles: true, + cancelable: true, + composed: true, + detail: t, + }); } function fe(e, t, n) { - he(e, t, ce({ error: t }, n)); + ae(e, t, le({ error: t }, n)); } - function Xt(e) { + function Ut(e) { return e === "htmx:afterProcessNode"; } - function Ft(e, t) { - se(Un(e), function (e) { + function jt(e, t, n) { + ie(Jn(e, [], n), function (e) { try { t(e); } catch (e) { - O(e); + H(e); } }); } - function O(e) { - if (console.error) { - console.error(e); - } else if (console.log) { - console.log("ERROR: ", e); - } + function H(e) { + console.error(e); } - function he(e, t, n) { - e = y(e); + function ae(e, t, n) { + e = w(e); if (n == null) { n = {}; } n.elt = e; - const r = Mt(t, n); - if (Q.logger && !Xt(t)) { + const r = Bt(t, n); + if (Q.logger && !Ut(t)) { Q.logger(e, t, n); } if (n.error) { - O(n.error); - he(e, "htmx:error", { errorInfo: n }); + H(n.error); + ae(e, "htmx:error", { errorInfo: n }); } let o = e.dispatchEvent(r); - const i = Dt(t); + const i = Xt(t); if (o && i !== t) { - const s = Mt(i, r.detail); + const s = Bt(i, r.detail); o = o && e.dispatchEvent(s); } - Ft(ue(e), function (e) { + jt(ce(e), function (e) { o = o && e.onEvent(t, r) !== false && !r.defaultPrevented; }); return o; } - let Bt = location.pathname + location.search; - function Ut() { - const e = ne().querySelector("[hx-history-elt],[data-hx-history-elt]"); - return e || ne().body; + let Vt = location.pathname + location.search; + function _t(e) { + Vt = e; + if (B()) { + sessionStorage.setItem("htmx-current-path-for-history", e); + } } - function jt(t, e) { + function zt() { + const e = te().querySelector("[hx-history-elt],[data-hx-history-elt]"); + return e || te().body; + } + function $t(t, e) { if (!B()) { return; } - const n = _t(e); - const r = ne().title; + const n = Kt(e); + const r = te().title; const o = window.scrollY; if (Q.config.historyCacheSize <= 0) { - localStorage.removeItem("htmx-history-cache"); + sessionStorage.removeItem("htmx-history-cache"); return; } t = U(t); - const i = S(localStorage.getItem("htmx-history-cache")) || []; + const i = v(sessionStorage.getItem("htmx-history-cache")) || []; for (let e = 0; e < i.length; e++) { if (i[e].url === t) { i.splice(e, 1); @@ -1983,27 +2004,27 @@ var htmx = (function () { } } const s = { url: t, content: n, title: r, scroll: o }; - he(ne().body, "htmx:historyItemCreated", { item: s, cache: i }); + ae(te().body, "htmx:historyItemCreated", { item: s, cache: i }); i.push(s); while (i.length > Q.config.historyCacheSize) { i.shift(); } while (i.length > 0) { try { - localStorage.setItem("htmx-history-cache", JSON.stringify(i)); + sessionStorage.setItem("htmx-history-cache", JSON.stringify(i)); break; } catch (e) { - fe(ne().body, "htmx:historyCacheError", { cause: e, cache: i }); + fe(te().body, "htmx:historyCacheError", { cause: e, cache: i }); i.shift(); } } } - function Vt(t) { + function Jt(t) { if (!B()) { return null; } t = U(t); - const n = S(localStorage.getItem("htmx-history-cache")) || []; + const n = v(sessionStorage.getItem("htmx-history-cache")) || []; for (let e = 0; e < n.length; e++) { if (n[e].url === t) { return n[e]; @@ -2011,36 +2032,35 @@ var htmx = (function () { } return null; } - function _t(e) { + function Kt(e) { const t = Q.config.requestClass; const n = e.cloneNode(true); - se(x(n, "." + t), function (e) { + ie(x(n, "." + t), function (e) { G(e, t); }); - se(x(n, "[data-disabled-by-htmx]"), function (e) { + ie(x(n, "[data-disabled-by-htmx]"), function (e) { e.removeAttribute("disabled"); }); return n.innerHTML; } - function zt() { - const e = Ut(); - const t = Bt || location.pathname + location.search; - let n; - try { - n = ne().querySelector( - '[hx-history="false" i],[data-hx-history="false" i]', - ); - } catch (e) { - n = ne().querySelector('[hx-history="false"],[data-hx-history="false"]'); + function Gt() { + const e = zt(); + let t = Vt; + if (B()) { + t = sessionStorage.getItem("htmx-current-path-for-history"); } + t = t || location.pathname + location.search; + const n = te().querySelector( + '[hx-history="false" i],[data-hx-history="false" i]', + ); if (!n) { - he(ne().body, "htmx:beforeHistorySave", { path: t, historyElt: e }); - jt(t, e); + ae(te().body, "htmx:beforeHistorySave", { path: t, historyElt: e }); + $t(t, e); } if (Q.config.historyEnabled) - history.replaceState({ htmx: true }, ne().title, window.location.href); + history.replaceState({ htmx: true }, te().title, location.href); } - function $t(e) { + function Wt(e) { if (Q.config.getCacheBusterParam) { e = e.replace(/org\.htmx\.cache-buster=[^&]*&?/, ""); if (Y(e, "&") || Y(e, "?")) { @@ -2050,121 +2070,122 @@ var htmx = (function () { if (Q.config.historyEnabled) { history.pushState({ htmx: true }, "", e); } - Bt = e; + _t(e); } - function Jt(e) { + function Zt(e) { if (Q.config.historyEnabled) history.replaceState({ htmx: true }, "", e); - Bt = e; + _t(e); } - function Kt(e) { - se(e, function (e) { + function Yt(e) { + ie(e, function (e) { e.call(undefined); }); } - function Gt(o) { - const e = new XMLHttpRequest(); - const i = { path: o, xhr: e }; - he(ne().body, "htmx:historyCacheMiss", i); - e.open("GET", o, true); - e.setRequestHeader("HX-Request", "true"); - e.setRequestHeader("HX-History-Restore-Request", "true"); - e.setRequestHeader("HX-Current-URL", ne().location.href); - e.onload = function () { + function Qt(e) { + const t = new XMLHttpRequest(); + const n = { swapStyle: "innerHTML", swapDelay: 0, settleDelay: 0 }; + const r = { path: e, xhr: t, historyElt: zt(), swapSpec: n }; + t.open("GET", e, true); + if (Q.config.historyRestoreAsHxRequest) { + t.setRequestHeader("HX-Request", "true"); + } + t.setRequestHeader("HX-History-Restore-Request", "true"); + t.setRequestHeader("HX-Current-URL", location.href); + t.onload = function () { if (this.status >= 200 && this.status < 400) { - he(ne().body, "htmx:historyCacheMissLoad", i); - const e = P(this.response); - const t = - e.querySelector("[hx-history-elt],[data-hx-history-elt]") || e; - const n = Ut(); - const r = xn(n); - kn(e.title); - qe(e); - Ve(n, t, r); - Te(); - Kt(r.tasks); - Bt = o; - he(ne().body, "htmx:historyRestore", { - path: o, + r.response = this.response; + ae(te().body, "htmx:historyCacheMissLoad", r); + $e(r.historyElt, r.response, n, { + contextElement: r.historyElt, + historyRequest: true, + }); + _t(r.path); + ae(te().body, "htmx:historyRestore", { + path: e, cacheMiss: true, - serverResponse: this.response, + serverResponse: r.response, }); } else { - fe(ne().body, "htmx:historyCacheMissLoadError", i); + fe(te().body, "htmx:historyCacheMissLoadError", r); } }; - e.send(); + if (ae(te().body, "htmx:historyCacheMiss", r)) { + t.send(); + } } - function Wt(e) { - zt(); + function en(e) { + Gt(); e = e || location.pathname + location.search; - const t = Vt(e); + const t = Jt(e); if (t) { - const n = P(t.content); - const r = Ut(); - const o = xn(r); - kn(t.title); - qe(n); - Ve(r, n, o); - Te(); - Kt(o.tasks); - E().setTimeout(function () { - window.scrollTo(0, t.scroll); - }, 0); - Bt = e; - he(ne().body, "htmx:historyRestore", { path: e, item: t }); + const n = { + swapStyle: "innerHTML", + swapDelay: 0, + settleDelay: 0, + scroll: t.scroll, + }; + const r = { path: e, item: t, historyElt: zt(), swapSpec: n }; + if (ae(te().body, "htmx:historyCacheHit", r)) { + $e(r.historyElt, t.content, n, { + contextElement: r.historyElt, + title: t.title, + }); + _t(r.path); + ae(te().body, "htmx:historyRestore", r); + } } else { if (Q.config.refreshOnHistoryMiss) { - window.location.reload(true); + Q.location.reload(true); } else { - Gt(e); + Qt(e); } } } - function Zt(e) { + function tn(e) { let t = we(e, "hx-indicator"); if (t == null) { t = [e]; } - se(t, function (e) { - const t = ie(e); + ie(t, function (e) { + const t = oe(e); t.requestCount = (t.requestCount || 0) + 1; e.classList.add.call(e.classList, Q.config.requestClass); }); return t; } - function Yt(e) { + function nn(e) { let t = we(e, "hx-disabled-elt"); if (t == null) { t = []; } - se(t, function (e) { - const t = ie(e); + ie(t, function (e) { + const t = oe(e); t.requestCount = (t.requestCount || 0) + 1; e.setAttribute("disabled", ""); e.setAttribute("data-disabled-by-htmx", ""); }); return t; } - function Qt(e, t) { - se(e.concat(t), function (e) { - const t = ie(e); + function rn(e, t) { + ie(e.concat(t), function (e) { + const t = oe(e); t.requestCount = (t.requestCount || 1) - 1; }); - se(e, function (e) { - const t = ie(e); + ie(e, function (e) { + const t = oe(e); if (t.requestCount === 0) { e.classList.remove.call(e.classList, Q.config.requestClass); } }); - se(t, function (e) { - const t = ie(e); + ie(t, function (e) { + const t = oe(e); if (t.requestCount === 0) { e.removeAttribute("disabled"); e.removeAttribute("data-disabled-by-htmx"); } }); } - function en(t, n) { + function on(t, n) { for (let e = 0; e < t.length; e++) { const r = t[e]; if (r.isSameNode(n)) { @@ -2173,7 +2194,7 @@ var htmx = (function () { } return false; } - function tn(e) { + function sn(e) { const t = e; if ( t.name === "" || @@ -2197,7 +2218,7 @@ var htmx = (function () { } return true; } - function nn(t, e, n) { + function ln(t, e, n) { if (t != null && e != null) { if (Array.isArray(e)) { e.forEach(function (e) { @@ -2208,7 +2229,7 @@ var htmx = (function () { } } } - function rn(t, n, r) { + function cn(t, n, r) { if (t != null && n != null) { let e = r.getAll(t); if (Array.isArray(n)) { @@ -2217,64 +2238,66 @@ var htmx = (function () { e = e.filter((e) => e !== n); } r.delete(t); - se(e, (e) => r.append(t, e)); + ie(e, (e) => r.append(t, e)); + } + } + function un(e) { + if (e instanceof HTMLSelectElement && e.multiple) { + return M(e.querySelectorAll("option:checked")).map(function (e) { + return e.value; + }); + } + if (e instanceof HTMLInputElement && e.files) { + return M(e.files); } + return e.value; } - function on(t, n, r, o, i) { - if (o == null || en(t, o)) { + function fn(t, n, r, e, o) { + if (e == null || on(t, e)) { return; } else { - t.push(o); - } - if (tn(o)) { - const s = ee(o, "name"); - let e = o.value; - if (o instanceof HTMLSelectElement && o.multiple) { - e = M(o.querySelectorAll("option:checked")).map(function (e) { - return e.value; - }); - } - if (o instanceof HTMLInputElement && o.files) { - e = M(o.files); - } - nn(s, e, n); - if (i) { - sn(o, r); + t.push(e); + } + if (sn(e)) { + const i = ee(e, "name"); + ln(i, un(e), n); + if (o) { + an(e, r); } } - if (o instanceof HTMLFormElement) { - se(o.elements, function (e) { + if (e instanceof HTMLFormElement) { + ie(e.elements, function (e) { if (t.indexOf(e) >= 0) { - rn(e.name, e.value, n); + cn(e.name, un(e), n); } else { t.push(e); } - if (i) { - sn(e, r); + if (o) { + an(e, r); } }); - new FormData(o).forEach(function (e, t) { + new FormData(e).forEach(function (e, t) { if (e instanceof File && e.name === "") { return; } - nn(t, e, n); + ln(t, e, n); }); } } - function sn(e, t) { + function an(e, t) { const n = e; if (n.willValidate) { - he(n, "htmx:validation:validate"); + ae(n, "htmx:validation:validate"); if (!n.checkValidity()) { t.push({ elt: n, message: n.validationMessage, validity: n.validity }); - he(n, "htmx:validation:failed", { + ae(n, "htmx:validation:failed", { message: n.validationMessage, validity: n.validity, }); } } } - function ln(n, e) { + function hn(n, e) { for (const t of e.keys()) { n.delete(t); } @@ -2283,47 +2306,47 @@ var htmx = (function () { }); return n; } - function cn(e, t) { + function dn(e, t) { const n = []; const r = new FormData(); const o = new FormData(); const i = []; - const s = ie(e); - if (s.lastButtonClicked && !le(s.lastButtonClicked)) { + const s = oe(e); + if (s.lastButtonClicked && !se(s.lastButtonClicked)) { s.lastButtonClicked = null; } let l = (e instanceof HTMLFormElement && e.noValidate !== true) || - te(e, "hx-validate") === "true"; + a(e, "hx-validate") === "true"; if (s.lastButtonClicked) { l = l && s.lastButtonClicked.formNoValidate !== true; } if (t !== "get") { - on(n, o, i, g(e, "form"), l); + fn(n, o, i, Lt(e), l); } - on(n, r, i, e, l); + fn(n, r, i, e, l); if ( s.lastButtonClicked || e.tagName === "BUTTON" || (e.tagName === "INPUT" && ee(e, "type") === "submit") ) { const u = s.lastButtonClicked || e; - const a = ee(u, "name"); - nn(a, u.value, o); + const f = ee(u, "name"); + ln(f, u.value, o); } const c = we(e, "hx-include"); - se(c, function (e) { - on(n, r, i, ue(e), l); + ie(c, function (e) { + fn(n, r, i, ce(e), l); if (!h(e, "form")) { - se(f(e).querySelectorAll(ot), function (e) { - on(n, r, i, e, l); + ie(p(e).querySelectorAll(ot), function (e) { + fn(n, r, i, e, l); }); } }); - ln(r, o); - return { errors: i, formData: r, values: An(r) }; + hn(r, o); + return { errors: i, formData: r, values: kn(r) }; } - function un(e, t, n) { + function pn(e, t, n) { if (e !== "") { e += "&"; } @@ -2334,47 +2357,47 @@ var htmx = (function () { e += encodeURIComponent(t) + "=" + r; return e; } - function an(e) { - e = qn(e); + function gn(e) { + e = Pn(e); let n = ""; e.forEach(function (e, t) { - n = un(n, t, e); + n = pn(n, t, e); }); return n; } - function fn(e, t, n) { + function mn(e, t, n) { const r = { "HX-Request": "true", "HX-Trigger": ee(e, "id"), "HX-Trigger-Name": ee(e, "name"), - "HX-Target": te(t, "id"), - "HX-Current-URL": ne().location.href, + "HX-Target": a(t, "id"), + "HX-Current-URL": location.href, }; - bn(e, "hx-headers", false, r); + Cn(e, "hx-headers", false, r); if (n !== undefined) { r["HX-Prompt"] = n; } - if (ie(e).boosted) { + if (oe(e).boosted) { r["HX-Boosted"] = "true"; } return r; } - function hn(n, e) { - const t = re(e, "hx-params"); + function yn(n, e) { + const t = ne(e, "hx-params"); if (t) { if (t === "none") { return new FormData(); } else if (t === "*") { return n; } else if (t.indexOf("not ") === 0) { - se(t.slice(4).split(","), function (e) { + ie(t.slice(4).split(","), function (e) { e = e.trim(); n.delete(e); }); return n; } else { const r = new FormData(); - se(t.split(","), function (t) { + ie(t.split(","), function (t) { t = t.trim(); if (n.has(t)) { n.getAll(t).forEach(function (e) { @@ -2388,21 +2411,21 @@ var htmx = (function () { return n; } } - function dn(e) { + function xn(e) { return !!ee(e, "href") && ee(e, "href").indexOf("#") >= 0; } - function gn(e, t) { - const n = t || re(e, "hx-swap"); + function bn(e, t) { + const n = t || ne(e, "hx-swap"); const r = { - swapStyle: ie(e).boosted ? "innerHTML" : Q.config.defaultSwapStyle, + swapStyle: oe(e).boosted ? "innerHTML" : Q.config.defaultSwapStyle, swapDelay: Q.config.defaultSwapDelay, settleDelay: Q.config.defaultSettleDelay, }; - if (Q.config.scrollIntoViewOnBoost && ie(e).boosted && !dn(e)) { + if (Q.config.scrollIntoViewOnBoost && oe(e).boosted && !xn(e)) { r.show = "top"; } if (n) { - const s = F(n); + const s = X(n); if (s.length > 0) { for (let e = 0; e < s.length; e++) { const l = s[e]; @@ -2422,11 +2445,11 @@ var htmx = (function () { r.scroll = u; r.scrollTarget = i; } else if (l.indexOf("show:") === 0) { - const a = l.slice(5); - var o = a.split(":"); - const f = o.pop(); + const f = l.slice(5); + var o = f.split(":"); + const a = o.pop(); var i = o.length > 0 ? o.join(":") : null; - r.show = f; + r.show = a; r.showTarget = i; } else if (l.indexOf("focus-scroll:") === 0) { const h = l.slice("focus-scroll:".length); @@ -2434,22 +2457,22 @@ var htmx = (function () { } else if (e == 0) { r.swapStyle = l; } else { - O("Unknown modifier in hx-swap: " + l); + H("Unknown modifier in hx-swap: " + l); } } } } return r; } - function pn(e) { + function vn(e) { return ( - re(e, "hx-encoding") === "multipart/form-data" || + ne(e, "hx-encoding") === "multipart/form-data" || (h(e, "form") && ee(e, "enctype") === "multipart/form-data") ); } - function mn(t, n, r) { + function wn(t, n, r) { let o = null; - Ft(n, function (e) { + jt(n, function (e) { if (o == null) { o = e.encodeParameters(t, r, n); } @@ -2457,23 +2480,23 @@ var htmx = (function () { if (o != null) { return o; } else { - if (pn(n)) { - return ln(new FormData(), qn(r)); + if (vn(n)) { + return hn(new FormData(), Pn(r)); } else { - return an(r); + return gn(r); } } } - function xn(e) { + function Sn(e) { return { tasks: [], elts: [e] }; } - function yn(e, t) { + function En(e, t) { const n = e[0]; const r = e[e.length - 1]; if (t.scroll) { var o = null; if (t.scrollTarget) { - o = ue(ae(n, t.scrollTarget)); + o = ce(ue(n, t.scrollTarget)); } if (t.scroll === "top" && (n || o)) { o = o || n; @@ -2483,6 +2506,11 @@ var htmx = (function () { o = o || r; o.scrollTop = o.scrollHeight; } + if (typeof t.scroll === "number") { + b().setTimeout(function () { + window.scrollTo(0, t.scroll); + }, 0); + } } if (t.show) { var o = null; @@ -2491,7 +2519,7 @@ var htmx = (function () { if (t.showTarget === "window") { e = "body"; } - o = ue(ae(n, e)); + o = ce(ue(n, e)); } if (t.show === "top" && (n || o)) { o = o || n; @@ -2503,16 +2531,16 @@ var htmx = (function () { } } } - function bn(r, e, o, i) { + function Cn(r, e, o, i, s) { if (i == null) { i = {}; } if (r == null) { return i; } - const s = te(r, e); - if (s) { - let e = s.trim(); + const l = a(r, e); + if (l) { + let e = l.trim(); let t = o; if (e === "unset") { return null; @@ -2529,27 +2557,31 @@ var htmx = (function () { } let n; if (t) { - n = vn( + n = On( r, function () { - return Function("return (" + e + ")")(); + if (s) { + return Function("event", "return (" + e + ")").call(r, s); + } else { + return Function("return (" + e + ")").call(r); + } }, {}, ); } else { - n = S(e); + n = v(e); } - for (const l in n) { - if (n.hasOwnProperty(l)) { - if (i[l] == null) { - i[l] = n[l]; + for (const c in n) { + if (n.hasOwnProperty(c)) { + if (i[c] == null) { + i[c] = n[c]; } } } } - return bn(ue(c(r)), e, o, i); + return Cn(ce(u(r)), e, o, i, s); } - function vn(e, t, n) { + function On(e, t, n) { if (Q.config.allowEval) { return t(); } else { @@ -2557,16 +2589,16 @@ var htmx = (function () { return n; } } - function wn(e, t) { - return bn(e, "hx-vars", true, t); + function Hn(e, t, n) { + return Cn(e, "hx-vars", true, n, t); } - function Sn(e, t) { - return bn(e, "hx-vals", false, t); + function Rn(e, t, n) { + return Cn(e, "hx-vals", false, n, t); } - function En(e) { - return ce(wn(e), Sn(e)); + function Tn(e, t) { + return le(Hn(e, t), Rn(e, t)); } - function Cn(t, n, r) { + function qn(t, n, r) { if (r !== null) { try { t.setRequestHeader(n, r); @@ -2576,33 +2608,33 @@ var htmx = (function () { } } } - function On(t) { - if (t.responseURL && typeof URL !== "undefined") { + function An(t) { + if (t.responseURL) { try { const e = new URL(t.responseURL); return e.pathname + e.search; } catch (e) { - fe(ne().body, "htmx:badResponseUrl", { url: t.responseURL }); + fe(te().body, "htmx:badResponseUrl", { url: t.responseURL }); } } } function R(e, t) { return t.test(e.getAllResponseHeaders()); } - function Rn(t, n, r) { + function Ln(t, n, r) { t = t.toLowerCase(); if (r) { if (r instanceof Element || typeof r === "string") { - return de(t, n, null, null, { - targetOverride: y(r) || ve, + return he(t, n, null, null, { + targetOverride: w(r) || ve, returnPromise: true, }); } else { - let e = y(r.target); - if ((r.target && !e) || (r.source && !e && !y(r.source))) { + let e = w(r.target); + if ((r.target && !e) || (r.source && !e && !w(r.source))) { e = ve; } - return de(t, n, y(r.source), r.event, { + return he(t, n, w(r.source), r.event, { handler: r.handler, headers: r.headers, values: r.values, @@ -2613,10 +2645,10 @@ var htmx = (function () { }); } } else { - return de(t, n, null, null, { returnPromise: true }); + return he(t, n, null, null, { returnPromise: true }); } } - function Hn(e) { + function Nn(e) { const t = []; while (e) { t.push(e); @@ -2624,25 +2656,21 @@ var htmx = (function () { } return t; } - function Tn(e, t, n) { - let r; - let o; - if (typeof URL === "function") { - o = new URL(t, document.location.href); - const i = document.location.origin; - r = i === o.origin; - } else { - o = t; - r = l(t, document.location.origin); - } + function In(e, t, n) { + const r = new URL( + t, + location.protocol !== "about:" ? location.href : window.origin, + ); + const o = location.protocol !== "about:" ? location.origin : window.origin; + const i = o === r.origin; if (Q.config.selfRequestsOnly) { - if (!r) { + if (!i) { return false; } } - return he(e, "htmx:validateUrl", ce({ url: o, sameHost: r }, n)); + return ae(e, "htmx:validateUrl", le({ url: r, sameHost: i }, n)); } - function qn(e) { + function Pn(e) { if (e instanceof FormData) return e; const t = new FormData(); for (const n in e) { @@ -2660,7 +2688,7 @@ var htmx = (function () { } return t; } - function Ln(r, o, e) { + function Dn(r, o, e) { return new Proxy(e, { get: function (t, e) { if (typeof e === "number") return t[e]; @@ -2696,7 +2724,7 @@ var htmx = (function () { }, }); } - function An(o) { + function kn(o) { return new Proxy(o, { get: function (e, t) { if (typeof t === "symbol") { @@ -2717,8 +2745,6 @@ var htmx = (function () { return function () { return o[t].apply(o, arguments); }; - } else { - return e[t]; } } const n = o.getAll(t); @@ -2727,7 +2753,7 @@ var htmx = (function () { } else if (n.length === 1) { return n[0]; } else { - return Ln(e, t, n); + return Dn(e, t, n); } }, set: function (t, n, e) { @@ -2760,7 +2786,7 @@ var htmx = (function () { }, }); } - function de(t, n, r, o, i, D) { + function he(t, n, r, o, i, k) { let s = null; let l = null; i = i != null ? i : {}; @@ -2771,38 +2797,41 @@ var htmx = (function () { }); } if (r == null) { - r = ne().body; + r = te().body; } - const M = i.handler || Dn; - const X = i.select || null; - if (!le(r)) { - oe(s); + const M = i.handler || jn; + const F = i.select || null; + if (!se(r)) { + re(s); return e; } - const c = i.targetOverride || ue(Ee(r)); + const c = i.targetOverride || ce(Ee(r)); if (c == null || c == ve) { - fe(r, "htmx:targetError", { target: te(r, "hx-target") }); - oe(l); + fe(r, "htmx:targetError", { target: ne(r, "hx-target") }); + re(l); return e; } - let u = ie(r); - const a = u.lastButtonClicked; - if (a) { - const L = ee(a, "formaction"); - if (L != null) { - n = L; - } - const A = ee(a, "formmethod"); + let u = oe(r); + const f = u.lastButtonClicked; + if (f) { + const A = ee(f, "formaction"); if (A != null) { - if (A.toLowerCase() !== "dialog") { - t = A; + n = A; + } + const L = ee(f, "formmethod"); + if (L != null) { + if (de.includes(L.toLowerCase())) { + t = L; + } else { + re(s); + return e; } } } - const f = re(r, "hx-confirm"); - if (D === undefined) { + const a = ne(r, "hx-confirm"); + if (k === undefined) { const K = function (e) { - return de(t, n, r, o, i, !!e); + return he(t, n, r, o, i, !!e); }; const G = { target: c, @@ -2812,83 +2841,83 @@ var htmx = (function () { triggeringEvent: o, etc: i, issueRequest: K, - question: f, + question: a, }; - if (he(r, "htmx:confirm", G) === false) { - oe(s); + if (ae(r, "htmx:confirm", G) === false) { + re(s); return e; } } let h = r; - let d = re(r, "hx-sync"); - let g = null; - let F = false; + let d = ne(r, "hx-sync"); + let p = null; + let X = false; if (d) { const N = d.split(":"); const I = N[0].trim(); if (I === "this") { h = Se(r, "hx-sync"); } else { - h = ue(ae(r, I)); + h = ce(ue(r, I)); } d = (N[1] || "drop").trim(); - u = ie(h); + u = oe(h); if (d === "drop" && u.xhr && u.abortable !== true) { - oe(s); + re(s); return e; } else if (d === "abort") { if (u.xhr) { - oe(s); + re(s); return e; } else { - F = true; + X = true; } } else if (d === "replace") { - he(h, "htmx:abort"); + ae(h, "htmx:abort"); } else if (d.indexOf("queue") === 0) { const W = d.split(" "); - g = (W[1] || "last").trim(); + p = (W[1] || "last").trim(); } } if (u.xhr) { if (u.abortable) { - he(h, "htmx:abort"); + ae(h, "htmx:abort"); } else { - if (g == null) { + if (p == null) { if (o) { - const P = ie(o); + const P = oe(o); if (P && P.triggerSpec && P.triggerSpec.queue) { - g = P.triggerSpec.queue; + p = P.triggerSpec.queue; } } - if (g == null) { - g = "last"; + if (p == null) { + p = "last"; } } if (u.queuedRequests == null) { u.queuedRequests = []; } - if (g === "first" && u.queuedRequests.length === 0) { + if (p === "first" && u.queuedRequests.length === 0) { u.queuedRequests.push(function () { - de(t, n, r, o, i); + he(t, n, r, o, i); }); - } else if (g === "all") { + } else if (p === "all") { u.queuedRequests.push(function () { - de(t, n, r, o, i); + he(t, n, r, o, i); }); - } else if (g === "last") { + } else if (p === "last") { u.queuedRequests = []; u.queuedRequests.push(function () { - de(t, n, r, o, i); + he(t, n, r, o, i); }); } - oe(s); + re(s); return e; } } - const p = new XMLHttpRequest(); - u.xhr = p; - u.abortable = F; + const g = new XMLHttpRequest(); + u.xhr = g; + u.abortable = X; const m = function () { u.xhr = null; u.abortable = false; @@ -2897,55 +2926,56 @@ var htmx = (function () { e(); } }; - const B = re(r, "hx-prompt"); + const B = ne(r, "hx-prompt"); if (B) { - var x = prompt(B); - if (x === null || !he(r, "htmx:prompt", { prompt: x, target: c })) { - oe(s); + var y = prompt(B); + if (y === null || !ae(r, "htmx:prompt", { prompt: y, target: c })) { + re(s); m(); return e; } } - if (f && !D) { - if (!confirm(f)) { - oe(s); + if (a && !k) { + if (!confirm(a)) { + re(s); m(); return e; } } - let y = fn(r, c, x); - if (t !== "get" && !pn(r)) { - y["Content-Type"] = "application/x-www-form-urlencoded"; + let x = mn(r, c, y); + if (t !== "get" && !vn(r)) { + x["Content-Type"] = "application/x-www-form-urlencoded"; } if (i.headers) { - y = ce(y, i.headers); + x = le(x, i.headers); } - const U = cn(r, t); + const U = dn(r, t); let b = U.errors; const j = U.formData; if (i.values) { - ln(j, qn(i.values)); + hn(j, Pn(i.values)); } - const V = qn(En(r)); - const v = ln(j, V); - let w = hn(v, r); + const V = Pn(Tn(r, o)); + const v = hn(j, V); + let w = yn(v, r); if (Q.config.getCacheBusterParam && t === "get") { w.set("org.htmx.cache-buster", ee(c, "id") || "true"); } if (n == null || n === "") { - n = ne().location.href; + n = location.href; } - const S = bn(r, "hx-request"); - const _ = ie(r).boosted; + const S = Cn(r, "hx-request"); + const _ = oe(r).boosted; let E = Q.config.methodsThatUseUrlParams.indexOf(t) >= 0; const C = { boosted: _, useUrlParams: E, formData: w, - parameters: An(w), + parameters: kn(w), unfilteredFormData: v, - unfilteredParameters: An(v), - headers: y, + unfilteredParameters: kn(v), + headers: x, + elt: r, target: c, verb: t, errors: b, @@ -2955,136 +2985,138 @@ var htmx = (function () { path: n, triggeringEvent: o, }; - if (!he(r, "htmx:configRequest", C)) { - oe(s); + if (!ae(r, "htmx:configRequest", C)) { + re(s); m(); return e; } n = C.path; t = C.verb; - y = C.headers; - w = qn(C.parameters); + x = C.headers; + w = Pn(C.parameters); b = C.errors; E = C.useUrlParams; if (b && b.length > 0) { - he(r, "htmx:validation:halted", C); - oe(s); + ae(r, "htmx:validation:halted", C); + re(s); m(); return e; } const z = n.split("#"); const $ = z[0]; const O = z[1]; - let R = n; + let H = n; if (E) { - R = $; + H = $; const Z = !w.keys().next().done; if (Z) { - if (R.indexOf("?") < 0) { - R += "?"; + if (H.indexOf("?") < 0) { + H += "?"; } else { - R += "&"; + H += "&"; } - R += an(w); + H += gn(w); if (O) { - R += "#" + O; + H += "#" + O; } } } - if (!Tn(r, R, C)) { + if (!In(r, H, C)) { fe(r, "htmx:invalidPath", C); - oe(l); + re(l); + m(); return e; } - p.open(t.toUpperCase(), R, true); - p.overrideMimeType("text/html"); - p.withCredentials = C.withCredentials; - p.timeout = C.timeout; + g.open(t.toUpperCase(), H, true); + g.overrideMimeType("text/html"); + g.withCredentials = C.withCredentials; + g.timeout = C.timeout; if (S.noHeaders) { } else { - for (const k in y) { - if (y.hasOwnProperty(k)) { - const Y = y[k]; - Cn(p, k, Y); + for (const D in x) { + if (x.hasOwnProperty(D)) { + const Y = x[D]; + qn(g, D, Y); } } } - const H = { - xhr: p, + const R = { + xhr: g, target: c, requestConfig: C, etc: i, boosted: _, - select: X, + select: F, pathInfo: { requestPath: n, - finalRequestPath: R, + finalRequestPath: H, responsePath: null, anchor: O, }, }; - p.onload = function () { + g.onload = function () { try { - const t = Hn(r); - H.pathInfo.responsePath = On(p); - M(r, H); - if (H.keepIndicators !== true) { - Qt(T, q); - } - he(r, "htmx:afterRequest", H); - he(r, "htmx:afterOnLoad", H); - if (!le(r)) { + const t = Nn(r); + R.pathInfo.responsePath = An(g); + M(r, R); + if (R.keepIndicators !== true) { + rn(T, q); + } + ae(r, "htmx:afterRequest", R); + ae(r, "htmx:afterOnLoad", R); + if (!se(r)) { let e = null; while (t.length > 0 && e == null) { const n = t.shift(); - if (le(n)) { + if (se(n)) { e = n; } } if (e) { - he(e, "htmx:afterRequest", H); - he(e, "htmx:afterOnLoad", H); + ae(e, "htmx:afterRequest", R); + ae(e, "htmx:afterOnLoad", R); } } - oe(s); - m(); + re(s); } catch (e) { - fe(r, "htmx:onLoadError", ce({ error: e }, H)); + fe(r, "htmx:onLoadError", le({ error: e }, R)); throw e; + } finally { + m(); } }; - p.onerror = function () { - Qt(T, q); - fe(r, "htmx:afterRequest", H); - fe(r, "htmx:sendError", H); - oe(l); + g.onerror = function () { + rn(T, q); + fe(r, "htmx:afterRequest", R); + fe(r, "htmx:sendError", R); + re(l); m(); }; - p.onabort = function () { - Qt(T, q); - fe(r, "htmx:afterRequest", H); - fe(r, "htmx:sendAbort", H); - oe(l); + g.onabort = function () { + rn(T, q); + fe(r, "htmx:afterRequest", R); + fe(r, "htmx:sendAbort", R); + re(l); m(); }; - p.ontimeout = function () { - Qt(T, q); - fe(r, "htmx:afterRequest", H); - fe(r, "htmx:timeout", H); - oe(l); + g.ontimeout = function () { + rn(T, q); + fe(r, "htmx:afterRequest", R); + fe(r, "htmx:timeout", R); + re(l); m(); }; - if (!he(r, "htmx:beforeRequest", H)) { - oe(s); + if (!ae(r, "htmx:beforeRequest", R)) { + re(s); m(); return e; } - var T = Zt(r); - var q = Yt(r); - se(["loadstart", "loadend", "progress", "abort"], function (t) { - se([p, p.upload], function (e) { + var T = tn(r); + var q = nn(r); + ie(["loadstart", "loadend", "progress", "abort"], function (t) { + ie([g, g.upload], function (e) { e.addEventListener(t, function (e) { - he(r, "htmx:xhr:" + t, { + ae(r, "htmx:xhr:" + t, { lengthComputable: e.lengthComputable, loaded: e.loaded, total: e.total, @@ -3092,12 +3124,12 @@ var htmx = (function () { }); }); }); - he(r, "htmx:beforeSend", H); - const J = E ? null : mn(p, r, w); - p.send(J); + ae(r, "htmx:beforeSend", R); + const J = E ? null : wn(g, r, w); + g.send(J); return e; } - function Nn(e, t) { + function Mn(e, t) { const n = t.xhr; let r = null; let o = null; @@ -3120,264 +3152,228 @@ var htmx = (function () { } const i = t.pathInfo.finalRequestPath; const s = t.pathInfo.responsePath; - const l = re(e, "hx-push-url"); - const c = re(e, "hx-replace-url"); - const u = ie(e).boosted; - let a = null; + const l = ne(e, "hx-push-url"); + const c = ne(e, "hx-replace-url"); + const u = oe(e).boosted; let f = null; + let a = null; if (l) { - a = "push"; - f = l; + f = "push"; + a = l; } else if (c) { - a = "replace"; - f = c; + f = "replace"; + a = c; } else if (u) { - a = "push"; - f = s || i; + f = "push"; + a = s || i; } - if (f) { - if (f === "false") { + if (a) { + if (a === "false") { return {}; } - if (f === "true") { - f = s || i; + if (a === "true") { + a = s || i; } - if (t.pathInfo.anchor && f.indexOf("#") === -1) { - f = f + "#" + t.pathInfo.anchor; + if (t.pathInfo.anchor && a.indexOf("#") === -1) { + a = a + "#" + t.pathInfo.anchor; } - return { type: a, path: f }; + return { type: f, path: a }; } else { return {}; } } - function In(e, t) { + function Fn(e, t) { var n = new RegExp(e.code); return n.test(t.toString(10)); } - function Pn(e) { + function Xn(e) { for (var t = 0; t < Q.config.responseHandling.length; t++) { var n = Q.config.responseHandling[t]; - if (In(n, e.status)) { + if (Fn(n, e.status)) { return n; } } return { swap: false }; } - function kn(e) { + function Bn(e) { if (e) { - const t = u("title"); + const t = f("title"); if (t) { - t.innerHTML = e; + t.textContent = e; } else { window.document.title = e; } } } - function Dn(o, i) { - const s = i.xhr; - let l = i.target; - const e = i.etc; - const c = i.select; - if (!he(o, "htmx:beforeOnLoad", i)) return; - if (R(s, /HX-Trigger:/i)) { - Je(s, "HX-Trigger", o); + function Un(e, t) { + if (t === "this") { + return e; + } + const n = ce(ue(e, t)); + if (n == null) { + fe(e, "htmx:targetError", { target: t }); + throw new Error(`Invalid re-target ${t}`); } - if (R(s, /HX-Location:/i)) { - zt(); - let e = s.getResponseHeader("HX-Location"); - var t; + return n; + } + function jn(t, e) { + const n = e.xhr; + let r = e.target; + const o = e.etc; + const i = e.select; + if (!ae(t, "htmx:beforeOnLoad", e)) return; + if (R(n, /HX-Trigger:/i)) { + Je(n, "HX-Trigger", t); + } + if (R(n, /HX-Location:/i)) { + Gt(); + let e = n.getResponseHeader("HX-Location"); + var s; if (e.indexOf("{") === 0) { - t = S(e); - e = t.path; - delete t.path; + s = v(e); + e = s.path; + delete s.path; } - Rn("get", e, t).then(function () { - $t(e); + Ln("get", e, s).then(function () { + Wt(e); }); return; } - const n = - R(s, /HX-Refresh:/i) && s.getResponseHeader("HX-Refresh") === "true"; - if (R(s, /HX-Redirect:/i)) { - i.keepIndicators = true; - location.href = s.getResponseHeader("HX-Redirect"); - n && location.reload(); + const l = + R(n, /HX-Refresh:/i) && n.getResponseHeader("HX-Refresh") === "true"; + if (R(n, /HX-Redirect:/i)) { + e.keepIndicators = true; + Q.location.href = n.getResponseHeader("HX-Redirect"); + l && Q.location.reload(); return; } - if (n) { - i.keepIndicators = true; - location.reload(); + if (l) { + e.keepIndicators = true; + Q.location.reload(); return; } - if (R(s, /HX-Retarget:/i)) { - if (s.getResponseHeader("HX-Retarget") === "this") { - i.target = o; - } else { - i.target = ue(ae(o, s.getResponseHeader("HX-Retarget"))); - } + const c = Mn(t, e); + const u = Xn(n); + const f = u.swap; + let a = !!u.error; + let h = Q.config.ignoreTitle || u.ignoreTitle; + let d = u.select; + if (u.target) { + e.target = Un(t, u.target); } - const u = Nn(o, i); - const r = Pn(s); - const a = r.swap; - let f = !!r.error; - let h = Q.config.ignoreTitle || r.ignoreTitle; - let d = r.select; - if (r.target) { - i.target = ue(ae(o, r.target)); + var p = o.swapOverride; + if (p == null && u.swapOverride) { + p = u.swapOverride; } - var g = e.swapOverride; - if (g == null && r.swapOverride) { - g = r.swapOverride; - } - if (R(s, /HX-Retarget:/i)) { - if (s.getResponseHeader("HX-Retarget") === "this") { - i.target = o; - } else { - i.target = ue(ae(o, s.getResponseHeader("HX-Retarget"))); - } + if (R(n, /HX-Retarget:/i)) { + e.target = Un(t, n.getResponseHeader("HX-Retarget")); } - if (R(s, /HX-Reswap:/i)) { - g = s.getResponseHeader("HX-Reswap"); + if (R(n, /HX-Reswap:/i)) { + p = n.getResponseHeader("HX-Reswap"); } - var p = s.response; - var m = ce( + var g = n.response; + var m = le( { - shouldSwap: a, - serverResponse: p, - isError: f, + shouldSwap: f, + serverResponse: g, + isError: a, ignoreTitle: h, selectOverride: d, - swapOverride: g, + swapOverride: p, }, - i, + e, ); - if (r.event && !he(l, r.event, m)) return; - if (!he(l, "htmx:beforeSwap", m)) return; - l = m.target; - p = m.serverResponse; - f = m.isError; + if (u.event && !ae(r, u.event, m)) return; + if (!ae(r, "htmx:beforeSwap", m)) return; + r = m.target; + g = m.serverResponse; + a = m.isError; h = m.ignoreTitle; d = m.selectOverride; - g = m.swapOverride; - i.target = l; - i.failed = f; - i.successful = !f; + p = m.swapOverride; + e.target = r; + e.failed = a; + e.successful = !a; if (m.shouldSwap) { - if (s.status === 286) { - lt(o); + if (n.status === 286) { + lt(t); } - Ft(o, function (e) { - p = e.transformResponse(p, s, o); + jt(t, function (e) { + g = e.transformResponse(g, n, t); }); - if (u.type) { - zt(); - } - var x = gn(o, g); - if (!x.hasOwnProperty("ignoreTitle")) { - x.ignoreTitle = h; - } - l.classList.add(Q.config.swappingClass); - let n = null; - let r = null; - if (c) { - d = c; + if (c.type) { + Gt(); } - if (R(s, /HX-Reselect:/i)) { - d = s.getResponseHeader("HX-Reselect"); + var y = bn(t, p); + if (!y.hasOwnProperty("ignoreTitle")) { + y.ignoreTitle = h; } - const y = re(o, "hx-select-oob"); - const b = re(o, "hx-select"); - let e = function () { - try { - if (u.type) { - he(ne().body, "htmx:beforeHistoryUpdate", ce({ history: u }, i)); - if (u.type === "push") { - $t(u.path); - he(ne().body, "htmx:pushedIntoHistory", { path: u.path }); + r.classList.add(Q.config.swappingClass); + if (i) { + d = i; + } + if (R(n, /HX-Reselect:/i)) { + d = n.getResponseHeader("HX-Reselect"); + } + const x = ne(t, "hx-select-oob"); + const b = ne(t, "hx-select"); + $e(r, g, y, { + select: d === "unset" ? null : d || b, + selectOOB: x, + eventInfo: e, + anchor: e.pathInfo.anchor, + contextElement: t, + afterSwapCallback: function () { + if (R(n, /HX-Trigger-After-Swap:/i)) { + let e = t; + if (!se(t)) { + e = te().body; + } + Je(n, "HX-Trigger-After-Swap", e); + } + }, + afterSettleCallback: function () { + if (R(n, /HX-Trigger-After-Settle:/i)) { + let e = t; + if (!se(t)) { + e = te().body; + } + Je(n, "HX-Trigger-After-Settle", e); + } + }, + beforeSwapCallback: function () { + if (c.type) { + ae(te().body, "htmx:beforeHistoryUpdate", le({ history: c }, e)); + if (c.type === "push") { + Wt(c.path); + ae(te().body, "htmx:pushedIntoHistory", { path: c.path }); } else { - Jt(u.path); - he(ne().body, "htmx:replacedInHistory", { path: u.path }); + Zt(c.path); + ae(te().body, "htmx:replacedInHistory", { path: c.path }); } } - $e(l, p, x, { - select: d || b, - selectOOB: y, - eventInfo: i, - anchor: i.pathInfo.anchor, - contextElement: o, - afterSwapCallback: function () { - if (R(s, /HX-Trigger-After-Swap:/i)) { - let e = o; - if (!le(o)) { - e = ne().body; - } - Je(s, "HX-Trigger-After-Swap", e); - } - }, - afterSettleCallback: function () { - if (R(s, /HX-Trigger-After-Settle:/i)) { - let e = o; - if (!le(o)) { - e = ne().body; - } - Je(s, "HX-Trigger-After-Settle", e); - } - oe(n); - }, - }); - } catch (e) { - fe(o, "htmx:swapError", i); - oe(r); - throw e; - } - }; - let t = Q.config.globalViewTransitions; - if (x.hasOwnProperty("transition")) { - t = x.transition; - } - if ( - t && - he(o, "htmx:beforeTransition", i) && - typeof Promise !== "undefined" && - document.startViewTransition - ) { - const v = new Promise(function (e, t) { - n = e; - r = t; - }); - const w = e; - e = function () { - document.startViewTransition(function () { - w(); - return v; - }); - }; - } - if (x.swapDelay > 0) { - E().setTimeout(e, x.swapDelay); - } else { - e(); - } + }, + }); } - if (f) { + if (a) { fe( - o, + t, "htmx:responseError", - ce( + le( { error: "Response Status Error Code " + - s.status + + n.status + " from " + - i.pathInfo.requestPath, + e.pathInfo.requestPath, }, - i, + e, ), ); } } - const Mn = {}; - function Xn() { + const Vn = {}; + function _n() { return { init: function (e) { return null; @@ -3402,16 +3398,16 @@ var htmx = (function () { }, }; } - function Fn(e, t) { + function zn(e, t) { if (t.init) { t.init(n); } - Mn[e] = ce(Xn(), t); + Vn[e] = le(_n(), t); } - function Bn(e) { - delete Mn[e]; + function $n(e) { + delete Vn[e]; } - function Un(e, n, r) { + function Jn(e, n, r) { if (n == undefined) { n = []; } @@ -3421,41 +3417,41 @@ var htmx = (function () { if (r == undefined) { r = []; } - const t = te(e, "hx-ext"); + const t = a(e, "hx-ext"); if (t) { - se(t.split(","), function (e) { + ie(t.split(","), function (e) { e = e.replace(/ /g, ""); if (e.slice(0, 7) == "ignore:") { r.push(e.slice(7)); return; } if (r.indexOf(e) < 0) { - const t = Mn[e]; + const t = Vn[e]; if (t && n.indexOf(t) < 0) { n.push(t); } } }); } - return Un(ue(c(e)), n, r); + return Jn(ce(u(e)), n, r); } - var jn = false; - ne().addEventListener("DOMContentLoaded", function () { - jn = true; + var Kn = false; + te().addEventListener("DOMContentLoaded", function () { + Kn = true; }); - function Vn(e) { - if (jn || ne().readyState === "complete") { + function Gn(e) { + if (Kn || te().readyState === "complete") { e(); } else { - ne().addEventListener("DOMContentLoaded", e); + te().addEventListener("DOMContentLoaded", e); } } - function _n() { + function Wn() { if (Q.config.includeIndicatorStyles !== false) { const e = Q.config.inlineStyleNonce ? ` nonce="${Q.config.inlineStyleNonce}"` : ""; - ne().head.insertAdjacentHTML( + te().head.insertAdjacentHTML( "beforeend", " Date: Mon, 30 Jun 2025 12:15:31 -0700 Subject: [PATCH 22/34] Create FUNDING.yml --- .github/FUNDING.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..25abf7018 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [melkeydev] From 5c2b505c30132e12857decda547e223289230620 Mon Sep 17 00:00:00 2001 From: Simon <41589344+sawatkins@users.noreply.github.com> Date: Sun, 6 Jul 2025 17:02:36 -0700 Subject: [PATCH 23/34] remove duplicate 'The' word (#408) --- docs/docs/advanced-flag/tailwind.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/advanced-flag/tailwind.md b/docs/docs/advanced-flag/tailwind.md index 4b869b090..56c9881b7 100644 --- a/docs/docs/advanced-flag/tailwind.md +++ b/docs/docs/advanced-flag/tailwind.md @@ -36,7 +36,7 @@ The project tree would look like this: ## Standalone Tailwind CLI -The The idea is to avoid using Node.js and npm to build output.css. +The idea is to avoid using Node.js and npm to build output.css. The Makefile will have entries for downloading and compiling CSS. It will automatically detect the OS and download the latest release from the [official repository](https://github.com/tailwindlabs/tailwindcss/releases). From 0c1cc7f87431d1510303461c20dc0f8f5d79cf98 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 19:51:30 -0700 Subject: [PATCH 24/34] update htmx version v2.0.6 (#409) Co-authored-by: Ujstor <116409846+Ujstor@users.noreply.github.com> --- .../advanced/files/htmx/htmx.min.js.tmpl | 129 +++++++++--------- 1 file changed, 65 insertions(+), 64 deletions(-) diff --git a/cmd/template/advanced/files/htmx/htmx.min.js.tmpl b/cmd/template/advanced/files/htmx/htmx.min.js.tmpl index 937d3c466..39250e01b 100644 --- a/cmd/template/advanced/files/htmx/htmx.min.js.tmpl +++ b/cmd/template/advanced/files/htmx/htmx.min.js.tmpl @@ -69,7 +69,7 @@ var htmx = (function () { parseInterval: null, location: location, _: null, - version: "2.0.5", + version: "2.0.6", }; Q.onLoad = j; Q.process = Ft; @@ -113,7 +113,7 @@ var htmx = (function () { makeFragment: P, mergeObjects: le, makeSettleInfo: Sn, - oobSwap: Re, + oobSwap: He, querySelectorExt: ue, settleImmediately: Yt, shouldCancel: ht, @@ -245,7 +245,7 @@ var htmx = (function () { try { n.insertBefore(t, e); } catch (e) { - H(e); + R(e); } finally { e.remove(); } @@ -351,7 +351,7 @@ var htmx = (function () { try { return JSON.parse(e); } catch (e) { - H(e); + R(e); return null; } } @@ -646,7 +646,7 @@ var htmx = (function () { } } if (r.length === 0) { - H('The selector "' + e + '" on ' + n + " returned no matches!"); + R('The selector "' + e + '" on ' + n + " returned no matches!"); return [ve]; } else { return r; @@ -693,7 +693,7 @@ var htmx = (function () { } }); } - function He(t, e) { + function Re(t, e) { const n = Jn(e); for (let e = 0; e < n.length; e++) { const r = n[e]; @@ -702,12 +702,12 @@ var htmx = (function () { return true; } } catch (e) { - H(e); + R(e); } } return t === "outerHTML"; } - function Re(e, o, i, t) { + function He(e, o, i, t) { t = t || te(); let n = "#" + CSS.escape(ee(o, "id")); let s = "outerHTML"; @@ -727,7 +727,7 @@ var htmx = (function () { const n = o.cloneNode(true); t = te().createDocumentFragment(); t.appendChild(n); - if (!He(s, e)) { + if (!Re(s, e)) { t = p(n); } const r = { shouldSwap: true, target: e, fragment: t }; @@ -979,7 +979,7 @@ var htmx = (function () { return; } } catch (e) { - H(e); + R(e); } } if (t === "innerHTML") { @@ -995,7 +995,7 @@ var htmx = (function () { if (Q.config.allowNestedOobSwaps || e.parentElement === null) { const t = a(e, "hx-swap-oob"); if (t != null) { - Re(t, e, n, r); + He(t, e, n, r); } } else { e.removeAttribute("hx-swap-oob"); @@ -1041,7 +1041,7 @@ var htmx = (function () { const l = s[1] || "true"; const c = n.querySelector("#" + e); if (c) { - Re(l, c, o, r); + He(l, c, o, r); } } } @@ -1478,8 +1478,9 @@ var htmx = (function () { if (t.form && t.type === "submit") { return true; } + t = t.closest("a"); if ( - t instanceof HTMLAnchorElement && + t && t.href && (t.getAttribute("href") === "#" || t.getAttribute("href").indexOf("#") !== 0) @@ -1750,7 +1751,7 @@ var htmx = (function () { let r = null; while ((r = n.iterateNext())) t.push(ce(r)); } - function Ht(e) { + function Rt(e) { const t = []; if (e instanceof DocumentFragment) { for (const n of e.childNodes) { @@ -1761,7 +1762,7 @@ var htmx = (function () { } return t; } - function Rt(e) { + function Ht(e) { if (e.querySelectorAll) { const n = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]"; @@ -1907,7 +1908,7 @@ var htmx = (function () { if (Mt(e)) { t.push(e); } - ie(Rt(e), function (e) { + ie(Ht(e), function (e) { if (ft(e)) { S(e); return; @@ -1916,7 +1917,7 @@ var htmx = (function () { t.push(e); } }); - ie(Ht(e), Dt); + ie(Rt(e), Dt); ie(t, kt); } function Xt(e) { @@ -1941,11 +1942,11 @@ var htmx = (function () { try { t(e); } catch (e) { - H(e); + R(e); } }); } - function H(e) { + function R(e) { console.error(e); } function ae(e, t, n) { @@ -1959,7 +1960,7 @@ var htmx = (function () { Q.logger(e, t, n); } if (n.error) { - H(n.error); + R(n.error); ae(e, "htmx:error", { errorInfo: n }); } let o = e.dispatchEvent(r); @@ -2457,7 +2458,7 @@ var htmx = (function () { } else if (e == 0) { r.swapStyle = l; } else { - H("Unknown modifier in hx-swap: " + l); + R("Unknown modifier in hx-swap: " + l); } } } @@ -2589,14 +2590,14 @@ var htmx = (function () { return n; } } - function Hn(e, t, n) { + function Rn(e, t, n) { return Cn(e, "hx-vars", true, n, t); } - function Rn(e, t, n) { + function Hn(e, t, n) { return Cn(e, "hx-vals", false, n, t); } function Tn(e, t) { - return le(Hn(e, t), Rn(e, t)); + return le(Rn(e, t), Hn(e, t)); } function qn(t, n, r) { if (r !== null) { @@ -2618,7 +2619,7 @@ var htmx = (function () { } } } - function R(e, t) { + function H(e, t) { return t.test(e.getAllResponseHeaders()); } function Ln(t, n, r) { @@ -3005,29 +3006,29 @@ var htmx = (function () { const z = n.split("#"); const $ = z[0]; const O = z[1]; - let H = n; + let R = n; if (E) { - H = $; + R = $; const Z = !w.keys().next().done; if (Z) { - if (H.indexOf("?") < 0) { - H += "?"; + if (R.indexOf("?") < 0) { + R += "?"; } else { - H += "&"; + R += "&"; } - H += gn(w); + R += gn(w); if (O) { - H += "#" + O; + R += "#" + O; } } } - if (!In(r, H, C)) { + if (!In(r, R, C)) { fe(r, "htmx:invalidPath", C); re(l); m(); return e; } - g.open(t.toUpperCase(), H, true); + g.open(t.toUpperCase(), R, true); g.overrideMimeType("text/html"); g.withCredentials = C.withCredentials; g.timeout = C.timeout; @@ -3040,7 +3041,7 @@ var htmx = (function () { } } } - const R = { + const H = { xhr: g, target: c, requestConfig: C, @@ -3049,7 +3050,7 @@ var htmx = (function () { select: F, pathInfo: { requestPath: n, - finalRequestPath: H, + finalRequestPath: R, responsePath: null, anchor: O, }, @@ -3057,13 +3058,13 @@ var htmx = (function () { g.onload = function () { try { const t = Nn(r); - R.pathInfo.responsePath = An(g); - M(r, R); - if (R.keepIndicators !== true) { + H.pathInfo.responsePath = An(g); + M(r, H); + if (H.keepIndicators !== true) { rn(T, q); } - ae(r, "htmx:afterRequest", R); - ae(r, "htmx:afterOnLoad", R); + ae(r, "htmx:afterRequest", H); + ae(r, "htmx:afterOnLoad", H); if (!se(r)) { let e = null; while (t.length > 0 && e == null) { @@ -3073,13 +3074,13 @@ var htmx = (function () { } } if (e) { - ae(e, "htmx:afterRequest", R); - ae(e, "htmx:afterOnLoad", R); + ae(e, "htmx:afterRequest", H); + ae(e, "htmx:afterOnLoad", H); } } re(s); } catch (e) { - fe(r, "htmx:onLoadError", le({ error: e }, R)); + fe(r, "htmx:onLoadError", le({ error: e }, H)); throw e; } finally { m(); @@ -3087,26 +3088,26 @@ var htmx = (function () { }; g.onerror = function () { rn(T, q); - fe(r, "htmx:afterRequest", R); - fe(r, "htmx:sendError", R); + fe(r, "htmx:afterRequest", H); + fe(r, "htmx:sendError", H); re(l); m(); }; g.onabort = function () { rn(T, q); - fe(r, "htmx:afterRequest", R); - fe(r, "htmx:sendAbort", R); + fe(r, "htmx:afterRequest", H); + fe(r, "htmx:sendAbort", H); re(l); m(); }; g.ontimeout = function () { rn(T, q); - fe(r, "htmx:afterRequest", R); - fe(r, "htmx:timeout", R); + fe(r, "htmx:afterRequest", H); + fe(r, "htmx:timeout", H); re(l); m(); }; - if (!ae(r, "htmx:beforeRequest", R)) { + if (!ae(r, "htmx:beforeRequest", H)) { re(s); m(); return e; @@ -3124,7 +3125,7 @@ var htmx = (function () { }); }); }); - ae(r, "htmx:beforeSend", R); + ae(r, "htmx:beforeSend", H); const J = E ? null : wn(g, r, w); g.send(J); return e; @@ -3133,13 +3134,13 @@ var htmx = (function () { const n = t.xhr; let r = null; let o = null; - if (R(n, /HX-Push:/i)) { + if (H(n, /HX-Push:/i)) { r = n.getResponseHeader("HX-Push"); o = "push"; - } else if (R(n, /HX-Push-Url:/i)) { + } else if (H(n, /HX-Push-Url:/i)) { r = n.getResponseHeader("HX-Push-Url"); o = "push"; - } else if (R(n, /HX-Replace-Url:/i)) { + } else if (H(n, /HX-Replace-Url:/i)) { r = n.getResponseHeader("HX-Replace-Url"); o = "replace"; } @@ -3222,10 +3223,10 @@ var htmx = (function () { const o = e.etc; const i = e.select; if (!ae(t, "htmx:beforeOnLoad", e)) return; - if (R(n, /HX-Trigger:/i)) { + if (H(n, /HX-Trigger:/i)) { Je(n, "HX-Trigger", t); } - if (R(n, /HX-Location:/i)) { + if (H(n, /HX-Location:/i)) { Gt(); let e = n.getResponseHeader("HX-Location"); var s; @@ -3240,8 +3241,8 @@ var htmx = (function () { return; } const l = - R(n, /HX-Refresh:/i) && n.getResponseHeader("HX-Refresh") === "true"; - if (R(n, /HX-Redirect:/i)) { + H(n, /HX-Refresh:/i) && n.getResponseHeader("HX-Refresh") === "true"; + if (H(n, /HX-Redirect:/i)) { e.keepIndicators = true; Q.location.href = n.getResponseHeader("HX-Redirect"); l && Q.location.reload(); @@ -3265,10 +3266,10 @@ var htmx = (function () { if (p == null && u.swapOverride) { p = u.swapOverride; } - if (R(n, /HX-Retarget:/i)) { + if (H(n, /HX-Retarget:/i)) { e.target = Un(t, n.getResponseHeader("HX-Retarget")); } - if (R(n, /HX-Reswap:/i)) { + if (H(n, /HX-Reswap:/i)) { p = n.getResponseHeader("HX-Reswap"); } var g = n.response; @@ -3312,7 +3313,7 @@ var htmx = (function () { if (i) { d = i; } - if (R(n, /HX-Reselect:/i)) { + if (H(n, /HX-Reselect:/i)) { d = n.getResponseHeader("HX-Reselect"); } const x = ne(t, "hx-select-oob"); @@ -3324,7 +3325,7 @@ var htmx = (function () { anchor: e.pathInfo.anchor, contextElement: t, afterSwapCallback: function () { - if (R(n, /HX-Trigger-After-Swap:/i)) { + if (H(n, /HX-Trigger-After-Swap:/i)) { let e = t; if (!se(t)) { e = te().body; @@ -3333,7 +3334,7 @@ var htmx = (function () { } }, afterSettleCallback: function () { - if (R(n, /HX-Trigger-After-Settle:/i)) { + if (H(n, /HX-Trigger-After-Settle:/i)) { let e = t; if (!se(t)) { e = te().body; From 1c077334b737f99287bb7734c21a8186330ea850 Mon Sep 17 00:00:00 2001 From: Eduardo Juliao <105523256+BlueBlizzardd@users.noreply.github.com> Date: Wed, 9 Jul 2025 22:56:27 -0400 Subject: [PATCH 25/34] Add tailwind config for LSP support in templ files (#413) --- cmd/program/program.go | 12 ++++++++++++ .../files/htmx/tailwind/tailwind.config.js.tmpl | 5 +++++ cmd/template/advanced/routes.go | 7 +++++++ 3 files changed, 24 insertions(+) create mode 100644 cmd/template/advanced/files/htmx/tailwind/tailwind.config.js.tmpl diff --git a/cmd/program/program.go b/cmd/program/program.go index e32bf272e..a615b5aa9 100644 --- a/cmd/program/program.go +++ b/cmd/program/program.go @@ -505,6 +505,18 @@ func (p *Project) CreateMainFile() error { return err } + htmxTailwindConfigJsFile, err := os.Create(fmt.Sprintf("%s/tailwind.config.js", projectPath)) + if err != nil { + return err + } + defer htmxTailwindConfigJsFile.Close() + + htmxTailwindConfigJsTemplate := advanced.HtmxTailwindConfigJsTemplate() + err = os.WriteFile(fmt.Sprintf("%s/tailwind.config.js", projectPath), htmxTailwindConfigJsTemplate, 0o644) + if err != nil { + return err + } + efsFile, err := os.Create(fmt.Sprintf("%s/%s/efs.go", projectPath, cmdWebPath)) if err != nil { return err diff --git a/cmd/template/advanced/files/htmx/tailwind/tailwind.config.js.tmpl b/cmd/template/advanced/files/htmx/tailwind/tailwind.config.js.tmpl new file mode 100644 index 000000000..fc4468507 --- /dev/null +++ b/cmd/template/advanced/files/htmx/tailwind/tailwind.config.js.tmpl @@ -0,0 +1,5 @@ +module.exports = { + content: ["./**/*.html", "./**/*.templ", "./**/*.go",], + theme: { extend: {}, }, + plugins: [], +} diff --git a/cmd/template/advanced/routes.go b/cmd/template/advanced/routes.go index e7860f58f..a671b5e7f 100644 --- a/cmd/template/advanced/routes.go +++ b/cmd/template/advanced/routes.go @@ -28,6 +28,9 @@ var inputCssTemplate []byte //go:embed files/tailwind/output.css.tmpl var outputCssTemplate []byte +//go:embed files/htmx/tailwind/tailwind.config.js.tmpl +var htmxTailwindConfigJsTemplate []byte + //go:embed files/htmx/htmx.min.js.tmpl var htmxMinJsTemplate []byte @@ -140,6 +143,10 @@ func OutputCssTemplate() []byte { return outputCssTemplate } +func HtmxTailwindConfigJsTemplate() []byte { + return htmxTailwindConfigJsTemplate +} + func HtmxJSTemplate() []byte { return htmxMinJsTemplate } From 3a472b46dc83a5d2b385617abf4f6f09f26273a2 Mon Sep 17 00:00:00 2001 From: ujstor <116409846+Ujstor@users.noreply.github.com> Date: Thu, 10 Jul 2025 04:56:51 +0200 Subject: [PATCH 26/34] tailwind docker config (#407) --- cmd/template/advanced/files/docker/dockerfile.tmpl | 4 ++-- docs/docs/advanced-flag/docker.md | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/template/advanced/files/docker/dockerfile.tmpl b/cmd/template/advanced/files/docker/dockerfile.tmpl index fd1467900..5690f6171 100644 --- a/cmd/template/advanced/files/docker/dockerfile.tmpl +++ b/cmd/template/advanced/files/docker/dockerfile.tmpl @@ -1,6 +1,6 @@ FROM golang:1.24.4-alpine AS build {{- if or (.AdvancedOptions.tailwind) (eq .DBDriver "sqlite") }} -RUN apk add --no-cache{{- if .AdvancedOptions.tailwind }} curl{{ end }}{{- if (eq .DBDriver "sqlite") }} alpine-sdk{{ end }} +RUN apk add --no-cache{{- if .AdvancedOptions.tailwind }} curl libstdc++ libgcc{{ end }}{{- if (eq .DBDriver "sqlite") }} alpine-sdk{{ end }} {{- end }} WORKDIR /app @@ -16,7 +16,7 @@ RUN go install github.com/a-h/templ/cmd/templ@latest && \ {{- end}} {{- if .AdvancedOptions.tailwind}} - curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 -o tailwindcss && \ + curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64-musl -o tailwindcss && \ chmod +x tailwindcss && \ ./tailwindcss -i cmd/web/styles/input.css -o cmd/web/assets/css/output.css {{- end }} diff --git a/docs/docs/advanced-flag/docker.md b/docs/docs/advanced-flag/docker.md index 1b2ef94a6..75959d490 100644 --- a/docs/docs/advanced-flag/docker.md +++ b/docs/docs/advanced-flag/docker.md @@ -6,7 +6,7 @@ The Dockerfile includes a two-stage build, and the final config depends on the u ```dockerfile FROM golang:1.24.4-alpine AS build -RUN apk add --no-cache curl +RUN apk add --no-cache curl libstdc++ libgcc WORKDIR /app @@ -15,8 +15,7 @@ RUN go mod download COPY . . -RUN apk add --no-cache libstdc++ libgcc && \ - go install github.com/a-h/templ/cmd/templ@latest && \ +RUN go install github.com/a-h/templ/cmd/templ@latest && \ templ generate && \ curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64-musl -o tailwindcss && \ chmod +x tailwindcss && \ From d16fa293d2ab8fa708c6e90e391ac0b67857bad3 Mon Sep 17 00:00:00 2001 From: Abel Penton <32851047+abelpenton@users.noreply.github.com> Date: Wed, 16 Jul 2025 22:42:40 -0300 Subject: [PATCH 27/34] Release to NPM (#414) * adding workflow and script to support npm distribution * refactor npm workflow * fixing workflow for npm publisher * improve npm authentication in workflow * updating docs for installation --- .github/workflows/npm-publish.yml | 61 ++++++++ .github/workflows/release.yml | 7 + README.md | 11 ++ docs/docs/installation.md | 20 +++ scripts/create-npm-packages.sh | 239 ++++++++++++++++++++++++++++++ 5 files changed, 338 insertions(+) create mode 100644 .github/workflows/npm-publish.yml create mode 100644 scripts/create-npm-packages.sh diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 000000000..6fcd960f3 --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,61 @@ +name: npm-publish + +on: + workflow_call: + inputs: + tag: + description: 'Release tag to publish (e.g., v1.0.0)' + required: true + type: string + workflow_dispatch: + inputs: + tag: + description: 'Release tag to publish (e.g., v1.0.0)' + required: true + type: string + +jobs: + npm-publish: + runs-on: ubuntu-latest + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag }} + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + registry-url: 'https://registry.npmjs.org' + + - name: Download release assets + run: | + TAG="${{ inputs.tag }}" + VERSION=${TAG#v} + mkdir -p dist + gh release download "$TAG" --dir dist + echo "VERSION=$VERSION" >> $GITHUB_ENV + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create npm packages + run: | + chmod +x ./scripts/create-npm-packages.sh + ./scripts/create-npm-packages.sh "$VERSION" + + - name: Publish platform-specific packages to npm + run: | + for platform_dir in platform-packages/go-blueprint-*; do + if [ -d "$platform_dir" ]; then + cd "$platform_dir" + npm publish --access public + cd - > /dev/null + fi + done + + - name: Publish main package to npm + run: | + cd npm-package + npm publish --access public \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 583ed0b20..0eb2a6bbe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,3 +27,10 @@ jobs: workdir: ./ env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + npm-publish: + needs: goreleaser + uses: ./.github/workflows/npm-publish.yml + with: + tag: ${{ github.ref_name }} + secrets: inherit \ No newline at end of file diff --git a/README.md b/README.md index f2ec319bc..9ef5f26e9 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ gives the option to integrate with one of the more popular Go frameworks (and th Install +### Go Install ```bash go install github.com/melkeydev/go-blueprint@latest ``` @@ -55,6 +56,16 @@ don't forget to update source ~/.zshrc ``` +### NPM Install +```bash +npm install -g go-blueprint +``` + +### Homebrew Install +```bash +brew install go-blueprint +``` + Then in a new terminal run: ```bash diff --git a/docs/docs/installation.md b/docs/docs/installation.md index de058c824..e5f708d48 100644 --- a/docs/docs/installation.md +++ b/docs/docs/installation.md @@ -36,6 +36,26 @@ This command installs the Go-Blueprint binary, automatically binding it to your > source ~/.zshrc > ``` +## NPM Install + +If you prefer using Node.js package manager, you can install Go-Blueprint via NPM. This method is convenient for developers who are already working in JavaScript/Node.js environments and want to integrate Go-Blueprint into their existing workflow. + +```bash +npm install -g go-blueprint +``` + +The `-g` flag installs Go-Blueprint globally, making it accessible from any directory on your system. + +## Homebrew Install + +For macOS and Linux users, Homebrew provides a simple way to install Go-Blueprint. Homebrew automatically handles dependencies and keeps the tool updated through its package management system. + +```bash +brew install go-blueprint +``` + +After installation via Homebrew, Go-Blueprint will be automatically added to your PATH, making it immediately available in your terminal. + ## Building and Installing from Source If you prefer to build and install Go-Blueprint directly from the source code, you can follow these steps: diff --git a/scripts/create-npm-packages.sh b/scripts/create-npm-packages.sh new file mode 100644 index 000000000..5aa907e98 --- /dev/null +++ b/scripts/create-npm-packages.sh @@ -0,0 +1,239 @@ +#!/bin/bash + +set -euo pipefail + +VERSION="$1" +PACKAGE_NAME="go-blueprint" +MAIN_PACKAGE_DIR="npm-package" +PLATFORM_PACKAGES_DIR="platform-packages" + +rm -rf "$MAIN_PACKAGE_DIR" "$PLATFORM_PACKAGES_DIR" + +mkdir -p "$MAIN_PACKAGE_DIR/bin" "$PLATFORM_PACKAGES_DIR" + +declare -A PLATFORM_MAP=( + ["go-blueprint_${VERSION}_Darwin_all"]="darwin-x64,darwin-arm64" + ["go-blueprint_${VERSION}_Linux_x86_64"]="linux-x64" + ["go-blueprint_${VERSION}_Linux_arm64"]="linux-arm64" + ["go-blueprint_${VERSION}_Windows_x86_64"]="win32-x64" + ["go-blueprint_${VERSION}_Windows_arm64"]="win32-arm64" +) + +declare -A OS_MAP=( + ["darwin-x64"]="darwin" + ["darwin-arm64"]="darwin" + ["linux-x64"]="linux" + ["linux-arm64"]="linux" + ["win32-x64"]="win32" + ["win32-arm64"]="win32" +) + +declare -A CPU_MAP=( + ["darwin-x64"]="x64" + ["darwin-arm64"]="arm64" + ["linux-x64"]="x64" + ["linux-arm64"]="arm64" + ["win32-x64"]="x64" + ["win32-arm64"]="arm64" +) + +OPTIONAL_DEPS="" +for archive in dist/*.tar.gz dist/*.zip; do + if [ -f "$archive" ]; then + archive_name=$(basename "$archive") + archive_name="${archive_name%.tar.gz}" + archive_name="${archive_name%.zip}" + + platform_keys="${PLATFORM_MAP[$archive_name]:-}" + + if [ -n "$platform_keys" ]; then + echo "Processing $archive for platforms: $platform_keys" + + IFS=',' read -ra PLATFORM_ARRAY <<< "$platform_keys" + for platform_key in "${PLATFORM_ARRAY[@]}"; do + platform_key=$(echo "$platform_key" | xargs) + + echo " Creating package for platform: $platform_key" + + platform_package_dir="$PLATFORM_PACKAGES_DIR/$PACKAGE_NAME-$platform_key" + mkdir -p "$platform_package_dir/bin" + + if [[ "$archive" == *.tar.gz ]]; then + tar -xzf "$archive" -C "$platform_package_dir/bin" + else + unzip -j "$archive" -d "$platform_package_dir/bin" + fi + + for doc_file in README.md README README.txt LICENSE LICENSE.md LICENSE.txt; do + if [ -f "$platform_package_dir/bin/$doc_file" ]; then + mv "$platform_package_dir/bin/$doc_file" "$platform_package_dir/" + fi + done + + ls -l "$platform_package_dir/bin" + chmod +x "$platform_package_dir/bin/"* + + os_value="${OS_MAP[$platform_key]}" + cpu_value="${CPU_MAP[$platform_key]}" + + files_array='["bin/"]' + for doc_file in README.md README README.txt LICENSE LICENSE.md LICENSE.txt; do + if [ -f "$platform_package_dir/$doc_file" ]; then + files_array="${files_array%]}, \"$doc_file\"]" + fi + done + + binary_name="go-blueprint" + if [[ "$os_value" == "win32" ]]; then + binary_name="go-blueprint.exe" + fi + + cat > "$platform_package_dir/package.json" << EOF +{ + "name": "$PACKAGE_NAME-$platform_key", + "version": "$VERSION", + "description": "Platform-specific binary for $PACKAGE_NAME ($platform_key)", + "os": ["$os_value"], + "cpu": ["$cpu_value"], + "bin": { + "go-blueprint": "bin/$binary_name" + }, + "files": $files_array, + "repository": { + "type": "git", + "url": "https://github.com/Melkeydev/go-blueprint.git" + }, + "author": "Melkeydev", + "license": "MIT" +} +EOF + + if [ -n "$OPTIONAL_DEPS" ]; then + OPTIONAL_DEPS="$OPTIONAL_DEPS," + fi + OPTIONAL_DEPS="$OPTIONAL_DEPS\"$PACKAGE_NAME-$platform_key\": \"$VERSION\"" + done + fi + fi +done + +cat > "$MAIN_PACKAGE_DIR/bin/go-blueprint" << 'EOF' +#!/usr/bin/env node + +const { execFileSync } = require('child_process') + +const packageName = 'go-blueprint' + +const platformPackages = { + 'darwin-x64': `${packageName}-darwin-x64`, + 'darwin-arm64': `${packageName}-darwin-arm64`, + 'linux-x64': `${packageName}-linux-x64`, + 'linux-arm64': `${packageName}-linux-arm64`, + 'win32-x64': `${packageName}-win32-x64`, + 'win32-arm64': `${packageName}-win32-arm64` +} + +function getBinaryPath() { + const platformKey = `${process.platform}-${process.arch}` + const platformPackageName = platformPackages[platformKey] + + if (!platformPackageName) { + console.error(`Platform ${platformKey} is not supported!`) + process.exit(1) + } + + try { + const binaryName = process.platform === 'win32' ? 'go-blueprint.exe' : 'go-blueprint' + return require.resolve(`${platformPackageName}/bin/${binaryName}`) + } catch (e) { + process.exit(1) + } +} + +try { + const binaryPath = getBinaryPath() + execFileSync(binaryPath, process.argv.slice(2), { stdio: 'inherit' }) +} catch (error) { + console.error('Failed to execute go-blueprint:', error.message) + process.exit(1) +} +EOF + +chmod +x "$MAIN_PACKAGE_DIR/bin/go-blueprint" + +cat > "$MAIN_PACKAGE_DIR/package.json" << EOF +{ + "name": "$PACKAGE_NAME", + "version": "$VERSION", + "description": "A CLI for scaffolding Go projects with modern tooling", + "main": "index.js", + "bin": { + "go-blueprint": "bin/go-blueprint" + }, + "optionalDependencies": { + $OPTIONAL_DEPS + }, + "keywords": ["go", "golang", "cli"], + "author": "Melkeydev", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Melkeydev/go-blueprint.git" + }, + "homepage": "https://github.com/Melkeydev/go-blueprint", + "engines": { + "node": ">=14.0.0" + }, + "files": [ + "bin/", + "index.js", + "README.md" + ] +} +EOF + +cat > "$MAIN_PACKAGE_DIR/index.js" << 'EOF' +const { execFileSync } = require('child_process') +const path = require('path') + +const binaryName = process.platform === 'win32' ? 'go-blueprint.exe' : 'go-blueprint' + +const packageName = 'go-blueprint' + +const platformPackages = { + 'darwin-x64': `${packageName}-darwin-x64`, + 'darwin-arm64': `${packageName}-darwin-arm64`, + 'linux-x64': `${packageName}-linux-x64`, + 'linux-arm64': `${packageName}-linux-arm64`, + 'win32-x64': `${packageName}-win32-x64`, + 'win32-arm64': `${packageName}-win32-arm64` +} + +function getBinaryPath() { + const platformKey = `${process.platform}-${process.arch}` + const platformPackageName = platformPackages[platformKey] + + if (!platformPackageName) { + throw new Error(`Platform ${platformKey} is not supported!`) + } + + try { + return require.resolve(`${platformPackageName}/bin/${binaryName}`) + } catch (e) { + throw new Error(`Platform-specific package ${platformPackageName} not found.`) + } +} + +module.exports = { + getBinaryPath, + run: function(...args) { + const binaryPath = getBinaryPath() + return execFileSync(binaryPath, args, { stdio: 'inherit' }) + } +} +EOF + +first_platform_dir=$(ls -1d "$PLATFORM_PACKAGES_DIR"/* | head -1 2>/dev/null || echo "") +if [ -n "$first_platform_dir" ] && [ -f "$first_platform_dir/README.md" ]; then + cp "$first_platform_dir/README.md" "$MAIN_PACKAGE_DIR/" +fi \ No newline at end of file From 98f8d0a51094e79c1db1409b338380104ab090d4 Mon Sep 17 00:00:00 2001 From: eggplannt Date: Thu, 17 Jul 2025 14:23:25 -0600 Subject: [PATCH 28/34] cleaned up sqlc support check --- cmd/create.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 280165ded..cf8c3af58 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -204,9 +204,12 @@ var createCmd = &cobra.Command{ } else { isInteractive = true step := optionSteps.Steps["advanced"] - if project.DBDriver != flags.Postgres && project.DBDriver != flags.MySql && project.DBDriver != flags.Sqlite { - step.Options = slices.DeleteFunc(step.Options, func(s steps.Item) bool { return s.Flag == "Sqlc" }) + sqlcSupportedDrivers := []flags.Database{flags.Postgres, flags.MySql, flags.Sqlite} + if !slices.Contains(sqlcSupportedDrivers, project.DBDriver) { + step.Options = slices.DeleteFunc(step.Options, func(s steps.Item) bool { + return s.Flag == "Sqlc" + }) } tprogram = tea.NewProgram((multiSelect.InitialModelMultiSelect(step.Options, options.Advanced, step.Headers, project))) if _, err := tprogram.Run(); err != nil { From 6304a75266d28d986cfe93cba08952e9e48c5bca Mon Sep 17 00:00:00 2001 From: eggplannt Date: Thu, 17 Jul 2025 14:29:00 -0600 Subject: [PATCH 29/34] avoid breaking changes --- .github/workflows/generate-linter-advanced.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generate-linter-advanced.yml b/.github/workflows/generate-linter-advanced.yml index 159e0394f..804edebdc 100644 --- a/.github/workflows/generate-linter-advanced.yml +++ b/.github/workflows/generate-linter-advanced.yml @@ -42,13 +42,13 @@ jobs: name: Install Templ & gen templates run: | go install github.com/a-h/templ/cmd/templ@latest - /home/runner/go/bin/templ generate -path ${{ env.PROJECT_DIRECTORY }} + $(go env GOPATH)/bin/templ generate -path ${{ env.PROJECT_DIRECTORY }} - if: ${{ matrix.advanced == 'sqlc'}} name: Install Sqlc & gen code run: | go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest - /home/runner/go/bin/sqlc generate -f ${{ env.PROJECT_DIRECTORY }}/sqlc.yaml + $(go env GOPATH)/bin/sqlc generate -f ${{ env.PROJECT_DIRECTORY }}/sqlc.yaml - name: golangci-lint run: | From ebd54fa5cf12ee61925f7d1475d768c960230d01 Mon Sep 17 00:00:00 2001 From: Nacho Vazquez Date: Sat, 19 Jul 2025 17:27:13 -0300 Subject: [PATCH 30/34] ci: add npm provenance (#415) --- .github/workflows/npm-publish.yml | 18 +++++++++++------- .github/workflows/release.yml | 11 +++++++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 6fcd960f3..1f8fe6a7e 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -4,32 +4,35 @@ on: workflow_call: inputs: tag: - description: 'Release tag to publish (e.g., v1.0.0)' + description: "Release tag to publish (e.g., v1.0.0)" required: true type: string workflow_dispatch: inputs: tag: - description: 'Release tag to publish (e.g., v1.0.0)' + description: "Release tag to publish (e.g., v1.0.0)" required: true type: string jobs: npm-publish: runs-on: ubuntu-latest + permissions: + contents: read + id-token: write env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} steps: - uses: actions/checkout@v4 with: ref: ${{ inputs.tag }} - + - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: 22 - registry-url: 'https://registry.npmjs.org' - + registry-url: "https://registry.npmjs.org" + - name: Download release assets run: | TAG="${{ inputs.tag }}" @@ -50,7 +53,7 @@ jobs: for platform_dir in platform-packages/go-blueprint-*; do if [ -d "$platform_dir" ]; then cd "$platform_dir" - npm publish --access public + npm publish --provenance --access public cd - > /dev/null fi done @@ -58,4 +61,5 @@ jobs: - name: Publish main package to npm run: | cd npm-package - npm publish --access public \ No newline at end of file + npm publish --provenance --access public + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0eb2a6bbe..c23c2fa21 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,9 +16,8 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.21.1' - - - name: Run GoReleaser + go-version: "1.21.1" + - name: Run GoReleaser uses: goreleaser/goreleaser-action@v5.0.0 with: distribution: goreleaser @@ -30,7 +29,11 @@ jobs: npm-publish: needs: goreleaser + permissions: + contents: read + id-token: write uses: ./.github/workflows/npm-publish.yml with: tag: ${{ github.ref_name }} - secrets: inherit \ No newline at end of file + secrets: inherit + From 03bdd5b64532e093c3461690c38152eed219053b Mon Sep 17 00:00:00 2001 From: Abel Penton <32851047+abelpenton@users.noreply.github.com> Date: Sat, 19 Jul 2025 18:07:09 -0300 Subject: [PATCH 31/34] remove tag reference from npm release (#417) --- .github/workflows/npm-publish.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 1f8fe6a7e..93bffd5e7 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -24,8 +24,6 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} steps: - uses: actions/checkout@v4 - with: - ref: ${{ inputs.tag }} - name: Set up Node.js uses: actions/setup-node@v4 From 81f56f8c24637d2fd2adb5f37071c6d4cb72b571 Mon Sep 17 00:00:00 2001 From: Abel Penton <32851047+abelpenton@users.noreply.github.com> Date: Sat, 19 Jul 2025 19:44:10 -0300 Subject: [PATCH 32/34] Adding NPM Melkeydev organization scope (#418) --- README.md | 2 +- docs/docs/installation.md | 2 +- scripts/create-npm-packages.sh | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9ef5f26e9..652b7fee9 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ source ~/.zshrc ### NPM Install ```bash -npm install -g go-blueprint +npm install -g @melkeydev/go-blueprint ``` ### Homebrew Install diff --git a/docs/docs/installation.md b/docs/docs/installation.md index e5f708d48..80b97a5e4 100644 --- a/docs/docs/installation.md +++ b/docs/docs/installation.md @@ -41,7 +41,7 @@ This command installs the Go-Blueprint binary, automatically binding it to your If you prefer using Node.js package manager, you can install Go-Blueprint via NPM. This method is convenient for developers who are already working in JavaScript/Node.js environments and want to integrate Go-Blueprint into their existing workflow. ```bash -npm install -g go-blueprint +npm install -g @melkeydev/go-blueprint ``` The `-g` flag installs Go-Blueprint globally, making it accessible from any directory on your system. diff --git a/scripts/create-npm-packages.sh b/scripts/create-npm-packages.sh index 5aa907e98..04d530435 100644 --- a/scripts/create-npm-packages.sh +++ b/scripts/create-npm-packages.sh @@ -3,7 +3,7 @@ set -euo pipefail VERSION="$1" -PACKAGE_NAME="go-blueprint" +PACKAGE_NAME="@melkeydev/go-blueprint" MAIN_PACKAGE_DIR="npm-package" PLATFORM_PACKAGES_DIR="platform-packages" @@ -55,7 +55,7 @@ for archive in dist/*.tar.gz dist/*.zip; do echo " Creating package for platform: $platform_key" - platform_package_dir="$PLATFORM_PACKAGES_DIR/$PACKAGE_NAME-$platform_key" + platform_package_dir="$PLATFORM_PACKAGES_DIR/go-blueprint-$platform_key" mkdir -p "$platform_package_dir/bin" if [[ "$archive" == *.tar.gz ]]; then @@ -122,7 +122,7 @@ cat > "$MAIN_PACKAGE_DIR/bin/go-blueprint" << 'EOF' const { execFileSync } = require('child_process') -const packageName = 'go-blueprint' +const packageName = '@melkeydev/go-blueprint' const platformPackages = { 'darwin-x64': `${packageName}-darwin-x64`, @@ -144,6 +144,7 @@ function getBinaryPath() { try { const binaryName = process.platform === 'win32' ? 'go-blueprint.exe' : 'go-blueprint' + const packagePath = platformPackageName.replace('@', '').replace('/', '-') return require.resolve(`${platformPackageName}/bin/${binaryName}`) } catch (e) { process.exit(1) @@ -198,7 +199,7 @@ const path = require('path') const binaryName = process.platform === 'win32' ? 'go-blueprint.exe' : 'go-blueprint' -const packageName = 'go-blueprint' +const packageName = '@melkeydev/go-blueprint' const platformPackages = { 'darwin-x64': `${packageName}-darwin-x64`, From d2ec8137a9d15722be53387235b794d025c28a0a Mon Sep 17 00:00:00 2001 From: Vicente Oros Date: Mon, 4 Aug 2025 23:48:28 -0600 Subject: [PATCH 33/34] Add sqlc documentation --- docs/docs/advanced-flag/advanced-flag.md | 3 + docs/docs/advanced-flag/sqlc.md | 101 +++++++++++++++++++++ docs/docs/creating-project/makefile.md | 9 ++ docs/docs/creating-project/project-init.md | 6 +- docs/mkdocs.yml | 73 +++++++-------- 5 files changed, 155 insertions(+), 37 deletions(-) create mode 100644 docs/docs/advanced-flag/sqlc.md diff --git a/docs/docs/advanced-flag/advanced-flag.md b/docs/docs/advanced-flag/advanced-flag.md index 124ef6b91..cfc6307cf 100644 --- a/docs/docs/advanced-flag/advanced-flag.md +++ b/docs/docs/advanced-flag/advanced-flag.md @@ -2,6 +2,7 @@ The `--advanced` flag in Blueprint serves as a switch to enable additional features during project creation. It is applied with the `create` command and unlocks the following features: + - **HTMX Support using Templ:** Enables the integration of HTMX support for dynamic web pages using Templ. @@ -20,6 +21,8 @@ Docker configuration for go project. - **React:** Frontend written in TypeScript, including an example fetch request to the backend. +- **SQLC:** +Generates SQL queries and typesafe code for database interactions using SQLC. To utilize the `--advanced` flag, use the following command: diff --git a/docs/docs/advanced-flag/sqlc.md b/docs/docs/advanced-flag/sqlc.md new file mode 100644 index 000000000..69c93552d --- /dev/null +++ b/docs/docs/advanced-flag/sqlc.md @@ -0,0 +1,101 @@ +When SQLC support is enabled, the project includes a configuration file and example SQL files. Running `sqlc generate` produces type-safe Go code for executing your queries using the standard `database/sql` interface by default. However, SQLC is also compatible with `pgx` v4 and v5. + +SQLC supports PostgreSQL, MySQL, and SQLite. + +## Project Structure + +The generated project will follow this structure: + +```bash +/ (Root) +├── cmd/ +│ └── api/ +│ └── main.go +├── internal/ +│ ├── database/ +│ │ ├── database.go +│ │ ├── database_test.go +│ │ ├── repository/ +│ │ │ ├── db.go +│ │ │ ├── models.go +│ │ │ ├── querier.go +│ │ │ └── query.sql.go +│ │ └── sql/ +│ │ ├── query.sql +│ │ └── schema.sql +│ └── server/ +│ ├── routes.go +│ ├── routes_test.go +│ └── server.go +├── go.mod +├── go.sum +├── Makefile +├── README.md +└── sqlc.yaml +``` + +## SQLC Configuration + +The `sqlc.yaml` configuration file is automatically created at the root of the project. It defines how SQLC generates Go code from your SQL schema and query files. + +```yaml +version: "2" +sql: + - engine: "postgresql" + queries: "internal/database/sql/query.sql" + schema: "internal/database/sql/schema.sql" + gen: + go: + package: "repository" + out: "internal/database/repository" + emit_json_tags: true + emit_prepared_queries: false + emit_interface: true + emit_empty_slices: true +``` + +## Example SQL Files + +### Schema + +```sql +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) UNIQUE NOT NULL, + created_at TIMESTAMP DEFAULT NOW() +); +``` + +### Queries + +```sql +-- name: GetUserByID :one +SELECT id, name, email FROM users WHERE id = $1; + +-- name: ListUsers :many +SELECT * FROM users ORDER BY created_at DESC; +``` + +## Makefile Integration + +The Makefile includes entries for installing SQLC and generating code. + + +```bash +sqlc-install: + # Installation logic goes here + +build: sqlc-install + @echo "Building..." + @sqlc generate + @go build -o main cmd/api/main.go +``` + +## Use SQLC in your project + +By default, the project includes SQL schema and query files. +To customize them: + +1. Modify the files under `internal/database/sql/`. +2. Run `sqlc generate` to regenerate the Go code reflecting your changes. diff --git a/docs/docs/creating-project/makefile.md b/docs/docs/creating-project/makefile.md index 2717797af..112da2c40 100644 --- a/docs/docs/creating-project/makefile.md +++ b/docs/docs/creating-project/makefile.md @@ -8,6 +8,7 @@ Makefile is designed for building, running, and testing a Go project. It include The default target that builds and test the application by running the `build` and `test` target. + ***`templ-install`*** This target installs the Go-based templating tool, `templ`, if it is not already installed. It supports: @@ -23,6 +24,14 @@ This target downloads and sets up `tailwindcss`, depending on the user's operati - **macOS**: Downloads the macOS binary. - **Windows**: Uses PowerShell to download the Windows executable. +***`sqlc-install`*** + +This target installs the `sqlc` tool, which is used for generating Go code from SQL queries. It supports: + +- **Unix-based systems**: Prompts the user to install `sqlc` if it is not already installed. +- **Windows**: Uses PowerShell to check for and install `sqlc`. + + ***`build`*** Builds the Go application and generates assets with `templ` and `tailwind`, if the corresponding advanced options are enabled: diff --git a/docs/docs/creating-project/project-init.md b/docs/docs/creating-project/project-init.md index 6ad0b23c6..4cb9516e8 100644 --- a/docs/docs/creating-project/project-init.md +++ b/docs/docs/creating-project/project-init.md @@ -69,8 +69,12 @@ Docker: ```bash go-blueprint create --advanced --feature docker ``` +Sqlc: +```bash +go-blueprint create --advanced --feature sqlc +``` Or all features at once: ```bash -go-blueprint create --name my-project --framework chi --driver mysql --git commit --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind --feature docker +go-blueprint create --name my-project --framework chi --driver mysql --git commit --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind --feature docker --feature sqlc ``` diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index b9ece5be4..76f8a5e62 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -18,7 +18,7 @@ theme: - navigation.sections - navigation.footer - toc.flow - palette: + palette: - scheme: default toggle: icon: material/brightness-7 @@ -33,44 +33,45 @@ nav: - Installation: installation.md - Blueprint UI: blueprint-ui.md - Project creation & default config: - - Project init: creating-project/project-init.md - - Makefile: creating-project/makefile.md - - Air: creating-project/air.md + - Project init: creating-project/project-init.md + - Makefile: creating-project/makefile.md + - Air: creating-project/air.md - Blueprint Core: - - Frameworks: blueprint-core/frameworks.md - - DB Drivers: blueprint-core/db-drivers.md + - Frameworks: blueprint-core/frameworks.md + - DB Drivers: blueprint-core/db-drivers.md - Advanced Flag: - - AF Usage: advanced-flag/advanced-flag.md - - HTMX and Templ: advanced-flag/htmx-templ.md - - Tailwind CSS: advanced-flag/tailwind.md - - GoReleaser & GoTest CI: advanced-flag/goreleaser.md - - Websocket: advanced-flag/websocket.md - - Docker: advanced-flag/docker.md - - React & Vite (TypeScript): advanced-flag/react-vite.md - - Testing endpoints: - - Server: endpoints-test/server.md - - DB Health Endpoints: - - SQL DBs: endpoints-test/sql.md - - Redis: endpoints-test/redis.md - - MongoDB: endpoints-test/mongo.md - - ScyllaDB: endpoints-test/scylladb.md - - Websocket: endpoints-test/websocket.md - - Web Endpoint: endpoints-test/web.md + - AF Usage: advanced-flag/advanced-flag.md + - HTMX and Templ: advanced-flag/htmx-templ.md + - Tailwind CSS: advanced-flag/tailwind.md + - GoReleaser & GoTest CI: advanced-flag/goreleaser.md + - Websocket: advanced-flag/websocket.md + - Docker: advanced-flag/docker.md + - React & Vite (TypeScript): advanced-flag/react-vite.md + - SQLC: advanced-flag/sqlc.md + - Testing endpoints: + - Server: endpoints-test/server.md + - DB Health Endpoints: + - SQL DBs: endpoints-test/sql.md + - Redis: endpoints-test/redis.md + - MongoDB: endpoints-test/mongo.md + - ScyllaDB: endpoints-test/scylladb.md + - Websocket: endpoints-test/websocket.md + - Web Endpoint: endpoints-test/web.md extra: - social: - - icon: fontawesome/brands/discord - link: https://discord.com/invite/HHZMSCu - name: Discord - - icon: fontawesome/brands/twitch - link: https://www.twitch.tv/melkey - name: Twitch - - icon: fontawesome/brands/youtube - link: https://www.youtube.com/@MelkeyDev - name: YouTube - - icon: fontawesome/brands/twitter - link: https://x.com/MelkeyDev - name: Twitter - generator: false + social: + - icon: fontawesome/brands/discord + link: https://discord.com/invite/HHZMSCu + name: Discord + - icon: fontawesome/brands/twitch + link: https://www.twitch.tv/melkey + name: Twitch + - icon: fontawesome/brands/youtube + link: https://www.youtube.com/@MelkeyDev + name: YouTube + - icon: fontawesome/brands/twitter + link: https://x.com/MelkeyDev + name: Twitter + generator: false copyright: Copyright © 2025 Melkey From f89c3a3c1c5ed6ce3c8a8bfbaa2f1729b05981ca Mon Sep 17 00:00:00 2001 From: Vicente Oros Date: Tue, 5 Aug 2025 21:37:30 -0600 Subject: [PATCH 34/34] update readme --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 652b7fee9..c5ce0ccea 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ 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 +- [SQLC](https://sqlc.dev/) for generating type-safe Go code from SQL queries Note: Selecting Tailwind option will automatically select HTMX unless React is explicitly selected @@ -214,10 +215,17 @@ React: go-blueprint create --advanced --feature react ``` +SQLC: + +```bash +go-blueprint create --advanced --feature sqlc +``` + + Or all features at once: ```bash -go-blueprint create --name my-project --framework chi --driver mysql --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind --feature docker --git commit --feature react +go-blueprint create --name my-project --framework chi --driver mysql --advanced --feature htmx --feature githubaction --feature websocket --feature tailwind --feature docker --git commit --feature react --feature sqlc ```