native-document 1.0.54 → 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/docs/anchor.md +17 -17
- package/hrm.js +7 -0
- package/package.json +1 -1
- 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 +1 -1
- 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/wrappers/ElementCreator.js +1 -1
- package/src/wrappers/SingletonView.js +1 -1
package/docs/anchor.md
CHANGED
|
@@ -10,7 +10,7 @@ Anchors are instances of the Anchor class that use two comment nodes as invisibl
|
|
|
10
10
|
|
|
11
11
|
```javascript
|
|
12
12
|
// Create an anchor instance
|
|
13
|
-
const anchor =
|
|
13
|
+
const anchor = Anchor("My Content");
|
|
14
14
|
// Or using the alias
|
|
15
15
|
const anchor = new NativeDocumentFragment("My Content");
|
|
16
16
|
|
|
@@ -39,7 +39,7 @@ fragment.appendChild(Div("Standard fragment content"));
|
|
|
39
39
|
|
|
40
40
|
```javascript
|
|
41
41
|
// Anchor is a NativeDocument class
|
|
42
|
-
const anchor =
|
|
42
|
+
const anchor = Anchor("Dynamic Area");
|
|
43
43
|
anchor.appendChild(Div("Dynamic content")); // Uses comment markers system
|
|
44
44
|
```
|
|
45
45
|
|
|
@@ -49,8 +49,8 @@ anchor.appendChild(Div("Dynamic content")); // Uses comment markers system
|
|
|
49
49
|
|
|
50
50
|
```javascript
|
|
51
51
|
// Create anchor with custom identifier
|
|
52
|
-
const contentAnchor =
|
|
53
|
-
const listAnchor =
|
|
52
|
+
const contentAnchor = Anchor("Content Area");
|
|
53
|
+
const listAnchor = Anchor("Todo List");
|
|
54
54
|
|
|
55
55
|
// Anchor needs to be added to parent container
|
|
56
56
|
const container = Div();
|
|
@@ -60,7 +60,7 @@ container.appendChild(contentAnchor);
|
|
|
60
60
|
### appendChild() - Add Content Between Markers
|
|
61
61
|
|
|
62
62
|
```javascript
|
|
63
|
-
const anchor =
|
|
63
|
+
const anchor = Anchor("Dynamic Section");
|
|
64
64
|
const container = Div();
|
|
65
65
|
container.appendChild(anchor);
|
|
66
66
|
|
|
@@ -80,7 +80,7 @@ anchor.appendChild(Div("Dynamic content 2"));
|
|
|
80
80
|
### insertBefore() - Positioned Insertion
|
|
81
81
|
|
|
82
82
|
```javascript
|
|
83
|
-
const anchor =
|
|
83
|
+
const anchor = Anchor("Ordered Content");
|
|
84
84
|
const element1 = Div("First");
|
|
85
85
|
const element2 = Div("Second");
|
|
86
86
|
|
|
@@ -92,7 +92,7 @@ anchor.insertBefore(element2, element1); // Inserts before element1
|
|
|
92
92
|
### replaceContent() - Replace All Content
|
|
93
93
|
|
|
94
94
|
```javascript
|
|
95
|
-
const anchor =
|
|
95
|
+
const anchor = Anchor("Replaceable");
|
|
96
96
|
anchor.appendChild(Div("Old content"));
|
|
97
97
|
|
|
98
98
|
// Replace all content between markers with new content
|
|
@@ -104,7 +104,7 @@ anchor.replaceContent(Div("New content"));
|
|
|
104
104
|
### remove() vs removeChildren() vs clear()
|
|
105
105
|
|
|
106
106
|
```javascript
|
|
107
|
-
const anchor =
|
|
107
|
+
const anchor = Anchor("Content Management");
|
|
108
108
|
|
|
109
109
|
// Remove all content between markers (markers remain)
|
|
110
110
|
anchor.remove(); // Content cleared, anchor can be reused
|
|
@@ -126,7 +126,7 @@ anchor.removeWithAnchors(); // Destroys the entire anchor system
|
|
|
126
126
|
### Access Markers
|
|
127
127
|
|
|
128
128
|
```javascript
|
|
129
|
-
const anchor =
|
|
129
|
+
const anchor = Anchor("My Anchor");
|
|
130
130
|
|
|
131
131
|
// Get the start and end comment nodes
|
|
132
132
|
const start = anchor.startElement();
|
|
@@ -141,7 +141,7 @@ console.log(end.textContent); // "/ Anchor End My Anchor"
|
|
|
141
141
|
### Working with Arrays of Content
|
|
142
142
|
|
|
143
143
|
```javascript
|
|
144
|
-
const anchor =
|
|
144
|
+
const anchor = Anchor("Multi Content");
|
|
145
145
|
const container = Div();
|
|
146
146
|
container.appendChild(anchor);
|
|
147
147
|
|
|
@@ -158,7 +158,7 @@ anchor.appendChild([
|
|
|
158
158
|
### Dynamic Content Updates
|
|
159
159
|
|
|
160
160
|
```javascript
|
|
161
|
-
const contentAnchor =
|
|
161
|
+
const contentAnchor = Anchor("Dynamic Updates");
|
|
162
162
|
const isLoading = Observable(true);
|
|
163
163
|
const data = Observable(null);
|
|
164
164
|
|
|
@@ -248,7 +248,7 @@ const fragment = Fragment(
|
|
|
248
248
|
|
|
249
249
|
```javascript
|
|
250
250
|
// Dynamic content area that can be updated multiple times
|
|
251
|
-
const anchor =
|
|
251
|
+
const anchor = Anchor("Updates");
|
|
252
252
|
anchor.appendChild(Div("Initial content"));
|
|
253
253
|
anchor.remove(); // Clear content
|
|
254
254
|
anchor.appendChild(Div("New content")); // Add different content - markers remain
|
|
@@ -259,7 +259,7 @@ anchor.appendChild(Div("New content")); // Add different content - markers remai
|
|
|
259
259
|
### Memory Management
|
|
260
260
|
```javascript
|
|
261
261
|
// Anchors are automatically cleaned up when removed from DOM
|
|
262
|
-
const anchor =
|
|
262
|
+
const anchor = Anchor("Temporary");
|
|
263
263
|
|
|
264
264
|
// Manual cleanup if needed
|
|
265
265
|
anchor.removeWithAnchors(); // Fully destroys anchor and frees memory
|
|
@@ -285,7 +285,7 @@ anchor.appendChild(Div("Item 2"));
|
|
|
285
285
|
```javascript
|
|
286
286
|
|
|
287
287
|
function ConditionalList(condition, items) {
|
|
288
|
-
const anchor =
|
|
288
|
+
const anchor = Anchor("ConditionalList");
|
|
289
289
|
|
|
290
290
|
const updateContent = (value) => {
|
|
291
291
|
console.log(value);
|
|
@@ -320,9 +320,9 @@ document.body.appendChild(Div([
|
|
|
320
320
|
|
|
321
321
|
```javascript
|
|
322
322
|
function LayoutManager() {
|
|
323
|
-
const header =
|
|
324
|
-
const content =
|
|
325
|
-
const footer =
|
|
323
|
+
const header = Anchor("Header");
|
|
324
|
+
const content = Anchor("Content");
|
|
325
|
+
const footer = Anchor("Footer");
|
|
326
326
|
|
|
327
327
|
return {
|
|
328
328
|
setHeader: (component) => header.replaceContent(component),
|
package/hrm.js
ADDED
package/package.json
CHANGED
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) {
|
|
@@ -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
|
+
}
|
|
@@ -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();
|