mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-01 22:52:21 +00:00
e74b4db44d
xterm-the-terminal-emulator and its three addons (fit, search, serialize) used to be imported at module scope by Terminal.tsx, BashExecModal.tsx, and HostConsole.tsx along with xterm's CSS. Even though only Terminal.tsx is rendered eagerly inside the editor layout, the static imports forced the ~660 KB xterm chunk plus the xterm.css bytes into every cold app start regardless of whether a user ever opened a terminal. Move the bootstrap into a new frontend/src/lib/xtermLoader.ts module. loadXtermModules() Promise.alls the four addon imports plus the CSS, caches the result on a shared promise, and returns the constructors. On rejection the cache is cleared so the next mount can retry instead of rethrowing the same failed promise. Three consumers (Terminal, BashExecModal, HostConsole) swap their value imports for type-only InstanceType aliases from the loader, then call loadXtermModules() inside their existing useEffect. A mounted/cancelled flag in each effect closure prevents initialisation if the component unmounts during the load. The vite.config.ts manualChunks group from #823 already groups all @xterm/* packages into the xterm chunk, so it now loads on demand instead of being bundled into the entry chunk.
54 lines
2.1 KiB
TypeScript
54 lines
2.1 KiB
TypeScript
/**
|
|
* Lazy-loaded xterm. The terminal is only mounted by surfaces the user
|
|
* explicitly opens (TerminalComponent, BashExecModal, HostConsole), but
|
|
* the eager top-level imports used to pull the ~660 KB @xterm chunk plus
|
|
* xterm.css into every cold app start. Defer the chunk until the first
|
|
* terminal mount; the loader caches the imported modules so subsequent
|
|
* mounts hit a warm cache.
|
|
*
|
|
* All four addons live in the same `xterm` manual chunk (see
|
|
* vite.config.ts), so loading the namespace is the same network cost
|
|
* whether one or four classes are needed downstream.
|
|
*/
|
|
|
|
export type Terminal = InstanceType<typeof import('@xterm/xterm')['Terminal']>;
|
|
export type FitAddon = InstanceType<typeof import('@xterm/addon-fit')['FitAddon']>;
|
|
export type SearchAddon = InstanceType<typeof import('@xterm/addon-search')['SearchAddon']>;
|
|
export type SerializeAddon = InstanceType<typeof import('@xterm/addon-serialize')['SerializeAddon']>;
|
|
|
|
export interface XtermModules {
|
|
Terminal: typeof import('@xterm/xterm')['Terminal'];
|
|
FitAddon: typeof import('@xterm/addon-fit')['FitAddon'];
|
|
SearchAddon: typeof import('@xterm/addon-search')['SearchAddon'];
|
|
SerializeAddon: typeof import('@xterm/addon-serialize')['SerializeAddon'];
|
|
}
|
|
|
|
let cachedPromise: Promise<XtermModules> | null = null;
|
|
|
|
export function loadXtermModules(): Promise<XtermModules> {
|
|
if (!cachedPromise) {
|
|
cachedPromise = (async () => {
|
|
const [xtermMod, fitMod, searchMod, serializeMod] = await Promise.all([
|
|
import('@xterm/xterm'),
|
|
import('@xterm/addon-fit'),
|
|
import('@xterm/addon-search'),
|
|
import('@xterm/addon-serialize'),
|
|
import('@xterm/xterm/css/xterm.css'),
|
|
]);
|
|
return {
|
|
Terminal: xtermMod.Terminal,
|
|
FitAddon: fitMod.FitAddon,
|
|
SearchAddon: searchMod.SearchAddon,
|
|
SerializeAddon: serializeMod.SerializeAddon,
|
|
};
|
|
})().catch((err) => {
|
|
// On a failed chunk fetch (transient network blip), drop the cached
|
|
// promise so the next terminal mount retries the load instead of
|
|
// permanently rethrowing.
|
|
cachedPromise = null;
|
|
throw err;
|
|
});
|
|
}
|
|
return cachedPromise;
|
|
}
|