@lytjs/platform-adapter 6.4.0 → 6.6.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/README.md +676 -0
- package/dist/index.cjs +1 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +53 -53
package/README.md
ADDED
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
# @lytjs/platform-adapter
|
|
2
|
+
|
|
3
|
+
> LytJS 跨平台渲染适配器,提供统一的渲染器抽象层,支持多平台扩展。
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@lytjs/platform-adapter)
|
|
6
|
+
[](https://gitee.com/lytjs/lytjs/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
## 简介
|
|
9
|
+
|
|
10
|
+
`@lytjs/platform-adapter` 是 LytJS 框架的跨平台渲染适配器包,提供了一套统一的渲染器抽象接口,允许开发者为不同平台(Web、移动端、桌面端、服务端等)创建自定义渲染适配器。它实现了适配器注册表模式,支持插件扩展和运行时平台切换。
|
|
11
|
+
|
|
12
|
+
### 核心特性
|
|
13
|
+
|
|
14
|
+
- **统一抽象层**:为不同平台提供统一的渲染器接口
|
|
15
|
+
- **适配器注册表**:集中管理所有平台适配器
|
|
16
|
+
- **插件扩展系统**:支持通过插件扩展适配器功能
|
|
17
|
+
- **运行时切换**:支持在运行时动态切换平台
|
|
18
|
+
- **类型安全**:完整的 TypeScript 类型推导
|
|
19
|
+
- **零外部依赖**:完全基于原生 API 实现
|
|
20
|
+
- **可扩展架构**:易于添加新的平台支持
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @lytjs/platform-adapter
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
或使用 pnpm:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add @lytjs/platform-adapter
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 依赖关系
|
|
35
|
+
|
|
36
|
+
`@lytjs/platform-adapter` 依赖以下 LytJS 核心包:
|
|
37
|
+
|
|
38
|
+
- `@lytjs/vdom` - 虚拟 DOM
|
|
39
|
+
- `@lytjs/common-vnode` - VNode 类型定义
|
|
40
|
+
- `@lytjs/common-is` - 工具函数
|
|
41
|
+
- `@lytjs/common-constants` - 常量定义
|
|
42
|
+
|
|
43
|
+
## 快速开始
|
|
44
|
+
|
|
45
|
+
### 创建渲染器
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
49
|
+
|
|
50
|
+
const renderer = createPlatformRenderer({
|
|
51
|
+
name: 'web',
|
|
52
|
+
platform: 'web',
|
|
53
|
+
createElement(type) {
|
|
54
|
+
return document.createElement(type);
|
|
55
|
+
},
|
|
56
|
+
insert(child, parent, anchor) {
|
|
57
|
+
parent.insertBefore(child, anchor);
|
|
58
|
+
},
|
|
59
|
+
remove(child, parent) {
|
|
60
|
+
parent.removeChild(child);
|
|
61
|
+
},
|
|
62
|
+
setElementText(el, text) {
|
|
63
|
+
el.textContent = text;
|
|
64
|
+
},
|
|
65
|
+
setElementAttribute(el, key, value) {
|
|
66
|
+
el.setAttribute(key, value);
|
|
67
|
+
},
|
|
68
|
+
createTextNode(text) {
|
|
69
|
+
return document.createTextNode(text);
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 注册适配器
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
78
|
+
|
|
79
|
+
adapterRegistry.register('web', webRenderer);
|
|
80
|
+
adapterRegistry.register('ssr', ssrRenderer);
|
|
81
|
+
adapterRegistry.register('wechat', wechatRenderer);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 使用适配器
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
88
|
+
|
|
89
|
+
const renderer = adapterRegistry.get('web');
|
|
90
|
+
if (renderer) {
|
|
91
|
+
renderer.render(vnode, container);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 主要 API
|
|
96
|
+
|
|
97
|
+
### `createPlatformRenderer(config)`
|
|
98
|
+
|
|
99
|
+
创建平台渲染器。
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
103
|
+
|
|
104
|
+
const renderer = createPlatformRenderer({
|
|
105
|
+
name: 'my-platform',
|
|
106
|
+
platform: 'custom',
|
|
107
|
+
features: {
|
|
108
|
+
portals: true,
|
|
109
|
+
suspense: true,
|
|
110
|
+
errorBoundary: true,
|
|
111
|
+
},
|
|
112
|
+
render(vnode, container) {
|
|
113
|
+
// 渲染逻辑
|
|
114
|
+
},
|
|
115
|
+
hydrate(vnode, container) {
|
|
116
|
+
// 水合逻辑
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### `adapterRegistry`
|
|
122
|
+
|
|
123
|
+
适配器注册表,提供适配器的注册、获取和枚举功能。
|
|
124
|
+
|
|
125
|
+
#### `register(platform, adapter)`
|
|
126
|
+
|
|
127
|
+
注册平台适配器。
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
131
|
+
|
|
132
|
+
adapterRegistry.register('web', webRenderer);
|
|
133
|
+
adapterRegistry.register('miniapp', miniappRenderer);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### `get(platform)`
|
|
137
|
+
|
|
138
|
+
获取指定平台的适配器。
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
142
|
+
|
|
143
|
+
const renderer = adapterRegistry.get('web');
|
|
144
|
+
if (renderer) {
|
|
145
|
+
renderer.render(vnode, container);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### `has(platform)`
|
|
150
|
+
|
|
151
|
+
检查平台是否已注册。
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
155
|
+
|
|
156
|
+
if (adapterRegistry.has('web')) {
|
|
157
|
+
console.log('Web 平台已注册');
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### `unregister(platform)`
|
|
162
|
+
|
|
163
|
+
注销平台适配器。
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
167
|
+
|
|
168
|
+
adapterRegistry.unregister('web');
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### `list()`
|
|
172
|
+
|
|
173
|
+
列出所有已注册的适配器。
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
177
|
+
|
|
178
|
+
const platforms = adapterRegistry.list();
|
|
179
|
+
console.log('已注册的适配器:', platforms);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### `getDefault()`
|
|
183
|
+
|
|
184
|
+
获取默认平台适配器。
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
188
|
+
|
|
189
|
+
const defaultRenderer = adapterRegistry.getDefault();
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### `setDefault(platform)`
|
|
193
|
+
|
|
194
|
+
设置默认平台适配器。
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
198
|
+
|
|
199
|
+
adapterRegistry.setDefault('web');
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### `clear()`
|
|
203
|
+
|
|
204
|
+
清除所有注册的适配器。
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
208
|
+
|
|
209
|
+
adapterRegistry.clear();
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## 类型定义
|
|
213
|
+
|
|
214
|
+
### PlatformAdapter
|
|
215
|
+
|
|
216
|
+
平台适配器接口。
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
interface PlatformAdapter {
|
|
220
|
+
name: string;
|
|
221
|
+
platform: string;
|
|
222
|
+
features?: PlatformFeatures;
|
|
223
|
+
createElement(type: string, props?: VNodeProps): VNode;
|
|
224
|
+
insert(child: VNode, parent: VNode, anchor?: VNode): void;
|
|
225
|
+
remove(child: VNode, parent: VNode): void;
|
|
226
|
+
setElementText(el: VNode, text: string): void;
|
|
227
|
+
setElementAttribute(el: VNode, key: string, value: any): void;
|
|
228
|
+
removeElementAttribute(el: VNode, key: string): void;
|
|
229
|
+
createTextNode(text: string): VNode;
|
|
230
|
+
render(vnode: VNode, container: VNode): void;
|
|
231
|
+
hydrate?(vnode: VNode, container: VNode): void;
|
|
232
|
+
unmount?(vnode: VNode): void;
|
|
233
|
+
createPortal?(vnode: VNode, container: VNode): void;
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### PlatformConfig
|
|
238
|
+
|
|
239
|
+
平台配置选项。
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
interface PlatformConfig {
|
|
243
|
+
name: string;
|
|
244
|
+
platform: string;
|
|
245
|
+
features?: PlatformFeatures;
|
|
246
|
+
render: (vnode: VNode, container: VNode) => void;
|
|
247
|
+
hydrate?: (vnode: VNode, container: VNode) => void;
|
|
248
|
+
unmount?: (vnode: VNode) => void;
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### PlatformPlugin
|
|
253
|
+
|
|
254
|
+
平台插件接口。
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
interface PlatformPlugin {
|
|
258
|
+
name: string;
|
|
259
|
+
version: string;
|
|
260
|
+
install(platform: PlatformAdapter, options?: any): void;
|
|
261
|
+
uninstall?(platform: PlatformAdapter): void;
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### PlatformFeatures
|
|
266
|
+
|
|
267
|
+
平台特性支持。
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
interface PlatformFeatures {
|
|
271
|
+
portals?: boolean;
|
|
272
|
+
suspense?: boolean;
|
|
273
|
+
errorBoundary?: boolean;
|
|
274
|
+
transition?: boolean;
|
|
275
|
+
teleport?: boolean;
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## 使用示例
|
|
280
|
+
|
|
281
|
+
### Web 平台适配器
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
import { createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
285
|
+
|
|
286
|
+
const webRenderer = createPlatformRenderer({
|
|
287
|
+
name: 'web',
|
|
288
|
+
platform: 'web',
|
|
289
|
+
features: {
|
|
290
|
+
portals: true,
|
|
291
|
+
suspense: true,
|
|
292
|
+
errorBoundary: true,
|
|
293
|
+
transition: true,
|
|
294
|
+
teleport: true,
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
createElement(type, props) {
|
|
298
|
+
const el = document.createElement(type);
|
|
299
|
+
if (props) {
|
|
300
|
+
Object.entries(props).forEach(([key, value]) => {
|
|
301
|
+
if (key.startsWith('on')) {
|
|
302
|
+
el.addEventListener(key.slice(2).toLowerCase(), value);
|
|
303
|
+
} else if (key === 'className') {
|
|
304
|
+
el.className = value;
|
|
305
|
+
} else {
|
|
306
|
+
el.setAttribute(key, value);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
return el;
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
insert(child, parent, anchor) {
|
|
314
|
+
parent.insertBefore(child, anchor || null);
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
remove(child, parent) {
|
|
318
|
+
parent.removeChild(child);
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
setElementText(el, text) {
|
|
322
|
+
el.textContent = text;
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
setElementAttribute(el, key, value) {
|
|
326
|
+
if (value === null || value === undefined) {
|
|
327
|
+
el.removeAttribute(key);
|
|
328
|
+
} else {
|
|
329
|
+
el.setAttribute(key, String(value));
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
removeElementAttribute(el, key) {
|
|
334
|
+
el.removeAttribute(key);
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
createTextNode(text) {
|
|
338
|
+
return document.createTextNode(text);
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
render(vnode, container) {
|
|
342
|
+
const el = this.createElement(vnode.type, vnode.props);
|
|
343
|
+
// 渲染逻辑
|
|
344
|
+
container.appendChild(el);
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
export default webRenderer;
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### 服务端渲染适配器
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
import { createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
355
|
+
|
|
356
|
+
const ssrRenderer = createPlatformRenderer({
|
|
357
|
+
name: 'ssr',
|
|
358
|
+
platform: 'server',
|
|
359
|
+
features: {
|
|
360
|
+
portals: true,
|
|
361
|
+
suspense: true,
|
|
362
|
+
errorBoundary: true,
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
createElement(type, props) {
|
|
366
|
+
return {
|
|
367
|
+
type,
|
|
368
|
+
props: props || {},
|
|
369
|
+
children: [],
|
|
370
|
+
text: '',
|
|
371
|
+
};
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
insert(child, parent) {
|
|
375
|
+
child.parent = parent;
|
|
376
|
+
parent.children = parent.children || [];
|
|
377
|
+
parent.children.push(child);
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
setElementText(el, text) {
|
|
381
|
+
el.text = text;
|
|
382
|
+
},
|
|
383
|
+
|
|
384
|
+
render(vnode, container) {
|
|
385
|
+
const html = this.stringify(vnode);
|
|
386
|
+
return html;
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
stringify(node) {
|
|
390
|
+
if (node.text) {
|
|
391
|
+
return node.text;
|
|
392
|
+
}
|
|
393
|
+
const children = (node.children || []).map((child) => this.stringify(child)).join('');
|
|
394
|
+
return `<${node.type}>${children}</${node.type}>`;
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
export default ssrRenderer;
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### 微信小程序适配器
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
import { createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
405
|
+
|
|
406
|
+
const miniappRenderer = createPlatformRenderer({
|
|
407
|
+
name: 'wechat-miniapp',
|
|
408
|
+
platform: 'miniapp',
|
|
409
|
+
features: {
|
|
410
|
+
portals: false,
|
|
411
|
+
suspense: false,
|
|
412
|
+
errorBoundary: false,
|
|
413
|
+
},
|
|
414
|
+
|
|
415
|
+
createElement(type, props) {
|
|
416
|
+
return {
|
|
417
|
+
type,
|
|
418
|
+
props: props || {},
|
|
419
|
+
children: [],
|
|
420
|
+
};
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
render(vnode, container) {
|
|
424
|
+
const data = this.flatten(vnode);
|
|
425
|
+
container.setData({ elements: data });
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
flatten(node, depth = 0) {
|
|
429
|
+
return {
|
|
430
|
+
tag: node.type,
|
|
431
|
+
attrs: node.props,
|
|
432
|
+
children: (node.children || []).map((child) => this.flatten(child, depth + 1)),
|
|
433
|
+
};
|
|
434
|
+
},
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
export default miniappRenderer;
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### 使用插件扩展
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
import { adapterRegistry, createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
444
|
+
|
|
445
|
+
const loggerPlugin = {
|
|
446
|
+
name: 'logger-plugin',
|
|
447
|
+
version: '1.0.0',
|
|
448
|
+
install(platform) {
|
|
449
|
+
const originalRender = platform.render;
|
|
450
|
+
platform.render = (vnode, container) => {
|
|
451
|
+
console.log(`[${platform.name}] 开始渲染`);
|
|
452
|
+
const start = performance.now();
|
|
453
|
+
originalRender.call(platform, vnode, container);
|
|
454
|
+
console.log(`[${platform.name}] 渲染完成,耗时: ${performance.now() - start}ms`);
|
|
455
|
+
};
|
|
456
|
+
},
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const webRenderer = createPlatformRenderer({
|
|
460
|
+
/* ... */
|
|
461
|
+
});
|
|
462
|
+
webRenderer.use?.(loggerPlugin);
|
|
463
|
+
|
|
464
|
+
adapterRegistry.register('web', webRenderer);
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### 运行时平台切换
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
471
|
+
|
|
472
|
+
function renderApp(vnode) {
|
|
473
|
+
const platform = getPlatformFromUA();
|
|
474
|
+
const renderer = adapterRegistry.get(platform);
|
|
475
|
+
|
|
476
|
+
if (!renderer) {
|
|
477
|
+
console.warn(`平台 ${platform} 未注册,使用默认渲染器`);
|
|
478
|
+
return adapterRegistry.getDefault()?.render(vnode, container);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return renderer.render(vnode, container);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function getPlatformFromUA() {
|
|
485
|
+
const ua = navigator.userAgent;
|
|
486
|
+
if (ua.includes('MiniProgram')) return 'wechat';
|
|
487
|
+
return 'web';
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### 动态加载适配器
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
import { adapterRegistry } from '@lytjs/platform-adapter';
|
|
495
|
+
|
|
496
|
+
async function loadPlatformAdapter(platform) {
|
|
497
|
+
const modules = {
|
|
498
|
+
web: () => import('./adapters/web'),
|
|
499
|
+
ssr: () => import('./adapters/ssr'),
|
|
500
|
+
miniapp: () => import('./adapters/miniapp'),
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
if (modules[platform]) {
|
|
504
|
+
const { default: adapter } = await modules[platform]();
|
|
505
|
+
adapterRegistry.register(platform, adapter);
|
|
506
|
+
return adapter;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
throw new Error(`未知的平台: ${platform}`);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
await loadPlatformAdapter('web');
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
## 高级用法
|
|
516
|
+
|
|
517
|
+
### 自定义渲染流水线
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
import { createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
521
|
+
|
|
522
|
+
const pipelineRenderer = createPlatformRenderer({
|
|
523
|
+
name: 'pipeline',
|
|
524
|
+
platform: 'custom',
|
|
525
|
+
features: {
|
|
526
|
+
portals: true,
|
|
527
|
+
suspense: true,
|
|
528
|
+
errorBoundary: true,
|
|
529
|
+
},
|
|
530
|
+
|
|
531
|
+
render(vnode, container) {
|
|
532
|
+
const pipeline = [this.optimize, this.transform, this.render];
|
|
533
|
+
|
|
534
|
+
let current = vnode;
|
|
535
|
+
for (const step of pipeline) {
|
|
536
|
+
current = step.call(this, current);
|
|
537
|
+
}
|
|
538
|
+
return current;
|
|
539
|
+
},
|
|
540
|
+
|
|
541
|
+
optimize(vnode) {
|
|
542
|
+
// 优化阶段
|
|
543
|
+
return vnode;
|
|
544
|
+
},
|
|
545
|
+
|
|
546
|
+
transform(vnode) {
|
|
547
|
+
// 转换阶段
|
|
548
|
+
return vnode;
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### 性能监控适配器
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
import { createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
557
|
+
|
|
558
|
+
function createMonitoredRenderer(config) {
|
|
559
|
+
const renderer = createPlatformRenderer(config);
|
|
560
|
+
|
|
561
|
+
const metrics = {
|
|
562
|
+
renderCount: 0,
|
|
563
|
+
totalTime: 0,
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
const originalRender = renderer.render;
|
|
567
|
+
renderer.render = function (vnode, container) {
|
|
568
|
+
const start = performance.now();
|
|
569
|
+
originalRender.call(this, vnode, container);
|
|
570
|
+
metrics.renderCount++;
|
|
571
|
+
metrics.totalTime += performance.now() - start;
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
renderer.getMetrics = () => ({ ...metrics });
|
|
575
|
+
|
|
576
|
+
return renderer;
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### 调试适配器
|
|
581
|
+
|
|
582
|
+
```typescript
|
|
583
|
+
import { createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
584
|
+
|
|
585
|
+
const debugRenderer = createPlatformRenderer({
|
|
586
|
+
name: 'debug',
|
|
587
|
+
platform: 'web',
|
|
588
|
+
// ... 基础配置
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
debugRenderer.debug = {
|
|
592
|
+
logVNode(vnode) {
|
|
593
|
+
console.log('VNode:', JSON.stringify(vnode, null, 2));
|
|
594
|
+
},
|
|
595
|
+
logTree(vnode, depth = 0) {
|
|
596
|
+
const indent = ' '.repeat(depth);
|
|
597
|
+
console.log(`${indent}${vnode.type}`);
|
|
598
|
+
(vnode.children || []).forEach((child) => this.logTree(child, depth + 1));
|
|
599
|
+
},
|
|
600
|
+
logOperations(operations) {
|
|
601
|
+
console.table(operations);
|
|
602
|
+
},
|
|
603
|
+
};
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
## 最佳实践
|
|
607
|
+
|
|
608
|
+
### 适配器命名规范
|
|
609
|
+
|
|
610
|
+
```typescript
|
|
611
|
+
// ✅ 推荐:使用平台标识符
|
|
612
|
+
adapterRegistry.register('web', webRenderer);
|
|
613
|
+
adapterRegistry.register('ssr', ssrRenderer);
|
|
614
|
+
adapterRegistry.register('wechat-miniapp', wechatRenderer);
|
|
615
|
+
adapterRegistry.register('alipay-miniapp', alipayRenderer);
|
|
616
|
+
|
|
617
|
+
// ❌ 避免:使用模糊名称
|
|
618
|
+
adapterRegistry.register('main', webRenderer);
|
|
619
|
+
adapterRegistry.register('alt', ssrRenderer);
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### 特性检测
|
|
623
|
+
|
|
624
|
+
```typescript
|
|
625
|
+
import { createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
626
|
+
|
|
627
|
+
const renderer = createPlatformRenderer({
|
|
628
|
+
name: 'adaptive',
|
|
629
|
+
platform: 'web',
|
|
630
|
+
|
|
631
|
+
features: {
|
|
632
|
+
portals: typeof document.createElement === 'function',
|
|
633
|
+
suspense: 'requestIdleCallback' in window,
|
|
634
|
+
errorBoundary: true,
|
|
635
|
+
},
|
|
636
|
+
});
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### 错误处理
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
import { createPlatformRenderer } from '@lytjs/platform-adapter';
|
|
643
|
+
|
|
644
|
+
const safeRenderer = createPlatformRenderer({
|
|
645
|
+
name: 'safe',
|
|
646
|
+
platform: 'web',
|
|
647
|
+
|
|
648
|
+
render(vnode, container) {
|
|
649
|
+
try {
|
|
650
|
+
// 渲染逻辑
|
|
651
|
+
} catch (error) {
|
|
652
|
+
console.error('渲染错误:', error);
|
|
653
|
+
this.handleError?.(error, vnode);
|
|
654
|
+
}
|
|
655
|
+
},
|
|
656
|
+
|
|
657
|
+
handleError(error, vnode) {
|
|
658
|
+
// 显示错误边界
|
|
659
|
+
},
|
|
660
|
+
});
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
## 浏览器兼容性
|
|
664
|
+
|
|
665
|
+
`@lytjs/platform-adapter` 支持所有现代浏览器。不同平台的适配器可能有特定的浏览器要求。
|
|
666
|
+
|
|
667
|
+
## 许可证
|
|
668
|
+
|
|
669
|
+
MIT License - [查看许可证](https://gitee.com/lytjs/lytjs/blob/main/LICENSE)
|
|
670
|
+
|
|
671
|
+
## 贡献指南
|
|
672
|
+
|
|
673
|
+
欢迎提交 Issue 和 Pull Request!
|
|
674
|
+
|
|
675
|
+
- [Gitee 仓库](https://gitee.com/lytjs/lytjs)
|
|
676
|
+
- [问题反馈](https://gitee.com/lytjs/lytjs/issues)
|
package/dist/index.cjs
CHANGED
|
@@ -22,9 +22,7 @@ var AdapterRegistry = class {
|
|
|
22
22
|
*/
|
|
23
23
|
register(adapter) {
|
|
24
24
|
if (!commonIs.isString(adapter.name) || adapter.name.length === 0) {
|
|
25
|
-
throw new Error(
|
|
26
|
-
"[platform-adapter] \u9002\u914D\u5668\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A"
|
|
27
|
-
);
|
|
25
|
+
throw new Error("[platform-adapter] \u9002\u914D\u5668\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
|
|
28
26
|
}
|
|
29
27
|
this.adapters.set(adapter.name, adapter);
|
|
30
28
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapter-registry.ts","../src/create-renderer.ts"],"names":["isString","isFunction"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * 平台适配器注册表\r\n *\r\n * @description\r\n * 管理所有已注册的平台适配器和插件,提供统一的注册、查询和插件管理能力。\r\n * 采用单例模式,全局共享同一个注册表实例。\r\n */\r\n\r\nimport { isString } from '@lytjs/common-is';\r\nimport type { PlatformAdapter, PlatformPlugin } from './types';\r\n\r\n/**\r\n * 适配器注册表类\r\n *\r\n * @description\r\n * 维护平台适配器的注册信息,支持适配器的注册、注销、查询,\r\n * 以及平台级插件的管理。\r\n */\r\nclass AdapterRegistry {\r\n /** 已注册的适配器映射(平台名称 -> 适配器实例) */\r\n private adapters = new Map<string, PlatformAdapter>();\r\n /** 平台插件映射(平台名称 -> 插件数组) */\r\n private plugins = new Map<string, PlatformPlugin[]>();\r\n\r\n /**\r\n * 注册平台适配器\r\n *\r\n * @description\r\n * 将适配器实例注册到注册表中,以适配器的 name 为键。\r\n * 如果同名适配器已存在,将被覆盖。\r\n *\r\n * @param adapter - 平台适配器实例\r\n * @throws {Error} 当 adapter.name 为空时抛出错误\r\n */\r\n register(adapter: PlatformAdapter): void {\r\n if (!isString(adapter.name) || adapter.name.length === 0) {\r\n throw new Error(\r\n '[platform-adapter] 适配器名称不能为空',\r\n );\r\n }\r\n this.adapters.set(adapter.name, adapter);\r\n }\r\n\r\n /**\r\n * 注销平台适配器\r\n *\r\n * @description\r\n * 从注册表中移除指定名称的适配器,同时清除该平台关联的所有插件。\r\n *\r\n * @param name - 平台名称\r\n * @returns 是否成功注销(名称不存在时返回 false)\r\n */\r\n unregister(name: string): boolean {\r\n const removed = this.adapters.delete(name);\r\n if (removed) {\r\n this.plugins.delete(name);\r\n }\r\n return removed;\r\n }\r\n\r\n /**\r\n * 获取指定名称的平台适配器\r\n *\r\n * @param name - 平台名称\r\n * @returns 适配器实例,未找到时返回 undefined\r\n */\r\n get(name: string): PlatformAdapter | undefined {\r\n return this.adapters.get(name);\r\n }\r\n\r\n /**\r\n * 检查指定名称的适配器是否已注册\r\n *\r\n * @param name - 平台名称\r\n * @returns 是否已注册\r\n */\r\n has(name: string): boolean {\r\n return this.adapters.has(name);\r\n }\r\n\r\n /**\r\n * 获取所有已注册的适配器名称\r\n *\r\n * @returns 平台名称数组\r\n */\r\n getNames(): string[] {\r\n return Array.from(this.adapters.keys());\r\n }\r\n\r\n /**\r\n * 为指定平台添加插件\r\n *\r\n * @description\r\n * 将插件添加到目标平台的插件列表中。\r\n * 如果目标平台不存在,会自动创建空的插件列表。\r\n *\r\n * @param platformName - 目标平台名称\r\n * @param plugin - 平台插件实例\r\n */\r\n addPlugin(platformName: string, plugin: PlatformPlugin): void {\r\n const existing = this.plugins.get(platformName);\r\n if (existing) {\r\n existing.push(plugin);\r\n } else {\r\n this.plugins.set(platformName, [plugin]);\r\n }\r\n }\r\n\r\n /**\r\n * 从指定平台移除插件\r\n *\r\n * @param platformName - 目标平台名称\r\n * @param pluginName - 要移除的插件名称\r\n * @returns 是否成功移除(插件不存在时返回 false)\r\n */\r\n removePlugin(platformName: string, pluginName: string): boolean {\r\n const list = this.plugins.get(platformName);\r\n if (!list) return false;\r\n\r\n const index = list.findIndex((p) => p.name === pluginName);\r\n if (index === -1) return false;\r\n\r\n list.splice(index, 1);\r\n return true;\r\n }\r\n\r\n /**\r\n * 获取指定平台的所有插件\r\n *\r\n * @param platformName - 目标平台名称\r\n * @returns 插件数组,平台无插件时返回空数组\r\n */\r\n getPlugins(platformName: string): PlatformPlugin[] {\r\n return this.plugins.get(platformName) ?? [];\r\n }\r\n}\r\n\r\n/** 全局适配器注册表单例 */\r\nexport const adapterRegistry = new AdapterRegistry();\r\n","/**\r\n * 跨平台渲染器工厂\r\n *\r\n * @description\r\n * 基于平台适配器创建渲染器实例,提供统一的 render/unmount API,\r\n * 屏蔽底层平台差异。\r\n */\r\n\r\nimport { isFunction, isString } from '@lytjs/common-is';\r\nimport type { VNode } from '@lytjs/common-vnode';\r\nimport type { PlatformAdapter, PlatformConfig } from './types';\r\n\r\n/**\r\n * 平台渲染器接口\r\n *\r\n * @description\r\n * 封装了平台适配器,提供高层的渲染和卸载 API。\r\n *\r\n * @template HN - 宿主节点类型\r\n * @template HE - 宿主元素类型(extends HN)\r\n */\r\nexport interface PlatformRenderer<HN, HE extends HN> {\r\n /**\r\n * 将 VNode 渲染到指定容器中\r\n *\r\n * @param vnode - 要渲染的虚拟节点\r\n * @param container - 宿主容器节点\r\n */\r\n render(vnode: VNode, container: HN): void;\r\n\r\n /**\r\n * 卸载指定容器中的内容\r\n *\r\n * @param container - 宿主容器节点\r\n */\r\n unmount(container: HN): void;\r\n\r\n /**\r\n * 获取关联的平台适配器\r\n *\r\n * @returns 平台适配器实例\r\n */\r\n getAdapter(): PlatformAdapter<HN, HE>;\r\n}\r\n\r\n/**\r\n * 创建平台渲染器\r\n *\r\n * @description\r\n * 工厂函数,基于给定的平台适配器和配置创建渲染器实例。\r\n * 渲染器内部委托适配器完成实际的 DOM 操作。\r\n *\r\n * @template HN - 宿主节点类型\r\n * @template HE - 宿主元素类型(extends HN)\r\n * @param adapter - 平台适配器实例\r\n * @param config - 平台配置(可选)\r\n * @returns 平台渲染器实例\r\n *\r\n * @example\r\n * ```typescript\r\n * const renderer = createPlatformRenderer(myAdapter, { debug: true });\r\n * renderer.render(vnode, container);\r\n * ```\r\n */\r\nexport function createPlatformRenderer<HN, HE extends HN>(\r\n adapter: PlatformAdapter<HN, HE>,\r\n config?: PlatformConfig,\r\n): PlatformRenderer<HN, HE> {\r\n // 当前容器中已渲染的子节点引用\r\n let currentChild: HN | null = null;\r\n\r\n const renderer: PlatformRenderer<HN, HE> = {\r\n /**\r\n * 将 VNode 渲染到容器中\r\n *\r\n * @description\r\n * 根据 vnode 类型创建对应的宿主节点,并插入到容器中。\r\n * 如果容器中已有内容,会先卸载再重新渲染。\r\n */\r\n render(vnode: VNode, container: HN): void {\r\n // 先卸载已有内容\r\n if (currentChild !== null) {\r\n this.unmount(container);\r\n }\r\n\r\n const node = createHostNodeFromVNode(adapter, vnode);\r\n if (node !== null) {\r\n adapter.insert(node, container, null);\r\n currentChild = node;\r\n }\r\n\r\n if (config?.debug) {\r\n // eslint-disable-next-line no-console\r\n console.log(`[platform-adapter:${adapter.name}] 渲染完成`, vnode);\r\n }\r\n },\r\n\r\n /**\r\n * 卸载容器中的内容\r\n */\r\n unmount(_container: HN): void {\r\n if (currentChild !== null) {\r\n adapter.remove(currentChild);\r\n currentChild = null;\r\n }\r\n\r\n if (config?.debug) {\r\n // eslint-disable-next-line no-console\r\n console.log(`[platform-adapter:${adapter.name}] 卸载完成`);\r\n }\r\n },\r\n\r\n /**\r\n * 获取关联的平台适配器\r\n */\r\n getAdapter(): PlatformAdapter<HN, HE> {\r\n return adapter;\r\n },\r\n };\r\n\r\n return renderer;\r\n}\r\n\r\n/**\r\n * 根据 VNode 类型创建宿主节点\r\n *\r\n * @description\r\n * 将 VNode 映射为平台适配器可操作的宿主节点。\r\n * 支持元素节点、文本节点和注释节点。\r\n *\r\n * @param adapter - 平台适配器\r\n * @param vnode - 虚拟节点\r\n * @returns 宿主节点,无法识别的类型返回 null\r\n */\r\nfunction createHostNodeFromVNode<HN, HE extends HN>(\r\n adapter: PlatformAdapter<HN, HE>,\r\n vnode: VNode,\r\n): HN | null {\r\n const { type, children } = vnode;\r\n\r\n // 元素节点\r\n if (isString(type)) {\r\n const el = adapter.createElement(type);\r\n\r\n // 设置属性\r\n if (vnode.props) {\r\n for (const [key, value] of Object.entries(vnode.props)) {\r\n if (key === 'style' && isString(value)) {\r\n adapter.setStyle(el, value);\r\n } else if (key === 'class' && isString(value)) {\r\n adapter.addClass(el, value);\r\n } else if (isString(value)) {\r\n adapter.setAttribute(el, key, value);\r\n }\r\n }\r\n }\r\n\r\n // 递归处理子节点\r\n if (children && isString(children)) {\r\n adapter.setElementText(el, children);\r\n }\r\n\r\n return el as unknown as HN;\r\n }\r\n\r\n // 文本节点\r\n if (isString(children)) {\r\n return adapter.createText(children);\r\n }\r\n\r\n // 注释节点\r\n if (isFunction(type) === false && type !== null && !isString(type)) {\r\n // 非 string、非 function、非 null 的 type 可能是 Comment Symbol\r\n return adapter.createComment(String(children ?? ''));\r\n }\r\n\r\n return null;\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapter-registry.ts","../src/create-renderer.ts"],"names":["isString","isFunction"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * 平台适配器注册表\n *\n * @description\n * 管理所有已注册的平台适配器和插件,提供统一的注册、查询和插件管理能力。\n * 采用单例模式,全局共享同一个注册表实例。\n */\n\nimport { isString } from '@lytjs/common-is';\nimport type { PlatformAdapter, PlatformPlugin } from './types';\n\n/**\n * 适配器注册表类\n *\n * @description\n * 维护平台适配器的注册信息,支持适配器的注册、注销、查询,\n * 以及平台级插件的管理。\n */\nclass AdapterRegistry {\n /** 已注册的适配器映射(平台名称 -> 适配器实例) */\n private adapters = new Map<string, PlatformAdapter>();\n /** 平台插件映射(平台名称 -> 插件数组) */\n private plugins = new Map<string, PlatformPlugin[]>();\n\n /**\n * 注册平台适配器\n *\n * @description\n * 将适配器实例注册到注册表中,以适配器的 name 为键。\n * 如果同名适配器已存在,将被覆盖。\n *\n * @param adapter - 平台适配器实例\n * @throws {Error} 当 adapter.name 为空时抛出错误\n */\n register(adapter: PlatformAdapter): void {\n if (!isString(adapter.name) || adapter.name.length === 0) {\n throw new Error('[platform-adapter] 适配器名称不能为空');\n }\n this.adapters.set(adapter.name, adapter);\n }\n\n /**\n * 注销平台适配器\n *\n * @description\n * 从注册表中移除指定名称的适配器,同时清除该平台关联的所有插件。\n *\n * @param name - 平台名称\n * @returns 是否成功注销(名称不存在时返回 false)\n */\n unregister(name: string): boolean {\n const removed = this.adapters.delete(name);\n if (removed) {\n this.plugins.delete(name);\n }\n return removed;\n }\n\n /**\n * 获取指定名称的平台适配器\n *\n * @param name - 平台名称\n * @returns 适配器实例,未找到时返回 undefined\n */\n get(name: string): PlatformAdapter | undefined {\n return this.adapters.get(name);\n }\n\n /**\n * 检查指定名称的适配器是否已注册\n *\n * @param name - 平台名称\n * @returns 是否已注册\n */\n has(name: string): boolean {\n return this.adapters.has(name);\n }\n\n /**\n * 获取所有已注册的适配器名称\n *\n * @returns 平台名称数组\n */\n getNames(): string[] {\n return Array.from(this.adapters.keys());\n }\n\n /**\n * 为指定平台添加插件\n *\n * @description\n * 将插件添加到目标平台的插件列表中。\n * 如果目标平台不存在,会自动创建空的插件列表。\n *\n * @param platformName - 目标平台名称\n * @param plugin - 平台插件实例\n */\n addPlugin(platformName: string, plugin: PlatformPlugin): void {\n const existing = this.plugins.get(platformName);\n if (existing) {\n existing.push(plugin);\n } else {\n this.plugins.set(platformName, [plugin]);\n }\n }\n\n /**\n * 从指定平台移除插件\n *\n * @param platformName - 目标平台名称\n * @param pluginName - 要移除的插件名称\n * @returns 是否成功移除(插件不存在时返回 false)\n */\n removePlugin(platformName: string, pluginName: string): boolean {\n const list = this.plugins.get(platformName);\n if (!list) return false;\n\n const index = list.findIndex((p) => p.name === pluginName);\n if (index === -1) return false;\n\n list.splice(index, 1);\n return true;\n }\n\n /**\n * 获取指定平台的所有插件\n *\n * @param platformName - 目标平台名称\n * @returns 插件数组,平台无插件时返回空数组\n */\n getPlugins(platformName: string): PlatformPlugin[] {\n return this.plugins.get(platformName) ?? [];\n }\n}\n\n/** 全局适配器注册表单例 */\nexport const adapterRegistry = new AdapterRegistry();\n","/**\n * 跨平台渲染器工厂\n *\n * @description\n * 基于平台适配器创建渲染器实例,提供统一的 render/unmount API,\n * 屏蔽底层平台差异。\n */\n\nimport { isFunction, isString } from '@lytjs/common-is';\nimport type { VNode } from '@lytjs/common-vnode';\nimport type { PlatformAdapter, PlatformConfig } from './types';\n\n/**\n * 平台渲染器接口\n *\n * @description\n * 封装了平台适配器,提供高层的渲染和卸载 API。\n *\n * @template HN - 宿主节点类型\n * @template HE - 宿主元素类型(extends HN)\n */\nexport interface PlatformRenderer<HN, HE extends HN> {\n /**\n * 将 VNode 渲染到指定容器中\n *\n * @param vnode - 要渲染的虚拟节点\n * @param container - 宿主容器节点\n */\n render(vnode: VNode, container: HN): void;\n\n /**\n * 卸载指定容器中的内容\n *\n * @param container - 宿主容器节点\n */\n unmount(container: HN): void;\n\n /**\n * 获取关联的平台适配器\n *\n * @returns 平台适配器实例\n */\n getAdapter(): PlatformAdapter<HN, HE>;\n}\n\n/**\n * 创建平台渲染器\n *\n * @description\n * 工厂函数,基于给定的平台适配器和配置创建渲染器实例。\n * 渲染器内部委托适配器完成实际的 DOM 操作。\n *\n * @template HN - 宿主节点类型\n * @template HE - 宿主元素类型(extends HN)\n * @param adapter - 平台适配器实例\n * @param config - 平台配置(可选)\n * @returns 平台渲染器实例\n *\n * @example\n * ```typescript\n * const renderer = createPlatformRenderer(myAdapter, { debug: true });\n * renderer.render(vnode, container);\n * ```\n */\nexport function createPlatformRenderer<HN, HE extends HN>(\n adapter: PlatformAdapter<HN, HE>,\n config?: PlatformConfig,\n): PlatformRenderer<HN, HE> {\n // 当前容器中已渲染的子节点引用\n let currentChild: HN | null = null;\n\n const renderer: PlatformRenderer<HN, HE> = {\n /**\n * 将 VNode 渲染到容器中\n *\n * @description\n * 根据 vnode 类型创建对应的宿主节点,并插入到容器中。\n * 如果容器中已有内容,会先卸载再重新渲染。\n */\n render(vnode: VNode, container: HN): void {\n // 先卸载已有内容\n if (currentChild !== null) {\n this.unmount(container);\n }\n\n const node = createHostNodeFromVNode(adapter, vnode);\n if (node !== null) {\n adapter.insert(node, container, null);\n currentChild = node;\n }\n\n if (config?.debug) {\n // eslint-disable-next-line no-console\n console.log(`[platform-adapter:${adapter.name}] 渲染完成`, vnode);\n }\n },\n\n /**\n * 卸载容器中的内容\n */\n unmount(_container: HN): void {\n if (currentChild !== null) {\n adapter.remove(currentChild);\n currentChild = null;\n }\n\n if (config?.debug) {\n // eslint-disable-next-line no-console\n console.log(`[platform-adapter:${adapter.name}] 卸载完成`);\n }\n },\n\n /**\n * 获取关联的平台适配器\n */\n getAdapter(): PlatformAdapter<HN, HE> {\n return adapter;\n },\n };\n\n return renderer;\n}\n\n/**\n * 根据 VNode 类型创建宿主节点\n *\n * @description\n * 将 VNode 映射为平台适配器可操作的宿主节点。\n * 支持元素节点、文本节点和注释节点。\n *\n * @param adapter - 平台适配器\n * @param vnode - 虚拟节点\n * @returns 宿主节点,无法识别的类型返回 null\n */\nfunction createHostNodeFromVNode<HN, HE extends HN>(\n adapter: PlatformAdapter<HN, HE>,\n vnode: VNode,\n): HN | null {\n const { type, children } = vnode;\n\n // 元素节点\n if (isString(type)) {\n const el = adapter.createElement(type);\n\n // 设置属性\n if (vnode.props) {\n for (const [key, value] of Object.entries(vnode.props)) {\n if (key === 'style' && isString(value)) {\n adapter.setStyle(el, value);\n } else if (key === 'class' && isString(value)) {\n adapter.addClass(el, value);\n } else if (isString(value)) {\n adapter.setAttribute(el, key, value);\n }\n }\n }\n\n // 递归处理子节点\n if (children && isString(children)) {\n adapter.setElementText(el, children);\n }\n\n return el as unknown as HN;\n }\n\n // 文本节点\n if (isString(children)) {\n return adapter.createText(children);\n }\n\n // 注释节点\n if (isFunction(type) === false && type !== null && !isString(type)) {\n // 非 string、非 function、非 null 的 type 可能是 Comment Symbol\n return adapter.createComment(String(children ?? ''));\n }\n\n return null;\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -20,9 +20,7 @@ var AdapterRegistry = class {
|
|
|
20
20
|
*/
|
|
21
21
|
register(adapter) {
|
|
22
22
|
if (!isString(adapter.name) || adapter.name.length === 0) {
|
|
23
|
-
throw new Error(
|
|
24
|
-
"[platform-adapter] \u9002\u914D\u5668\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A"
|
|
25
|
-
);
|
|
23
|
+
throw new Error("[platform-adapter] \u9002\u914D\u5668\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
|
|
26
24
|
}
|
|
27
25
|
this.adapters.set(adapter.name, adapter);
|
|
28
26
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapter-registry.ts","../src/create-renderer.ts"],"names":["isString"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * 平台适配器注册表\r\n *\r\n * @description\r\n * 管理所有已注册的平台适配器和插件,提供统一的注册、查询和插件管理能力。\r\n * 采用单例模式,全局共享同一个注册表实例。\r\n */\r\n\r\nimport { isString } from '@lytjs/common-is';\r\nimport type { PlatformAdapter, PlatformPlugin } from './types';\r\n\r\n/**\r\n * 适配器注册表类\r\n *\r\n * @description\r\n * 维护平台适配器的注册信息,支持适配器的注册、注销、查询,\r\n * 以及平台级插件的管理。\r\n */\r\nclass AdapterRegistry {\r\n /** 已注册的适配器映射(平台名称 -> 适配器实例) */\r\n private adapters = new Map<string, PlatformAdapter>();\r\n /** 平台插件映射(平台名称 -> 插件数组) */\r\n private plugins = new Map<string, PlatformPlugin[]>();\r\n\r\n /**\r\n * 注册平台适配器\r\n *\r\n * @description\r\n * 将适配器实例注册到注册表中,以适配器的 name 为键。\r\n * 如果同名适配器已存在,将被覆盖。\r\n *\r\n * @param adapter - 平台适配器实例\r\n * @throws {Error} 当 adapter.name 为空时抛出错误\r\n */\r\n register(adapter: PlatformAdapter): void {\r\n if (!isString(adapter.name) || adapter.name.length === 0) {\r\n throw new Error(\r\n '[platform-adapter] 适配器名称不能为空',\r\n );\r\n }\r\n this.adapters.set(adapter.name, adapter);\r\n }\r\n\r\n /**\r\n * 注销平台适配器\r\n *\r\n * @description\r\n * 从注册表中移除指定名称的适配器,同时清除该平台关联的所有插件。\r\n *\r\n * @param name - 平台名称\r\n * @returns 是否成功注销(名称不存在时返回 false)\r\n */\r\n unregister(name: string): boolean {\r\n const removed = this.adapters.delete(name);\r\n if (removed) {\r\n this.plugins.delete(name);\r\n }\r\n return removed;\r\n }\r\n\r\n /**\r\n * 获取指定名称的平台适配器\r\n *\r\n * @param name - 平台名称\r\n * @returns 适配器实例,未找到时返回 undefined\r\n */\r\n get(name: string): PlatformAdapter | undefined {\r\n return this.adapters.get(name);\r\n }\r\n\r\n /**\r\n * 检查指定名称的适配器是否已注册\r\n *\r\n * @param name - 平台名称\r\n * @returns 是否已注册\r\n */\r\n has(name: string): boolean {\r\n return this.adapters.has(name);\r\n }\r\n\r\n /**\r\n * 获取所有已注册的适配器名称\r\n *\r\n * @returns 平台名称数组\r\n */\r\n getNames(): string[] {\r\n return Array.from(this.adapters.keys());\r\n }\r\n\r\n /**\r\n * 为指定平台添加插件\r\n *\r\n * @description\r\n * 将插件添加到目标平台的插件列表中。\r\n * 如果目标平台不存在,会自动创建空的插件列表。\r\n *\r\n * @param platformName - 目标平台名称\r\n * @param plugin - 平台插件实例\r\n */\r\n addPlugin(platformName: string, plugin: PlatformPlugin): void {\r\n const existing = this.plugins.get(platformName);\r\n if (existing) {\r\n existing.push(plugin);\r\n } else {\r\n this.plugins.set(platformName, [plugin]);\r\n }\r\n }\r\n\r\n /**\r\n * 从指定平台移除插件\r\n *\r\n * @param platformName - 目标平台名称\r\n * @param pluginName - 要移除的插件名称\r\n * @returns 是否成功移除(插件不存在时返回 false)\r\n */\r\n removePlugin(platformName: string, pluginName: string): boolean {\r\n const list = this.plugins.get(platformName);\r\n if (!list) return false;\r\n\r\n const index = list.findIndex((p) => p.name === pluginName);\r\n if (index === -1) return false;\r\n\r\n list.splice(index, 1);\r\n return true;\r\n }\r\n\r\n /**\r\n * 获取指定平台的所有插件\r\n *\r\n * @param platformName - 目标平台名称\r\n * @returns 插件数组,平台无插件时返回空数组\r\n */\r\n getPlugins(platformName: string): PlatformPlugin[] {\r\n return this.plugins.get(platformName) ?? [];\r\n }\r\n}\r\n\r\n/** 全局适配器注册表单例 */\r\nexport const adapterRegistry = new AdapterRegistry();\r\n","/**\r\n * 跨平台渲染器工厂\r\n *\r\n * @description\r\n * 基于平台适配器创建渲染器实例,提供统一的 render/unmount API,\r\n * 屏蔽底层平台差异。\r\n */\r\n\r\nimport { isFunction, isString } from '@lytjs/common-is';\r\nimport type { VNode } from '@lytjs/common-vnode';\r\nimport type { PlatformAdapter, PlatformConfig } from './types';\r\n\r\n/**\r\n * 平台渲染器接口\r\n *\r\n * @description\r\n * 封装了平台适配器,提供高层的渲染和卸载 API。\r\n *\r\n * @template HN - 宿主节点类型\r\n * @template HE - 宿主元素类型(extends HN)\r\n */\r\nexport interface PlatformRenderer<HN, HE extends HN> {\r\n /**\r\n * 将 VNode 渲染到指定容器中\r\n *\r\n * @param vnode - 要渲染的虚拟节点\r\n * @param container - 宿主容器节点\r\n */\r\n render(vnode: VNode, container: HN): void;\r\n\r\n /**\r\n * 卸载指定容器中的内容\r\n *\r\n * @param container - 宿主容器节点\r\n */\r\n unmount(container: HN): void;\r\n\r\n /**\r\n * 获取关联的平台适配器\r\n *\r\n * @returns 平台适配器实例\r\n */\r\n getAdapter(): PlatformAdapter<HN, HE>;\r\n}\r\n\r\n/**\r\n * 创建平台渲染器\r\n *\r\n * @description\r\n * 工厂函数,基于给定的平台适配器和配置创建渲染器实例。\r\n * 渲染器内部委托适配器完成实际的 DOM 操作。\r\n *\r\n * @template HN - 宿主节点类型\r\n * @template HE - 宿主元素类型(extends HN)\r\n * @param adapter - 平台适配器实例\r\n * @param config - 平台配置(可选)\r\n * @returns 平台渲染器实例\r\n *\r\n * @example\r\n * ```typescript\r\n * const renderer = createPlatformRenderer(myAdapter, { debug: true });\r\n * renderer.render(vnode, container);\r\n * ```\r\n */\r\nexport function createPlatformRenderer<HN, HE extends HN>(\r\n adapter: PlatformAdapter<HN, HE>,\r\n config?: PlatformConfig,\r\n): PlatformRenderer<HN, HE> {\r\n // 当前容器中已渲染的子节点引用\r\n let currentChild: HN | null = null;\r\n\r\n const renderer: PlatformRenderer<HN, HE> = {\r\n /**\r\n * 将 VNode 渲染到容器中\r\n *\r\n * @description\r\n * 根据 vnode 类型创建对应的宿主节点,并插入到容器中。\r\n * 如果容器中已有内容,会先卸载再重新渲染。\r\n */\r\n render(vnode: VNode, container: HN): void {\r\n // 先卸载已有内容\r\n if (currentChild !== null) {\r\n this.unmount(container);\r\n }\r\n\r\n const node = createHostNodeFromVNode(adapter, vnode);\r\n if (node !== null) {\r\n adapter.insert(node, container, null);\r\n currentChild = node;\r\n }\r\n\r\n if (config?.debug) {\r\n // eslint-disable-next-line no-console\r\n console.log(`[platform-adapter:${adapter.name}] 渲染完成`, vnode);\r\n }\r\n },\r\n\r\n /**\r\n * 卸载容器中的内容\r\n */\r\n unmount(_container: HN): void {\r\n if (currentChild !== null) {\r\n adapter.remove(currentChild);\r\n currentChild = null;\r\n }\r\n\r\n if (config?.debug) {\r\n // eslint-disable-next-line no-console\r\n console.log(`[platform-adapter:${adapter.name}] 卸载完成`);\r\n }\r\n },\r\n\r\n /**\r\n * 获取关联的平台适配器\r\n */\r\n getAdapter(): PlatformAdapter<HN, HE> {\r\n return adapter;\r\n },\r\n };\r\n\r\n return renderer;\r\n}\r\n\r\n/**\r\n * 根据 VNode 类型创建宿主节点\r\n *\r\n * @description\r\n * 将 VNode 映射为平台适配器可操作的宿主节点。\r\n * 支持元素节点、文本节点和注释节点。\r\n *\r\n * @param adapter - 平台适配器\r\n * @param vnode - 虚拟节点\r\n * @returns 宿主节点,无法识别的类型返回 null\r\n */\r\nfunction createHostNodeFromVNode<HN, HE extends HN>(\r\n adapter: PlatformAdapter<HN, HE>,\r\n vnode: VNode,\r\n): HN | null {\r\n const { type, children } = vnode;\r\n\r\n // 元素节点\r\n if (isString(type)) {\r\n const el = adapter.createElement(type);\r\n\r\n // 设置属性\r\n if (vnode.props) {\r\n for (const [key, value] of Object.entries(vnode.props)) {\r\n if (key === 'style' && isString(value)) {\r\n adapter.setStyle(el, value);\r\n } else if (key === 'class' && isString(value)) {\r\n adapter.addClass(el, value);\r\n } else if (isString(value)) {\r\n adapter.setAttribute(el, key, value);\r\n }\r\n }\r\n }\r\n\r\n // 递归处理子节点\r\n if (children && isString(children)) {\r\n adapter.setElementText(el, children);\r\n }\r\n\r\n return el as unknown as HN;\r\n }\r\n\r\n // 文本节点\r\n if (isString(children)) {\r\n return adapter.createText(children);\r\n }\r\n\r\n // 注释节点\r\n if (isFunction(type) === false && type !== null && !isString(type)) {\r\n // 非 string、非 function、非 null 的 type 可能是 Comment Symbol\r\n return adapter.createComment(String(children ?? ''));\r\n }\r\n\r\n return null;\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapter-registry.ts","../src/create-renderer.ts"],"names":["isString"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * 平台适配器注册表\n *\n * @description\n * 管理所有已注册的平台适配器和插件,提供统一的注册、查询和插件管理能力。\n * 采用单例模式,全局共享同一个注册表实例。\n */\n\nimport { isString } from '@lytjs/common-is';\nimport type { PlatformAdapter, PlatformPlugin } from './types';\n\n/**\n * 适配器注册表类\n *\n * @description\n * 维护平台适配器的注册信息,支持适配器的注册、注销、查询,\n * 以及平台级插件的管理。\n */\nclass AdapterRegistry {\n /** 已注册的适配器映射(平台名称 -> 适配器实例) */\n private adapters = new Map<string, PlatformAdapter>();\n /** 平台插件映射(平台名称 -> 插件数组) */\n private plugins = new Map<string, PlatformPlugin[]>();\n\n /**\n * 注册平台适配器\n *\n * @description\n * 将适配器实例注册到注册表中,以适配器的 name 为键。\n * 如果同名适配器已存在,将被覆盖。\n *\n * @param adapter - 平台适配器实例\n * @throws {Error} 当 adapter.name 为空时抛出错误\n */\n register(adapter: PlatformAdapter): void {\n if (!isString(adapter.name) || adapter.name.length === 0) {\n throw new Error('[platform-adapter] 适配器名称不能为空');\n }\n this.adapters.set(adapter.name, adapter);\n }\n\n /**\n * 注销平台适配器\n *\n * @description\n * 从注册表中移除指定名称的适配器,同时清除该平台关联的所有插件。\n *\n * @param name - 平台名称\n * @returns 是否成功注销(名称不存在时返回 false)\n */\n unregister(name: string): boolean {\n const removed = this.adapters.delete(name);\n if (removed) {\n this.plugins.delete(name);\n }\n return removed;\n }\n\n /**\n * 获取指定名称的平台适配器\n *\n * @param name - 平台名称\n * @returns 适配器实例,未找到时返回 undefined\n */\n get(name: string): PlatformAdapter | undefined {\n return this.adapters.get(name);\n }\n\n /**\n * 检查指定名称的适配器是否已注册\n *\n * @param name - 平台名称\n * @returns 是否已注册\n */\n has(name: string): boolean {\n return this.adapters.has(name);\n }\n\n /**\n * 获取所有已注册的适配器名称\n *\n * @returns 平台名称数组\n */\n getNames(): string[] {\n return Array.from(this.adapters.keys());\n }\n\n /**\n * 为指定平台添加插件\n *\n * @description\n * 将插件添加到目标平台的插件列表中。\n * 如果目标平台不存在,会自动创建空的插件列表。\n *\n * @param platformName - 目标平台名称\n * @param plugin - 平台插件实例\n */\n addPlugin(platformName: string, plugin: PlatformPlugin): void {\n const existing = this.plugins.get(platformName);\n if (existing) {\n existing.push(plugin);\n } else {\n this.plugins.set(platformName, [plugin]);\n }\n }\n\n /**\n * 从指定平台移除插件\n *\n * @param platformName - 目标平台名称\n * @param pluginName - 要移除的插件名称\n * @returns 是否成功移除(插件不存在时返回 false)\n */\n removePlugin(platformName: string, pluginName: string): boolean {\n const list = this.plugins.get(platformName);\n if (!list) return false;\n\n const index = list.findIndex((p) => p.name === pluginName);\n if (index === -1) return false;\n\n list.splice(index, 1);\n return true;\n }\n\n /**\n * 获取指定平台的所有插件\n *\n * @param platformName - 目标平台名称\n * @returns 插件数组,平台无插件时返回空数组\n */\n getPlugins(platformName: string): PlatformPlugin[] {\n return this.plugins.get(platformName) ?? [];\n }\n}\n\n/** 全局适配器注册表单例 */\nexport const adapterRegistry = new AdapterRegistry();\n","/**\n * 跨平台渲染器工厂\n *\n * @description\n * 基于平台适配器创建渲染器实例,提供统一的 render/unmount API,\n * 屏蔽底层平台差异。\n */\n\nimport { isFunction, isString } from '@lytjs/common-is';\nimport type { VNode } from '@lytjs/common-vnode';\nimport type { PlatformAdapter, PlatformConfig } from './types';\n\n/**\n * 平台渲染器接口\n *\n * @description\n * 封装了平台适配器,提供高层的渲染和卸载 API。\n *\n * @template HN - 宿主节点类型\n * @template HE - 宿主元素类型(extends HN)\n */\nexport interface PlatformRenderer<HN, HE extends HN> {\n /**\n * 将 VNode 渲染到指定容器中\n *\n * @param vnode - 要渲染的虚拟节点\n * @param container - 宿主容器节点\n */\n render(vnode: VNode, container: HN): void;\n\n /**\n * 卸载指定容器中的内容\n *\n * @param container - 宿主容器节点\n */\n unmount(container: HN): void;\n\n /**\n * 获取关联的平台适配器\n *\n * @returns 平台适配器实例\n */\n getAdapter(): PlatformAdapter<HN, HE>;\n}\n\n/**\n * 创建平台渲染器\n *\n * @description\n * 工厂函数,基于给定的平台适配器和配置创建渲染器实例。\n * 渲染器内部委托适配器完成实际的 DOM 操作。\n *\n * @template HN - 宿主节点类型\n * @template HE - 宿主元素类型(extends HN)\n * @param adapter - 平台适配器实例\n * @param config - 平台配置(可选)\n * @returns 平台渲染器实例\n *\n * @example\n * ```typescript\n * const renderer = createPlatformRenderer(myAdapter, { debug: true });\n * renderer.render(vnode, container);\n * ```\n */\nexport function createPlatformRenderer<HN, HE extends HN>(\n adapter: PlatformAdapter<HN, HE>,\n config?: PlatformConfig,\n): PlatformRenderer<HN, HE> {\n // 当前容器中已渲染的子节点引用\n let currentChild: HN | null = null;\n\n const renderer: PlatformRenderer<HN, HE> = {\n /**\n * 将 VNode 渲染到容器中\n *\n * @description\n * 根据 vnode 类型创建对应的宿主节点,并插入到容器中。\n * 如果容器中已有内容,会先卸载再重新渲染。\n */\n render(vnode: VNode, container: HN): void {\n // 先卸载已有内容\n if (currentChild !== null) {\n this.unmount(container);\n }\n\n const node = createHostNodeFromVNode(adapter, vnode);\n if (node !== null) {\n adapter.insert(node, container, null);\n currentChild = node;\n }\n\n if (config?.debug) {\n // eslint-disable-next-line no-console\n console.log(`[platform-adapter:${adapter.name}] 渲染完成`, vnode);\n }\n },\n\n /**\n * 卸载容器中的内容\n */\n unmount(_container: HN): void {\n if (currentChild !== null) {\n adapter.remove(currentChild);\n currentChild = null;\n }\n\n if (config?.debug) {\n // eslint-disable-next-line no-console\n console.log(`[platform-adapter:${adapter.name}] 卸载完成`);\n }\n },\n\n /**\n * 获取关联的平台适配器\n */\n getAdapter(): PlatformAdapter<HN, HE> {\n return adapter;\n },\n };\n\n return renderer;\n}\n\n/**\n * 根据 VNode 类型创建宿主节点\n *\n * @description\n * 将 VNode 映射为平台适配器可操作的宿主节点。\n * 支持元素节点、文本节点和注释节点。\n *\n * @param adapter - 平台适配器\n * @param vnode - 虚拟节点\n * @returns 宿主节点,无法识别的类型返回 null\n */\nfunction createHostNodeFromVNode<HN, HE extends HN>(\n adapter: PlatformAdapter<HN, HE>,\n vnode: VNode,\n): HN | null {\n const { type, children } = vnode;\n\n // 元素节点\n if (isString(type)) {\n const el = adapter.createElement(type);\n\n // 设置属性\n if (vnode.props) {\n for (const [key, value] of Object.entries(vnode.props)) {\n if (key === 'style' && isString(value)) {\n adapter.setStyle(el, value);\n } else if (key === 'class' && isString(value)) {\n adapter.addClass(el, value);\n } else if (isString(value)) {\n adapter.setAttribute(el, key, value);\n }\n }\n }\n\n // 递归处理子节点\n if (children && isString(children)) {\n adapter.setElementText(el, children);\n }\n\n return el as unknown as HN;\n }\n\n // 文本节点\n if (isString(children)) {\n return adapter.createText(children);\n }\n\n // 注释节点\n if (isFunction(type) === false && type !== null && !isString(type)) {\n // 非 string、非 function、非 null 的 type 可能是 Comment Symbol\n return adapter.createComment(String(children ?? ''));\n }\n\n return null;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@lytjs/platform-adapter",
|
|
3
|
-
"version": "6.
|
|
4
|
-
"description": "LytJS 跨平台渲染适配器",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/index.cjs",
|
|
7
|
-
"module": "./dist/index.mjs",
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
|
-
"import": "./dist/index.mjs",
|
|
13
|
-
"require": "./dist/index.cjs"
|
|
14
|
-
},
|
|
15
|
-
"./package.json": "./package.json"
|
|
16
|
-
},
|
|
17
|
-
"files": [
|
|
18
|
-
"dist"
|
|
19
|
-
],
|
|
20
|
-
"sideEffects": false,
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsup",
|
|
23
|
-
"dev": "tsup --watch",
|
|
24
|
-
"test": "vitest run",
|
|
25
|
-
"test:watch": "vitest",
|
|
26
|
-
"type-check": "tsc --noEmit",
|
|
27
|
-
"lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
28
|
-
"clean": "rm -rf dist"
|
|
29
|
-
},
|
|
30
|
-
"dependencies": {
|
|
31
|
-
"@lytjs/common-is": "
|
|
32
|
-
"@lytjs/common-constants": "
|
|
33
|
-
"@lytjs/vdom": "
|
|
34
|
-
"@lytjs/common-vnode": "
|
|
35
|
-
},
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"tsup": "^8.0.0",
|
|
38
|
-
"typescript": "^5.4.0",
|
|
39
|
-
"vitest": "^3.0.0"
|
|
40
|
-
},
|
|
41
|
-
"license": "MIT",
|
|
42
|
-
"repository": {
|
|
43
|
-
"type": "git",
|
|
44
|
-
"url": "https://gitee.com/lytjs/lytjs.git",
|
|
45
|
-
"directory": "packages/ecosystem/packages/platform-adapter"
|
|
46
|
-
},
|
|
47
|
-
"keywords": [
|
|
48
|
-
"lytjs",
|
|
49
|
-
"platform-adapter",
|
|
50
|
-
"cross-platform",
|
|
51
|
-
"renderer"
|
|
52
|
-
]
|
|
53
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@lytjs/platform-adapter",
|
|
3
|
+
"version": "6.6.0",
|
|
4
|
+
"description": "LytJS 跨平台渲染适配器",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"type-check": "tsc --noEmit",
|
|
27
|
+
"lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
28
|
+
"clean": "rm -rf dist"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@lytjs/common-is": "workspace:*",
|
|
32
|
+
"@lytjs/common-constants": "workspace:*",
|
|
33
|
+
"@lytjs/vdom": "workspace:*",
|
|
34
|
+
"@lytjs/common-vnode": "workspace:*"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"tsup": "^8.0.0",
|
|
38
|
+
"typescript": "^5.4.0",
|
|
39
|
+
"vitest": "^3.0.0"
|
|
40
|
+
},
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://gitee.com/lytjs/lytjs.git",
|
|
45
|
+
"directory": "packages/ecosystem/packages/platform-adapter"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"lytjs",
|
|
49
|
+
"platform-adapter",
|
|
50
|
+
"cross-platform",
|
|
51
|
+
"renderer"
|
|
52
|
+
]
|
|
53
|
+
}
|