lahama 1.0.0 → 2.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.
Files changed (2) hide show
  1. package/dist/lahama.js +268 -0
  2. package/package.json +6 -5
package/dist/lahama.js ADDED
@@ -0,0 +1,268 @@
1
+ function withoutNulls(arr) {
2
+ return arr.filter((item) => item != null)
3
+ }
4
+ const a = { };
5
+ const b = { };
6
+ console.log(a === b);
7
+
8
+ const DOM_TYPES = {
9
+ TEXT : 'text',
10
+ ELEMENT : 'element',
11
+ FRAGMENT : 'fragment',
12
+ };
13
+ function h(tag, props = {} , children = []) {
14
+ return {
15
+ tag,
16
+ props,
17
+ children: mapTextNodes(withoutNulls(children)),
18
+ type : DOM_TYPES.ELEMENT,
19
+ }
20
+ }
21
+ function mapTextNodes(children) {
22
+ return children.map((child) =>
23
+ typeof child === 'string' ? hString(child) : child)
24
+ }
25
+ function hString(str) {
26
+ return { type : DOM_TYPES.TEXT, value : str}
27
+ }
28
+ function hFragment(vNodes) {
29
+ return {
30
+ type : DOM_TYPES.FRAGMENT,
31
+ children : mapTextNodes(withoutNulls(vNodes)),
32
+ }
33
+ }
34
+
35
+ function addEventListener(eventName, handler, el) {
36
+ el.addEventListener(eventName, handler);
37
+ return handler
38
+ }
39
+ function addEventListeners(listeners = {}, el) {
40
+ const addedListeners = {};
41
+ Object.entries(listeners).forEach(([eventName, handler]) => {
42
+ addedListeners[eventName] = addEventListener(eventName, handler, el);
43
+ });
44
+ return addedListeners
45
+ }
46
+ function removeEventListeners(listeners = {}, el) {
47
+ Object.entries(listeners).forEach(([eventName, handler]) => {
48
+ el.removeEventListener(eventName, handler);
49
+ });
50
+ }
51
+
52
+ function destroyDom(vdom) {
53
+ const { type } = vdom;
54
+ switch (type) {
55
+ case DOM_TYPES.TEXT : {
56
+ removeTextNode(vdom);
57
+ break
58
+ }
59
+ case DOM_TYPES.ELEMENT : {
60
+ removeElementNode(vdom);
61
+ break
62
+ }
63
+ case DOM_TYPES.FRAGMENT : {
64
+ removeFragmentNode(vdom);
65
+ break
66
+ }
67
+ default : {
68
+ throw new Error(`Can't destroy DOM of type ${type}`)
69
+ }
70
+ }
71
+ delete vdom.el;
72
+ }
73
+ function removeTextNode(vdom) {
74
+ const { el } = vdom;
75
+ el.remove();
76
+ }
77
+ function removeElementNode(vdom) {
78
+ const { el , children, listeners } = vdom;
79
+ el.remove();
80
+ children.forEach(destroyDom);
81
+ if (listeners) {
82
+ removeEventListeners(listeners, el);
83
+ delete vdom.listeners;
84
+ }
85
+ }
86
+ function removeFragmentNode(vdom) {
87
+ const { children } = vdom;
88
+ children.forEach(destroyDom);
89
+ }
90
+
91
+ function setAttributes(el, attrs) {
92
+ const { class : className, style, ...otherAttrs} = attrs;
93
+ if (className) {
94
+ setClass(el, className);
95
+ }
96
+ if (style) {
97
+ Object.entries(style).forEach(([prop, value]) => {
98
+ setStyle(el, prop, value);
99
+ });
100
+ }
101
+ for (const [name, value] of Object.entries(otherAttrs)) {
102
+ setAttribute(el, name, value);
103
+ }
104
+ }
105
+ function setClass(el, className) {
106
+ el.className = '';
107
+ if (typeof className === 'string') {
108
+ el.className = className;
109
+ }
110
+ if (Array.isArray(className)) {
111
+ el.classList.add(...className);
112
+ }
113
+ }
114
+ function setStyle(el, name, value) {
115
+ el.style[name] = value;
116
+ }
117
+ function removeAttributeCustom(el, name) {
118
+ el[name] = null;
119
+ el.removeAttribute(name);
120
+ }
121
+ function setAttribute(el, name, value) {
122
+ if (value == null) {
123
+ removeAttributeCustom(el, name);
124
+ } else if (name.startsWith('data-')) {
125
+ el.setAttribute(name, value);
126
+ } else {
127
+ el[name] = value;
128
+ }
129
+ }
130
+
131
+ function mountDom(vdom, parentEl, index) {
132
+ switch (vdom.type) {
133
+ case DOM_TYPES.TEXT : {
134
+ createTextNode(vdom, parentEl, index);
135
+ break
136
+ }
137
+ case DOM_TYPES.ELEMENT : {
138
+ createElementNode(vdom, parentEl, index);
139
+ break
140
+ }
141
+ case DOM_TYPES.FRAGMENT : {
142
+ createFragmentNodes(vdom, parentEl, index);
143
+ break
144
+ }
145
+ default : {
146
+ throw new Error(`Can't mount DOM of type: ${vdom.type}`)
147
+ }
148
+ }
149
+ }
150
+ function insert(el, parentEl, index) {
151
+ if (index == null) {
152
+ parentEl.append(el);
153
+ return
154
+ }
155
+ if (index < 0) {
156
+ throw new Error(
157
+ `Index must be a positive integer, got &{index}`)
158
+ }
159
+ const children = parentEl.childNodes;
160
+ if (index >= children.length) {
161
+ parentEl.append(el);
162
+ } else {
163
+ parentEl.insertBefore(el, children[index]);
164
+ }
165
+ }
166
+ function createTextNode(vdom, parentEl, index) {
167
+ const { value } = vdom;
168
+ const textNode = document.createTextNode(value);
169
+ vdom.el = textNode;
170
+ insert(textNode, parentEl, index);
171
+ }
172
+ function createElementNode(vdom, parentEl, index) {
173
+ const { tag, props , children } = vdom;
174
+ const element = document.createElement(tag);
175
+ addProps(element, props , vdom);
176
+ vdom.el = element;
177
+ //!function mountDom(vnode, parentEl) {
178
+ children.forEach((child) => mountDom(child, element));
179
+ insert(element, parentEl, index);
180
+ }
181
+ function addProps(el, props, vdom) {
182
+ const { on : events, ...attrs } = props;
183
+ vdom.listeners = addEventListeners(events, el);
184
+ setAttributes(el, attrs);
185
+ }
186
+ function createFragmentNodes(vdom, parentEl, index) {
187
+ const { children } = vdom;
188
+ vdom.el = parentEl;
189
+ children.forEach((child, i) => mountDom(child, parentEl, index ? index + i : null));
190
+ }
191
+
192
+ class Dispatcher {
193
+ #subs = new Map()
194
+ #afterHandlers = []
195
+ subscribe(commandName, handler) {
196
+ if (!this.#subs.has(commandName)) {
197
+ this.#subs.set(commandName, []);
198
+ }
199
+ const handlersArray = this.#subs.get(commandName);
200
+ if (handlersArray.includes(handler)) {
201
+ return () => {
202
+ }
203
+ }
204
+ handlersArray.push(handler);
205
+ return () => {
206
+ const idx = handlersArray.indexOf(handler);
207
+ handlersArray.splice(idx, 1);
208
+ }
209
+ }
210
+ afterEveryCommand(handler) {
211
+ this.#afterHandlers.push(handler);
212
+ return () => {
213
+ const idx = this.#afterHandlers.indexOf(handler);
214
+ this.#afterHandlers.splice(idx, 1);
215
+ }
216
+ }
217
+ dispatch(commandName, payload) {
218
+ if (this.#subs.has(commandName)) {
219
+ this.#subs.get(commandName).forEach((handler) => handler(payload));
220
+ } else {
221
+ console.warn(`No handlers for command : ${commandName}`);
222
+ }
223
+ this.#afterHandlers.forEach((handler) => handler());
224
+ }
225
+ }
226
+
227
+ function createApp({ state, view, reducers = {} }) {
228
+ let parentEl = null;
229
+ let vdom = null;
230
+ let isMounted = false;
231
+ const dispatcher = new Dispatcher();
232
+ const subscriptions = [dispatcher.afterEveryCommand(renderApp)];
233
+ function emit(eventName, payload) {
234
+ dispatcher.dispatch(eventName, payload);
235
+ }
236
+ for (const actionName in reducers) {
237
+ const reducer = reducers[actionName];
238
+ const subs = dispatcher.subscribe(actionName, (payload) => {
239
+ state = reducer(state, payload);
240
+ });
241
+ subscriptions.push(subs);
242
+ }
243
+ function renderApp() {
244
+ view(state, emit);
245
+ vdom = patchDom();
246
+ }
247
+ return {
248
+ mount(_parentEl) {
249
+ if (isMounted) {
250
+ throw new Error("The application is already mounted")
251
+ }
252
+ parentEl = _parentEl;
253
+ vdom = view(state, emit);
254
+ mountDom(vdom, parentEl);
255
+ isMounted = true;
256
+ },
257
+ unmount() {
258
+ destroyDom(vdom);
259
+ vdom = null;
260
+ subscriptions.forEach((unsubscribe) => unsubscribe());
261
+ isMounted = false;
262
+ },
263
+ }
264
+ }
265
+ function patchDom(vdom, newVdom, parentEl) {
266
+ }
267
+
268
+ export { createApp, h, hFragment, hString };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lahama",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "main": "dist/lahama.js",
6
6
  "files": [
@@ -12,7 +12,7 @@
12
12
  "lint": "eslint src",
13
13
  "lint:fix": "eslint src --fix",
14
14
  "test": "vitest",
15
- "test:run" : "vitest run"
15
+ "test:run": "vitest run"
16
16
  },
17
17
  "keywords": [],
18
18
  "author": "",
@@ -20,14 +20,15 @@
20
20
  "type": "module",
21
21
  "devDependencies": {
22
22
  "eslint": "^9.39.0",
23
- "jsdom": "^27.1.0",
23
+ "jsdom": "^27.2.0",
24
24
  "rollup": "^4.52.5",
25
25
  "rollup-plugin-cleanup": "^3.2.1",
26
26
  "rollup-plugin-filesize": "^10.0.0",
27
- "vitest": "^4.0.6"
27
+ "vitest": "^4.0.15"
28
28
  },
29
29
  "dependencies": {
30
30
  "@eslint/js": "^9.39.0",
31
- "globals": "^16.5.0"
31
+ "globals": "^16.5.0",
32
+ "lahama": "^1.1.0"
32
33
  }
33
34
  }