@geektech/tsone 0.0.2 → 0.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/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 {
@@ -78,13 +88,73 @@ 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`, and `dist`.
134
+
135
+ ```text
136
+ tsone dev [--host <host>] [--port <port>]
137
+ tsone build [--out-dir <path>]
138
+ ```
139
+
140
+ `dev` accepts host/port overrides and `build` accepts the output-directory
141
+ override; both `--port 3000` and `--port=3000` forms are valid. Build output
142
+ must remain a safe child directory inside the project root.
143
+
144
+ Programmatic tooling is imported from `@geektech/tsone-cli`, not from the
145
+ framework root. It exports `defineConfig`, `resolveConfig`, `startDevServer`,
146
+ and `build`. The dev server is owned by the caller, which must call
147
+ `server.stop()`; `build()` returns absolute `root` and `outDir` values plus
148
+ `assetsBuilt`.
149
+
150
+ The development server serves HTTP only. Proxy targets may use HTTP or HTTPS.
151
+ Rules use literal prefix matching with the longest match first, retain
152
+ query/body/end-to-end headers, optionally apply `changeOrigin` and `rewrite`,
153
+ and return `502 Bad Gateway` when the upstream is unreachable. CLI v1 has no
154
+ config plugins, WebSocket, HMR, SSR, functional config, `public/` copying, or
155
+ public minify/sourcemap settings.
156
+
157
+ ## Routing
88
158
 
89
159
  ```typescript
90
160
  import { Component, VNode, createApp } from '@geektech/tsone';
@@ -103,11 +173,11 @@ class Layout extends Component {
103
173
  children: [
104
174
  {
105
175
  component: RouterLink,
106
- props: { to: '/', children: ['首页'] },
176
+ props: { to: '/', children: ['Home'] },
107
177
  },
108
178
  {
109
179
  component: RouterLink,
110
- props: { to: '/users/42', children: ['用户'] },
180
+ props: { to: '/users/42', children: ['User'] },
111
181
  },
112
182
  { component: RouterView },
113
183
  ],
@@ -119,14 +189,18 @@ const router = createRouter({
119
189
  mode: 'history',
120
190
  routes: [
121
191
  { path: '/', component: HomePage },
122
- { path: '/users/:id', component: UserPage, meta: { title: '用户详情' } },
192
+ {
193
+ path: '/users/:id',
194
+ component: UserPage,
195
+ meta: { title: 'User Details' },
196
+ },
123
197
  ],
124
198
  });
125
199
 
126
- createApp({ root: Layout, rootElement: '#app' }).use(router).mount();
200
+ createApp({ root: Layout }).use(router).mount();
127
201
  ```
128
202
 
129
- ## 开发命令
203
+ ## Development Commands
130
204
 
131
205
  ```bash
132
206
  bun install
@@ -137,18 +211,47 @@ bun run docs
137
211
  bun run docs:build
138
212
  ```
139
213
 
140
- 文档站点内容维护在 `packages/tsone/docs/app/content/*.ts` typed content registry 中。
214
+ The root `playground/` directory contains two standalone projects:
141
215
 
142
- 生成静态文档产物:
216
+ ```bash
217
+ bun run dev
218
+ bun run dev:site
219
+ bun run dev:admin
220
+ ```
221
+
222
+ - `playground/official-site`: product website example
223
+ - `playground/admin-dashboard`: admin dashboard example
224
+
225
+ Documentation content is maintained in the typed-content registry. Chinese
226
+ content lives in `packages/tsone/docs/app/content/zh/`, while English content
227
+ lives in `packages/tsone/docs/app/content/en/`. Every logical route must exist
228
+ in both directories.
229
+
230
+ Chinese and English catalogs each contain exactly 14 logical routes. Add or
231
+ remove a route in both catalogs in the same change.
232
+
233
+ Content links stay locale-neutral: never write `/en/` manually. Chinese public
234
+ routes are unprefixed, while English routes use `/en/`. Browser-language
235
+ detection runs only at `/`; manual selection takes precedence and persists for
236
+ later visits.
237
+
238
+ Build the static documentation site with:
143
239
 
144
240
  ```bash
145
241
  bun run docs:build
146
242
  ```
147
243
 
148
- 基础 HTML 文档壳也可以由 TSone 生成,`body` 传入组件或 VNode,不传 HTML 字符串。该 API 会通过 TSone 渲染器挂载节点;在 Bun/Node 静态生成环境中,请先提供 DOM-like document:
244
+ The build fails strictly for missing, extra, duplicate, empty, or
245
+ mixed-language pages.
246
+
247
+ The base HTML document shell can also be generated from `createApp`. It emits a
248
+ `#app` mount node by default; pass `rootElement` only to use a different target.
249
+ When a custom `body` is needed, pass a component or VNode rather than an HTML
250
+ string. This API renders the mount node through TSone's renderer. In a Bun or
251
+ Node static-generation environment, provide a DOM-like document first:
149
252
 
150
253
  ```typescript
151
- import { renderHtmlDocument, type StyleSheet } from '@geektech/tsone';
254
+ import { createApp, type StyleSheet } from '@geektech/tsone';
152
255
 
153
256
  const styles: StyleSheet = [
154
257
  {
@@ -157,38 +260,55 @@ const styles: StyleSheet = [
157
260
  },
158
261
  ];
159
262
 
160
- const html = renderHtmlDocument({
161
- lang: 'zh-CN',
162
- title: 'TSone App',
163
- body: { component: App, props: { message: 'Hello TSone' } },
164
- styles,
263
+ const app = createApp({
264
+ root: App,
265
+ document: {
266
+ lang: 'en',
267
+ title: 'TSone App',
268
+ styles,
269
+ },
270
+ });
271
+
272
+ const html = app.renderHtmlDocument({
165
273
  scripts: [{ type: 'module', src: '/assets/app.js' }],
166
274
  });
167
275
  ```
168
276
 
169
- ## 公开 API
277
+ ## Public API
170
278
 
171
- 主入口 `@geektech/tsone`:
279
+ The main `@geektech/tsone` entry point exports:
172
280
 
173
281
  - `createApp(options)`
282
+ - `createApp({ root, rootProps })`
174
283
  - `Component<Props, State>`
284
+ - `TransitionGroup` / `TransitionGroupProps` / `TransitionAnimationType`
175
285
  - `VNode`
176
286
  - `h()` / `createComponent()` / `slot()` / `each()`
287
+ - `Tag(tag, options)` for arbitrary HTML elements
177
288
  - `Div()` / `Span()` / `P()` / `Button()` / `Input()`
289
+ - `Section()` / `Main()` / `Header()` / `Footer()` / `Nav()` / `Article()` /
290
+ `Aside()`
291
+ - `H1()` through `H6()` / `Strong()` / `Em()` / `Small()` / `Pre()` /
292
+ `Code()` / `Blockquote()`
293
+ - `Ul()` / `Ol()` / `Li()` / `A()` / `Img()`
294
+ - `Form()` / `Label()` / `Textarea()` / `Select()` / `Option()`
295
+ - `Table()` / `Thead()` / `Tbody()` / `Tr()` / `Th()` / `Td()`
178
296
  - `Directions` / `ModelBinding`
179
- - `InjectionKey`、组件和应用的 `provide()` / `inject()`
297
+ - `InjectionKey` and component/application `provide()` / `inject()`
180
298
  - `createForm()` / `required()` / `minLength()` / `validate()`
299
+ - `createApp(options).renderHtmlDocument(options)`
181
300
  - `renderHtmlDocument(options)`
182
301
  - `StyleSheet` / `renderStyleSheet(styles)`
183
302
  - `reactive()` / `readonly()`
184
303
  - `effect()` / `stop()`
185
304
  - `computed()`
186
305
  - `ref()` / `isRef()` / `unref()`
187
- - `version`,当前为 `0.0.2`
306
+ - `version`, currently `0.0.2`
188
307
 
189
- ## 渲染、通信与表单
308
+ ## Rendering, Communication, and Forms
190
309
 
191
- `directions.if` 可以控制元素、组件或插槽的挂载;不满足条件时会卸载节点:
310
+ `directions.if` controls whether an element, component, or slot is mounted. The
311
+ node is unmounted when the condition is false:
192
312
 
193
313
  ```typescript
194
314
  {
@@ -197,7 +317,8 @@ const html = renderHtmlDocument({
197
317
  }
198
318
  ```
199
319
 
200
- `each()` 为列表产生稳定 key,供渲染器在排序、插入和删除时复用节点:
320
+ `each()` creates stable keys for list items so the renderer can reuse nodes
321
+ during reordering, insertion, and removal:
201
322
 
202
323
  ```typescript
203
324
  const items = each(
@@ -207,7 +328,30 @@ const items = each(
207
328
  );
208
329
  ```
209
330
 
210
- 组件事件可订阅并用返回的函数取消订阅;组件 VNode 可通过 `emitters` 声明父级监听器。
331
+ `TransitionGroup` animates the enter and exit of direct keyed children:
332
+
333
+ ```typescript
334
+ {
335
+ component: TransitionGroup,
336
+ props: { tag: 'ul', type: 'fade', duration: 300 },
337
+ children: each(
338
+ this.state.items,
339
+ (item) => Li({ children: [item.label] }),
340
+ (item) => item.id
341
+ ),
342
+ }
343
+ ```
344
+
345
+ The animation types are `fade`, `slide-up`, `slide-down`, `slide-left`,
346
+ `slide-right`, and `scale`. The defaults are a `div` wrapper, `fade`, and
347
+ `300` ms with `ease` easing. Every direct child must have a unique key. Initial
348
+ children animate in, and removed children stay mounted until their exit ends.
349
+ TSone automatically disables these animations for
350
+ `prefers-reduced-motion: reduce` or when Web Animations is unavailable.
351
+ Reordering reuses and moves existing nodes without a reorder or FLIP animation.
352
+
353
+ Component events return an unsubscribe function, while component VNodes can
354
+ declare parent listeners through `emitters`:
211
355
 
212
356
  ```typescript
213
357
  const stopListening = child.on('saved', (payload) => console.log(payload));
@@ -215,7 +359,8 @@ stopListening();
215
359
  // { component: Editor, emitters: { saved: (payload) => this.save(payload) } }
216
360
  ```
217
361
 
218
- 依赖注入从当前组件向父级再到应用实例查找:
362
+ Dependency injection resolves values from the current component, then its
363
+ parents, and finally the application instance:
219
364
 
220
365
  ```typescript
221
366
  const THEME: InjectionKey<{ mode: string }> = Symbol('theme');
@@ -223,7 +368,8 @@ app.provide(THEME, { mode: 'dark' });
223
368
  const theme = this.inject(THEME, { mode: 'light' });
224
369
  ```
225
370
 
226
- `directions.model` 支持点分隔路径和转换函数,原生 input、textarea、checkbox、radio select 会同步:
371
+ `directions.model` supports dot-separated paths and conversion functions.
372
+ Native input, textarea, checkbox, radio, and select controls stay synchronized:
227
373
 
228
374
  ```typescript
229
375
  Input({
@@ -238,13 +384,13 @@ Input({
238
384
  });
239
385
  ```
240
386
 
241
- 校验是纯函数,不负责错误 UI 或提交:
387
+ Validation uses pure functions and does not own error UI or submission:
242
388
 
243
389
  ```typescript
244
390
  const form = createForm(this.state, {
245
- 'profile.name': [required('请输入姓名'), minLength(2)],
391
+ 'profile.name': [required('Name is required'), minLength(2)],
246
392
  'profile.age': [
247
- validate((value) => Number(value) >= 18 || '年龄须不小于 18'),
393
+ validate((value) => Number(value) >= 18 || 'Age must be at least 18'),
248
394
  ],
249
395
  });
250
396
 
@@ -252,7 +398,7 @@ const result = form.validate();
252
398
  form.resetErrors();
253
399
  ```
254
400
 
255
- 路由入口 `@geektech/tsone/router`:
401
+ The `@geektech/tsone/router` entry point exports:
256
402
 
257
403
  - `createRouter({ routes, mode, base })`
258
404
  - `Router`
@@ -262,12 +408,12 @@ form.resetErrors();
262
408
  - `RouteRecord`
263
409
  - `RouteLocation`
264
410
 
265
- 样式入口 `@geektech/tsone/style`:
411
+ The `@geektech/tsone/style` entry point exports:
266
412
 
267
413
  - `StyleManager`
268
414
  - `StyleSheet` / `renderStyleSheet(styles)`
269
415
 
270
- ## 发布前检查
416
+ ## Pre-Publish Checklist
271
417
 
272
418
  ```bash
273
419
  bun test
@@ -276,10 +422,11 @@ bun run build
276
422
  bun pm pack --cwd packages/tsone --dry-run
277
423
  ```
278
424
 
279
- ## 贡献
425
+ ## Contributing
280
426
 
281
- 欢迎提交 Issue Pull Request。开源发布前请确保测试、类型检查和构建均通过。
427
+ Issues and pull requests are welcome. Before publishing an open-source release,
428
+ make sure the tests, type checks, and build all pass.
282
429
 
283
- ## 许可证
430
+ ## License
284
431
 
285
432
  [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
- }
@@ -57,11 +57,47 @@ export declare function isComponentNode(vnode: VNode): vnode is ComponentNode;
57
57
  export declare function isHTMLNode(vnode: VNode): vnode is HTMLNode;
58
58
  export declare function isSlotProvider(vnode: VNode): vnode is SlotProvider;
59
59
  export declare function h(tag: string, props?: HTMLProps, children?: Array<VNode | string>, listeners?: EventListeners, key?: string | number, directions?: Directions): HTMLNode;
60
+ export declare function Tag(tag: string, options?: ElementShortcutOptions): HTMLNode;
60
61
  export declare const Div: ElementShortcut;
61
62
  export declare const Span: ElementShortcut;
62
63
  export declare const P: ElementShortcut;
63
64
  export declare const Button: ElementShortcut;
64
65
  export declare const Input: ElementShortcut;
66
+ export declare const Section: ElementShortcut;
67
+ export declare const Main: ElementShortcut;
68
+ export declare const Header: ElementShortcut;
69
+ export declare const Footer: ElementShortcut;
70
+ export declare const Nav: ElementShortcut;
71
+ export declare const Article: ElementShortcut;
72
+ export declare const Aside: ElementShortcut;
73
+ export declare const H1: ElementShortcut;
74
+ export declare const H2: ElementShortcut;
75
+ export declare const H3: ElementShortcut;
76
+ export declare const H4: ElementShortcut;
77
+ export declare const H5: ElementShortcut;
78
+ export declare const H6: ElementShortcut;
79
+ export declare const Strong: ElementShortcut;
80
+ export declare const Em: ElementShortcut;
81
+ export declare const Small: ElementShortcut;
82
+ export declare const Pre: ElementShortcut;
83
+ export declare const Code: ElementShortcut;
84
+ export declare const Blockquote: ElementShortcut;
85
+ export declare const Ul: ElementShortcut;
86
+ export declare const Ol: ElementShortcut;
87
+ export declare const Li: ElementShortcut;
88
+ export declare const A: ElementShortcut;
89
+ export declare const Img: ElementShortcut;
90
+ export declare const Form: ElementShortcut;
91
+ export declare const Label: ElementShortcut;
92
+ export declare const Textarea: ElementShortcut;
93
+ export declare const Select: ElementShortcut;
94
+ export declare const Option: ElementShortcut;
95
+ export declare const Table: ElementShortcut;
96
+ export declare const Thead: ElementShortcut;
97
+ export declare const Tbody: ElementShortcut;
98
+ export declare const Tr: ElementShortcut;
99
+ export declare const Th: ElementShortcut;
100
+ export declare const Td: ElementShortcut;
65
101
  export declare function createComponent<P extends VNodeComponentProps>(componentClass: ComponentConstructor<P>, props?: P, children?: Array<VNode | string>, key?: string | number, directions?: Directions): ComponentNode<P>;
66
102
  export declare function slot(name: string, key?: string | number, directions?: Directions): SlotProvider;
67
103
  export declare function each<T>(items: readonly T[], render: (item: T, index: number) => VNode | string, key: (item: T, index: number) => string | number): VNode[];