@geektech/tsone 0.0.2 → 0.2.1

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 CHANGED
@@ -1,34 +1,44 @@
1
1
  # TSone
2
2
 
3
- 轻量级纯 TypeScript 前端框架,提供响应式系统、类组件、策略化渲染和路由能力。
3
+ English | [简体中文](./README-zh.md)
4
4
 
5
- ## 特性
5
+ A lightweight frontend framework written entirely in TypeScript, with
6
+ reactivity, class-based components, strategy-driven rendering, and routing.
6
7
 
7
- - 纯 TypeScript 实现,TypeScript为第一公民,公开 API 提供类型定义
8
- - Bun 原生工具链:安装、测试、构建、示例服务和文档服务均由 Bun 驱动
9
- - 文档内容由 TSone 的 TypeScript typed content registry 提供
10
- - 响应式系统:`reactive`、`effect`、`computed`
11
- - 面向对象组件模型:`Component<Props, State>`、生命周期、事件、插槽
12
- - 策略模式渲染层:文本、元素、组件、插槽按 VNode 类型分发
13
- - 内置路由:`createRouter`、`RouterView`、`RouterLink`
14
- - 轻量级运行时,生产包无外部运行时依赖
8
+ ## Features
15
9
 
16
- ## 仓库结构
10
+ - TypeScript-first implementation with typed public APIs
11
+ - Bun-native tooling for installation, testing, builds, playgrounds, and docs
12
+ - Documentation backed by a TypeScript typed-content registry
13
+ - Reactive primitives: `reactive`, `effect`, and `computed`
14
+ - Object-oriented components with `Component<Props, State>`, lifecycle hooks,
15
+ events, and slots
16
+ - Strategy-based rendering that dispatches text, elements, components, and
17
+ slots by VNode type
18
+ - Built-in routing with `createRouter`, `RouterView`, and `RouterLink`
19
+ - Lightweight runtime with no external production dependencies
17
20
 
18
- 本仓库是 Bun workspace monorepo,当前发布包位于 `packages/tsone/`。
19
- 根目录命令会代理到该包,包内仍保留自己的源码、测试、示例、文档和发布配置。
21
+ ## Repository Structure
20
22
 
21
- ## 安装
23
+ This repository is a Bun workspace monorepo with two published packages:
24
+ `packages/tsone/` contains the browser framework `@geektech/tsone`, and
25
+ `packages/tsone-cli/` contains the Bun-native development tooling
26
+ `@geektech/tsone-cli`. Standalone example projects live in the root
27
+ `playground/` directory.
28
+
29
+ ## Installation
22
30
 
23
31
  ```bash
24
- bun add @geektech/tsone
32
+ bun add @geektech/tsone @geektech/tsone-cli
25
33
  ```
26
34
 
27
35
  ```bash
28
- pnpm add @geektech/tsone
36
+ pnpm add @geektech/tsone @geektech/tsone-cli
29
37
  ```
30
38
 
31
- ## 快速开始
39
+ The framework and CLI require Bun `>=1.3.0` for the documented workflow.
40
+
41
+ ## Quick Start
32
42
 
33
43
  ```typescript
34
44
  import {
@@ -48,7 +58,7 @@ class App extends Component<Record<string, never>, AppState> {
48
58
  protected initState(): AppState {
49
59
  return {
50
60
  count: 0,
51
- version: '0.0.2',
61
+ version: '0.2.1',
52
62
  };
53
63
  }
54
64
 
@@ -78,13 +88,95 @@ class App extends Component<Record<string, never>, AppState> {
78
88
  const state = reactive({ ready: true });
79
89
  const status = computed(() => (state.ready ? 'ready' : 'pending'));
80
90
 
81
- const app = createApp({ root: App, rootElement: '#app', state });
91
+ const app = createApp({ root: App, state });
82
92
  app.mount();
83
93
 
84
94
  console.log(status.value);
85
95
  ```
86
96
 
