From 45f819512d8ee1402b5a9c9fb92174abbcbdbba7 Mon Sep 17 00:00:00 2001 From: undefined Date: Tue, 11 Apr 2023 16:30:58 +0800 Subject: [PATCH] ui: lazyload api --- packages/ui-default/api.ts | 38 +++---------------- .../ui-default/components/monaco/loader.ts | 15 ++++---- packages/ui-default/lazyload.ts | 34 +++++++++++++++++ 3 files changed, 47 insertions(+), 40 deletions(-) create mode 100644 packages/ui-default/lazyload.ts diff --git a/packages/ui-default/api.ts b/packages/ui-default/api.ts index c44a7fb0..b0a674aa 100644 --- a/packages/ui-default/api.ts +++ b/packages/ui-default/api.ts @@ -16,38 +16,10 @@ export { default as React } from 'react'; export { default as ReactDOM } from 'react-dom/client'; export * from './misc/Page'; export { initPageLoader } from './hydro'; +export * from './lazyload'; +import { load } from './lazyload'; -const lazyModules = {}; -const features = {}; -export default async function load(name: string) { - if (window.node_modules[name]) return window.node_modules[name]; - if (name === 'echarts') return import('echarts'); - if (name === 'moment') return import('moment'); - if (!window.lazyloadMetadata?.[`${name}.lazy.js`]) throw new Error(`Module ${name} not found`); - if (lazyModules[name]) return lazyModules[name]; - const tag = document.createElement('script'); - tag.src = `/lazy/${window.lazyloadMetadata[`${name}.lazy.js`]}/${name}.lazy.js`; - lazyModules[name] = new Promise((resolve, reject) => { - tag.onerror = reject; - const timeout = setTimeout(reject, 30000); - window.lazyModuleResolver[name] = (item) => { - clearTimeout(timeout); - resolve(item); - }; - }); - document.body.appendChild(tag); - return lazyModules[name]; -} -export async function getFeatures(name: string) { - const legacy = Object.keys(window.externalModules).filter((i) => i === name || i.startsWith(`${name}@`)); - const c = Object.keys(features).filter((i) => i === name || i.startsWith(`${name}@`)); - return legacy.concat(c); -} - -export function provideFeature(name: string, content: string) { - features[name] = content; -} - +export default load; export interface EventMap { } import AutoComplete from './components/autocomplete'; @@ -57,7 +29,7 @@ import ProblemSelectAutoComplete from './components/autocomplete/ProblemSelectAu import UserSelectAutoComplete from './components/autocomplete/UserSelectAutoComplete'; export { - load, AutoComplete, UserSelectAutoComplete, ProblemSelectAutoComplete, DomainSelectAutoComplete, CustomSelectAutoComplete, + AutoComplete, UserSelectAutoComplete, ProblemSelectAutoComplete, DomainSelectAutoComplete, CustomSelectAutoComplete, }; export function addPage(page: import('./misc/Page').Page | (() => Promise | void)) { window.Hydro.extraPages.push(page); @@ -82,7 +54,7 @@ import ReactDOM from 'react-dom/client'; import * as redux from 'react-redux'; const modules = { - _, $, React, redux, ReactDOM, load, + _, $, React, redux, ReactDOM, }; declare global { diff --git a/packages/ui-default/components/monaco/loader.ts b/packages/ui-default/components/monaco/loader.ts index 24c47601..66cbad88 100644 --- a/packages/ui-default/components/monaco/loader.ts +++ b/packages/ui-default/components/monaco/loader.ts @@ -1,4 +1,4 @@ -import { load as loadModule } from '@hydrooj/ui-default'; +import { getFeatures, load as loadModule } from '../../lazyload'; let loaded; @@ -35,11 +35,12 @@ const loaders = { typescript: () => import('./languages/typescript'), yaml: () => import('./languages/yaml'), external: async (monaco, feat) => { - const items = Object.keys(window.externalModules).filter((i) => i === `monaco-${feat}` || i.startsWith(`monaco-${feat}@`)); - for (const item of items) { - let apply = (item.startsWith('http') || item.startsWith('/')) - ? await legacyLoadExternalModule(window.externalModules[item]) - : (await loadModule(item)).apply; + for (const item of await getFeatures(`monaco-${feat}`)) { + let apply = typeof item === 'function' + ? item + : (item.startsWith('http') || item.startsWith('/')) + ? await legacyLoadExternalModule(window.externalModules[item]) + : (await loadModule(item)).apply; if (typeof apply !== 'function') apply = apply.default || apply.apply; if (typeof apply === 'function') await apply(monaco); } @@ -65,7 +66,7 @@ export async function load(features = ['markdown']) { for (const feat of features) { if (loaded.includes(feat)) continue; if (!loaders[feat]) { - const items = Object.keys(window.externalModules).filter((i) => i === `monaco-${feat}` || i.startsWith(`monaco-${feat}@`)); + const items = await getFeatures(`monaco-${feat}`); if (!items.length) { console.warn('Unknown monaco feature:', feat); continue; diff --git a/packages/ui-default/lazyload.ts b/packages/ui-default/lazyload.ts new file mode 100644 index 00000000..e70557a9 --- /dev/null +++ b/packages/ui-default/lazyload.ts @@ -0,0 +1,34 @@ +const lazyModules = {}; +const features: Record Promise)> = {}; +export default async function load(name: string) { + if (window.node_modules[name]) return window.node_modules[name]; + if (name === 'echarts') return import('echarts'); + if (name === 'moment') return import('moment'); + if (!window.lazyloadMetadata?.[`${name}.lazy.js`]) throw new Error(`Module ${name} not found`); + if (lazyModules[name]) return lazyModules[name]; + const tag = document.createElement('script'); + tag.src = `/lazy/${window.lazyloadMetadata[`${name}.lazy.js`]}/${name}.lazy.js`; + lazyModules[name] = new Promise((resolve, reject) => { + tag.onerror = reject; + const timeout = setTimeout(reject, 30000); + window.lazyModuleResolver[name] = (item) => { + clearTimeout(timeout); + resolve(item); + }; + }); + document.body.appendChild(tag); + return lazyModules[name]; +} +export { load }; +export async function getFeatures(name: string) { + const legacy = Object.keys(window.externalModules).filter((i) => i === name || i.startsWith(`${name}@`)) + .map((i) => window.externalModules[i]); + const c = Object.keys(features).filter((i) => i === name || i.startsWith(`${name}@`)) + .map((i) => features[i]); + console.log(legacy, c, features); + return c.concat(legacy); +} + +export function provideFeature(name: string, content: string | (() => Promise)) { + features[name] = content; +}