native-document 1.0.54 → 1.0.56

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 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 = new Anchor("My Content");
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 = new Anchor("Dynamic Area");
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 = new Anchor("Content Area");
53
- const listAnchor = new Anchor("Todo List");
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 = new Anchor("Dynamic Section");
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 = new Anchor("Ordered Content");
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 = new Anchor("Replaceable");
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 = new Anchor("Content Management");
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 = new Anchor("My 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 = new Anchor("Multi Content");
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 = new Anchor("Dynamic Updates");
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 = new Anchor("Updates");
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 = new Anchor("Temporary");
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 = new Anchor("ConditionalList");
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 = new Anchor("Header");
324
- const content = new Anchor("Content");
325
- const footer = new Anchor("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
@@ -0,0 +1,7 @@
1
+ import NdViteHotReload from "./src/hrm/nd-vite-hot-reload.js";
2
+ import transformComponent from "./src/hrm/transformComponent.js";
3
+
4
+ export {
5
+ transformComponent,
6
+ NdViteHotReload,
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "native-document",
3
- "version": "1.0.54",
3
+ "version": "1.0.56",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -12,10 +12,13 @@
12
12
  "license": "ISC",
13
13
  "description": "",
14
14
  "devDependencies": {
15
+ "@babel/parser": "^7.28.5",
16
+ "@babel/traverse": "^7.28.5",
15
17
  "@rollup/plugin-alias": "^5.1.1",
16
18
  "@rollup/plugin-replace": "^6.0.2",
17
19
  "@rollup/plugin-terser": "^0.4.4",
18
20
  "eslint": "^9.33.0",
21
+ "magic-string": "^0.30.21",
19
22
  "rollup": "^4.53.3"
20
23
  }
21
- }
24
+ }
@@ -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 = new Anchor('ForEach Array');
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 = new Anchor('ForEach');
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 = new Anchor('Show if : '+(comment || ''));
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 = new Anchor('Match');
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,74 @@
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
+ import { parse } from '@babel/parser';
8
+ import { default as traverse } from '@babel/traverse';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+
14
+ export default function transformComponent(id, code) {
15
+ let hasDefaultExport = false;
16
+ let componentName = null;
17
+ let exportStart = 0;
18
+ let exportEnd = 0;
19
+ // TODO: move this line outside the function
20
+ const hrmHookTemplate = fs.readFileSync(__dirname + '/hrm.hook.template.js', 'utf8');
21
+ const s = new MagicString('');
22
+ const codeParsed = parse(code, {
23
+ sourceType: 'module',
24
+ plugins: []
25
+ });
26
+
27
+ console.log(traverse)
28
+ traverse(codeParsed, {
29
+ ExportDefaultDeclaration(path) {
30
+ hasDefaultExport = true;
31
+ const declaration = path.node.declaration;
32
+
33
+ if (declaration.id) {
34
+ componentName = declaration.id.name;
35
+ } else if (declaration.type === 'Identifier') {
36
+ componentName = declaration.name;
37
+ } else {
38
+ componentName = 'AnonymousComponent';
39
+ }
40
+
41
+ exportStart = path.node.start;
42
+ exportEnd = path.node.end;
43
+ }
44
+ });
45
+ if (!hasDefaultExport) {
46
+ return null;
47
+ }
48
+
49
+ const originalExport = code.slice(exportStart, exportEnd);
50
+ const hrmComponentName = `__HRM_${componentName}__`;
51
+ const newExport = originalExport.replace(
52
+ 'export default',
53
+ `const ${hrmComponentName} =`
54
+ );
55
+ s.overwrite(exportStart, exportEnd, newExport);
56
+
57
+ const data = {
58
+ id: id,
59
+ };
60
+ let hrmHookTemplateFormatted = hrmHookTemplate;
61
+ for(const key in data) {
62
+ hrmHookTemplateFormatted = hrmHookTemplateFormatted.replace(new RegExp("\\$\{"+key+"}", 'ig'), data[key]);
63
+ }
64
+
65
+ s.append('import ComponentRegistry from "native-document/src/hrm/ComponentRegistry";');
66
+ s.append(code);
67
+ s.append(`export default ComponentRegistry.register('${id}', ${hrmComponentName}, { preserveState: ${options.preserveState} });`);
68
+ s.append(hrmHookTemplateFormatted);
69
+
70
+ return {
71
+ code: s.toString(),
72
+ map: s.generateMap({ source: id, hires: true })
73
+ };
74
+ }
@@ -65,7 +65,7 @@ export const ElementCreator = {
65
65
  $nodeCache.set(name, node);
66
66
  return node.cloneNode();
67
67
  }
68
- return new Anchor('Fragment');
68
+ return Anchor('Fragment');
69
69
  },
70
70
  /**
71
71
  *
@@ -21,7 +21,7 @@ export function SingletonView($viewCreator) {
21
21
 
22
22
  this.createSection = (name, fn) => {
23
23
  $components = $components || {};
24
- const anchor = new Anchor('Component '+name);
24
+ const anchor = Anchor('Component '+name);
25
25
 
26
26
  $components[name] = function(...args) {
27
27
  anchor.removeChildren();