87
- ## 路由
97
+ `createApp` uses `#app` as its default mount target, so an application can call
98
+ `app.mount()` immediately after creation. If the target is not currently in the
99
+ document, `mount()` safely skips that attempt. Pass `rootElement` only when you
100
+ need to override the default target.
101
+
102
+ ## Development Tooling
103
+
104
+ The separate `@geektech/tsone-cli` package provides `tsone dev` and
105
+ `tsone build`. Its default entry is `src/main.ts`; that module must expose the
106
+ application as `export const app`, and the value must provide
107
+ `renderHtmlDocument()`.
108
+
109
+ Create an optional `tsone.config.ts` at the project root:
110
+
111
+ The config file supports only a plain-object default export; functional or
112
+ function-valued config is not supported.
113
+
114
+ ```typescript
115
+ import { defineConfig } from '@geektech/tsone-cli';
116
+
117
+ export default defineConfig({
118
+ server: {
119
+ proxy: {
120
+ '/api': {
121
+ target: 'http://localhost:3000',
122
+ changeOrigin: true,
123
+ rewrite: (path) => path.replace(/^\/api/, ''),
124
+ },
125
+ },
126
+ },
127
+ build: { outDir: 'dist' },
128
+ });
129
+ ```
130
+
131
+ The string proxy shorthand is also supported, for example
132
+ `{ '/backend': 'http://localhost:4000' }`. Defaults are `src/main.ts`,
133
+ `127.0.0.1`, port `52211`, an empty `server.proxy`, `dist`, and no additional
134
+ pages.
135
+
136
+ Multi-page applications map routes to page entries in the same config file;
137
+ each page entry exposes `app` with `renderHtmlDocument()` exactly like the root
138
+ entry:
139
+
140
+ ```typescript
141
+ export default defineConfig({
142
+ entry: 'src/main.ts',
143
+ pages: {
144
+ '/about': 'src/about.ts',
145
+ '/docs/guide': 'src/guide.ts',
146
+ },
147
+ });
148
+ ```
149
+
150
+ `pages` keys must start with `/` and may nest; the root `/` is served by
151
+ `entry`. During development each page is served at its route, and `tsone build`
152
+ emits one HTML document per page (`index.html`, `about.html`,
153
+ `docs/guide.html`) with page-relative asset URLs.
154
+
155
+ ```text
156
+ tsone dev [--host <host>] [--port <port>] [--no-watch]
157
+ tsone build [--out-dir <path>]
158
+ ```
159
+
160
+ `dev` accepts host/port overrides and `--no-watch`; `build` accepts the
161
+ output-directory override; both `--port 3000` and `--port=3000` forms are
162
+ valid. `tsone dev` watches the project by default and notifies browsers to
163
+ reload over `/__tsone/reload` when a watched file changes. Build output must
164
+ remain a safe child directory inside the project root.
165
+
166
+ Programmatic tooling is imported from `@geektech/tsone-cli`, not from the
167
+ framework root. It exports `defineConfig`, `resolveConfig`, `startDevServer`,
168
+ and `build`. The dev server is owned by the caller, which must call
169
+ `server.stop()`; `build()` returns absolute `root` and `outDir` values plus
170
+ `assetsBuilt`.
171
+
172
+ The development server serves HTTP only. Proxy targets may use HTTP or HTTPS.
173
+ Rules use literal prefix matching with the longest match first, retain
174
+ query/body/end-to-end headers, optionally apply `changeOrigin` and `rewrite`,
175
+ and return `502 Bad Gateway` when the upstream is unreachable. CLI v1 has no
176
+ config plugins, WebSocket, HMR, SSR, functional config, `public/` copying, or
177
+ public minify/sourcemap settings.
178
+
179
+ ## Routing
88
180
 
