dalila 1.1.1 → 1.2.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/README.md +75 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/watch-testing.d.ts +13 -0
- package/dist/core/watch-testing.js +16 -0
- package/dist/core/watch.d.ts +67 -5
- package/dist/core/watch.js +89 -13
- package/dist/internal/watch-testing.d.ts +1 -0
- package/dist/internal/watch-testing.js +8 -0
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Dalila is a **SPA**, **DOM-first**, **HTML natural** framework based on **signal
|
|
|
10
10
|
- 🚀 **Signals-based reactivity** - Automatic dependency tracking
|
|
11
11
|
- 🎯 **DOM-first rendering** - Direct DOM manipulation, no Virtual DOM
|
|
12
12
|
- 🔄 **Scope-based lifecycle** - Automatic cleanup (best effort)
|
|
13
|
+
- 🧿 **DOM lifecycle watchers** — `watch()` + helpers (`useEvent`, `useInterval`, `useTimeout`, `useFetch`) with scope-based cleanup
|
|
13
14
|
- 🛣️ **SPA router** - Basic routing with loaders and AbortSignal
|
|
14
15
|
- 📦 **Context system** - Reactive dependency injection
|
|
15
16
|
- 🔧 **Scheduler & batching** - Group updates into a single frame
|
|
@@ -95,6 +96,80 @@ effectAsync(async (signal) => {
|
|
|
95
96
|
});
|
|
96
97
|
```
|
|
97
98
|
|
|
99
|
+
### Lifecycle / Cleanup (Scopes + DOM)
|
|
100
|
+
|
|
101
|
+
Dalila is DOM-first. That means a lot of your "lifecycle" work is not React-like rendering —
|
|
102
|
+
it's **attaching listeners, timers, and async work to real DOM nodes**.
|
|
103
|
+
|
|
104
|
+
Dalila's rule is simple:
|
|
105
|
+
|
|
106
|
+
- **Inside a scope** → cleanup is automatic on `scope.dispose()`
|
|
107
|
+
- **Outside a scope** → you must call `dispose()` manually (Dalila warns once)
|
|
108
|
+
|
|
109
|
+
#### `watch(node, fn)` — DOM lifecycle primitive
|
|
110
|
+
|
|
111
|
+
`watch()` runs a reactive function while a DOM node is connected.
|
|
112
|
+
When the node disconnects, the effect is disposed. If the node reconnects later, it starts again.
|
|
113
|
+
It's the primitive that enables DOM lifecycle without a VDOM.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { watch, signal } from "dalila";
|
|
117
|
+
|
|
118
|
+
const count = signal(0);
|
|
119
|
+
|
|
120
|
+
const dispose = watch(someNode, () => {
|
|
121
|
+
// Reactive while connected because watch() runs this inside an effect()
|
|
122
|
+
someNode.textContent = String(count());
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// later (optional if inside a scope)
|
|
126
|
+
dispose();
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### Lifecycle helpers
|
|
130
|
+
|
|
131
|
+
Built on the same mental model, Dalila provides small helpers that always return an **idempotent** `dispose()`:
|
|
132
|
+
`useEvent`, `useInterval`, `useTimeout`, `useFetch`.
|
|
133
|
+
|
|
134
|
+
**Inside a scope (recommended)**
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { createScope, withScope, useEvent, useInterval, useFetch } from "dalila";
|
|
138
|
+
|
|
139
|
+
const scope = createScope();
|
|
140
|
+
|
|
141
|
+
withScope(scope, () => {
|
|
142
|
+
useEvent(button, "click", onClick);
|
|
143
|
+
useInterval(tick, 1000);
|
|
144
|
+
|
|
145
|
+
const user = useFetch("/api/user");
|
|
146
|
+
|
|
147
|
+
// Optional manual cleanup:
|
|
148
|
+
// user.dispose();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
scope.dispose(); // stops listener, interval, and aborts fetch
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Disposing the scope stops listeners/timers and aborts in-flight async work created inside the scope.
|
|
155
|
+
|
|
156
|
+
**Outside a scope (manual cleanup)**
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { useInterval } from "dalila";
|
|
160
|
+
|
|
161
|
+
const dispose = useInterval(() => console.log("tick"), 1000);
|
|
162
|
+
|
|
163
|
+
// later...
|
|
164
|
+
dispose(); // required
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### Why helpers vs native APIs?
|
|
168
|
+
|
|
169
|
+
You *can* use `addEventListener` / `setTimeout` / `setInterval` directly, but then cleanup becomes "manual discipline".
|
|
170
|
+
In a DOM-first app, listeners/timers are the #1 source of silent leaks when the UI changes.
|
|
171
|
+
Scopes make cleanup a **default**, not a convention — so UI changes don't silently leak listeners, timers, or in-flight async work.
|
|
172
|
+
|
|
98
173
|
### Conditional Rendering
|
|
99
174
|
|
|
100
175
|
Dalila provides two primitives for branching UI:
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./scope.js";
|
|
2
2
|
export * from "./signal.js";
|
|
3
|
-
export
|
|
3
|
+
export { watch, onMount, onCleanup, useEvent, useInterval, useTimeout, useFetch } from "./watch.js";
|
|
4
4
|
export * from "./when.js";
|
|
5
5
|
export * from "./match.js";
|
|
6
6
|
export * from "./for.js";
|
package/dist/core/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./scope.js";
|
|
2
2
|
export * from "./signal.js";
|
|
3
|
-
export
|
|
3
|
+
export { watch, onMount, onCleanup, useEvent, useInterval, useTimeout, useFetch } from "./watch.js";
|
|
4
4
|
export * from "./when.js";
|
|
5
5
|
export * from "./match.js";
|
|
6
6
|
export * from "./for.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Testing utilities for watch.ts
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT exported in the public API (src/index.ts).
|
|
5
|
+
* It should only be imported directly by test files.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Reset all warning flags to their initial state.
|
|
11
|
+
* Use this in tests to ensure deterministic warning behavior.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resetWarnings(): void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Testing utilities for watch.ts
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT exported in the public API (src/index.ts).
|
|
5
|
+
* It should only be imported directly by test files.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
import { __resetWarningsForTests } from './watch.js';
|
|
10
|
+
/**
|
|
11
|
+
* Reset all warning flags to their initial state.
|
|
12
|
+
* Use this in tests to ensure deterministic warning behavior.
|
|
13
|
+
*/
|
|
14
|
+
export function resetWarnings() {
|
|
15
|
+
__resetWarningsForTests();
|
|
16
|
+
}
|
package/dist/core/watch.d.ts
CHANGED
|
@@ -1,19 +1,81 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Reset warning flags (used by src/internal/watch-testing.ts for deterministic tests).
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
export declare function __resetWarningsForTests(): void;
|
|
6
|
+
/**
|
|
7
|
+
* watch(node, fn)
|
|
8
|
+
*
|
|
9
|
+
* Runs `fn` inside a reactive effect while `node` is connected to the document.
|
|
10
|
+
* - Signal reads inside `fn` are tracked (because `fn` runs inside effect()).
|
|
11
|
+
* - When the node disconnects, the effect is disposed.
|
|
12
|
+
* - If the node reconnects later, the effect may start again (best-effort, based on DOM mutations).
|
|
3
13
|
*
|
|
4
|
-
*
|
|
5
|
-
* 1)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
14
|
+
* Implementation notes / fixes:
|
|
15
|
+
* 1) Scope capture: the scope at watch() time is stored on the entry so effects that start later
|
|
16
|
+
* still get created "inside" the original scope (fixes late-start effects created outside scope).
|
|
17
|
+
* 2) Memory safety: watches uses WeakMap<Node, ...> so detached nodes are not kept alive.
|
|
18
|
+
* 3) Observer lifecycle: we track ctx.watchCount (WeakMap has no .size) to know when to disconnect.
|
|
8
19
|
*/
|
|
9
20
|
export declare function watch(node: Node, fn: () => void): () => void;
|
|
21
|
+
/**
|
|
22
|
+
* onMount(fn)
|
|
23
|
+
*
|
|
24
|
+
* Executes fn immediately. Minimal semantic helper to document mount-time logic in DOM-first code.
|
|
25
|
+
* Unlike React, there's no deferred execution or batching — it runs synchronously.
|
|
26
|
+
*/
|
|
10
27
|
export declare function onMount(fn: () => void): void;
|
|
28
|
+
/**
|
|
29
|
+
* onCleanup(fn)
|
|
30
|
+
*
|
|
31
|
+
* Registers fn to run when the current scope is disposed.
|
|
32
|
+
* If called outside a scope, fn is never called (no-op).
|
|
33
|
+
*/
|
|
11
34
|
export declare function onCleanup(fn: () => void): void;
|
|
35
|
+
/**
|
|
36
|
+
* useEvent(target, type, handler, options?)
|
|
37
|
+
*
|
|
38
|
+
* Attaches an event listener and returns an idempotent dispose() function.
|
|
39
|
+
* - Inside a scope: listener is removed automatically on scope.dispose().
|
|
40
|
+
* - Outside a scope: you must call dispose() manually (warns once).
|
|
41
|
+
*/
|
|
12
42
|
export declare function useEvent<T extends EventTarget>(target: T, type: string, handler: (event: Event) => void, options?: AddEventListenerOptions): () => void;
|
|
43
|
+
/**
|
|
44
|
+
* useInterval(fn, ms)
|
|
45
|
+
*
|
|
46
|
+
* Starts an interval and returns an idempotent dispose() function.
|
|
47
|
+
* - Inside a scope: interval is cleared automatically on scope.dispose().
|
|
48
|
+
* - Outside a scope: you must call dispose() manually (warns once).
|
|
49
|
+
*/
|
|
13
50
|
export declare function useInterval(fn: () => void, ms: number): () => void;
|
|
51
|
+
/**
|
|
52
|
+
* useTimeout(fn, ms)
|
|
53
|
+
*
|
|
54
|
+
* Starts a timeout and returns an idempotent dispose() function.
|
|
55
|
+
* - Inside a scope: timeout is cleared automatically on scope.dispose().
|
|
56
|
+
* - Outside a scope: you must call dispose() manually (warns once).
|
|
57
|
+
*/
|
|
14
58
|
export declare function useTimeout(fn: () => void, ms: number): () => void;
|
|
59
|
+
/**
|
|
60
|
+
* useFetch(url, options)
|
|
61
|
+
*
|
|
62
|
+
* Small convenience for "fetch + reactive state + abort" built on effectAsync().
|
|
63
|
+
* Returns { data, loading, error, dispose }.
|
|
64
|
+
*
|
|
65
|
+
* Behavior:
|
|
66
|
+
* - Runs the fetch inside effectAsync, which provides an AbortSignal.
|
|
67
|
+
* - If url is a function, it tracks signal reads (reactive).
|
|
68
|
+
* - Calling dispose() aborts the in-flight request (via effectAsync's signal).
|
|
69
|
+
* - Inside a scope: auto-disposed on scope.dispose().
|
|
70
|
+
* - Outside a scope: you must call dispose() manually (warns once).
|
|
71
|
+
*
|
|
72
|
+
* Limitations:
|
|
73
|
+
* - No refresh(), no caching, no invalidation.
|
|
74
|
+
* - For those features, use createResource / query().
|
|
75
|
+
*/
|
|
15
76
|
export declare function useFetch<T>(url: string | (() => string), options?: RequestInit): {
|
|
16
77
|
data: () => T | null;
|
|
17
78
|
loading: () => boolean;
|
|
18
79
|
error: () => Error | null;
|
|
80
|
+
dispose: () => void;
|
|
19
81
|
};
|
package/dist/core/watch.js
CHANGED
|
@@ -10,6 +10,14 @@ const documentWatchers = new WeakMap();
|
|
|
10
10
|
*/
|
|
11
11
|
let hasWarnedNoScope = false;
|
|
12
12
|
const warnedFunctions = new Set();
|
|
13
|
+
/**
|
|
14
|
+
* Reset warning flags (used by src/internal/watch-testing.ts for deterministic tests).
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export function __resetWarningsForTests() {
|
|
18
|
+
warnedFunctions.clear();
|
|
19
|
+
hasWarnedNoScope = false;
|
|
20
|
+
}
|
|
13
21
|
/**
|
|
14
22
|
* Walk a subtree recursively, calling visitor for each node.
|
|
15
23
|
* Supports any Node type (Element, Text, Comment, etc.) via childNodes.
|
|
@@ -23,8 +31,12 @@ function walkSubtree(root, visitor) {
|
|
|
23
31
|
}
|
|
24
32
|
}
|
|
25
33
|
/**
|
|
26
|
-
* Start a watch entry effect
|
|
27
|
-
*
|
|
34
|
+
* Start a watch entry effect inside the entry's captured scope (if any).
|
|
35
|
+
*
|
|
36
|
+
* Why:
|
|
37
|
+
* - `effect()` captures getCurrentScope() at creation time.
|
|
38
|
+
* - watch entries can start later (when a node becomes connected), so we must re-enter
|
|
39
|
+
* the original scope to preserve cleanup semantics.
|
|
28
40
|
*/
|
|
29
41
|
function startEntryEffect(entry) {
|
|
30
42
|
if (entry.effectStarted || entry.cleanup)
|
|
@@ -105,12 +117,18 @@ function getDocumentWatchContext(doc) {
|
|
|
105
117
|
return ctx;
|
|
106
118
|
}
|
|
107
119
|
/**
|
|
108
|
-
*
|
|
120
|
+
* watch(node, fn)
|
|
109
121
|
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
122
|
+
* Runs `fn` inside a reactive effect while `node` is connected to the document.
|
|
123
|
+
* - Signal reads inside `fn` are tracked (because `fn` runs inside effect()).
|
|
124
|
+
* - When the node disconnects, the effect is disposed.
|
|
125
|
+
* - If the node reconnects later, the effect may start again (best-effort, based on DOM mutations).
|
|
126
|
+
*
|
|
127
|
+
* Implementation notes / fixes:
|
|
128
|
+
* 1) Scope capture: the scope at watch() time is stored on the entry so effects that start later
|
|
129
|
+
* still get created "inside" the original scope (fixes late-start effects created outside scope).
|
|
130
|
+
* 2) Memory safety: watches uses WeakMap<Node, ...> so detached nodes are not kept alive.
|
|
131
|
+
* 3) Observer lifecycle: we track ctx.watchCount (WeakMap has no .size) to know when to disconnect.
|
|
114
132
|
*/
|
|
115
133
|
export function watch(node, fn) {
|
|
116
134
|
const currentScope = getCurrentScope();
|
|
@@ -160,9 +178,9 @@ export function watch(node, fn) {
|
|
|
160
178
|
const entries = ctx.watches.get(node);
|
|
161
179
|
if (entries) {
|
|
162
180
|
entries.delete(entry);
|
|
163
|
-
// If empty,
|
|
181
|
+
// If empty, delete the entry from WeakMap to free the Set immediately
|
|
164
182
|
if (entries.size === 0)
|
|
165
|
-
|
|
183
|
+
ctx.watches.delete(node);
|
|
166
184
|
}
|
|
167
185
|
// Decrement active watch count and disconnect observer if none left
|
|
168
186
|
ctx.watchCount--;
|
|
@@ -177,14 +195,33 @@ export function watch(node, fn) {
|
|
|
177
195
|
}
|
|
178
196
|
return dispose;
|
|
179
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* onMount(fn)
|
|
200
|
+
*
|
|
201
|
+
* Executes fn immediately. Minimal semantic helper to document mount-time logic in DOM-first code.
|
|
202
|
+
* Unlike React, there's no deferred execution or batching — it runs synchronously.
|
|
203
|
+
*/
|
|
180
204
|
export function onMount(fn) {
|
|
181
205
|
fn();
|
|
182
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* onCleanup(fn)
|
|
209
|
+
*
|
|
210
|
+
* Registers fn to run when the current scope is disposed.
|
|
211
|
+
* If called outside a scope, fn is never called (no-op).
|
|
212
|
+
*/
|
|
183
213
|
export function onCleanup(fn) {
|
|
184
214
|
const scope = getCurrentScope();
|
|
185
215
|
if (scope)
|
|
186
216
|
scope.onCleanup(fn);
|
|
187
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* useEvent(target, type, handler, options?)
|
|
220
|
+
*
|
|
221
|
+
* Attaches an event listener and returns an idempotent dispose() function.
|
|
222
|
+
* - Inside a scope: listener is removed automatically on scope.dispose().
|
|
223
|
+
* - Outside a scope: you must call dispose() manually (warns once).
|
|
224
|
+
*/
|
|
188
225
|
export function useEvent(target, type, handler, options) {
|
|
189
226
|
const scope = getCurrentScope();
|
|
190
227
|
if (!scope && !warnedFunctions.has('useEvent')) {
|
|
@@ -204,6 +241,13 @@ export function useEvent(target, type, handler, options) {
|
|
|
204
241
|
scope.onCleanup(dispose);
|
|
205
242
|
return dispose;
|
|
206
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* useInterval(fn, ms)
|
|
246
|
+
*
|
|
247
|
+
* Starts an interval and returns an idempotent dispose() function.
|
|
248
|
+
* - Inside a scope: interval is cleared automatically on scope.dispose().
|
|
249
|
+
* - Outside a scope: you must call dispose() manually (warns once).
|
|
250
|
+
*/
|
|
207
251
|
export function useInterval(fn, ms) {
|
|
208
252
|
const scope = getCurrentScope();
|
|
209
253
|
if (!scope && !warnedFunctions.has('useInterval')) {
|
|
@@ -223,6 +267,13 @@ export function useInterval(fn, ms) {
|
|
|
223
267
|
scope.onCleanup(dispose);
|
|
224
268
|
return dispose;
|
|
225
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* useTimeout(fn, ms)
|
|
272
|
+
*
|
|
273
|
+
* Starts a timeout and returns an idempotent dispose() function.
|
|
274
|
+
* - Inside a scope: timeout is cleared automatically on scope.dispose().
|
|
275
|
+
* - Outside a scope: you must call dispose() manually (warns once).
|
|
276
|
+
*/
|
|
226
277
|
export function useTimeout(fn, ms) {
|
|
227
278
|
const scope = getCurrentScope();
|
|
228
279
|
if (!scope && !warnedFunctions.has('useTimeout')) {
|
|
@@ -242,12 +293,34 @@ export function useTimeout(fn, ms) {
|
|
|
242
293
|
scope.onCleanup(dispose);
|
|
243
294
|
return dispose;
|
|
244
295
|
}
|
|
245
|
-
|
|
296
|
+
/**
|
|
297
|
+
* useFetch(url, options)
|
|
298
|
+
*
|
|
299
|
+
* Small convenience for "fetch + reactive state + abort" built on effectAsync().
|
|
300
|
+
* Returns { data, loading, error, dispose }.
|
|
301
|
+
*
|
|
302
|
+
* Behavior:
|
|
303
|
+
* - Runs the fetch inside effectAsync, which provides an AbortSignal.
|
|
304
|
+
* - If url is a function, it tracks signal reads (reactive).
|
|
305
|
+
* - Calling dispose() aborts the in-flight request (via effectAsync's signal).
|
|
306
|
+
* - Inside a scope: auto-disposed on scope.dispose().
|
|
307
|
+
* - Outside a scope: you must call dispose() manually (warns once).
|
|
308
|
+
*
|
|
309
|
+
* Limitations:
|
|
310
|
+
* - No refresh(), no caching, no invalidation.
|
|
311
|
+
* - For those features, use createResource / query().
|
|
312
|
+
*/
|
|
246
313
|
export function useFetch(url, options) {
|
|
314
|
+
const scope = getCurrentScope();
|
|
315
|
+
if (!scope && !warnedFunctions.has('useFetch')) {
|
|
316
|
+
warnedFunctions.add('useFetch');
|
|
317
|
+
console.warn('[Dalila] useFetch() called outside scope. ' +
|
|
318
|
+
'Request will not auto-cleanup. Call the returned dispose() function manually.');
|
|
319
|
+
}
|
|
247
320
|
const data = signal(null);
|
|
248
321
|
const loading = signal(false);
|
|
249
322
|
const error = signal(null);
|
|
250
|
-
effectAsync(async (signal) => {
|
|
323
|
+
const dispose = effectAsync(async (signal) => {
|
|
251
324
|
try {
|
|
252
325
|
loading.set(true);
|
|
253
326
|
error.set(null);
|
|
@@ -259,7 +332,7 @@ export function useFetch(url, options) {
|
|
|
259
332
|
if (!response.ok) {
|
|
260
333
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
261
334
|
}
|
|
262
|
-
const result = await response.json();
|
|
335
|
+
const result = (await response.json());
|
|
263
336
|
data.set(result);
|
|
264
337
|
}
|
|
265
338
|
catch (err) {
|
|
@@ -273,5 +346,8 @@ export function useFetch(url, options) {
|
|
|
273
346
|
}
|
|
274
347
|
}
|
|
275
348
|
});
|
|
276
|
-
|
|
349
|
+
// Match the other lifecycle helpers: if we are inside a scope, dispose automatically.
|
|
350
|
+
if (scope)
|
|
351
|
+
scope.onCleanup(dispose);
|
|
352
|
+
return { data, loading, error, dispose };
|
|
277
353
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resetWarnings(): void;
|
package/package.json
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dalila",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "DOM-first reactive framework based on signals",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
8
14
|
"scripts": {
|
|
9
15
|
"build": "tsc",
|
|
10
16
|
"dev": "tsc --watch",
|