From e8bdc469211b8f715d35abf96b193285f375938a Mon Sep 17 00:00:00 2001 From: PupilTong <12288479+PupilTong@users.noreply.github.com> Date: Fri, 18 Apr 2025 11:46:55 +0800 Subject: [PATCH] feat: allow user to implement custom template load function (#587) ```js lynxView.customTemplateLoader = (url) => { return (await (await fetch(url, { method: 'GET', })).json()) } ``` fix #229 --------- Signed-off-by: PupilTong <12288479+PupilTong@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .changeset/small-carpets-reply.md | 13 ++++++++ .../web-core/src/apis/LynxView.ts | 10 +++++++ packages/web-platform/web-core/src/index.ts | 1 + .../web-core/src/uiThread/startUIThread.ts | 7 ++--- .../web-core/src/utils/loadTemplate.ts | 13 +++++--- .../web-tests/shell-project/index.ts | 30 +++++++++++++++++++ .../web-tests/tests/react.spec.ts | 6 ++++ 7 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 .changeset/small-carpets-reply.md diff --git a/.changeset/small-carpets-reply.md b/.changeset/small-carpets-reply.md new file mode 100644 index 0000000000..8bba0fdb4c --- /dev/null +++ b/.changeset/small-carpets-reply.md @@ -0,0 +1,13 @@ +--- +"@lynx-js/web-core": patch +--- + +feat: allow user to implement custom template load function + +```js +lynxView.customTemplateLoader = (url) => { + return (await (await fetch(url, { + method: 'GET', + })).json()); +}; +``` diff --git a/packages/web-platform/web-core/src/apis/LynxView.ts b/packages/web-platform/web-core/src/apis/LynxView.ts index e52cdd162d..43020fee2d 100644 --- a/packages/web-platform/web-core/src/apis/LynxView.ts +++ b/packages/web-platform/web-core/src/apis/LynxView.ts @@ -8,6 +8,7 @@ import { } from './createLynxView.js'; import { type Cloneable, + type LynxTemplate, type NapiModulesCall, type NapiModulesMap, type NativeModulesCall, @@ -44,6 +45,7 @@ export type INapiModulesCall = ( * @property {INapiModulesCall} onNapiModulesCall [optional] the NapiModule value handler. * @property {"false" | "true" | null} injectHeadLinks [optional] @default true set it to "false" to disable injecting the styles into shadowroot * @property {number} lynxGroupId [optional] (attribute: "lynx-group-id") the background shared context id, which is used to share webworker between different lynx cards + * @property {(string)=>Promise} customTemplateLoader [optional] the custom template loader, which is used to load the template * * @event error lynx card fired an error * @@ -293,6 +295,13 @@ export class LynxView extends HTMLElement { } } + /** + * @public + * allow user to customize the template loader + * @param url the url of the template + */ + customTemplateLoader?: (url: string) => Promise; + /** * @private the flag to group all changes into one render operation */ @@ -352,6 +361,7 @@ export class LynxView extends HTMLElement { new CustomEvent('error', {}), ); }, + customTemplateLoader: this.customTemplateLoader, }, }); this.#instance = lynxView; diff --git a/packages/web-platform/web-core/src/index.ts b/packages/web-platform/web-core/src/index.ts index e0bfc03e8f..62fb7b9c60 100644 --- a/packages/web-platform/web-core/src/index.ts +++ b/packages/web-platform/web-core/src/index.ts @@ -3,3 +3,4 @@ // LICENSE file in the root directory of this source tree. export { createLynxView } from './apis/createLynxView.js'; export { LynxView } from './apis/LynxView.js'; +export type { LynxTemplate } from '@lynx-js/web-constants'; diff --git a/packages/web-platform/web-core/src/uiThread/startUIThread.ts b/packages/web-platform/web-core/src/uiThread/startUIThread.ts index b75861f3c4..af8e9b49aa 100644 --- a/packages/web-platform/web-core/src/uiThread/startUIThread.ts +++ b/packages/web-platform/web-core/src/uiThread/startUIThread.ts @@ -16,6 +16,7 @@ import { mainThreadStartEndpoint, markTimingEndpoint, sendGlobalEventEndpoint, + type LynxTemplate, type MainThreadStartConfigs, type NapiModulesCall, type NativeModulesCall, @@ -34,10 +35,10 @@ export function startUIThread( nativeModulesCall: NativeModulesCall; napiModulesCall: NapiModulesCall; onError?: () => void; + customTemplateLoader?: (url: string) => Promise; }, ): LynxView { const createLynxStartTiming = performance.now() + performance.timeOrigin; - const { nativeModulesMap, napiModulesMap } = configs; const { mainThreadRpc, backgroundRpc, @@ -56,13 +57,11 @@ export function startUIThread( }; markTimingInternal('create_lynx_start', undefined, createLynxStartTiming); markTimingInternal('load_template_start'); - loadTemplate(templateUrl).then((template) => { + loadTemplate(templateUrl, callbacks.customTemplateLoader).then((template) => { markTimingInternal('load_template_end'); mainThreadStart({ ...configs, template, - nativeModulesMap, - napiModulesMap, }); }); registerReportErrorHandler( diff --git a/packages/web-platform/web-core/src/utils/loadTemplate.ts b/packages/web-platform/web-core/src/utils/loadTemplate.ts index d153bfce9d..06fa34d715 100644 --- a/packages/web-platform/web-core/src/utils/loadTemplate.ts +++ b/packages/web-platform/web-core/src/utils/loadTemplate.ts @@ -113,12 +113,17 @@ const backgroundInjectWithBind = [ 'Component', ]; -export async function loadTemplate(url: string): Promise { +export async function loadTemplate( + url: string, + customTemplateLoader?: (url: string) => Promise, +): Promise { const cachedTemplate = TemplateCache[url]; if (cachedTemplate) return cachedTemplate; - const template = (await (await fetch(url, { - method: 'GET', - })).json()) as LynxTemplate; + const template = customTemplateLoader + ? await customTemplateLoader(url) + : (await (await fetch(url, { + method: 'GET', + })).json()) as LynxTemplate; const decodedTemplate: LynxTemplate = { ...template, lepusCode: generateJavascriptUrl( diff --git a/packages/web-platform/web-tests/shell-project/index.ts b/packages/web-platform/web-tests/shell-project/index.ts index 773151d7db..35afdcdce5 100644 --- a/packages/web-platform/web-tests/shell-project/index.ts +++ b/packages/web-platform/web-tests/shell-project/index.ts @@ -2,6 +2,7 @@ // Licensed under the Apache License Version 2.0 that can be found in the // LICENSE file in the root directory of this source tree. +import type { LynxTemplate } from '@lynx-js/web-core'; import { lynxViewTests } from './lynx-view.ts'; const nativeModulesMap = { @@ -45,6 +46,35 @@ if (casename) { return data.color; } }; + if (casename.includes('custom-template-loader')) { + lynxView.customTemplateLoader = async () => { + const template: LynxTemplate = { + styleInfo: {}, + pageConfig: { + enableCSSSelector: true, + enableRemoveCSSScope: true, + defaultDisplayLinear: true, + defaultOverflowVisible: true, + }, + customSections: {}, + lepusCode: { + root: ` + + let root = __CreatePage('page', 0); + __AddInlineStyle(root, 'min-height', '80px'); + __AddInlineStyle(root, 'width', '80px'); + __AddInlineStyle(root, 'background', 'green'); + __SetID(root, 'target'); + __FlushElementTree(); + `, + }, + manifest: { + '/app-service.js': '', + }, + }; + return template; + }; + } }); if (casename2) { lynxViewTests(lynxView2 => { diff --git a/packages/web-platform/web-tests/tests/react.spec.ts b/packages/web-platform/web-tests/tests/react.spec.ts index 379a87ff2b..a99773c00b 100644 --- a/packages/web-platform/web-tests/tests/react.spec.ts +++ b/packages/web-platform/web-tests/tests/react.spec.ts @@ -342,6 +342,12 @@ test.describe('reactlynx3 tests', () => { }); }); test.describe('apis', () => { + test('api-custom-template-loader', async ({ page }, { title }) => { + await goto(page, title); + await wait(100); + const target = page.locator('#target'); + await expect(target).toHaveCSS('background-color', 'rgb(0, 128, 0)'); // green + }); test('api-animation-event', async ({ page }, { title }) => { await goto(page, title); await page.locator('#tap1').click();