Skip to content
This repository was archived by the owner on Jan 11, 2023. It is now read-only.

Commit e0de230

Browse files
authored
Slot-based routing (#573)
1 parent c637687 commit e0de230

File tree

22 files changed

+141
-99
lines changed

22 files changed

+141
-99
lines changed

runtime/internal/error.svelte

+7-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
<svelte:component this={child.component} {...child.props}/>
1+
<h1>{status}</h1>
2+
3+
<p>{error.message}</p>
4+
5+
{#if process.env.NODE_ENV === 'development'}
6+
<pre>{error.stack}</pre>
7+
{/if}

runtime/internal/layout.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<svelte:component this={child.component} {...child.props}/>
1+
<slot></slot>

runtime/src/app/app.ts

+25-52
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { writable } from 'svelte/store.mjs';
2-
import Sapper from '@sapper/internal/Sapper.svelte';
2+
import App from '@sapper/internal/App.svelte';
33
import { stores } from '@sapper/internal/shared';
44
import { Root, root_preload, ErrorComponent, ignore, components, routes } from '@sapper/internal/manifest-client';
55
import {
@@ -180,8 +180,13 @@ async function render(redirect: Redirect, branch: any[], props: any, page: Page)
180180
stores.preloading.set(false);
181181

182182
if (root_component) {
183-
root_component.props = props;
183+
root_component.$set(props);
184184
} else {
185+
props.session = session;
186+
props.level0 = {
187+
props: await root_preloaded
188+
};
189+
185190
// first load — remove SSR'd <head> contents
186191
const start = document.querySelector('#sapper-head-start');
187192
const end = document.querySelector('#sapper-head-end');
@@ -192,13 +197,9 @@ async function render(redirect: Redirect, branch: any[], props: any, page: Page)
192197
detach(end);
193198
}
194199

195-
root_component = new Sapper({
200+
root_component = new App({
196201
target,
197-
props: {
198-
Root,
199-
props,
200-
session
201-
},
202+
props,
202203
hydrate: true
203204
});
204205
}
@@ -211,13 +212,14 @@ async function render(redirect: Redirect, branch: any[], props: any, page: Page)
211212
export async function hydrate_target(target: Target): Promise<{
212213
redirect?: Redirect;
213214
props?: any;
214-
branch?: Array<{ Component: ComponentConstructor, preload: (page) => Promise<any>, segment: string }>
215+
branch?: Array<{ Component: ComponentConstructor, preload: (page) => Promise<any>, segment: string }>;
215216
}> {
216217
const { route, page } = target;
217218
const segments = page.path.split('/').filter(Boolean);
218219

219220
let redirect: Redirect = null;
220-
let error: { statusCode: number, message: Error | string } = null;
221+
222+
const props = { error: null, status: 200, segments: [segments[0]] };
221223

222224
const preload_context = {
223225
fetch: (url: string, opts?: any) => fetch(url, opts),
@@ -227,8 +229,9 @@ export async function hydrate_target(target: Target): Promise<{
227229
}
228230
redirect = { statusCode, location };
229231
},
230-
error: (statusCode: number, message: Error | string) => {
231-
error = { statusCode, message };
232+
error: (status: number, error: Error | string) => {
233+
props.error = typeof error === 'string' ? new Error(error) : error;
234+
props.status = status;
232235
}
233236
};
234237

@@ -241,15 +244,19 @@ export async function hydrate_target(target: Target): Promise<{
241244
}
242245

243246
let branch;
247+
let l = 1;
244248

245249
try {
246250
branch = await Promise.all(route.parts.map(async (part, i) => {
251+
props.segments[l] = segments[i + 1]; // TODO make this less confusing
247252
if (!part) return null;
248253

254+
const j = l++;
255+
249256
const segment = segments[i];
250257
if (!session_dirty && current_branch[i] && current_branch[i].segment === segment) return current_branch[i];
251258

252-
const { default: Component, preload } = await load_component(components[part.i]);
259+
const { default: component, preload } = await load_component(components[part.i]);
253260

254261
let preloaded;
255262
if (ready || !initial_data.preloaded[i + 1]) {
@@ -264,49 +271,15 @@ export async function hydrate_target(target: Target): Promise<{
264271
preloaded = initial_data.preloaded[i + 1];
265272
}
266273

267-
return { Component, preloaded, segment };
274+
return (props[`level${j}`] = { component, props: preloaded, segment });
268275
}));
269-
} catch (e) {
270-
error = { statusCode: 500, message: e };
276+
} catch (error) {
277+
props.error = error;
278+
props.status = 500;
271279
branch = [];
272280
}
273281

274-
if (redirect) return { redirect };
275-
276-
if (error) {
277-
// TODO be nice if this was less of a special case
278-
return {
279-
props: {
280-
child: {
281-
component: ErrorComponent,
282-
props: {
283-
error: typeof error.message === 'string' ? new Error(error.message) : error.message,
284-
status: error.statusCode
285-
}
286-
}
287-
},
288-
branch
289-
};
290-
}
291-
292-
const props = Object.assign({}, await root_preloaded, {
293-
child: { segment: segments[0] }
294-
});
295-
296-
let level = props.child;
297-
298-
branch.forEach((node, i) => {
299-
if (!node) return;
300-
301-
level.component = node.Component;
302-
level.props = Object.assign({}, node.preloaded, {
303-
child: { segment: segments[i + 1] }
304-
});
305-
306-
level = level.props.child;
307-
});
308-
309-
return { props, branch };
282+
return { redirect, props, branch };
310283
}
311284

312285
function load_css(chunk: string) {

runtime/src/server/middleware/get_page_handler.ts

+30-27
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { IGNORE } from '../constants';
99
import { Manifest, Page, Props, Req, Res } from './types';
1010
import { build_dir, dev, src_dir } from '@sapper/internal/manifest-server';
1111
import { stores } from '@sapper/internal/shared';
12-
import Sapper from '@sapper/internal/Sapper.svelte';
12+
import App from '@sapper/internal/App.svelte';
1313

1414
export function get_page_handler(
1515
manifest: Manifest,
@@ -38,7 +38,7 @@ export function get_page_handler(
3838
}
3939

4040
async function handle_page(page: Page, req: Req, res: Res, status = 200, error: Error | string = null) {
41-
const isSWIndexHtml = req.path === '/service-worker-index.html';
41+
const is_service_worker_index = req.path === '/service-worker-index.html';
4242
const build_info: {
4343
bundler: 'rollup' | 'webpack',
4444
shimport: string | null,
@@ -52,7 +52,7 @@ export function get_page_handler(
5252
// preload main.js and current route
5353
// TODO detect other stuff we can preload? images, CSS, fonts?
5454
let preloaded_chunks = Array.isArray(build_info.assets.main) ? build_info.assets.main : [build_info.assets.main];
55-
if (!error && !isSWIndexHtml) {
55+
if (!error && !is_service_worker_index) {
5656
page.parts.forEach(part => {
5757
if (!part) return;
5858

@@ -152,7 +152,7 @@ export function get_page_handler(
152152

153153

154154
let toPreload = [root_preloaded];
155-
if (!isSWIndexHtml) {
155+
if (!is_service_worker_index) {
156156
toPreload = toPreload.concat(page.parts.map(part => {
157157
if (!part) return null;
158158

@@ -193,48 +193,51 @@ export function get_page_handler(
193193

194194
const segments = req.path.split('/').filter(Boolean);
195195

196-
const props = Object.assign({}, preloaded[0], {
197-
child: {
196+
// TODO make this less confusing
197+
const layout_segments = [segments[0]];
198+
let l = 1;
199+
200+
page.parts.forEach((part, i) => {
201+
layout_segments[l] = segments[i + 1];
202+
if (!part) return null;
203+
l++;
204+
});
205+
206+
const props = {
207+
segments: layout_segments,
208+
status: error ? status : 200,
209+
error: error ? error instanceof Error ? error : { message: error } : null,
210+
session: writable(session),
211+
level0: {
212+
props: preloaded[0]
213+
},
214+
level1: {
198215
segment: segments[0],
199216
props: {}
200217
}
201-
});
218+
};
202219

203-
let level = props.child;
204-
if (!isSWIndexHtml) {
220+
if (!is_service_worker_index) {
221+
let l = 1;
205222
for (let i = 0; i < page.parts.length; i += 1) {
206223
const part = page.parts[i];
207224
if (!part) continue;
208225

209-
Object.assign(level, {
226+
props[`level${l++}`] = {
210227
component: part.component,
211-
props: Object.assign({}, preloaded[i + 1])
212-
});
213-
214-
level.props.child = <Props["child"]>{
215-
segment: segments[i + 1],
216-
props: {}
228+
props: preloaded[i + 1],
229+
segment: segments[i]
217230
};
218-
level = level.props.child;
219231
}
220232
}
221233

222-
if (error) {
223-
props.child.props.error = error instanceof Error ? error : { message: error };
224-
props.child.props.status = status;
225-
}
226-
227234
stores.page.set({
228235
path: req.path,
229236
query: req.query,
230237
params: params
231238
});
232239

233-
const { html, head, css } = Sapper.render({
234-
Root: manifest.root,
235-
props: props,
236-
session: writable(session)
237-
});
240+
const { html, head, css } = App.render(props);
238241

239242
const serialized = {
240243
preloaded: `[${preloaded.map(data => try_serialize(data)).join(',')}]`,

src/api/build.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as fs from 'fs';
22
import * as path from 'path';
33
import minify_html from './utils/minify_html';
4-
import { create_compilers, create_main_manifests, create_manifest_data, create_serviceworker_manifest } from '../core';
4+
import { create_compilers, create_app, create_manifest_data, create_serviceworker_manifest } from '../core';
55
import { copy_shimport } from './utils/copy_shimport';
66
import read_template from '../core/read_template';
77
import { CompileResult } from '../core/create_compilers/interfaces';
@@ -71,7 +71,7 @@ export async function build({
7171
const manifest_data = create_manifest_data(routes);
7272

7373
// create src/node_modules/@sapper/app.mjs and server.mjs
74-
create_main_manifests({
74+
create_app({
7575
bundler,
7676
manifest_data,
7777
cwd,

src/api/dev.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as http from 'http';
44
import * as child_process from 'child_process';
55
import * as ports from 'port-authority';
66
import { EventEmitter } from 'events';
7-
import { create_manifest_data, create_main_manifests, create_compilers, create_serviceworker_manifest } from '../core';
7+
import { create_manifest_data, create_app, create_compilers, create_serviceworker_manifest } from '../core';
88
import { Compiler, Compilers } from '../core/create_compilers';
99
import { CompileResult } from '../core/create_compilers/interfaces';
1010
import Deferred from './utils/Deferred';
@@ -162,7 +162,7 @@ class Watcher extends EventEmitter {
162162

163163
try {
164164
manifest_data = create_manifest_data(routes);
165-
create_main_manifests({
165+
create_app({
166166
bundler: this.bundler,
167167
manifest_data,
168168
dev: true,
@@ -190,7 +190,7 @@ class Watcher extends EventEmitter {
190190
() => {
191191
try {
192192
const new_manifest_data = create_manifest_data(routes);
193-
create_main_manifests({
193+
create_app({
194194
bundler: this.bundler,
195195
manifest_data, // TODO is this right? not new_manifest_data?
196196
dev: true,

src/api/utils/copy_runtime.ts

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const runtime = [
66
'app.mjs',
77
'server.mjs',
88
'internal/shared.mjs',
9-
'internal/Sapper.svelte',
109
'internal/layout.svelte',
1110
'internal/error.svelte'
1211
].map(file => ({

src/core.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export * from './core/create_manifests';
1+
export * from './core/create_app';
22
export { default as create_compilers } from './core/create_compilers/index';
33
export { default as create_manifest_data } from './core/create_manifest_data';

0 commit comments

Comments
 (0)