ReviewOS

also looking at this

stacks/ts-syntax-highlighter

fix: browser-safe config loading and correct dual-theme token colors

#477
Open jmadore-payfacto wants to merge fix/browser-safe-config-and-dual-theme-colors into main
6 files +237 -21

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

packages/ts-syntax-highlighter/src/config.tsmodified+7-1
Changes to packages/ts-syntax-highlighter/src/config.ts
@@ -1,5 +1,4 @@
11import type { SyntaxHighlighterConfig } from './types'
2import { loadConfig } from 'bunfig'
32
43export const defaultConfig: SyntaxHighlighterConfig = {
54 verbose: false,
@@ -14,6 +13,13 @@ let _config: SyntaxHighlighterConfig | null = null
1413
1514export async function loadSyntaxHighlighterConfig(): Promise<SyntaxHighlighterConfig> {
1615 if (!_config) {
16 // `bunfig` reads config files from disk and is Node/Bun-only, so it is
17 // imported dynamically here rather than at module scope. A static
18 // top-level import would drag its own eager, unguarded `process.env`
19 // access into every consumer's bundle purely by importing this
20 // package's index, crashing immediately in a browser even for callers
21 // who never touch config loading.
22 const { loadConfig } = await import('bunfig')
1723 _config = await loadConfig({
1824 name: 'syntax',
1925 defaultConfig,
packages/ts-syntax-highlighter/test/config.test.tsadded+32-0
Changes to packages/ts-syntax-highlighter/test/config.test.ts
@@ -0,0 +1,32 @@
1import { readFileSync } from 'node:fs'
2import { describe, expect, it } from 'bun:test'
3import { config, defaultConfig, getConfig, isVerbose, loadSyntaxHighlighterConfig } from '../src/config'
4
5describe('config', () => {
6 it('exposes a usable default config without requiring loadSyntaxHighlighterConfig to run', () => {
7 expect(config).toEqual(defaultConfig)
8 expect(getConfig('theme')).toBe(defaultConfig.theme)
9 expect(isVerbose()).toBe(false)
10 })
11
12 it('still loads config from disk when a caller opts in', async () => {
13 const loaded = await loadSyntaxHighlighterConfig()
14 expect(loaded).toBeTruthy()
15 expect(loaded.theme).toBeTruthy()
16 })
17
18 it('does not statically import the Node/Bun-only config loader at module scope', () => {
19 // `bunfig` reads config files from disk and, as of the version this
20 // package depends on, runs its own eager, unguarded `process.env`
21 // access the moment it is imported - safe under Bun/Node, but a hard
22 // crash for any browser consumer that pulls it in just by importing
23 // this package's index, whether or not they ever call
24 // `loadSyntaxHighlighterConfig()`. A static top-level import here
25 // regresses that; it must stay dynamic, inside the function that
26 // actually needs it.
27 const source = readFileSync(new URL('../src/config.ts', import.meta.url), 'utf-8')
28
29 expect(source).not.toMatch(/^import\s+.*from\s+['"]bunfig['"]/m)
30 expect(source).toContain('await import(\'bunfig\')')
31 })
32})