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.
- package/package.json +6 -6
- package/src/compiler/index.js +2 -2
- package/src/compiler/phases/1-parse/index.js +0 -5
- package/src/compiler/phases/3-transform/client/index.js +1621 -1673
- package/src/compiler/phases/3-transform/server/index.js +350 -77
- package/src/compiler/utils.js +60 -0
- package/src/runtime/{index.js → index-client.js} +6 -0
- package/src/runtime/index-server.js +32 -0
- package/src/runtime/internal/client/runtime.js +4 -0
- package/src/runtime/internal/server/context.js +67 -0
- package/src/runtime/internal/server/index.js +107 -22
- package/src/server/index.js +1 -1
- package/src/utils/escaping.js +26 -0
- package/tests/client/__snapshots__/basic.test.ripple.snap +9 -0
- package/tests/client/basic.test.ripple +13 -0
|
@@ -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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
27
|
+
head = '';
|
|
28
|
+
body = '';
|
|
29
|
+
#parent = null;
|
|
6
30
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
31
|
+
constructor(parent) {
|
|
32
|
+
this.#parent = parent;
|
|
33
|
+
}
|
|
10
34
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
35
|
+
push(str) {
|
|
36
|
+
this.body += str;
|
|
37
|
+
}
|
|
14
38
|
}
|
|
15
39
|
|
|
16
|
-
export async function
|
|
17
|
-
|
|
40
|
+
export async function render(component) {
|
|
41
|
+
const output = new Output(null);
|
|
18
42
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
50
|
+
const { head, body } = output;
|
|
26
51
|
|
|
27
|
-
|
|
52
|
+
return { head, body };
|
|
28
53
|
}
|
|
29
54
|
|
|
30
55
|
export function push_component() {
|
|
31
|
-
|
|
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
|
-
|
|
64
|
+
var component = active_component;
|
|
65
|
+
active_component = component.p;
|
|
36
66
|
}
|
|
37
67
|
|
|
38
68
|
export async function async(fn) {
|
|
39
|
-
|
|
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
|
+
}
|
package/src/server/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
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 === '&' ? '&' : ch === '"' ? '"' : '<');
|
|
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
|
});
|