@thestarweb/star-framework-app-web 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,23 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.1.0](https://github.com/star-ss-qaq/star-node-framework/compare/@thestarweb/star-framework-app-web@1.0.2...@thestarweb/star-framework-app-web@1.1.0) (2026-11-01)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **app-web:** 修复a标签包裹其他元素时未被本地路由正确识别的问题 ([547be67](https://github.com/star-ss-qaq/star-node-framework/commit/547be677b8a50c389db57163e072043bf1283afd))
12
+ * **app-web:** 增加解除context监听注册功能,而不是只能等卸载时被动删除 ([8728c21](https://github.com/star-ss-qaq/star-node-framework/commit/8728c21028b6289e62284dc24c21809ac541090e))
13
+
14
+
15
+ ### Features
16
+
17
+ * **app-web:** RenderContext.warpRenderToString不再将非异步的renderToString包装成异步方法 ([d30cb0b](https://github.com/star-ss-qaq/star-node-framework/commit/d30cb0b93cc7290cda7eb3628d04900b37f528da))
18
+
19
+
20
+
21
+
22
+
6
23
  ## [1.0.2](https://github.com/star-ss-qaq/star-node-framework/compare/@thestarweb/star-framework-app-web@1.0.1...@thestarweb/star-framework-app-web@1.0.2) (2026-10-27)
7
24
 
8
25
  **Note:** Version bump only for package @thestarweb/star-framework-app-web
@@ -44,7 +44,7 @@ export const clientRender = __assignType(async (routes) => {
44
44
  });
45
45
  const addedEl = (WeakSet.Ω = [['!']], new WeakSet());
46
46
  function handleAClick(ev) {
47
- const target = ev.target;
47
+ const target = this;
48
48
  if (target.download || target.target || ev.defaultPrevented)
49
49
  return;
50
50
  const rawHref = target.getAttribute("href");
@@ -56,7 +56,7 @@ export const clientRender = __assignType(async (routes) => {
56
56
  ev.preventDefault();
57
57
  }
58
58
  }
59
- handleAClick.__type = ['ev', 'handleAClick', 'P!2!"/"'];
59
+ handleAClick.__type = ['this', 'ev', 'handleAClick', 'P!2!!2""/#'];
60
60
  window.addEventListener("click", __assignType((ev) => {
61
61
  if (ev.target instanceof HTMLElement) {
62
62
  let el = ev.target;
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../../src/view/utils/get-render/client.ts"],"names":[],"mappings":";;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;;4BAEd,gBAAgB;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EACN,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,GACvB,MAAM,aAAa,CAAC;AAErB,SAAS,aAAa,CAAC,QAAoB;IAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC;IAClC,OAAO,CAAC,SAAS,gBAAG,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE;QACrC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;QACrC,IAAI,GAAG;YAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC,6CAAA,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE;QACxC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;AACJ,CAAC;;AAED,SAAS,UAAU,CAAC,IAAc;IACjC,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;AAC7B,CAAC;;AAED,MAAM,CAAC,MAAM,YAAY,gBAAG,KAAK,EAAE,MAAqC,EAAE,EAAE;IAC3E,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAI,MAAc,CAAC,mBAAmB,CAAC,CAAC;IAClE,OAAQ,MAAc,CAAC,mBAAmB,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,KAAK,IAAI,EAAE;QAC1B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACvD,OAAO,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACzB,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,uBAAuB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC;IAC7E,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,aAAa,CAAC,KAAK,IAAI,EAAE;QACxB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,CAAC,GAAG,MAAM,uBAAuB,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QACjE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,IAAO,OAAO,cAAX,IAAI,OAAO,EAAqB,CAAA,CAAC;IACjD,SAAS,YAAY,CAAC,EAAc;QACnC,MAAM,MAAM,GAAG,EAAE,CAAC,MAA2B,CAAC;QAC9C,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,gBAAgB;YAAE,OAAO;QACpE,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO;QAChD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACtB,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YACzE,EAAE,CAAC,cAAc,EAAE,CAAC;QACrB,CAAC;IACF,CAAC;;IACD,MAAM,CAAC,gBAAgB,CACtB,OAAO,eACP,CAAC,EAAE,EAAE,EAAE;QACN,IAAI,EAAE,CAAC,MAAM,YAAY,WAAW,EAAE,CAAC;YACtC,IAAI,EAAE,GAAuB,EAAE,CAAC,MAAqB,CAAC;YACtD,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACnC,IAAI,EAAE,YAAY,iBAAiB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBACzD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBAChB,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;gBAClD,CAAC;gBACD,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC;YACvB,CAAC;QACF,CAAC;IACF,CAAC,0BACD,IAAI,CACJ,CAAC;IACF,MAAM,cAAc,GAAG,YAAY,EAAE,CAAC;IACtC,cAAc,CAAC,OAAO,cAAC,CAAC,EAAE,EAAE,EAAE;QAC7B,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAiB,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC,yBAAC,CAAC;AACJ,CAAC,uEAAA,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../../src/view/utils/get-render/client.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,41 +1,48 @@
1
1
  {
2
- "name": "@thestarweb/star-framework-app-web",
3
- "version": "1.0.2",
4
- "description": "Star Framework Web App",
5
- "main": "dist/index.js",
6
- "types": "src/index.ts",
7
- "type": "module",
8
- "publishConfig": {
9
- "access": "public"
10
- },
11
- "scripts": {
12
- "build": "tspc -p tsconfig.build.json",
13
- "dev": "tspc -p tsconfig.build.json --watch",
14
- "test": "vitest"
15
- },
16
- "dependencies": {
17
- "@deepkit/type": "^1.0.19",
18
- "@deepkit/type-compiler": "^1.0.0",
19
- "@thestarweb/star-framework-cli": "^1.1.1",
20
- "@thestarweb/star-framework-param-helper": "^1.0.0",
21
- "@thestarweb/star-framework-route": "^1.0.0",
22
- "@thestarweb/star-framework-utils": "^1.0.0",
23
- "@thestarweb/star-framework-web-runtime-node": "^1.0.1",
24
- "node-mocks-http": "^1.18.1",
25
- "qs": "6.15.3",
26
- "tslib": "^2.8.1",
27
- "txml": "^6.0.3"
28
- },
29
- "peerDependencies": {
30
- "@thestarweb/star-framework-cli": "^0.0.1",
31
- "@types/qs": "6.15.1"
32
- },
33
- "devDependencies": {
34
- "@types/node": "^26.0.1",
35
- "ts-patch": "^4.0.1",
36
- "typescript": "^6.0.3",
37
- "vitest": "^4.1.9"
38
- },
39
- "license": "ISC",
40
- "gitHead": "bbe5b17a0f551f9b201c17893b72a7bd4ca362e3"
2
+ "name": "@thestarweb/star-framework-app-web",
3
+ "version": "1.1.0",
4
+ "description": "Star Framework Web App",
5
+ "main": "dist/index.js",
6
+ "types": "src/index.ts",
7
+ "type": "module",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "scripts": {
12
+ "build": "tspc -p tsconfig.build.json",
13
+ "dev": "tspc -p tsconfig.build.json --watch",
14
+ "test": "vitest"
15
+ },
16
+ "dependencies": {
17
+ "@deepkit/type": "^1.0.19",
18
+ "@deepkit/type-compiler": "^1.0.0",
19
+ "@thestarweb/star-framework-cli": "^1.1.2",
20
+ "@thestarweb/star-framework-param-helper": "^1.0.1",
21
+ "@thestarweb/star-framework-route": "^1.0.1",
22
+ "@thestarweb/star-framework-utils": "^1.0.1",
23
+ "@thestarweb/star-framework-web-runtime-node": "^1.0.2",
24
+ "node-mocks-http": "^1.18.1",
25
+ "qs": "6.15.3",
26
+ "tslib": "^2.8.1",
27
+ "txml": "^6.0.3"
28
+ },
29
+ "peerDependencies": {
30
+ "@thestarweb/star-framework-cli": "^0.0.1",
31
+ "@types/qs": "6.15.1"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^26.0.1",
35
+ "ts-patch": "^4.0.1",
36
+ "typescript": "^6.0.3",
37
+ "vitest": "^4.1.9"
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "src",
42
+ "resource",
43
+ "CHANGELOG.md",
44
+ "README.md"
45
+ ],
46
+ "license": "ISC",
47
+ "gitHead": "f8c6c6d01c2e268505db24d5e53c52ceddfbd5f7"
41
48
  }
@@ -1,59 +1,66 @@
1
- export class SFDataContext<T> {
2
- private constructor(private _root: SFRawDataContext<T>) {}
3
- get value() {
4
- return this._root.value;
5
- }
6
- private callbacks = new Set<(newValue: T) => void>();
7
- private children = new Set<SFDataContext<T>>();
8
- fock() {
9
- return this._root.createChild();
10
- }
11
- watch(callback: (newValue: T) => void, updateNow = false) {
12
- if (this.callbacks.size === 0)
13
- // @ts-ignore 2341
14
- this._root.children.add(this);
15
- this.callbacks.add(callback);
16
- if (updateNow) callback(this.value);
17
- }
18
- destroy() {
19
- this._root.destroyChild(this);
20
- }
21
- }
22
- export class SFRawDataContext<T> {
23
- constructor(private _value: T) {}
24
- get value() {
25
- return this._value;
26
- }
27
- update(newValue: T) {
28
- this._value = newValue;
29
- const called = new Set();
30
- this.children.forEach((i) => {
31
- (i as any).callbacks.forEach((c: any) => {
32
- if (!called.has(c)) {
33
- called.add(c);
34
- c(newValue);
35
- }
36
- });
37
- });
38
- }
39
- private children = new Set<SFDataContext<T>>();
40
- createChild() {
41
- const child: SFDataContext<T> =
42
- // @ts-ignore
43
- new SFDataContext(this);
44
- return child;
45
- }
46
- destroyChild(child: SFDataContext<T>) {
47
- this.children.delete(child);
48
- }
49
- }
50
- export function forkDataContextRecord<T extends Record<keyof T, any>>(
51
- data: T,
52
- ): T {
53
- return Object.fromEntries(
54
- Object.entries(data).map(([k, context]) => [
55
- k,
56
- context instanceof SFDataContext ? context.fock() : context,
57
- ]),
58
- ) as T;
59
- }
1
+ export class SFDataContext<T> {
2
+ private constructor(private _root: SFRawDataContext<T>) {}
3
+ get value() {
4
+ return this._root.value;
5
+ }
6
+ private callbacks = new Set<(newValue: T) => void>();
7
+ private children = new Set<SFDataContext<T>>();
8
+ fock() {
9
+ return this._root.createChild();
10
+ }
11
+ watch(callback: (newValue: T) => void, updateNow = false) {
12
+ if (this.callbacks.size === 0)
13
+ // @ts-ignore 2341
14
+ this._root.children.add(this);
15
+ this.callbacks.add(callback);
16
+ if (updateNow) callback(this.value);
17
+ return () => {
18
+ this.callbacks.delete(callback);
19
+ if (this.callbacks.size === 0) {
20
+ // @ts-ignore 2341
21
+ this._root.children.delete(this);
22
+ }
23
+ };
24
+ }
25
+ destroy() {
26
+ this._root.destroyChild(this);
27
+ }
28
+ }
29
+ export class SFRawDataContext<T> {
30
+ constructor(private _value: T) {}
31
+ get value() {
32
+ return this._value;
33
+ }
34
+ update(newValue: T) {
35
+ this._value = newValue;
36
+ const called = new Set();
37
+ this.children.forEach((i) => {
38
+ (i as any).callbacks.forEach((c: any) => {
39
+ if (!called.has(c)) {
40
+ called.add(c);
41
+ c(newValue);
42
+ }
43
+ });
44
+ });
45
+ }
46
+ private children = new Set<SFDataContext<T>>();
47
+ createChild() {
48
+ const child: SFDataContext<T> =
49
+ // @ts-ignore
50
+ new SFDataContext(this);
51
+ return child;
52
+ }
53
+ destroyChild(child: SFDataContext<T>) {
54
+ this.children.delete(child);
55
+ }
56
+ }
57
+ export function forkDataContextRecord<T extends Record<keyof T, any>>(
58
+ data: T,
59
+ ): T {
60
+ return Object.fromEntries(
61
+ Object.entries(data).map(([k, context]) => [
62
+ k,
63
+ context instanceof SFDataContext ? context.fock() : context,
64
+ ]),
65
+ ) as T;
66
+ }
@@ -1,225 +1,227 @@
1
- import {
2
- forkDataContextRecord,
3
- RenderDataContext,
4
- SFDataContext,
5
- SystemRenderDataContext,
6
- UserRenderDataContext,
7
- } from "../data-context/index.js";
8
- import { parseHtmlToVDom } from "../html/parse-html-to-v-dom.js";
9
- import { findRouteDom } from "../utils/index.js";
10
-
11
- export interface RenderInstance {
12
- unMount?(): void;
13
- updateProp?(newProp: any): void;
14
- updateChild?(newContext: RenderContext): void;
15
- }
16
- export class WarpRenderInstance {
17
- constructor(
18
- public readonly context: RenderContext,
19
- public readonly dom: HTMLElement,
20
- public readonly rawInstance: RenderInstance,
21
- public readonly dataContext: RenderDataContext,
22
- ) {
23
- this.context._instances.push(this);
24
- }
25
- unMount() {
26
- if (this.rawInstance.unMount) {
27
- this.rawInstance.unMount();
28
- } else {
29
- this.dom.innerHTML = "";
30
- }
31
- Object.values(this.dataContext).forEach((o) => {
32
- Object.values<any>(o).forEach(
33
- (c) => c instanceof SFDataContext && c.destroy(),
34
- );
35
- });
36
- this.context._instances = this.context._instances.filter((i) => i !== this);
37
- }
38
- }
39
-
40
- export interface IRender {
41
- fetchResource?(): Promise<void> | void;
42
- renderToString?(
43
- dataContext: RenderDataContext,
44
- prop: any,
45
- chrild: RenderContext,
46
- ): string | Promise<string>;
47
- mount?(
48
- dom: HTMLElement,
49
- dataContext: RenderDataContext,
50
- prop: any,
51
- chrild: RenderContext,
52
- ): RenderInstance;
53
- hydrate?(
54
- dom: HTMLElement,
55
- dataContext: RenderDataContext,
56
- prop: any,
57
- chrild: RenderContext,
58
- ): RenderInstance;
59
- }
60
- export class RenderContext {
61
- private constructor(
62
- private _systemDataContext: SystemRenderDataContext,
63
- private _render: IRender,
64
- private _prop: any,
65
- // 结尾始终要跟一个空的RenderContext
66
- // 原因:比如一个layout命中了 但是下面的页面没有命中,但是layout切换页面时,layout本身不应该重新渲染,所以需要一开始给它一个占位的空姐点,然后更新这个空节点
67
- private _child?: RenderContext,
68
- ) {}
69
- public static createNoop(systemDataContext: SystemRenderDataContext) {
70
- return new RenderContext(systemDataContext, {}, {});
71
- }
72
- public static create(
73
- systemDataContext: SystemRenderDataContext,
74
- render: IRender,
75
- prop: any,
76
- child: RenderContext = this.createNoop(systemDataContext),
77
- ) {
78
- return new RenderContext(systemDataContext, render, prop, child);
79
- }
80
- get render() {
81
- return this._render;
82
- }
83
- get prop() {
84
- return this._prop;
85
- }
86
- get child() {
87
- return this._child;
88
- }
89
- async warpRenderToString(dataContext: UserRenderDataContext) {
90
- if (this._child) {
91
- return (
92
- (await this.render.renderToString?.(
93
- {
94
- system: forkDataContextRecord(this._systemDataContext),
95
- user: forkDataContextRecord(dataContext),
96
- },
97
- this.prop,
98
- this._child,
99
- )) || ""
100
- );
101
- }
102
- return "";
103
- }
104
- /**
105
- * @internal
106
- */
107
- _instances: WarpRenderInstance[] = [];
108
- mount(dom: HTMLElement, dataContext: UserRenderDataContext) {
109
- let instance: RenderInstance = {};
110
- const fullDataContext = {
111
- system: forkDataContextRecord(this._systemDataContext),
112
- user: forkDataContextRecord(dataContext),
113
- };
114
- if (this._child) {
115
- if (this.render.mount) {
116
- instance = this.render.mount(
117
- dom,
118
- fullDataContext,
119
- this.prop,
120
- this._child,
121
- );
122
- } else if (this.render.renderToString) {
123
- Promise.resolve(
124
- this.render.renderToString(fullDataContext, this.prop, this._child),
125
- ).then((html) => (dom.innerHTML = html));
126
- }
127
- }
128
- return new WarpRenderInstance(this, dom, instance, fullDataContext);
129
- }
130
- hydrate(dom: HTMLElement, dataContext: UserRenderDataContext) {
131
- let instance: RenderInstance = {};
132
- const fullDataContext = {
133
- system: forkDataContextRecord(this._systemDataContext),
134
- user: forkDataContextRecord(dataContext),
135
- };
136
- if (this._child) {
137
- if (this.render.hydrate) {
138
- instance = this.render.hydrate(
139
- dom,
140
- fullDataContext,
141
- this.prop,
142
- this._child,
143
- );
144
- } else if (this.render.renderToString) {
145
- // const vDom = parseHtmlToVDom(
146
- // this.render.renderToString(this.prop, this._child),
147
- // );
148
- const routeDoms = findRouteDom(dom);
149
- routeDoms.forEach((r) => this.child!.hydrate(r, dataContext));
150
- } else if (this.render.mount) {
151
- instance = this.render.mount(
152
- dom,
153
- fullDataContext,
154
- this.prop,
155
- this._child,
156
- );
157
- }
158
- }
159
- return new WarpRenderInstance(this, dom, instance, fullDataContext);
160
- }
161
- unmountAll() {
162
- this._instances.forEach((i) => i.unMount());
163
- }
164
- async updateTree(node: RenderContext, allowRender = true) {
165
- let shoudldRerender = false;
166
- const unRerenderTask: (() => any)[] = [];
167
- if (this._render !== node._render) {
168
- this._render = node._render;
169
- if (allowRender) {
170
- shoudldRerender = true;
171
- }
172
- }
173
- if (this._prop !== node._prop) {
174
- this._prop = node._prop;
175
- if (allowRender) {
176
- if (this._instances.every((i) => i.rawInstance.updateProp)) {
177
- unRerenderTask.push(() => {
178
- this._instances.forEach((i) => {
179
- i.rawInstance.updateProp!(node._prop);
180
- });
181
- });
182
- } else {
183
- shoudldRerender = true;
184
- }
185
- }
186
- }
187
- let childUpdated = false;
188
- let shoudUpdateSubtree = false;
189
- if (this._child !== node._child) {
190
- if (!this._child || !node._child) {
191
- this._child = node._child;
192
- } else {
193
- shoudUpdateSubtree = true;
194
- }
195
- if (this._instances.every((i) => i.rawInstance.updateChild)) {
196
- childUpdated = true;
197
- unRerenderTask.push(() => {
198
- this._instances.forEach((i) => {
199
- if (i.rawInstance.updateChild) {
200
- i.rawInstance.updateChild(node._child!);
201
- }
202
- });
203
- });
204
- }
205
- }
206
- let isRerender = allowRender && shoudldRerender;
207
-
208
- if (shoudUpdateSubtree) {
209
- await this._child?.updateTree(
210
- node._child!,
211
- allowRender && !childUpdated && !isRerender,
212
- );
213
- }
214
- if (isRerender) {
215
- await Promise.all(
216
- this._instances.map(async (i) => {
217
- i.unMount();
218
- await this.mount(i.dom, i.dataContext.user);
219
- }),
220
- );
221
- } else {
222
- await Promise.all(unRerenderTask.map((i) => i()));
223
- }
224
- }
225
- }
1
+ import {
2
+ forkDataContextRecord,
3
+ RenderDataContext,
4
+ SFDataContext,
5
+ SystemRenderDataContext,
6
+ UserRenderDataContext,
7
+ } from "../data-context/index.js";
8
+ import { parseHtmlToVDom } from "../html/parse-html-to-v-dom.js";
9
+ import { findRouteDom } from "../utils/index.js";
10
+
11
+ export interface RenderInstance {
12
+ unMount?(): void;
13
+ updateProp?(newProp: any): void;
14
+ updateChild?(newContext: RenderContext): void;
15
+ }
16
+ export class WarpRenderInstance {
17
+ constructor(
18
+ public readonly context: RenderContext,
19
+ public readonly dom: HTMLElement,
20
+ public readonly rawInstance: RenderInstance,
21
+ public readonly dataContext: RenderDataContext,
22
+ ) {
23
+ this.context._instances.push(this);
24
+ }
25
+ unMount() {
26
+ if (this.rawInstance.unMount) {
27
+ this.rawInstance.unMount();
28
+ } else {
29
+ this.dom.innerHTML = "";
30
+ }
31
+ Object.values(this.dataContext).forEach((o) => {
32
+ Object.values<any>(o).forEach(
33
+ (c) => c instanceof SFDataContext && c.destroy(),
34
+ );
35
+ });
36
+ this.context._instances = this.context._instances.filter((i) => i !== this);
37
+ }
38
+ }
39
+
40
+ export interface IRender {
41
+ fetchResource?(): Promise<void> | void;
42
+ renderToString?(
43
+ dataContext: RenderDataContext,
44
+ prop: any,
45
+ chrild: RenderContext,
46
+ ): string | Promise<string>;
47
+ mount?(
48
+ dom: HTMLElement,
49
+ dataContext: RenderDataContext,
50
+ prop: any,
51
+ chrild: RenderContext,
52
+ ): RenderInstance;
53
+ hydrate?(
54
+ dom: HTMLElement,
55
+ dataContext: RenderDataContext,
56
+ prop: any,
57
+ chrild: RenderContext,
58
+ ): RenderInstance;
59
+ }
60
+ export class RenderContext {
61
+ private constructor(
62
+ private _systemDataContext: SystemRenderDataContext,
63
+ private _render: IRender,
64
+ private _prop: any,
65
+ // 结尾始终要跟一个空的RenderContext
66
+ // 原因:比如一个layout命中了 但是下面的页面没有命中,但是layout切换页面时,layout本身不应该重新渲染,所以需要一开始给它一个占位的空姐点,然后更新这个空节点
67
+ private _child?: RenderContext,
68
+ ) {}
69
+ public static createNoop(systemDataContext: SystemRenderDataContext) {
70
+ return new RenderContext(systemDataContext, {}, {});
71
+ }
72
+ public static create(
73
+ systemDataContext: SystemRenderDataContext,
74
+ render: IRender,
75
+ prop: any,
76
+ child: RenderContext = this.createNoop(systemDataContext),
77
+ ) {
78
+ return new RenderContext(systemDataContext, render, prop, child);
79
+ }
80
+ get render() {
81
+ return this._render;
82
+ }
83
+ get prop() {
84
+ return this._prop;
85
+ }
86
+ get child() {
87
+ return this._child;
88
+ }
89
+ warpRenderToString(dataContext: UserRenderDataContext) {
90
+ if (this._child) {
91
+ const res = this.render.renderToString?.(
92
+ {
93
+ system: forkDataContextRecord(this._systemDataContext),
94
+ user: forkDataContextRecord(dataContext),
95
+ },
96
+ this.prop,
97
+ this._child,
98
+ );
99
+ if (res instanceof Promise) {
100
+ return res.then((res) => res || "");
101
+ }
102
+ return res || "";
103
+ }
104
+ return "";
105
+ }
106
+ /**
107
+ * @internal
108
+ */
109
+ _instances: WarpRenderInstance[] = [];
110
+ mount(dom: HTMLElement, dataContext: UserRenderDataContext) {
111
+ let instance: RenderInstance = {};
112
+ const fullDataContext = {
113
+ system: forkDataContextRecord(this._systemDataContext),
114
+ user: forkDataContextRecord(dataContext),
115
+ };
116
+ if (this._child) {
117
+ if (this.render.mount) {
118
+ instance = this.render.mount(
119
+ dom,
120
+ fullDataContext,
121
+ this.prop,
122
+ this._child,
123
+ );
124
+ } else if (this.render.renderToString) {
125
+ Promise.resolve(
126
+ this.render.renderToString(fullDataContext, this.prop, this._child),
127
+ ).then((html) => (dom.innerHTML = html));
128
+ }
129
+ }
130
+ return new WarpRenderInstance(this, dom, instance, fullDataContext);
131
+ }
132
+ hydrate(dom: HTMLElement, dataContext: UserRenderDataContext) {
133
+ let instance: RenderInstance = {};
134
+ const fullDataContext = {
135
+ system: forkDataContextRecord(this._systemDataContext),
136
+ user: forkDataContextRecord(dataContext),
137
+ };
138
+ if (this._child) {
139
+ if (this.render.hydrate) {
140
+ instance = this.render.hydrate(
141
+ dom,
142
+ fullDataContext,
143
+ this.prop,
144
+ this._child,
145
+ );
146
+ } else if (this.render.renderToString) {
147
+ // const vDom = parseHtmlToVDom(
148
+ // this.render.renderToString(this.prop, this._child),
149
+ // );
150
+ const routeDoms = findRouteDom(dom);
151
+ routeDoms.forEach((r) => this.child!.hydrate(r, dataContext));
152
+ } else if (this.render.mount) {
153
+ instance = this.render.mount(
154
+ dom,
155
+ fullDataContext,
156
+ this.prop,
157
+ this._child,
158
+ );
159
+ }
160
+ }
161
+ return new WarpRenderInstance(this, dom, instance, fullDataContext);
162
+ }
163
+ unmountAll() {
164
+ this._instances.forEach((i) => i.unMount());
165
+ }
166
+ async updateTree(node: RenderContext, allowRender = true) {
167
+ let shoudldRerender = false;
168
+ const unRerenderTask: (() => any)[] = [];
169
+ if (this._render !== node._render) {
170
+ this._render = node._render;
171
+ if (allowRender) {
172
+ shoudldRerender = true;
173
+ }
174
+ }
175
+ if (this._prop !== node._prop) {
176
+ this._prop = node._prop;
177
+ if (allowRender) {
178
+ if (this._instances.every((i) => i.rawInstance.updateProp)) {
179
+ unRerenderTask.push(() => {
180
+ this._instances.forEach((i) => {
181
+ i.rawInstance.updateProp!(node._prop);
182
+ });
183
+ });
184
+ } else {
185
+ shoudldRerender = true;
186
+ }
187
+ }
188
+ }
189
+ let childUpdated = false;
190
+ let shoudUpdateSubtree = false;
191
+ if (this._child !== node._child) {
192
+ if (!this._child || !node._child) {
193
+ this._child = node._child;
194
+ } else {
195
+ shoudUpdateSubtree = true;
196
+ }
197
+ if (this._instances.every((i) => i.rawInstance.updateChild)) {
198
+ childUpdated = true;
199
+ unRerenderTask.push(() => {
200
+ this._instances.forEach((i) => {
201
+ if (i.rawInstance.updateChild) {
202
+ i.rawInstance.updateChild(node._child!);
203
+ }
204
+ });
205
+ });
206
+ }
207
+ }
208
+ let isRerender = allowRender && shoudldRerender;
209
+
210
+ if (shoudUpdateSubtree) {
211
+ await this._child?.updateTree(
212
+ node._child!,
213
+ allowRender && !childUpdated && !isRerender,
214
+ );
215
+ }
216
+ if (isRerender) {
217
+ await Promise.all(
218
+ this._instances.map(async (i) => {
219
+ i.unMount();
220
+ await this.mount(i.dom, i.dataContext.user);
221
+ }),
222
+ );
223
+ } else {
224
+ await Promise.all(unRerenderTask.map((i) => i()));
225
+ }
226
+ }
227
+ }
@@ -1,76 +1,76 @@
1
- import { parseRoute } from "../../../route/index.js";
2
- import { RenderContext } from "../../render/index.js";
3
- import { RenderConfig, ViewMeta } from "../../types.js";
4
- import { findRouteDom } from "../find-route-dom.js";
5
- import {
6
- createRootRenderContext,
7
- ssrToBrowserDataKey,
8
- updateRootRenderContext,
9
- } from "./common.js";
10
-
11
- function listenerRoute(callback: () => void) {
12
- const rawPush = history.pushState;
13
- history.pushState = (data, un, url) => {
14
- rawPush.call(history, data, un, url);
15
- if (url) setTimeout(callback);
16
- };
17
- window.addEventListener("popstate", () => {
18
- setTimeout(callback);
19
- });
20
- }
21
-
22
- function handleMeta(meta: ViewMeta) {
23
- document.title = meta.title;
24
- }
25
-
26
- export const clientRender = async (routes: ReturnType<typeof parseRoute>) => {
27
- const { config, pageData } = (window as any)[ssrToBrowserDataKey];
28
- delete (window as any)[ssrToBrowserDataKey];
29
- const route = routes("GET", location.pathname);
30
- const getData = async () => {
31
- const res = await fetch(location.href);
32
- if (res.headers.get("content-type")?.includes("json")) {
33
- return await res.json();
34
- }
35
- return undefined;
36
- };
37
- const context = await createRootRenderContext(config, route, () => pageData);
38
- handleMeta(context.meta);
39
- listenerRoute(async () => {
40
- const route = routes("GET", location.pathname);
41
- const c = await updateRootRenderContext(context, route, getData);
42
- handleMeta(c.meta);
43
- });
44
- const addedEl = new WeakSet<HTMLAnchorElement>();
45
- function handleAClick(ev: MouseEvent) {
46
- const target = ev.target as HTMLAnchorElement;
47
- if (target.download || target.target || ev.defaultPrevented) return;
48
- const rawHref = target.getAttribute("href");
49
- if (!rawHref || rawHref.startsWith("#")) return;
50
- const route = routes("GET", target.pathname);
51
- if (route.finalRoute) {
52
- history.pushState(null, "", target.href.substring(target.origin.length));
53
- ev.preventDefault();
54
- }
55
- }
56
- window.addEventListener(
57
- "click",
58
- (ev) => {
59
- if (ev.target instanceof HTMLElement) {
60
- let el: HTMLElement | null = ev.target as HTMLElement;
61
- while (el && el !== document.body) {
62
- if (el instanceof HTMLAnchorElement && !addedEl.has(el)) {
63
- addedEl.add(el);
64
- el.addEventListener("click", handleAClick, true);
65
- }
66
- el = el.parentElement;
67
- }
68
- }
69
- },
70
- true,
71
- );
72
- const routesElements = findRouteDom();
73
- routesElements.forEach((el) => {
74
- context.render.hydrate(el as HTMLElement, {});
75
- });
76
- };
1
+ import { parseRoute } from "../../../route/index.js";
2
+ import { RenderContext } from "../../render/index.js";
3
+ import { RenderConfig, ViewMeta } from "../../types.js";
4
+ import { findRouteDom } from "../find-route-dom.js";
5
+ import {
6
+ createRootRenderContext,
7
+ ssrToBrowserDataKey,
8
+ updateRootRenderContext,
9
+ } from "./common.js";
10
+
11
+ function listenerRoute(callback: () => void) {
12
+ const rawPush = history.pushState;
13
+ history.pushState = (data, un, url) => {
14
+ rawPush.call(history, data, un, url);
15
+ if (url) setTimeout(callback);
16
+ };
17
+ window.addEventListener("popstate", () => {
18
+ setTimeout(callback);
19
+ });
20
+ }
21
+
22
+ function handleMeta(meta: ViewMeta) {
23
+ document.title = meta.title;
24
+ }
25
+
26
+ export const clientRender = async (routes: ReturnType<typeof parseRoute>) => {
27
+ const { config, pageData } = (window as any)[ssrToBrowserDataKey];
28
+ delete (window as any)[ssrToBrowserDataKey];
29
+ const route = routes("GET", location.pathname);
30
+ const getData = async () => {
31
+ const res = await fetch(location.href);
32
+ if (res.headers.get("content-type")?.includes("json")) {
33
+ return await res.json();
34
+ }
35
+ return undefined;
36
+ };
37
+ const context = await createRootRenderContext(config, route, () => pageData);
38
+ handleMeta(context.meta);
39
+ listenerRoute(async () => {
40
+ const route = routes("GET", location.pathname);
41
+ const c = await updateRootRenderContext(context, route, getData);
42
+ handleMeta(c.meta);
43
+ });
44
+ const addedEl = new WeakSet<HTMLAnchorElement>();
45
+ function handleAClick(this: HTMLAnchorElement, ev: MouseEvent) {
46
+ const target = this;
47
+ if (target.download || target.target || ev.defaultPrevented) return;
48
+ const rawHref = target.getAttribute("href");
49
+ if (!rawHref || rawHref.startsWith("#")) return;
50
+ const route = routes("GET", target.pathname);
51
+ if (route.finalRoute) {
52
+ history.pushState(null, "", target.href.substring(target.origin.length));
53
+ ev.preventDefault();
54
+ }
55
+ }
56
+ window.addEventListener(
57
+ "click",
58
+ (ev) => {
59
+ if (ev.target instanceof HTMLElement) {
60
+ let el: HTMLElement | null = ev.target as HTMLElement;
61
+ while (el && el !== document.body) {
62
+ if (el instanceof HTMLAnchorElement && !addedEl.has(el)) {
63
+ addedEl.add(el);
64
+ el.addEventListener("click", handleAClick, true);
65
+ }
66
+ el = el.parentElement;
67
+ }
68
+ }
69
+ },
70
+ true,
71
+ );
72
+ const routesElements = findRouteDom();
73
+ routesElements.forEach((el) => {
74
+ context.render.hydrate(el as HTMLElement, {});
75
+ });
76
+ };
package/test/body.test.ts DELETED
@@ -1,29 +0,0 @@
1
- import { expect, test } from "vitest";
2
- import { JSONBody } from "../src/server/body/json.js";
3
- import { Readable } from "stream";
4
-
5
- test("JSONBody", async () => {
6
- const p = JSONBody({ "content-type": "application/json" }, { type: "any" })!;
7
- async function check(name: string, data: any, str?: string) {
8
- const stream = new ReadableStream({
9
- start(controller) {
10
- controller.enqueue(
11
- new TextEncoder().encode(str || JSON.stringify(data)),
12
- );
13
- controller.close();
14
- },
15
- });
16
- expect(await p(stream), name).toEqual(data);
17
- }
18
- await check("string 最基础的字符串", "awa");
19
- await check("string 带有常规转义字符的字符串", "1\r\n3dw\t");
20
- await check("null", null);
21
- await check("true", true);
22
- await check("false", false);
23
- await check("array 空数组", []);
24
- await check("array 有内容数组", ["ewf"]);
25
- await check("object 空对象", {});
26
- await check("object 有内容的对象", { a: "123456" });
27
- await check("整数", 123);
28
- await check("嵌套", [{ a: 1, b: "666", c: [true, null] }]);
29
- });
@@ -1,9 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "rootDir": "./src",
4
- "outDir": "./dist"
5
- },
6
- "extends": "../../tsconfig.json",
7
- "reflection": true,
8
- "include": ["src/**/*"]
9
- }
package/tsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "noEmit": true,
4
- "rootDir": "./"
5
- },
6
- "extends":"./tsconfig.build.json",
7
- "include": ["src/**/*", "tests/**/*"]
8
- }