UNPKG

@lynx-js/web-core

Version:

This is an internal experimental package, do not use

201 lines 8.7 kB
import { loadUnknownElementEventName, systemInfoBase, } from '../../constants.js'; import { BackgroundThread } from './Background.js'; import { I18nManager } from './I18n.js'; import { WASMJSBinding } from './elementAPIs/WASMJSBinding.js'; import { ExposureServices } from './ExposureServices.js'; import { createElementAPI } from './elementAPIs/createElementAPI.js'; import { createMainThreadGlobalAPIs } from './createMainThreadGlobalAPIs.js'; import { templateManager } from './TemplateManager.js'; import { loadAllWebElements } from '../webElementsDynamicLoader.js'; // @ts-expect-error import IN_SHADOW_CSS_MODERN from '../../../css/in_shadow.css?inline'; import { requestIdleCallbackImpl } from './utils/requestIdleCallback.js'; loadAllWebElements().catch((e) => { console.error('[lynx-web] Failed to load web elements', e); }); const IN_SHADOW_CSS = URL.createObjectURL(new Blob([IN_SHADOW_CSS_MODERN], { type: 'text/css' })); const linkElement = document.createElement('link'); linkElement.rel = 'stylesheet'; linkElement.href = IN_SHADOW_CSS; linkElement.type = 'text/css'; linkElement.fetchPriority = 'high'; linkElement.blocking = 'render'; const pixelRatio = window.devicePixelRatio; const screenWidth = window.screen.availWidth * pixelRatio; const screenHeight = window.screen.availHeight * pixelRatio; export function createSystemInfo(browserConfig) { return Object.freeze({ ...systemInfoBase, // some information only available on main thread, we should read and pass to worker pixelRatio, pixelWidth: screenWidth, pixelHeight: screenHeight, ...browserConfig, }); } export class LynxViewInstance { parentDom; initData; globalprops; templateUrl; rootDom; mtsRealm; isSSR; transformVW; transformVH; transformREM; mainThreadGlobalThis; mtsWasmBinding; backgroundThread; i18nManager; exposureServices; webElementsLoadingPromises = []; #queryComponentCache = new Map(); #pageConfig; #nativeModulesMap; #napiModulesMap; lepusCodeUrls = new Map(); systemInfo; constructor(parentDom, initData, globalprops, templateUrl, rootDom, mtsRealm, isSSR, lynxGroupId, nativeModulesMap = {}, napiModulesMap = {}, initI18nResources, transformVW = false, transformVH = false, transformREM = false, browserConfig) { this.parentDom = parentDom; this.initData = initData; this.globalprops = globalprops; this.templateUrl = templateUrl; this.rootDom = rootDom; this.mtsRealm = mtsRealm; this.isSSR = isSSR; this.transformVW = transformVW; this.transformVH = transformVH; this.transformREM = transformREM; this.systemInfo = createSystemInfo(browserConfig); if (!isSSR) { this.rootDom.append(linkElement.cloneNode(false)); } this.#nativeModulesMap = nativeModulesMap; this.#napiModulesMap = napiModulesMap; this.mainThreadGlobalThis = mtsRealm.globalWindow; this.backgroundThread = new BackgroundThread(lynxGroupId, this); this.i18nManager = new I18nManager(this.backgroundThread, this.rootDom, initI18nResources); this.mtsWasmBinding = new WASMJSBinding(this); this.exposureServices = new ExposureServices(this); this.backgroundThread.markTiming('create_lynx_start'); } onPageConfigReady(config) { if (this.#pageConfig) { return; } // create element APIs this.#pageConfig = config; const enableCSSSelector = config['enableCSSSelector'] == 'true'; const defaultDisplayLinear = config['defaultDisplayLinear'] == 'true'; const defaultOverflowVisible = config['defaultOverflowVisible'] == 'true'; Object.assign(this.mtsRealm.globalWindow, createElementAPI(this.rootDom, this.mtsWasmBinding, enableCSSSelector, defaultDisplayLinear, defaultOverflowVisible, this.transformVW, this.transformVH, this.transformREM), createMainThreadGlobalAPIs(this)); } onStyleInfoReady(currentUrl) { if (this.mtsWasmBinding.wasmContext) { const resource = templateManager.getStyleSheet(currentUrl); if (resource) { this.mtsWasmBinding.wasmContext.push_style_sheet(resource, this.templateUrl === currentUrl ? undefined : currentUrl); } } } async onMTSScriptsLoaded(currentUrl, isLazy) { this.backgroundThread.markTiming('lepus_execute_start'); const urlMap = templateManager.getBundle(currentUrl) ?.lepusCode; this.lepusCodeUrls.set(currentUrl, urlMap); if (!isLazy) { await this.mtsRealm.loadScript(urlMap['root']); this.onMTSScriptsExecuted(); } } onMTSScriptsExecuted() { this.backgroundThread.markTiming('lepus_execute_end'); this.webElementsLoadingPromises.length = 0; this.backgroundThread.markTiming('data_processor_start'); const processedData = this.#pageConfig?.['enableJSDataProcessor'] !== 'true' && this.mainThreadGlobalThis.processData ? this.mainThreadGlobalThis.processData?.(this.initData) : this.initData; this.backgroundThread.markTiming('data_processor_end'); this.backgroundThread.startWebWorker(processedData, this.globalprops, templateManager.getBundle(this.templateUrl).config.cardType, templateManager.getBundle(this.templateUrl)?.customSections, this.#nativeModulesMap, this.#napiModulesMap); if (this.isSSR) { this.rootDom.querySelector('[part="page"]')?.remove(); } this.mainThreadGlobalThis.renderPage?.(processedData); this.mainThreadGlobalThis.__FlushElementTree(); } async onBTSScriptsLoaded(url) { const btsUrls = templateManager.getBundle(url) ?.backgroundCode; await this.backgroundThread.updateBTSChunk(url, btsUrls); this.backgroundThread.startBTS(); } loadUnknownElement(tagName) { if (tagName.includes('-') && !customElements.get(tagName)) { this.rootDom.dispatchEvent(new CustomEvent(loadUnknownElementEventName, { detail: { tagName, }, })); this.webElementsLoadingPromises.push(customElements.whenDefined(tagName).then(() => { })); } } queryComponent(url) { if (this.#queryComponentCache.has(url)) { return this.#queryComponentCache.get(url); } const promise = templateManager.fetchBundle(url, Promise.resolve(this), this.transformVW, this.transformVH, this.transformREM, { enableCSSSelector: this.#pageConfig['enableCSSSelector'], }) .then(async () => { const urlMap = this.lepusCodeUrls.get(url); const rootUrl = urlMap?.['root']; if (!rootUrl) { throw new Error(`[lynx-web] Missing root URL for component: ${url}`); } let lepusRootChunkExport = await this.mtsRealm.loadScript(rootUrl); lepusRootChunkExport = this.mainThreadGlobalThis.processEvalResult?.(lepusRootChunkExport, url) ?? lepusRootChunkExport; return lepusRootChunkExport; }); this.#queryComponentCache.set(url, promise); return promise; } async updateData(data, processorName) { const processedData = this.#pageConfig['enableJSDataProcessor'] !== 'true' && this.mainThreadGlobalThis.processData ? this.mainThreadGlobalThis.processData(data, processorName) : data; this.mainThreadGlobalThis.updatePage?.(processedData, { processorName }); await this.backgroundThread.updateData(processedData, { processorName }); } async updateGlobalProps(data) { await this.backgroundThread.updateGlobalProps(data); } reportError(error, release, fileName) { this.rootDom.dispatchEvent(new CustomEvent('error', { detail: { sourceMap: { offset: { line: 2, col: 0, }, }, error, release, fileName, }, bubbles: true, cancelable: true, composed: true, })); } async [Symbol.asyncDispose]() { await this.backgroundThread[Symbol.asyncDispose](); this.exposureServices.dispose(); requestIdleCallbackImpl(() => { this.mtsWasmBinding.dispose(); }); } } //# sourceMappingURL=LynxViewInstance.js.map