react-codemirror-runmode 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -203,4 +203,26 @@ describe('React Highlighter', () => {
203
203
  expect(element).toHaveProperty('className', 'ͼp')
204
204
  expect(screen.getByText('123')).toHaveProperty('className', 'ͼu')
205
205
  })
206
+
207
+ it('swaps in a freshly created wrapper once highlighting resolves', async () => {
208
+ const { container } = render(
209
+ <code>
210
+ <Highlighter lang="js" theme={oneDarkHighlightStyle}>
211
+ {'const x = 123'}
212
+ </Highlighter>
213
+ </code>
214
+ )
215
+ const pendingWrapper = container.querySelector('code > span')
216
+ expect(pendingWrapper).not.toBeNull()
217
+
218
+ await sleep(100)
219
+
220
+ // Reusing the pending element would make React insert every highlighted span
221
+ // into the already-attached DOM one at a time, which is ~25x slower on large
222
+ // input. The wrapper must be a genuinely new element.
223
+ const resolvedWrapper = container.querySelector('code > span')
224
+ expect(resolvedWrapper).not.toBe(pendingWrapper)
225
+ expect(resolvedWrapper?.getAttribute('style')).toContain('display: contents')
226
+ expect(container.textContent).toBe('const x = 123')
227
+ })
206
228
  })
@@ -0,0 +1,64 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+
4
+ import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark'
5
+ import { createRoot } from 'react-dom/client'
6
+ import { bench, describe } from 'vitest'
7
+
8
+ import { highlightCode, Highlighter } from '../src'
9
+
10
+ const page = readFileSync(resolve(process.cwd(), 'test/fixtures/large.html'), 'utf8')
11
+
12
+ /**
13
+ * Rendering ~90k spans into jsdom costs seconds per iteration, so the defaults
14
+ * (5 warmup + 10 timed runs) would take minutes. Keep the run bounded.
15
+ */
16
+ const RENDER_BENCH_OPTIONS = { time: 0, iterations: 5, warmupTime: 0, warmupIterations: 1 }
17
+
18
+ /**
19
+ * Resolves once the highlighted spans are in the document. The pending state is
20
+ * itself a single wrapper `<span>`, so waiting for *any* span would return
21
+ * immediately and measure nothing — hence the count.
22
+ */
23
+ const waitForHighlight = (host: HTMLElement) =>
24
+ new Promise<void>(resolve => {
25
+ const poll = () => (host.querySelectorAll('span').length > 1 ? resolve() : setTimeout(poll, 5))
26
+ poll()
27
+ })
28
+
29
+ describe(`html highlighting (${(page.length / 1024).toFixed(0)}KB)`, () => {
30
+ bench('highlightCode: parse + tokenize', async () => {
31
+ await highlightCode(
32
+ 'html',
33
+ page,
34
+ oneDarkHighlightStyle,
35
+ undefined,
36
+ undefined,
37
+ (text, style, from, to) => ({ text, style, from, to })
38
+ )
39
+ })
40
+
41
+ // Rendered through createRoot rather than @testing-library/react so the
42
+ // measurement excludes act() overhead and each iteration starts on an empty DOM.
43
+ bench(
44
+ 'Highlighter: parse + tokenize + React render into jsdom',
45
+ async () => {
46
+ const host = document.createElement('div')
47
+ document.body.appendChild(host)
48
+ const root = createRoot(host)
49
+
50
+ root.render(
51
+ <code>
52
+ <Highlighter lang="html" theme={oneDarkHighlightStyle}>
53
+ {page}
54
+ </Highlighter>
55
+ </code>
56
+ )
57
+ await waitForHighlight(host)
58
+
59
+ root.unmount()
60
+ host.remove()
61
+ },
62
+ RENDER_BENCH_OPTIONS
63
+ )
64
+ })
package/tsconfig.json CHANGED
@@ -15,6 +15,8 @@
15
15
  "outDir": "dist",
16
16
  "declaration": true,
17
17
  "declarationDir": "dist",
18
+ "noUncheckedIndexedAccess": true,
19
+ "exactOptionalPropertyTypes": true,
18
20
  "skipLibCheck": true
19
21
  }
20
22
  }