updated flake, added debug handler, and added example for debug handler#8
updated flake, added debug handler, and added example for debug handler#8connerohnesorge merged 4 commits intomainfrom
Conversation
WalkthroughThe changes introduce a new debug handler in the Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant HTTP Server
participant Handler (templ)
participant twerge.DebugHandler
User->>HTTP Server: Sends HTTP request
HTTP Server->>Handler (templ): Route handler invoked
Handler (templ)->>twerge.DebugHandler: Call It() with class string
twerge.DebugHandler-->>Handler (templ): Return original class string
Handler (templ)-->>HTTP Server: Rendered HTML response
HTTP Server-->>User: Sends HTML response
sequenceDiagram
participant OS
participant Main
participant HTTP Server
Main->>HTTP Server: Start server in goroutine
OS-->>Main: Sends interrupt/termination signal
Main->>HTTP Server: Initiate graceful shutdown with timeout
HTTP Server-->>Main: Shutdown complete
Main->>Main: Wait for goroutines, then exit
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (10)
examples/simple-debug/input.css (1)
6-7: Development tool identification.The code is marked as generated by "twerge(devel)", indicating this is from a development version of the tool. For a stable example, consider removing the "(devel)" suffix once the tool reaches a stable release.
debug.go (3)
22-22: Remove the GoImpl comment directive.The
:GoImplcomment appears to be an IDE-specific directive that shouldn't be in the final code. This is likely a development aid that should be removed before committing.-// :GoImpl d *DebugHandler twerge.Handler
27-28: Fix documentation reference from Generator to DebugHandler.The comment incorrectly refers to a "Generator" when it should reference the "DebugHandler" for consistency.
-// Cache returns the cache of the [Generator]. +// Cache returns the cache of the [DebugHandler].
30-30: Fix documentation reference from Generator to DebugHandler.The comment incorrectly refers to a "Generator" when it should reference the "DebugHandler" for consistency.
-// SetCache sets the cache of the [Generator]. +// SetCache sets the cache of the [DebugHandler].examples/simple-debug/gen.go (1)
51-63: Refactor timing code to avoid duplication.The timing code in
runTailwind()duplicates logic already present in the main function. Consider extracting this to a helper function.+func timed(name string, fn func() error) error { + start := time.Now() + defer func() { + elapsed := time.Since(start) + fmt.Printf("(%s) Done in %s.\n", name, elapsed) + }() + return fn() +} func runTailwind() { - start := time.Now() - defer func() { - elapsed := time.Since(start) - fmt.Printf("(tailwind) Done in %s.\n", elapsed) - }() cmd := exec.Command("tailwindcss", "-i", "input.css", "-o", "_static/dist/styles.css") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - panic(err) - } + return timed("tailwind", func() error { + return cmd.Run() + }) }examples/simple-debug/views/view.templ (2)
33-35: Improve placeholder content comment.The "Content" placeholder text could be confusing to users. Consider making it clearer that this is where dynamic content should be inserted.
- <!-- Page content will be inserted here --> - Content + <!-- + Page content will be inserted here. + Replace this placeholder with dynamic content from your application. + --> + <div class={ twerge.It("p-4 bg-white rounded shadow") }> + <h2 class={ twerge.It("text-xl font-bold") }>Welcome to Stellar</h2> + <p class={ twerge.It("mt-2") }>This is a placeholder. Replace with your content.</p> + </div>
52-52: Update copyright year.The copyright year is set to 1999, which seems outdated. Consider updating it to a more recent year or using dynamic generation.
- © 1999 stellar. All rights reserved. + © 2023 stellar. All rights reserved.examples/simple-debug/main.go (3)
43-45: Remove unused parameter.The function signature includes an unused parameter
_ func(string) string. Consider removing it if it's not needed.-func Run( - ctx context.Context, - _ func(string) string, -) error { +func Run(ctx context.Context) error {And update the call in main:
- if err := Run(context.Background(), os.Getenv); err != nil { + if err := Run(context.Background()); err != nil {
36-39: Remove unnecessary error return in AddRoutes.The
AddRoutesfunction always returns nil, making the error return unnecessary. Either implement error handling or simplify the function.-func AddRoutes( - mux *http.ServeMux, -) error { - mux.Handle("/{$}", templ.Handler(views.View())) - return nil +func AddRoutes(mux *http.ServeMux) { + mux.Handle("/", templ.Handler(views.View())) +}And update the call:
- err := AddRoutes(mux) - if err != nil { - slog.Error("error adding routes", slog.String("error", err.Error())) - log.Fatal(err) - } + AddRoutes(mux)
64-64: Add comment explaining debug handler usage.Line 64 sets the default handler to a debug handler without explanation. Add a comment indicating this is for demonstration purposes.
+ // Use the DebugHandler for this example. Not recommended for production use. twerge.SetDefault(twerge.New(twerge.NewDebugHandler()))
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
examples/simple-debug/go.sumis excluded by!**/*.sumflake.lockis excluded by!**/*.lock
📒 Files selected for processing (8)
debug.go(1 hunks)examples/simple-debug/gen.go(1 hunks)examples/simple-debug/go.mod(1 hunks)examples/simple-debug/input.css(1 hunks)examples/simple-debug/main.go(1 hunks)examples/simple-debug/tailwind.config.js(1 hunks)examples/simple-debug/views/view.templ(1 hunks)examples/simple-debug/views/view_templ.go(24 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
debug.go (1)
twerge.go (2)
CacheValue(17-26)It(55-57)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: checks
🔇 Additional comments (8)
examples/simple-debug/go.mod (1)
1-5: Module configuration looks good.The module is properly configured with a clear path and dependency on the templ package. The Go version specified (1.24.1) is very recent, which is fine for an example but might limit compatibility with older Go installations.
examples/simple-debug/tailwind.config.js (1)
1-8: Minimal but sufficient Tailwind configuration.The configuration is intentionally minimal, focusing only on the content source without any theme extensions or plugins, which is appropriate for a simple debug example.
examples/simple-debug/views/view_templ.go (4)
3-3: Version update to v0.3.865.The templ version has been updated from v0.3.850 to v0.3.865, which is a good practice to keep dependencies current.
34-34: Using Tailwind CSS from CDN.The template now uses Tailwind CSS from a CDN instead of a local stylesheet, which simplifies the example setup. This is appropriate for a debug example, though be aware that using CDN resources introduces external dependencies.
50-50: Updated error file path references.All error return statements now correctly reference
views/view.templinstead of the previous path, which improves error reporting in this example.Also applies to: 72-72, 94-94, 116-116, 138-138, 160-160, 182-182, 204-204, 226-226, 248-248, 270-270, 292-292, 314-314, 336-336, 358-358, 380-380, 402-402, 424-424, 446-446, 468-468, 490-490, 512-512
518-518: Simplified footer HTML structure.The footer's closing HTML has been simplified by removing the inclusion of a custom JavaScript script tag that was previously present. This keeps the example focused on the CSS debugging aspects.
examples/simple-debug/input.css (2)
1-4: Standard Tailwind CSS imports.The file properly imports Tailwind's base, components, and utilities layers, which is the recommended setup.
5-98: Generated utility classes with clear structure.The file contains generated utility classes (.tw-0 through .tw-17) with each class including a comment that shows the original Tailwind classes it was generated from. This approach:
- Makes the generation process transparent
- Provides a clear mapping between numeric IDs and their component classes
- Uses standard Tailwind @apply directive for composition
This structure supports the debug example's purpose of demonstrating Tailwind class handling.
| @@ -0,0 +1,8 @@ | |||
| /** @type {import('tailwindcss').Config} */ | |||
| export default { | |||
| content: ["./classes/classes.html"], | |||
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify content path for Tailwind scanning.
The content path ./classes/classes.html seems very specific. Make sure this file exists in your project structure and contains all the Tailwind classes you need to be included in the generated CSS.
🏁 Script executed:
#!/bin/bash
# Check if the classes.html file exists at the expected location
if [ -f "./examples/simple-debug/classes/classes.html" ]; then
echo "✅ File exists: ./examples/simple-debug/classes/classes.html"
echo "File content:"
cat "./examples/simple-debug/classes/classes.html"
else
echo "❌ File not found: ./examples/simple-debug/classes/classes.html"
# Check if the directory exists
if [ -d "./examples/simple-debug/classes" ]; then
echo "Directory exists, but file is missing"
echo "Files in directory:"
ls -la "./examples/simple-debug/classes"
else
echo "Directory does not exist"
fi
fiLength of output: 296
Incorrect content path: missing classes/classes.html in examples/simple-debug
The Tailwind config’s content entry on line 3 points to ./classes/classes.html, but that file (and directory) does not exist under examples/simple-debug. Update this path to match where your HTML files actually live. For example:
• If your HTML is directly under the example root:
content: ["./examples/simple-debug/*.html"],• If it lives in a public folder:
content: ["./examples/simple-debug/public/**/*.html"],Locations to update:
- examples/simple-debug/tailwind.config.js:3
🤖 Prompt for AI Agents (early access)
In examples/simple-debug/tailwind.config.js at line 3, the content path
"./classes/classes.html" is incorrect because the file and directory do not
exist. Update the content array to point to the actual location of your HTML
files, such as "./examples/simple-debug/*.html" if the HTML files are in the
example root, or adjust the path accordingly if they are in a subfolder like
"public". This ensures Tailwind scans the correct files for class usage.
examples/simple-debug/gen.go
Outdated
| elapsed := time.Since(start) | ||
| fmt.Printf("(tailwind) Done in %s.\n", elapsed) | ||
| }() | ||
| cmd := exec.Command("tailwindcss", "-i", "input.css", "-o", "_static/dist/styles.css") |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add error handling for missing Tailwind CLI.
The code assumes that the tailwindcss command is available in the PATH, but doesn't provide helpful error messages if it's not installed.
#!/bin/bash
# Check if tailwindcss is installed
which tailwindcss > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Error: tailwindcss not found in PATH"
echo "Please install it with: npm install -g tailwindcss"
exit 1
fi🤖 Prompt for AI Agents (early access)
In examples/simple-debug/gen.go at line 57, the code runs the tailwindcss
command without checking if it is installed, which can cause unclear errors. Add
a check before executing the command to verify if the tailwindcss CLI is
available in the system PATH. If it is missing, return or log a clear error
message instructing the user to install tailwindcss, preventing the command from
running and failing silently.
examples/simple-debug/gen.go
Outdated
| "time" | ||
|
|
||
| "github.com/conneroisu/twerge" | ||
| "github.com/conneroisu/twerge/examples/simple/views" |
There was a problem hiding this comment.
Update import path to match directory structure.
The import path references examples/simple/views but the directory path is examples/simple-debug/views, which may cause compilation errors.
- "github.com/conneroisu/twerge/examples/simple/views"
+ "github.com/conneroisu/twerge/examples/simple-debug/views"📝 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.
| "github.com/conneroisu/twerge/examples/simple/views" | |
| - "github.com/conneroisu/twerge/examples/simple/views" | |
| + "github.com/conneroisu/twerge/examples/simple-debug/views" |
🤖 Prompt for AI Agents (early access)
In examples/simple-debug/gen.go at line 14, the import path incorrectly
references "examples/simple/views" instead of "examples/simple-debug/views".
Update the import statement to use the correct path
"github.com/conneroisu/twerge/examples/simple-debug/views" to match the
directory structure and avoid compilation errors.
| <meta charset="UTF-8"/> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | ||
| <title>stellar</title> | ||
| <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script> |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Clarify CDN usage vs local CSS builds.
The template uses Tailwind CSS from a CDN, but the project also generates local CSS files via the build script. This inconsistency might lead to style differences between development and production.
Consider one of these approaches:
- Use the locally generated CSS file for consistency with the build process
- Add a comment explaining why the CDN is used instead of the local build
- <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
+ <!-- For development purposes only - use local CSS in production -->
+ <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
+ <!-- Uncomment for production: <link rel="stylesheet" href="/_static/dist/styles.css"> -->Run this script to check if the build process is generating CSS files correctly:
🏁 Script executed:
#!/bin/bash
# Check if the local CSS file exists and if CDN is still referenced
if [ -f "_static/dist/styles.css" ]; then
echo "Local CSS file exists:"
ls -la "_static/dist/styles.css"
echo "File size: $(du -h _static/dist/styles.css | cut -f1)"
# Check first few lines of the CSS file
echo "CSS file preview:"
head -n 5 "_static/dist/styles.css"
else
echo "Local CSS file does not exist. Build process may be incomplete."
fiLength of output: 173
Fix local CSS build or clarify CDN usage
The check shows that _static/dist/styles.css doesn’t exist, so the template always falls back to the CDN. Please address this by choosing one of the following:
• Implement or correct the build step so that _static/dist/styles.css is generated, and then load it in production:
- <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
+ <!-- In production, load the locally built CSS -->
+ <link rel="stylesheet" href="/_static/dist/styles.css">• If you intend to rely on the CDN in all environments, add a clarifying comment:
<!-- Tailwind CSS is loaded via CDN because no local build is configured -->
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>🤖 Prompt for AI Agents (early access)
In examples/simple-debug/views/view.templ at line 12, the template loads
Tailwind CSS from a CDN while the local CSS file _static/dist/styles.css does
not exist, causing inconsistency. Fix this by either implementing or correcting
the build process to generate the local CSS file and update the template to load
it instead of the CDN, or if relying on the CDN is intentional, add a comment
above the script tag explaining that Tailwind CSS is loaded via CDN because no
local build is configured.
| func AddRoutes( | ||
| mux *http.ServeMux, | ||
| ) error { | ||
| mux.Handle("/{$}", templ.Handler(views.View())) |
There was a problem hiding this comment.
Fix route pattern syntax.
The route pattern "/{$}" appears to be invalid. If you intend to match the root path, use "/" instead. If you need to match all paths, use "/" and handle sub-paths in your handler.
- mux.Handle("/{$}", templ.Handler(views.View()))
+ mux.Handle("/", templ.Handler(views.View()))📝 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.
| mux.Handle("/{$}", templ.Handler(views.View())) | |
| mux.Handle("/", templ.Handler(views.View())) |
🤖 Prompt for AI Agents (early access)
In examples/simple-debug/main.go at line 37, the route pattern "/{$}" is
invalid. Replace it with "/" to correctly match the root path. If you want to
handle all sub-paths, keep the pattern as "/" and implement the necessary logic
inside the handler to manage sub-paths.
| 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 | ||
| 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 | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Refactor duplicate shutdown logic.
There's significant code duplication between the signal-based shutdown and context-based shutdown paths. Consider refactoring this to a separate function.
+// performGracefulShutdown attempts to gracefully shut down the HTTP server
+func performGracefulShutdown(httpServer *http.Server, wg *sync.WaitGroup) {
+ // 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")
+}
// 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")
+ performGracefulShutdown(httpServer, &wg)
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()
+ performGracefulShutdown(httpServer, &wg)
return nil
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents (early access)
In examples/simple-debug/main.go between lines 113 and 164, the shutdown logic
for both signal-based and context-based cases is duplicated. Refactor by
extracting the shutdown steps—creating a shutdown context with timeout, calling
httpServer.Shutdown, logging errors if any, and waiting for goroutines—into a
separate function. Then call this function from both select cases to eliminate
duplication and improve code clarity.
…g a read lock, which could cause data races if the map is being modified concurrently.
Summary by CodeRabbit
New Features
Chores