airx 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Xulai (Alain) Yin
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,29 @@
1
+ # airx
2
+
3
+ Arix is a front-end framework based on JSX, but it doesn't have hooks like React.
4
+
5
+ ## Use
6
+
7
+ ```tsx
8
+ import * as airx from 'airx'
9
+
10
+ const outsideCount = airx.createRef(1)
11
+
12
+ function App() {
13
+ const innerCount = airx.createRef(1)
14
+
15
+ const handleClick = () => {
16
+ innerCount.value += 1
17
+ outsideCount.value +=1
18
+ }
19
+
20
+ return () => (
21
+ <button onClick={handleClick}>
22
+ {innerCount.value}
23
+ {outsideCount.value}
24
+ </button>
25
+ )
26
+ }
27
+
28
+ airx.createApp(<App />).mount(document.getElementById('app'))
29
+ ```
@@ -0,0 +1,40 @@
1
+ import * as symbol from './symbol';
2
+ type AirxElementType<P> = string | AirxComponent<P>;
3
+ /**
4
+ * AirxElement 表示一个 Airx 元素
5
+ * type 表示元素的类型,可能是一个 html 标签,
6
+ * 也可能是一个自定义组件
7
+ * props 表示元素的属性
8
+ * children 表示元素的子元素
9
+ */
10
+ export interface AirxElement<P = unknown> {
11
+ type: AirxElementType<P>;
12
+ props: {
13
+ [propKey: string]: unknown;
14
+ };
15
+ [symbol.airxElementSymbol]: true;
16
+ }
17
+ export type AirxChildren = null | string | number | boolean | undefined | AirxElement<never> | Array<AirxElement<never>>;
18
+ /**
19
+ * 函数式组件接收自己的 props,并返回一个 AirxElement
20
+ */
21
+ export type AirxComponent<P = unknown> = (props: P, ctx: AirxComponentContext) => AirxComponentRender;
22
+ export type AirxComponentRender = () => AirxChildren;
23
+ /**
24
+ * createElement 是用于创建 AirxElement 的工具函数
25
+ */
26
+ export declare function createElement<P = unknown>(type: AirxElementType<P>, props: {
27
+ [key: string]: unknown;
28
+ } & P, ...children: AirxChildren[]): AirxElement<P>;
29
+ export declare function isValidElement(element: unknown): element is AirxElement;
30
+ export declare function Fragment(props: {
31
+ children: AirxElement;
32
+ }): () => AirxElement<unknown>;
33
+ export type AirxComponentUnmountedListener = () => void;
34
+ export type AirxComponentMountedListener = () => () => void | void;
35
+ export interface AirxComponentLifecycle {
36
+ onMounted: (listener: AirxComponentMountedListener) => void;
37
+ onUnmounted: (listener: AirxComponentUnmountedListener) => void;
38
+ }
39
+ export type AirxComponentContext = AirxComponentLifecycle;
40
+ export {};
@@ -0,0 +1,23 @@
1
+ import * as symbol from './symbol';
2
+ /**
3
+ * createElement 是用于创建 AirxElement 的工具函数
4
+ */
5
+ export function createElement(type, props, ...children) {
6
+ return {
7
+ type,
8
+ props: {
9
+ ...props,
10
+ children
11
+ },
12
+ [symbol.airxElementSymbol]: true
13
+ };
14
+ }
15
+ export function isValidElement(element) {
16
+ return typeof element === 'object'
17
+ && element !== null
18
+ && Reflect.get(element, symbol.airxElementSymbol);
19
+ }
20
+ export function Fragment(props) {
21
+ return () => props.children;
22
+ }
23
+ //# sourceMappingURL=element.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element.js","sourceRoot":"","sources":["../source/element.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,UAAU,CAAA;AAgClC;;GAEG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAwB,EACxB,KAAqC,EACrC,GAAG,QAAwB;IAE3B,OAAO;QACL,IAAI;QACJ,KAAK,EAAE;YACL,GAAG,KAAK;YACR,QAAQ;SACT;QACD,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,IAAI;KACjC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,OAAO,OAAO,OAAO,KAAK,QAAQ;WAC7B,OAAO,KAAK,IAAI;WAChB,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,iBAAiB,CAAC,CAAA;AACrD,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAgC;IACvD,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAA;AAC7B,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { Plugin } from './plugin';
2
+ import { AirxElement } from './element';
3
+ export * from './types';
4
+ export { render } from './render';
5
+ export { createRef } from './reactive';
6
+ export { Fragment, createElement, AirxComponent, AirxElement, AirxChildren, AirxComponentContext } from './element';
7
+ export interface AirxApp {
8
+ plugin: (plugins: Plugin[]) => AirxApp;
9
+ mount: (container: HTMLElement) => AirxApp;
10
+ }
11
+ export declare function createApp(element: AirxElement<any>): AirxApp;
@@ -0,0 +1,20 @@
1
+ import { render } from './render';
2
+ export * from './types';
3
+ export { render } from './render';
4
+ export { createRef } from './reactive';
5
+ export { Fragment, createElement } from './element';
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ export function createApp(element) {
8
+ const app = {
9
+ plugin: (plugins) => {
10
+ console.log(plugins);
11
+ return app;
12
+ },
13
+ mount: (container) => {
14
+ render(element, container);
15
+ return app;
16
+ }
17
+ };
18
+ return app;
19
+ }
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,cAAc,SAAS,CAAA;AAEvB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,EACL,QAAQ,EACR,aAAa,EAKd,MAAM,WAAW,CAAA;AAQlB,8DAA8D;AAC9D,MAAM,UAAU,SAAS,CAAC,OAAyB;IACjD,MAAM,GAAG,GAAY;QACnB,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE;YAClB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;YACpB,OAAO,GAAG,CAAA;QACZ,CAAC;QAED,KAAK,EAAE,CAAC,SAAsB,EAAE,EAAE;YAChC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;YAC1B,OAAO,GAAG,CAAA;QACZ,CAAC;KACF,CAAA;IAED,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function createLogger(name: string): {
2
+ debug: (...args: unknown[]) => void;
3
+ };
@@ -0,0 +1,13 @@
1
+ const isDev = localStorage.getItem('airx-dev');
2
+ export function createLogger(name) {
3
+ function getPrintPrefix() {
4
+ const date = new Date().toLocaleString();
5
+ return `[${date}][${name}]`;
6
+ }
7
+ function debug(...args) {
8
+ if (isDev)
9
+ console.log(getPrintPrefix(), ...args);
10
+ }
11
+ return { debug };
12
+ }
13
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../source/logger.ts"],"names":[],"mappings":"AAAA,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;AAC9C,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,SAAS,cAAc;QACrB,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,cAAc,EAAE,CAAA;QACxC,OAAO,IAAI,IAAI,KAAK,IAAI,GAAG,CAAA;IAC7B,CAAC;IAED,SAAS,KAAK,CAAC,GAAG,IAAe;QAC/B,IAAI,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA;IACnD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,CAAA;AAClB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export interface Plugin {
2
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../source/plugin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ export declare function createCollector(): {
2
+ complete: () => Ref<unknown>[];
3
+ collect: <R = unknown>(process: () => R) => R;
4
+ };
5
+ interface Ref<T = unknown> {
6
+ value: T;
7
+ }
8
+ export declare function watch<T = unknown>(ref: Ref<T>, listener: () => unknown): () => void;
9
+ export declare function createRef<T>(obj: T): Ref<T>;
10
+ export {};
@@ -0,0 +1,67 @@
1
+ import * as symbol from './symbol';
2
+ /** TODO: 污染全局总是不好的 */
3
+ const globalContext = {
4
+ dependencies: new Set()
5
+ };
6
+ export function createCollector() {
7
+ const newDependencies = new Set();
8
+ return {
9
+ complete: () => [...newDependencies.values()],
10
+ collect: (process) => {
11
+ const beforeDeps = globalContext.dependencies;
12
+ globalContext.dependencies = newDependencies;
13
+ const result = process();
14
+ globalContext.dependencies = beforeDeps;
15
+ return result;
16
+ }
17
+ };
18
+ }
19
+ function triggerRef(ref) {
20
+ requestAnimationFrame(() => {
21
+ const deps = Reflect.get(ref, symbol.airxReactiveDependenciesSymbol);
22
+ for (const dep of deps) {
23
+ dep();
24
+ }
25
+ });
26
+ }
27
+ function createRefObject(value) {
28
+ const object = Object.create({ value });
29
+ Reflect.defineProperty(object, symbol.airxReactiveDependenciesSymbol, {
30
+ configurable: false,
31
+ enumerable: false,
32
+ writable: true,
33
+ value: new Set()
34
+ });
35
+ return object;
36
+ }
37
+ function isRefObject(obj) {
38
+ return obj != null
39
+ && typeof obj === 'object'
40
+ && Reflect.has(obj, symbol.airxReactiveDependenciesSymbol);
41
+ }
42
+ export function watch(ref, listener) {
43
+ const deps = Reflect.get(ref, symbol.airxReactiveDependenciesSymbol);
44
+ deps.add(listener);
45
+ return () => { deps.delete(listener); };
46
+ }
47
+ export function createRef(obj) {
48
+ const ref = isRefObject(obj) ? obj : createRefObject(obj);
49
+ if (!globalContext.dependencies.has(ref)) {
50
+ globalContext.dependencies.add(ref);
51
+ }
52
+ return new Proxy(ref, {
53
+ get(target, key, receiver) {
54
+ const result = Reflect.get(target, key, receiver);
55
+ if (!globalContext.dependencies.has(ref)) {
56
+ globalContext.dependencies.add(ref);
57
+ }
58
+ return result;
59
+ },
60
+ set(target, key, value, receiver) {
61
+ const result = Reflect.set(target, key, value, receiver);
62
+ triggerRef(target);
63
+ return result;
64
+ },
65
+ });
66
+ }
67
+ //# sourceMappingURL=reactive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactive.js","sourceRoot":"","sources":["../source/reactive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { AirxElement, AirxComponentRender, AirxComponentContext, AirxComponentMountedListener, AirxComponentUnmountedListener } from './element';
2
+ export type Disposer = () => void;
3
+ declare class InnerAirxComponentContext implements AirxComponentContext {
4
+ private disposers;
5
+ private mountListeners;
6
+ private unmountedListeners;
7
+ triggerMounted(): void;
8
+ triggerUnmounted(): void;
9
+ onMounted(listener: AirxComponentMountedListener): void;
10
+ onUnmounted(listener: AirxComponentUnmountedListener): void;
11
+ addDisposer(...disposers: Disposer[]): void;
12
+ dispose(): void;
13
+ /**
14
+ * 当 context 传递给外部消费时,隐藏内部实现,仅暴露接口定义的内容
15
+ * @returns 和 AirxComponentContext 接口完全一致的对象
16
+ */
17
+ getSafeContext(): AirxComponentContext;
18
+ }
19
+ /**
20
+ * 树结构
21
+ * instance ←--------------
22
+ * | ↑ ↑
23
+ * child parent parent
24
+ * ↓ | |
25
+ * instance -sibling→ instance...
26
+ */
27
+ interface Instance {
28
+ domRef?: HTMLElement;
29
+ child?: Instance;
30
+ parent?: Instance;
31
+ sibling?: Instance;
32
+ deletions?: Set<Instance>;
33
+ element?: AirxElement;
34
+ beforeElement?: AirxElement;
35
+ render?: AirxComponentRender;
36
+ memoProps?: object;
37
+ requiredUpdate?: boolean;
38
+ context: InnerAirxComponentContext;
39
+ }
40
+ export declare function render(element: AirxElement, domRef: HTMLElement): Instance;
41
+ export {};