amateras 0.1.1 → 0.3.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.
Files changed (68) hide show
  1. package/README.md +25 -8
  2. package/ext/css/src/index.ts +103 -49
  3. package/ext/css/src/lib/colorAssign.ts +6 -0
  4. package/ext/css/src/lib/colors/amber.ts +25 -0
  5. package/ext/css/src/lib/colors/blackwhite.ts +13 -0
  6. package/ext/css/src/lib/colors/blue.ts +25 -0
  7. package/ext/css/src/lib/colors/cyan.ts +25 -0
  8. package/ext/css/src/lib/colors/emerald.ts +25 -0
  9. package/ext/css/src/lib/colors/fuchsia.ts +25 -0
  10. package/ext/css/src/lib/colors/gray.ts +25 -0
  11. package/ext/css/src/lib/colors/green.ts +25 -0
  12. package/ext/css/src/lib/colors/indigo.ts +25 -0
  13. package/ext/css/src/lib/colors/lime.ts +25 -0
  14. package/ext/css/src/lib/colors/neutral.ts +25 -0
  15. package/ext/css/src/lib/colors/orange.ts +25 -0
  16. package/ext/css/src/lib/colors/pink.ts +25 -0
  17. package/ext/css/src/lib/colors/purple.ts +25 -0
  18. package/ext/css/src/lib/colors/red.ts +25 -0
  19. package/ext/css/src/lib/colors/rose.ts +25 -0
  20. package/ext/css/src/lib/colors/sky.ts +25 -0
  21. package/ext/css/src/lib/colors/slate.ts +25 -0
  22. package/ext/css/src/lib/colors/stone.ts +25 -0
  23. package/ext/css/src/lib/colors/teal.ts +25 -0
  24. package/ext/css/src/lib/colors/violet.ts +25 -0
  25. package/ext/css/src/lib/colors/yellow.ts +25 -0
  26. package/ext/css/src/lib/colors/zinc.ts +25 -0
  27. package/ext/css/src/lib/colors.ts +23 -0
  28. package/ext/css/src/structure/$CSSKeyframesRule.ts +2 -5
  29. package/ext/css/src/structure/$CSSMediaRule.ts +3 -23
  30. package/ext/css/src/structure/$CSSRule.ts +6 -18
  31. package/ext/css/src/structure/$CSSStyleRule.ts +10 -12
  32. package/ext/html/html.ts +24 -58
  33. package/ext/html/node/$Anchor.ts +49 -0
  34. package/ext/html/node/$Canvas.ts +16 -0
  35. package/ext/html/node/$Dialog.ts +16 -0
  36. package/ext/html/node/$Form.ts +16 -0
  37. package/ext/html/node/$Image.ts +72 -0
  38. package/ext/html/node/$Input.ts +169 -0
  39. package/ext/html/node/$Label.ts +16 -0
  40. package/ext/html/node/$Media.ts +16 -0
  41. package/ext/html/node/$OptGroup.ts +23 -0
  42. package/ext/html/node/$Option.ts +40 -0
  43. package/ext/html/node/$Select.ts +76 -0
  44. package/ext/html/node/$TextArea.ts +16 -0
  45. package/ext/router/README.md +81 -0
  46. package/ext/router/index.ts +66 -0
  47. package/ext/router/node/Page.ts +27 -0
  48. package/ext/router/node/Route.ts +53 -0
  49. package/ext/router/node/Router.ts +138 -0
  50. package/ext/router/node/RouterAnchor.ts +8 -0
  51. package/ext/ssr/env.ts +61 -0
  52. package/ext/ssr/index.ts +47 -0
  53. package/ext/ssr/package.json +10 -0
  54. package/package.json +8 -4
  55. package/src/core.ts +43 -30
  56. package/src/global.ts +6 -0
  57. package/src/lib/assign.ts +4 -3
  58. package/src/lib/assignHelper.ts +11 -13
  59. package/src/lib/native.ts +14 -3
  60. package/src/node/$Element.ts +204 -23
  61. package/src/node/$HTMLElement.ts +76 -0
  62. package/src/node/$Node.ts +145 -53
  63. package/src/node/node.ts +8 -6
  64. package/src/structure/Signal.ts +4 -4
  65. package/tsconfig.json +1 -1
  66. package/ext/css/src/structure/$CSSKeyframeRule.ts +0 -13
  67. package/ext/html/node/$HTMLElement.ts +0 -7
  68. package/ext/html/node/type.ts +0 -96
