@ydant/core 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/LICENSE +21 -0
- package/README.md +194 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.es.js +131 -0
- package/dist/index.umd.js +1 -0
- package/dist/mount.d.ts +24 -0
- package/dist/plugin.d.ts +79 -0
- package/dist/render/context.d.ts +24 -0
- package/dist/render/index.d.ts +9 -0
- package/dist/render/iterator.d.ts +9 -0
- package/dist/render/types.d.ts +30 -0
- package/dist/types.d.ts +96 -0
- package/dist/utils.d.ts +8 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 cwd-k2
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# @ydant/core
|
|
2
|
+
|
|
3
|
+
Rendering engine and plugin system for Ydant.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
**@ydant/core is a pure engine that doesn't know "what" to render.**
|
|
8
|
+
|
|
9
|
+
Core provides only:
|
|
10
|
+
|
|
11
|
+
- Generator processing
|
|
12
|
+
- Plugin dispatch
|
|
13
|
+
- Context management
|
|
14
|
+
|
|
15
|
+
It doesn't assume DOM existence. All concrete operations (DOM manipulation, lifecycle, keyed elements) are delegated to plugins. This separation keeps core's API surface minimal and stable, allowing plugins to extend functionality without modifying core.
|
|
16
|
+
|
|
17
|
+
For user-facing APIs like element factories and primitives, see `@ydant/base`.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @ydant/core
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { mount } from "@ydant/core";
|
|
29
|
+
import { createBasePlugin, div, text, type Component } from "@ydant/base";
|
|
30
|
+
|
|
31
|
+
const App: Component = () => div(() => [text("Hello!")]);
|
|
32
|
+
|
|
33
|
+
mount(App, document.getElementById("app")!, {
|
|
34
|
+
plugins: [createBasePlugin()],
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
|
|
40
|
+
### Mount
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
function mount(component: Component, container: HTMLElement, options?: MountOptions): void;
|
|
44
|
+
|
|
45
|
+
interface MountOptions {
|
|
46
|
+
plugins?: Plugin[];
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Plugin System
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
interface Plugin {
|
|
54
|
+
name: string;
|
|
55
|
+
types: string[];
|
|
56
|
+
/** Plugin names that must be registered before this plugin */
|
|
57
|
+
dependencies?: string[];
|
|
58
|
+
/** Initialize plugin-specific properties in RenderContext */
|
|
59
|
+
initContext?(ctx: Record<string, unknown>, parentCtx?: Record<string, unknown>): void;
|
|
60
|
+
/** Extend PluginAPI with plugin-specific methods */
|
|
61
|
+
extendAPI?(api: Record<string, unknown>, ctx: Record<string, unknown>): void;
|
|
62
|
+
/** Merge parent context into child context (called when creating child contexts) */
|
|
63
|
+
mergeChildContext?(childCtx: Record<string, unknown>, parentCtx: Record<string, unknown>): void;
|
|
64
|
+
/** Process a child element */
|
|
65
|
+
process(child: Child, api: PluginAPI): PluginResult;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface PluginResult {
|
|
69
|
+
value?: unknown; // Value to pass back via next()
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Types
|
|
74
|
+
|
|
75
|
+
| Type | Description |
|
|
76
|
+
| ------------------ | --------------------------------------------------------------------- |
|
|
77
|
+
| `Tagged<T,P>` | Helper type for tagged unions: `{ type: T } & P` |
|
|
78
|
+
| `Child` | Union of all yieldable types (extended by plugins) |
|
|
79
|
+
| `ChildNext` | Union of values passed via `next()` (extended by plugins) |
|
|
80
|
+
| `ChildReturn` | Union of return values (extended by plugins) |
|
|
81
|
+
| `Render` | `Generator<Child, ChildReturn, ChildNext>` - Base rendering generator |
|
|
82
|
+
| `Component` | `() => Render` - Root component type |
|
|
83
|
+
| `ComponentWith<P>` | `(props: P) => Render` - Component type with props |
|
|
84
|
+
| `Builder` | `() => Instructor \| Instruction[]` - Element factory argument |
|
|
85
|
+
| `Instructor` | `Iterator<Child, ChildReturn, ChildNext>` - Internal iterator |
|
|
86
|
+
| `Instruction` | `Generator<Child, ChildReturn, ChildNext>` - Primitive return type |
|
|
87
|
+
|
|
88
|
+
### Plugin Extension Interfaces
|
|
89
|
+
|
|
90
|
+
Plugins can extend these interfaces via module augmentation:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
declare module "@ydant/core" {
|
|
94
|
+
// Extend RenderContext with custom properties
|
|
95
|
+
interface RenderContextExtensions {
|
|
96
|
+
myProperty: MyType;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Extend PluginAPI with custom methods
|
|
100
|
+
interface PluginAPIExtensions {
|
|
101
|
+
myMethod(): void;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Extend Child types (yieldable values)
|
|
105
|
+
interface PluginChildExtensions {
|
|
106
|
+
MyType: Tagged<"mytype", { value: string }>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Extend values passed via next()
|
|
110
|
+
interface PluginNextExtensions {
|
|
111
|
+
MyResult: MyResultType;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Extend return values
|
|
115
|
+
interface PluginReturnExtensions {
|
|
116
|
+
MyResult: MyResultType;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### RenderContext
|
|
122
|
+
|
|
123
|
+
The rendering context holds state during rendering. Plugins extend it via `RenderContextExtensions`:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
interface RenderContextCore {
|
|
127
|
+
parent: Node;
|
|
128
|
+
currentElement: Element | null;
|
|
129
|
+
plugins: Map<string, Plugin>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type RenderContext = RenderContextCore & RenderContextExtensions;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Utilities
|
|
136
|
+
|
|
137
|
+
| Function | Description |
|
|
138
|
+
| ---------------------- | -------------------------------------- |
|
|
139
|
+
| `isTagged(value, tag)` | Type guard for tagged union types |
|
|
140
|
+
| `toChildren(result)` | Normalize array/iterator to Instructor |
|
|
141
|
+
|
|
142
|
+
## Creating Plugins
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import type { Plugin, PluginAPI, PluginResult, Child } from "@ydant/core";
|
|
146
|
+
|
|
147
|
+
// 1. Declare type extensions
|
|
148
|
+
declare module "@ydant/core" {
|
|
149
|
+
interface RenderContextExtensions {
|
|
150
|
+
myData: Map<string, unknown>;
|
|
151
|
+
}
|
|
152
|
+
interface PluginAPIExtensions {
|
|
153
|
+
getMyData(key: string): unknown;
|
|
154
|
+
setMyData(key: string, value: unknown): void;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 2. Create the plugin
|
|
159
|
+
export function createMyPlugin(): Plugin {
|
|
160
|
+
return {
|
|
161
|
+
name: "my-plugin",
|
|
162
|
+
types: ["mytype"],
|
|
163
|
+
dependencies: ["base"], // Ensure base plugin is registered first
|
|
164
|
+
|
|
165
|
+
// Initialize context properties
|
|
166
|
+
initContext(ctx, parentCtx) {
|
|
167
|
+
ctx.myData = parentCtx?.myData
|
|
168
|
+
? new Map(parentCtx.myData as Map<string, unknown>)
|
|
169
|
+
: new Map();
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
// Merge parent context into child context
|
|
173
|
+
mergeChildContext(childCtx, parentCtx) {
|
|
174
|
+
childCtx.myData = new Map(parentCtx.myData as Map<string, unknown>);
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// Extend PluginAPI with methods
|
|
178
|
+
extendAPI(api, ctx) {
|
|
179
|
+
const myData = ctx.myData as Map<string, unknown>;
|
|
180
|
+
api.getMyData = (key: string) => myData.get(key);
|
|
181
|
+
api.setMyData = (key: string, value: unknown) => myData.set(key, value);
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
// Process child elements
|
|
185
|
+
process(child: Child, api: PluginAPI): PluginResult {
|
|
186
|
+
if ((child as { type: string }).type === "mytype") {
|
|
187
|
+
// Process the child using api.getMyData(), api.setMyData()
|
|
188
|
+
return { value: result };
|
|
189
|
+
}
|
|
190
|
+
return {};
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ydant/core
|
|
3
|
+
*
|
|
4
|
+
* 純粋な DSL 処理系とプラグインシステム
|
|
5
|
+
*/
|
|
6
|
+
export type { CleanupFn, Tagged, Child, ChildOfType, ChildNext, ChildReturn, Builder, Instructor, Instruction, Primitive, ChildContent, Render, Component, PluginChildExtensions, PluginNextExtensions, PluginReturnExtensions, } from './types';
|
|
7
|
+
export type { Plugin, PluginAPI, PluginAPIExtensions, PluginResult, MountOptions } from './plugin';
|
|
8
|
+
export { isTagged, toChildren } from './utils';
|
|
9
|
+
export { mount } from './mount';
|
|
10
|
+
export { render, processIterator, createRenderContext, createPluginAPIFactory } from './render';
|
|
11
|
+
export type { RenderContext, RenderContextCore, RenderContextExtensions } from './render';
|
package/dist/index.es.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
function C(t, o) {
|
|
2
|
+
return t.type === o;
|
|
3
|
+
}
|
|
4
|
+
function y(t) {
|
|
5
|
+
return Array.isArray(t) ? (function* () {
|
|
6
|
+
for (const o of t)
|
|
7
|
+
yield* o;
|
|
8
|
+
})() : t;
|
|
9
|
+
}
|
|
10
|
+
function p(t, o) {
|
|
11
|
+
const n = /* @__PURE__ */ new Set();
|
|
12
|
+
for (const i of t.values())
|
|
13
|
+
n.has(i.name) || (n.add(i.name), o(i));
|
|
14
|
+
}
|
|
15
|
+
function A(t, o, n) {
|
|
16
|
+
return {
|
|
17
|
+
parent: t,
|
|
18
|
+
currentElement: o,
|
|
19
|
+
plugins: n
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function d(t, o, n, i) {
|
|
23
|
+
const r = A(t, o, n);
|
|
24
|
+
return p(n, (e) => {
|
|
25
|
+
e.initContext?.(r, i);
|
|
26
|
+
}), r;
|
|
27
|
+
}
|
|
28
|
+
function g(t) {
|
|
29
|
+
return function o(n) {
|
|
30
|
+
if (n._cachedAPI)
|
|
31
|
+
return n._cachedAPI;
|
|
32
|
+
const i = {
|
|
33
|
+
// ========================================================================
|
|
34
|
+
// コア機能(プラグインが拡張する基盤)
|
|
35
|
+
// ========================================================================
|
|
36
|
+
get parent() {
|
|
37
|
+
return n.parent;
|
|
38
|
+
},
|
|
39
|
+
get currentElement() {
|
|
40
|
+
return n.currentElement;
|
|
41
|
+
},
|
|
42
|
+
processChildren(e, u) {
|
|
43
|
+
const l = u?.parent ?? n.parent, s = d(
|
|
44
|
+
l,
|
|
45
|
+
l instanceof globalThis.Element ? l : null,
|
|
46
|
+
n.plugins,
|
|
47
|
+
n
|
|
48
|
+
), c = y(e());
|
|
49
|
+
t(c, s), p(n.plugins, (P) => {
|
|
50
|
+
P.mergeChildContext?.(n, s);
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
createChildAPI(e) {
|
|
54
|
+
const u = d(
|
|
55
|
+
e,
|
|
56
|
+
e instanceof globalThis.Element ? e : null,
|
|
57
|
+
n.plugins,
|
|
58
|
+
n
|
|
59
|
+
);
|
|
60
|
+
return o(u);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
p(n.plugins, (e) => {
|
|
64
|
+
e.extendAPI?.(i, n);
|
|
65
|
+
});
|
|
66
|
+
const r = i;
|
|
67
|
+
return n._cachedAPI = r, r;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
let a = null;
|
|
71
|
+
function h(t, o) {
|
|
72
|
+
a || (a = g(h));
|
|
73
|
+
let n = t.next();
|
|
74
|
+
for (; !n.done; ) {
|
|
75
|
+
const i = n.value;
|
|
76
|
+
if (i && typeof i == "object" && "type" in i) {
|
|
77
|
+
const r = i.type, e = o.plugins.get(r);
|
|
78
|
+
if (e) {
|
|
79
|
+
const u = a(o), l = e.process(i, u);
|
|
80
|
+
n = t.next(l.value);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
n = t.next();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let f = null;
|
|
88
|
+
function m(t, o, n) {
|
|
89
|
+
o.innerHTML = "";
|
|
90
|
+
const i = d(o, null, n);
|
|
91
|
+
f || (f = g(h));
|
|
92
|
+
let r = t.next();
|
|
93
|
+
for (; !r.done; ) {
|
|
94
|
+
const e = r.value;
|
|
95
|
+
if (e && typeof e == "object" && "type" in e) {
|
|
96
|
+
const u = e.type, l = n.get(u);
|
|
97
|
+
if (l) {
|
|
98
|
+
const s = f(i), c = l.process(e, s);
|
|
99
|
+
r = t.next(c.value);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
r = t.next(void 0);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function I(t, o, n) {
|
|
107
|
+
const i = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Set();
|
|
108
|
+
if (n?.plugins) {
|
|
109
|
+
for (const e of n.plugins) {
|
|
110
|
+
r.add(e.name);
|
|
111
|
+
for (const u of e.types)
|
|
112
|
+
i.set(u, e);
|
|
113
|
+
}
|
|
114
|
+
for (const e of n.plugins)
|
|
115
|
+
if (e.dependencies)
|
|
116
|
+
for (const u of e.dependencies)
|
|
117
|
+
r.has(u) || console.warn(
|
|
118
|
+
`[ydant] Plugin "${e.name}" depends on "${u}", but "${u}" is not registered.`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
m(t(), o, i);
|
|
122
|
+
}
|
|
123
|
+
export {
|
|
124
|
+
g as createPluginAPIFactory,
|
|
125
|
+
d as createRenderContext,
|
|
126
|
+
C as isTagged,
|
|
127
|
+
I as mount,
|
|
128
|
+
h as processIterator,
|
|
129
|
+
m as render,
|
|
130
|
+
y as toChildren
|
|
131
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(l,s){typeof exports=="object"&&typeof module<"u"?s(exports):typeof define=="function"&&define.amd?define(["exports"],s):(l=typeof globalThis<"u"?globalThis:l||self,s(l.YdantCore={}))})(this,(function(l){"use strict";function s(t,i){return t.type===i}function m(t){return Array.isArray(t)?(function*(){for(const i of t)yield*i})():t}function d(t,i){const e=new Set;for(const o of t.values())e.has(o.name)||(e.add(o.name),i(o))}function A(t,i,e){return{parent:t,currentElement:i,plugins:e}}function a(t,i,e,o){const u=A(t,i,e);return d(e,n=>{n.initContext?.(u,o)}),u}function p(t){return function i(e){if(e._cachedAPI)return e._cachedAPI;const o={get parent(){return e.parent},get currentElement(){return e.currentElement},processChildren(n,r){const c=r?.parent??e.parent,f=a(c,c instanceof globalThis.Element?c:null,e.plugins,e),P=m(n());t(P,f),d(e.plugins,v=>{v.mergeChildContext?.(e,f)})},createChildAPI(n){const r=a(n,n instanceof globalThis.Element?n:null,e.plugins,e);return i(r)}};d(e.plugins,n=>{n.extendAPI?.(o,e)});const u=o;return e._cachedAPI=u,u}}let g=null;function h(t,i){g||(g=p(h));let e=t.next();for(;!e.done;){const o=e.value;if(o&&typeof o=="object"&&"type"in o){const u=o.type,n=i.plugins.get(u);if(n){const r=g(i),c=n.process(o,r);e=t.next(c.value);continue}}e=t.next()}}let y=null;function C(t,i,e){i.innerHTML="";const o=a(i,null,e);y||(y=p(h));let u=t.next();for(;!u.done;){const n=u.value;if(n&&typeof n=="object"&&"type"in n){const r=n.type,c=e.get(r);if(c){const f=y(o),P=c.process(n,f);u=t.next(P.value);continue}}u=t.next(void 0)}}function I(t,i,e){const o=new Map,u=new Set;if(e?.plugins){for(const n of e.plugins){u.add(n.name);for(const r of n.types)o.set(r,n)}for(const n of e.plugins)if(n.dependencies)for(const r of n.dependencies)u.has(r)||console.warn(`[ydant] Plugin "${n.name}" depends on "${r}", but "${r}" is not registered.`)}C(t(),i,o)}l.createPluginAPIFactory=p,l.createRenderContext=a,l.isTagged=s,l.mount=I,l.processIterator=h,l.render=C,l.toChildren=m,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
|
package/dist/mount.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { MountOptions } from './plugin';
|
|
2
|
+
import { Render } from './types';
|
|
3
|
+
type Component = () => Render;
|
|
4
|
+
/**
|
|
5
|
+
* Component を DOM にマウントする
|
|
6
|
+
*
|
|
7
|
+
* @param app - マウントするコンポーネント
|
|
8
|
+
* @param parent - マウント先の DOM 要素
|
|
9
|
+
* @param options - マウントオプション(プラグインなど)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { mount } from "@ydant/core";
|
|
14
|
+
* import { createBasePlugin, div, text } from "@ydant/base";
|
|
15
|
+
*
|
|
16
|
+
* const App = () => div(() => [text("Hello!")]);
|
|
17
|
+
*
|
|
18
|
+
* mount(App, document.getElementById("app")!, {
|
|
19
|
+
* plugins: [createBasePlugin()],
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function mount(app: Component, parent: HTMLElement, options?: MountOptions): void;
|
|
24
|
+
export {};
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Child, ChildNext } from './types';
|
|
2
|
+
import { RenderContext, RenderContextCore, RenderContextExtensions } from './render/types';
|
|
3
|
+
/**
|
|
4
|
+
* プラグインが PluginAPI を拡張するためのインターフェース
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* // @ydant/base で DOM 操作用のメソッドを追加
|
|
9
|
+
* declare module "@ydant/core" {
|
|
10
|
+
* interface PluginAPIExtensions extends BasePluginAPI {}
|
|
11
|
+
* }
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export interface PluginAPIExtensions {
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* プラグインが使用できる API
|
|
18
|
+
*
|
|
19
|
+
* 実際のメソッドは @ydant/base の PluginAPIExtensions 拡張で定義される
|
|
20
|
+
*/
|
|
21
|
+
export type PluginAPI = PluginAPIExtensions;
|
|
22
|
+
/**
|
|
23
|
+
* プラグインの処理結果
|
|
24
|
+
*/
|
|
25
|
+
export interface PluginResult {
|
|
26
|
+
/** ジェネレータに返す値 */
|
|
27
|
+
value?: ChildNext | undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* DOM レンダラープラグイン
|
|
31
|
+
*/
|
|
32
|
+
export interface Plugin {
|
|
33
|
+
/** プラグイン識別子 */
|
|
34
|
+
readonly name: string;
|
|
35
|
+
/** このプラグインが処理する type タグの配列 */
|
|
36
|
+
readonly types: readonly string[];
|
|
37
|
+
/** 依存するプラグインの name 配列 */
|
|
38
|
+
readonly dependencies?: readonly string[];
|
|
39
|
+
/**
|
|
40
|
+
* RenderContext を初期化する
|
|
41
|
+
*
|
|
42
|
+
* mount 時および子コンテキスト作成時に呼び出される。
|
|
43
|
+
* プラグインは RenderContextExtensions で定義した独自プロパティを
|
|
44
|
+
* ここで初期化する。
|
|
45
|
+
*
|
|
46
|
+
* @param ctx - 初期化対象のコンテキスト(構築途中のため Partial)
|
|
47
|
+
* @param parentCtx - 親コンテキスト(ルートの場合は undefined)
|
|
48
|
+
*/
|
|
49
|
+
initContext?(ctx: RenderContextCore & Partial<RenderContextExtensions>, parentCtx?: RenderContext): void;
|
|
50
|
+
/**
|
|
51
|
+
* PluginAPI を拡張する
|
|
52
|
+
*
|
|
53
|
+
* プラグイン固有のメソッドを PluginAPI に追加する。
|
|
54
|
+
* PluginAPIExtensions で定義した独自のメソッドをここで実装する。
|
|
55
|
+
*
|
|
56
|
+
* @param api - 拡張対象の PluginAPI オブジェクト
|
|
57
|
+
* @param ctx - 現在の RenderContext(initContext 後なので構築済み)
|
|
58
|
+
*/
|
|
59
|
+
extendAPI?(api: Partial<PluginAPIExtensions>, ctx: RenderContext): void;
|
|
60
|
+
/**
|
|
61
|
+
* 子コンテキストの状態を親コンテキストにマージする
|
|
62
|
+
*
|
|
63
|
+
* processChildren 内で子イテレータ処理後に呼び出される。
|
|
64
|
+
* プラグインは子コンテキストから親コンテキストへの状態伝搬をここで実装する。
|
|
65
|
+
*
|
|
66
|
+
* @param parentCtx - 親コンテキスト
|
|
67
|
+
* @param childCtx - 子コンテキスト
|
|
68
|
+
*/
|
|
69
|
+
mergeChildContext?(parentCtx: RenderContext, childCtx: RenderContext): void;
|
|
70
|
+
/** Child を処理する */
|
|
71
|
+
process(child: Child, api: PluginAPI): PluginResult;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* mount のオプション
|
|
75
|
+
*/
|
|
76
|
+
export interface MountOptions {
|
|
77
|
+
/** 使用するプラグインの配列 */
|
|
78
|
+
plugins?: Plugin[];
|
|
79
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Instructor } from '../types';
|
|
2
|
+
import { Plugin, PluginAPI } from '../plugin';
|
|
3
|
+
import { RenderContext, RenderContextCore } from './types';
|
|
4
|
+
/** RenderContext のコア部分を作成 */
|
|
5
|
+
export declare function createRenderContextCore(parent: Node, currentElement: globalThis.Element | null, plugins: Map<string, Plugin>): RenderContextCore;
|
|
6
|
+
/**
|
|
7
|
+
* RenderContext を作成し、各プラグインで初期化
|
|
8
|
+
*
|
|
9
|
+
* @param parent - 親ノード
|
|
10
|
+
* @param currentElement - 現在の要素
|
|
11
|
+
* @param plugins - 登録されたプラグイン
|
|
12
|
+
* @param parentCtx - 親コンテキスト(子コンテキスト作成時)
|
|
13
|
+
*/
|
|
14
|
+
export declare function createRenderContext(parent: Node, currentElement: globalThis.Element | null, plugins: Map<string, Plugin>, parentCtx?: RenderContext): RenderContext;
|
|
15
|
+
/**
|
|
16
|
+
* RenderContext から PluginAPI を作成
|
|
17
|
+
*
|
|
18
|
+
* processIterator を循環参照で受け取る必要があるため、
|
|
19
|
+
* ファクトリ関数として実装
|
|
20
|
+
*
|
|
21
|
+
* NOTE: core は基本的な API のみを提供し、プラグイン固有のメソッドは
|
|
22
|
+
* 各プラグインの extendAPI で追加される。
|
|
23
|
+
*/
|
|
24
|
+
export declare function createPluginAPIFactory(processIterator: (iter: Instructor, ctx: RenderContext) => void): (ctx: RenderContext) => PluginAPI;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Render } from '../types';
|
|
2
|
+
import { Plugin } from '../plugin';
|
|
3
|
+
/**
|
|
4
|
+
* Render(ジェネレータ)を DOM に描画
|
|
5
|
+
*/
|
|
6
|
+
export declare function render(gen: Render, parent: HTMLElement, plugins: Map<string, Plugin>): void;
|
|
7
|
+
export { processIterator } from './iterator';
|
|
8
|
+
export { createRenderContext, createPluginAPIFactory } from './context';
|
|
9
|
+
export type { RenderContext, RenderContextCore, RenderContextExtensions } from './types';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Instructor } from '../types';
|
|
2
|
+
import { RenderContext } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Child イテレータを処理し、DOM に反映する
|
|
5
|
+
*
|
|
6
|
+
* すべての type はプラグインで処理される。
|
|
7
|
+
* 対応するプラグインがない type はスキップされる。
|
|
8
|
+
*/
|
|
9
|
+
export declare function processIterator(iter: Instructor, ctx: RenderContext): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Plugin } from '../plugin';
|
|
2
|
+
/**
|
|
3
|
+
* プラグインが RenderContext を拡張するためのインターフェース
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* // @ydant/base で keyed 要素管理用のプロパティを追加
|
|
8
|
+
* declare module "@ydant/core" {
|
|
9
|
+
* interface RenderContextExtensions {
|
|
10
|
+
* pendingKey: string | number | null;
|
|
11
|
+
* keyedNodes: Map<string | number, unknown>;
|
|
12
|
+
* }
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export interface RenderContextExtensions {
|
|
17
|
+
}
|
|
18
|
+
/** レンダリングコンテキスト(コア部分) */
|
|
19
|
+
export interface RenderContextCore {
|
|
20
|
+
/** 親ノード */
|
|
21
|
+
parent: Node;
|
|
22
|
+
/** 現在処理中の要素 */
|
|
23
|
+
currentElement: globalThis.Element | null;
|
|
24
|
+
/** 登録されたプラグイン */
|
|
25
|
+
plugins: Map<string, Plugin>;
|
|
26
|
+
/** キャッシュされた PluginAPI(内部用) */
|
|
27
|
+
_cachedAPI?: import('../plugin').PluginAPI;
|
|
28
|
+
}
|
|
29
|
+
/** レンダリングコンテキスト */
|
|
30
|
+
export type RenderContext = RenderContextCore & RenderContextExtensions;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** Tagged Union を作成するヘルパー型 */
|
|
2
|
+
export type Tagged<T extends string, P = {}> = {
|
|
3
|
+
type: T;
|
|
4
|
+
} & P;
|
|
5
|
+
/** ライフサイクルや副作用のクリーンアップ関数 */
|
|
6
|
+
export type CleanupFn = () => void;
|
|
7
|
+
/**
|
|
8
|
+
* プラグインが Child 型を拡張するためのインターフェース
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* // @ydant/base で Element 型を追加
|
|
13
|
+
* declare module "@ydant/core" {
|
|
14
|
+
* interface PluginChildExtensions {
|
|
15
|
+
* Element: Tagged<"element", { tag: string; children: Instructor }>;
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export interface PluginChildExtensions {
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* プラグインが next() に渡す値の型を拡張するためのインターフェース
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* // @ydant/base で Slot を追加
|
|
28
|
+
* declare module "@ydant/core" {
|
|
29
|
+
* interface PluginNextExtensions {
|
|
30
|
+
* Slot: Slot;
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export interface PluginNextExtensions {
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* プラグインが return で返す値の型を拡張するためのインターフェース
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // @ydant/base で Slot を追加
|
|
43
|
+
* declare module "@ydant/core" {
|
|
44
|
+
* interface PluginReturnExtensions {
|
|
45
|
+
* Slot: Slot;
|
|
46
|
+
* }
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export interface PluginReturnExtensions {
|
|
51
|
+
}
|
|
52
|
+
/** 子要素として yield できるもの(プラグインによって拡張可能) */
|
|
53
|
+
export type Child = PluginChildExtensions[keyof PluginChildExtensions];
|
|
54
|
+
/** Child から特定の type を抽出するヘルパー型 */
|
|
55
|
+
export type ChildOfType<T extends string> = Extract<Child, {
|
|
56
|
+
type: T;
|
|
57
|
+
}>;
|
|
58
|
+
/** next() に渡される値の型(プラグインによって拡張可能) */
|
|
59
|
+
export type ChildNext = void | PluginNextExtensions[keyof PluginNextExtensions];
|
|
60
|
+
/** return で返される値の型(プラグインによって拡張可能) */
|
|
61
|
+
export type ChildReturn = void | PluginReturnExtensions[keyof PluginReturnExtensions];
|
|
62
|
+
/** レンダリング命令の Iterator(内部処理用) */
|
|
63
|
+
export type Instructor = Iterator<Child, ChildReturn, ChildNext>;
|
|
64
|
+
/** レンダリング命令(text, attr, on 等)の戻り値型 */
|
|
65
|
+
export type Instruction = Generator<Child, ChildReturn, ChildNext>;
|
|
66
|
+
/** 要素ファクトリ(div, span 等)の引数型 */
|
|
67
|
+
export type Builder = () => Instructor | Instruction[];
|
|
68
|
+
/** 副作用のみを実行する DSL プリミティブの戻り値型 */
|
|
69
|
+
export type Primitive<T extends Child> = Generator<T, void, void>;
|
|
70
|
+
/** コンポーネントの children として渡すビルダー関数の戻り値型 */
|
|
71
|
+
export type ChildContent = Generator<Child, unknown, ChildNext>;
|
|
72
|
+
/**
|
|
73
|
+
* Element を yield し、最終的に ChildReturn を返すジェネレーター
|
|
74
|
+
*
|
|
75
|
+
* base パッケージでは Slot が PluginReturnExtensions に追加されるため、
|
|
76
|
+
* より具体的な型 (Generator<Child, Slot, Slot>) として使用される
|
|
77
|
+
*/
|
|
78
|
+
export type Render = Generator<Child, ChildReturn, ChildNext>;
|
|
79
|
+
/**
|
|
80
|
+
* コンポーネント型
|
|
81
|
+
*
|
|
82
|
+
* - `Component` — 引数なし `() => Render`
|
|
83
|
+
* - `Component<Props>` — Props を受け取る `(props: Props) => Render`
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const App: Component = () => div(function* () { ... });
|
|
88
|
+
*
|
|
89
|
+
* interface CounterProps { initial: number }
|
|
90
|
+
* const Counter: Component<CounterProps> = (props) =>
|
|
91
|
+
* div(function* () {
|
|
92
|
+
* yield* text(`Count: ${props.initial}`);
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export type Component<P = void> = [P] extends [void] ? () => Render : (props: P) => Render;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Tagged, Child, ChildOfType, Instruction, Instructor } from './types';
|
|
2
|
+
/** Tagged 型の判定関数(Child に対する型ガード付きオーバーロード) */
|
|
3
|
+
export declare function isTagged<T extends Child["type"]>(value: Child, tag: T): value is ChildOfType<T>;
|
|
4
|
+
export declare function isTagged<T extends string>(value: {
|
|
5
|
+
type: string;
|
|
6
|
+
}, tag: T): value is Tagged<T, Record<string, unknown>>;
|
|
7
|
+
/** Builder の結果を Instructor に正規化する */
|
|
8
|
+
export declare function toChildren(result: Instructor | Instruction[]): Instructor;
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ydant/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Generator-based DOM rendering DSL",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"dsl",
|
|
7
|
+
"generator",
|
|
8
|
+
"rendering",
|
|
9
|
+
"ui",
|
|
10
|
+
"ydant"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/cwd-k2/ydant#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/cwd-k2/ydant/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "cwd-k2",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/cwd-k2/ydant.git",
|
|
21
|
+
"directory": "packages/core"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"main": "./dist/index.umd.js",
|
|
29
|
+
"module": "./dist/index.es.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"@ydant/dev": {
|
|
35
|
+
"types": "./src/index.ts",
|
|
36
|
+
"default": "./src/index.ts"
|
|
37
|
+
},
|
|
38
|
+
"import": "./dist/index.es.js",
|
|
39
|
+
"require": "./dist/index.umd.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "vite build",
|
|
44
|
+
"typecheck": "tsc --noEmit"
|
|
45
|
+
}
|
|
46
|
+
}
|