Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

templ generate fails on a large file #1049

Open
diyor28 opened this issue Jan 17, 2025 · 4 comments
Open

templ generate fails on a large file #1049

diyor28 opened this issue Jan 17, 2025 · 4 comments

Comments

@diyor28
Copy link

diyor28 commented Jan 17, 2025

Describe the Bug

The templ generate command fails to generate .go code for a large file, citing syntax errors. However, when the file is divided into smaller parts, the generation works without any issues.

To Reproduce

Steps to reproduce the behavior:

  1. Run templ generate on the following file: Gist Link
  2. Observe the error output.

Expected Behavior

The .go code should be generated successfully without errors, even for large files.

Screenshots

Error Screenshot

Logs

templ generate
(✗) Error [ error=failed to generate code for "/Users/diyorkhaydarov/Projects/sdk/icons/icons.templ": /Users/diyorkhaydarov/Projects/sdk/icons/icons.templ parsing error: templ: malformed templ expression, expected `templ functionName() {`: line 20, col 0 ]
(✗) Command failed: generation completed with 1 errors

templ info Output

(✓) os [ goos=darwin goarch=arm64 ]
(✓) go [ location=/opt/homebrew/bin/go version=go version go1.23.2 darwin/arm64 ]
(✓) gopls [ location=/Users/diyorkhaydarov/go/bin/gopls version=golang.org/x/tools/gopls v0.17.1 ]
(✓) templ [ location=/Users/diyorkhaydarov/go/bin/templ version=v0.3.819 ]
@epicbytes
Copy link

epicbytes commented Jan 17, 2025

Use SVG sprites for easier filtering by running the SVGs through an optimizer to reduce their size. You can then include the sprites using a common component like Icon and specify the ID of the chunk in the sprite.

The advantage of this approach is that it doesn't bloat the HTML response, and static resources are processed by the browser as static content, benefiting from features like caching.

package static

import "..../components/shared"

type icon struct {
	Icon      shared.SpritesBase `json:"icon"`
	IconClass templ.CSSClasses   `json:"icon-class"`
}

type IconOption func(*icon)

templ (i *icon) render() {
	<svg class={ i.IconClass }>
		<use crossorigin="anonymous" xlink:href={ i.Icon.SpritePath() }></use>
	</svg>
}

func WithIconClass(iconClass templ.CSSClasses) IconOption {
	return func(i *icon) {
		i.IconClass = iconClass
	}
}

func NewIcon(iconName shared.SpritesBase, options ...IconOption) templ.Component {
	newIcon := &icon{
		Icon:      iconName,
		IconClass: templ.Classes("w-4", "h-4", "flex-shrink-0"),
	}

	for _, opt := range options {
		opt(newIcon)
	}

	return newIcon.render()
}

To preserve types, you can use a pass through the sprite to generate constants.

package sprites

type SpriteSprite string

func (sprite SpriteSprite) SpritePath() string {
	return "/public/sprite.svg#" + string(sprite)
}

const (
	SPRITE_A_ARROW_DOWN                       SpriteSprite = "a-arrow-down"
	SPRITE_A_ARROW_UP                         SpriteSprite = "a-arrow-up"
	SPRITE_A_LARGE_SMALL                      SpriteSprite = "a-large-small"
	SPRITE_ACCESSIBILITY                      SpriteSprite = "accessibility"
	SPRITE_ACTIVITY                           SpriteSprite = "activity"
	SPRITE_AIR_VENT                           SpriteSprite = "air-vent"
	SPRITE_AIRPLAY                            SpriteSprite = "airplay"
	SPRITE_ALARM_CLOCK                        SpriteSprite = "alarm-clock"
)
<td>
			<span class="flex gap-2 items-center">
				@static.NewIcon(sprites.SPRITE_LANGUAGES, static.WithIconClass(templ.Classes("w-8 h-8")))
				<span class="flex flex-col">
					<span class="font-bold">{ item.GetKey() }</span>
					<span class="text-xs">{ item.GetScope() }</span>
				</span>
			</span>
		</td>

@diyor28
Copy link
Author

diyor28 commented Jan 17, 2025

Thank you for the suggestion! I'll look into that.
What about the initial issue however? I still believe templ cli still shouldn't behave like this

@harrisbisset
Copy link
Contributor

TLDR: It fails on the 101st @svg with the original input data, and fails on the 601st if/else branch (within @svg) when deleting if/else branches.

Testing

This is all testing a single file.

templateparser.go

log.Print(pi.Peek(1))
log.Print(pi.Peek(2))
log.Print(pi.Peek(3))
if _, ok, err = parse.All(openBraceWithOptionalPadding, parse.StringFrom(parse.Optional(parse.NewLine))).Parse(pi); err != nil || !ok {
	err = parse.Error("templ: malformed templ expression, expected `templ functionName() {`", pi.PositionAt(start))
	return
}

Outputs when running using the provided file.

2025/01/17 17:14:25  true
2025/01/17 17:14:25  {true
2025/01/17 17:14:25  {
true
... (same as above * 7)
2025/01/17 17:14:25 strue
2025/01/17 17:14:25 svtrue
2025/01/17 17:14:25 svgtrue

Despite it being potentially caused by the 101st @svg, it seems to fail on the 9th function, while giving the error for the first function.

More Testing on Input Data

If you remove the else if statements (not the if or else) from the 101st, then it will compile. After this, no more templ functions can have if/else statements I believe.

I did also test if a file with a lot (499) of if/else branches wrapped in @svg(props Props), but only 1 function, compiles and it does (takes 3 minutes).
The file also compiles if all the code is removed from the inside of the templ expresssions, ie

templ functionName(props Props) {
}

@a-h
Copy link
Owner

a-h commented Jan 18, 2025

The templ parser for some elements has some look ahead limits - ie it will only look for a closing brace that's x characters ahead, it's likely this file is hitting those because of the inclusion of large quantities of data in the file.

It could be a case of reviewing those limits, but I haven't had chance to look into this yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants