Skip to content

Add new blog example#9

Closed
connerohnesorge wants to merge 1 commit intomainfrom
codex/add-complete-example
Closed

Add new blog example#9
connerohnesorge wants to merge 1 commit intomainfrom
codex/add-complete-example

Conversation

@connerohnesorge
Copy link
Owner

@connerohnesorge connerohnesorge commented Jun 6, 2025

Summary

  • add a blog example under examples/
  • include blog views, static assets and generated class map
  • document how to run the blog example

Testing

  • go test ./...

https://chatgpt.com/codex/tasks/task_e_6843658a40b083339580585f676855ed

Summary by CodeRabbit

  • New Features

    • Introduced a complete blog example with a Go-based HTTP server, including graceful shutdown, static asset serving, and structured logging.
    • Added a templated blog page with a responsive header, navigation, sample posts, and a styled footer using Tailwind CSS.
    • Provided pre-built utility CSS classes for consistent styling.
    • Included setup instructions and configuration files for easy local deployment.
  • Documentation

    • Added a comprehensive README with setup and usage instructions.

@coderabbitai
Copy link

coderabbitai bot commented Jun 6, 2025

Walkthrough

A new blog example application is introduced, featuring a Go-based HTTP server, templated views, Tailwind CSS integration, and static asset embedding. The changes include setup files, code generation utilities, a Tailwind configuration, reusable CSS class definitions, and a README with usage instructions. All necessary components for running and building the example are provided.

Changes

File(s) Change Summary
examples/blog/README.md Added a README with an overview, features, and setup instructions for the blog example.
examples/blog/_static/doc.go Introduced Go package to embed static assets using Go's embed package.
examples/blog/classes/classes.go, classes.html Added generated cache mapping for CSS classes and a corresponding HTML file with class references.
examples/blog/gen.go Added code generation and Tailwind CSS build tool with timing and error handling.
examples/blog/go.mod Created a new Go module with dependency on templ.
examples/blog/input.css Added generated CSS file with .tw-* utility classes using Tailwind's @apply directive.
examples/blog/main.go Implemented the main HTTP server with graceful shutdown, route registration, and logging.
examples/blog/tailwind.config.js Added Tailwind CSS configuration targeting generated HTML for purging and default theme.
examples/blog/views/view.templ Created a templated blog view rendering the HTML structure with Tailwind classes and dynamic content.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant HTTPServer
    participant ViewTemplate
    participant StaticAssets

    User->>HTTPServer: Sends HTTP request (e.g., "/")
    HTTPServer->>ViewTemplate: Renders blog view
    ViewTemplate-->>HTTPServer: Returns HTML content
    HTTPServer-->>User: Serves HTML response

    User->>HTTPServer: Requests static asset (e.g., "/static/style.css")
    HTTPServer->>StaticAssets: Reads embedded asset
    StaticAssets-->>HTTPServer: Returns asset data
    HTTPServer-->>User: Serves static file
Loading
sequenceDiagram
    participant Developer
    participant CodeGenTool
    participant TailwindCLI

    Developer->>CodeGenTool: Run code generation (gen.go)
    CodeGenTool->>CodeGenTool: Generate Go code, HTML, and CSS input
    CodeGenTool->>TailwindCLI: Run Tailwind to build CSS
    TailwindCLI-->>CodeGenTool: Outputs compiled CSS
    CodeGenTool-->>Developer: Build complete, ready to run server
Loading

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (1.64.8)

level=warning msg="[runner] Can't run linter goanalysis_metalinter: buildir: failed to load package : could not load export data: no export data for "github.com/conneroisu/twerge""
level=error msg="Running error: can't run linter goanalysis_metalinter\nbuildir: failed to load package : could not load export data: no export data for "github.com/conneroisu/twerge""

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (8)
examples/blog/_static/doc.go (1)

6-7: Verify embed patterns for static assets

The //go:embed dist/* directive only captures files directly under dist. If your build produces nested directories, switch to a recursive pattern (e.g., //go:embed dist/**).

examples/blog/README.md (2)

10-10: Add a Prerequisites section

Before “## Setup and Running,” list required tools (Go ≥1.24, Node.js/Tailwind CLI, templ CLI) so users know what to install upfront.


23-23: Replace bare URL with markdown link