89
181
  ```typescript
90
182
  import { Component, VNode, createApp } from '@geektech/tsone';
@@ -103,11 +195,11 @@ class Layout extends Component {
103
195
  children: [
104
196
  {
105
197
  component: RouterLink,
106
- props: { to: '/', children: ['首页'] },
198
+ props: { to: '/', children: ['Home'] },
107
199
  },
108
200
  {
109
201
  component: RouterLink,
110
- props: { to: '/users/42', children: ['用户'] },
202
+ props: { to: '/users/42', children: ['User'] },
111
203
  },
112
204
  { component: RouterView },
113
205
  ],
@@ -119,14 +211,18 @@ const router = createRouter({
119
211
  mode: 'history',
120
212
  routes: [
121
213
  { path: '/', component: HomePage },
122
- { path: '/users/:id', component: UserPage, meta: { title: '用户详情' } },
214
+ {
215
+ path: '/users/:id',
216
+ component: UserPage,
217
+ meta: { title: 'User Details' },
218
+ },
123
219
  ],
124
220
  });
125
221
 
126
- createApp({ root: Layout, rootElement: '#app' }).use(router).mount();
222
+ createApp({ root: Layout }).use(router).mount();
127
223
  ```
128
224
 
129
- ## 开发命令
225
+ ## Development Commands
130
226
 
131
227
  ```bash
132
228
  bun install
@@ -137,18 +233,47 @@ bun run docs
137
233
  bun run docs:build
138
234
  ```
139
235
 
140
- 文档站点内容维护在 `packages/tsone/docs/app/content/*.ts` typed content registry 中。
236
+ The root `playground/` directory contains two standalone projects:
237
+
238
+ ```bash
239
+ bun run dev
240
+ bun run dev:site
241
+ bun run dev:admin
242
+ ```
243
+
244
+ - `playground/official-site`: product website example
245
+ - `playground/admin-dashboard`: admin dashboard example
246
+
247
+ Documentation content is maintained in the typed-content registry. Chinese
248
+ content lives in `packages/tsone/docs/app/content/zh/`, while English content
249
+ lives in `packages/tsone/docs/app/content/en/`. Every logical route must exist
250
+ in both directories.
251
+
252
+ Chinese and English catalogs each contain exactly 14 logical routes. Add or
253
+ remove a route in both catalogs in the same change.
254
+
255
+ Content links stay locale-neutral: never write `/en/` manually. Chinese public
256
+ routes are unprefixed, while English routes use `/en/`. Browser-language
257
+ detection runs only at `/`; manual selection takes precedence and persists for
258
+ later visits.
141
259
 
142
- 生成静态文档产物:
260
+ Build the static documentation site with:
143
261
 
144
262
  ```bash
145
263
  bun run docs:build
146
264
  ```
147
265
 
148
- 基础 HTML 文档壳也可以由 TSone 生成,`body` 传入组件或 VNode,不传 HTML 字符串。该 API 会通过 TSone 渲染器挂载节点;在 Bun/Node 静态生成环境中,请先提供 DOM-like document:
266
+ The build fails strictly for missing, extra, duplicate, empty, or
267
+ mixed-language pages.
268
+
269
+ The base HTML document shell can also be generated from `createApp`. It emits a
270
+ `#app` mount node by default; pass `rootElement` only to use a different target.
271
+ When a custom `body` is needed, pass a component or VNode rather than an HTML
272
+ string. This API renders the mount node through TSone's renderer. In a Bun or
273
+ Node static-generation environment, provide a DOM-like document first:
149
274
 
150
275
  ```typescript
151
- import { renderHtmlDocument, type StyleSheet } from '@geektech/tsone';
276
+ import { createApp, type StyleSheet } from '@geektech/tsone';
152
277
 
153
278
  const styles: StyleSheet = [
154
279
  {
@@ -157,38 +282,55 @@ const styles: StyleSheet = [
157
282
  },
158
283
  ];
159
284
 
160
- const html = renderHtmlDocument({
161
- lang: 'zh-CN',
162
- title: 'TSone App',
163
- body: { component: App, props: { message: 'Hello TSone' } },
164
- styles,
285
+ const app = createApp({
286
+ root: App,
287
+ document: {
288
+ lang: 'en',
289
+ title: 'TSone App',
290
+ styles,
291
+ },
292
+ });
293
+
294
+ const html = app.renderHtmlDocument({
165
295
  scripts: [{ type: 'module', src: '/assets/app.js' }],
166
296
  });
167
297
  ```
