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();