ripple 0.2.76 → 0.2.78

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.
@@ -1,40 +1,125 @@
1
+ /** @import { Derived } from '#client' */
2
+
3
+ import { DERIVED, UNINITIALIZED } from '../client/constants';
4
+ import { is_tracked_object } from '../client/utils';
5
+
6
+ import { escape } from '../../../utils/escaping.js';
7
+ import { is_boolean_attribute } from '../../../compiler/utils';
8
+
9
+ export { escape };
10
+
11
+ export let active_component = null;
12
+
13
+ /**
14
+ * `<div translate={false}>` should be rendered as `<div translate="no">` and _not_
15
+ * `<div translate="false">`, which is equivalent to `<div translate="yes">`. There
16
+ * may be other odd cases that need to be added to this list in future
17
+ * @type {Record<string, Map<any, string>>}
18
+ */
19
+ const replacements = {
20
+ translate: new Map([
21
+ [true, 'yes'],
22
+ [false, 'no'],
23
+ ]),
24
+ };
1
25
 
2
26
  class Output {
3
- head = '';
4
- body = '';
5
- #parent = null;
27
+ head = '';
28
+ body = '';
29
+ #parent = null;
6
30
 
7
- constructor(parent) {
8
- this.#parent = parent;
9
- }
31
+ constructor(parent) {
32
+ this.#parent = parent;
33
+ }
10
34
 
11
- push(str) {
12
- this.body += str;
13
- }
35
+ push(str) {
36
+ this.body += str;
37
+ }
14
38
  }
15
39
 
16
- export async function renderToString(component) {
17
- const output = new Output(null);
40
+ export async function render(component) {
41
+ const output = new Output(null);
18
42
 
19
- if (component.async) {
20
- await component(output, {});
21
- } else {
22
- component(output, {});
23
- }
43
+ // TODO add expando "async" property to component functions during SSR
44
+ if (component.async) {
45
+ await component(output, {});
46
+ } else {
47
+ component(output, {});
48
+ }
24
49
 
25
- const { head, body } = output;
50
+ const { head, body } = output;
26
51
 
27
- return { head, body };
52
+ return { head, body };
28
53
  }
29
54
 
30
55
  export function push_component() {
31
- debugger;
56
+ var component = {
57
+ c: null,
58
+ p: active_component,
59
+ };
60
+ active_component = component;
32
61
  }
33
62
 
34
63
  export function pop_component() {
35
- debugger;
64
+ var component = active_component;
65
+ active_component = component.p;
36
66
  }
37
67
 
38
68
  export async function async(fn) {
39
- // TODO
40
- }
69
+ // TODO
70
+ }
71
+
72
+ function get_derived(tracked) {
73
+ let v = tracked.v;
74
+
75
+ if (v === UNINITIALIZED) {
76
+ v = tracked.fn();
77
+ tracked.v = v;
78
+ }
79
+ return v;
80
+ }
81
+
82
+ export function get(tracked) {
83
+ // reflect back the value if it's not boxed
84
+ if (!is_tracked_object(tracked)) {
85
+ return tracked;
86
+ }
87
+
88
+ return (tracked.f & DERIVED) !== 0 ? get_derived(/** @type {Derived} */ (tracked)) : tracked.v;
89
+ }
90
+
91
+ /**
92
+ * @template V
93
+ * @param {string} name
94
+ * @param {V} value
95
+ * @param {boolean} [is_boolean]
96
+ * @returns {string}
97
+ */
98
+ export function attr(name, value, is_boolean = false) {
99
+ if (name === 'hidden' && value !== 'until-found') {
100
+ is_boolean = true;
101
+ }
102
+ if (value == null || (!value && is_boolean)) return '';
103
+ const normalized = (name in replacements && replacements[name].get(value)) || value;
104
+ const assignment = is_boolean ? '' : `="${escape(normalized, true)}"`;
105
+ return ` ${name}${assignment}`;
106
+ }
107
+
108
+ export function spread_attrs(attrs, css_hash) {
109
+ let attr_str = '';
110
+ let name;
111
+
112
+ for (name in attrs) {
113
+ var value = attrs[name];
114
+
115
+ if (typeof value === 'function') continue;
116
+
117
+ if (name === 'class' && css_hash) {
118
+ value = (value == null ? '' : value) + ' ' + css_hash;
119
+ }
120
+
121
+ attr_str += attr(name, value, is_boolean_attribute(name));
122
+ }
123
+
124
+ return attr_str;
125
+ }
@@ -1 +1 @@
1
- export { renderToString } from '../runtime/internal/server/index.js';
1
+ export { render } from '../runtime/internal/server/index.js';
@@ -0,0 +1,26 @@
1
+ const ATTR_REGEX = /[&"<]/g;
2
+ const CONTENT_REGEX = /[&<]/g;
3
+
4
+ /**
5
+ * @template V
6
+ * @param {V} value
7
+ * @param {boolean} [is_attr]
8
+ */
9
+ export function escape(value, is_attr) {
10
+ const str = String(value ?? '');
11
+
12
+ const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX;
13
+ pattern.lastIndex = 0;
14
+
15
+ let escaped = '';
16
+ let last = 0;
17
+
18
+ while (pattern.test(str)) {
19
+ const i = pattern.lastIndex - 1;
20
+ const ch = str[i];
21
+ escaped += str.substring(last, i) + (ch === '&' ? '&amp;' : ch === '"' ? '&quot;' : '&lt;');
22
+ last = i + 1;
23
+ }
24
+
25
+ return escaped + str.substring(last);
26
+ }
@@ -111,3 +111,12 @@ exports[`basic > renders simple JS expression logic correctly 1`] = `
111
111
 
112
112
  </div>
113
113
  `;
114
+
115
+ exports[`basic > should handle lexical scopes correctly 1`] = `
116
+ <div>
117
+ <section>
118
+ Nested scope variable
119
+ </section>
120
+
121
+ </div>
122
+ `;
@@ -1401,4 +1401,17 @@ describe('basic', () => {
1401
1401
  const pre = container.querySelectorAll('pre')[0];
1402
1402
  expect(pre.textContent).toBe('true');
1403
1403
  });
1404
+
1405
+ it('should handle lexical scopes correctly', () => {
1406
+ component App() {
1407
+ <section>
1408
+ let sectionData = 'Nested scope variable';
1409
+
1410
+ {sectionData}
1411
+ </section>
1412
+ }
1413
+
1414
+ render(App);
1415
+ expect(container).toMatchSnapshot();
1416
+ });
1404
1417
  });