ripple 0.2.13 → 0.2.14

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Ripple is a TypeScript UI framework for the web",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.2.13",
6
+ "version": "0.2.14",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index.js",
9
9
  "main": "src/runtime/index.js",
@@ -48,6 +48,10 @@
48
48
  "default": "./src/jsx-runtime.js"
49
49
  }
50
50
  },
51
+ "imports": {
52
+ "#client": "./src/runtime/internal/client/types.d.ts",
53
+ "#client/constants": "./src/internal/client/constants.js"
54
+ },
51
55
  "dependencies": {
52
56
  "@jridgewell/sourcemap-codec": "^1.5.5",
53
57
  "acorn": "^8.15.0",
@@ -60,6 +64,7 @@
60
64
  },
61
65
  "devDependencies": {
62
66
  "@types/estree": "^1.0.8",
67
+ "@types/node": "^24.3.0",
63
68
  "typescript": "^5.9.2"
64
69
  }
65
70
  }
@@ -21,6 +21,5 @@ export function compile_to_volar_mappings(source, filename) {
21
21
  const analysis = analyze(ast, filename);
22
22
  const transformed = transform(filename, source, analysis, true);
23
23
 
24
- // Use Svelte's approach to convert esrap sourcemap to Volar mappings
25
24
  return convert_source_map_to_mappings(transformed.js.map, source, transformed.js.code);
26
25
  }
@@ -131,7 +131,32 @@ function read_body(parser) {
131
131
  }
132
132
 
133
133
  function read_at_rule(parser) {
134
- debugger;
134
+ const start = parser.index;
135
+ parser.eat('@', true);
136
+
137
+ const name = read_identifier(parser);
138
+
139
+ const prelude = read_value(parser);
140
+
141
+ /** @type {AST.CSS.Block | null} */
142
+ let block = null;
143
+
144
+ if (parser.match('{')) {
145
+ // e.g. `@media (...) {...}`
146
+ block = read_block(parser);
147
+ } else {
148
+ // e.g. `@import '...'`
149
+ parser.eat(';', true);
150
+ }
151
+
152
+ return {
153
+ type: 'Atrule',
154
+ start,
155
+ end: parser.index,
156
+ name,
157
+ prelude,
158
+ block
159
+ };
135
160
  }
136
161
 
137
162
  function read_rule(parser) {
@@ -5,7 +5,7 @@ import {
5
5
  get_delegated_event,
6
6
  is_event_attribute,
7
7
  is_inside_component,
8
- is_svelte_import,
8
+ is_ripple_import,
9
9
  is_tracked_name,
10
10
  } from '../../utils.js';
11
11
  import { extract_paths } from '../../../utils/ast.js';
@@ -156,7 +156,7 @@ const visitors = {
156
156
  const init_is_untracked =
157
157
  declarator.init !== null &&
158
158
  declarator.init.type === 'CallExpression' &&
159
- is_svelte_import(declarator.init.callee, context) &&
159
+ is_ripple_import(declarator.init.callee, context) &&
160
160
  declarator.init.callee.type === 'Identifier' &&
161
161
  (declarator.init.callee.name === 'untrack' || declarator.init.callee.name === 'deferred');
162
162
 
@@ -16,7 +16,7 @@ import {
16
16
  escape_html,
17
17
  is_boolean_attribute,
18
18
  is_dom_property,
19
- is_svelte_import,
19
+ is_ripple_import,
20
20
  is_declared_within_component,
21
21
  is_inside_call_expression,
22
22
  } from '../../utils.js';
@@ -131,7 +131,7 @@ const visitors = {
131
131
  (parent?.type === 'MemberExpression' && parent.property === node) ||
132
132
  is_inside_call_expression(context) ||
133
133
  !context.path.some((node) => node.type === 'Component') ||
134
- (is_svelte_import(callee, context) &&
134
+ (is_ripple_import(callee, context) &&
135
135
  (callee.type !== 'Identifier' ||
136
136
  (callee.name !== 'array' && callee.name !== 'deferred'))) ||
137
137
  is_declared_within_component(callee, context)
@@ -150,6 +150,25 @@ const visitors = {
150
150
  );
151
151
  },
152
152
 
153
+ NewExpression(node, context) {
154
+ const callee = node.callee;
155
+ const parent = context.path.at(-1);
156
+
157
+ if (context.state.metadata?.tracking === false) {
158
+ context.state.metadata.tracking = true;
159
+ }
160
+
161
+ return b.call(
162
+ '$.with_scope',
163
+ b.id('__block'),
164
+ b.thunk({
165
+ ...node,
166
+ callee: context.visit(callee),
167
+ arguments: node.arguments.map((arg) => context.visit(arg)),
168
+ }),
169
+ );
170
+ },
171
+
153
172
  MemberExpression(node, context) {
154
173
  const parent = context.path.at(-1);
155
174
 
@@ -1543,7 +1562,7 @@ export function transform(filename, source, analysis, to_ts) {
1543
1562
  };
1544
1563
 
1545
1564
  const program = /** @type {ESTree.Program} */ (
1546
- walk(/** @type {AST.SvelteNode} */ (analysis.ast), state, visitors)
1565
+ walk((analysis.ast), state, visitors)
1547
1566
  );
1548
1567
 
1549
1568
  for (const hoisted of state.hoisted) {
@@ -2,6 +2,7 @@ import MagicString from 'magic-string';
2
2
  import { walk } from 'zimmerframe';
3
3
 
4
4
  const regex_css_browser_prefix = /^-((webkit)|(moz)|(o)|(ms))-/;
5
+ const regex_css_name_boundary = /^[\s,;}]$/;
5
6
 
6
7
  const is_keyframes_node = (node) => remove_css_prefix(node.name) === 'keyframes';
7
8
 
@@ -229,7 +229,7 @@ export class Scope {
229
229
  /**
230
230
  * A set of all the names referenced with this scope
231
231
  * — useful for generating unique names
232
- * @type {Map<string, { node: Identifier; path: AST.SvelteNode[] }[]>}
232
+ * @type {Map<string, { node: Identifier; path: AST.Node[] }[]>}
233
233
  */
234
234
  references = new Map();
235
235
 
@@ -362,7 +362,7 @@ export class Scope {
362
362
 
363
363
  /**
364
364
  * @param {Identifier} node
365
- * @param {AST.SvelteNode[]} path
365
+ * @param {AST.Node[]} path
366
366
  */
367
367
  reference(node, path) {
368
368
  path = [...path]; // ensure that mutations to path afterwards don't affect this reference
@@ -367,7 +367,7 @@ export function is_tracked_name(name) {
367
367
  return typeof name === 'string' && name.startsWith('$') && name.length > 1 && name[1] !== '$';
368
368
  }
369
369
 
370
- export function is_svelte_import(callee, context) {
370
+ export function is_ripple_import(callee, context) {
371
371
  if (callee.type === 'Identifier') {
372
372
  const binding = context.state.scope.get(callee.name);
373
373
 
@@ -35,10 +35,18 @@ const introspect_methods = [
35
35
 
36
36
  let init = false;
37
37
 
38
- class RippleArray extends Array {
38
+ export class RippleArray extends Array {
39
39
  #tracked_elements = [];
40
40
  #tracked_index;
41
41
 
42
+ static from(arrayLike, mapFn, thisArg) {
43
+ return new RippleArray(Array.from(arrayLike, mapFn, thisArg));
44
+ }
45
+
46
+ static of(...elements) {
47
+ return new RippleArray(...elements);
48
+ }
49
+
42
50
  constructor(...elements) {
43
51
  super(...elements);
44
52
 
@@ -210,6 +218,3 @@ export function get_all_elements(array) {
210
218
  return arr;
211
219
  }
212
220
 
213
- export function array(...elements) {
214
- return new RippleArray(...elements);
215
- }
@@ -1,3 +1,5 @@
1
+ /** @import { Block } from '#client' */
2
+
1
3
  import { destroy_block, root } from './internal/client/blocks.js';
2
4
  import { handle_root_events } from './internal/client/events.js';
3
5
  import { init_operations } from './internal/client/operations.js';
@@ -7,6 +9,11 @@ import { create_anchor } from './internal/client/utils.js';
7
9
  // Re-export JSX runtime functions for jsxImportSource: "ripple"
8
10
  export { jsx, jsxs, Fragment } from '../jsx-runtime.js';
9
11
 
12
+ /**
13
+ * @param {(anchor: Node, props: Record<string, any>, active_block: Block | null) => void} component
14
+ * @param {{ props?: Record<string, any>, target: HTMLElement }} options
15
+ * @returns {() => void}
16
+ */
10
17
  export function mount(component, options) {
11
18
  init_operations();
12
19
 
@@ -24,13 +31,14 @@ export function mount(component, options) {
24
31
  return () => {
25
32
  cleanup_events();
26
33
  destroy_block(_root);
27
- target.removeChild(anchor_node);
28
34
  };
29
35
  }
30
36
 
37
+ export { create_context as createContext } from './internal/client/context.js';
38
+
31
39
  export { flush_sync as flushSync, untrack, deferred } from './internal/client/runtime.js';
32
40
 
33
- export { array } from './array.js';
41
+ export { RippleArray } from './array.js';
34
42
 
35
43
  export { keyed } from './internal/client/for.js';
36
44
 
@@ -1,3 +1,5 @@
1
+ /** @import { Block, Computed } from '#client' */
2
+
1
3
  import {
2
4
  BLOCK_HAS_RUN,
3
5
  BRANCH_BLOCK,
@@ -23,6 +25,9 @@ import {
23
25
  } from './runtime';
24
26
  import { suspend } from './try';
25
27
 
28
+ /**
29
+ * @param {Function} fn
30
+ */
26
31
  export function user_effect(fn) {
27
32
  if (active_block === null) {
28
33
  throw new Error(
@@ -45,22 +50,41 @@ export function user_effect(fn) {
45
50
  return block(EFFECT_BLOCK, fn);
46
51
  }
47
52
 
53
+ /**
54
+ * @param {Function} fn
55
+ */
48
56
  export function effect(fn) {
49
57
  return block(EFFECT_BLOCK, fn);
50
58
  }
51
59
 
60
+ /**
61
+ * @param {Function} fn
62
+ * @param {number} [flags]
63
+ */
52
64
  export function render(fn, flags = 0) {
53
65
  return block(RENDER_BLOCK | flags, fn);
54
66
  }
55
67
 
68
+ /**
69
+ * @param {any} element
70
+ * @param {any} fn
71
+ * @param {number} [flags]
72
+ */
56
73
  export function render_spread(element, fn, flags = 0) {
57
74
  return block(RENDER_BLOCK | flags, apply_element_spread(element, fn));
58
75
  }
59
76
 
77
+ /**
78
+ * @param {Function} fn
79
+ * @param {number} [flags]
80
+ */
60
81
  export function branch(fn, flags = 0) {
61
82
  return block(BRANCH_BLOCK | flags, fn);
62
83
  }
63
84
 
85
+ /**
86
+ * @param {() => any} fn
87
+ */
64
88
  export function async(fn) {
65
89
  return block(BRANCH_BLOCK, async () => {
66
90
  const unsuspend = suspend();
@@ -118,7 +142,14 @@ function push_block(block, parent_block) {
118
142
  }
119
143
  }
120
144
 
145
+ /**
146
+ * @param {number} flags
147
+ * @param {Function} fn
148
+ * @param {any} state
149
+ * @returns {Block}
150
+ */
121
151
  export function block(flags, fn, state = null) {
152
+ /** @type {Block} */
122
153
  var block = {
123
154
  c: active_component,
124
155
  d: null,
@@ -134,7 +165,7 @@ export function block(flags, fn, state = null) {
134
165
  };
135
166
 
136
167
  if (active_reaction !== null && (active_reaction.f & COMPUTED) !== 0) {
137
- (active_reaction.blocks ??= []).push(block);
168
+ (/** @type {Computed} */ (active_reaction).blocks ??= []).push(block);
138
169
  }
139
170
 
140
171
  if (active_block !== null) {
@@ -0,0 +1,69 @@
1
+ /** @import { Component } from '#client' */
2
+
3
+ import { active_component } from './runtime';
4
+
5
+ /**
6
+ * @template T
7
+ */
8
+ export class Context {
9
+ /**
10
+ * @param {T} initial_value
11
+ */
12
+ constructor(initial_value) {
13
+ /** @type {T} */
14
+ this._v = initial_value;
15
+ }
16
+
17
+ get() {
18
+ const component = active_component;
19
+ const context = this;
20
+
21
+ if (component === null) {
22
+ throw new Error('No active component found, cannot get context');
23
+ }
24
+ /** @type {Component | null} */
25
+ let current_component = component;
26
+
27
+ while (current_component !== null) {
28
+ const context_map = current_component.c;
29
+
30
+ if (context_map?.has(context)) {
31
+ return context_map.get(context);
32
+ }
33
+
34
+ current_component = current_component.p;
35
+ }
36
+
37
+ return context._v;
38
+ }
39
+
40
+ /**
41
+ * @template T
42
+ * @param {T} value
43
+ */
44
+ set(value) {
45
+ const component = active_component;
46
+ const context = this;
47
+
48
+ if (component === null) {
49
+ throw new Error('No active component found, cannot set context');
50
+ }
51
+
52
+ let current_context = component.c;
53
+
54
+ if (current_context === null) {
55
+ current_context = component.c = new Map();
56
+ }
57
+
58
+ current_context.set(context, value);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * @template T
64
+ * @param {T} initial_value
65
+ * @returns {Context<T>}
66
+ */
67
+ export function create_context(initial_value) {
68
+ return new Context(initial_value);
69
+ }
@@ -15,6 +15,11 @@ var all_registered_events = new Set();
15
15
  /** @type {Set<(events: Array<string>) => void>} */
16
16
  var root_event_handles = new Set();
17
17
 
18
+ /**
19
+ * @this {EventTarget}
20
+ * @param {Event} event
21
+ * @returns {void}
22
+ */
18
23
  export function handle_event_propagation(event) {
19
24
  var handler_element = this;
20
25
  var owner_document = /** @type {Node} */ (handler_element).ownerDocument;
@@ -148,6 +153,12 @@ export function handle_event_propagation(event) {
148
153
  }
149
154
  }
150
155
 
156
+ /**
157
+ * @param {string} event_name
158
+ * @param {EventTarget} dom
159
+ * @param {EventListener} [handler]
160
+ * @param {AddEventListenerOptions} [options]
161
+ */
151
162
  function create_event(event_name, dom, handler, options = {}) {
152
163
  var block = active_block;
153
164
 
@@ -25,7 +25,7 @@ function get_setters(element) {
25
25
  var element_proto = Element.prototype;
26
26
 
27
27
  // Stop at Element, from there on there's only unnecessary setters we're not interested in
28
- // Do not use contructor.name here as that's unreliable in some browser environments
28
+ // Do not use constructor.name here as that's unreliable in some browser environments
29
29
  while (element_proto !== proto) {
30
30
  descriptors = get_descriptors(proto);
31
31
 
@@ -1,3 +1,5 @@
1
+ /** @import { Block, Component, Dependency, Computed, Tracked, Ref } from '#client' */
2
+
1
3
  import {
2
4
  destroy_block,
3
5
  destroy_non_branch_children,
@@ -37,9 +39,13 @@ import {
37
39
  const FLUSH_MICROTASK = 0;
38
40
  const FLUSH_SYNC = 1;
39
41
 
42
+ /** @type {null | Block} */
40
43
  export let active_block = null;
44
+ /** @type {null | Block | Computed} */
41
45
  export let active_reaction = null;
46
+ /** @type {null | Block} */
42
47
  export let active_scope = null;
48
+ /** @type {null | Component} */
43
49
  export let active_component = null;
44
50
 
45
51
  var old_values = new Map();
@@ -49,9 +55,12 @@ let scheduler_mode = FLUSH_MICROTASK;
49
55
  // Used for handling scheduling
50
56
  let is_micro_task_queued = false;
51
57
  let clock = 0;
58
+ /** @type {Block[]} */
52
59
  let queued_root_blocks = [];
60
+ /** @type {(() => void)[]} */
53
61
  let queued_microtasks = [];
54
62
  let flush_count = 0;
63
+ /** @type {null | Dependency} */
55
64
  let active_dependency = null;
56
65
 
57
66
  export let tracking = false;
@@ -61,22 +70,37 @@ function increment_clock() {
61
70
  return ++clock;
62
71
  }
63
72
 
73
+ /**
74
+ * @param {Block | null} block
75
+ */
64
76
  export function set_active_block(block) {
65
77
  active_block = block;
66
78
  }
67
79
 
80
+ /**
81
+ * @param {Block | null} reaction
82
+ */
68
83
  export function set_active_reaction(reaction) {
69
84
  active_reaction = reaction;
70
85
  }
71
86
 
87
+ /**
88
+ * @param {Component | null} component
89
+ */
72
90
  export function set_active_component(component) {
73
91
  active_component = component;
74
92
  }
75
93
 
94
+ /**
95
+ * @param {boolean} value
96
+ */
76
97
  export function set_tracking(value) {
77
98
  tracking = value;
78
99
  }
79
100
 
101
+ /**
102
+ * @param {Block} block
103
+ */
80
104
  export function run_teardown(block) {
81
105
  var fn = block.t;
82
106
  if (fn !== null) {
@@ -100,6 +124,9 @@ export function run_teardown(block) {
100
124
  }
101
125
  }
102
126
 
127
+ /**
128
+ * @param {Computed} computed
129
+ */
103
130
  function update_computed(computed) {
104
131
  var value = computed.v;
105
132
 
@@ -113,6 +140,9 @@ function update_computed(computed) {
113
140
  }
114
141
  }
115
142
 
143
+ /**
144
+ * @param {Computed} computed
145
+ */
116
146
  function destroy_computed_children(computed) {
117
147
  var blocks = computed.blocks;
118
148
 
@@ -124,6 +154,9 @@ function destroy_computed_children(computed) {
124
154
  }
125
155
  }
126
156
 
157
+ /**
158
+ * @param {Computed} computed
159
+ */
127
160
  function run_computed(computed) {
128
161
  var previous_block = active_block;
129
162
  var previous_reaction = active_reaction;
@@ -154,6 +187,10 @@ function run_computed(computed) {
154
187
  }
155
188
  }
156
189
 
190
+ /**
191
+ * @param {unknown} error
192
+ * @param {Block} block
193
+ */
157
194
  export function handle_error(error, block) {
158
195
  var current = block;
159
196
 
@@ -169,6 +206,9 @@ export function handle_error(error, block) {
169
206
  throw error;
170
207
  }
171
208
 
209
+ /**
210
+ * @param {Block} block
211
+ */
172
212
  export function run_block(block) {
173
213
  var previous_block = active_block;
174
214
  var previous_reaction = active_reaction;
@@ -210,6 +250,12 @@ export function run_block(block) {
210
250
  }
211
251
  }
212
252
 
253
+ /**
254
+ *
255
+ * @param {any} v
256
+ * @param {Block} b
257
+ * @returns {Tracked}
258
+ */
213
259
  export function tracked(v, b) {
214
260
  return {
215
261
  b,
@@ -219,6 +265,11 @@ export function tracked(v, b) {
219
265
  };
220
266
  }
221
267
 
268
+ /**
269
+ * @param {any} fn
270
+ * @param {any} block
271
+ * @returns {Computed}
272
+ */
222
273
  export function computed(fn, block) {
223
274
  return {
224
275
  b: block,
@@ -231,12 +282,17 @@ export function computed(fn, block) {
231
282
  };
232
283
  }
233
284
 
285
+ /**
286
+ * @param {Tracked} tracked
287
+ * @returns {Dependency}
288
+ */
234
289
  function create_dependency(tracked) {
235
- var existing = active_reaction.d;
290
+ var reaction = /** @type {Computed | Block} **/ (active_reaction);
291
+ var existing = reaction.d;
236
292
 
237
293
  // Recycle tracking entries
238
294
  if (existing !== null) {
239
- active_reaction.d = existing.n;
295
+ reaction.d = existing.n;
240
296
  existing.c = tracked.c;
241
297
  existing.t = tracked;
242
298
  existing.n = null;
@@ -250,6 +306,9 @@ function create_dependency(tracked) {
250
306
  };
251
307
  }
252
308
 
309
+ /**
310
+ * @param {Dependency | null} tracking
311
+ */
253
312
  function is_tracking_dirty(tracking) {
254
313
  if (tracking === null) {
255
314
  return false;
@@ -258,7 +317,7 @@ function is_tracking_dirty(tracking) {
258
317
  var tracked = tracking.t;
259
318
 
260
319
  if ((tracked.f & COMPUTED) !== 0) {
261
- update_computed(tracked);
320
+ update_computed(/** @type {Computed} **/ (tracked));
262
321
  }
263
322
 
264
323
  if (tracked.c > tracking.c) {
@@ -270,6 +329,9 @@ function is_tracking_dirty(tracking) {
270
329
  return false;
271
330
  }
272
331
 
332
+ /**
333
+ * @param {Block} block
334
+ */
273
335
  function is_block_dirty(block) {
274
336
  var flags = block.f;
275
337
 
@@ -405,6 +467,9 @@ function capture_deferred(fn) {
405
467
  return [value, deferred];
406
468
  }
407
469
 
470
+ /**
471
+ * @param {Block} block
472
+ */
408
473
  function flush_deferred_upodates(block) {
409
474
  var current = block.first;
410
475
  var is_awaited = false;
@@ -432,7 +497,11 @@ function flush_deferred_upodates(block) {
432
497
  return is_awaited;
433
498
  }
434
499
 
500
+ /**
501
+ * @param {Block} root_block
502
+ */
435
503
  function flush_updates(root_block) {
504
+ /** @type {Block | null} */
436
505
  var current = root_block;
437
506
  var containing_update = null;
438
507
  var effects = [];
@@ -457,6 +526,7 @@ function flush_updates(root_block) {
457
526
  handle_error(error, current);
458
527
  }
459
528
  }
529
+ /** @type {Block | null} */
460
530
  var child = current.first;
461
531
 
462
532
  if (child !== null) {
@@ -465,6 +535,7 @@ function flush_updates(root_block) {
465
535
  }
466
536
  }
467
537
 
538
+ /** @type {Block} */
468
539
  var parent = current.p;
469
540
  current = current.next;
470
541
 
@@ -493,6 +564,9 @@ function flush_updates(root_block) {
493
564
  }
494
565
  }
495
566
 
567
+ /**
568
+ * @param {Block[]} root_blocks
569
+ */
496
570
  function flush_queued_root_blocks(root_blocks) {
497
571
  for (let i = 0; i < root_blocks.length; i++) {
498
572
  flush_updates(root_blocks[i]);
@@ -523,6 +597,9 @@ function flush_microtasks() {
523
597
  old_values.clear();
524
598
  }
525
599
 
600
+ /**
601
+ * @param { (() => void) } [fn]
602
+ */
526
603
  export function queue_microtask(fn) {
527
604
  if (!is_micro_task_queued) {
528
605
  is_micro_task_queued = true;
@@ -533,6 +610,9 @@ export function queue_microtask(fn) {
533
610
  }
534
611
  }
535
612
 
613
+ /**
614
+ * @param {Block} block
615
+ */
536
616
  export function schedule_update(block) {
537
617
  if (scheduler_mode === FLUSH_MICROTASK) {
538
618
  queue_microtask();
@@ -552,14 +632,17 @@ export function schedule_update(block) {
552
632
  queued_root_blocks.push(current);
553
633
  }
554
634
 
635
+ /**
636
+ * @param {Tracked} tracked
637
+ */
555
638
  function register_dependency(tracked) {
556
- var depedency = active_dependency;
639
+ var dependency = active_dependency;
557
640
 
558
- if (depedency === null) {
559
- depedency = create_dependency(tracked);
560
- active_dependency = depedency;
641
+ if (dependency === null) {
642
+ dependency = create_dependency(tracked);
643
+ active_dependency = dependency;
561
644
  } else {
562
- var current = depedency;
645
+ var current = dependency;
563
646
 
564
647
  while (current !== null) {
565
648
  if (current.t === tracked) {
@@ -573,11 +656,14 @@ function register_dependency(tracked) {
573
656
  current = next;
574
657
  }
575
658
 
576
- depedency = create_dependency(tracked);
577
- current.n = depedency;
659
+ dependency = create_dependency(tracked);
660
+ current.n = dependency;
578
661
  }
579
662
  }
580
663
 
664
+ /**
665
+ * @param {Computed} computed
666
+ */
581
667
  export function get_computed(computed) {
582
668
  update_computed(computed);
583
669
  if (tracking) {
@@ -587,10 +673,18 @@ export function get_computed(computed) {
587
673
  return computed.v;
588
674
  }
589
675
 
676
+ /**
677
+ * @param {Computed | Tracked} tracked
678
+ */
590
679
  export function get(tracked) {
591
- return (tracked.f & COMPUTED) !== 0 ? get_computed(tracked) : get_tracked(tracked);
680
+ return (tracked.f & COMPUTED) !== 0
681
+ ? get_computed(/** @type {Computed} */ (tracked))
682
+ : get_tracked(tracked);
592
683
  }
593
684
 
685
+ /**
686
+ * @param {Tracked} tracked
687
+ */
594
688
  export function get_tracked(tracked) {
595
689
  var value = tracked.v;
596
690
  if (tracking) {
@@ -602,6 +696,11 @@ export function get_tracked(tracked) {
602
696
  return value;
603
697
  }
604
698
 
699
+ /**
700
+ * @param {Computed | Tracked} tracked
701
+ * @param {any} value
702
+ * @param {Block} block
703
+ */
605
704
  export function set(tracked, value, block) {
606
705
  var old_value = tracked.v;
607
706
 
@@ -622,6 +721,11 @@ export function set(tracked, value, block) {
622
721
  }
623
722
  }
624
723
 
724
+ /**
725
+ * @template T
726
+ * @param {() => T} fn
727
+ * @returns {T}
728
+ */
625
729
  export function untrack(fn) {
626
730
  var previous_tracking = tracking;
627
731
  var previous_dependency = active_dependency;
@@ -635,11 +739,17 @@ export function untrack(fn) {
635
739
  }
636
740
  }
637
741
 
742
+ /**
743
+ * @template T
744
+ * @param {() => T} [fn]
745
+ * @returns {T}
746
+ */
638
747
  export function flush_sync(fn) {
639
748
  var previous_scheduler_mode = scheduler_mode;
640
749
  var previous_queued_root_blocks = queued_root_blocks;
641
750
 
642
751
  try {
752
+ /** @type {Block[]} */
643
753
  var root_blocks = [];
644
754
 
645
755
  scheduler_mode = FLUSH_SYNC;
@@ -656,7 +766,7 @@ export function flush_sync(fn) {
656
766
 
657
767
  flush_count = 0;
658
768
 
659
- return result;
769
+ return /** @type {T} */ (result);
660
770
  } finally {
661
771
  scheduler_mode = previous_scheduler_mode;
662
772
  queued_root_blocks = previous_queued_root_blocks;
@@ -908,6 +1018,7 @@ export function scope() {
908
1018
 
909
1019
  export function push_component() {
910
1020
  var component = {
1021
+ c: null,
911
1022
  e: null,
912
1023
  m: false,
913
1024
  p: active_component,
@@ -916,7 +1027,7 @@ export function push_component() {
916
1027
  }
917
1028
 
918
1029
  export function pop_component() {
919
- var component = active_component;
1030
+ var component = /** @type {Component} */ (active_component);
920
1031
  component.m = true;
921
1032
  var effects = component.e;
922
1033
  if (effects !== null) {
@@ -943,6 +1054,11 @@ export function use_prop() {
943
1054
  return Symbol(USE_PROP);
944
1055
  }
945
1056
 
1057
+ /**
1058
+ * @template T
1059
+ * @param {T} value
1060
+ * @returns {Ref<T>}
1061
+ */
946
1062
  export function ref(value) {
947
1063
  var block = active_block || active_scope;
948
1064
  if (!block) {
@@ -0,0 +1,53 @@
1
+ import type { Context } from './context.js';
2
+
3
+ export type Component = {
4
+ c: null | Map<Context<any>, any>;
5
+ e: null | Array<{
6
+ b: Block;
7
+ fn: Function;
8
+ r: null | Block;
9
+ }>;
10
+ p: null | Component;
11
+ m: boolean;
12
+ };
13
+
14
+ export type Dependency = {
15
+ c: number;
16
+ t: Tracked | Computed;
17
+ n: null | Dependency;
18
+ };
19
+
20
+ export type Tracked = {
21
+ b: Block;
22
+ c: number;
23
+ f: number;
24
+ v: any;
25
+ };
26
+
27
+ export type Computed = {
28
+ b: Block,
29
+ blocks: null | Block[],
30
+ c: number,
31
+ d: null,
32
+ f: number,
33
+ fn: Function,
34
+ v: any,
35
+ };
36
+
37
+ export type Block = {
38
+ c: null | Component;
39
+ d: null | Dependency;
40
+ first: null | Block;
41
+ f: number;
42
+ fn: any;
43
+ last: null;
44
+ next: null;
45
+ p: null | Block;
46
+ prev: null;
47
+ s: any;
48
+ t: Tracked | null;
49
+ };
50
+
51
+ export type Ref<T> = {
52
+ $current: T;
53
+ };
@@ -116,7 +116,7 @@ export function call(callee, ...args) {
116
116
  if (typeof callee === 'string') callee = id(callee);
117
117
  args = args.slice();
118
118
 
119
- // replacing missing arguments with `undefined`, unless they're at the end in which case remove them
119
+ // replacing missing arguments with `void(0)`, unless they're at the end in which case remove them
120
120
  let i = args.length;
121
121
  let popping = true;
122
122
  while (i--) {
@@ -124,7 +124,7 @@ export function call(callee, ...args) {
124
124
  if (popping) {
125
125
  args.pop();
126
126
  } else {
127
- args[i] = id('undefined');
127
+ args[i] = void0;
128
128
  }
129
129
  } else {
130
130
  popping = false;
@@ -773,6 +773,8 @@ export function jsx_spread_attribute(argument) {
773
773
  };
774
774
  }
775
775
 
776
+ export const void0 = unary('void', literal(0));
777
+
776
778
  export {
777
779
  await_builder as await,
778
780
  let_builder as let,
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
 
3
- import { mount, flushSync, array } from 'ripple';
3
+ import { mount, flushSync, RippleArray } from 'ripple';
4
4
 
5
5
  describe('array', () => {
6
6
  let container;
@@ -23,7 +23,7 @@ describe('array', () => {
23
23
 
24
24
  it('handles direct assignment', () => {
25
25
  component ArrayTest() {
26
- let items = array(1, 2, 3);
26
+ let items = new RippleArray(1, 2, 3);
27
27
 
28
28
  <button onClick={() => items[items.$length] = items.$length + 1}>{'increment'}</button>
29
29
 
@@ -51,4 +51,4 @@ describe('array', () => {
51
51
  expect(container.querySelectorAll('pre')[0].textContent).toBe('[1,2,3,4,5]');
52
52
  expect(container.querySelectorAll('pre')[1].textContent).toBe('5');
53
53
  });
54
- });
54
+ });
@@ -0,0 +1,46 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+
3
+ import { mount, createContext } from 'ripple';
4
+
5
+ describe('context', () => {
6
+ let container;
7
+
8
+ function render(component) {
9
+ mount(component, {
10
+ target: container
11
+ });
12
+ }
13
+
14
+ beforeEach(() => {
15
+ container = document.createElement('div');
16
+ document.body.appendChild(container);
17
+ });
18
+
19
+ afterEach(() => {
20
+ document.body.removeChild(container);
21
+ });
22
+
23
+ it('creates a reactive ref with initial value', () => {
24
+ const MyContext = createContext(null);
25
+
26
+ component Child() {
27
+ const value = MyContext.get(MyContext);
28
+
29
+ <div>{value}</div>
30
+ }
31
+
32
+ component TestContext() {
33
+ const value = MyContext.get(MyContext);
34
+
35
+ console.log(value);
36
+
37
+ MyContext.set("Hello from context!");
38
+
39
+ <Child />
40
+ }
41
+
42
+ render(TestContext);
43
+
44
+ expect(container.querySelector('div').textContent).toBe('Hello from context!');
45
+ });
46
+ });
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
 
3
- import { mount, flushSync, array } from 'ripple';
3
+ import { mount, flushSync, RippleArray } from 'ripple';
4
4
 
5
5
  describe('for statements', () => {
6
6
  let container;
@@ -37,7 +37,7 @@ describe('for statements', () => {
37
37
 
38
38
  it('render a simple dynamic array', () => {
39
39
  component App() {
40
- const items = array('Item 1', 'Item 2', 'Item 3');
40
+ const items = new RippleArray('Item 1', 'Item 2', 'Item 3');
41
41
 
42
42
  for (const item of items) {
43
43
  <div class={item}>{item}</div>
@@ -56,4 +56,4 @@ describe('for statements', () => {
56
56
 
57
57
  expect(container).toMatchSnapshot();
58
58
  });
59
- });
59
+ });
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
 
3
- import { mount, flushSync, array } from 'ripple';
3
+ import { mount, flushSync, RippleArray } from 'ripple';
4
4
 
5
5
  describe('@use element decorators', () => {
6
6
  let container;
@@ -36,7 +36,7 @@ describe('@use element decorators', () => {
36
36
  let _node;
37
37
 
38
38
  component Component() {
39
- let items = array(...[1, 2, 3]);
39
+ let items = RippleArray.from([1, 2, 3]);
40
40
 
41
41
  function ref(node) {
42
42
  _node = node;
@@ -56,4 +56,4 @@ describe('@use element decorators', () => {
56
56
 
57
57
  expect(_node).toBe(document.querySelector('pre'));
58
58
  })
59
- });
59
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "esnext",
4
+ "lib": ["esnext", "dom", "dom.iterable"],
5
+ "target": "esnext",
6
+ "noEmit": true,
7
+ "moduleResolution": "bundler",
8
+ "resolveJsonModule": true,
9
+ "noEmitOnError": true,
10
+ "noErrorTruncation": true,
11
+ "allowSyntheticDefaultImports": true,
12
+ "verbatimModuleSyntax": true,
13
+ "types": ["node"],
14
+ "jsx": "preserve",
15
+ "jsxImportSource": "ripple",
16
+ "strict": true,
17
+ "allowJs": true,
18
+ "checkJs": true,
19
+ "paths": {
20
+ "ripple": ["./types/index.d.ts"],
21
+ }
22
+ },
23
+ "include": [
24
+ "./*.js",
25
+ "./src/",
26
+ "./tests/*/test.ripple",
27
+ ]
28
+ }
package/types/index.d.ts CHANGED
@@ -17,9 +17,30 @@ export interface Ref<T> {
17
17
 
18
18
  export declare function ref<T>(value: T): Ref<T>;
19
19
 
20
- export interface RippleArray<T> extends Array<T> {
21
- $length: number;
22
- toJSON(): T[];
20
+ export declare class RippleArray<T> extends Array<T> {
21
+ static from<T>(arrayLike: ArrayLike<T>): RippleArray<T>;
22
+ static from<T, U>(
23
+ arrayLike: ArrayLike<T>,
24
+ mapFn: (v: T, k: number) => U,
25
+ thisArg?: any
26
+ ): RippleArray<U>;
27
+ static from<T>(iterable: Iterable<T>): RippleArray<T>;
28
+ static from<T, U>(
29
+ iterable: Iterable<T>,
30
+ mapFn: (v: T, k: number) => U,
31
+ thisArg?: any
32
+ ): RippleArray<U>;
33
+
34
+ static of<T>(...items: T[]): RippleArray<T>;
35
+
36
+ $length: number;
37
+
38
+ toJSON(): T[];
23
39
  }
24
40
 
25
- export declare function array<T>(...elements: T[]): RippleArray<T>;
41
+ export type Context<T> = {
42
+ get(): T;
43
+ set(value: T): void;
44
+ };
45
+
46
+ export declare function createContext<T>(initialValue: T): Context<T>;