@@ -0,0 +1,138 @@
1
+ import type { AnchorTarget } from "#html/$Anchor";
2
+ import { _Array_from, _document, _instanceof, _Object_fromEntries, forEach } from "#lib/native";
3
+ import { Page } from "./Page";
4
+ import { BaseRouteNode, Route } from "./Route";
5
+
6
+ // history index
7
+ let index = 0;
8
+ const _location = location;
9
+ const {origin} = _location;
10
+ const _history = history;
11
+ const documentElement = _document.documentElement;
12
+ const [PUSH, REPLACE] = [1, 2] as const;
13
+ const [FORWARD, BACK] = ['forward', 'back'] as const;
14
+
15
+ // disable browser scroll restoration
16
+ _history.scrollRestoration = 'manual';
17
+
18
+ /** convert path string to URL object */
19
+ const toURL = (path: string | URL) =>
20
+ _instanceof(path, URL) ? path : path.startsWith('http') ? new URL(path) : new URL(path.startsWith(origin) ? path : origin + path);
21
+
22
+ /** handle history state with push and replace state. */
23
+ const historyHandler = async (path: string | URL | Nullish, mode: 1 | 2, target?: AnchorTarget) => {
24
+ if (!path) return;
25
+ const url = toURL(path);
26
+ if (url.href === _location.href) return;
27
+ if (target && target !== '_self') return open(url, target);
28
+ if (url.origin !== origin) return open(url, target);
29
+ _history.replaceState({
30
+ index: index,
31
+ x: documentElement.scrollLeft,
32
+ y: documentElement.scrollTop
33
+ }, '', _location.href);
34
+ if (mode === PUSH) index += 1;
35
+ Router.direction = FORWARD;
36
+ history[mode === PUSH ? 'pushState' : 'replaceState']({index}, '' , url)
37
+ for (let router of Router.routers) router.routes.size && await router.resolve(path)
38
+ }
39
+
40
+ export class Router extends BaseRouteNode<''> {
41
+ static pageRouters = new Map<Page, Router>();
42
+ static routers = new Set<Router>();
43
+ pageMap = new Map<string, Page>();
44
+ static direction: 'back' | 'forward' = FORWARD;
45
+ constructor(page?: Page) {
46
+ super('', () => [], 'router')
47
+ Router.routers.add(this);
48
+ if (page) Router.pageRouters.set(page, this);
49
+ }
50
+
51
+ static open(path: string | URL | Nullish, target?: AnchorTarget) {
52
+ historyHandler(path, PUSH, target);
53
+ return this;
54
+ }
55
+
56
+ static back() {
57
+ _history.back();
58
+ return this;
59
+ }
60
+
61
+ static forward() {
62
+ _history.forward();
63
+ return this;
64
+ }
65
+
66
+ static replace(path: string | URL | Nullish) {
67
+ historyHandler(path, REPLACE);
68
+ return this;
69
+ }
70
+
71
+ async resolve(path: string | URL) {
72
+ const {pathname, searchParams, hash, href} = toURL(path);
73
+ const routeData = { params: {} as {[key: string]: string}, query: _Object_fromEntries(searchParams) }
74
+ const split = (p: string) => p.replaceAll(/\/+/g, '/').split('/').map(path => `/${path}`);
75
+
76
+ function determineRoute(parentRoute: BaseRouteNode<any>, path: string, hash: string | undefined): [route: Route | null, pathId: string][] {
77
+ const targetPathSplit = split(path);
78
+ hash && targetPathSplit.push(hash);
79
+ if (!parentRoute.routes.size) return [];
80
+ routeLoop: for (const route of _Array_from(parentRoute.routes.values()).sort((a, b) => b.path.length - a.path.length)) {
81
+ const routePathSplit = split(route.path);
82
+ let pathId = '';
83
+ splitLoop: for (let i = 0; i < routePathSplit.length; i++) {
84
+ const pass = () => pathId += targetSnippet;
85
+ const [routeSnippet, targetSnippet] = [routePathSplit[i], targetPathSplit[i]];
86
+ if (!routeSnippet || !targetSnippet) continue routeLoop;
87
+ // process params in path
88
+ if (routeSnippet.includes(':')) {
89
+ if (targetSnippet === '/') continue routeLoop;
90
+ const [prefix, paramName] = routeSnippet.split(':') as [string, string];
91
+ if (!targetSnippet.startsWith(prefix)) continue routeLoop;
92
+ routeData.params[paramName] = targetSnippet.replace(`${prefix}`, '');
93
+ pass();
94
+ continue splitLoop;
95
+ }
96
+ if (routeSnippet === '/' && route.routes.size) continue splitLoop;
97
+ if (routeSnippet !== targetSnippet) continue routeLoop;
98
+ pass()
99
+ }
100
+ return [[route, pathId], ...determineRoute(route, path, hash)];
101
+ }
102
+ return [[null, parentRoute.path + '$$NOT_FOUND$$']];
103
+ }
104
+ // analytics
105
+ const targetRoutes = determineRoute(this, pathname + '/', hash);
106
+ // build pages
107
+ let prevPage: null | Page = null, prevRoute: BaseRouteNode<any> = this;
108
+ for (const [route, pathId] of targetRoutes) {
109
+ const page = this.pageMap.get(pathId) ?? new Page(route ?? prevRoute.routes.get('404') ?? new Route('404', () => null), routeData);
110
+ await route?.build(routeData, page);
111
+ _document && (_document.title = page.pageTitle() ?? _document.title);
112
+ this.pageMap.set(pathId, page);
113
+
114
+ if (href === _location.href) {
115
+ if (prevPage) Router.pageRouters.get(prevPage)?.content(page);
116
+ else this.content(page);
117
+ }
118
+ prevPage = page;
119
+ if (route) prevRoute = route;
120
+ }
121
+ let { x, y } = _history.state ?? {x: 0, y: 0};
122
+ scrollTo(x, y);
123
+ return this;
124
+ }
125
+
126
+ listen() {
127
+ const resolve = () => {
128
+ const stateIndex = _history.state?.index ?? 0;
129
+ if (index > stateIndex) Router.direction = BACK;
130
+ if (index < stateIndex) Router.direction = FORWARD;
131
+ index = stateIndex;
132
+ this.resolve(_location.href);
133
+ }
134
+ addEventListener('popstate', resolve);
135
+ resolve();
136
+ return this;
137
+ }
138
+ }
@@ -0,0 +1,8 @@
1
+ import { $Anchor } from "#html/$Anchor";
2
+
3
+ export class RouterAnchor extends $Anchor {
4
+ constructor() {
5
+ super();
6
+ this.on('click', e => { e.preventDefault(); $.open(this.href(), this.target()) })
7
+ }
8
+ }
package/ext/ssr/env.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { NODE } from "esm-env";
2
+ import { _Array_from, _Object_assign, _Object_entries, isNull } from "../../src/lib/native";
3
+ import type { $Node } from "../../src/node/$Node";
4
+
5
+ if (NODE) {
6
+ //@ts-expect-error
7
+ global.window = undefined;
8
+ //@ts-expect-error
9
+ global.document = undefined;
10
+ //@ts-expect-error
11
+ global.Node = class Node {
12
+ nodeName: string;
13
+ attributes = {};
14
+ childNodes = new Set<Node>();
15
+ parent?: Node;
16
+ $!: $Node;
17
+ constructor(nodeName: string) {
18
+ this.nodeName = nodeName.toUpperCase();
19
+ }
20
+ get textContent() { return _Array_from(this.childNodes.values()).map(node => node.textContent).join() }
21
+ set textContent(content: string | null) {
22
+ this.childNodes.clear();
23
+ content && this.childNodes.add(new Text(content) as unknown as Node);
24
+ }
25
+ appendChild(node: Node) {
26
+ if (this.childNodes.has(node)) this.childNodes.delete(node);
27
+ this.childNodes.add(node);
28
+ node.parent = this;
29
+ }
30
+ addEventListener(type: string, cb: () => void) { }
31
+ setAttribute(key: string, value: string) { _Object_assign(this.attributes, {[key]: value}) }
32
+ remove() {
33
+ this.parent?.removeChild(this);
34
+ }
35
+ removeChild(node: Node) {
36
+ this.childNodes.delete(node);
37
+ }
38
+
39
+ get outerHTML() {
40
+ const attr = _Object_entries(this.attributes).map(([key, value]) => `${key}="${value}"`).join(' ');
41
+ const tagName = this.nodeName.toLowerCase();
42
+ const IS_VOID_ELEMENT = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'].includes(tagName);
43
+ return `<${tagName}${attr ? ` ${attr}` : ''}>${_Array_from(this.childNodes).map(node => `${node.$}`).join('')}${IS_VOID_ELEMENT && !this.childNodes.size ? '' : `</${tagName}>`}`;
44
+ }
45
+ }
46
+ //@ts-expect-error
47
+ global.Element = class Element {}
48
+ //@ts-expect-error
49
+ global.HTMLElement = class HTMLElement {}
50
+ //@ts-expect-error
51
+ global.Text = class Text {
52
+ textContent: string;
53
+ $: this;
54
+ constructor(textContent: string) {
55
+ this.textContent = textContent;
56
+ this.$ = this;
57
+ }
58
+
59
+ toString() { return this.textContent }
60
+ }
61
+ }
@@ -0,0 +1,47 @@
1
+ import './env';
2
+ import 'amateras';
3
+ import { _Array_from, _document, _instanceof, _Object_assign, _Object_defineProperty, forEach } from "amateras/lib/native";
4
+ import { $Element, $Node, $Text } from "amateras/node";
5
+ import { BROWSER, NODE } from 'esm-env';
6
+
7
+ declare module 'amateras/core' {
8
+ export namespace $ {
9
+ export function mount(id: string, $node: $Element): void;
10
+ export function serverSide(cb: () => any): void;
11
+ }
12
+ }
13
+
14
+ export function onserver<T>(cb: () => T): T | '' {
15
+ if (NODE) return cb();
16
+ return '';
17
+ }
18
+
19
+ export function onclient<T>(cb: () => T): T | undefined {
20
+ if (BROWSER) return cb();
21
+ return;
22
+ }
23
+
24
+ _Object_assign($, {
25
+ mount(id: string, $node: $Element) {
26
+ if (!BROWSER) return;
27
+ const node = _document.querySelector(`#${id}`);
28
+ if (!node) throw 'Target node of mounting not found';
29
+ getData(node, $node);
30
+ node.replaceWith($node.node);
31
+
32
+ function getData(node: Node, $node: $Node) {
33
+ if (node.nodeName === 'SIGNAL' && _instanceof(node, Element) && _instanceof($node, $Text)) {
34
+ const type = $(node).attr()['type'];
35
+ return forEach($node.signals, signal => signal.value(type === 'number' ? Number(node.textContent) : type === 'boolean' ? node.textContent == 'true' ? true : false : node.textContent));
36
+ }
37
+ if (_instanceof(node, Text)) return $node.textContent(node.textContent);
38
+ if (_instanceof(node, Element) && _instanceof($node, $Element)) $node.attr($(node).attr());
39
+ const arr = _Array_from($node.childNodes);
40
+ forEach(node.childNodes, (_node, i) => {
41
+ const targetChildNode = arr.at(i);
42
+ if (!targetChildNode) throw 'Target DOM tree not matched';
43
+ getData(_node, targetChildNode.$)
44
+ })
45
+ }
46
+ }
47
+ })
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@amateras/ssr",
3
+ "peerDependencies": {
4
+ "amateras": "../../",
5
+ "esm-env": "^1.2.2"
6
+ },
7
+ "imports": {
8
+ "#structure/*": "./src/structure/*.ts"
9
+ }
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amateras",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Amateras is a DOM Utility library.",
5
5
  "module": "index.ts",