Wrap the URL or use a link to satisfy MD034 (no-bare-urls). For example:

Open your browser and navigate to [http://localhost:8080](http://localhost:8080)
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

23-23: Bare URL used
null

(MD034, no-bare-urls)

examples/blog/gen.go (1)

51-63: Document external dependency requirement.

The runTailwind() function assumes the tailwindcss CLI is available in PATH. Consider adding a dependency check or documenting this requirement in the README.

 func runTailwind() {
 	start := time.Now()
 	defer func() {
 		elapsed := time.Since(start)
 		fmt.Printf("(tailwind) Done in %s.\n", elapsed)
 	}()
+	// Check if tailwindcss is available
+	if _, err := exec.LookPath("tailwindcss"); err != nil {
+		panic("tailwindcss CLI not found in PATH. Please install it first.")
+	}
 	cmd := exec.Command("tailwindcss", "-i", "input.css", "-o", "_static/dist/styles.css")
examples/blog/views/view.templ (1)

58-58: Update copyright year to reflect current year.

The copyright year is set to 1999, which seems incorrect for a new example. Consider updating to the current year or using a more appropriate date.

-						&copy; 1999 twerge blog. All rights reserved.
+						&copy; 2024 twerge blog. All rights reserved.
examples/blog/input.css (1)

5-113: Fix inconsistent indentation in generated CSS classes.

The generated CSS classes have inconsistent indentation - some use tabs (lines 8-10) while others use spaces (lines 94, 99, 104, 109). Consider standardizing to either tabs or spaces throughout.

Example of inconsistency:

  • Lines 8-10: Use tabs for indentation
  • Lines 94, 99, 104, 109: Use spaces for indentation

Since this is generated code, the fix should be applied in the generation tool to ensure consistency.

examples/blog/main.go (2)

24-32: Good timeout configuration practices.

The timeout values are reasonable for a web server. Consider making the host configurable via environment variables for different deployment environments.

 const (
-	defaultHost       = "0.0.0.0"
+	defaultHost       = "localhost" // More secure default, bind to all interfaces only when needed
 	defaultPort       = "8080"

46-46: Remove unused parameter.

The second parameter in the Run function is never used. Consider removing it or documenting its intended purpose.

 func Run(
 	ctx context.Context,
-	_ func(string) string,
 ) error {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9920fd7 and 52ab485.

⛔ Files ignored due to path filters (2)
  • examples/blog/_static/dist/styles.css is excluded by !**/dist/**
  • examples/blog/go.sum is excluded by !**/*.sum
📒 Files selected for processing (10)
  • examples/blog/README.md (1 hunks)
  • examples/blog/_static/doc.go (1 hunks)
  • examples/blog/classes/classes.go (1 hunks)
  • examples/blog/classes/classes.html (1 hunks)
  • examples/blog/gen.go (1 hunks)
  • examples/blog/go.mod (1 hunks)
  • examples/blog/input.css (1 hunks)
  • examples/blog/main.go (1 hunks)
  • examples/blog/tailwind.config.js (1 hunks)
  • examples/blog/views/view.templ (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
examples/blog/classes/classes.go (2)
examples/dashboard/classes/classes.go (2)
  • SetCache (6-8)
  • ClassMapStr (10-299)
twerge.go (3)
  • Default (35-35)
  • Handler (72-76)
  • CacheValue (17-26)
examples/blog/gen.go (2)
tw.go (1)
  • CodeGen (26-56)
twerge.go (1)
  • Default (35-35)
🪛 markdownlint-cli2 (0.17.2)
examples/blog/README.md

23-23: Bare URL used
null

(MD034, no-bare-urls)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: checks
🔇 Additional comments (13)
examples/blog/go.mod (1)

1-6: Approve Go module setup

The go.mod correctly declares the module path and pins the templ dependency. Remember to run go mod tidy after merging to clean up any unused entries.

examples/blog/classes/classes.html (1)

1-25: Skip review for generated file

This HTML is auto-generated by twerge(devel) and should not be edited manually.

examples/blog/tailwind.config.js (1)

1-8: Approve Tailwind configuration

The content purge target is correctly set to ./classes/classes.html and the default theme/plugins are maintained. Ensure this covers all utility classes used at runtime.

examples/blog/classes/classes.go (2)

1-8: LGTM! Generated code follows expected pattern.

The generated code structure correctly implements the twerge caching pattern, with appropriate "DO NOT EDIT" warnings and proper function signature matching the Handler interface.


10-95:

Details

✅ Verification successful

Verify consistency between generated mappings and CSS classes.

The class mappings look well-structured, but ensure the Generated field values (tw-0 through tw-20) align with the CSS class definitions in the input.css file.


🏁 Script executed:

#!/bin/bash
# Description: Verify that all generated class names in the Go cache map have corresponding CSS definitions

# Extract generated class names from the cache map
rg -o '"tw-\d+"' examples/blog/classes/classes.go | sort -u > /tmp/go_classes.txt

# Extract CSS class names from input.css
rg -o '\.tw-\d+' examples/blog/input.css | sed 's/\.//' | sort -u > /tmp/css_classes.txt

# Compare the two lists
echo "Generated classes in Go file:"
cat /tmp/go_classes.txt

echo -e "\nCSS classes in input.css:"
cat /tmp/css_classes.txt

echo -e "\nMissing in CSS (should be empty):"
comm -23 /tmp/go_classes.txt /tmp/css_classes.txt

echo -e "\nMissing in Go cache (should be empty):"
comm -13 /tmp/go_classes.txt /tmp/css_classes.txt

Length of output: 1064


🏁 Script executed:

#!/bin/bash
# Re-verify that all generated class names in the Go cache map have corresponding CSS definitions using grep

# Extract generated class names from the Go cache map
rg -o '"tw-[0-9]\+"' examples/blog/classes/classes.go | sort -u > /tmp/go_classes.txt

# Extract CSS class names from input.css (strip leading dot)
rg -o '\.tw-[0-9]\+' examples/blog/input.css | sed 's/\.//' | sort -u > /tmp/css_classes.txt

# Compare lists
echo "Missing in CSS (defined in Go but not in CSS):"
grep -Fxv -f /tmp/css_classes.txt /tmp/go_classes.txt || echo "  (none)"

echo -e "\nMissing in Go (defined in CSS but not in Go):"
grep -Fxv -f /tmp/go_classes.txt /tmp/css_classes.txt || echo "  (none)"

Length of output: 513


Generated class mappings are fully consistent with CSS definitions

Verified that every Generated identifier (tw-0 through tw-20) in examples/blog/classes/classes.go has a matching rule in examples/blog/input.css. No missing or extra entries were found—no further action required.

examples/blog/gen.go (2)

1-3: LGTM! Proper build constraints for generation tool.

The build constraints correctly prevent this tool from being included in normal builds while allowing it to be executed via go run.


34-42:

Details

❓ Verification inconclusive

Verify template component rendering and file paths.

The code generation call looks correct, but ensure that the views.View() component can be rendered and that the hardcoded file paths exist in the expected locations.


🏁 Script executed:

#!/bin/bash
# Description: Verify the generated view component exists and file paths are valid

# Check if the view component file exists
echo "Checking for view component files:"
fd -t f "view.*\.go$" examples/blog/views/

# Check if the target directories exist for generated files
echo -e "\nChecking target directories:"
[ -d "examples/blog/classes" ] && echo "✓ classes/ directory exists" || echo "✗ classes/ directory missing"

# Check if input.css exists
[ -f "examples/blog/input.css" ] && echo "✓ input.css exists" || echo "✗ input.css missing"

# Verify the view template can be found
echo -e "\nSearching for View template definition:"
rg -A 5 "func View\(\)" examples/blog/views/

Length of output: 522


🏁 Script executed:

#!/bin/bash
# Re-verify the contents of examples/blog/views and presence of View()

echo "Checking if views directory exists:"
if [ -d "examples/blog/views" ]; then
  echo "✓ examples/blog/views exists"
else
  echo "✗ examples/blog/views is missing"
  exit 1
fi

echo -e "\nListing all files under examples/blog/views:"
find examples/blog/views -maxdepth 1 -type f -print || echo "Failed to list files"

echo -e "\nListing all Go source files under examples/blog/views:"
find examples/blog/views -type f -name '*.go' -print

echo -e "\nSearching for 'func View' definition:"
rg -n "func View" examples/blog/views

echo -e "\nSearching for any View references in the package:"
rg -n "View\(" examples/blog/views

Length of output: 887


Ensure the views.View() function is defined in the examples/blog/views package

The codegen call in examples/blog/gen.go (lines 34–42) references views.View(), but examples/blog/views only contains view.templ—no Go source defines func View(). To fix:

• Add or generate a Go file in examples/blog/views (e.g. view.go) with:

package views

// View returns the rendered template or component for your blog.
func View() /* return type */ {
    // implementation or call into your templating engine
}

• If you rely on your templ tool to convert view.templ into Go code, run or include that code‐generation step so that func View() is present at build time.

• Re-run go build (or your templ generator) in examples/blog/views and confirm that views.View() compiles and returns the expected template.

fix_required

examples/blog/views/view.templ (2)

5-13: LGTM! Proper HTML5 structure with semantic elements.

The document structure follows HTML5 best practices with proper meta tags, title, and viewport configuration.


12-12:

Details

✅ Verification successful

Verify CSS resource path consistency.

The stylesheet reference uses /dist/styles.css but the generation tool outputs to _static/dist/styles.css. Ensure the server properly maps these paths.


🏁 Script executed:

#!/bin/bash
# Description: Check CSS output path consistency between template and generation tool

echo "CSS reference in template:"
rg 'href="/dist/styles\.css"' examples/blog/views/view.templ

echo -e "\nCSS output path in generation tool:"
rg '_static/dist/styles\.css' examples/blog/gen.go

echo -e "\nCheck if server maps /dist to _static/dist:"
rg -A 5 -B 5 '/dist|_static' examples/blog/main.go

Length of output: 1061


✅ CSS resource path mapping confirmed.
The template’s <link href="/dist/styles.css">, the Tailwind output to _static/dist/styles.css, and the server route

mux.Handle("/dist/", http.FileServer(http.FS(static.Assets)))

all align correctly. No further changes needed.

examples/blog/input.css (2)

1-4: LGTM! Proper Tailwind CSS imports.

The standard Tailwind CSS imports are correctly structured to include base styles, components, and utilities.


8-110:

Details

❓ Verification inconclusive

Verify CSS class definitions match the Go cache mappings.

The CSS class names and their utility combinations should align with the cache mappings in classes.go. Ensure the @apply directives use the same utility order as the Merged field in the cache.


🏁 Script executed:

#!/bin/bash
# Description: Compare CSS class utilities with Go cache Merged field values

echo "Extracting utility classes from CSS @apply directives:"
rg -A 1 '\.tw-\d+' examples/blog/input.css | rg '@apply' | sed 's/.*@apply\s*//' | sed 's/\s*;.*//' > /tmp/css_utilities.txt

echo -e "\nExtracting Merged values from Go cache:"
rg 'Merged:\s*"[^"]*"' examples/blog/classes/classes.go | sed 's/.*Merged:\s*"//' | sed 's/".*//' > /tmp/go_utilities.txt

echo -e "\nComparing utility combinations (should match when sorted):"
echo "CSS utilities:"
cat /tmp/css_utilities.txt | head -5

echo -e "\nGo cache Merged values:"
cat /tmp/go_utilities.txt | head -5

echo -e "\nChecking for mismatches:"
paste -d'|' /tmp/css_utilities.txt /tmp/go_utilities.txt | while IFS='|' read css_utils go_utils; do
    css_sorted=$(echo "$css_utils" | tr ' ' '\n' | sort | tr '\n' ' ' | sed 's/ $//')
    go_sorted=$(echo "$go_utils" | tr ' ' '\n' | sort | tr '\n' ' ' | sed 's/ $//')
    if [ "$css_sorted" != "$go_sorted" ]; then
        echo "MISMATCH:"
        echo "  CSS: $css_utils"
        echo "  Go:  $go_utils"
    fi
done

Length of output: 1382


Verify class mappings between CSS and Go cache for each tw-<id>

#!/bin/bash
echo "Checking mismatches between CSS and Go mappings for tw-0 to tw-20:"

for id in {0..20}; do
    css=$(grep -A1 "^\.tw-$id " -n examples/blog/input.css \
          | grep "@apply" \
          | sed -E 's/.*@apply (.*);/\1/')
    go=$(awk '/case "'"$id"'":/{getline; if(/Merged:/){gsub(/.*Merged: "([^"]+)".*/,"\\1",$0); print}}' \
         examples/blog/classes/classes.go)
    if [ -z "$css" ] || [ -z "$go" ]; then
        echo "  tw-$id: missing CSS or Go mapping"
        continue
    fi
    css_sorted=$(echo "$css" | tr ' ' '\n' | sort | tr '\n' ' ' | sed 's/ $//')
    go_sorted=$(echo "$go"  | tr ' ' '\n' | sort | tr '\n' ' ' | sed 's/ $//')
    if [ "$css_sorted" != "$go_sorted" ]; then
        echo "MISMATCH for tw-$id:"
        echo "  CSS: $css"
        echo "  Go:  $go"
    fi
done

Run this script to pinpoint any tw-<id> entries whose utility sets differ between examples/blog/input.css and the Merged field in examples/blog/classes/classes.go. Resolve any mismatches so that each CSS class’s @apply utilities exactly match the corresponding Go cache mapping.

examples/blog/main.go (2)

1-22: LGTM! Clean package structure and imports.

The package declaration and imports are well-organized and appropriate for an HTTP server application.


169-173: LGTM! Clean main function.

The main function is appropriately simple and follows Go conventions.

</div>
</footer>
<!-- Custom JavaScript -->
<script src="/static/js/main.js"></script>
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify JavaScript file exists and path mapping.

The template references /static/js/main.js but it's unclear if this file exists or if the path is properly mapped by the server.


🏁 Script executed:

#!/bin/bash
# Description: Check if the referenced JavaScript file exists and verify path mapping

echo "JavaScript reference in template:"
rg 'src="/static/js/main\.js"' examples/blog/views/view.templ

echo -e "\nSearching for main.js file:"
fd "main\.js" examples/blog/

echo -e "\nChecking static file mappings in server:"
rg -A 5 -B 5 'static.*js|js.*static' examples/blog/main.go

echo -e "\nChecking _static directory structure:"
fd -t f "\.js$" examples/blog/_static/ 2>/dev/null || echo "No JS files found in _static/"

Length of output: 578


🏁 Script executed:

#!/bin/bash
# Search for main.js anywhere in the repository
echo "=== Searching for main.js files ==="
fd "main\.js" .

# List all directories named 'static'
echo -e "\n=== Listing 'static' directories ==="
fd -t d --ignore-case static .

# List all .js files under any 'static' directory
echo -e "\n=== Listing .js files inside 'static' directories ==="
find . -type f -path "*/static/*" -name "*.js"

# Search for Go HTTP static file server setup
echo -e "\n=== Searching for FileServer usage in Go code ==="
rg -A3 -B3 'FileServer' .

echo -e "\n=== Searching for StripPrefix usage in Go code ==="
rg -A3 -B3 'StripPrefix' .

Length of output: 6478


Verify JavaScript asset path and server mapping

The template at examples/blog/views/view.templ (line 63) references /static/js/main.js, but:

  • No main.js exists in examples/blog/_static (or elsewhere in the repo).
  • The Go server in examples/blog/main.go only serves embedded assets under the /dist/ prefix (mux.Handle("/dist/", http.FileServer(http.FS(static.Assets)))).

Please either:

  • Update the <script> tag to point to the correct served path (e.g. /dist/js/main.js), or
  • Place your compiled main.js under the _static/js/ directory and adjust the server routing to serve /static/.

Suggested change:

-   <script src="/static/js/main.js"></script>
+   <script src="/dist/js/main.js"></script>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<script src="/static/js/main.js"></script>
<script src="/dist/js/main.js"></script>
🤖 Prompt for AI Agents
In examples/blog/views/view.templ at line 63, the script tag references
/static/js/main.js, but this file does not exist and the server only serves
static files under the /dist/ prefix. To fix this, update the script src
attribute to /dist/js/main.js to match the server's static file serving path,
ensuring the JavaScript file is correctly loaded.

Comment on lines +66 to +72
classes.SetCache()
mux := http.NewServeMux()
err := AddRoutes(mux)
if err != nil {
slog.Error("error adding routes", slog.String("error", err.Error()))
log.Fatal(err)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unnecessary error handling after AddRoutes simplification.

Since AddRoutes no longer returns an error, this error handling can be removed.

 	classes.SetCache()
 	mux := http.NewServeMux()
-	err := AddRoutes(mux)
-	if err != nil {
-		slog.Error("error adding routes", slog.String("error", err.Error()))
-		log.Fatal(err)
-	}
+	AddRoutes(mux)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
classes.SetCache()
mux := http.NewServeMux()
err := AddRoutes(mux)
if err != nil {
slog.Error("error adding routes", slog.String("error", err.Error()))
log.Fatal(err)
}
classes.SetCache()
mux := http.NewServeMux()
AddRoutes(mux)
🤖 Prompt for AI Agents
In examples/blog/main.go around lines 66 to 72, the AddRoutes function no longer
returns an error, so the existing error handling after calling AddRoutes is
unnecessary. Remove the error variable declaration, the if statement checking
for the error, and the associated logging and fatal calls to simplify the code.

Comment on lines +34 to +41
// AddRoutes adds routes to the mux
func AddRoutes(
mux *http.ServeMux,
) error {
mux.Handle("/{$}", templ.Handler(views.View()))
mux.Handle("/dist/", http.FileServer(http.FS(static.Assets)))
return nil
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify the AddRoutes function.

The function always returns nil, making the error return unnecessary. Also, there's a mismatch between the route /dist/ and the documentation that mentions /static/.

-// AddRoutes adds routes to the mux
-func AddRoutes(
-	mux *http.ServeMux,
-) error {
+// AddRoutes adds routes to the mux
+func AddRoutes(mux *http.ServeMux) {
 	mux.Handle("/{$}", templ.Handler(views.View()))
 	mux.Handle("/dist/", http.FileServer(http.FS(static.Assets)))
-	return nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// AddRoutes adds routes to the mux
func AddRoutes(
mux *http.ServeMux,
) error {
mux.Handle("/{$}", templ.Handler(views.View()))
mux.Handle("/dist/", http.FileServer(http.FS(static.Assets)))
return nil
}
// AddRoutes adds routes to the mux
func AddRoutes(mux *http.ServeMux) {
mux.Handle("/{$}", templ.Handler(views.View()))
mux.Handle("/dist/", http.FileServer(http.FS(static.Assets)))
}
🤖 Prompt for AI Agents
In examples/blog/main.go around lines 34 to 41, simplify the AddRoutes function
by removing the error return type since it always returns nil. Also, update the
route from "/dist/" to "/static/" to match the documentation and ensure
consistency. Adjust the mux.Handle call accordingly to serve static assets under
the "/static/" path.

Comment on lines +118 to +166
case <-innerCtx.Done():
// Signal received, initiate graceful shutdown
slog.Info("shutdown signal received, shutting down server...")

// Create shutdown context with timeout
shutdownCtx, cancel := context.WithTimeout(
context.Background(), // Use a fresh context for shutdown
shutdownTimeout,
)
defer cancel()

// Attempt graceful shutdown
err := httpServer.Shutdown(shutdownCtx)
if err != nil {
slog.Error("error during server shutdown",
slog.String("error", err.Error()),
slog.Duration("timeout", shutdownTimeout),
)
}

// Wait for all goroutines to finish
slog.Info("waiting for server shutdown to complete")
wg.Wait()
slog.Info("server shutdown completed")
return nil
case <-ctx.Done():
// Parent context cancelled
slog.Info("parent context cancelled, shutting down...")

// Create shutdown context with timeout
shutdownCtx, cancel := context.WithTimeout(
context.Background(), // Use a fresh context for shutdown
shutdownTimeout,
)
defer cancel()

// Attempt graceful shutdown
err := httpServer.Shutdown(shutdownCtx)
if err != nil {
slog.Error("error during server shutdown",
slog.String("error", err.Error()),
slog.Duration("timeout", shutdownTimeout),
)
}

// Wait for all goroutines to finish
wg.Wait()
return nil
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Eliminate duplicate shutdown code.

The shutdown logic for signal handling and parent context cancellation is nearly identical, leading to code duplication.

+	// Helper function for graceful shutdown
+	performShutdown := func(reason string) error {
+		slog.Info(reason)
+		shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
+		defer cancel()
+		
+		err := httpServer.Shutdown(shutdownCtx)
+		if err != nil {
+			slog.Error("error during server shutdown",
+				slog.String("error", err.Error()),
+				slog.Duration("timeout", shutdownTimeout),
+			)
+		}
+		wg.Wait()
+		return nil
+	}
+
 	// Wait for either server error or shutdown signal
 	select {
 	case err := <-serverErrors:
 		return fmt.Errorf("server error: %w", err)
 	case <-innerCtx.Done():
-		// Signal received, initiate graceful shutdown
-		slog.Info("shutdown signal received, shutting down server...")
-
-		// Create shutdown context with timeout
-		shutdownCtx, cancel := context.WithTimeout(
-			context.Background(), // Use a fresh context for shutdown
-			shutdownTimeout,
-		)
-		defer cancel()
-
-		// Attempt graceful shutdown
-		err := httpServer.Shutdown(shutdownCtx)
-		if err != nil {
-			slog.Error("error during server shutdown",
-				slog.String("error", err.Error()),
-				slog.Duration("timeout", shutdownTimeout),
-			)
-		}
-
-		// Wait for all goroutines to finish
-		slog.Info("waiting for server shutdown to complete")
-		wg.Wait()
-		slog.Info("server shutdown completed")
-		return nil
+		return performShutdown("shutdown signal received, shutting down server...")
 	case <-ctx.Done():
-		// Parent context cancelled
-		slog.Info("parent context cancelled, shutting down...")
-
-		// Create shutdown context with timeout
-		shutdownCtx, cancel := context.WithTimeout(
-			context.Background(), // Use a fresh context for shutdown
-			shutdownTimeout,
-		)
-		defer cancel()
-
-		// Attempt graceful shutdown
-		err := httpServer.Shutdown(shutdownCtx)
-		if err != nil {
-			slog.Error("error during server shutdown",
-				slog.String("error", err.Error()),
-				slog.Duration("timeout", shutdownTimeout),
-			)
-		}
-
-		// Wait for all goroutines to finish
-		wg.Wait()
-		return nil
+		return performShutdown("parent context cancelled, shutting down...")
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case <-innerCtx.Done():
// Signal received, initiate graceful shutdown
slog.Info("shutdown signal received, shutting down server...")
// Create shutdown context with timeout
shutdownCtx, cancel := context.WithTimeout(
context.Background(), // Use a fresh context for shutdown
shutdownTimeout,
)
defer cancel()
// Attempt graceful shutdown
err := httpServer.Shutdown(shutdownCtx)
if err != nil {
slog.Error("error during server shutdown",
slog.String("error", err.Error()),
slog.Duration("timeout", shutdownTimeout),
)
}
// Wait for all goroutines to finish
slog.Info("waiting for server shutdown to complete")
wg.Wait()
slog.Info("server shutdown completed")
return nil
case <-ctx.Done():
// Parent context cancelled
slog.Info("parent context cancelled, shutting down...")
// Create shutdown context with timeout
shutdownCtx, cancel := context.WithTimeout(
context.Background(), // Use a fresh context for shutdown
shutdownTimeout,
)
defer cancel()
// Attempt graceful shutdown
err := httpServer.Shutdown(shutdownCtx)
if err != nil {
slog.Error("error during server shutdown",
slog.String("error", err.Error()),
slog.Duration("timeout", shutdownTimeout),
)
}
// Wait for all goroutines to finish
wg.Wait()
return nil
}
// Helper function for graceful shutdown
performShutdown := func(reason string) error {
slog.Info(reason)
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()
err := httpServer.Shutdown(shutdownCtx)
if err != nil {
slog.Error("error during server shutdown",
slog.String("error", err.Error()),
slog.Duration("timeout", shutdownTimeout),
)
}
wg.Wait()
return nil
}
// Wait for either server error or shutdown signal
select {
case err := <-serverErrors:
return fmt.Errorf("server error: %w", err)
case <-innerCtx.Done():
return performShutdown("shutdown signal received, shutting down server...")
case <-ctx.Done():
return performShutdown("parent context cancelled, shutting down...")
}
🤖 Prompt for AI Agents
In examples/blog/main.go around lines 118 to 166, the shutdown logic for both
signal handling and parent context cancellation is duplicated. Refactor by
extracting the common shutdown steps into a separate function that accepts the
necessary contexts and parameters, then call this function from both cases to
eliminate redundancy and improve code maintainability.

@connerohnesorge connerohnesorge deleted the codex/add-complete-example branch June 9, 2025 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant