dom-expressions 0.33.2 → 0.33.3
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 +2 -2
- package/src/server.js +92 -55
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dom-expressions",
|
|
3
3
|
"description": "A Fine-Grained Runtime for Performant DOM Rendering",
|
|
4
|
-
"version": "0.33.
|
|
4
|
+
"version": "0.33.3",
|
|
5
5
|
"author": "Ryan Carniato",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -22,5 +22,5 @@
|
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"babel-plugin-jsx-dom-expressions": "^0.33.0"
|
|
24
24
|
},
|
|
25
|
-
"gitHead": "
|
|
25
|
+
"gitHead": "ecf8a9328c17b9dead125ebc9ecce0bdc2bca5a4"
|
|
26
26
|
}
|
package/src/server.js
CHANGED
|
@@ -39,8 +39,8 @@ export function renderToStringAsync(code, options = {}) {
|
|
|
39
39
|
writeResource(id, p, error) {
|
|
40
40
|
if (error) return (scripts += `_$HY.set("${id}", ${serializeError(p)});`);
|
|
41
41
|
if (!p || typeof p !== "object" || !("then" in p))
|
|
42
|
-
return (scripts += serializeSet(dedupe, id, p));
|
|
43
|
-
p.then(d => (scripts += serializeSet(dedupe, id, d))).catch(
|
|
42
|
+
return (scripts += serializeSet(dedupe, id, p)) + ";";
|
|
43
|
+
p.then(d => (scripts += serializeSet(dedupe, id, d) + ";")).catch(
|
|
44
44
|
() => (scripts += `_$HY.set("${id}", {});`)
|
|
45
45
|
);
|
|
46
46
|
}
|
|
@@ -91,6 +91,7 @@ export function renderToStream(code, options = {}) {
|
|
|
91
91
|
const { nonce, onCompleteShell, onCompleteAll, renderId } = options;
|
|
92
92
|
const tmp = [];
|
|
93
93
|
const tasks = [];
|
|
94
|
+
const blockingResources = [];
|
|
94
95
|
const registry = new Map();
|
|
95
96
|
const dedupe = new WeakMap();
|
|
96
97
|
const checkEnd = () => {
|
|
@@ -106,6 +107,13 @@ export function renderToStream(code, options = {}) {
|
|
|
106
107
|
completed = true;
|
|
107
108
|
}
|
|
108
109
|
};
|
|
110
|
+
const pushTask = task => {
|
|
111
|
+
tasks.push(task);
|
|
112
|
+
if (!scheduled) {
|
|
113
|
+
Promise.resolve().then(writeTasks);
|
|
114
|
+
scheduled = true;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
109
117
|
const writeTasks = () => {
|
|
110
118
|
if (tasks.length && !completed) {
|
|
111
119
|
buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>${tasks.join(";")}</script>`);
|
|
@@ -115,6 +123,7 @@ export function renderToStream(code, options = {}) {
|
|
|
115
123
|
};
|
|
116
124
|
|
|
117
125
|
let writable;
|
|
126
|
+
let firstFlushed = false;
|
|
118
127
|
let completed = false;
|
|
119
128
|
let scriptFlushed = false;
|
|
120
129
|
let scheduled = true;
|
|
@@ -132,57 +141,58 @@ export function renderToStream(code, options = {}) {
|
|
|
132
141
|
suspense: {},
|
|
133
142
|
assets: [],
|
|
134
143
|
nonce,
|
|
135
|
-
writeResource(id, p, error) {
|
|
136
|
-
if (
|
|
137
|
-
Promise.resolve().then(writeTasks);
|
|
138
|
-
scheduled = true;
|
|
139
|
-
}
|
|
140
|
-
if (error) return tasks.push(`_$HY.set("${id}", ${serializeError(p)})`);
|
|
144
|
+
writeResource(id, p, error, wait) {
|
|
145
|
+
if (error) return pushTask(`_$HY.set("${id}", ${serializeError(p)})`);
|
|
141
146
|
if (!p || typeof p !== "object" || !("then" in p))
|
|
142
|
-
return
|
|
143
|
-
|
|
147
|
+
return pushTask(serializeSet(dedupe, id, p));
|
|
148
|
+
if (wait && !firstFlushed) blockingResources.push(p);
|
|
149
|
+
else pushTask(`_$HY.init("${id}")`);
|
|
144
150
|
p.then(d => {
|
|
145
|
-
!completed &&
|
|
146
|
-
buffer.write(
|
|
147
|
-
`<script${nonce ? ` nonce="${nonce}"` : ""}>${serializeSet(dedupe, id, d)}</script>`
|
|
148
|
-
);
|
|
151
|
+
!completed && pushTask(serializeSet(dedupe, id, d));
|
|
149
152
|
}).catch(() => {
|
|
150
|
-
!completed &&
|
|
151
|
-
buffer.write(`<script${nonce ? ` nonce="${nonce}"` : ""}>_$HY.set("${id}", {})</script>`);
|
|
153
|
+
!completed && pushTask(`_$HY.set("${id}", {})`);
|
|
152
154
|
});
|
|
153
155
|
},
|
|
154
156
|
registerFragment(key) {
|
|
155
|
-
registry.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
scheduled = true;
|
|
157
|
+
if (!registry.has(key)) {
|
|
158
|
+
registry.set(key, []);
|
|
159
|
+
pushTask(`_$HY.init("${key}")`);
|
|
159
160
|
}
|
|
160
|
-
tasks.push(`_$HY.init("${key}")`);
|
|
161
161
|
return (value, error) => {
|
|
162
162
|
if (registry.has(key)) {
|
|
163
163
|
const keys = registry.get(key);
|
|
164
164
|
registry.delete(key);
|
|
165
165
|
if (waitForFragments(registry, key)) return;
|
|
166
166
|
if ((value !== undefined || error) && !completed) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
167
|
+
if (!firstFlushed) {
|
|
168
|
+
Promise.resolve().then(
|
|
169
|
+
() => (html = replacePlaceholder(html, key, value !== undefined ? value : ""))
|
|
170
|
+
);
|
|
171
|
+
pushTask(
|
|
172
|
+
`${keys.length ? keys.map(k => `_$HY.unset("${k}");`) : ""}_$HY.set("${key}",${
|
|
173
|
+
error ? serializeError(error) : "null"
|
|
174
|
+
})`
|
|
175
|
+
);
|
|
176
|
+
} else {
|
|
177
|
+
buffer.write(`<div hidden id="${key}">${value !== undefined ? value : " "}</div>`);
|
|
178
|
+
pushTask(
|
|
179
|
+
`${keys.length ? keys.map(k => `_$HY.unset("${k}")`).join(";") : ""}$df("${key}"${
|
|
180
|
+
error ? "," + serializeError(error) : ""
|
|
181
|
+
})${!scriptFlushed ? ";" + REPLACE_SCRIPT : ""}`
|
|
182
|
+
);
|
|
183
|
+
scriptFlushed = true;
|
|
184
|
+
}
|
|
175
185
|
}
|
|
176
186
|
}
|
|
177
|
-
checkEnd();
|
|
187
|
+
if (firstFlushed) checkEnd();
|
|
178
188
|
return true;
|
|
179
189
|
};
|
|
180
190
|
}
|
|
181
191
|
};
|
|
182
192
|
|
|
183
193
|
let html = resolveSSRNode(escape(code()));
|
|
184
|
-
|
|
185
|
-
|
|
194
|
+
function doShell() {
|
|
195
|
+
html = injectAssets(sharedConfig.context.assets, html);
|
|
186
196
|
if (tasks.length) html = injectScripts(html, tasks.join(";"), nonce);
|
|
187
197
|
buffer.write(html);
|
|
188
198
|
tasks.length = 0;
|
|
@@ -193,32 +203,40 @@ export function renderToStream(code, options = {}) {
|
|
|
193
203
|
!completed && buffer.write(v);
|
|
194
204
|
}
|
|
195
205
|
});
|
|
196
|
-
}
|
|
206
|
+
}
|
|
197
207
|
|
|
198
208
|
return {
|
|
199
209
|
pipe(w) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
210
|
+
Promise.allSettled(blockingResources).then(() => {
|
|
211
|
+
doShell();
|
|
212
|
+
buffer = writable = w;
|
|
213
|
+
tmp.forEach(chunk => buffer.write(chunk));
|
|
214
|
+
firstFlushed = true;
|
|
215
|
+
if (completed) writable.end();
|
|
216
|
+
else setTimeout(checkEnd);
|
|
217
|
+
});
|
|
204
218
|
},
|
|
205
219
|
pipeTo(w) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
Promise.allSettled(blockingResources).then(() => {
|
|
221
|
+
doShell();
|
|
222
|
+
const encoder = new TextEncoder();
|
|
223
|
+
const writer = w.getWriter();
|
|
224
|
+
writable = {
|
|
225
|
+
end() {
|
|
226
|
+
writer.releaseLock();
|
|
227
|
+
w.close();
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
buffer = {
|
|
231
|
+
write(payload) {
|
|
232
|
+
writer.write(encoder.encode(payload));
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
tmp.forEach(chunk => buffer.write(chunk));
|
|
236
|
+
firstFlushed = true;
|
|
237
|
+
if (completed) writable.end();
|
|
238
|
+
else setTimeout(checkEnd);
|
|
239
|
+
});
|
|
222
240
|
}
|
|
223
241
|
};
|
|
224
242
|
}
|
|
@@ -465,9 +483,28 @@ function waitForFragments(registry, key) {
|
|
|
465
483
|
|
|
466
484
|
function serializeSet(registry, key, value) {
|
|
467
485
|
const exist = registry.get(value);
|
|
468
|
-
if (exist) return `_$HY.set("${key}", _$HY.r["${exist}"][0])
|
|
486
|
+
if (exist) return `_$HY.set("${key}", _$HY.r["${exist}"][0])`;
|
|
469
487
|
value !== null && typeof value === "object" && registry.set(value, key);
|
|
470
|
-
return `_$HY.set("${key}", ${devalue(value)})
|
|
488
|
+
return `_$HY.set("${key}", ${devalue(value)})`;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function replacePlaceholder(html, key, value) {
|
|
492
|
+
const nextRegex = /(<[/]?span[^>]*>)/g;
|
|
493
|
+
const marker = `<span id="pl-${key}">`;
|
|
494
|
+
|
|
495
|
+
const first = html.indexOf(marker);
|
|
496
|
+
if (first === -1) return html;
|
|
497
|
+
nextRegex.lastIndex = first + marker.length;
|
|
498
|
+
let match;
|
|
499
|
+
let open = 0,
|
|
500
|
+
close = 0;
|
|
501
|
+
while ((match = nextRegex.exec(html))) {
|
|
502
|
+
if (match[0][1] === "/") {
|
|
503
|
+
close++;
|
|
504
|
+
if (close > open) break;
|
|
505
|
+
} else open++;
|
|
506
|
+
}
|
|
507
|
+
return html.slice(0, first) + value + html.slice(nextRegex.lastIndex);
|
|
471
508
|
}
|
|
472
509
|
|
|
473
510
|
/* istanbul ignore next */
|