6
6
  "type": "module",
@@ -20,8 +20,7 @@
20
20
  "#lib/*": "./src/lib/*.ts",
21
21
  "#structure/*": "./src/structure/*.ts",
22
22
  "#html": "./ext/html/html.ts",
23
- "#css": "./ext/css/css.ts",
24
- "#css/*": "./ext/css/*.ts"
23
+ "#html/*": "./ext/html/node/*.ts"
25
24
  },
26
25
  "exports": {
27
26
  ".": "./src/index.ts",
@@ -31,7 +30,12 @@
31
30
  "./lib/*": "./src/lib/*.ts",
32
31
  "./structure/*": "./src/structure/*.ts",
33
32
  "./html": "./ext/html/html.ts",
34
- "./css": "./ext/css/src/index.ts"
33
+ "./html/*": "./ext/html/node/*.ts",
34
+ "./css": "./ext/css/src/index.ts",
35
+ "./css/colors": "./ext/css/src/lib/colors.ts",
36
+ "./css/color/*": "./ext/css/src/lib/colors/*.ts",
37
+ "./router": "./ext/router/index.ts",
38
+ "./ssr": "./ext/ssr/index.ts"
35
39
  },
36
40
  "workspaces": [
37
41
  "./ext/*"
package/src/core.ts CHANGED
@@ -1,46 +1,56 @@
1
1
  import './global';
2
2
  import { Signal } from "#structure/Signal";
3
- import { $Element } from "#node/$Element";
4
- import { $Node, type $NodeContentTypes } from '#node/$Node';
3
+ import { $Element, type $Event } from "#node/$Element";
4
+ import { $Node, type $NodeContentResolver, type $NodeContentTypes } from '#node/$Node';
5
5
  import '#node/node';
6
- import { _instanceof, _Object_defineProperty, _Object_entries, isFunction, isObject, isString, isUndefined } from '#lib/native';
6
+ import { _instanceof, isString, isFunction, _Object_assign, isObject, isNull, _Object_entries, _Object_defineProperty, forEach, isNumber, _Array_from, _document } from '#lib/native';
7
+ import type { $HTMLElement } from '#node/$HTMLElement';
7
8
 
8
- const tagNameMap: {[key: string]: Constructor<$Node>} = {}
9
- export function $<K extends (...args: any[]) => $Node>(fn: K, ...args: Parameters<K>): ReturnType<K>;
10
- export function $<K extends $NodeContentTypes | undefined | void, F extends () => K, P extends Parameters<F>>(fn: F, ...args: any[]): K;
11
- export function $<K extends $Node, T extends Constructor<K>, P extends ConstructorParameters<T>>(construct: T, ...args: P): K;
9
+ const nodeNameMap: {[key: string]: Constructor<$Node>} = {}
10
+
11
+ export function $<F extends (...args: any[]) => $NodeContentResolver<$Node>, N extends number>(number: N, fn: F, ...args: Parameters<F>): Repeat<ReturnType<F>, N>;
12
+ export function $<F extends (...args: any[]) => $NodeContentResolver<$Node>>(fn: F, ...args: Parameters<F>): ReturnType<F>;
13
+ export function $<T extends Constructor<$Node>, P extends ConstructorParameters<T>, N extends number>(number: N, construct: T, ...args: P): Repeat<InstanceType<T>, N>;
14
+ export function $<T extends Constructor<$Node>, P extends ConstructorParameters<T>>(construct: T, ...args: P): InstanceType<T>;
15
+ export function $<N extends $Node>($node: N, ...args: any[]): N;
16
+ export function $<H extends HTMLElement>(element: H, ...args: any[]): $HTMLElement<H>;
17
+ export function $<E extends Element>(element: E, ...args: any[]): $Element<E>;
12
18
  export function $<K extends TemplateStringsArray>(string: K, ...values: any[]): $NodeContentTypes[];
13
- export function $<K extends $Node>($node: K): K;
14
- export function $<K extends Element>(element: K): $Element<K>;
15
- export function $<K extends keyof HTMLElementTagNameMap>(tagname: K): $Element<HTMLElementTagNameMap[K]>
16
- export function $(tagname: string): $Element<HTMLElement>
17
- export function $(resolver: string | HTMLElement | $Node | Function | TemplateStringsArray, ...args: any[]) {
19
+ export function $<K extends keyof HTMLElementTagNameMap, N extends number>(number: N, tagname: K): Repeat<$HTMLElement<HTMLElementTagNameMap[K]>, N>;
20
+ export function $<K extends keyof HTMLElementTagNameMap>(tagname: K): $HTMLElement<HTMLElementTagNameMap[K]>;
21
+ export function $<Ev extends $Event<$Element, Event>>(event: Ev): Ev['target']['$'];
22
+ export function $<N extends number>(number: N, tagname: string): Repeat<$HTMLElement<HTMLElement>, N>;
23
+ export function $(tagname: string): $HTMLElement<HTMLElement>
24
+ export function $(resolver: string | number | Element | $Node | Function | TemplateStringsArray | Event, ...args: any[]) {
18
25
  if (_instanceof(resolver, $Node)) return resolver;
19
- if (isString(resolver) && tagNameMap[resolver]) return new tagNameMap[resolver]();
26
+ if (isString(resolver) && nodeNameMap[resolver]) return new nodeNameMap[resolver](...args);
20
27
  if (isFunction(resolver))
21
- if (resolver.prototype?.constructor) return resolver.prototype.constructor(...args);
28
+ if (resolver.prototype?.constructor) return new resolver.prototype.constructor(...args);
22
29
  else return resolver(...args);
23
30
  if (resolver instanceof Array) {
24
31
  const iterate = args.values();
25
32
  return resolver.map(str => [str ?? undefined, iterate.next().value]).flat().filter(item => item);
26
33
  }
27
34
  if (_instanceof(resolver, Node) && _instanceof(resolver.$, $Node)) return resolver.$;
35
+ if (_instanceof(resolver, Event)) return $(resolver.currentTarget as Element);
36
+ if (isNumber(resolver)) return _Array_from({length: resolver}).map(_ => $(args[0], ...args.slice(1)));
28
37
  return new $Element(resolver);
29
38
  }
30
39
 
31
40
  export namespace $ {
41
+ export const stylesheet = new CSSStyleSheet();
42
+ _document.adoptedStyleSheets.push(stylesheet);
32
43
  type SignalProcess<T> = T extends Array<any> ? {} : T extends object ? { [key in keyof T as `${string & key}$`]: SignalFunction<T[key]> } : {};
33
- export type SignalFunction<T> = ({(value: ((newValue: T) => T) | T): SignalFunction<T>, (): T}) & { signal: Signal<T> } & SignalProcess<T>;
44
+ export type SignalFunction<T> = {signal: Signal<T>, set: (newValue: T | ((oldValue: T) => T)) => SignalFunction<T>} & (() => T) & SignalProcess<T>;
34
45
  export function signal<T>(value: T): SignalFunction<T>
35
46
  export function signal<T>(value: T) {
36
47
  const signal = new Signal<T>(value);
37
- const signalFn = function (newValue?: T) {
38
- if (!arguments.length) return signal.value();
39
- if (!isUndefined(newValue)) signal.value(newValue);
40
- return signalFn;
41
- }
42
- _Object_defineProperty(signalFn, 'signal', { value: signal });
43
- if (isObject(value)) {
48
+ const signalFn = function () { return signal.value(); }
49
+ _Object_assign(signalFn, {
50
+ signal,
51
+ set(newValue: T) { return signal.value(newValue), signalFn; }
52
+ })
53
+ if (isObject(value) && !isNull(value)) {
44
54
  for (const [key, val] of _Object_entries(value)) {
45
55
  const val$ = $.signal(val);
46
56
  val$.signal.subscribe(newValue => { value[key as keyof typeof value] = newValue; signal.emit() });
@@ -53,14 +63,14 @@ export namespace $ {
53
63
  export type ComputeFunction<T> = ({(): T}) & { signal: Signal<T> };
54
64
  export function compute<T>(process: () => T) {
55
65
  let subscribed = false;
56
- const signalFn: SignalFunction<any> = $.signal(null);
66
+ const signalFn: SignalFunction<any> = signal(null);
57
67
  function computeFn() {
58
- if (!subscribed) return signalFn(subscribe())();
59
- else return signalFn(process())();
68
+ if (!subscribed) return signalFn.set(subscribe())();
69
+ else return signalFn.set(process())();
60
70
  }
61
71
  function subscribe () {
62
72
  const signalHandler = (signal: Signal<any>) => {
63
- signal.subscribe(() => signalFn(process()))
73
+ signal.subscribe(() => signalFn.set(process()))
64
74
  }
65
75
  Signal.listeners.add(signalHandler);
66
76
  const result = process();
@@ -68,16 +78,19 @@ export namespace $ {
68
78
  subscribed = true;
69
79
  return result;
70
80
  }
71
- _Object_defineProperty(computeFn, 'signal', { value: signalFn.signal });
81
+ _Object_assign(computeFn, { signal: signalFn.signal });
72
82
  return computeFn as ComputeFunction<T>
73
83
  }
74
84
 
75
- export function registerTagName(tagname: string, $node: Constructor<$Node>) {
76
- tagNameMap[tagname] = $node;
85
+ export function assign(resolver: [nodeName: string, $node: Constructor<$Node>][]): void;
86
+ export function assign(nodeName: string, $node: Constructor<$Node>): void;
87
+ export function assign(resolver: string | [nodeName: string, $node: Constructor<$Node>][], $node?: Constructor<$Node>) {
88
+ if (isString(resolver)) $node && (nodeNameMap[resolver] = $node);
89
+ else forEach(resolver, ([nodeName, $node]) => nodeNameMap[nodeName] = $node);
77
90
  return $;
78
91
  }
79
92
 
80
- export function orArrayResolver<T>(item: OrArray<T>): T[] {
93
+ export function toArray<T>(item: OrArray<T>): T[] {
81
94
  return _instanceof(item, Array) ? item : [item];
82
95
  }
83
96
 
package/src/global.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { $Element } from '#node/$Element';
2
2
  import type { $Node } from '#node/$Node';
3
3
  import * as core from '#core';
4
+ import type { Signal } from '#structure/Signal';
4
5
 
5
6
  declare global {
6
7
  export import $ = core.$;
@@ -10,6 +11,11 @@ declare global {
10
11
  type OrPromise<T> = T | Promise<T>;
11
12
  type OrNullish<T> = T | Nullish;
12
13
  type Constructor<T> = { new (...args: any[]): T }
14
+ type $Parameter<T> = T | undefined | Signal<T> | Signal<T | undefined>
15
+ type Repeat<T, N extends number, Acc extends T[] = []> =
16
+ Acc['length'] extends N
17
+ ? Acc
18
+ : Repeat<T, N, [...Acc, T]>;
13
19
  type Prettify<T> = {
14
20
  [K in keyof T]: T[K];
15
21
  } & {};
package/src/lib/assign.ts CHANGED
@@ -6,17 +6,18 @@ export function assign(target: any, {set, get, fn}: {
6
6
  get?: string[],
7
7
  fn?: string[]
8
8
  }) {
9
+ const [GET, SET, FN] = ['get', 'set', 'fn'] as const;
9
10
  const filterAndMap = (type: 'get' | 'set' | 'fn', arr: string[] | undefined) => {
10
11
  return arr?.map(prop => [type, prop]) ?? []
11
12
  }
12
- const list = [...filterAndMap('get', get), ...filterAndMap('set', set), ...filterAndMap('fn', fn)] as [string, string][];
13
+ const list = [...filterAndMap(GET, get), ...filterAndMap(SET, set), ...filterAndMap(FN, fn)] as [string, string][];
13
14
  for (const [type, prop] of list) {
14
15
  _Object_defineProperty(target.prototype, prop, {
15
- ...(type === 'get' ? {
16
+ ...(type === GET ? {
16
17
  get() { return this.node[prop as any] }
17
18
  } : {
18
19
  writable: true,
19
- ...(type === 'set' ? {
20
+ ...(type === SET ? {
20
21
  // set
21
22
  value: function (this, args: any) {
22
23
  if (!arguments.length) return this.node[prop];
@@ -2,18 +2,16 @@ import type { $Node } from "#node/$Node";
2
2
  import { assign } from "./assign";
3
3
  import { _Object_entries, _Object_getOwnPropertyDescriptors } from "./native";
4
4
 
5
- export function assignHelper(targets: [object: Constructor<Node>, target: Constructor<$Node>, tagname?: string][]) {
6
- for (const [object, target, tagname] of targets) {
7
- const {set, get, fn} = { set: [] as string[], get: [] as string[], fn: [] as string[] }
8
- // assign native object properties to target
9
- for (const [prop, value] of _Object_entries(_Object_getOwnPropertyDescriptors(object.prototype))) {
10
- if (prop in target.prototype) continue;
11
- if (value.get && !value.set) get.push(prop);
12
- else if (value.value) fn.push(prop);
13
- else if (value.get && value.set) set.push(prop);
14
- }
15
- assign(target, {set, get, fn})
16
- // register tagname
17
- if (tagname) $.registerTagName(tagname, target)
5
+ export function assignHelper(object: Constructor<Node>, target: Constructor<$Node>, tagname?: string) {
6
+ const [set, get, fn] = [[], [], []] as [string[], string[], string[]]
7
+ // assign native object properties to target
8
+ for (const [prop, value] of _Object_entries(_Object_getOwnPropertyDescriptors(object.prototype))) {
9
+ if (prop in target.prototype) continue;
10
+ if (value.get && !value.set) get.push(prop);
11
+ else if (value.value) fn.push(prop);
12
+ else if (value.get && value.set) set.push(prop);
18
13
  }
14
+ assign(target, {set, get, fn})
15
+ // register tagname
16
+ if (tagname) $.assign(tagname, target)
19
17
  }
package/src/lib/native.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // document
2
+ export const _document = document;
1
3
  // Object
2
4
  export const _Object_fromEntries = Object.fromEntries;
3
5
  export const _Object_entries = Object.entries;
@@ -7,6 +9,13 @@ export const _Object_defineProperty = Object.defineProperty;
7
9
  export const _Object_getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
8
10
  // Array
9
11
  export const _Array_from = Array.from;
12
+ export function forEach<N extends Node>(set: NodeListOf<N>, fn: (value: N, index: number, parent: NodeListOf<N>) => any, thisArgs?: any): void;
13
+ export function forEach<K, V>(set: Map<K, V>, fn: (value: V, key: K, index: number, map: Map<K, V>) => any, thisArgs?: any): void;
14
+ export function forEach<T>(set: Set<T>, fn: (value: T, index: number, set: Set<T>) => any, thisArgs?: any): void;
15
+ export function forEach<T>(arr: Array<T>, fn: (value: T, index: number, array: Array<T>) => any, thisArgs?: any): void;
16
+ export function forEach<T>(arr: any, fn: any, thisArgs?: any) {
17
+ arr.forEach(fn, thisArgs)
18
+ }
10
19
  // typeof
11
20
  export function _typeof(target: any, type: 'string' | 'number' | 'object' | 'boolean' | 'function' | 'bigint' | 'symbol' | 'undefined') {
12
21
  return typeof target === type;
@@ -29,6 +38,8 @@ export function isUndefined(target: any): target is undefined {
29
38
  export function isNull(target: any): target is null {
30
39
  return target === null;
31
40
  }
32
- export function _instanceof<T>(target: any, instance: abstract new (...args: any[]) => T): target is T {
33
- return target instanceof instance;
34
- }
41
+ export function _instanceof<T extends (abstract new (...args: any[]) => any)[]>(target: any, ...instance: T): target is InstanceType<T[number]> {
42
+ return !!instance.find(i => target instanceof i);
43
+ }
44
+ // JSON
45
+ export const _JSON_stringify = JSON.stringify;