Skip to content

Commit 51392be

Browse files
meh
1 parent e39b4b7 commit 51392be

File tree

4 files changed

+58
-55
lines changed

4 files changed

+58
-55
lines changed

packages/svelte/src/internal/server/dev.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ function print_error(payload, message) {
4040

4141
// eslint-disable-next-line no-console
4242
console.error(message);
43-
payload.out.push({
44-
type: 'head',
45-
content: `<script>console.error(${JSON.stringify(message)})</script>`
46-
});
43+
payload.child(
44+
(payload) => payload.push(`<script>console.error(${JSON.stringify(message)})</script>`),
45+
'head'
46+
);
4747
}
4848

4949
export function reset_elements() {

packages/svelte/src/internal/server/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,11 @@ export async function render_async(component, options = {}) {
191191
* @returns {void}
192192
*/
193193
export function head(payload, fn) {
194-
payload.out.push({ type: 'head', content: BLOCK_OPEN });
195-
payload.child(fn, 'head');
196-
payload.out.push({ type: 'head', content: BLOCK_CLOSE });
194+
payload.child((payload) => {
195+
payload.push(BLOCK_OPEN);
196+
payload.child(fn);
197+
payload.push(BLOCK_CLOSE);
198+
}, 'head');
197199
}
198200

199201
/**

packages/svelte/src/internal/server/payload.js

Lines changed: 46 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
/** @typedef {{ type: 'head' | 'body', content: string }} TNode */
2-
/** @typedef {{ [key in TNode['type']]: string }} AccumulatedContent */
1+
/** @typedef {'head' | 'body'} PayloadType */
2+
/** @typedef {{ [key in PayloadType]: string }} AccumulatedContent */
33
/** @typedef {{ start: number, end: number, fn: (content: AccumulatedContent) => AccumulatedContent | Promise<AccumulatedContent> }} Compaction */
44

5-
// TODO we test for `instanceof AsyncContentTree` in some tight loops -- we might optimize
6-
// by giving the tree a symbol property and checking that instead if we can actually notice any impact
7-
85
/**
9-
* /**
10-
* A base class for payloads. Payloads are basically a tree of `string | Payload`s, where each
11-
* `Payload` in the tree represents work that may or may not have completed. A payload can be
12-
* {@link collect}ed to aggregate the content from itself and all of its children, but this will
13-
* throw if any of the children are performing asynchronous work. A payload can also be collected
14-
* asynchronously with {@link collect_async}, which will wait for all children to complete before
15-
* collecting their contents.
6+
* Payloads are basically a tree of `string | Payload`s, where each `Payload` in the tree represents
7+
* work that may or may not have completed. A payload can be {@link collect}ed to aggregate the
8+
* content from itself and all of its children, but this will throw if any of the children are
9+
* performing asynchronous work. A payload can also be collected asynchronously with
10+
* {@link collect_async}, which will wait for all children to complete before collecting their
11+
* contents.
12+
*
13+
* The `string` values within a payload are always associated with the {@link type} of that payload. To switch types,
14+
* call {@link child} with a different `type` argument.
1615
*/
1716
export class Payload {
1817
/**
19-
* @type {TNode['type']}
18+
* @type {PayloadType}
2019
*/
2120
type;
2221

@@ -25,7 +24,7 @@ export class Payload {
2524

2625
/**
2726
* The contents of the payload.
28-
* @type {(TNode | Payload)[]}
27+
* @type {(string | Payload)[]}
2928
*/
3029
out = [];
3130

@@ -55,7 +54,7 @@ export class Payload {
5554
* @param {TreeState} [global]
5655
* @param {{ select_value: string | undefined }} [local]
5756
* @param {Payload | undefined} [parent]
58-
* @param {TNode['type']} [type]
57+
* @param {PayloadType} [type]
5958
*/
6059
constructor(global = new TreeState(), local = { select_value: undefined }, parent, type) {
6160
this.global = global;
@@ -69,7 +68,7 @@ export class Payload {
6968
* but has its own `out` array and `promise` property. The child payload is automatically
7069
* inserted into the parent payload's `out` array.
7170
* @param {(tree: Payload) => void | Promise<void>} render
72-
* @param {TNode['type']} [type]
71+
* @param {PayloadType} [type]
7372
* @returns {void}
7473
*/
7574
child(render, type) {
@@ -81,13 +80,9 @@ export class Payload {
8180
}
8281
}
8382

84-
/**
85-
* This is a convenience function that allows pushing strings, and will automatically use the configured `type` of this
86-
* payload. It's fine to push content of a different type to the `out` array; it's just more annoying to write.
87-
* @param {string} content
88-
*/
83+
/** @param {string} content */
8984
push(content) {
90-
this.out.push({ type: this.type, content });
85+
this.out.push(content);
9186
}
9287

9388
/**
@@ -100,25 +95,24 @@ export class Payload {
10095
const to_compact = this.out.splice(start, end - start, child);
10196
const promises = Payload.#collect_promises(to_compact, []);
10297

103-
/** @param {AccumulatedContent | Promise<AccumulatedContent>} res */
104-
const push_result = (res) => {
98+
const push_result = () => {
99+
const res = fn(Payload.#collect_content(to_compact, this.type));
105100
if (res instanceof Promise) {
106-
child.promise = res.then((resolved) => {
101+
const promise = res.then((resolved) => {
107102
Payload.#push_accumulated_content(child, resolved);
108103
});
104+
return promise;
109105
} else {
110106
Payload.#push_accumulated_content(child, res);
111107
}
112108
};
113109

114110
if (promises.length > 0) {
115-
// we have to wait for the accumulated work associated with all branches to complete,
111+
// we have to wait for the accumulated work associated with all pruned branches to complete,
116112
// then we can accumulate their content to compact it.
117-
child.promise = Promise.all(promises)
118-
.then(() => fn(Payload.#collect_content(to_compact)))
119-
.then(push_result);
113+
child.promise = Promise.all(promises).then(push_result);
120114
} else {
121-
push_result(fn(Payload.#collect_content(to_compact)));
115+
push_result();
122116
}
123117
}
124118

@@ -136,26 +130,27 @@ export class Payload {
136130
async collect_async() {
137131
// TODO: Should probably use `Promise.allSettled` here just so we can report detailed errors
138132
await Promise.all(Payload.#collect_promises(this.out, this.promise ? [this.promise] : []));
139-
return Payload.#collect_content(this.out);
133+
return Payload.#collect_content(this.out, this.type);
140134
}
141135

142136
/**
143-
* Collect all of the code from the `out` array and return it as a string.
137+
* Collect all of the code from the `out` array and return it as a string. Throws if any of the children are
138+
* performing asynchronous work.
144139
* @returns {AccumulatedContent}
145140
*/
146141
collect() {
147142
const promises = Payload.#collect_promises(this.out, this.promise ? [this.promise] : []);
148143
if (promises.length > 0) {
149-
// TODO is there a good way to report where this is? Probably by using some sort of loc or stack trace in `child` creation
144+
// TODO is there a good way to report where this is? Probably by using some sort of loc or stack trace in `child` creation.
150145
throw new Error('Encountered an asynchronous component while rendering synchronously');
151146
}
152147

153-
return Payload.#collect_content(this.out);
148+
return Payload.#collect_content(this.out, this.type);
154149
}
155150

156151
copy() {
157152
const copy = new Payload(this.global, this.local, this.parent, this.type);
158-
copy.out = this.out.map((item) => (item instanceof Payload ? item.copy() : item));
153+
copy.out = this.out.map((item) => (typeof item === 'string' ? item : item.copy()));
159154
copy.promise = this.promise;
160155
return copy;
161156
}
@@ -167,7 +162,7 @@ export class Payload {
167162
this.global.subsume(other.global);
168163
this.local = other.local;
169164
this.out = other.out.map((item) => {
170-
if (item instanceof Payload) {
165+
if (typeof item !== 'string') {
171166
item.subsume(item);
172167
}
173168
return item;
@@ -177,34 +172,34 @@ export class Payload {
177172
}
178173

179174
/**
180-
* @param {(TNode | Payload)[]} items
175+
* @param {(string | Payload)[]} items
181176
* @param {Promise<void>[]} promises
182177
* @returns {Promise<void>[]}
183178
*/
184179
static #collect_promises(items, promises) {
185180
for (const item of items) {
186-
if (item instanceof Payload) {
187-
if (item.promise) {
188-
promises.push(item.promise);
189-
}
190-
Payload.#collect_promises(item.out, promises);
181+
if (typeof item === 'string') continue;
182+
if (item.promise) {
183+
promises.push(item.promise);
191184
}
185+
Payload.#collect_promises(item.out, promises);
192186
}
193187
return promises;
194188
}
195189

196190
/**
197191
* Collect all of the code from the `out` array and return it as a string.
198-
* @param {(TNode | Payload)[]} items
192+
* @param {(string | Payload)[]} items
193+
* @param {PayloadType} current_type
199194
* @param {AccumulatedContent} content
200195
* @returns {AccumulatedContent}
201196
*/
202-
static #collect_content(items, content = { head: '', body: '' }) {
197+
static #collect_content(items, current_type, content = { head: '', body: '' }) {
203198
for (const item of items) {
204-
if (item instanceof Payload) {
205-
Payload.#collect_content(item.out, content);
199+
if (typeof item === 'string') {
200+
content[current_type] += item;
206201
} else {
207-
content[item.type] += item.content;
202+
Payload.#collect_content(item.out, item.type, content);
208203
}
209204
}
210205
return content;
@@ -216,7 +211,10 @@ export class Payload {
216211
*/
217212
static #push_accumulated_content(tree, accumulated_content) {
218213
for (const [type, content] of Object.entries(accumulated_content)) {
219-
tree.out.push({ type: /** @type {TNode['type']} */ (type), content });
214+
if (!content) continue;
215+
const child = new Payload(tree.global, tree.local, tree, /** @type {PayloadType} */ (type));
216+
child.push(content);
217+
tree.out.push(child);
220218
}
221219
}
222220
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { test } from '../../test';
2+
3+
export default test({ load_compiled: true });

0 commit comments

Comments
 (0)