168
298
 
169
- ## 公开 API
299
+ ## Public API
170
300
 
171
- 主入口 `@geektech/tsone`:
301
+ The main `@geektech/tsone` entry point exports:
172
302
 
173
303
  - `createApp(options)`
304
+ - `createApp({ root, rootProps })`
174
305
  - `Component<Props, State>`
306
+ - `TransitionGroup` / `TransitionGroupProps` / `TransitionAnimationType`
175
307
  - `VNode`
176
308
  - `h()` / `createComponent()` / `slot()` / `each()`
309
+ - `Tag(tag, options)` for arbitrary HTML elements
177
310
  - `Div()` / `Span()` / `P()` / `Button()` / `Input()`
311
+ - `Section()` / `Main()` / `Header()` / `Footer()` / `Nav()` / `Article()` /
312
+ `Aside()`
313
+ - `H1()` through `H6()` / `Strong()` / `Em()` / `Small()` / `Pre()` /
314
+ `Code()` / `Blockquote()`
315
+ - `Ul()` / `Ol()` / `Li()` / `A()` / `Img()`
316
+ - `Form()` / `Label()` / `Textarea()` / `Select()` / `Option()`
317
+ - `Table()` / `Thead()` / `Tbody()` / `Tr()` / `Th()` / `Td()`
178
318
  - `Directions` / `ModelBinding`
179
- - `InjectionKey`、组件和应用的 `provide()` / `inject()`
319
+ - `InjectionKey` and component/application `provide()` / `inject()`
180
320
  - `createForm()` / `required()` / `minLength()` / `validate()`
321
+ - `createApp(options).renderHtmlDocument(options)`
181
322
  - `renderHtmlDocument(options)`
182
323
  - `StyleSheet` / `renderStyleSheet(styles)`
183
324
  - `reactive()` / `readonly()`
184
325
  - `effect()` / `stop()`
185
326
  - `computed()`
186
327
  - `ref()` / `isRef()` / `unref()`
187
- - `version`,当前为 `0.0.2`
328
+ - `version`, currently `0.2.1`
188
329
 
189
- ## 渲染、通信与表单
330
+ ## Rendering, Communication, and Forms
190
331
 
191
- `directions.if` 可以控制元素、组件或插槽的挂载;不满足条件时会卸载节点:
332
+ `directions.if` controls whether an element, component, or slot is mounted. The
333
+ node is unmounted when the condition is false:
192
334
 
193
335
  ```typescript
194
336
  {
@@ -197,7 +339,8 @@ const html = renderHtmlDocument({
197
339
  }
198
340
  ```
199
341
 
200
- `each()` 为列表产生稳定 key,供渲染器在排序、插入和删除时复用节点:
342
+ `each()` creates stable keys for list items so the renderer can reuse nodes
343
+ during reordering, insertion, and removal:
201
344
 
202
345
  ```typescript
203
346
  const items = each(
@@ -207,7 +350,30 @@ const items = each(
207
350
  );
208
351
  ```
209
352
 
210
- 组件事件可订阅并用返回的函数取消订阅;组件 VNode 可通过 `emitters` 声明父级监听器。
353
+ `TransitionGroup` animates the enter and exit of direct keyed children:
354
+
355
+ ```typescript
356
+ {
357
+ component: TransitionGroup,
358
+ props: { tag: 'ul', type: 'fade', duration: 300 },
359
+ children: each(
360
+ this.state.items,
361
+ (item) => Li({ children: [item.label] }),
362
+ (item) => item.id
363
+ ),
364
+ }
365
+ ```
366
+
367
+ The animation types are `fade`, `slide-up`, `slide-down`, `slide-left`,
368
+ `slide-right`, and `scale`. The defaults are a `div` wrapper, `fade`, and
369
+ `300` ms with `ease` easing. Every direct child must have a unique key. Initial
370
+ children animate in, and removed children stay mounted until their exit ends.
371
+ TSone automatically disables these animations for
372
+ `prefers-reduced-motion: reduce` or when Web Animations is unavailable.
373
+ Reordering reuses and moves existing nodes without a reorder or FLIP animation.
374
+
375
+ Component events return an unsubscribe function, while component VNodes can
376
+ declare parent listeners through `emitters`:
211
377
 
