also looking at this
fix: browser-safe config loading and correct dual-theme token colors
#477
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.
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | import type { SyntaxHighlighterConfig } from './types' |
| 2 | import { loadConfig } from 'bunfig' | |
| 3 | 2 | |
| 4 | 3 | export const defaultConfig: SyntaxHighlighterConfig = { |
| 5 | 4 | verbose: false, |
| @@ -14,6 +13,13 @@ let _config: SyntaxHighlighterConfig | null = null | ||
| 14 | 13 | |
| 15 | 14 | export async function loadSyntaxHighlighterConfig(): Promise<SyntaxHighlighterConfig> { |
| 16 | 15 | 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') | |
| 17 | 23 | _config = await loadConfig({ |
| 18 | 24 | name: 'syntax', |
| 19 | 25 | defaultConfig, |
| @@ -0,0 +1,32 @@ | ||
| 1 | import { readFileSync } from 'node:fs' | |
| 2 | import { describe, expect, it } from 'bun:test' | |
| 3 | import { config, defaultConfig, getConfig, isVerbose, loadSyntaxHighlighterConfig } from '../src/config' | |
| 4 | ||
| 5 | describe('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 | }) | |