diff --git a/.changeset/lemon-colts-hang.md b/.changeset/lemon-colts-hang.md new file mode 100644 index 0000000000..7a588eaee2 --- /dev/null +++ b/.changeset/lemon-colts-hang.md @@ -0,0 +1,14 @@ +--- +"@lynx-js/web-mainthread-apis": patch +"@lynx-js/web-core-server": patch +"@lynx-js/web-constants": patch +"@lynx-js/web-core": patch +--- + +feat: move SSR hydrate essential info to the ssr attribute + +We found that in browser there is no simple tool to decode a base64 string + +Therefore we move the data to `ssr` attribute + +Also fix some ssr issues diff --git a/packages/web-platform/web-constants/src/types/MainThreadGlobalThis.ts b/packages/web-platform/web-constants/src/types/MainThreadGlobalThis.ts index a2aaf15625..a71113a7f2 100644 --- a/packages/web-platform/web-constants/src/types/MainThreadGlobalThis.ts +++ b/packages/web-platform/web-constants/src/types/MainThreadGlobalThis.ts @@ -342,7 +342,7 @@ export interface MainThreadGlobalThis { lynx: MainThreadLynx; processData?: ProcessDataCallback; ssrEncode?: () => string; - ssrHydrate?: (encodeData?: string) => void; + ssrHydrate?: (encodeData?: string | null) => void; _ReportError: (error: Error, _: unknown) => void; _SetSourceMapRelease: (errInfo: JSErrorInfo) => void; __OnLifecycleEvent: (lifeCycleEvent: Cloneable) => void; diff --git a/packages/web-platform/web-constants/src/types/SSR.ts b/packages/web-platform/web-constants/src/types/SSR.ts new file mode 100644 index 0000000000..cd6b73361d --- /dev/null +++ b/packages/web-platform/web-constants/src/types/SSR.ts @@ -0,0 +1,17 @@ +import type { AddEventPAPI } from './MainThreadGlobalThis.js'; + +export type SSREventReplayInfo = [ + number, + Parameters[1], + Parameters[2], + Parameters[3], +]; + +export type SSRDumpInfo = { + ssrEncodeData: string | null | undefined; + events: SSREventReplayInfo[]; +}; + +export type SSRDehydrateHooks = { + __AddEvent: AddEventPAPI; +}; diff --git a/packages/web-platform/web-constants/src/types/index.ts b/packages/web-platform/web-constants/src/types/index.ts index 3aadd5fd0b..7115892c44 100644 --- a/packages/web-platform/web-constants/src/types/index.ts +++ b/packages/web-platform/web-constants/src/types/index.ts @@ -18,3 +18,4 @@ export * from './MainThreadLynx.js'; export * from './I18n.js'; export * from './BackThreadStartConfigs.js'; export * from './MarkTiming.js'; +export * from './SSR.js'; diff --git a/packages/web-platform/web-core-server/src/createLynxView.ts b/packages/web-platform/web-core-server/src/createLynxView.ts index fb20919b88..ba8ef21337 100644 --- a/packages/web-platform/web-core-server/src/createLynxView.ts +++ b/packages/web-platform/web-core-server/src/createLynxView.ts @@ -3,9 +3,10 @@ import { inShadowRootStyles, lynxPartIdAttribute, lynxUniqueIdAttribute, - type AddEventPAPI, type InitI18nResources, type StartMainThreadContextConfig, + type SSREventReplayInfo, + type SSRDumpInfo, } from '@lynx-js/web-constants'; import { Rpc } from '@lynx-js/web-worker-rpc'; import { prepareMainThreadAPIs } from '@lynx-js/web-mainthread-apis'; @@ -117,6 +118,7 @@ export async function createLynxView( }, }); const i18nResources = new I18nResources(); + const events: SSREventReplayInfo[] = []; const { startMainThread } = prepareMainThreadAPIs( backgroundThreadRpc, offscreenDocument, @@ -140,6 +142,16 @@ export async function createLynxView( i18nResources.setData(initI18nResources); return i18nResources; }, + { + __AddEvent(element, eventName, eventData, eventOptions) { + events.push([ + Number(element.getAttribute(lynxUniqueIdAttribute)!), + eventName, + eventData, + eventOptions, + ]); + }, + }, ); const runtime = await startMainThread({ template, @@ -155,17 +167,6 @@ export async function createLynxView( initI18nResources, }); - const events: [ - number, - Parameters[1], - Parameters[2], - Parameters[3], - ][] = []; - runtime.__AddEvent = (element, eventType, eventName, newEventHandler) => { - const uniqueId = runtime.__GetElementUniqueID(element); - events.push([uniqueId, eventType, eventName, newEventHandler]); - }; - const elementTemplates = { ...builtinElementTemplates, ...overrideElementTemplates, @@ -174,11 +175,17 @@ export async function createLynxView( async function renderToString(): Promise { await firstPaintReadyPromise; const ssrEncodeData = runtime?.ssrEncode?.(); + const ssrDumpInfo: SSRDumpInfo = { + ssrEncodeData, + events, + }; const buffer: string[] = []; buffer.push( '', ); - - if (ssrEncodeData) { - buffer.push( - '', - ); // encodeURI to avoid XSS - } buffer.push(''); return buffer.join(''); } diff --git a/packages/web-platform/web-mainthread-apis/src/createMainThreadGlobalThis.ts b/packages/web-platform/web-mainthread-apis/src/createMainThreadGlobalThis.ts index 8389cb8ed5..49853e2a40 100644 --- a/packages/web-platform/web-mainthread-apis/src/createMainThreadGlobalThis.ts +++ b/packages/web-platform/web-mainthread-apis/src/createMainThreadGlobalThis.ts @@ -57,6 +57,7 @@ import { type MinimalRawEventObject, type I18nResourceTranslationOptions, lynxDisposedAttribute, + type SSRDehydrateHooks, } from '@lynx-js/web-constants'; import { globalMuteableVars } from '@lynx-js/web-constants'; import { createMainThreadLynx } from './createMainThreadLynx.js'; @@ -149,6 +150,7 @@ export interface MainThreadRuntimeConfig { & Pick & Partial>; jsContext: LynxContextEventTarget; + ssrHooks?: SSRDehydrateHooks; } export function createMainThreadGlobalThis( @@ -166,6 +168,7 @@ export function createMainThreadGlobalThis( rootDom, globalProps, styleInfo, + ssrHooks, } = config; const lynxUniqueIdToElement: WeakRef[] = []; const elementToRuntimeInfoMap: WeakMap = @@ -669,7 +672,7 @@ export function createMainThreadGlobalThis( : undefined, __MarkTemplateElement, __MarkPartElement, - __AddEvent, + __AddEvent: ssrHooks?.__AddEvent ?? __AddEvent, __GetEvent, __GetEvents, __SetEvents, diff --git a/packages/web-platform/web-mainthread-apis/src/prepareMainThreadAPIs.ts b/packages/web-platform/web-mainthread-apis/src/prepareMainThreadAPIs.ts index 68df5e1024..a071cdb819 100644 --- a/packages/web-platform/web-mainthread-apis/src/prepareMainThreadAPIs.ts +++ b/packages/web-platform/web-mainthread-apis/src/prepareMainThreadAPIs.ts @@ -24,6 +24,7 @@ import { type I18nResources, dispatchI18nResourceEndpoint, type Cloneable, + type SSRDehydrateHooks, } from '@lynx-js/web-constants'; import { registerCallLepusMethodHandler } from './crossThreadHandlers/registerCallLepusMethodHandler.js'; import { registerGetCustomSectionHandler } from './crossThreadHandlers/registerGetCustomSectionHandler.js'; @@ -47,6 +48,7 @@ export function prepareMainThreadAPIs( options: I18nResourceTranslationOptions, ) => void, initialI18nResources: (data: InitI18nResources) => I18nResources, + ssrHooks?: SSRDehydrateHooks, ) { const postTimingFlags = backgroundThreadRpc.createCall( postTimingFlagsEndpoint, @@ -115,6 +117,7 @@ export function prepareMainThreadAPIs( styleInfo, lepusCode: lepusCodeLoaded, rootDom, + ssrHooks, callbacks: { mainChunkReady: () => { markTimingInternal('data_processor_start'); diff --git a/packages/web-platform/web-mainthread-apis/src/pureElementPAPIs.ts b/packages/web-platform/web-mainthread-apis/src/pureElementPAPIs.ts index 7262b90393..b07b405690 100644 --- a/packages/web-platform/web-mainthread-apis/src/pureElementPAPIs.ts +++ b/packages/web-platform/web-mainthread-apis/src/pureElementPAPIs.ts @@ -314,10 +314,12 @@ export const __SetInlineStyles: SetInlineStylesPAPI = /*#__PURE__*/ ( element.setAttribute('style', transformInlineStyleString(value)); } else { const { transformedStyle } = transformParsedStyles( - Object.entries(value).map(([k, value]) => [ - hyphenateStyleName(k), - value, - ]), + Object.entries(value).map(([k, value]) => + [ + hyphenateStyleName(k), + value?.toString?.() ?? '', + ] as [string, string] + ), ); const transformedStyleStr = transformedStyle.map(( [property, value], diff --git a/packages/web-platform/web-tests/tests/__snapshots__/server.vitest.spec.ts.snap b/packages/web-platform/web-tests/tests/__snapshots__/server.vitest.spec.ts.snap index 133269e614..a39d078421 100644 --- a/packages/web-platform/web-tests/tests/__snapshots__/server.vitest.spec.ts.snap +++ b/packages/web-platform/web-tests/tests/__snapshots__/server.vitest.spec.ts.snap @@ -1,34 +1,34 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`server-tests > basic-performance-div-10 1`] = ` -"" + }
" `; exports[`server-tests > basic-performance-event-div-100 1`] = ` -"" + }
" `; exports[`server-tests > basic-performance-nest-level-100 1`] = ` -"" + }
" `; diff --git a/packages/web-platform/web-tests/tests/server.vitest.spec.ts b/packages/web-platform/web-tests/tests/server.vitest.spec.ts index 85d7c0f247..22f814ba3d 100644 --- a/packages/web-platform/web-tests/tests/server.vitest.spec.ts +++ b/packages/web-platform/web-tests/tests/server.vitest.spec.ts @@ -20,4 +20,9 @@ describe('server-tests', () => { const html = await genTemplate('config-css-selector-false-exchange-class'); expect(html).toContain('[l-uid="2"]'); }); + + test('basic-bindtap-contains-bind-event', async () => { + const html = await genTemplate('basic-bindtap'); + expect(html).toContain('bindEvent'); + }); });