@erickxavier/no-js 1.5.2 → 1.6.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/dist/cjs/no.js +5 -5
- package/dist/cjs/no.js.map +4 -4
- package/dist/esm/no.js +5 -5
- package/dist/esm/no.js.map +4 -4
- package/dist/iife/no.js +6 -6
- package/dist/iife/no.js.map +4 -4
- package/package.json +1 -1
- package/src/context.js +28 -2
- package/src/devtools.js +264 -0
- package/src/directives/http.js +3 -0
- package/src/directives/state.js +5 -0
- package/src/index.js +3 -13
- package/src/registry.js +18 -0
- package/src/router.js +8 -0
package/package.json
CHANGED
package/src/context.js
CHANGED
|
@@ -2,19 +2,25 @@
|
|
|
2
2
|
// REACTIVE CONTEXT
|
|
3
3
|
// ═══════════════════════════════════════════════════════════════════════
|
|
4
4
|
|
|
5
|
-
import { _stores, _refs, _routerInstance, _currentEl } from "./globals.js";
|
|
5
|
+
import { _config, _stores, _refs, _routerInstance, _currentEl } from "./globals.js";
|
|
6
6
|
import { _i18n } from "./i18n.js";
|
|
7
|
+
import { _devtoolsEmit, _ctxRegistry } from "./devtools.js";
|
|
7
8
|
|
|
8
9
|
let _batchDepth = 0;
|
|
9
10
|
const _batchQueue = new Set();
|
|
11
|
+
let _ctxId = 0;
|
|
12
|
+
|
|
13
|
+
export function _resetCtxId() { _ctxId = 0; }
|
|
10
14
|
|
|
11
15
|
export function _startBatch() {
|
|
12
16
|
_batchDepth++;
|
|
17
|
+
_devtoolsEmit("batch:start", { depth: _batchDepth });
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
export function _endBatch() {
|
|
16
21
|
_batchDepth--;
|
|
17
22
|
if (_batchDepth === 0 && _batchQueue.size > 0) {
|
|
23
|
+
_devtoolsEmit("batch:end", { depth: 0, queueSize: _batchQueue.size });
|
|
18
24
|
const fns = [..._batchQueue];
|
|
19
25
|
_batchQueue.clear();
|
|
20
26
|
fns.forEach((fn) => {
|
|
@@ -28,6 +34,7 @@ export function createContext(data = {}, parent = null) {
|
|
|
28
34
|
const listeners = new Set();
|
|
29
35
|
const raw = {};
|
|
30
36
|
Object.assign(raw, data);
|
|
37
|
+
if (_config.devtools) raw.__devtoolsId = ++_ctxId;
|
|
31
38
|
let notifying = false;
|
|
32
39
|
|
|
33
40
|
function notify() {
|
|
@@ -94,7 +101,15 @@ export function createContext(data = {}, parent = null) {
|
|
|
94
101
|
set(target, key, value) {
|
|
95
102
|
const old = target[key];
|
|
96
103
|
target[key] = value;
|
|
97
|
-
if (old !== value)
|
|
104
|
+
if (old !== value) {
|
|
105
|
+
notify();
|
|
106
|
+
_devtoolsEmit("ctx:updated", {
|
|
107
|
+
id: target.__devtoolsId,
|
|
108
|
+
key,
|
|
109
|
+
oldValue: old,
|
|
110
|
+
newValue: value,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
98
113
|
return true;
|
|
99
114
|
},
|
|
100
115
|
has(target, key) {
|
|
@@ -105,6 +120,17 @@ export function createContext(data = {}, parent = null) {
|
|
|
105
120
|
};
|
|
106
121
|
|
|
107
122
|
const proxy = new Proxy(raw, handler);
|
|
123
|
+
|
|
124
|
+
if (_config.devtools && raw.__devtoolsId) {
|
|
125
|
+
_ctxRegistry.set(raw.__devtoolsId, proxy);
|
|
126
|
+
_devtoolsEmit("ctx:created", {
|
|
127
|
+
id: raw.__devtoolsId,
|
|
128
|
+
parentId: parent?.__raw?.__devtoolsId ?? null,
|
|
129
|
+
keys: Object.keys(data),
|
|
130
|
+
elementTag: _currentEl?.tagName?.toLowerCase() ?? null,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
108
134
|
return proxy;
|
|
109
135
|
}
|
|
110
136
|
|
package/src/devtools.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
2
|
+
// DEVTOOLS PROTOCOL
|
|
3
|
+
// Zero-overhead runtime inspection, mutation, and monitoring.
|
|
4
|
+
// All hooks are guarded by _config.devtools — no cost when disabled.
|
|
5
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
import { _config, _stores, _refs, _routerInstance } from "./globals.js";
|
|
8
|
+
import { _i18n } from "./i18n.js";
|
|
9
|
+
|
|
10
|
+
// ─── Context registry (populated by createContext when devtools enabled) ────
|
|
11
|
+
// Maps __devtoolsId → Proxy reference for inspect/mutate commands.
|
|
12
|
+
export const _ctxRegistry = new Map();
|
|
13
|
+
|
|
14
|
+
// ─── Emit a devtools event ──────────────────────────────────────────────────
|
|
15
|
+
export function _devtoolsEmit(type, data) {
|
|
16
|
+
if (!_config.devtools || typeof window === "undefined") return;
|
|
17
|
+
window.dispatchEvent(
|
|
18
|
+
new CustomEvent("nojs:devtools", {
|
|
19
|
+
detail: { type, data, timestamp: Date.now() },
|
|
20
|
+
}),
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ─── Serialization helpers ──────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
function _safeSnapshot(proxy) {
|
|
27
|
+
if (!proxy || !proxy.__isProxy) return null;
|
|
28
|
+
const raw = proxy.__raw;
|
|
29
|
+
const snapshot = {};
|
|
30
|
+
for (const key of Object.keys(raw)) {
|
|
31
|
+
if (key.startsWith("__")) continue;
|
|
32
|
+
const val = raw[key];
|
|
33
|
+
if (val && typeof val === "object" && val.__isProxy) {
|
|
34
|
+
snapshot[key] = _safeSnapshot(val);
|
|
35
|
+
} else {
|
|
36
|
+
try {
|
|
37
|
+
// Verify serializable — drop functions and circular refs
|
|
38
|
+
JSON.stringify(val);
|
|
39
|
+
snapshot[key] = val;
|
|
40
|
+
} catch {
|
|
41
|
+
snapshot[key] = String(val);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return snapshot;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function _elementTag(el) {
|
|
49
|
+
if (!el || !el.tagName) return null;
|
|
50
|
+
const tag = el.tagName.toLowerCase();
|
|
51
|
+
const id = el.id ? `#${el.id}` : "";
|
|
52
|
+
const cls = el.className && typeof el.className === "string"
|
|
53
|
+
? "." + el.className.trim().split(/\s+/).join(".")
|
|
54
|
+
: "";
|
|
55
|
+
return tag + id + cls;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─── Inspect commands ───────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
function _inspectElement(selector) {
|
|
61
|
+
const el = document.querySelector(selector);
|
|
62
|
+
if (!el) return { error: "Element not found", selector };
|
|
63
|
+
const ctx = el.__ctx;
|
|
64
|
+
return {
|
|
65
|
+
selector,
|
|
66
|
+
tag: _elementTag(el),
|
|
67
|
+
hasContext: !!ctx,
|
|
68
|
+
contextId: ctx?.__raw?.__devtoolsId ?? null,
|
|
69
|
+
data: ctx ? _safeSnapshot(ctx) : null,
|
|
70
|
+
directives: [...el.attributes]
|
|
71
|
+
.filter((a) => !["class", "id", "style"].includes(a.name))
|
|
72
|
+
.map((a) => ({ name: a.name, value: a.value })),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function _inspectStore(name) {
|
|
77
|
+
const store = _stores[name];
|
|
78
|
+
if (!store) return { error: "Store not found", name };
|
|
79
|
+
return {
|
|
80
|
+
name,
|
|
81
|
+
contextId: store.__raw?.__devtoolsId ?? null,
|
|
82
|
+
data: _safeSnapshot(store),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function _inspectTree(selector) {
|
|
87
|
+
const root = selector ? document.querySelector(selector) : document.body;
|
|
88
|
+
if (!root) return { error: "Root not found", selector };
|
|
89
|
+
|
|
90
|
+
function walk(el) {
|
|
91
|
+
const ctx = el.__ctx;
|
|
92
|
+
const node = {
|
|
93
|
+
tag: _elementTag(el),
|
|
94
|
+
contextId: ctx?.__raw?.__devtoolsId ?? null,
|
|
95
|
+
children: [],
|
|
96
|
+
};
|
|
97
|
+
for (const child of el.children) {
|
|
98
|
+
if (child.tagName === "TEMPLATE" || child.tagName === "SCRIPT") continue;
|
|
99
|
+
if (child.__ctx || child.__declared) {
|
|
100
|
+
node.children.push(walk(child));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return node;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return walk(root);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function _mutateContext(id, key, value) {
|
|
110
|
+
const proxy = _ctxRegistry.get(id);
|
|
111
|
+
if (!proxy) return { error: "Context not found", id };
|
|
112
|
+
proxy[key] = value;
|
|
113
|
+
return { ok: true, id, key };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function _mutateStore(name, key, value) {
|
|
117
|
+
const store = _stores[name];
|
|
118
|
+
if (!store) return { error: "Store not found", name };
|
|
119
|
+
store[key] = value;
|
|
120
|
+
return { ok: true, name, key };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function _getStats() {
|
|
124
|
+
let listenerCount = 0;
|
|
125
|
+
for (const [, proxy] of _ctxRegistry) {
|
|
126
|
+
if (proxy.__listeners) listenerCount += proxy.__listeners.size;
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
contexts: _ctxRegistry.size,
|
|
130
|
+
stores: Object.keys(_stores).length,
|
|
131
|
+
listeners: listenerCount,
|
|
132
|
+
refs: Object.keys(_refs).length,
|
|
133
|
+
hasRouter: !!_routerInstance,
|
|
134
|
+
locale: _i18n.locale,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ─── Highlight overlay ──────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
let _highlightOverlay = null;
|
|
141
|
+
|
|
142
|
+
function _highlightElement(selector) {
|
|
143
|
+
_unhighlight();
|
|
144
|
+
const el = document.querySelector(selector);
|
|
145
|
+
if (!el) return;
|
|
146
|
+
const rect = el.getBoundingClientRect();
|
|
147
|
+
_highlightOverlay = document.createElement("div");
|
|
148
|
+
_highlightOverlay.id = "__nojs_devtools_highlight__";
|
|
149
|
+
Object.assign(_highlightOverlay.style, {
|
|
150
|
+
position: "fixed",
|
|
151
|
+
top: rect.top + "px",
|
|
152
|
+
left: rect.left + "px",
|
|
153
|
+
width: rect.width + "px",
|
|
154
|
+
height: rect.height + "px",
|
|
155
|
+
background: "rgba(66, 133, 244, 0.25)",
|
|
156
|
+
border: "2px solid rgba(66, 133, 244, 0.8)",
|
|
157
|
+
pointerEvents: "none",
|
|
158
|
+
zIndex: "2147483647",
|
|
159
|
+
borderRadius: "3px",
|
|
160
|
+
});
|
|
161
|
+
document.body.appendChild(_highlightOverlay);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function _unhighlight() {
|
|
165
|
+
if (_highlightOverlay) {
|
|
166
|
+
_highlightOverlay.remove();
|
|
167
|
+
_highlightOverlay = null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ─── Command handler ────────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
function _handleDevtoolsCommand(event) {
|
|
174
|
+
const { command, args = {} } = event.detail || {};
|
|
175
|
+
let result;
|
|
176
|
+
|
|
177
|
+
switch (command) {
|
|
178
|
+
case "inspect:element":
|
|
179
|
+
result = _inspectElement(args.selector);
|
|
180
|
+
break;
|
|
181
|
+
case "inspect:store":
|
|
182
|
+
result = _inspectStore(args.name);
|
|
183
|
+
break;
|
|
184
|
+
case "inspect:tree":
|
|
185
|
+
result = _inspectTree(args.selector);
|
|
186
|
+
break;
|
|
187
|
+
case "mutate:context":
|
|
188
|
+
result = _mutateContext(args.id, args.key, args.value);
|
|
189
|
+
break;
|
|
190
|
+
case "mutate:store":
|
|
191
|
+
result = _mutateStore(args.name, args.key, args.value);
|
|
192
|
+
break;
|
|
193
|
+
case "get:config":
|
|
194
|
+
result = { ..._config };
|
|
195
|
+
break;
|
|
196
|
+
case "get:routes":
|
|
197
|
+
result = _routerInstance ? _routerInstance.routes || [] : [];
|
|
198
|
+
break;
|
|
199
|
+
case "get:stats":
|
|
200
|
+
result = _getStats();
|
|
201
|
+
break;
|
|
202
|
+
case "highlight:element":
|
|
203
|
+
_highlightElement(args.selector);
|
|
204
|
+
result = { ok: true };
|
|
205
|
+
break;
|
|
206
|
+
case "unhighlight":
|
|
207
|
+
_unhighlight();
|
|
208
|
+
result = { ok: true };
|
|
209
|
+
break;
|
|
210
|
+
default:
|
|
211
|
+
result = { error: "Unknown command", command };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Respond with result
|
|
215
|
+
window.dispatchEvent(
|
|
216
|
+
new CustomEvent("nojs:devtools:response", {
|
|
217
|
+
detail: { command, result, timestamp: Date.now() },
|
|
218
|
+
}),
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ─── Initialization ─────────────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
export function initDevtools(nojs) {
|
|
225
|
+
if (!_config.devtools || typeof window === "undefined") return;
|
|
226
|
+
|
|
227
|
+
// Listen for commands
|
|
228
|
+
window.addEventListener("nojs:devtools:cmd", _handleDevtoolsCommand);
|
|
229
|
+
|
|
230
|
+
// Expose public API on window
|
|
231
|
+
window.__NOJS_DEVTOOLS__ = {
|
|
232
|
+
// Data access
|
|
233
|
+
stores: _stores,
|
|
234
|
+
config: _config,
|
|
235
|
+
refs: _refs,
|
|
236
|
+
router: _routerInstance,
|
|
237
|
+
version: nojs.version,
|
|
238
|
+
|
|
239
|
+
// Inspect API
|
|
240
|
+
inspect: (selector) => _inspectElement(selector),
|
|
241
|
+
inspectStore: (name) => _inspectStore(name),
|
|
242
|
+
inspectTree: (selector) => _inspectTree(selector),
|
|
243
|
+
stats: () => _getStats(),
|
|
244
|
+
|
|
245
|
+
// Mutation API
|
|
246
|
+
mutate: (id, key, value) => _mutateContext(id, key, value),
|
|
247
|
+
mutateStore: (name, key, value) => _mutateStore(name, key, value),
|
|
248
|
+
|
|
249
|
+
// Visual
|
|
250
|
+
highlight: (selector) => _highlightElement(selector),
|
|
251
|
+
unhighlight: () => _unhighlight(),
|
|
252
|
+
|
|
253
|
+
// Event subscription shorthand
|
|
254
|
+
on: (type, fn) => {
|
|
255
|
+
const handler = (e) => {
|
|
256
|
+
if (!type || e.detail.type === type) fn(e.detail);
|
|
257
|
+
};
|
|
258
|
+
window.addEventListener("nojs:devtools", handler);
|
|
259
|
+
return () => window.removeEventListener("nojs:devtools", handler);
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
console.log("[No.JS DevTools] enabled — access via window.__NOJS_DEVTOOLS__");
|
|
264
|
+
}
|
package/src/directives/http.js
CHANGED
|
@@ -16,6 +16,7 @@ import { evaluate, _execStatement, _interpolate } from "../evaluate.js";
|
|
|
16
16
|
import { _doFetch, _cacheGet, _cacheSet } from "../fetch.js";
|
|
17
17
|
import { findContext, _clearDeclared, _cloneTemplate } from "../dom.js";
|
|
18
18
|
import { registerDirective, processTree } from "../registry.js";
|
|
19
|
+
import { _devtoolsEmit } from "../devtools.js";
|
|
19
20
|
|
|
20
21
|
const HTTP_METHODS = ["get", "post", "put", "patch", "delete"];
|
|
21
22
|
|
|
@@ -199,6 +200,7 @@ for (const method of HTTP_METHODS) {
|
|
|
199
200
|
_routerInstance.push(redirectPath);
|
|
200
201
|
|
|
201
202
|
_emitEvent("fetch:success", { url: resolvedUrl, data });
|
|
203
|
+
_devtoolsEmit("fetch:success", { method, url: resolvedUrl });
|
|
202
204
|
} catch (err) {
|
|
203
205
|
// SwitchMap: silently ignore aborted requests
|
|
204
206
|
if (err.name === "AbortError") return;
|
|
@@ -209,6 +211,7 @@ for (const method of HTTP_METHODS) {
|
|
|
209
211
|
);
|
|
210
212
|
_emitEvent("fetch:error", { url: resolvedUrl, error: err });
|
|
211
213
|
_emitEvent("error", { url: resolvedUrl, error: err });
|
|
214
|
+
_devtoolsEmit("fetch:error", { method, url: resolvedUrl, error: err.message });
|
|
212
215
|
|
|
213
216
|
if (errorTpl) {
|
|
214
217
|
const clone = _cloneTemplate(errorTpl);
|
package/src/directives/state.js
CHANGED
|
@@ -7,6 +7,7 @@ import { createContext } from "../context.js";
|
|
|
7
7
|
import { evaluate, _execStatement } from "../evaluate.js";
|
|
8
8
|
import { findContext } from "../dom.js";
|
|
9
9
|
import { registerDirective } from "../registry.js";
|
|
10
|
+
import { _devtoolsEmit } from "../devtools.js";
|
|
10
11
|
|
|
11
12
|
registerDirective("state", {
|
|
12
13
|
priority: 0,
|
|
@@ -63,6 +64,10 @@ registerDirective("store", {
|
|
|
63
64
|
? evaluate(valueAttr, createContext()) || {}
|
|
64
65
|
: {};
|
|
65
66
|
_stores[storeName] = createContext(data);
|
|
67
|
+
_devtoolsEmit("store:created", {
|
|
68
|
+
name: storeName,
|
|
69
|
+
keys: Object.keys(data),
|
|
70
|
+
});
|
|
66
71
|
}
|
|
67
72
|
_log("store", storeName);
|
|
68
73
|
},
|
package/src/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import { evaluate, resolve } from "./evaluate.js";
|
|
|
22
22
|
import { findContext, _loadRemoteTemplates, _loadRemoteTemplatesPhase1, _loadRemoteTemplatesPhase2, _processTemplateIncludes } from "./dom.js";
|
|
23
23
|
import { registerDirective, processTree } from "./registry.js";
|
|
24
24
|
import { _createRouter } from "./router.js";
|
|
25
|
+
import { initDevtools } from "./devtools.js";
|
|
25
26
|
|
|
26
27
|
// Side-effect imports: register built-in filters
|
|
27
28
|
import "./filters.js";
|
|
@@ -126,18 +127,7 @@ const NoJS = {
|
|
|
126
127
|
_loadRemoteTemplatesPhase2();
|
|
127
128
|
|
|
128
129
|
// DevTools integration
|
|
129
|
-
|
|
130
|
-
window.__NOJS_DEVTOOLS__ = {
|
|
131
|
-
stores: _stores,
|
|
132
|
-
config: _config,
|
|
133
|
-
refs: _refs,
|
|
134
|
-
router: _routerInstance,
|
|
135
|
-
filters: Object.keys(_filters),
|
|
136
|
-
validators: Object.keys(_validators),
|
|
137
|
-
version: NoJS.version,
|
|
138
|
-
};
|
|
139
|
-
_log("DevTools enabled — access via window.__NOJS_DEVTOOLS__");
|
|
140
|
-
}
|
|
130
|
+
initDevtools(NoJS);
|
|
141
131
|
},
|
|
142
132
|
|
|
143
133
|
// Register custom directive
|
|
@@ -218,7 +208,7 @@ const NoJS = {
|
|
|
218
208
|
resolve,
|
|
219
209
|
|
|
220
210
|
// Version
|
|
221
|
-
version: "1.
|
|
211
|
+
version: "1.6.0",
|
|
222
212
|
};
|
|
223
213
|
|
|
224
214
|
export default NoJS;
|
package/src/registry.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { _currentEl, _setCurrentEl, _storeWatchers } from "./globals.js";
|
|
6
6
|
import { _i18nListeners } from "./i18n.js";
|
|
7
|
+
import { _devtoolsEmit, _ctxRegistry } from "./devtools.js";
|
|
7
8
|
|
|
8
9
|
const _directives = new Map();
|
|
9
10
|
|
|
@@ -52,6 +53,13 @@ export function processElement(el) {
|
|
|
52
53
|
m.init(el, m.name, m.value);
|
|
53
54
|
}
|
|
54
55
|
_setCurrentEl(prev);
|
|
56
|
+
|
|
57
|
+
if (matched.length > 0) {
|
|
58
|
+
_devtoolsEmit("directive:init", {
|
|
59
|
+
element: el.tagName?.toLowerCase(),
|
|
60
|
+
directives: matched.map((m) => ({ name: m.name, value: m.value })),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
55
63
|
}
|
|
56
64
|
|
|
57
65
|
export function processTree(root) {
|
|
@@ -68,6 +76,8 @@ export function processTree(root) {
|
|
|
68
76
|
// ─── Disposal: proactive cleanup of watchers/listeners/disposers ────────
|
|
69
77
|
|
|
70
78
|
function _disposeElement(node) {
|
|
79
|
+
const ctxId = node.__ctx?.__raw?.__devtoolsId;
|
|
80
|
+
|
|
71
81
|
if (node.__ctx && node.__ctx.__listeners) {
|
|
72
82
|
for (const fn of node.__ctx.__listeners) {
|
|
73
83
|
_storeWatchers.delete(fn);
|
|
@@ -80,6 +90,14 @@ function _disposeElement(node) {
|
|
|
80
90
|
node.__disposers = null;
|
|
81
91
|
}
|
|
82
92
|
node.__declared = false;
|
|
93
|
+
|
|
94
|
+
if (ctxId != null) {
|
|
95
|
+
_ctxRegistry.delete(ctxId);
|
|
96
|
+
_devtoolsEmit("ctx:disposed", {
|
|
97
|
+
id: ctxId,
|
|
98
|
+
elementTag: node.tagName?.toLowerCase(),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
83
101
|
}
|
|
84
102
|
|
|
85
103
|
export function _disposeTree(root) {
|
package/src/router.js
CHANGED
|
@@ -8,6 +8,7 @@ import { evaluate } from "./evaluate.js";
|
|
|
8
8
|
import { findContext, _clearDeclared, _loadTemplateElement, _processTemplateIncludes } from "./dom.js";
|
|
9
9
|
import { processTree, _disposeTree } from "./registry.js";
|
|
10
10
|
import { _animateIn } from "./animations.js";
|
|
11
|
+
import { _devtoolsEmit } from "./devtools.js";
|
|
11
12
|
|
|
12
13
|
export function _createRouter() {
|
|
13
14
|
const routes = [];
|
|
@@ -101,6 +102,13 @@ export function _createRouter() {
|
|
|
101
102
|
await _renderRoute(matched);
|
|
102
103
|
listeners.forEach((fn) => fn(current));
|
|
103
104
|
|
|
105
|
+
_devtoolsEmit("route:navigate", {
|
|
106
|
+
path: current.path,
|
|
107
|
+
params: current.params,
|
|
108
|
+
query: current.query,
|
|
109
|
+
hash: current.hash,
|
|
110
|
+
});
|
|
111
|
+
|
|
104
112
|
// Scroll to anchor if hash is present (e.g. route="/docs#cheatsheet")
|
|
105
113
|
if (current.hash) {
|
|
106
114
|
const anchorId = current.hash.slice(1);
|