212
378
  ```typescript
213
379
  const stopListening = child.on('saved', (payload) => console.log(payload));
@@ -215,7 +381,8 @@ stopListening();
215
381
  // { component: Editor, emitters: { saved: (payload) => this.save(payload) } }
216
382
  ```
217
383
 
218
- 依赖注入从当前组件向父级再到应用实例查找:
384
+ Dependency injection resolves values from the current component, then its
385
+ parents, and finally the application instance:
219
386
 
220
387
  ```typescript
221
388
  const THEME: InjectionKey<{ mode: string }> = Symbol('theme');
@@ -223,7 +390,8 @@ app.provide(THEME, { mode: 'dark' });
223
390
  const theme = this.inject(THEME, { mode: 'light' });
224
391
  ```
225
392
 
226
- `directions.model` 支持点分隔路径和转换函数,原生 input、textarea、checkbox、radio select 会同步:
393
+ `directions.model` supports dot-separated paths and conversion functions.
394
+ Native input, textarea, checkbox, radio, and select controls stay synchronized:
227
395
 
228
396
  ```typescript
229
397
  Input({
@@ -238,13 +406,13 @@ Input({
238
406
  });
239
407
  ```
240
408
 
241
- 校验是纯函数,不负责错误 UI 或提交:
409
+ Validation uses pure functions and does not own error UI or submission:
242
410
 
243
411
  ```typescript
244
412
  const form = createForm(this.state, {
245
- 'profile.name': [required('请输入姓名'), minLength(2)],
413
+ 'profile.name': [required('Name is required'), minLength(2)],
246
414
  'profile.age': [
247
- validate((value) => Number(value) >= 18 || '年龄须不小于 18'),
415
+ validate((value) => Number(value) >= 18 || 'Age must be at least 18'),
248
416
  ],
249
417
  });
250
418
 
@@ -252,7 +420,7 @@ const result = form.validate();
252
420
  form.resetErrors();
253
421
  ```
254
422
 
255
- 路由入口 `@geektech/tsone/router`:
423
+ The `@geektech/tsone/router` entry point exports:
256
424
 
257
425
  - `createRouter({ routes, mode, base })`
258
426
  - `Router`
@@ -262,12 +430,12 @@ form.resetErrors();
262
430
  - `RouteRecord`
263
431
  - `RouteLocation`
264
432
 
265
- 样式入口 `@geektech/tsone/style`:
433
+ The `@geektech/tsone/style` entry point exports:
266
434
 
267
435
  - `StyleManager`
268
436
  - `StyleSheet` / `renderStyleSheet(styles)`
269
437
 
270
- ## 发布前检查
438
+ ## Pre-Publish Checklist
271
439
 
272
440
  ```bash
273
441
  bun test
@@ -276,10 +444,11 @@ bun run build
276
444
  bun pm pack --cwd packages/tsone --dry-run
277
445
  ```
278
446
 
279
- ## 贡献
447
+ ## Contributing
280
448
 
281
- 欢迎提交 Issue Pull Request。开源发布前请确保测试、类型检查和构建均通过。
449
+ Issues and pull requests are welcome. Before publishing an open-source release,
450
+ make sure the tests, type checks, and build all pass.
282
451
 
283
- ## 许可证
452
+ ## License
284
453
 
285
454
  [MIT](LICENSE)
