mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-15 20:34:44 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b28cdff233 | ||
|
|
7f586ff1a3 | ||
|
|
731d4f535c | ||
|
|
f8c731ca21 | ||
|
|
39eb3be01e |
@@ -1,5 +1,13 @@
|
|||||||
# sapper changelog
|
# sapper changelog
|
||||||
|
|
||||||
|
## 0.15.2
|
||||||
|
|
||||||
|
* Collapse component chains where no intermediate layout component is specified ([#312](https://github.com/sveltejs/sapper/issues/312))
|
||||||
|
|
||||||
|
## 0.15.1
|
||||||
|
|
||||||
|
* Prevent confusing error when no root layout is specified
|
||||||
|
|
||||||
## 0.15.0
|
## 0.15.0
|
||||||
|
|
||||||
* Nested routes (consult [migration guide](https://sapper.svelte.technology/guide#0-14-to-0-15) and docs on [layouts](https://sapper.svelte.technology/guide#layouts)) ([#262](https://github.com/sveltejs/sapper/issues/262))
|
* Nested routes (consult [migration guide](https://sapper.svelte.technology/guide#0-14-to-0-15) and docs on [layouts](https://sapper.svelte.technology/guide#layouts)) ([#262](https://github.com/sveltejs/sapper/issues/262))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sapper",
|
"name": "sapper",
|
||||||
"version": "0.15.0",
|
"version": "0.15.2",
|
||||||
"description": "Military-grade apps, engineered by Svelte",
|
"description": "Military-grade apps, engineered by Svelte",
|
||||||
"main": "dist/middleware.ts.js",
|
"main": "dist/middleware.ts.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ function generate_client(
|
|||||||
|
|
||||||
let code = `
|
let code = `
|
||||||
// This file is generated by Sapper — do not edit it!
|
// This file is generated by Sapper — do not edit it!
|
||||||
import root from '${posixify(`${path_to_routes}/${routes.root.file}`)}';
|
import root from '${get_file(path_to_routes, routes.root)}';
|
||||||
import error from '${posixify(`${path_to_routes}/_error.html`)}';
|
import error from '${posixify(`${path_to_routes}/_error.html`)}';
|
||||||
|
|
||||||
${routes.components.map(component =>
|
${routes.components.map(component =>
|
||||||
@@ -79,6 +79,8 @@ function generate_client(
|
|||||||
pattern: ${page.pattern},
|
pattern: ${page.pattern},
|
||||||
parts: [
|
parts: [
|
||||||
${page.parts.map(part => {
|
${page.parts.map(part => {
|
||||||
|
if (part === null) return 'null';
|
||||||
|
|
||||||
if (part.params.length > 0) {
|
if (part.params.length > 0) {
|
||||||
const props = part.params.map((param, i) => `${param}: match[${i + 1}]`);
|
const props = part.params.map((param, i) => `${param}: match[${i + 1}]`);
|
||||||
return `{ component: ${part.component.name}, params: match => ({ ${props.join(', ')} }) }`;
|
return `{ component: ${part.component.name}, params: match => ({ ${props.join(', ')} }) }`;
|
||||||
@@ -124,7 +126,7 @@ function generate_server(
|
|||||||
`import * as ${route.name} from '${posixify(`${path_to_routes}/${route.file}`)}';`),
|
`import * as ${route.name} from '${posixify(`${path_to_routes}/${route.file}`)}';`),
|
||||||
routes.components.map(component =>
|
routes.components.map(component =>
|
||||||
`import ${component.name} from '${get_file(path_to_routes, component)}';`),
|
`import ${component.name} from '${get_file(path_to_routes, component)}';`),
|
||||||
`import root from '${posixify(`${path_to_routes}/${routes.root.file}`)}';`,
|
`import root from '${get_file(path_to_routes, routes.root)}';`,
|
||||||
`import error from '${posixify(`${path_to_routes}/_error.html`)}';`
|
`import error from '${posixify(`${path_to_routes}/_error.html`)}';`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -150,6 +152,8 @@ function generate_server(
|
|||||||
pattern: ${page.pattern},
|
pattern: ${page.pattern},
|
||||||
parts: [
|
parts: [
|
||||||
${page.parts.map(part => {
|
${page.parts.map(part => {
|
||||||
|
if (part === null) return 'null';
|
||||||
|
|
||||||
const props = [
|
const props = [
|
||||||
`name: "${part.component.name}"`,
|
`name: "${part.component.name}"`,
|
||||||
`component: ${part.component.name}`
|
`component: ${part.component.name}`
|
||||||
|
|||||||
@@ -101,27 +101,21 @@ export default function create_routes(cwd = locations.routes()) {
|
|||||||
|
|
||||||
if (item.is_dir) {
|
if (item.is_dir) {
|
||||||
const index = path.join(dir, item.basename, '_layout.html');
|
const index = path.join(dir, item.basename, '_layout.html');
|
||||||
const layout = fs.existsSync(index)
|
|
||||||
? {
|
|
||||||
name: `${get_slug(item.file)}__layout`,
|
|
||||||
file: `${item.file}/_layout.html`
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (layout) {
|
const component = fs.existsSync(index) && {
|
||||||
components.push(layout);
|
name: `${get_slug(item.file)}__layout`,
|
||||||
} else if (components.indexOf(default_layout) === -1) {
|
file: `${item.file}/_layout.html`
|
||||||
components.push(default_layout);
|
};
|
||||||
}
|
|
||||||
|
if (component) components.push(component);
|
||||||
|
|
||||||
walk(
|
walk(
|
||||||
path.join(dir, item.basename),
|
path.join(dir, item.basename),
|
||||||
segments,
|
segments,
|
||||||
params,
|
params,
|
||||||
stack.concat({
|
component
|
||||||
component: layout || default_layout,
|
? stack.concat({ component, params })
|
||||||
params
|
: stack.concat(null)
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -292,6 +292,8 @@ function get_page_handler(manifest: Manifest, store_getter: (req: Req) => Store)
|
|||||||
let preloaded_chunks = Array.isArray(chunks.main) ? chunks.main : [chunks.main];
|
let preloaded_chunks = Array.isArray(chunks.main) ? chunks.main : [chunks.main];
|
||||||
if (!error) {
|
if (!error) {
|
||||||
page.parts.forEach(part => {
|
page.parts.forEach(part => {
|
||||||
|
if (!part) return;
|
||||||
|
|
||||||
// using concat because it could be a string or an array. thanks webpack!
|
// using concat because it could be a string or an array. thanks webpack!
|
||||||
preloaded_chunks = preloaded_chunks.concat(chunks[part.name]);
|
preloaded_chunks = preloaded_chunks.concat(chunks[part.name]);
|
||||||
});
|
});
|
||||||
@@ -366,6 +368,8 @@ function get_page_handler(manifest: Manifest, store_getter: (req: Req) => Store)
|
|||||||
: {};
|
: {};
|
||||||
|
|
||||||
Promise.all([root_preloaded].concat(page.parts.map(part => {
|
Promise.all([root_preloaded].concat(page.parts.map(part => {
|
||||||
|
if (!part) return null;
|
||||||
|
|
||||||
return part.component.preload
|
return part.component.preload
|
||||||
? part.component.preload.call(preload_context, {
|
? part.component.preload.call(preload_context, {
|
||||||
path: req.path,
|
path: req.path,
|
||||||
@@ -411,23 +415,28 @@ function get_page_handler(manifest: Manifest, store_getter: (req: Req) => Store)
|
|||||||
|
|
||||||
const data = Object.assign({}, props, preloaded[0], {
|
const data = Object.assign({}, props, preloaded[0], {
|
||||||
params: {},
|
params: {},
|
||||||
child: {}
|
child: {
|
||||||
|
segment: segments[0]
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let level = data.child;
|
let level = data.child;
|
||||||
for (let i = 0; i < page.parts.length; i += 1) {
|
for (let i = 0; i < page.parts.length; i += 1) {
|
||||||
const part = page.parts[i];
|
const part = page.parts[i];
|
||||||
|
if (!part) continue;
|
||||||
|
|
||||||
const get_params = part.params || (() => ({}));
|
const get_params = part.params || (() => ({}));
|
||||||
|
|
||||||
Object.assign(level, {
|
Object.assign(level, {
|
||||||
segment: segments[i],
|
|
||||||
component: part.component,
|
component: part.component,
|
||||||
props: Object.assign({}, props, {
|
props: Object.assign({}, props, {
|
||||||
params: get_params(match)
|
params: get_params(match)
|
||||||
}, preloaded[i + 1])
|
}, preloaded[i + 1])
|
||||||
});
|
});
|
||||||
|
|
||||||
level.props.child = <Props["child"]>{};
|
level.props.child = <Props["child"]>{
|
||||||
|
segment: segments[i + 1]
|
||||||
|
};
|
||||||
level = level.props.child;
|
level = level.props.child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,14 +77,14 @@ function select_route(url: URL): Target {
|
|||||||
|
|
||||||
let current_token: {};
|
let current_token: {};
|
||||||
|
|
||||||
function render(data: any, changed_from: number, scroll: ScrollPosition, token: {}) {
|
function render(data: any, nullable_depth: number, scroll: ScrollPosition, token: {}) {
|
||||||
if (current_token !== token) return;
|
if (current_token !== token) return;
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
// first, clear out highest-level root component
|
// first, clear out highest-level root component
|
||||||
let level = data.child;
|
let level = data.child;
|
||||||
for (let i = 0; i < changed_from; i += 1) {
|
for (let i = 0; i < nullable_depth; i += 1) {
|
||||||
if (i === changed_from) break;
|
if (i === nullable_depth) break;
|
||||||
level = level.props.child;
|
level = level.props.child;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ let root_data: any;
|
|||||||
function prepare_page(target: Target): Promise<{
|
function prepare_page(target: Target): Promise<{
|
||||||
redirect?: Redirect;
|
redirect?: Redirect;
|
||||||
data?: any;
|
data?: any;
|
||||||
changed_from?: number;
|
nullable_depth?: number;
|
||||||
}> {
|
}> {
|
||||||
if (root) {
|
if (root) {
|
||||||
root.set({ preloading: true });
|
root.set({ preloading: true });
|
||||||
@@ -179,6 +179,7 @@ function prepare_page(target: Target): Promise<{
|
|||||||
|
|
||||||
return Promise.all(page.parts.map(async (part, i) => {
|
return Promise.all(page.parts.map(async (part, i) => {
|
||||||
if (i < changed_from) return null;
|
if (i < changed_from) return null;
|
||||||
|
if (!part) return null;
|
||||||
|
|
||||||
const { default: Component } = await part.component();
|
const { default: Component } = await part.component();
|
||||||
const req = {
|
const req = {
|
||||||
@@ -231,33 +232,43 @@ function prepare_page(target: Target): Promise<{
|
|||||||
const data = {
|
const data = {
|
||||||
path,
|
path,
|
||||||
preloading: false,
|
preloading: false,
|
||||||
child: Object.assign({}, root_props.child)
|
child: Object.assign({}, root_props.child, {
|
||||||
|
segment: new_segments[0]
|
||||||
|
})
|
||||||
};
|
};
|
||||||
if (changed(query, root_props.query)) data.query = query;
|
if (changed(query, root_props.query)) data.query = query;
|
||||||
if (changed(params, root_props.params)) data.params = params;
|
if (changed(params, root_props.params)) data.params = params;
|
||||||
|
|
||||||
let level = data.child;
|
let level = data.child;
|
||||||
|
let nullable_depth = 0;
|
||||||
|
|
||||||
for (let i = 0; i < page.parts.length; i += 1) {
|
for (let i = 0; i < page.parts.length; i += 1) {
|
||||||
const part = page.parts[i];
|
const part = page.parts[i];
|
||||||
|
if (!part) continue;
|
||||||
|
|
||||||
const get_params = part.params || (() => ({}));
|
const get_params = part.params || (() => ({}));
|
||||||
|
|
||||||
if (i < changed_from) {
|
if (i < changed_from) {
|
||||||
level.props.path = path;
|
level.props.path = path;
|
||||||
level.props.query = query;
|
level.props.query = query;
|
||||||
level.props.child = Object.assign({}, level.props.child);
|
level.props.child = Object.assign({}, level.props.child);
|
||||||
|
|
||||||
|
nullable_depth += 1;
|
||||||
} else {
|
} else {
|
||||||
level.segment = new_segments[i];
|
|
||||||
level.component = results[i].Component;
|
level.component = results[i].Component;
|
||||||
level.props = Object.assign({}, level.props, props, {
|
level.props = Object.assign({}, level.props, props, {
|
||||||
params: get_params(target.match),
|
params: get_params(target.match),
|
||||||
}, results[i].preloaded);
|
}, results[i].preloaded);
|
||||||
level.props.child = {};
|
|
||||||
|
level.props.child = {
|
||||||
|
segment: new_segments[i + 1]
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
level = level.props.child;
|
level = level.props.child;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { data, changed_from };
|
return { data, nullable_depth };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,12 +293,12 @@ async function navigate(target: Target, id: number): Promise<any> {
|
|||||||
prefetching = null;
|
prefetching = null;
|
||||||
|
|
||||||
const token = current_token = {};
|
const token = current_token = {};
|
||||||
const { redirect, data, changed_from } = await loaded;
|
const { redirect, data, nullable_depth } = await loaded;
|
||||||
|
|
||||||
if (redirect) {
|
if (redirect) {
|
||||||
await goto(redirect.location, { replaceState: true });
|
await goto(redirect.location, { replaceState: true });
|
||||||
} else {
|
} else {
|
||||||
render(data, changed_from, scroll_history[id], token);
|
render(data, nullable_depth, scroll_history[id], token);
|
||||||
document.activeElement.blur();
|
document.activeElement.blur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,7 +364,7 @@ function handle_popstate(event: PopStateEvent) {
|
|||||||
|
|
||||||
let prefetching: {
|
let prefetching: {
|
||||||
href: string;
|
href: string;
|
||||||
promise: Promise<{ redirect?: Redirect, data?: any, changed_from?: number }>;
|
promise: Promise<{ redirect?: Redirect, data?: any, nullable_depth?: number }>;
|
||||||
} = null;
|
} = null;
|
||||||
|
|
||||||
export function prefetch(href: string) {
|
export function prefetch(href: string) {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
<span>x: {segment} {count}</span>
|
|
||||||
<svelte:component this={child.component} {...child.props}/>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import counts from './_counts.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
preload() {
|
|
||||||
return {
|
|
||||||
count: counts.x += 1
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
oncreate() {
|
|
||||||
this.set({
|
|
||||||
segment: this.get().params.x
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -629,7 +629,6 @@ function run({ mode, basepath = '' }) {
|
|||||||
.evaluate(() => document.querySelector('#sapper').textContent)
|
.evaluate(() => document.querySelector('#sapper').textContent)
|
||||||
.then(text => {
|
.then(text => {
|
||||||
assert.deepEqual(text.split('\n').filter(Boolean), [
|
assert.deepEqual(text.split('\n').filter(Boolean), [
|
||||||
'x: foo 1',
|
|
||||||
'y: bar 1',
|
'y: bar 1',
|
||||||
'z: baz 1'
|
'z: baz 1'
|
||||||
]);
|
]);
|
||||||
@@ -642,7 +641,6 @@ function run({ mode, basepath = '' }) {
|
|||||||
})
|
})
|
||||||
.then(text => {
|
.then(text => {
|
||||||
assert.deepEqual(text.split('\n').filter(Boolean), [
|
assert.deepEqual(text.split('\n').filter(Boolean), [
|
||||||
'x: foo 1',
|
|
||||||
'y: bar 1',
|
'y: bar 1',
|
||||||
'z: qux 2'
|
'z: qux 2'
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -2,13 +2,6 @@ const path = require('path');
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const { create_routes } = require('../../../dist/core.ts.js');
|
const { create_routes } = require('../../../dist/core.ts.js');
|
||||||
|
|
||||||
|
|
||||||
const _default_layout = {
|
|
||||||
default: true,
|
|
||||||
name: '_default_layout',
|
|
||||||
file: null
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('create_routes', () => {
|
describe('create_routes', () => {
|
||||||
it('creates routes', () => {
|
it('creates routes', () => {
|
||||||
const { components, pages, server_routes } = create_routes(path.join(__dirname, 'samples/basic'));
|
const { components, pages, server_routes } = create_routes(path.join(__dirname, 'samples/basic'));
|
||||||
@@ -21,7 +14,6 @@ describe('create_routes', () => {
|
|||||||
assert.deepEqual(components, [
|
assert.deepEqual(components, [
|
||||||
index,
|
index,
|
||||||
about,
|
about,
|
||||||
_default_layout,
|
|
||||||
blog,
|
blog,
|
||||||
blog_$slug
|
blog_$slug
|
||||||
]);
|
]);
|
||||||
@@ -44,7 +36,7 @@ describe('create_routes', () => {
|
|||||||
{
|
{
|
||||||
pattern: /^\/blog\/?$/,
|
pattern: /^\/blog\/?$/,
|
||||||
parts: [
|
parts: [
|
||||||
{ component: _default_layout, params: [] },
|
null,
|
||||||
{ component: blog, params: [] }
|
{ component: blog, params: [] }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -52,7 +44,7 @@ describe('create_routes', () => {
|
|||||||
{
|
{
|
||||||
pattern: /^\/blog\/([^\/]+?)\/?$/,
|
pattern: /^\/blog\/([^\/]+?)\/?$/,
|
||||||
parts: [
|
parts: [
|
||||||
{ component: _default_layout, params: [] },
|
null,
|
||||||
{ component: blog_$slug, params: ['slug'] }
|
{ component: blog_$slug, params: ['slug'] }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -110,15 +102,15 @@ describe('create_routes', () => {
|
|||||||
it('sorts routes correctly', () => {
|
it('sorts routes correctly', () => {
|
||||||
const { pages } = create_routes(path.join(__dirname, 'samples/sorting'));
|
const { pages } = create_routes(path.join(__dirname, 'samples/sorting'));
|
||||||
|
|
||||||
assert.deepEqual(pages.map(p => p.parts.map(part => part.component.file)), [
|
assert.deepEqual(pages.map(p => p.parts.map(part => part && part.component.file)), [
|
||||||
['index.html'],
|
['index.html'],
|
||||||
['about.html'],
|
['about.html'],
|
||||||
[_default_layout.file, 'post/index.html'],
|
[null, 'post/index.html'],
|
||||||
[_default_layout.file, 'post/bar.html'],
|
[null, 'post/bar.html'],
|
||||||
[_default_layout.file, 'post/foo.html'],
|
[null, 'post/foo.html'],
|
||||||
[_default_layout.file, 'post/f[xx].html'],
|
[null, 'post/f[xx].html'],
|
||||||
[_default_layout.file, 'post/[id([0-9-a-z]{3,})].html'],
|
[null, 'post/[id([0-9-a-z]{3,})].html'],
|
||||||
[_default_layout.file, 'post/[id].html'],
|
[null, 'post/[id].html'],
|
||||||
['[wildcard].html']
|
['[wildcard].html']
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user