native-document 1.0.53 → 1.0.55
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/dist/native-document.dev.js +337 -86
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.devtools.min.js +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/anchor.md +17 -17
- package/hrm.js +7 -0
- package/index.js +2 -1
- package/package.json +1 -1
- package/src/data/Observable.js +3 -2
- package/src/data/ObservableArray.js +8 -2
- package/src/data/ObservableItem.js +23 -1
- package/src/data/observable-helpers/array.js +3 -2
- package/src/data/observable-helpers/object.js +31 -43
- package/src/elements/anchor.js +2 -0
- package/src/elements/control/for-each-array.js +1 -1
- package/src/elements/control/for-each.js +1 -1
- package/src/elements/control/show-if.js +1 -1
- package/src/elements/control/switch.js +14 -3
- package/src/hrm/ComponentRegistry.js +83 -0
- package/src/hrm/hrm.hook.template.js +52 -0
- package/src/hrm/nd-vite-hot-reload.js +46 -0
- package/src/hrm/transformComponent.js +32 -0
- package/src/utils/helpers.js +33 -1
- package/src/utils/validator.js +1 -1
- package/src/wrappers/ElementCreator.js +1 -1
- package/src/wrappers/NDElement.js +83 -1
- package/src/wrappers/NdPrototype.js +12 -2
- package/src/wrappers/SingletonView.js +1 -1
- package/types/memoize.d.ts +16 -6
- package/types/nd-element.d.ts +245 -231
- package/types/observable.d.ts +24 -5
|
@@ -5,13 +5,15 @@ import ObservableChecker from "./ObservableChecker";
|
|
|
5
5
|
import PluginsManager from "../utils/plugins-manager";
|
|
6
6
|
import Validator from "../utils/validator";
|
|
7
7
|
import {ObservableWhen} from "./ObservableWhen";
|
|
8
|
+
import {deepClone} from "../utils/helpers.js";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
*
|
|
11
12
|
* @param {*} value
|
|
13
|
+
* @param {{ propagation: boolean, reset: boolean} | null} configs
|
|
12
14
|
* @class ObservableItem
|
|
13
15
|
*/
|
|
14
|
-
export default function ObservableItem(value) {
|
|
16
|
+
export default function ObservableItem(value, configs = null) {
|
|
15
17
|
this.$previousValue = null;
|
|
16
18
|
this.$currentValue = value;
|
|
17
19
|
this.$isCleanedUp = false;
|
|
@@ -20,6 +22,14 @@ export default function ObservableItem(value) {
|
|
|
20
22
|
this.$watchers = null;
|
|
21
23
|
|
|
22
24
|
this.$memoryId = null;
|
|
25
|
+
|
|
26
|
+
if(configs) {
|
|
27
|
+
this.configs = configs;
|
|
28
|
+
if(configs.reset) {
|
|
29
|
+
this.$initialValue = Validator.isObject(value) ? deepClone(value) : value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
23
33
|
PluginsManager.emit('CreateObservable', this);
|
|
24
34
|
}
|
|
25
35
|
|
|
@@ -259,4 +269,16 @@ ObservableItem.prototype.toBool = function() {
|
|
|
259
269
|
|
|
260
270
|
ObservableItem.prototype.toggle = function() {
|
|
261
271
|
this.set(!this.$currentValue);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
ObservableItem.prototype.reset = function() {
|
|
275
|
+
if(!this.configs?.reset) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const resetValue = (Validator.isObject(this.$initialValue))
|
|
279
|
+
? deepClone(this.$initialValue, (observable) => {
|
|
280
|
+
observable.reset();
|
|
281
|
+
})
|
|
282
|
+
: this.$initialValue;
|
|
283
|
+
this.set(resetValue)
|
|
262
284
|
};
|
|
@@ -5,8 +5,9 @@ import ObservableArray from "../ObservableArray.js";
|
|
|
5
5
|
/**
|
|
6
6
|
*
|
|
7
7
|
* @param {Array} target
|
|
8
|
+
* @param {{propagation: boolean, deep: boolean, reset: boolean}|null} configs
|
|
8
9
|
* @returns {ObservableArray}
|
|
9
10
|
*/
|
|
10
|
-
Observable.array = function(target) {
|
|
11
|
-
return new ObservableArray(target);
|
|
11
|
+
Observable.array = function(target, configs = null) {
|
|
12
|
+
return new ObservableArray(target, configs);
|
|
12
13
|
};
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import Validator from "../../utils/validator";
|
|
2
2
|
import {Observable} from "../Observable";
|
|
3
|
-
import ObservableItem from "../ObservableItem";
|
|
4
|
-
|
|
5
|
-
|
|
6
3
|
|
|
7
4
|
const ObservableObjectValue = function(data) {
|
|
8
5
|
const result = {};
|
|
@@ -45,40 +42,47 @@ const ObservableGet = function(target, property) {
|
|
|
45
42
|
/**
|
|
46
43
|
*
|
|
47
44
|
* @param {Object} initialValue
|
|
48
|
-
* @param {{propagation: boolean, deep: boolean}} configs
|
|
45
|
+
* @param {{propagation: boolean, deep: boolean, reset: boolean}|null} configs
|
|
49
46
|
* @returns {Proxy}
|
|
50
47
|
*/
|
|
51
|
-
Observable.init = function(initialValue,
|
|
48
|
+
Observable.init = function(initialValue, configs = null) {
|
|
52
49
|
const data = {};
|
|
53
50
|
for(const key in initialValue) {
|
|
54
51
|
const itemValue = initialValue[key];
|
|
55
52
|
if(Array.isArray(itemValue)) {
|
|
56
|
-
if(deep) {
|
|
53
|
+
if(configs?.deep !== false) {
|
|
57
54
|
const mappedItemValue = itemValue.map(item => {
|
|
58
55
|
if(Validator.isJson(item)) {
|
|
59
|
-
return Observable.json(item,
|
|
56
|
+
return Observable.json(item, configs);
|
|
60
57
|
}
|
|
61
58
|
if(Validator.isArray(item)) {
|
|
62
|
-
return Observable.array(item,
|
|
59
|
+
return Observable.array(item, configs);
|
|
63
60
|
}
|
|
64
|
-
return Observable(item);
|
|
61
|
+
return Observable(item, configs);
|
|
65
62
|
});
|
|
66
|
-
data[key] = Observable.array(mappedItemValue,
|
|
63
|
+
data[key] = Observable.array(mappedItemValue, configs);
|
|
67
64
|
continue;
|
|
68
65
|
}
|
|
69
|
-
data[key] = Observable.array(itemValue,
|
|
66
|
+
data[key] = Observable.array(itemValue, configs);
|
|
70
67
|
continue;
|
|
71
68
|
}
|
|
72
69
|
if(Validator.isObservable(itemValue) || Validator.isProxy(itemValue)) {
|
|
73
70
|
data[key] = itemValue;
|
|
74
71
|
continue;
|
|
75
72
|
}
|
|
76
|
-
data[key] = Observable(itemValue);
|
|
73
|
+
data[key] = Observable(itemValue, configs);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const $reset = () => {
|
|
77
|
+
for(const key in data) {
|
|
78
|
+
const item = data[key];
|
|
79
|
+
item.reset();
|
|
80
|
+
}
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
const $val = () => ObservableObjectValue(data);
|
|
80
84
|
|
|
81
|
-
const $clone = () => Observable.init($val(),
|
|
85
|
+
const $clone = () => Observable.init($val(), configs);
|
|
82
86
|
|
|
83
87
|
const $updateWith = (values) => {
|
|
84
88
|
Observable.update(proxy, values);
|
|
@@ -88,34 +92,17 @@ Observable.init = function(initialValue, { propagation= false, deep = true } = {
|
|
|
88
92
|
|
|
89
93
|
const proxy = new Proxy(data, {
|
|
90
94
|
get(target, property) {
|
|
91
|
-
if(property === '__isProxy__') {
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
if(property === '$
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
if(property === '$
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
if(property === '
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
if(property === '$observables') {
|
|
104
|
-
return Object.values(target);
|
|
105
|
-
}
|
|
106
|
-
if(property === '$set' || property === '$updateWith') {
|
|
107
|
-
return $updateWith;
|
|
108
|
-
}
|
|
109
|
-
if(property === '$get') {
|
|
110
|
-
return $get;
|
|
111
|
-
}
|
|
112
|
-
if(property === '$val') {
|
|
113
|
-
return $val;
|
|
114
|
-
}
|
|
115
|
-
if(target[property] !== undefined) {
|
|
116
|
-
return target[property];
|
|
117
|
-
}
|
|
118
|
-
return undefined;
|
|
95
|
+
if(property === '__isProxy__') { return true; }
|
|
96
|
+
if(property === '$value') { return $val() }
|
|
97
|
+
if(property === 'get' || property === '$get') { return $get; }
|
|
98
|
+
if(property === 'val' || property === '$val') { return $val; }
|
|
99
|
+
if(property === 'set' || property === '$set' || property === '$updateWith') { return $updateWith; }
|
|
100
|
+
if(property === 'observables' || property === '$observables') { return Object.values(target); }
|
|
101
|
+
if(property === 'keys'|| property === '$keys') { return Object.keys(initialValue); }
|
|
102
|
+
if(property === 'clone' || property === '$clone') { return $clone; }
|
|
103
|
+
if(property === 'reset') { return $reset; }
|
|
104
|
+
if(property === 'configs') { return configs; }
|
|
105
|
+
return target[property];
|
|
119
106
|
},
|
|
120
107
|
set(target, prop, newValue) {
|
|
121
108
|
if(target[prop] !== undefined) {
|
|
@@ -166,6 +153,7 @@ Observable.value = function(data) {
|
|
|
166
153
|
|
|
167
154
|
Observable.update = function($target, newData) {
|
|
168
155
|
const data = Validator.isProxy(newData) ? newData.$value : newData;
|
|
156
|
+
const configs = $target.configs;
|
|
169
157
|
|
|
170
158
|
for(const key in data) {
|
|
171
159
|
const targetItem = $target[key];
|
|
@@ -178,9 +166,9 @@ Observable.update = function($target, newData) {
|
|
|
178
166
|
if(Validator.isObservable(firstElementFromOriginalValue) || Validator.isProxy(firstElementFromOriginalValue)) {
|
|
179
167
|
const newValues = newValue.map(item => {
|
|
180
168
|
if(Validator.isProxy(firstElementFromOriginalValue)) {
|
|
181
|
-
return Observable.init(item);
|
|
169
|
+
return Observable.init(item, configs);
|
|
182
170
|
}
|
|
183
|
-
return Observable(item);
|
|
171
|
+
return Observable(item, configs);
|
|
184
172
|
});
|
|
185
173
|
targetItem.set(newValues);
|
|
186
174
|
continue;
|
package/src/elements/anchor.js
CHANGED
|
@@ -105,6 +105,8 @@ export default function Anchor(name, isUniqueChild = false) {
|
|
|
105
105
|
parent.insertBefore(child, anchorEnd);
|
|
106
106
|
};
|
|
107
107
|
|
|
108
|
+
element.setContent = element.replaceContent;
|
|
109
|
+
|
|
108
110
|
element.insertBefore = function(child, anchor = null) {
|
|
109
111
|
element.appendChild(child, anchor);
|
|
110
112
|
};
|
|
@@ -6,7 +6,7 @@ import { ElementCreator } from "../../wrappers/ElementCreator";
|
|
|
6
6
|
import NativeDocumentError from "../../errors/NativeDocumentError";
|
|
7
7
|
|
|
8
8
|
export function ForEachArray(data, callback, key, configs = {}) {
|
|
9
|
-
const element =
|
|
9
|
+
const element = Anchor('ForEach Array');
|
|
10
10
|
const blockEnd = element.endElement();
|
|
11
11
|
const blockStart = element.startElement();
|
|
12
12
|
|
|
@@ -15,7 +15,7 @@ import NativeDocumentError from "../../errors/NativeDocumentError";
|
|
|
15
15
|
* @returns {DocumentFragment}
|
|
16
16
|
*/
|
|
17
17
|
export function ForEach(data, callback, key, { shouldKeepItemsInCache = false } = {}) {
|
|
18
|
-
const element =
|
|
18
|
+
const element = Anchor('ForEach');
|
|
19
19
|
const blockEnd = element.endElement();
|
|
20
20
|
const blockStart = element.startElement();
|
|
21
21
|
|
|
@@ -16,7 +16,7 @@ export const ShowIf = function(condition, child, { comment = null, shouldKeepInC
|
|
|
16
16
|
if(!(Validator.isObservable(condition)) && !Validator.isObservableWhenResult(condition)) {
|
|
17
17
|
return DebugManager.warn('ShowIf', "ShowIf : condition must be an Observable / "+comment, condition);
|
|
18
18
|
}
|
|
19
|
-
const element =
|
|
19
|
+
const element = Anchor('Show if : '+(comment || ''));
|
|
20
20
|
|
|
21
21
|
let childElement = null;
|
|
22
22
|
const getChildElement = () => {
|
|
@@ -18,7 +18,7 @@ export const Match = function($condition, values, shouldKeepInCache = true) {
|
|
|
18
18
|
throw new NativeDocumentError("Toggle : condition must be an Observable");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const anchor =
|
|
21
|
+
const anchor = Anchor('Match');
|
|
22
22
|
const cache = new Map();
|
|
23
23
|
|
|
24
24
|
const getItem = function(key) {
|
|
@@ -49,9 +49,20 @@ export const Match = function($condition, values, shouldKeepInCache = true) {
|
|
|
49
49
|
if(content) {
|
|
50
50
|
anchor.appendChild(content);
|
|
51
51
|
}
|
|
52
|
-
})
|
|
52
|
+
});
|
|
53
53
|
|
|
54
|
-
return anchor
|
|
54
|
+
return anchor.nd.with({
|
|
55
|
+
add(key, view, shouldFocusOn = false) {
|
|
56
|
+
values[key] = view;
|
|
57
|
+
if(shouldFocusOn) {
|
|
58
|
+
$condition.set(key);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
remove(key) {
|
|
62
|
+
shouldKeepInCache && cache.delete(key);
|
|
63
|
+
delete values[key];
|
|
64
|
+
}
|
|
65
|
+
});
|
|
55
66
|
}
|
|
56
67
|
|
|
57
68
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {Anchor} from "../../elements.js";
|
|
2
|
+
|
|
3
|
+
const ComponentRegistry = (function() {
|
|
4
|
+
console.log('ça s excecute')
|
|
5
|
+
|
|
6
|
+
const registry = new Map();
|
|
7
|
+
|
|
8
|
+
const wrapper = function(id, factory, metadata, registryItem) {
|
|
9
|
+
const factoryName = factory.name;
|
|
10
|
+
|
|
11
|
+
return function(...args) {
|
|
12
|
+
const lastParams = args[0];
|
|
13
|
+
console.log({ lastParams })
|
|
14
|
+
if(lastParams?.__instance) {
|
|
15
|
+
const instance = lastParams.__instance;
|
|
16
|
+
const componentArgs = Array.from(instance.context.args).pop();
|
|
17
|
+
const newInstance = factory(...componentArgs);
|
|
18
|
+
instance.anchor.setContent(newInstance);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const instance = factory(...args);
|
|
22
|
+
const anchor = Anchor(factoryName);
|
|
23
|
+
anchor.setContent(instance);
|
|
24
|
+
registryItem.instances.add({
|
|
25
|
+
anchor,
|
|
26
|
+
context: {
|
|
27
|
+
args
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
console.log({ instance, anchor });
|
|
31
|
+
return anchor;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} id
|
|
38
|
+
* @param {Function} factory
|
|
39
|
+
* @param {Object} metadata
|
|
40
|
+
*/
|
|
41
|
+
register(id, factory, metadata = {}) {
|
|
42
|
+
if (!registry.has(id)) {
|
|
43
|
+
registry.set(id, {
|
|
44
|
+
factory,
|
|
45
|
+
instances: new Set(),
|
|
46
|
+
metadata,
|
|
47
|
+
states: {},
|
|
48
|
+
version: 0
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return wrapper(id, factory, metadata, registry.get(id));
|
|
52
|
+
},
|
|
53
|
+
update(id, newFactory) {
|
|
54
|
+
const component = registry.get(id);
|
|
55
|
+
if(!component) {
|
|
56
|
+
console.warn(`[HMR] Component ${id} not found`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
console.log(`[HMR] Updating ${component.instances.size} instance(s) of ${id}`);
|
|
60
|
+
const oldFactory = component.factory;
|
|
61
|
+
component.factory = newFactory;
|
|
62
|
+
component.version++;
|
|
63
|
+
const instances = Array.from(component.instances);
|
|
64
|
+
for (const instance of instances) {
|
|
65
|
+
try {
|
|
66
|
+
this.updateInstance(instance, newFactory);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error('[HMR] Update failed:', error);
|
|
69
|
+
// Rollback
|
|
70
|
+
component.factory = oldFactory;
|
|
71
|
+
component.version--;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
updateInstance(instance, newFactory) {
|
|
77
|
+
console.log(Array.from(registry.entries()))
|
|
78
|
+
return newFactory({ __instance: instance });
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}());
|
|
82
|
+
|
|
83
|
+
export default ComponentRegistry;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
if (import.meta.hot) {
|
|
3
|
+
import.meta.hot.accept((newModule) => {
|
|
4
|
+
console.log('[HMR Browser] Update accepted for ${id}');
|
|
5
|
+
|
|
6
|
+
if (!newModule) {
|
|
7
|
+
console.error('[HMR Browser] newModule is undefined!');
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
if (typeof window === 'undefined') {
|
|
13
|
+
console.error('[HMR Browser] window is undefined!');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
} catch (e) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// const NativeDocument = window.NativeDocument;
|
|
21
|
+
// if (!NativeDocument) {
|
|
22
|
+
// console.error('[HMR Browser] NativeDocument not found on window!');
|
|
23
|
+
// return;
|
|
24
|
+
// }
|
|
25
|
+
|
|
26
|
+
if (!ComponentRegistry) {
|
|
27
|
+
console.error('[HMR Browser] ComponentRegistry not found!');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log('[HMR Browser] Calling ComponentRegistry.update()');
|
|
32
|
+
try {
|
|
33
|
+
ComponentRegistry.update('${id}', newModule.default);
|
|
34
|
+
console.log('[HMR Browser] ✓ Update successful');
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('[HMR Browser] ✗ Update failed:', error);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
import.meta.hot.on('vite:error', (payload) => {
|
|
41
|
+
console.error('[HMR Browser] Vite error:', payload);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
import.meta.hot.on('nd:update', (payload) => {
|
|
46
|
+
console.error('[HMR Browser] ND:Update:', payload);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
import.meta.hot.dispose(() => {
|
|
50
|
+
console.log('[HMR Browser] Disposing ${id}');
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// vite-plugin-native-document-hmr.js
|
|
2
|
+
import transformComponent from "./transformComponent.js";
|
|
3
|
+
|
|
4
|
+
export default function NdViteHotReload(options) {
|
|
5
|
+
const {
|
|
6
|
+
include = /\.nd\.js$/,
|
|
7
|
+
preserveState = true
|
|
8
|
+
} = options;
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
name: 'vite-plugin-native-document-hmr',
|
|
12
|
+
apply: 'serve',
|
|
13
|
+
enforce: 'post',
|
|
14
|
+
|
|
15
|
+
handleHotUpdate({ file, server, modules }) {
|
|
16
|
+
if (!include.test(file)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Notify the browser about the change
|
|
21
|
+
server.ws.send({
|
|
22
|
+
type: 'nd-hmr-file',
|
|
23
|
+
event: 'nd:update',
|
|
24
|
+
data: { file, msg: 'The content has changed' }
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// We will manage all manually
|
|
28
|
+
return modules.filter(Boolean);
|
|
29
|
+
},
|
|
30
|
+
transform(code, id) {
|
|
31
|
+
if (!include.test(id)) return null;
|
|
32
|
+
if (id.includes('node_modules')) return null;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
return transformComponent(id, code, { preserveState });
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error(`[NativeDocument] Transform error in ${id}:`, error);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
configResolved() {
|
|
43
|
+
console.log('[NativeDocument] HMR Plugin loaded ✓');
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
// import { parse } from '@babel/parser';
|
|
4
|
+
import MagicString from 'magic-string';
|
|
5
|
+
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
export default function transformComponent(id, code) {
|
|
12
|
+
// TODO: move this line outside the function
|
|
13
|
+
const hrmHookTemplate = fs.readFileSync(__dirname + '/hrm.hook.template.js', 'utf8');
|
|
14
|
+
const s = new MagicString('');
|
|
15
|
+
|
|
16
|
+
const data = {
|
|
17
|
+
id: id,
|
|
18
|
+
};
|
|
19
|
+
let hrmHookTemplateFormatted = hrmHookTemplate;
|
|
20
|
+
for(const key in data) {
|
|
21
|
+
hrmHookTemplateFormatted = hrmHookTemplateFormatted.replace(new RegExp("\\$\{"+key+"}", 'ig'), data[key]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
s.append('import ComponentRegistry from "native-document/src/hrm/ComponentRegistry";');
|
|
25
|
+
s.append(code);
|
|
26
|
+
s.append(hrmHookTemplateFormatted);
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
code: s.toString(),
|
|
30
|
+
map: s.generateMap({ source: id, hires: true })
|
|
31
|
+
};
|
|
32
|
+
}
|
package/src/utils/helpers.js
CHANGED
|
@@ -57,4 +57,36 @@ export const getKey = (item, defaultKey, key) => {
|
|
|
57
57
|
|
|
58
58
|
export const trim = function(str, char) {
|
|
59
59
|
return str.replace(new RegExp(`^[${char}]+|[${char}]+$`, 'g'), '');
|
|
60
|
-
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const deepClone = (value, onObservableFound) => {
|
|
63
|
+
// Primitives
|
|
64
|
+
if (value === null || typeof value !== 'object') {
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Dates
|
|
69
|
+
if (value instanceof Date) {
|
|
70
|
+
return new Date(value.getTime());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Arrays
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
return value.map(item => deepClone(item));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Observables - keep the référence
|
|
79
|
+
if (Validator.isObservable(value)) {
|
|
80
|
+
onObservableFound && onObservableFound(value);
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Objects
|
|
85
|
+
const cloned = {};
|
|
86
|
+
for (const key in value) {
|
|
87
|
+
if (value.hasOwnProperty(key)) {
|
|
88
|
+
cloned[key] = deepClone(value[key]);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return cloned;
|
|
92
|
+
};
|
package/src/utils/validator.js
CHANGED
|
@@ -50,7 +50,7 @@ const Validator = {
|
|
|
50
50
|
return typeof value === 'function' && value.constructor.name === 'AsyncFunction';
|
|
51
51
|
},
|
|
52
52
|
isObject(value) {
|
|
53
|
-
return typeof value === 'object';
|
|
53
|
+
return typeof value === 'object' && value !== null;
|
|
54
54
|
},
|
|
55
55
|
isJson(value) {
|
|
56
56
|
return typeof value === 'object' && value !== null && !Array.isArray(value) && value.constructor.name === 'Object';
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import DocumentObserver from "./DocumentObserver";
|
|
2
2
|
import PluginsManager from "../utils/plugins-manager";
|
|
3
3
|
import Validator from "../utils/validator";
|
|
4
|
+
import NativeDocumentError from "../errors/NativeDocumentError.js";
|
|
5
|
+
import DebugManager from "../utils/debug-manager.js";
|
|
6
|
+
|
|
4
7
|
import {EVENTS} from "../utils/events";
|
|
5
8
|
|
|
6
9
|
export function NDElement(element) {
|
|
@@ -19,6 +22,11 @@ NDElement.prototype.ref = function(target, name) {
|
|
|
19
22
|
return this;
|
|
20
23
|
};
|
|
21
24
|
|
|
25
|
+
NDElement.prototype.refSelf = function(target, name) {
|
|
26
|
+
target[name] = this;
|
|
27
|
+
return this;
|
|
28
|
+
};
|
|
29
|
+
|
|
22
30
|
NDElement.prototype.unmountChildren = function() {
|
|
23
31
|
let element = this.$element;
|
|
24
32
|
for(let i = 0, length = element.children.length; i < length; i++) {
|
|
@@ -209,4 +217,78 @@ for(const event of EVENTS) {
|
|
|
209
217
|
captureEventWrapper(this.$element, eventName, directHandler);
|
|
210
218
|
return this;
|
|
211
219
|
};
|
|
212
|
-
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
NDElement.prototype.with = function(methods) {
|
|
223
|
+
if (!methods || typeof methods !== 'object') {
|
|
224
|
+
throw new NativeDocumentError('extend() requires an object of methods');
|
|
225
|
+
}
|
|
226
|
+
if(process.env.NODE_ENV === 'development') {
|
|
227
|
+
if (!this.$localExtensions) {
|
|
228
|
+
this.$localExtensions = new Map();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
for (const name in methods) {
|
|
233
|
+
const method = methods[name];
|
|
234
|
+
|
|
235
|
+
if (typeof method !== 'function') {
|
|
236
|
+
console.warn(`⚠️ extends(): "${name}" is not a function, skipping`);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if(process.env.NODE_ENV === 'development') {
|
|
240
|
+
if (this[name] && !this.$localExtensions.has(name)) {
|
|
241
|
+
DebugManager.warn('NDElement.extend', `Method "${name}" already exists and will be overwritten`);
|
|
242
|
+
}
|
|
243
|
+
this.$localExtensions.set(name, method);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
this[name] = method.bind(this);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return this;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
NDElement.extend = function(methods) {
|
|
253
|
+
if (!methods || typeof methods !== 'object') {
|
|
254
|
+
throw new NativeDocumentError('NDElement.extend() requires an object of methods');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (Array.isArray(methods)) {
|
|
258
|
+
throw new NativeDocumentError('NDElement.extend() requires an object, not an array');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const protectedMethods = new Set([
|
|
262
|
+
'constructor', 'valueOf', '$element', '$observer',
|
|
263
|
+
'ref', 'remove', 'cleanup', 'with', 'extend', 'attach',
|
|
264
|
+
'lifecycle', 'mounted', 'unmounted', 'unmountChildren'
|
|
265
|
+
]);
|
|
266
|
+
|
|
267
|
+
for (const name in methods) {
|
|
268
|
+
if (!methods.hasOwnProperty(name)) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const method = methods[name];
|
|
273
|
+
|
|
274
|
+
if (typeof method !== 'function') {
|
|
275
|
+
DebugManager.warn('NDElement.extend', `"${name}" is not a function, skipping`);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (protectedMethods.has(name)) {
|
|
280
|
+
DebugManager.error('NDElement.extend', `Cannot override protected method "${name}"`);
|
|
281
|
+
throw new NativeDocumentError(`Cannot override protected method "${name}"`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (NDElement.prototype[name]) {
|
|
285
|
+
DebugManager.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
NDElement.prototype[name] = method;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
PluginsManager.emit('NDElementExtended', methods);
|
|
292
|
+
|
|
293
|
+
return NDElement;
|
|
294
|
+
};
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { NDElement } from "./NDElement";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const property = {
|
|
4
4
|
configurable: true,
|
|
5
5
|
get() {
|
|
6
|
-
return
|
|
6
|
+
return new NDElement(this);
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
Object.defineProperty(HTMLElement.prototype, 'nd', property);
|
|
11
|
+
|
|
12
|
+
Object.defineProperty(DocumentFragment.prototype, 'nd', property);
|
|
13
|
+
Object.defineProperty(NDElement.prototype, 'nd', {
|
|
14
|
+
configurable: true,
|
|
15
|
+
get: function() {
|
|
16
|
+
return this;
|
|
7
17
|
}
|
|
8
18
|
});
|
|
9
19
|
|
|
@@ -21,7 +21,7 @@ export function SingletonView($viewCreator) {
|
|
|
21
21
|
|
|
22
22
|
this.createSection = (name, fn) => {
|
|
23
23
|
$components = $components || {};
|
|
24
|
-
const anchor =
|
|
24
|
+
const anchor = Anchor('Component '+name);
|
|
25
25
|
|
|
26
26
|
$components[name] = function(...args) {
|
|
27
27
|
anchor.removeChildren();
|