@@ -0,0 +1,7 @@
1
+ import { Component } from '../component';
2
+ import { type TransitionGroupNode, type TransitionGroupProps } from './types';
3
+ export declare class TransitionGroup extends Component<TransitionGroupProps> {
4
+ protected initState(): object;
5
+ protected initStyles(): void;
6
+ protected render(): TransitionGroupNode;
7
+ }
@@ -0,0 +1,2 @@
1
+ export { TransitionGroup } from './TransitionGroup';
2
+ export type { TransitionAnimationType, TransitionGroupProps } from './types';
@@ -0,0 +1,16 @@
1
+ import type { TransitionGroupOptions } from './types';
2
+ export interface ListAnimationRun {
3
+ animation: Animation;
4
+ token: symbol;
5
+ keyframes: Keyframe[];
6
+ options: KeyframeAnimationOptions;
7
+ finished: Promise<'finished' | 'cancelled'>;
8
+ }
9
+ export declare class ListAnimationController {
10
+ private readonly runs;
11
+ playEnter(element: HTMLElement, options: TransitionGroupOptions): ListAnimationRun | null;
12
+ playExit(element: HTMLElement, options: TransitionGroupOptions): ListAnimationRun | null;
13
+ cancel(element: HTMLElement): void;
14
+ private play;
15
+ private canAnimate;
16
+ }
@@ -0,0 +1,15 @@
1
+ import { ElementRenderStrategy } from '../renderer/element-strategy';
2
+ import type { RenderRuntimeContext, Renderable } from '../renderer/types';
3
+ import { type TransitionGroupNode } from './types';
4
+ export declare class TransitionGroupRenderStrategy extends ElementRenderStrategy<TransitionGroupNode> {
5
+ private readonly entries;
6
+ private readonly animations;
7
+ matches(vnode: Renderable): vnode is TransitionGroupNode;
8
+ protected mountChildren(element: HTMLElement, groupVNode: TransitionGroupNode, context: RenderRuntimeContext): void;
9
+ protected updateChildren(element: HTMLElement, _oldVNode: TransitionGroupNode, newVNode: TransitionGroupNode, context: RenderRuntimeContext): void;
10
+ protected unmountChildren(element: HTMLElement, vnode: TransitionGroupNode, context: RenderRuntimeContext): void;
11
+ private playEnter;
12
+ private startExit;
13
+ private finishExit;
14
+ private placeActiveEntries;
15
+ }
@@ -0,0 +1,25 @@
1
+ import { type EventListeners, type HTMLNode, type HTMLProps, type VNode } from '../vnode';
2
+ export declare const TRANSITION_ANIMATION_TYPES: readonly ["fade", "slide-up", "slide-down", "slide-left", "slide-right", "scale"];
3
+ export type TransitionAnimationType = (typeof TRANSITION_ANIMATION_TYPES)[number];
4
+ export interface TransitionGroupOptions {
5
+ type: TransitionAnimationType;
6
+ duration: number;
7
+ }
8
+ export interface TransitionGroupProps {
9
+ tag?: string;
10
+ type?: TransitionAnimationType;
11
+ duration?: number;
12
+ elementProps?: HTMLProps;
13
+ listeners?: EventListeners;
14
+ children?: VNode[];
15
+ }
16
+ export interface TransitionGroupNode extends HTMLNode {
17
+ transitionGroup: TransitionGroupOptions;
18
+ children?: VNode[];
19
+ }
20
+ export type KeyedTransitionChild = VNode & {
21
+ key: string | number;
22
+ };
23
+ export declare function normalizeTransitionGroupProps(props: TransitionGroupProps): Required<Pick<TransitionGroupProps, 'tag' | 'type' | 'duration'>>;
24
+ export declare function validateTransitionGroupChildren(children: Array<VNode | string>): KeyedTransitionChild[];
25
+ export declare function isTransitionGroupNode(vnode: unknown): vnode is TransitionGroupNode;
@@ -1,4 +1,5 @@
1
- import { ComponentConstructor, InjectionKey, InjectionResult } from './component';
1
+ import { ComponentConstructor, ComponentProps, InjectionKey, InjectionResult } from './component';
2
+ import { type HtmlDocumentBody, type HtmlDocumentOptions } from './document';
2
3
  import type { Router } from '../router';
