refactor/code-splitting #31

Merged
ilia merged 32 commits from refactor/code-splitting into main 2026-04-08 06:34:20 +00:00
4 changed files with 40 additions and 32 deletions
Showing only changes of commit 5249d88df7 - Show all commits

View File

@@ -1,5 +1,8 @@
import { SvelteMap } from 'svelte/reactivity';
import { getEffectiveConcurrency } from './utils/getEffectiveConcurrency/getEffectiveConcurrency';
import {
getEffectiveConcurrency,
yieldToMainThread,
} from './utils';
/** Loading state of a font. Failed loads may be retried up to MAX_RETRIES. */
export type FontStatus = 'loading' | 'loaded' | 'error';
@@ -143,37 +146,7 @@ export class AppliedFontsManager {
}
}
/** Yields to main thread during CPU-intensive parsing. Uses scheduler.yield() (Chrome/Edge) or MessageChannel fallback. */
async #yieldToMain(): Promise<void> {
// @ts-expect-error - scheduler not in TypeScript lib yet
if (typeof scheduler !== 'undefined' && 'yield' in scheduler) {
// @ts-expect-error - scheduler.yield not in TypeScript lib yet
await scheduler.yield();
} else {
await new Promise<void>(resolve => {
const ch = new MessageChannel();
ch.port1.onmessage = () => resolve();
ch.port2.postMessage(null);
});
}
}
/** Returns optimal concurrent fetches based on Network Information API: 1 for 2G, 2 for 3G, 4 for 4G/default. */
#getEffectiveConcurrency(): number {
const nav = navigator as any;
const conn = nav.connection;
if (!conn) return 4;
switch (conn.effectiveType) {
case 'slow-2g':
case '2g':
return 1;
case '3g':
return 2;
default:
return 4;
}
}
/** Returns true if data-saver mode is enabled (defers non-critical weights). */
#shouldDeferNonCritical(): boolean {
@@ -261,7 +234,7 @@ export class AppliedFontsManager {
: (performance.now() - lastYield > YIELD_INTERVAL);
if (shouldYield) {
await this.#yieldToMain();
await yieldToMainThread();
lastYield = performance.now();
}
}

View File

@@ -0,0 +1,2 @@
export { getEffectiveConcurrency } from './getEffectiveConcurrency/getEffectiveConcurrency';
export { yieldToMainThread } from './yieldToMainThread/yieldToMainThread';

View File

@@ -0,0 +1,17 @@
import { yieldToMainThread } from './yieldToMainThread';
describe('yieldToMainThread', () => {
it('uses scheduler.yield when available', async () => {
const mockYield = vi.fn().mockResolvedValue(undefined);
vi.stubGlobal('scheduler', { yield: mockYield });
await yieldToMainThread();
expect(mockYield).toHaveBeenCalledOnce();
vi.unstubAllGlobals();
});
it('falls back to MessageChannel when scheduler is unavailable', async () => {
// scheduler is not defined in jsdom by default
await expect(yieldToMainThread()).resolves.toBeUndefined();
});
});

View File

@@ -0,0 +1,16 @@
/**
* Yields to main thread during CPU-intensive parsing. Uses scheduler.yield() where available or MessageChannel fallback.
*/
export async function yieldToMainThread(): Promise<void> {
// @ts-expect-error - scheduler not in TypeScript lib yet
if (typeof scheduler !== 'undefined' && 'yield' in scheduler) {
// @ts-expect-error - scheduler.yield not in TypeScript lib yet
await scheduler.yield();
} else {
await new Promise<void>(resolve => {
const ch = new MessageChannel();
ch.port1.onmessage = () => resolve();
ch.port2.postMessage(null);
});
}
}