Skip to content

Bug: pinned shiki ^3.0.0 hangs render on Go struct{} fields #802

@evillguy

Description

@evillguy

Prerequisites

Describe the issue

Some Go files freeze the browser tab when they render in a diff. I tracked it down to syntax highlighting: shiki runs into catastrophic backtracking on Go anonymous-struct fields (struct{}) when the struct tag has a long unbroken run of non-whitespace characters.

The cause is the pinned shiki version. @pierre/diffs requires shiki ^3.0.0, which installs 3.23.0. Its default shiki-js engine converts the Go grammar's struct_variables_types_fields rule (which uses \S+) into a JS RegExp that backtracks exponentially. shiki tracked the same problem in shikijs/shiki#1026.

The cost climbs fast with the length of that non-whitespace run: roughly 190ms at 20 chars, 4.6s at 25, and it never returns past about 30. A field that isn't struct{} doesn't hit it.

Reproduction

Stackblitz

import { CodeView } from "@pierre/diffs/react";
 import type { CodeViewFileItem } from "@pierre/diffs";

 // Go anonymous-struct field with one long no-space struct tag.
 const contents = "type T struct {\n\tX struct{} `" + "x".repeat(40) + "`\n}\n";

 const exampleItem: CodeViewFileItem = {
   id: "foo",
   type: "file",
   file: { name: "x.go", contents },
 };

 function App() {
   return (
     <CodeView
       items={[exampleItem]}
       style={{ height: 600, overflow: "auto" }}
       // Uncomment to confirm the cause (Oniguruma engine renders instantly):
       // options={{ preferredHighlighter: "shiki-wasm" }}
     />
   );
 }

 export default App;

Expected: highlights in a few ms.
Actual: the tab hangs on load (main-thread catastrophic backtracking). Shrinking the "x".repeat(40) shows the exponential curve: ~20 ≈ 190 ms, ~25 ≈ 4.6 s, ~30+ never returns. Switching to shiki-wasm allows the page to render.

What browser(s) are you seeing the problem on?

Chrome

What version of @pierre/diffs are you using?

1.2.5 (also 1.2.9 / 1.3.0-beta.4)


AI disclosure: issue written with assistance from Claude Opus 4.8 based on usage in my private codebase.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions