ripple 0.2.87 → 0.2.89

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 an elegant TypeScript UI framework",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.2.87",
6
+ "version": "0.2.89",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index-client.js",
9
9
  "main": "src/runtime/index-client.js",
@@ -66,6 +66,7 @@
66
66
  "@jridgewell/sourcemap-codec": "^1.5.5",
67
67
  "acorn": "^8.15.0",
68
68
  "acorn-typescript": "^1.4.13",
69
+ "clsx": "^2.1.1",
69
70
  "esrap": "^2.1.0",
70
71
  "is-reference": "^3.0.3",
71
72
  "magic-string": "^0.30.18",
@@ -80,13 +80,17 @@ function RipplePlugin(config) {
80
80
  // Look ahead to see if this is followed by [ for tuple syntax
81
81
  if (this.pos + 1 < this.input.length) {
82
82
  const nextChar = this.input.charCodeAt(this.pos + 1);
83
- if (nextChar === 91) {
84
- // [ character
85
- // This is a tuple literal #[
86
- // Consume both # and [
83
+ if (nextChar === 91 || nextChar === 123) {
84
+ // [ or { character
85
+ // This is a tuple literal #[ or #{
86
+ // Consume both # and [ or {
87
87
  ++this.pos; // consume #
88
- ++this.pos; // consume [
89
- return this.finishToken(tt.bracketL, '#[');
88
+ ++this.pos; // consume [ or {
89
+ if (nextChar === 123) {
90
+ return this.finishToken(tt.braceL, '#{');
91
+ } else {
92
+ return this.finishToken(tt.bracketL, '#[');
93
+ }
90
94
  }
91
95
  }
92
96
  }
@@ -181,6 +185,8 @@ function RipplePlugin(config) {
181
185
  // Check if this is a tuple literal starting with #[
182
186
  if (this.type === tt.bracketL && this.value === '#[') {
183
187
  return this.parseTrackedArrayExpression();
188
+ } else if (this.type === tt.braceL && this.value === '#{') {
189
+ return this.parseTrackedObjectExpression();
184
190
  }
185
191
 
186
192
  return super.parseExprAtom(refDestructuringErrors, forNew, forInit);
@@ -221,6 +227,38 @@ function RipplePlugin(config) {
221
227
  return this.finishNode(node, 'TrackedArrayExpression');
222
228
  }
223
229
 
230
+ parseTrackedObjectExpression() {
231
+ const node = this.startNode();
232
+ this.next(); // consume the '#{'
233
+
234
+ node.properties = [];
235
+
236
+ // Parse object properties similar to regular object parsing
237
+ let first = true;
238
+ while (!this.eat(tt.braceR)) {
239
+ if (!first) {
240
+ this.expect(tt.comma);
241
+ if (this.afterTrailingComma(tt.braceR)) break;
242
+ } else {
243
+ first = false;
244
+ }
245
+
246
+ if (this.type === tt.ellipsis) {
247
+ // Spread property
248
+ const prop = this.parseSpread();
249
+ node.properties.push(prop);
250
+ if (this.type === tt.comma && this.input.charCodeAt(this.pos) === 125) {
251
+ this.raise(this.pos, 'Trailing comma is not permitted after the rest element');
252
+ }
253
+ } else {
254
+ // Regular property
255
+ node.properties.push(this.parseProperty(false, {}));
256
+ }
257
+ }
258
+
259
+ return this.finishNode(node, 'TrackedObjectExpression');
260
+ }
261
+
224
262
  parseExportDefaultDeclaration() {
225
263
  // Check if this is "export default component"
226
264
  if (this.value === 'component') {
@@ -793,7 +831,36 @@ function RipplePlugin(config) {
793
831
  this.next();
794
832
  }
795
833
  } else {
796
- if (open.name.name === 'style') {
834
+ if (open.name.name === 'script') {
835
+ let content = '';
836
+
837
+ // TODO implement this where we get a string for content of the content of the script tag
838
+ // This is a temporary workaround to get the content of the script tag
839
+ const start = open.end;
840
+ const input = this.input.slice(start);
841
+ const end = input.indexOf('</script>');
842
+ content = input.slice(0, end);
843
+
844
+ const newLines = content.match(regex_newline_characters)?.length;
845
+ if (newLines) {
846
+ this.curLine = open.loc.end.line + newLines;
847
+ this.lineStart = start + content.lastIndexOf('\n') + 1;
848
+ }
849
+ this.pos = start + content.length + 1;
850
+
851
+ this.type = tok.jsxTagStart;
852
+ this.next();
853
+ if (this.value === '/') {
854
+ this.next();
855
+ this.jsx_parseElementName();
856
+ this.exprAllowed = true;
857
+ this.#path.pop();
858
+ this.next();
859
+ }
860
+
861
+ element.content = content;
862
+ this.finishNode(element, 'Element');
863
+ } else if (open.name.name === 'style') {
797
864
  // jsx_parseOpeningElementAt treats ID selectors (ie. #myid) or type selectors (ie. div) as identifier and read it
798
865
  // So backtrack to the end of the <style> tag to make sure everything is included
799
866
  const start = open.end;
@@ -488,7 +488,9 @@ const visitors = {
488
488
  if (attr.name.type === 'Identifier') {
489
489
  attribute_names.add(attr.name);
490
490
  }
491
- visit(attr.value, state);
491
+ if (attr.value !== null) {
492
+ visit(attr.value, state);
493
+ }
492
494
  } else if (attr.type === 'SpreadAttribute') {
493
495
  visit(attr.argument, state);
494
496
  } else if (attr.type === 'RefAttribute') {
@@ -133,16 +133,12 @@ function visit_title_element(node, context) {
133
133
  ),
134
134
  );
135
135
  } else {
136
- debugger;
136
+ context.state.init.push(
137
+ b.stmt(b.assignment('=', b.id('_$_.document.title'), result)),
138
+ );
137
139
  }
138
140
  }
139
141
 
140
- function visit_style_element(node, context) {
141
- context.state.template.push(
142
- b.literal(`<style>${sanitize_template_string(node.css)}</style>`),
143
- );
144
- }
145
-
146
142
  const visitors = {
147
143
  _: function set_scope(node, { next, state }) {
148
144
  const scope = state.scopes.get(node);
@@ -342,6 +338,25 @@ const visitors = {
342
338
  );
343
339
  },
344
340
 
341
+ TrackedObjectExpression(node, context) {
342
+ if (context.state.to_ts) {
343
+ if (!context.state.imports.has(`import { TrackedObject } from 'ripple'`)) {
344
+ context.state.imports.add(`import { TrackedObject } from 'ripple'`);
345
+ }
346
+
347
+ return b.new(
348
+ b.id('TrackedObject'),
349
+ b.object(node.properties.map((prop) => context.visit(prop)))
350
+ );
351
+ }
352
+
353
+ return b.call(
354
+ '_$_.tracked_object',
355
+ b.object(node.properties.map((prop) => context.visit(prop))),
356
+ b.id('__block'),
357
+ );
358
+ },
359
+
345
360
  MemberExpression(node, context) {
346
361
  const parent = context.path.at(-1);
347
362
 
@@ -487,9 +502,19 @@ const visitors = {
487
502
  Element(node, context) {
488
503
  const { state, visit } = context;
489
504
 
490
- if (context.state.inside_head && node.id.type === 'Identifier' && node.id.name === 'style') {
491
- state.template.push(`<style>${sanitize_template_string(node.css)}</style>`);
492
- return;
505
+ if (context.state.inside_head) {
506
+ if (node.id.type === 'Identifier' && node.id.name === 'style') {
507
+ state.template.push(`<style>${sanitize_template_string(node.css)}</style>`);
508
+ return;
509
+ }
510
+ if (node.id.type === 'Identifier' && node.id.name === 'script') {
511
+ const id = state.flush_node();
512
+ state.template.push('<!>');
513
+ context.state.init.push(
514
+ b.stmt(b.call('_$_.script', id, b.literal(sanitize_template_string(node.content)))),
515
+ );
516
+ return;
517
+ }
493
518
  }
494
519
 
495
520
  const is_dom_element = is_element_dom_element(node);
@@ -698,18 +723,18 @@ const visitors = {
698
723
  const metadata = { tracking: false, await: false };
699
724
  let expression = visit(class_attribute.value, { ...state, metadata });
700
725
 
701
- if (node.metadata.scoped && state.component.css) {
702
- expression = b.binary('+', expression, b.literal(' ' + state.component.css.hash));
703
- }
726
+ const hash_arg = node.metadata.scoped && state.component.css
727
+ ? b.literal(state.component.css.hash)
728
+ : undefined;
704
729
  const is_html = context.state.metadata.namespace === 'html' && node.id.name !== 'svg';
705
730
 
706
731
  if (metadata.tracking) {
707
732
  local_updates.push(
708
- b.stmt(b.call('_$_.set_class', id, expression, undefined, b.literal(is_html))),
733
+ b.stmt(b.call('_$_.set_class', id, expression, hash_arg, b.literal(is_html))),
709
734
  );
710
735
  } else {
711
736
  state.init.push(
712
- b.stmt(b.call('_$_.set_class', id, expression, undefined, b.literal(is_html))),
737
+ b.stmt(b.call('_$_.set_class', id, expression, hash_arg, b.literal(is_html))),
713
738
  );
714
739
  }
715
740
  }
@@ -766,7 +791,8 @@ const visitors = {
766
791
  if (attr.type === 'Attribute') {
767
792
  if (attr.name.type === 'Identifier') {
768
793
  const metadata = { tracking: false, await: false };
769
- let property = visit(attr.value, { ...state, metadata });
794
+ let property =
795
+ attr.value === null ? b.literal(true) : visit(attr.value, { ...state, metadata });
770
796
 
771
797
  if (metadata.tracking || attr.name.tracked) {
772
798
  if (attr.name.name === 'children') {
@@ -1139,6 +1165,14 @@ const visitors = {
1139
1165
  return context.next();
1140
1166
  },
1141
1167
 
1168
+ ExportNamedDeclaration(node, context) {
1169
+ if (!context.state.to_ts && node.exportKind === 'type') {
1170
+ return b.empty;
1171
+ }
1172
+
1173
+ return context.next();
1174
+ },
1175
+
1142
1176
  TryStatement(node, context) {
1143
1177
  if (!is_inside_component(context)) {
1144
1178
  context.next();
@@ -1598,7 +1632,8 @@ function transform_children(children, context) {
1598
1632
  for (const head_element of head_elements) {
1599
1633
  visit_head_element(head_element, context);
1600
1634
  }
1601
- if (context.state.inside_head) {
1635
+
1636
+ if (context.state.inside_head) {
1602
1637
  const title_element = children.find(
1603
1638
  (node) =>
1604
1639
  node.type === 'Element' && node.id.type === 'Identifier' && node.id.name === 'title',
@@ -1,7 +1,6 @@
1
1
  /** @import { Block } from '#client' */
2
- import { MAX_ARRAY_LENGTH, TRACKED_ARRAY, UNINITIALIZED } from './internal/client/constants.js';
3
- import { get, safe_scope, set, tracked } from './internal/client/runtime.js';
4
- import { get_descriptor } from './internal/client/utils.js';
2
+ import { safe_scope } from './internal/client/runtime.js';
3
+ import { array_proxy } from './proxy.js';
5
4
 
6
5
  /**
7
6
  * @template T
@@ -15,8 +14,7 @@ export function TrackedArray(...elements) {
15
14
  }
16
15
 
17
16
  var block = safe_scope();
18
-
19
- return proxy({ elements, block });
17
+ return array_proxy({ elements, block });
20
18
  }
21
19
 
22
20
  /**
@@ -29,7 +27,7 @@ export function TrackedArray(...elements) {
29
27
  TrackedArray.from = function (arrayLike, mapFn, thisArg) {
30
28
  var block = safe_scope();
31
29
  var elements = mapFn ? Array.from(arrayLike, mapFn, thisArg) : Array.from(arrayLike);
32
- return proxy({ elements, block, from_static: true });
30
+ return array_proxy({ elements, block, from_static: true });
33
31
  };
34
32
 
35
33
  /**
@@ -40,7 +38,7 @@ TrackedArray.from = function (arrayLike, mapFn, thisArg) {
40
38
  TrackedArray.of = function (...items) {
41
39
  var block = safe_scope();
42
40
  var elements = Array.of(...items);
43
- return proxy({ elements, block, from_static: true });
41
+ return array_proxy({ elements, block, from_static: true });
44
42
  };
45
43
 
46
44
  /**
@@ -55,246 +53,15 @@ TrackedArray.fromAsync = async function (arrayLike, mapFn, thisArg) {
55
53
  var elements = mapFn
56
54
  ? await Array.fromAsync(arrayLike, mapFn, thisArg)
57
55
  : await Array.fromAsync(arrayLike);
58
- return proxy({ elements, block, from_static: true });
56
+ return array_proxy({ elements, block, from_static: true });
59
57
  };
60
58
 
61
59
  /**
62
60
  * @template T
63
- * @param {{
64
- * elements: Iterable<T>,
65
- * block: Block,
66
- * from_static?: boolean,
67
- * use_array?: boolean
68
- * }} params
69
- * @returns {TrackedArray<T>}
70
- */
71
- function proxy({ elements, block, from_static = false, use_array = false }) {
72
- /** @type {T[]} */
73
- var arr;
74
- var first;
75
-
76
- if (
77
- from_static &&
78
- (first = get_first_if_length(/** @type {Array<T>} */ (elements))) !== undefined
79
- ) {
80
- arr = new Array();
81
- arr[0] = /** @type {T} */ (/** @type {unknown} */ (first));
82
- } else if (use_array) {
83
- arr = /** @type {T[]} */ (elements);
84
- } else {
85
- arr = new Array(...elements);
86
- }
87
-
88
- var tracked_elements = new Map();
89
- var tracked_len = tracked(arr.length, block);
90
- tracked_elements.set('length', tracked_len);
91
-
92
- return new Proxy(arr, {
93
- get(target, prop, receiver) {
94
- var t = tracked_elements.get(prop);
95
- var exists = prop in target;
96
-
97
- if (t === undefined && (!exists || get_descriptor(target, prop)?.writable)) {
98
- t = tracked(exists ? /** @type {any} */ (target)[prop] : UNINITIALIZED, block);
99
- tracked_elements.set(prop, t);
100
- }
101
-
102
- if (t !== undefined) {
103
- var v = get(t);
104
- return v === UNINITIALIZED ? undefined : v;
105
- }
106
-
107
- var result = Reflect.get(target, prop, receiver);
108
-
109
- if (typeof result === 'function') {
110
- if (methods_returning_arrays.has(/** @type {string} */ (prop))) {
111
- /** @type {(this: any, ...args: any[]) => any} */
112
- return function (...args) {
113
- var output = Reflect.apply(result, receiver, args);
114
-
115
- if (Array.isArray(output) && output !== target) {
116
- return proxy({ elements: output, block, use_array: true });
117
- }
118
-
119
- return output;
120
- };
121
- }
122
-
123
- // When generating an iterator, we need to ensure that length is tracked
124
- if (prop === 'entries' || prop === 'values' || prop === 'keys') {
125
- receiver.length;
126
- }
127
- }
128
-
129
- return result;
130
- },
131
-
132
- set(target, prop, value, receiver) {
133
- var t = tracked_elements.get(prop);
134
- var exists = prop in target;
135
-
136
- if (prop === 'length') {
137
- for (var i = value; i < tracked_len.v; i += 1) {
138
- var other_t = tracked_elements.get(i + '');
139
- if (other_t !== undefined) {
140
- set(other_t, UNINITIALIZED, block);
141
- } else if (i in target) {
142
- // If the item exists in the original, we need to create a uninitialized tracked,
143
- // else a later read of the property would result in a tracked being created with
144
- // the value of the original item at that index.
145
- other_t = tracked(UNINITIALIZED, block);
146
- tracked_elements.set(i + '', other_t);
147
- }
148
- }
149
- }
150
-
151
- // If we haven't yet created a tracked for this property, we need to ensure
152
- // we do so otherwise if we read it later, then the write won't be tracked and
153
- // the heuristics of effects will be different vs if we had read the proxied
154
- // object property before writing to that property.
155
- if (t === undefined) {
156
- if (!exists || get_descriptor(target, prop)?.writable) {
157
- t = tracked(undefined, block);
158
- set(t, value, block);
159
-
160
- tracked_elements.set(prop, t);
161
- }
162
- } else {
163
- exists = t.v !== UNINITIALIZED;
164
-
165
- set(t, value, block);
166
- }
167
-
168
- var result = Reflect.set(target, prop, value, receiver);
169
-
170
- if (!exists) {
171
- // If we have mutated an array directly, we might need to
172
- // signal that length has also changed. Do it before updating metadata
173
- // to ensure that iterating over the array as a result of a metadata update
174
- // will not cause the length to be out of sync.
175
- if (typeof prop === 'string') {
176
- var n = Number(prop);
177
-
178
- if (Number.isInteger(n) && n >= tracked_len.v) {
179
- set(tracked_len, n + 1, block);
180
- }
181
- }
182
- }
183
-
184
- return result;
185
- },
186
-
187
- setPrototypeOf() {
188
- throw new Error(`Cannot set prototype of \`TrackedArray\``);
189
- },
190
-
191
- deleteProperty(target, prop) {
192
- var t = tracked_elements.get(prop);
193
-
194
- if (t === undefined) {
195
- if (prop in target) {
196
- const t = tracked(UNINITIALIZED, block);
197
- tracked_elements.set(prop, t);
198
- }
199
- } else {
200
- set(t, UNINITIALIZED, block);
201
- }
202
-
203
- return Reflect.deleteProperty(target, prop);
204
- },
205
-
206
- has(target, prop) {
207
- if (prop === TRACKED_ARRAY) {
208
- return true;
209
- }
210
- var t = tracked_elements.get(prop);
211
- var exists = (t !== undefined && t.v !== UNINITIALIZED) || Reflect.has(target, prop);
212
-
213
- if (t !== undefined || !exists || get_descriptor(target, prop)?.writable) {
214
- if (t === undefined) {
215
- t = tracked(exists ? /** @type {any} */ (target)[prop] : UNINITIALIZED, block);
216
-
217
- tracked_elements.set(prop, t);
218
- }
219
-
220
- var value = get(t);
221
- if (value === UNINITIALIZED) {
222
- return false;
223
- }
224
- }
225
-
226
- return exists;
227
- },
228
-
229
- defineProperty(_, prop, descriptor) {
230
- if (
231
- !('value' in descriptor) ||
232
- descriptor.configurable === false ||
233
- descriptor.enumerable === false ||
234
- descriptor.writable === false
235
- ) {
236
- // we disallow non-basic descriptors, because unless they are applied to the
237
- // target object — which we avoid, so that state can be forked — we will run
238
- // afoul of the various invariants
239
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/getOwnPropertyDescriptor#invariants
240
- throw new Error(
241
- 'Only basic property descriptors are supported with value and configurable, enumerable, and writable set to true',
242
- );
243
- }
244
-
245
- var t = tracked_elements.get(prop);
246
-
247
- if (t === undefined) {
248
- t = tracked(descriptor.value, block);
249
- tracked_elements.set(prop, t);
250
- } else {
251
- set(t, descriptor.value, block);
252
- }
253
-
254
- return true;
255
- },
256
- });
257
- }
258
-
259
- /**
260
- * @template T
261
- * @param {Array<T>} array
262
- * @returns {number | void}
263
- */
264
- function get_first_if_length(array) {
265
- var first = array[0];
266
-
267
- if (
268
- array.length === 1 &&
269
- 0 in array &&
270
- Number.isInteger(first) &&
271
- /** @type {number} */ (first) >= 0 &&
272
- /** @type {number} */ (first) <= MAX_ARRAY_LENGTH
273
- ) {
274
- return /** @type {number} */ (first);
275
- }
276
- }
277
-
278
- const methods_returning_arrays = new Set([
279
- 'concat',
280
- 'filter',
281
- 'flat',
282
- 'flatMap',
283
- 'map',
284
- 'slice',
285
- 'splice',
286
- 'toReversed',
287
- 'toSorted',
288
- 'toSpliced',
289
- 'with',
290
- ]);
291
-
292
- /**
293
- * @template T
294
- * @param {Iterable<T>} elements
61
+ * @param {Array<T>} elements
295
62
  * @param {Block} block
296
63
  * @returns {TrackedArray<T>}
297
64
  */
298
65
  export function tracked_array(elements, block) {
299
- return proxy({ elements, block, from_static: true });
300
- }
66
+ return array_proxy({ elements, block, from_static: true });
67
+ }
@@ -45,12 +45,14 @@ export { create_context as createContext } from './internal/client/context.js';
45
45
  export {
46
46
  flush_sync as flushSync,
47
47
  track,
48
- trackSplit,
48
+ track_split as trackSplit,
49
49
  untrack,
50
50
  } from './internal/client/runtime.js';
51
51
 
52
52
  export { TrackedArray } from './array.js';
53
53
 
54
+ export { TrackedObject } from './object.js';
55
+
54
56
  export { TrackedSet } from './set.js';
55
57
 
56
58
  export { TrackedMap } from './map.js';
@@ -22,6 +22,8 @@ export var CONTROL_FLOW_BLOCK = FOR_BLOCK | IF_BLOCK | TRY_BLOCK | COMPOSITE_BLO
22
22
  export var UNINITIALIZED = Symbol();
23
23
  /** @type {unique symbol} */
24
24
  export const TRACKED_ARRAY = Symbol();
25
+ /** @type {unique symbol} */
26
+ export const TRACKED_OBJECT = Symbol();
25
27
  export var COMPUTED_PROPERTY = Symbol();
26
28
  export var REF_PROP = 'ref';
27
29
  /** @type {unique symbol} */