diff --git a/.changeset/go-and-gruvbox.md b/.changeset/go-and-gruvbox.md
new file mode 100644
index 0000000..8ed13db
--- /dev/null
+++ b/.changeset/go-and-gruvbox.md
@@ -0,0 +1,5 @@
+---
+"@tanstack/highlight": minor
+---
+
+Add an isolated Go language definition with the `golang` alias and isolated Gruvbox Dark and Light themes. Existing selective language bundles and the core are unchanged.
diff --git a/README.md b/README.md
index 7a5f833..b5b10f9 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,21 @@
+
+
+
+
+
+
+
+
# TanStack Highlight
Tiny, synchronous syntax highlighting for blogs and documentation.
@@ -172,11 +190,11 @@ const css = createThemeCss({
For TanStack Markdown, set `codeBlockSelector` to its `pre.tm-code` wrapper and `lineNumbersSelector` to `.tm-code--line-numbers`. See the [Markdown pipeline guide](docs/guides/markdown-pipelines.md#tanstack-markdown).
-Available themes: Aurora X, Dracula, GitHub Dark, GitHub Light, Monokai, Nord, One Dark Pro, Solarized Dark, and Solarized Light.
+Available themes: Aurora X, Dracula, GitHub Dark, GitHub Light, Gruvbox Dark, Gruvbox Light, Monokai, Nord, One Dark Pro, Solarized Dark, and Solarized Light.
## Languages
-`apache`, `css`, `diff`, `dockerfile`, `ejs`, `env`, `html`, `http`, `js`, `json`, `jsx`, `markdown`, `mermaid`, `nginx`, `plaintext`, `python`, `scheme`, `shell`, `sql`, `svelte`, `toml`, `ts`, `tsrx`, `tsx`, `vue`, and `yaml`.
+`apache`, `css`, `diff`, `dockerfile`, `ejs`, `env`, `go`, `html`, `http`, `js`, `json`, `jsx`, `markdown`, `mermaid`, `nginx`, `plaintext`, `python`, `scheme`, `shell`, `sql`, `svelte`, `toml`, `ts`, `tsrx`, `tsx`, `vue`, and `yaml`.
Each language is available from `@tanstack/highlight/languages/`. The aggregate `@tanstack/highlight/languages` entry can tree-shake, while direct subpaths make isolation explicit. Importing only core helpers from the root entry also removes unused language registrations in a compatible bundler.
@@ -199,7 +217,7 @@ Local browser bundles, minified with esbuild and compressed independently. KB us
| Core + TSX | 9.46 KB | 4.03 KB | 3.66 KB |
| Octane MDX + TypeScript | 13.21 KB | 5.37 KB | 4.90 KB |
| Nine-language docs set | 15.39 KB | 5.97 KB | 5.44 KB |
-| All 26 languages | 22.59 KB | 8.29 KB | 7.49 KB |
+| All 27 languages | 23.93 KB | 8.66 KB | 7.78 KB |
On 80 real JavaScript/TypeScript/JSX/TSX TanStack docs fixtures repeated across 5,040 blocks, using the median of three runs after warmup:
diff --git a/docs/guides/performance.md b/docs/guides/performance.md
index d4b54c9..29b9710 100644
--- a/docs/guides/performance.md
+++ b/docs/guides/performance.md
@@ -16,7 +16,7 @@ CI measures selective browser bundles and highlighting performance on real docum
| TSX | TSX | 4.03 KB | 4.1 KB |
| Octane | TypeScript plus Octane MDX adapter | 5.37 KB | 5.5 KB |
| Docs | CSS, HTML, JS, JSON, JSX, Markdown, Shell, TS, TSX | 5.97 KB | 6.1 KB |
-| All | All 26 definitions | 8.29 KB | 8.3 KB |
+| All | All 27 definitions | 8.66 KB | 8.9 KB |
KB uses 1,000 bytes. Core helpers imported from the root tree-shake to the same engine size. The standalone theme helper is 695 gzip bytes.
diff --git a/docs/guides/themes.md b/docs/guides/themes.md
index 921ea92..6fb0334 100644
--- a/docs/guides/themes.md
+++ b/docs/guides/themes.md
@@ -154,6 +154,8 @@ The defaults remain `pre.th-code` and `.th-code--line-numbers`. Token classes st
| Dracula | `draculaTheme` | Dark | `themes/dracula` |
| GitHub Dark | `githubDarkTheme` | Dark | `themes/github-dark` |
| GitHub Light | `githubLightTheme` | Light | `themes/github-light` |
+| Gruvbox Dark | `gruvboxDarkTheme` | Dark | `themes/gruvbox-dark` |
+| Gruvbox Light | `gruvboxLightTheme` | Light | `themes/gruvbox-light` |
| Monokai | `monokaiTheme` | Dark | `themes/monokai` |
| Nord | `nordTheme` | Dark | `themes/nord` |
| One Dark Pro | `oneDarkProTheme` | Dark | `themes/one-dark-pro` |
diff --git a/docs/language-support.md b/docs/language-support.md
index f7de9bc..35b2b29 100644
--- a/docs/language-support.md
+++ b/docs/language-support.md
@@ -16,6 +16,7 @@ Every language is an isolated definition imported from `@tanstack/highlight/lang
| Dockerfile | `dockerfile` | `docker` | Common directives, variables, commands |
| EJS | `ejs` | - | HTML plus optional JavaScript delegation |
| Env | `env` | `dotenv` | Properties, values, comments |
+| Go | `go` | `golang` | Raw strings, runes, comments, declarations |
| HTML | `html` | `htm`, `xml`, `angular-html` | Optional JavaScript/TypeScript and CSS delegation |
| HTTP | `http` | - | Methods, headers, protocol, paths |
| JavaScript | `js` | `javascript`, `mjs`, `cjs`, `js-vue` | Templates, interpolation, regex literals |
diff --git a/docs/reference/default-entry.md b/docs/reference/default-entry.md
index 101ea22..6f6f756 100644
--- a/docs/reference/default-entry.md
+++ b/docs/reference/default-entry.md
@@ -43,7 +43,7 @@ Returns the canonical registered language name for a name or alias. Names are tr
function listLanguages(): Array
```
-Returns the 26 canonical language names registered in `defaultHighlighter`.
+Returns the 27 canonical language names registered in `defaultHighlighter`.
### `tokenize`
diff --git a/docs/reference/languages.md b/docs/reference/languages.md
index 2de8bee..3089e6b 100644
--- a/docs/reference/languages.md
+++ b/docs/reference/languages.md
@@ -27,6 +27,7 @@ const highlighter = createHighlighter({
| `dockerfile` | `@tanstack/highlight/languages/dockerfile` | `docker` |
| `ejs` | `@tanstack/highlight/languages/ejs` | None |
| `env` | `@tanstack/highlight/languages/env` | `dotenv` |
+| `go` | `@tanstack/highlight/languages/go` | `golang` |
| `html` | `@tanstack/highlight/languages/html` | `htm`, `xml`, `angular-html` |
| `http` | `@tanstack/highlight/languages/http` | None |
| `js` | `@tanstack/highlight/languages/js` | `javascript`, `mjs`, `cjs`, `js-vue` |
@@ -48,6 +49,6 @@ const highlighter = createHighlighter({
| `vue` | `@tanstack/highlight/languages/vue` | None |
| `yaml` | `@tanstack/highlight/languages/yaml` | `yml` |
-`@tanstack/highlight/languages` re-exports `apache`, `css`, `diff`, `dockerfile`, `ejs`, `env`, `html`, `http`, `js`, `json`, `jsx`, `markdown`, `mermaid`, `nginx`, `plaintext`, `python`, `scheme`, `shell`, `sql`, `svelte`, `toml`, `ts`, `tsrx`, `tsx`, `vue`, and `yaml`. The barrel is convenient but individual subpaths make bundle intent explicit.
+`@tanstack/highlight/languages` re-exports `apache`, `css`, `diff`, `dockerfile`, `ejs`, `env`, `go`, `html`, `http`, `js`, `json`, `jsx`, `markdown`, `mermaid`, `nginx`, `plaintext`, `python`, `scheme`, `shell`, `sql`, `svelte`, `toml`, `ts`, `tsrx`, `tsx`, `vue`, and `yaml`. The barrel is convenient but individual subpaths make bundle intent explicit.
See the [language support matrix](../language-support) for the context-aware behavior and current scope of each registration.
diff --git a/docs/reference/themes.md b/docs/reference/themes.md
index 522638e..f2fac98 100644
--- a/docs/reference/themes.md
+++ b/docs/reference/themes.md
@@ -12,6 +12,8 @@ Bundled themes are isolated objects. Importing one theme does not include anothe
| `draculaTheme` | `@tanstack/highlight/themes/dracula` | Dark |
| `githubDarkTheme` | `@tanstack/highlight/themes/github-dark` | Dark |
| `githubLightTheme` | `@tanstack/highlight/themes/github-light` | Light |
+| `gruvboxDarkTheme` | `@tanstack/highlight/themes/gruvbox-dark` | Dark |
+| `gruvboxLightTheme` | `@tanstack/highlight/themes/gruvbox-light` | Light |
| `monokaiTheme` | `@tanstack/highlight/themes/monokai` | Dark |
| `nordTheme` | `@tanstack/highlight/themes/nord` | Dark |
| `oneDarkProTheme` | `@tanstack/highlight/themes/one-dark-pro` | Dark |
diff --git a/docs/test-strategy.md b/docs/test-strategy.md
index 36bd38b..9ebfb69 100644
--- a/docs/test-strategy.md
+++ b/docs/test-strategy.md
@@ -27,7 +27,7 @@ The suite protects the package's actual product boundary: valid code commonly pu
| TSX | TSX | 4.1 KB |
| Octane | TypeScript plus Octane MDX adapter | 5.5 KB |
| Docs | CSS, HTML, JS, JSON, JSX, Markdown, Shell, TS, TSX | 6.1 KB |
-| All | All 26 definitions | 8.3 KB |
+| All | All 27 definitions | 8.9 KB |
The selective profiles are the primary product metric. The all-language profile protects the convenience entry from unbounded growth. Bundle graphs reject unexpected language or theme code. Package tests repeat isolation checks through public exports after building.
diff --git a/scripts/language-utils.mjs b/scripts/language-utils.mjs
index c31105c..451eb1e 100644
--- a/scripts/language-utils.mjs
+++ b/scripts/language-utils.mjs
@@ -8,6 +8,7 @@ export const supportedLanguages = [
'dockerfile',
'ejs',
'env',
+ 'go',
'html',
'http',
'js',
@@ -39,6 +40,7 @@ const aliases = {
cmd: 'shell',
console: 'shell',
dotenv: 'env',
+ golang: 'go',
htm: 'html',
javascript: 'js',
'js-vue': 'js',
diff --git a/scripts/measure-size.mjs b/scripts/measure-size.mjs
index b848ba1..57738c8 100644
--- a/scripts/measure-size.mjs
+++ b/scripts/measure-size.mjs
@@ -77,7 +77,7 @@ const profiles = {
globalThis.highlighter = defaultHighlighter
`,
languages: 'all',
- limits: { minified: 23_000, gzip: 8_300, brotli: 7_500 },
+ limits: { minified: 24_500, gzip: 8_900, brotli: 8_000 },
},
reactAdapter: {
source: `export * from './src/react.ts'`,
diff --git a/skills/configure-selective-highlighting/references/languages.md b/skills/configure-selective-highlighting/references/languages.md
index 657ba15..769972c 100644
--- a/skills/configure-selective-highlighting/references/languages.md
+++ b/skills/configure-selective-highlighting/references/languages.md
@@ -10,6 +10,7 @@ Import only the definitions the application registers.
| Dockerfile | `dockerfile` | `@tanstack/highlight/languages/dockerfile` | `docker` |
| EJS | `ejs` | `@tanstack/highlight/languages/ejs` | - |
| Env | `env` | `@tanstack/highlight/languages/env` | `dotenv` |
+| Go | `go` | `@tanstack/highlight/languages/go` | `golang` |
| HTML | `html` | `@tanstack/highlight/languages/html` | `htm`, `xml`, `angular-html` |
| HTTP | `http` | `@tanstack/highlight/languages/http` | - |
| JavaScript | `js` | `@tanstack/highlight/languages/js` | `javascript`, `mjs`, `cjs`, `js-vue` |
diff --git a/skills/theme-and-annotate-code/references/themes-and-tokens.md b/skills/theme-and-annotate-code/references/themes-and-tokens.md
index 43d9c75..ba9a955 100644
--- a/skills/theme-and-annotate-code/references/themes-and-tokens.md
+++ b/skills/theme-and-annotate-code/references/themes-and-tokens.md
@@ -8,6 +8,8 @@
| Dracula | `draculaTheme` | dark | `@tanstack/highlight/themes/dracula` |
| GitHub Dark | `githubDarkTheme` | dark | `@tanstack/highlight/themes/github-dark` |
| GitHub Light | `githubLightTheme` | light | `@tanstack/highlight/themes/github-light` |
+| Gruvbox Dark | `gruvboxDarkTheme` | dark | `@tanstack/highlight/themes/gruvbox-dark` |
+| Gruvbox Light | `gruvboxLightTheme` | light | `@tanstack/highlight/themes/gruvbox-light` |
| Monokai | `monokaiTheme` | dark | `@tanstack/highlight/themes/monokai` |
| Nord | `nordTheme` | dark | `@tanstack/highlight/themes/nord` |
| One Dark Pro | `oneDarkProTheme` | dark | `@tanstack/highlight/themes/one-dark-pro` |
diff --git a/src/index.ts b/src/index.ts
index 0fe2930..9de48a2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -11,6 +11,7 @@ import { diff } from './languages/diff.js'
import { dockerfile } from './languages/dockerfile.js'
import { ejs } from './languages/ejs.js'
import { env } from './languages/env.js'
+import { go } from './languages/go.js'
import { html } from './languages/html.js'
import { http } from './languages/http.js'
import { js } from './languages/js.js'
@@ -39,6 +40,7 @@ export type HighlightLanguage =
| 'dockerfile'
| 'ejs'
| 'env'
+ | 'go'
| 'html'
| 'http'
| 'js'
@@ -107,6 +109,7 @@ export const allLanguages = [
dockerfile,
ejs,
env,
+ go,
html,
http,
js,
diff --git a/src/languages/go.ts b/src/languages/go.ts
new file mode 100644
index 0000000..c899748
--- /dev/null
+++ b/src/languages/go.ts
@@ -0,0 +1,40 @@
+import { defineLanguage } from '../core.js'
+import { patternTokenizer } from '../internal/patterns.js'
+
+export const go = defineLanguage({
+ name: 'go',
+ aliases: ['golang'],
+ tokenize: patternTokenizer([
+ {
+ className: (match) =>
+ match[0].startsWith('/') ? 'comment' : 'string',
+ regex:
+ /\/\/[^\n]*|\/\*[\s\S]*?\*\/|`[\s\S]*?`|"(?:\\.|[^"\\\n])*"|'(?:\\.|[^'\\\n])*'/g,
+ },
+ {
+ className: 'keyword',
+ regex:
+ /\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go|goto|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/g,
+ },
+ { className: 'literal', regex: /\b(?:false|iota|nil|true)\b/g },
+ { className: 'type', regex: /\btype\s+([A-Za-z_]\w*)/g, group: 1 },
+ {
+ className: 'type',
+ regex:
+ /\b(?:any|bool|byte|comparable|complex64|complex128|error|float32|float64|int|int8|int16|int32|int64|rune|string|uint|uint8|uint16|uint32|uint64|uintptr)\b/g,
+ },
+ { className: 'function', regex: /\b[A-Za-z_]\w*(?=\s*\()/g },
+ {
+ className: 'number',
+ regex:
+ /(?:^|[^\w.])((?:0[bB]_?[01](?:_?[01])*|0[oO]_?[0-7](?:_?[0-7])*|0[xX](?:(?:_?[\da-fA-F](?:_?[\da-fA-F])*(?:\.(?:[\da-fA-F](?:_?[\da-fA-F])*)?)?|\.[\da-fA-F](?:_?[\da-fA-F])*)[pP][+-]?\d(?:_?\d)*|_?[\da-fA-F](?:_?[\da-fA-F])*)|\d(?:_?\d)*\.(?:\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|\d(?:_?\d)*[eE][+-]?\d(?:_?\d)*|\.\d(?:_?\d)*(?:[eE][+-]?\d(?:_?\d)*)?|\d(?:_?\d)*)(?:i)?)(?![\w.])/g,
+ group: 1,
+ },
+ { className: 'property', regex: /\.\s*([A-Za-z_]\w*)/g, group: 1 },
+ {
+ className: 'operator',
+ regex:
+ /\.\.\.|<<=?|>>=?|&\^=?|:=|<-|\+\+|--|==|!=|<=|>=|&&|\|\||[+\-*/%&|^!<>=~]=?/g,
+ },
+ ]),
+})
diff --git a/src/languages/index.ts b/src/languages/index.ts
index ef279c9..50c2791 100644
--- a/src/languages/index.ts
+++ b/src/languages/index.ts
@@ -4,6 +4,7 @@ export { diff } from './diff.js'
export { dockerfile } from './dockerfile.js'
export { ejs } from './ejs.js'
export { env } from './env.js'
+export { go } from './go.js'
export { html } from './html.js'
export { http } from './http.js'
export { js } from './js.js'
diff --git a/src/themes/gruvbox-dark.ts b/src/themes/gruvbox-dark.ts
new file mode 100644
index 0000000..d1518a4
--- /dev/null
+++ b/src/themes/gruvbox-dark.ts
@@ -0,0 +1,33 @@
+import type { HighlightTheme } from '../theme.js'
+
+export const gruvboxDarkTheme = {
+ background: '#282828',
+ foreground: '#ebdbb2',
+ name: 'gruvbox-dark',
+ type: 'dark',
+ tokens: {
+ attr: '#8ec07c',
+ 'code-inline': '#8ec07c',
+ command: '#b8bb26',
+ comment: '#928374',
+ deleted: '#fb4934',
+ function: '#b8bb26',
+ heading: '#b8bb26',
+ inserted: '#b8bb26',
+ keyword: '#fb4934',
+ link: '#d3869b',
+ literal: '#d3869b',
+ meta: '#928374',
+ number: '#d3869b',
+ operator: '#ebdbb2',
+ property: '#8ec07c',
+ selector: '#fe8019',
+ string: '#b8bb26',
+ tag: '#8ec07c',
+ token: '#ebdbb2',
+ type: '#fabd2f',
+ variable: '#83a598',
+ },
+} as const satisfies HighlightTheme
+
+export default gruvboxDarkTheme
diff --git a/src/themes/gruvbox-light.ts b/src/themes/gruvbox-light.ts
new file mode 100644
index 0000000..b1e404c
--- /dev/null
+++ b/src/themes/gruvbox-light.ts
@@ -0,0 +1,33 @@
+import type { HighlightTheme } from '../theme.js'
+
+export const gruvboxLightTheme = {
+ background: '#fbf1c7',
+ foreground: '#3c3836',
+ name: 'gruvbox-light',
+ type: 'light',
+ tokens: {
+ attr: '#427b58',
+ 'code-inline': '#427b58',
+ command: '#79740e',
+ comment: '#928374',
+ deleted: '#9d0006',
+ function: '#79740e',
+ heading: '#79740e',
+ inserted: '#79740e',
+ keyword: '#9d0006',
+ link: '#8f3f71',
+ literal: '#8f3f71',
+ meta: '#928374',
+ number: '#8f3f71',
+ operator: '#3c3836',
+ property: '#427b58',
+ selector: '#af3a03',
+ string: '#79740e',
+ tag: '#427b58',
+ token: '#3c3836',
+ type: '#b57614',
+ variable: '#076678',
+ },
+} as const satisfies HighlightTheme
+
+export default gruvboxLightTheme
diff --git a/test/fixtures.ts b/test/fixtures.ts
index f2ebe9b..a3ead0d 100644
--- a/test/fixtures.ts
+++ b/test/fixtures.ts
@@ -29,6 +29,35 @@ export function Counter() @{
'th-number',
],
},
+ {
+ lang: 'golang',
+ normalized: 'go',
+ code: `package main
+
+import "fmt"
+
+type Greeter struct {
+ Name string
+}
+
+func (g Greeter) Greet() {
+ count := 2
+ url := \`https://go.dev\`
+ initial := 'G'
+ fmt.Println(url, initial, g.Name, count, true) // greet
+}`,
+ expectedClasses: [
+ 'th-keyword',
+ 'th-string',
+ 'th-type',
+ 'th-function',
+ 'th-property',
+ 'th-number',
+ 'th-literal',
+ 'th-comment',
+ 'th-operator',
+ ],
+ },
{
lang: 'tsx',
normalized: 'tsx',
diff --git a/test/highlight.contract.test.ts b/test/highlight.contract.test.ts
index 49137b9..a8bd809 100644
--- a/test/highlight.contract.test.ts
+++ b/test/highlight.contract.test.ts
@@ -28,11 +28,17 @@ describe('language inventory', () => {
it('lists the normalized support targets once', () => {
const languages = listLanguages()
+ const fixtureLanguages = new Set(
+ languageFixtures.map((fixture) => fixture.normalized),
+ )
expect(new Set(languages).size).toBe(languages.length)
for (const fixture of languageFixtures) {
expect(languages).toContain(fixture.normalized)
}
+ for (const language of languages) {
+ expect(fixtureLanguages, `missing representative fixture for ${language}`).toContain(language)
+ }
})
})
@@ -82,6 +88,7 @@ describe('output contract', () => {
it('keeps comment markers inside strings and quotes inside comments', () => {
const cases = [
['ts', `const url = "https://example.com" // "comment"`],
+ ['go', `url := "https://go.dev" // "comment"`],
['python', `value = "# not a comment" # "comment"`],
['shell', `echo "# not a comment" # "comment"`],
['yaml', `url: "https://example.com/#hash" # "comment"`],
diff --git a/test/real-doc-fixtures.test.ts b/test/real-doc-fixtures.test.ts
index f8480de..74242e4 100644
--- a/test/real-doc-fixtures.test.ts
+++ b/test/real-doc-fixtures.test.ts
@@ -11,12 +11,14 @@ type DocFixture = {
}
const fixtures = fixtureData.fixtures as Array
+const languagesWithoutRealDocFixtures = new Set(['go'])
describe('real TanStack docs fixtures', () => {
- it('covers every normalized language target with sampled docs code', () => {
+ it('covers normalized language targets available in TanStack docs', () => {
const fixtureLanguages = new Set(fixtures.map((fixture) => fixture.lang))
for (const language of listLanguages()) {
+ if (languagesWithoutRealDocFixtures.has(language)) continue
expect(fixtureLanguages, `missing real docs fixture for ${language}`).toContain(language)
}
})
@@ -53,8 +55,9 @@ describe('real TanStack docs fixtures', () => {
}
}
+ const fixtureLanguages = new Set(fixtures.map((fixture) => fixture.lang))
for (const language of listLanguages()) {
- if (language === 'plaintext') continue
+ if (language === 'plaintext' || !fixtureLanguages.has(language)) continue
expect(highlightedLanguages, `${language} should highlight at least one real docs fixture`).toContain(language)
}
})
diff --git a/test/regressions.test.ts b/test/regressions.test.ts
index 6671856..a8549a6 100644
--- a/test/regressions.test.ts
+++ b/test/regressions.test.ts
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
import { createHighlighter } from '../src/core'
import { css } from '../src/languages/css'
import { dockerfile } from '../src/languages/dockerfile'
+import { go } from '../src/languages/go'
import { html } from '../src/languages/html'
import { http } from '../src/languages/http'
import { js } from '../src/languages/js'
@@ -19,6 +20,7 @@ const highlighter = createHighlighter({
languages: [
css,
dockerfile,
+ go,
html,
http,
js,
@@ -305,6 +307,46 @@ export function View() @{