vasille-jsx 4.3.4 → 5.0.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,19 +1,30 @@
1
- # Vasille
1
+ # Steel Frame
2
2
 
3
- ![Vasille.js logo](https://raw.githubusercontent.com/vasille-js/vasille-js/refs/heads/v4/doc/img/logo.png)
3
+ ![Vasille.js logo](https://raw.githubusercontent.com/vasille-js/vasille-js/refs/heads/v5/doc/img/logo.png)
4
4
 
5
- `Vasille Web` is a front-end framework, which is developed to provide bulletproof frontends.
5
+ `SteelFrameKit` is a front-end development kit, which is developed to provide bulletproof frontends.
6
6
 
7
7
  [![npm](https://img.shields.io/npm/v/vasille?style=flat-square)](https://www.npmjs.com/package/vasille)
8
8
 
9
9
  ## Table of content
10
10
 
11
- * [Installation](#installation)
12
- * [How to use Vasille](#how-to-use-vasille)
13
- * [How SAFE is Vasille](#how-safe-is-vasille)
14
- * [How INTUITIVE is Vasille](#how-intuitive-is-vasille)
15
- * [How POWERFUL is Vasille](#how-powerful-is-vasille)
16
- * [Road Map](#road-map)
11
+ - [Steel Frame](#steel-frame)
12
+ - [Table of content](#table-of-content)
13
+ - [Installation](#installation)
14
+ - [How to use SteelFramekit](#how-to-use-steelframekit)
15
+ - [Full documentation:](#full-documentation)
16
+ - [Examples](#examples)
17
+ - [How SAFE is SteelFrameKit](#how-safe-is-steelframekit)
18
+ - [How INTUITIVE is SteelFrameKit](#how-intuitive-is-steelframekit)
19
+ - [How POWERFUL is SteelFrameKit](#how-powerful-is-steelframekit)
20
+ - [Road map](#road-map)
21
+ - [Change log](#change-log)
22
+ - [5.0.0](#500)
23
+ - [4.3.0](#430)
24
+ - [4.2.0](#420)
25
+ - [4.1.0](#410)
26
+ - [4.0.0](#400)
27
+ - [Questions](#questions)
17
28
 
18
29
 
19
30
  <hr>
@@ -21,21 +32,22 @@
21
32
  ## Installation
22
33
 
23
34
  ```
24
- npm install vasille-web --save
35
+ npm install steel-frame --save
25
36
  ```
26
37
 
27
- ## How to use Vasille
38
+ ## How to use SteelFramekit
28
39
 
29
40
  Create an app from a template
30
41
 
31
42
  ```bash
32
- $ npm create vasille
43
+ $ npm create steel-frame
33
44
  ```
34
45
 
35
46
  ### Full documentation:
36
- * [Learn `Vasille` in 5 minutes](https://github.com/vasille-js/vasille-js/blob/v4/doc/V4-API.md)
37
- * [Vasille Router Documentation](https://github.com/vasille-js/vasille-js/blob/v4/doc/Router-API.md)
38
- * [Vasille Compostion function](https://github.com/vasille-js/vasille-js/blob/v4/doc/Compositions.md)
47
+ * [Learn `SteelFrameKit` in 5 minutes](https://github.com/vasille-js/vasille-js/blob/v4/doc/V4-API.md)
48
+ * [Router Documentation](https://github.com/vasille-js/vasille-js/blob/v4/doc/Router-API.md)
49
+ * [Compostion functions](https://github.com/vasille-js/vasille-js/blob/v4/doc/Compositions.md)
50
+ * [Dependency injection](https://github.com/vasille-js/vasille-js/blob/v4/doc/Context.md)
39
51
 
40
52
  ### Examples
41
53
  * [TypeScript Example](https://github.com/vasille-js/example-typescript)
@@ -43,14 +55,14 @@ $ npm create vasille
43
55
 
44
56
  <hr>
45
57
 
46
- ## How SAFE is Vasille
58
+ ## How SAFE is SteelFrameKit
47
59
 
48
60
  The safe of your application is ensured by
49
61
  * `100%` coverage of code by unit tests.
50
62
  Each function, each branch is working as designed.
51
63
  * OOP, DRY, KISS and SOLID principles are applied.
52
64
  * `strong typing` makes your javascript/typescript code safe as C++ code.
53
- All entities of `vasille` core library are strongly typed, including:
65
+ All entities of `SteelFrameKit` core library are strongly typed, including:
54
66
  * data fields & properties.
55
67
  * computed properties (function parameters and result).
56
68
  * methods.
@@ -60,11 +72,11 @@ All entities of `vasille` core library are strongly typed, including:
60
72
  * references to children.
61
73
  * No asynchronous code, when the line of code is executed, the DOM and reactive things are already synced.
62
74
 
63
- ## How INTUITIVE is Vasille
75
+ ## How INTUITIVE is SteelFrameKit
64
76
 
65
77
  There is the "Hello World":
66
78
  ```typescript jsx
67
- import { compose, mount } from "vasille-dx";
79
+ import { compose, mount } from "steel-frame";
68
80
 
69
81
  const App = compose(() => {
70
82
  <p>Hello world</p>;
@@ -73,7 +85,7 @@ const App = compose(() => {
73
85
  mount(document.body, App, {});
74
86
  ```
75
87
 
76
- ## How POWERFUL is Vasille
88
+ ## How POWERFUL is SteelFrameKit
77
89
 
78
90
  All of these are supported:
79
91
  * Components.
@@ -85,25 +97,30 @@ All of these are supported:
85
97
  * 2-way data binding in components.
86
98
  * Logic block (if, else).
87
99
  * Loops (array, map, set).
100
+ * Dependency injection.
88
101
 
89
102
  <hr>
90
103
 
91
104
  ## Road map
92
105
 
93
- * [x] Update the `Vasille Core` library to version 3.0.
106
+ * [x] Update the `core` library to version 3.0.
94
107
  * [x] `100%` Test Coverage for core Library v3.
95
- * [x] Develop the `Vasille JSX` library.
108
+ * [x] Develop the `JSX` library.
96
109
  * [x] `100%` Test Coverage for the JSX library.
97
- * [x] Develop the `Vasille Babel Plugin`.
110
+ * [x] Develop the `Babel Plugin`.
98
111
  * [x] `100%` Test Coverage fot babel plugin.
99
112
  * [x] Add CSS support (define styles in components).
100
113
  * [x] Add router.
101
114
  * [x] Add SSG (static site generation).
115
+ * [ ] Develop tools extension for debugging (WIP).
102
116
  * [ ] Add SSR (server side rendering).
103
- * [ ] Develop tools extension for debugging.
104
117
 
105
118
  ## Change log
106
119
 
120
+ ### 5.0.0
121
+
122
+ Add support for context and dependencies injection.
123
+
107
124
  ### 4.3.0
108
125
 
109
126
  Add new function `safe` which make functions safe, errors are reported automatically.
@@ -114,19 +131,14 @@ Add support for inlined conditions in JSX, binary `&&` and ternary `?:` operator
114
131
 
115
132
  ### 4.1.0
116
133
 
117
- Added SSG (static site generation) as build option `vasille-web build static`.
134
+ Added SSG (static site generation) as build option `sf build static`.
118
135
 
119
136
  ### 4.0.0
120
137
 
121
- Initial version of the framework with file based routing and building scripts (`vasille-web dev` and `vasille-web build spa`).
138
+ Initial version of the framework with file based routing and building scripts (`sf dev` and `sf build spa`).
122
139
 
123
140
  ## Questions
124
141
 
125
142
  If you have questions, feel free to contact the maintainer of the project:
126
143
 
127
144
  * [Author's Email](mailto:vas.lixcode@gmail.com)
128
- * [Author's Telegram](https://t.me/lixcode)
129
-
130
- <hr>
131
-
132
- **Made in Moldova** 🇲🇩
package/lib/components.js CHANGED
@@ -72,9 +72,6 @@ export function Watch({ $model, slot: _slot }, ctx, defaultSlot) {
72
72
  ctx.create(new CoreWatch({ model: $model, slot: safe(slot) }, ctx.runner));
73
73
  }
74
74
  }
75
- export function Debug({ $model }, ctx) {
76
- ctx.debug($model);
77
- }
78
75
  export function Delay({ time, slot: _slot }, ctx, defaultSlot) {
79
76
  const fragment = new Fragment(ctx.runner);
80
77
  const slot = _slot ?? defaultSlot;
package/lib/compose.js CHANGED
@@ -25,14 +25,10 @@ export function store(fn) {
25
25
  return fn(new Reactive());
26
26
  }
27
27
  export function model(fn) {
28
- return o => {
28
+ return (o, parent) => {
29
29
  const ctx = new Reactive();
30
- return {
31
- ...fn(ctx, o),
32
- destroy() {
33
- ctx.destroy();
34
- },
35
- };
30
+ parent?.bind(ctx);
31
+ return fn(ctx, o);
36
32
  };
37
33
  }
38
34
  export function mount(tag, view, runner, $) {
@@ -0,0 +1,103 @@
1
+ import { reportError, safe, userError } from "vasille";
2
+ import { DevArrayModel, DevArrayView, DevFragment, DevMapModel, DevMapView, DevSetModel, DevSetView, DevSwitchedNode, DevWatch as DevCoreWatch, } from "vasille/dev";
3
+ export function DevSlot({ model, slot, ...options }, ctx, defaultSlot, usage) {
4
+ try {
5
+ if (model) {
6
+ model(options, ctx);
7
+ }
8
+ else if (slot) {
9
+ slot({}, ctx);
10
+ }
11
+ else if (defaultSlot) {
12
+ defaultSlot(ctx);
13
+ }
14
+ }
15
+ catch (e) {
16
+ ctx.runner.inspector.reportComponentSlotError({
17
+ targetId: "id" in ctx && typeof ctx.id === "number" ? ctx.id : 0,
18
+ error: e,
19
+ usage: usage,
20
+ time: Date.now(),
21
+ });
22
+ reportError(e);
23
+ }
24
+ }
25
+ export function DevSwitch(options, ctx, _slot, usage) {
26
+ ctx.create(new DevSwitchedNode(usage, ctx.runner, options.cases, options.default));
27
+ }
28
+ export function DevFor({ of: model, slot: _slot }, ctx, defaultSlot, usage) {
29
+ const slot = _slot ?? defaultSlot;
30
+ if (!slot) {
31
+ return;
32
+ }
33
+ if (model instanceof DevArrayModel) {
34
+ ctx.create(new DevArrayView({
35
+ model,
36
+ slot: slot,
37
+ }, ctx.runner, usage));
38
+ }
39
+ else if (model instanceof DevMapModel) {
40
+ ctx.create(new DevMapView({
41
+ model,
42
+ slot,
43
+ }, ctx.runner, usage));
44
+ }
45
+ else if (model instanceof DevSetModel) {
46
+ ctx.create(new DevSetView({
47
+ model,
48
+ slot: slot,
49
+ }, ctx.runner, usage));
50
+ }
51
+ // fallback if is used external Array/Map/Set
52
+ else {
53
+ const safeSlot = safe(slot);
54
+ console.warn("Vasille <For of/> fallback detected. Please provide reactive data.");
55
+ if (model instanceof Array) {
56
+ model.forEach((value) => {
57
+ safeSlot(ctx, value, value);
58
+ });
59
+ }
60
+ else if (model instanceof Map) {
61
+ model.forEach((value, key) => {
62
+ safeSlot(ctx, value, key);
63
+ });
64
+ }
65
+ else if (model instanceof Set) {
66
+ model.forEach(value => {
67
+ safeSlot(ctx, value, value);
68
+ });
69
+ }
70
+ else {
71
+ throw userError("wrong use of `<For of/>` component", "wrong-model");
72
+ }
73
+ }
74
+ }
75
+ export function DevWatch({ $model, slot: _slot }, ctx, defaultSlot, usage) {
76
+ const slot = _slot ?? defaultSlot;
77
+ /* istanbul ignore else */
78
+ if (slot) {
79
+ ctx.create(new DevCoreWatch({ model: $model, slot: safe(slot) }, ctx.runner, usage));
80
+ }
81
+ }
82
+ export function DevDelay({ time, slot: _slot }, ctx, defaultSlot, usage) {
83
+ const fragment = new DevFragment(ctx.runner, null, usage, "Delay", {
84
+ time,
85
+ slot: _slot,
86
+ });
87
+ const slot = _slot ?? defaultSlot;
88
+ let timer;
89
+ ctx.create(fragment, function (node) {
90
+ /* istanbul ignore else */
91
+ if (slot) {
92
+ timer = setTimeout(() => {
93
+ safe(slot)(node);
94
+ timer = undefined;
95
+ }, time);
96
+ }
97
+ node.runOnDestroy(() => {
98
+ if (timer !== undefined) {
99
+ clearTimeout(timer);
100
+ }
101
+ });
102
+ });
103
+ }
@@ -0,0 +1,72 @@
1
+ import { DevReactive, remapObject, toDevIdOrValue } from "vasille/dev";
2
+ import { DevApp, DevFragment, ModelId } from "vasille/dev";
3
+ import { earlyInspector } from "./early-inspector.js";
4
+ export function devView(renderer, declaration, name) {
5
+ return function (props, node, slot, usage) {
6
+ const { callback } = props;
7
+ if (!node) {
8
+ throw new Error("Vasille: Component context is missing");
9
+ }
10
+ const frag = new DevFragment(node.runner, declaration, usage ?? null, name, props);
11
+ if (slot) {
12
+ props.slot = slot;
13
+ }
14
+ node.create(frag);
15
+ try {
16
+ const result = renderer(frag, props);
17
+ if (result !== undefined && result !== null && callback) {
18
+ callback(result);
19
+ }
20
+ }
21
+ catch (e) {
22
+ node.runner.inspector.reportComponentError({
23
+ targetId: frag.id,
24
+ error: e,
25
+ time: Date.now(),
26
+ });
27
+ reportError(e);
28
+ }
29
+ finally {
30
+ node.runner.inspector.composeTime({
31
+ id: frag.id,
32
+ time: Date.now(),
33
+ });
34
+ }
35
+ };
36
+ }
37
+ export function devStore(fn, declaration, name) {
38
+ const reactive = new DevReactive({ inspector: earlyInspector });
39
+ earlyInspector.createStore({ id: reactive.id, declaration, name, time: Date.now() });
40
+ return fn(reactive);
41
+ }
42
+ export function devModel(fn, declaration, name) {
43
+ return (o, parent, usage) => {
44
+ const ctx = new DevReactive({ inspector: earlyInspector });
45
+ const id = ctx.id;
46
+ earlyInspector.createCustomModel({
47
+ id,
48
+ declaration,
49
+ usage,
50
+ name,
51
+ time: Date.now(),
52
+ props: remapObject(o, toDevIdOrValue),
53
+ });
54
+ if (parent) {
55
+ parent.runOnDestroy(() => ctx.destroy());
56
+ }
57
+ return {
58
+ ...fn(ctx, o),
59
+ [ModelId]: id,
60
+ };
61
+ };
62
+ }
63
+ export function devMount(tag, view, runner, $, inspector) {
64
+ const root = new DevApp(tag, runner);
65
+ const frag = new DevFragment(runner, null, null, "Root", {});
66
+ // share information about created stores
67
+ earlyInspector.connect(inspector);
68
+ root.create(frag, function () {
69
+ view($, frag);
70
+ });
71
+ return root;
72
+ }
@@ -0,0 +1,111 @@
1
+ export class AbstractInspector {
2
+ addContextState(state) {
3
+ this.send(this.addContextState.name, state);
4
+ }
5
+ composeTime(time) {
6
+ this.send(this.composeTime.name, time);
7
+ }
8
+ createComponent(comp) {
9
+ this.send(this.createComponent.name, comp);
10
+ }
11
+ createCustomModel(model) {
12
+ this.send(this.createCustomModel.name, model);
13
+ }
14
+ createModel(model) {
15
+ this.send(this.createModel.name, model);
16
+ }
17
+ createNode(node) {
18
+ this.send(this.createNode.name, node);
19
+ }
20
+ createStore(store) {
21
+ this.send(this.createStore.name, store);
22
+ }
23
+ createTag(tag) {
24
+ this.send(this.createTag.name, tag);
25
+ }
26
+ destroy(data) {
27
+ this.send(this.destroy.name, data);
28
+ }
29
+ eventTrigger(call) {
30
+ this.send(this.eventTrigger.name, call);
31
+ }
32
+ functionCall(call) {
33
+ this.send(this.functionCall.name, call);
34
+ }
35
+ functionReturn(result) {
36
+ this.send(this.functionReturn.name, result);
37
+ }
38
+ functionThrows(error) {
39
+ this.send(this.functionThrows.name, error);
40
+ }
41
+ newExpression(expr) {
42
+ this.send(this.newExpression.name, expr);
43
+ }
44
+ newReference(ref) {
45
+ this.send(this.newReference.name, ref);
46
+ }
47
+ registerExecutionPosition(pos) {
48
+ this.send(this.registerExecutionPosition.name, pos);
49
+ }
50
+ registeredRoutes(routes) {
51
+ this.send(this.registeredRoutes.name, routes);
52
+ }
53
+ reportComponentError(error) {
54
+ this.send(this.reportComponentError.name, error);
55
+ }
56
+ reportComponentSlotError(error) {
57
+ this.send(this.reportComponentSlotError.name, error);
58
+ }
59
+ reportError(err) {
60
+ this.send(this.reportError.name, err);
61
+ }
62
+ reportExpressionCalculationError(error) {
63
+ this.send(this.reportExpressionCalculationError.name, error);
64
+ }
65
+ reportReferenceError(error) {
66
+ this.send(this.reportReferenceError.name, error);
67
+ }
68
+ routerActionCall(call) {
69
+ this.send(this.routerActionCall.name, call);
70
+ }
71
+ routerStateChange(change) {
72
+ this.send(this.routerStateChange.name, change);
73
+ }
74
+ routerTargetResult(data) {
75
+ this.send(this.routerTargetResult.name, data);
76
+ }
77
+ setElementParent(parent) {
78
+ this.send(this.setElementParent.name, parent);
79
+ }
80
+ updateExpression(update) {
81
+ this.send(this.updateExpression.name, update);
82
+ }
83
+ updateModel(update) {
84
+ this.send(this.updateModel.name, update);
85
+ }
86
+ updateReference(update) {
87
+ this.send(this.updateReference.name, update);
88
+ }
89
+ }
90
+ export class EarlyInspector extends AbstractInspector {
91
+ constructor() {
92
+ super(...arguments);
93
+ this.queue = [];
94
+ }
95
+ connect(inspector) {
96
+ this.inspector = inspector;
97
+ for (const item of this.queue) {
98
+ inspector[item[0]](item[1]);
99
+ }
100
+ this.queue = [];
101
+ }
102
+ send(name, data) {
103
+ if (this.inspector) {
104
+ this.inspector[name](data);
105
+ }
106
+ else {
107
+ this.queue.push([name, data]);
108
+ }
109
+ }
110
+ }
111
+ export const earlyInspector = new EarlyInspector();
@@ -0,0 +1,21 @@
1
+ import { setErrorHandler as coreSetErrorHandler } from "vasille";
2
+ import { earlyInspector } from "./early-inspector.js";
3
+ export { DevDelay, DevWatch, DevFor, DevSwitch, DevSlot } from "./components.js";
4
+ export { devStore, devModel, devMount, devView } from "./compose.js";
5
+ export { devArrayModel, devMapModel, devEnsure, devExpr, devMatch, devSetModel, devRef, devSet } from "./internal.js";
6
+ export { devAwaited } from "./library.js";
7
+ export { AbstractInspector, EarlyInspector, earlyInspector } from "./early-inspector.js";
8
+ function devErrorHandler(e) {
9
+ earlyInspector.reportError({
10
+ targetId: 0,
11
+ error: e instanceof Error ? (e.stack ?? e.message) : `${e}`,
12
+ time: Date.now(),
13
+ });
14
+ }
15
+ coreSetErrorHandler(devErrorHandler);
16
+ export function setErrorHandler(fn) {
17
+ coreSetErrorHandler(e => {
18
+ devErrorHandler(e);
19
+ fn(e);
20
+ });
21
+ }
@@ -0,0 +1,33 @@
1
+ import { DevArrayModel, DevExpression, DevIValue, DevMapModel, DevReference, DevSetModel, } from "vasille/dev";
2
+ import { match, set } from "../internal.js";
3
+ export function devExpr(ctx, func, values, depsCode, declaration, inspector) {
4
+ return new DevExpression(func, values, ctx, depsCode, declaration, inspector, false);
5
+ }
6
+ export function devRef(v, declaration, inspector) {
7
+ return new DevReference(v, declaration, inspector);
8
+ }
9
+ export function devSetModel(inspector, usage, ctx, data) {
10
+ return new DevSetModel(inspector, usage, data, ctx);
11
+ }
12
+ export function devMapModel(inspector, usage, ctx, data) {
13
+ return new DevMapModel(inspector, usage, data, ctx);
14
+ }
15
+ export function devArrayModel(inspector, usage, ctx, data) {
16
+ return new DevArrayModel(inspector, usage, data, ctx);
17
+ }
18
+ export function devEnsure(obj, key, declaration, inspector) {
19
+ if (!obj) {
20
+ return undefined;
21
+ }
22
+ return key in obj ? obj[key] : (obj[key] = devRef(undefined, declaration, inspector));
23
+ }
24
+ export function devMatch(name, data, declaration, inspector) {
25
+ return match(name, data, v => devRef(v, declaration, inspector));
26
+ }
27
+ export function devSet(o, key, value, declaration, inspector, executionPosition) {
28
+ if (o[key] instanceof DevIValue) {
29
+ o[key].update(value, executionPosition);
30
+ return value;
31
+ }
32
+ return set(o, key, value, v => devRef(v, declaration, inspector));
33
+ }
@@ -0,0 +1,8 @@
1
+ import { awaited } from "../library.js";
2
+ import { devRef } from "./internal.js";
3
+ export function devAwaited(target, callback, declaration, inspector) {
4
+ let i = 0;
5
+ const result = awaited(target, v => devRef(v, declaration[i++], inspector));
6
+ callback(result[0], result[1]);
7
+ return result;
8
+ }
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
- export { Debug, Delay, For, Slot, Watch, Switch } from "./components.js";
1
+ export { Delay, For, Slot, Watch, Switch } from "./components.js";
2
2
  export { view, mount, model, store } from "./compose.js";
3
3
  export { awaited } from "./library.js";
4
- export { ref, arrayModel, backward, mapModel, expr, setModel, forward, set, ensure, match, extract, } from "./internal.js";
4
+ export { ref, arrayModel, mapModel, expr, setModel, set, ensure, match } from "./internal.js";
5
5
  export { setErrorHandler } from "vasille";
package/lib/internal.js CHANGED
@@ -1,13 +1,7 @@
1
- import { IValue, Expression, Reference, Forward, Backward, SetModel, MapModel, ArrayModel, } from "vasille";
1
+ import { IValue, Expression, Reference, SetModel, MapModel, ArrayModel } from "vasille";
2
2
  export function expr(ctx, func, values) {
3
3
  return new Expression(func, values, ctx);
4
4
  }
5
- export function forward(ctx, v) {
6
- return new Forward(v, ctx);
7
- }
8
- export function backward(v) {
9
- return new Backward(v);
10
- }
11
5
  /**
12
6
  * It transforms a non-reactive value to a reactive one.
13
7
  * 1. `let a = 0` to `const a = ref(0)`
@@ -40,19 +34,19 @@ export function arrayModel(ctx, data) {
40
34
  * Use when a value must be IValue but can be undefined
41
35
  * 1. `let a = obj.$key` to `const a = ensure(obj.$key)`
42
36
  */
43
- export function ensure(data) {
44
- return data instanceof IValue ? data : new Reference(data);
37
+ export function ensure(obj, key) {
38
+ return !obj ? ref(undefined) : key in obj ? obj[key] : (obj[key] = ref(undefined));
45
39
  }
46
40
  /**
47
41
  * Used for destruction with computed values
48
42
  * 1. `{[a]: a1} = {x: 2}` to `{[a]: a1 = match("a1")} = {x: 2}`
49
43
  * 1. `{[a]: a1 = 3} = {x: 2}` to `{[a]: a1 = match("a1", 3)} = {x: 2}`
50
44
  */
51
- export function match(name, data) {
45
+ export function match(name, data, createRef = ref) {
52
46
  const iValueRequired = typeof name === "string" && name.startsWith("$");
53
47
  const isIValue = data instanceof IValue;
54
48
  if (iValueRequired && !isIValue) {
55
- return new Reference(data);
49
+ return createRef(data);
56
50
  }
57
51
  if (!iValueRequired && isIValue) {
58
52
  return data.V;
@@ -64,7 +58,7 @@ export function match(name, data) {
64
58
  * 1. `obj.$key = 23` to `set(obj, "$key", 23)`
65
59
  * 2. `arr[0] = 23` to `set(arr, 0, 23)`
66
60
  */
67
- export function set(o, key, value) {
61
+ export function set(o, key, value, createRef = ref) {
68
62
  if (o[key] instanceof IValue) {
69
63
  o[key].V = value;
70
64
  }
@@ -72,13 +66,18 @@ export function set(o, key, value) {
72
66
  o.replace(key, value);
73
67
  }
74
68
  else if (typeof key === "string" && key.charAt(0) === "$") {
75
- o[key] = new Reference(value);
69
+ o[key] = createRef(value);
76
70
  }
77
71
  else {
78
72
  o[key] = value;
79
73
  }
80
74
  return value;
81
75
  }
82
- export function extract(value) {
83
- return value instanceof IValue ? value.V : value;
76
+ export function forward(value) {
77
+ return new Expression(v => v, [value]);
78
+ }
79
+ export function backward(value) {
80
+ const r = new Reference(value.V);
81
+ r.on(v => (value.V = v));
82
+ return r;
84
83
  }
package/lib/library.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ref } from "./internal.js";
2
- export function awaited(target) {
3
- const value = ref(undefined);
4
- const err = ref(undefined);
2
+ export function awaited(target, createRef = ref) {
3
+ const err = createRef(undefined);
4
+ const value = createRef(undefined);
5
5
  let running = false;
6
6
  function run() {
7
7
  if (running) {
package/package.json CHANGED
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "vasille-jsx",
3
- "version": "4.3.4",
3
+ "version": "5.0.0",
4
4
  "description": "The same framework which is designed to build bulletproof frontends (JSX components)",
5
5
  "main": "lib/index.js",
6
6
  "exports": {
7
- "types": "./types/index.d.ts",
8
- "import": "./lib/index.js",
9
- "browser": "./lib/index.js"
7
+ ".": {
8
+ "types": "./types/index.d.ts",
9
+ "import": "./lib/index.js",
10
+ "browser": "./lib/index.js"
11
+ },
12
+ "./dev": {
13
+ "types": "./types/dev/index.d.ts",
14
+ "import": "./lib/dev/index.js",
15
+ "browser": "./lib/dev/index.js"
16
+ }
10
17
  },
11
18
  "types": "./types/index.d.ts",
12
19
  "scripts": {
@@ -43,9 +50,6 @@
43
50
  "firefox 21",
44
51
  "opera 15"
45
52
  ],
46
- "dependencies": {
47
- "vasille": "^4.3.0"
48
- },
49
53
  "devDependencies": {
50
54
  "@types/jest": "^30.0.0",
51
55
  "@types/jsdom": "^21.1.7",
@@ -53,11 +57,14 @@
53
57
  "cross-env": "^10.0.0",
54
58
  "eslint": "^9.33.0",
55
59
  "eslint-plugin-compat": "^6.0.2",
56
- "jest": "^30.2.0",
60
+ "jest": "^30.0.5",
57
61
  "jsdom": "^26.1.0",
58
62
  "prettier": "^3.6.2",
59
63
  "ts-jest": "^29.4.0",
60
64
  "typescript": "^5.8.3",
61
65
  "typescript-eslint": "^8.39.1"
66
+ },
67
+ "dependencies": {
68
+ "vasille": "5.0.0"
62
69
  }
63
70
  }
@@ -23,10 +23,6 @@ interface WatchOptions<Node, Element, TagOptions extends object, T> {
23
23
  slot?: (ctx: Fragment<Node, Element, TagOptions>, value: T) => void;
24
24
  }
25
25
  export declare function Watch<Node, Element, TagOptions extends object, T>({ $model, slot: _slot }: WatchOptions<Node, Element, TagOptions, T>, ctx: Fragment<Node, Element, TagOptions>, defaultSlot?: (ctx: Fragment<Node, Element, TagOptions>) => void): void;
26
- interface DebugOptions {
27
- $model: IValue<unknown>;
28
- }
29
- export declare function Debug<Node, Element, TagOptions extends object>({ $model }: DebugOptions, ctx: Fragment<Node, Element, TagOptions>): void;
30
26
  interface DelayOptions<Node, Element, TagOptions extends object> {
31
27
  time?: number;
32
28
  slot?: (ctx: Fragment<Node, Element, TagOptions>) => unknown;
@@ -1,5 +1,5 @@
1
- import { Fragment, App, Runner, Reactive, Destroyable } from "vasille";
2
- interface CompositionProps {
1
+ import { Fragment, App, Runner, Reactive } from "vasille";
2
+ export interface CompositionProps {
3
3
  slot?: (...args: any[]) => void;
4
4
  }
5
5
  export type Composed<Node, Element, TagOptions extends object, In extends CompositionProps, Out> = ($: In & {
@@ -7,6 +7,5 @@ export type Composed<Node, Element, TagOptions extends object, In extends Compos
7
7
  }, node?: Fragment<Node, Element, TagOptions>, slot?: In["slot"]) => void;
8
8
  export declare function view<Node, Element, TagOptions extends object, In extends CompositionProps, Out>(renderer: (node: Fragment<Node, Element, TagOptions>, input: In) => Out): Composed<Node, Element, TagOptions, In, Out>;
9
9
  export declare function store<Out extends object>(fn: (ctx: Reactive) => Out): Out;
10
- export declare function model<In extends object, Out extends object>(fn: (ctx: Reactive, o: In) => Out): (o: In) => Out & Destroyable;
10
+ export declare function model<In extends object, Out extends object>(fn: (ctx: Reactive, o: In) => Out): (o: In, parent?: Reactive) => Out;
11
11
  export declare function mount<Node, Element, TagOptions extends object, T>(tag: Element, view: ($: T, node: Fragment<Node, Element, TagOptions>) => unknown, runner: Runner<Node, Element, TagOptions>, $: T): App<Node, Element, TagOptions>;
12
- export {};
@@ -0,0 +1,33 @@
1
+ import { Fragment } from "vasille";
2
+ import { DevIValue, StaticPosition } from "vasille/dev";
3
+ import { IDevRunner } from "vasille/dev";
4
+ interface DevSlotOptions<Node, Element, TagOptions extends object, T extends object> {
5
+ model?: (input: T, ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>) => void;
6
+ slot?: (input: object, ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>) => void;
7
+ }
8
+ export declare function DevSlot<Node, Element, TagOptions extends object, T extends object = {}>({ model, slot, ...options }: DevSlotOptions<Node, Element, TagOptions, T> & T, ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>, defaultSlot: ((ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>) => void) | undefined, usage: StaticPosition): void;
9
+ interface DevSwitchOptions<Node, Element, TagOptions extends object> {
10
+ cases: {
11
+ $case: DevIValue<unknown>;
12
+ slot: (ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>) => void;
13
+ }[];
14
+ default?: (ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>) => void;
15
+ slot?: never;
16
+ }
17
+ export declare function DevSwitch<Node, Element, TagOptions extends object>(options: DevSwitchOptions<Node, Element, TagOptions>, ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>, _slot: undefined, usage: StaticPosition): void;
18
+ interface DevForOptions<Node, Element, TagOptions extends object, T, K, V> {
19
+ of: T;
20
+ slot?: (ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>, value: T, index: K) => void;
21
+ }
22
+ export declare function DevFor<Node, Element, TagOptions extends object, T extends Set<unknown> | Map<unknown, unknown> | unknown[], K = T extends unknown[] ? number : T extends Set<infer R> ? R : T extends Map<infer R, unknown> ? R : never, V = T extends (infer R)[] ? R : T extends Set<infer R> ? R : T extends Map<unknown, infer R> ? R : never>({ of: model, slot: _slot }: DevForOptions<Node, Element, TagOptions, T, K, V>, ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>, defaultSlot: ((ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>) => void) | undefined, usage: StaticPosition): void;
23
+ interface DevWatchOptions<Node, Element, TagOptions extends object, T> {
24
+ $model: DevIValue<T>;
25
+ slot?: (ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>, value: T) => void;
26
+ }
27
+ export declare function DevWatch<Node, Element, TagOptions extends object, T>({ $model, slot: _slot }: DevWatchOptions<Node, Element, TagOptions, T>, ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>, defaultSlot: (ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>) => void | undefined, usage: StaticPosition): void;
28
+ interface DevDelayOptions<Node, Element, TagOptions extends object> {
29
+ time?: number;
30
+ slot?: (ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>) => unknown;
31
+ }
32
+ export declare function DevDelay<Node, Element, TagOptions extends object>({ time, slot: _slot }: DevDelayOptions<Node, Element, TagOptions>, ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>, defaultSlot: (ctx: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>) => void | undefined, usage: StaticPosition): void;
33
+ export {};
@@ -0,0 +1,12 @@
1
+ import { App, Fragment, Reactive } from "vasille";
2
+ import { DevReactive, StaticPosition } from "vasille/dev";
3
+ import { IDevRunner } from "vasille/dev";
4
+ import { CompositionProps } from "../compose.js";
5
+ import { DevRunner, DevTagOptions, Inspector } from "vasille/dev";
6
+ export type DevComposed<Node, Element, TagOptions extends object, In extends CompositionProps, Out> = ($: In & {
7
+ callback?(data: Out | undefined): void;
8
+ }, node?: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>, slot?: In["slot"], usage?: StaticPosition) => void;
9
+ export declare function devView<Node, Element, TagOptions extends object, In extends CompositionProps, Out>(renderer: (node: Fragment<Node, Element, TagOptions, IDevRunner<Node, Element, TagOptions>>, input: In) => Out, declaration: StaticPosition, name: string): DevComposed<Node, Element, TagOptions, In, Out>;
10
+ export declare function devStore<Out extends object>(fn: (ctx: Reactive) => Out, declaration: StaticPosition, name: string): Out;
11
+ export declare function devModel<In extends object, Out extends object>(fn: (ctx: DevReactive<IDevRunner<unknown, unknown, object>>, o: In) => Out, declaration: StaticPosition, name: string): (o: In, parent: Reactive | undefined, usage: StaticPosition) => Out;
12
+ export declare function devMount<T>(tag: Element, view: ($: T, node: Fragment<Node, Element, DevTagOptions, IDevRunner<Node, Element, DevTagOptions>>) => unknown, runner: DevRunner, $: T, inspector: Inspector): App<Node, Element, DevTagOptions>;
@@ -0,0 +1,40 @@
1
+ import { Inspector, ProtocolComponent, ProtocolCustomModel, ProtocolExecutionPosition, ProtocolExpression, ProtocolExpressionError, ProtocolExpressionUpdate, ProtocolModel, ProtocolModelUpdate, ProtocolNode, ProtocolParent, ProtocolReference, ProtocolReferenceError, ProtocolReferenceUpdate, ProtocolState, ProtocolStore, ProtocolTag, ProtocolRouterActionCall, ProtocolRouterStateChange, ProtocolRouterTargetResult, ProtocolRoutes, ProtocolSlotError, ProtocolFunctionCall, ProtocolFunctionResult, ProtocolFunctionError, ProtocolEventTrigger, ProtocolComposeTime, ProtocolError, DestroyData } from "vasille/dev";
2
+ export declare abstract class AbstractInspector implements Inspector {
3
+ addContextState(state: ProtocolState): void;
4
+ composeTime(time: ProtocolComposeTime): void;
5
+ createComponent(comp: ProtocolComponent): void;
6
+ createCustomModel(model: ProtocolCustomModel): void;
7
+ createModel(model: ProtocolModel): void;
8
+ createNode(node: ProtocolNode): void;
9
+ createStore(store: ProtocolStore): void;
10
+ createTag(tag: ProtocolTag): void;
11
+ destroy(data: DestroyData): void;
12
+ eventTrigger(call: ProtocolEventTrigger): void;
13
+ functionCall(call: ProtocolFunctionCall): void;
14
+ functionReturn(result: ProtocolFunctionResult): void;
15
+ functionThrows(error: ProtocolFunctionError): void;
16
+ newExpression(expr: ProtocolExpression): void;
17
+ newReference(ref: ProtocolReference): void;
18
+ registerExecutionPosition(pos: ProtocolExecutionPosition): void;
19
+ registeredRoutes(routes: ProtocolRoutes): void;
20
+ reportComponentError(error: ProtocolError): void;
21
+ reportComponentSlotError(error: ProtocolSlotError): void;
22
+ reportError(err: ProtocolError): void;
23
+ reportExpressionCalculationError(error: ProtocolExpressionError): void;
24
+ reportReferenceError(error: ProtocolReferenceError): void;
25
+ routerActionCall(call: ProtocolRouterActionCall): void;
26
+ routerStateChange(change: ProtocolRouterStateChange): void;
27
+ routerTargetResult(data: ProtocolRouterTargetResult): void;
28
+ setElementParent(parent: ProtocolParent): void;
29
+ updateExpression(update: ProtocolExpressionUpdate): void;
30
+ updateModel(update: ProtocolModelUpdate): void;
31
+ updateReference(update: ProtocolReferenceUpdate): void;
32
+ protected abstract send(name: string, data: object): void;
33
+ }
34
+ export declare class EarlyInspector extends AbstractInspector {
35
+ protected inspector: Inspector | undefined;
36
+ protected queue: [string, object][];
37
+ connect(inspector: Inspector): void;
38
+ protected send(name: string, data: object): void;
39
+ }
40
+ export declare const earlyInspector: EarlyInspector;
@@ -0,0 +1,6 @@
1
+ export { DevDelay, DevWatch, DevFor, DevSwitch, DevSlot } from "./components.js";
2
+ export { devStore, type DevComposed, devModel, devMount, devView } from "./compose.js";
3
+ export { devArrayModel, devMapModel, devEnsure, devExpr, devMatch, devSetModel, devRef, devSet } from "./internal.js";
4
+ export { devAwaited } from "./library.js";
5
+ export { AbstractInspector, EarlyInspector, earlyInspector } from "./early-inspector.js";
6
+ export declare function setErrorHandler(fn: (e: unknown) => void): void;
@@ -0,0 +1,10 @@
1
+ import { Reactive } from "vasille";
2
+ import { DevArrayModel, DevExpression, DevIValue, DevMapModel, DevSetModel, ExecutionPosition, Inspector, KindOfDevIValue, StaticPosition } from "vasille/dev";
3
+ export declare function devExpr<T, Args extends unknown[]>(ctx: Reactive | undefined, func: (...args: Args) => T, values: KindOfDevIValue<Args>, depsCode: string[], declaration: StaticPosition, inspector: Inspector): DevExpression<T, Args>;
4
+ export declare function devRef<T>(v: T, declaration: StaticPosition, inspector?: Inspector): DevIValue<T>;
5
+ export declare function devSetModel(inspector: Inspector | undefined, usage: StaticPosition, ctx: Reactive | undefined, data?: unknown[]): DevSetModel<unknown>;
6
+ export declare function devMapModel(inspector: Inspector | undefined, usage: StaticPosition, ctx: Reactive | undefined, data?: [unknown, unknown][]): DevMapModel<unknown, unknown>;
7
+ export declare function devArrayModel(inspector: Inspector | undefined, usage: StaticPosition, ctx: Reactive | undefined, data?: unknown[] | number): DevArrayModel<unknown>;
8
+ export declare function devEnsure<T extends object>(obj: T | null | undefined, key: keyof T, declaration: StaticPosition, inspector: Inspector | undefined): T[keyof T] | undefined;
9
+ export declare function devMatch(name: string | number | symbol, data: unknown, declaration: StaticPosition, inspector: Inspector | undefined): any;
10
+ export declare function devSet(o: object, key: string | symbol | number, value: unknown, declaration: StaticPosition, inspector: Inspector | undefined, executionPosition: ExecutionPosition): unknown;
@@ -0,0 +1,3 @@
1
+ import { Inspector, StaticPosition } from "vasille/dev";
2
+ import { IValue } from "vasille";
3
+ export declare function devAwaited<T>(target: () => Promise<T>, callback: (error: IValue<unknown>, data: IValue<unknown>) => void, declaration: [StaticPosition, StaticPosition], inspector?: Inspector): [IValue<unknown>, IValue<unknown>, () => void];
package/types/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { Debug, Delay, For, Slot, Watch, Switch } from "./components.js";
1
+ export { Delay, For, Slot, Watch, Switch } from "./components.js";
2
2
  export { view, mount, model, store, type Composed } from "./compose.js";
3
3
  export { awaited } from "./library.js";
4
- export { ref, arrayModel, backward, mapModel, expr, setModel, forward, set, ensure, match, extract, } from "./internal.js";
4
+ export { ref, arrayModel, mapModel, expr, setModel, set, ensure, match } from "./internal.js";
5
5
  export { setErrorHandler } from "vasille";
@@ -1,7 +1,5 @@
1
- import { IValue, Reactive, KindOfIValue, Expression, SetModel, MapModel, ArrayModel } from "vasille";
1
+ import { IValue, Reactive, KindOfIValue, Expression, Reference, SetModel, MapModel, ArrayModel } from "vasille";
2
2
  export declare function expr<T, Args extends unknown[]>(ctx: Reactive | undefined, func: (...args: Args) => T, values: KindOfIValue<Args>): Expression<T, Args>;
3
- export declare function forward<T>(ctx: Reactive | undefined, v: IValue<T>): IValue<T>;
4
- export declare function backward<T>(v: IValue<T>): IValue<T>;
5
3
  /**
6
4
  * It transforms a non-reactive value to a reactive one.
7
5
  * 1. `let a = 0` to `const a = ref(0)`
@@ -26,17 +24,18 @@ export declare function arrayModel(ctx: Reactive | undefined, data?: unknown[] |
26
24
  * Use when a value must be IValue but can be undefined
27
25
  * 1. `let a = obj.$key` to `const a = ensure(obj.$key)`
28
26
  */
29
- export declare function ensure(data: unknown): IValue<any>;
27
+ export declare function ensure<T extends object>(obj: T | null | undefined, key: keyof T): IValue<undefined> | T[keyof T];
30
28
  /**
31
29
  * Used for destruction with computed values
32
30
  * 1. `{[a]: a1} = {x: 2}` to `{[a]: a1 = match("a1")} = {x: 2}`
33
31
  * 1. `{[a]: a1 = 3} = {x: 2}` to `{[a]: a1 = match("a1", 3)} = {x: 2}`
34
32
  */
35
- export declare function match(name: string | number | symbol, data?: unknown): any;
33
+ export declare function match(name: string | number | symbol, data?: unknown, createRef?: typeof ref): any;
36
34
  /**
37
35
  * Set a value of a field (alternative to proxies)
38
36
  * 1. `obj.$key = 23` to `set(obj, "$key", 23)`
39
37
  * 2. `arr[0] = 23` to `set(arr, 0, 23)`
40
38
  */
41
- export declare function set(o: object, key: string | symbol | number, value: unknown): unknown;
42
- export declare function extract<T>(value: IValue<T> | T): T;
39
+ export declare function set(o: object, key: string | symbol | number, value: unknown, createRef?: typeof ref): unknown;
40
+ export declare function forward<T>(value: IValue<T>): Expression<T, [v: T]>;
41
+ export declare function backward<T>(value: IValue<T>): Reference<T>;
@@ -1,2 +1,3 @@
1
1
  import { IValue } from "vasille";
2
- export declare function awaited<T>(target: () => Promise<T>): [IValue<unknown>, IValue<unknown>, () => void];
2
+ import { ref } from "./internal.js";
3
+ export declare function awaited<T>(target: () => Promise<T>, createRef?: typeof ref): [IValue<unknown>, IValue<unknown>, () => void];