evanw/esbuild · error · LexerPanic

Unexpected "-"

Error message

Unexpected "-"

What it means

In parseJSXTag's member-expression chain ('<a.b.c>'), after a '.' the following identifier segment must not contain a '-'. Dashes are illegal in JavaScript identifiers, so a hyphenated segment after a dot is rejected.

Source

Thrown at internal/js_parser/js_parser.go:5240

	// Otherwise, this is an identifier
	tag := js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: p.storeNameInRef(tagName)}}

	// Parse a member expression chain
	var chain strings.Builder
	chain.WriteString(tagName.String)
	for p.lexer.Token == js_lexer.TDot {
		p.lexer.NextInsideJSXElement()
		memberRange := p.lexer.Range()
		member := p.lexer.Identifier
		p.lexer.ExpectInsideJSXElement(js_lexer.TIdentifier)

		// Dashes are not allowed in member expression chains
		index := strings.IndexByte(member.String, '-')
		if index >= 0 {
			p.log.AddError(&p.tracker, logger.Range{Loc: logger.Loc{Start: memberRange.Loc.Start + int32(index)}},
				"Unexpected \"-\"")
			panic(js_lexer.LexerPanic{})
		}

		chain.WriteString("." + member.String)
		tag = js_ast.Expr{Loc: loc, Data: p.dotOrMangledPropParse(tag, member, memberRange.Loc, js_ast.OptionalChainNone, wasOriginallyDot)}
		tagRange.Len = memberRange.Loc.Start + memberRange.Len - tagRange.Loc.Start
	}

	return tagRange, chain.String(), tag
}

func (p *parser) parseJSXElement(loc logger.Loc) js_ast.Expr {
	// Keep track of the location of the first JSX element for error messages
	if p.firstJSXElementLoc.Start == -1 {
		p.firstJSXElementLoc = loc
	}

	// Parse the tag
	startRange, startText, startTagOrNil := p.parseJSXTag()

View on GitHub (pinned to f6058f8364)

Solutions

  1. Rename the member so it has no hyphen, e.g. '<ui.Button />'.
  2. For kebab-case custom elements, use a single tag '<my-el>' (no dots).
  3. Reference the component through a properly-named variable instead of a dotted path.

Example fix

// before
<ui.button-1 />
// after
<ui.Button />
Defensive patterns

Strategy: validation

Validate before calling

import "github.com/evanw/esbuild/pkg/api"
res := api.Transform(jsx, api.TransformOptions{Loader: api.LoaderJSX})
for _, m := range res.Errors {
  if strings.Contains(m.Text, "Unexpected \"-\"") {
    // a dotted JSX tag member contained a hyphen
  }
}

Type guard

// Flag dotted JSX tags whose member segment has a hyphen: <a.b-c>.
var reDashedMember = regexp.MustCompile(`<[-A-Za-z0-9]+(?:\.[-A-Za-z0-9]*)*\.[A-Za-z0-9]*-[A-Za-z0-9-]*`)
func jsxTagHasDashedMember(src string) bool { return reDashedMember.MatchString(src) }

Prevention

When it happens

Trigger: '<Foo.Bar-Baz />' or '<ui.button-1 />' — a dotted JSX tag whose member segment contains a hyphen.

Common situations: Trying to reach a kebab-case property via dot notation; confusing custom-element names ('<my-el>') with member access.

Related errors


AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09). Data as JSON: /api/errors/f9b30c8892cb673a. Report an issue: GitHub.