Repository navigation
Expand file tree
/
Copy pathDiffWorkerPoolProvider.tsx
More file actions
157 lines (144 loc) · 5.12 KB
/
Copy pathDiffWorkerPoolProvider.tsx
File metadata and controls
157 lines (144 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import { WorkerPoolContext, useWorkerPool } from "@pierre/diffs/react";
import { WorkerPoolManager } from "@pierre/diffs/worker";
import DiffsWorker from "@pierre/diffs/worker/worker.js?worker";
import * as Schema from "effect/Schema";
import { useEffect, useState, useSyncExternalStore, type ReactNode } from "react";
import { getRenderedAppearance, subscribeRenderedAppearance } from "../hooks/useTheme";
import { resolveDiffThemeName } from "../lib/diffRendering";
import { PREFERRED_HIGHLIGHTER } from "../lib/syntaxHighlighting";
export class DiffWorkerError extends Schema.TaggedError<DiffWorkerError>()("DiffWorkerError", {
operation: Schema.Literals(["create-worker", "get-render-options", "set-render-options"]),
themeName: Schema.Literals(["pierre-dark", "t3-light"]),
cause: Schema.Defect(),
}) {
override get message(): string {
return `Diff worker operation ${this.operation} failed for theme ${this.themeName}.`;
}
}
const DIFF_WORKER_IDLE_TTL_MS = 30_000;
function resolveDiffWorkerPoolSize() {
const cores =
typeof navigator === "undefined" ? 4 : Math.max(1, navigator.hardwareConcurrency || 4);
return Math.max(2, Math.min(6, Math.floor(cores / 2)));
}
let sharedWorkerPool:
| {
readonly pool: WorkerPoolManager;
readonly unsubscribeTheme: () => void;
consumers: number;
idleTimer: ReturnType<typeof setTimeout> | undefined;
}
| undefined;
function applyDiffWorkerTheme(pool: WorkerPoolManager) {
const themeName = resolveDiffThemeName(getRenderedAppearance());
let operation: DiffWorkerError["operation"] = "get-render-options";
void (async () => {
try {
const current = pool.getDiffRenderOptions();
if (current.theme === themeName) return;
operation = "set-render-options";
await pool.setRenderOptions({ ...current, theme: themeName });
} catch (cause) {
console.error(new DiffWorkerError({ operation, themeName, cause }));
}
})();
}
// The pool follows the rendered appearance for as long as it lives, so it never depends on a
// mounted provider to carry a theme change across.
function createDiffWorkerPool(poolSize: number) {
const themeName = resolveDiffThemeName(getRenderedAppearance());
const pool = new WorkerPoolManager(
{
workerFactory: () => {
try {
return new DiffsWorker();
} catch (cause) {
throw new DiffWorkerError({ operation: "create-worker", themeName, cause });
}
},
poolSize,
totalASTLRUCacheSize: 240,
},
{
theme: themeName,
preferredHighlighter: PREFERRED_HIGHLIGHTER,
tokenizeMaxLineLength: 1_000,
useTokenTransformer: true,
},
);
return {
pool,
unsubscribeTheme: subscribeRenderedAppearance(() => applyDiffWorkerTheme(pool)),
consumers: 0,
idleTimer: undefined,
};
}
function acquireDiffWorkerPool(poolSize: number) {
const entry = (sharedWorkerPool ??= createDiffWorkerPool(poolSize));
clearTimeout(entry.idleTimer);
entry.idleTimer = undefined;
entry.consumers += 1;
return entry;
}
// Plain-text views do not queue a highlight task that could retry a blank first render.
function DiffWorkerReady({ children }: { children?: ReactNode }) {
const workerPool = useWorkerPool();
const [readyPool, setReadyPool] = useState<WorkerPoolManager>();
const ready = workerPool
? readyPool === workerPool || workerPool.isInitialized() || !workerPool.isWorkingPool()
: typeof window === "undefined";
// oxlint-disable-next-line t3code/no-unreasoned-state-sync -- initializes the diff worker pool and marks it ready when its async startup settles
useEffect(() => {
if (ready || !workerPool) return;
let mounted = true;
const finish = () => {
if (mounted) setReadyPool(workerPool);
};
void workerPool.initialize().then(finish, finish);
return () => {
mounted = false;
};
}, [ready, workerPool]);
return ready ? (
children
) : (
<div
role="status"
className="flex min-h-0 flex-1 items-center justify-center p-4 text-xs text-muted-foreground"
>
Loading code...
</div>
);
}
// Module-level so the subscribe identity is stable: a new one would release and reacquire
// the shared pool on every render.
function subscribeSharedWorkerPool(onStoreChange: () => void) {
if (typeof window === "undefined") return () => {};
const entry = acquireDiffWorkerPool(resolveDiffWorkerPoolSize());
onStoreChange();
return () => {
entry.consumers -= 1;
if (entry.consumers !== 0) return;
entry.idleTimer = setTimeout(() => {
entry.idleTimer = undefined;
entry.unsubscribeTheme();
entry.pool.terminate();
if (sharedWorkerPool === entry) sharedWorkerPool = undefined;
}, DIFF_WORKER_IDLE_TTL_MS);
};
}
export function useDiffWorkerPool() {
return useSyncExternalStore(
subscribeSharedWorkerPool,
() => sharedWorkerPool?.pool,
() => undefined,
);
}
export function DiffWorkerPoolProvider({ children }: { children?: ReactNode }) {
const workerPool = useDiffWorkerPool();
return (
<WorkerPoolContext value={workerPool}>
<DiffWorkerReady>{children}</DiffWorkerReady>
</WorkerPoolContext>
);
}