3
4
  export interface Plugin {
4
5
  install: (app: OneApp, ...args: unknown[]) => void;
@@ -6,15 +7,23 @@ export interface Plugin {
6
7
  onUpdated?: (app: OneApp) => void;
7
8
  onBeforeUnmount?: (app: OneApp) => void;
8
9
  }
9
- export interface AppOptions<TState = Record<string, unknown>, TConfig = Record<string, unknown>> {
10
+ export type AppDocumentOptions = Partial<Omit<HtmlDocumentOptions, 'body'>> & {
11
+ body?: HtmlDocumentBody;
12
+ };
13
+ export type AppDocumentRenderOptions = AppDocumentOptions;
14
+ export interface AppOptions<TState = Record<string, unknown>, TConfig = Record<string, unknown>, TRootProps extends ComponentProps = ComponentProps> {
10
15
  /** 根组件构造函数 */
11
- root?: ComponentConstructor;
12
- /** 应用挂载点 */
16
+ root?: ComponentConstructor<TRootProps>;
17
+ /** 根组件 props */
18
+ rootProps?: TRootProps;
19
+ /** 应用挂载点,默认 #app */
13
20
  rootElement?: string | Element;
14
21
  /** 全局状态 */
15
22
  state?: TState;
16
23
  /** 全局配置 */
17
24
  config?: TConfig;
25
+ /** HTML 文档壳配置,用于 dev/build 生成入口页面 */
26
+ document?: AppDocumentOptions;
18
27
  }
19
28
  export interface AppContext<TConfig = Record<string, unknown>> {
20
29
  app: OneApp;
@@ -22,7 +31,7 @@ export interface AppContext<TConfig = Record<string, unknown>> {
22
31
  config: TConfig;
23
32
  router?: Router;
24
33
  }
25
- export declare class OneApp<TState extends object = Record<string, unknown>, TConfig extends object = Record<string, unknown>> {
34
+ export declare class OneApp<TState extends object = Record<string, unknown>, TConfig extends object = Record<string, unknown>, TRootProps extends ComponentProps = ComponentProps> {
26
35
  private options;
27
36
  private container;
28
37
  private rootInstance;
@@ -33,7 +42,7 @@ export declare class OneApp<TState extends object = Record<string, unknown>, TCo
33
42
  private plugins;
34
43
  private unmountedCallback?;
35
44
  router?: Router;
36
- constructor(options?: AppOptions<TState, TConfig>);
45
+ constructor(options?: AppOptions<TState, TConfig, TRootProps>);
37
46
  private handleError;
38
47
  /**
39
48
  * 渲染错误UI
@@ -58,7 +67,7 @@ export declare class OneApp<TState extends object = Record<string, unknown>, TCo
58
67
  /**
59
68
  * 更新根组件
60
69
  */
61
- updateRootComponent(component: ComponentConstructor): void;
70
+ updateRootComponent(component: ComponentConstructor<TRootProps>): void;
62
71
  /**
63
72
  * 更新应用状态
64
73
  */
@@ -83,10 +92,18 @@ export declare class OneApp<TState extends object = Record<string, unknown>, TCo
83
92
  * 监听应用卸载
84
93
  */
85
94
  onUnmounted(callback: () => void): this;
95
+ /**
96
+ * 生成应用入口 HTML 文档
97
+ */
98
+ renderHtmlDocument(options?: AppDocumentRenderOptions): string;
86
99
  /**
87
100
  * 解析根元素
88
101
  */
89
102
  private resolveRootElement;
103
+ private resolveMountContainer;
104
+ private createMountDocumentBody;
105
+ private createMountElementFromSelector;
106
+ private mergeDocumentScripts;
90
107
  private onMounted;
91
108
  private onUpdated;
92
109
  private onBeforeUnmount;
@@ -94,4 +111,4 @@ export declare class OneApp<TState extends object = Record<string, unknown>, TCo
94
111
  /**
95
112
  * 创建应用实例
96
113
  */
97
- export declare function createApp<TState extends object = Record<string, unknown>, TConfig extends object = Record<string, unknown>>(options?: AppOptions<TState, TConfig>): OneApp<TState, TConfig>;
114
+ export declare function createApp<TState extends object = Record<string, unknown>, TConfig extends object = Record<string, unknown>, TRootProps extends ComponentProps = ComponentProps>(options?: AppOptions<TState, TConfig, TRootProps>): OneApp<TState, TConfig, TRootProps>;
@@ -7,3 +7,4 @@ export * from './renderer';
7
7
  export * from './document';
8
8
  export * from './model';
9
9
  export * from './form';
10
+ export * from './animation';
@@ -0,0 +1,25 @@
1
+ import { HTMLNode } from '../vnode';
2
+ import type { RenderRuntimeContext, RenderStrategy, Renderable } from './types';
3
+ export declare class ElementRenderStrategy<TNode extends HTMLNode = HTMLNode> implements RenderStrategy<TNode> {
4
+ private readonly listeners;
5
+ private readonly effects;
6
+ private readonly modelBindings;
7
+ matches(vnode: Renderable): vnode is TNode;
8
+ mount(vnode: TNode, context: RenderRuntimeContext): Node;
9
+ patch(oldVNode: TNode, newVNode: TNode, currentNode: Node, context: RenderRuntimeContext): Node;
10
+ unmount(vnode: TNode, currentNode: Node, context: RenderRuntimeContext): void;
11
+ protected mountChildren(element: HTMLElement, vnode: TNode, context: RenderRuntimeContext): void;
12
+ protected updateChildren(element: HTMLElement, oldVNode: TNode, newVNode: TNode, context: RenderRuntimeContext): void;
13
+ protected unmountChildren(element: HTMLElement, vnode: TNode, context: RenderRuntimeContext): void;
14
+ private applyProps;
15
+ private applyDirections;
16
+ private updateOrdinaryChildren;
17
+ private updateKeyedChildren;
18
+ private hasOnlyKeyedChildren;
19
+ private assertNoDuplicateKeys;
20
+ private getVNodeKey;
21
+ private collectListeners;
22
+ private updateListeners;
23
+ private setupReactiveAttribute;
24
+ private trackEffect;
25
+ }
@@ -1,4 +1,5 @@
1
- import { ComponentNode, HTMLNode, SlotProvider } from './vnode';
1
+ import { ComponentNode, SlotProvider } from './vnode';
2
+ export { ElementRenderStrategy } from './renderer/element-strategy';
2
3
  export type { ComponentInstance, RenderRuntimeContext, RenderStrategy, Renderable, RendererHost, } from './renderer/types';
3
4
  import type { RenderRuntimeContext, RenderStrategy, Renderable } from './renderer/types';
4
5
  export declare class RendererContext {
@@ -40,23 +41,3 @@ export declare class SlotRenderStrategy implements RenderStrategy<SlotProvider>
40
41
  private mountSlotChildren;
41
42
  private unmountSlotChildren;
42
43
  }
43
- export declare class ElementRenderStrategy implements RenderStrategy<HTMLNode> {
44
- private readonly listeners;
45
- private readonly effects;
46
- private readonly modelBindings;
47
- matches(vnode: Renderable): vnode is HTMLNode;
48
- mount(vnode: HTMLNode, context: RenderRuntimeContext): Node;
49
- patch(oldVNode: HTMLNode, newVNode: HTMLNode, currentNode: Node, context: RenderRuntimeContext): Node;
50
- unmount(vnode: HTMLNode, currentNode: Node, context: RenderRuntimeContext): void;
51
- private applyProps;
52
- private applyDirections;
53
- private updateChildren;
54
- private updateKeyedChildren;
55
- private hasOnlyKeyedChildren;
56
- private assertNoDuplicateKeys;
57
- private getVNodeKey;
58
- private collectListeners;
59
- private updateListeners;
60
- private setupReactiveAttribute;
61
- private trackEffect;
62
- }