This commit is contained in:
Rich Harris
2018-12-20 23:16:03 -05:00
parent 02cef046aa
commit 82a023c302
8 changed files with 59 additions and 58 deletions

34
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "sapper", "name": "sapper",
"version": "0.24.0", "version": "0.24.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -1805,7 +1805,7 @@
}, },
"es6-promisify": { "es6-promisify": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
"dev": true, "dev": true,
"requires": { "requires": {
@@ -2362,14 +2362,12 @@
"balanced-match": { "balanced-match": {
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@@ -2384,20 +2382,17 @@
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"console-control-strings": { "console-control-strings": {
"version": "1.1.0", "version": "1.1.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"core-util-is": { "core-util-is": {
"version": "1.0.2", "version": "1.0.2",
@@ -2514,8 +2509,7 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"ini": { "ini": {
"version": "1.3.5", "version": "1.3.5",
@@ -2527,7 +2521,6 @@
"version": "1.0.0", "version": "1.0.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
@@ -2542,7 +2535,6 @@
"version": "3.0.4", "version": "3.0.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@@ -2550,14 +2542,12 @@
"minimist": { "minimist": {
"version": "0.0.8", "version": "0.0.8",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"minipass": { "minipass": {
"version": "2.2.4", "version": "2.2.4",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.1", "safe-buffer": "^5.1.1",
"yallist": "^3.0.0" "yallist": "^3.0.0"
@@ -2576,7 +2566,6 @@
"version": "0.5.1", "version": "0.5.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "0.0.8"
} }
@@ -2657,8 +2646,7 @@
"number-is-nan": { "number-is-nan": {
"version": "1.0.1", "version": "1.0.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true
"optional": true
}, },
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
@@ -2670,7 +2658,6 @@
"version": "1.4.0", "version": "1.4.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
} }
@@ -2792,7 +2779,6 @@
"version": "1.0.2", "version": "1.0.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"code-point-at": "^1.0.0", "code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0", "is-fullwidth-code-point": "^1.0.0",
@@ -4400,7 +4386,7 @@
}, },
"pify": { "pify": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true "dev": true
}, },

View File

@@ -129,6 +129,7 @@ prog.command('dev')
}); });
} catch (err) { } catch (err) {
console.log(colors.bold.red(`> ${err.message}`)); console.log(colors.bold.red(`> ${err.message}`));
console.log(colors.gray(err.stack));
process.exit(1); process.exit(1);
} }
}); });
@@ -171,6 +172,7 @@ prog.command('build [dest]')
console.error(`\n> Finished in ${elapsed(start)}. Type ${colors.bold.cyan(`node ${dest}`)} to run the app.`); console.error(`\n> Finished in ${elapsed(start)}. Type ${colors.bold.cyan(`node ${dest}`)} to run the app.`);
} catch (err) { } catch (err) {
console.log(`${colors.bold.red(`> ${err.message}`)}`); console.log(`${colors.bold.red(`> ${err.message}`)}`);
console.log(colors.gray(err.stack));
process.exit(1); process.exit(1);
} }
}); });

View File

@@ -47,7 +47,8 @@ export default class RollupResult implements CompileResult {
} else { } else {
for (const name in compiler.input) { for (const name in compiler.input) {
const file = compiler.input[name]; const file = compiler.input[name];
this.assets[name] = compiler.chunks.find(chunk => file in chunk.modules).fileName; const chunk = compiler.chunks.find(chunk => file in chunk.modules);
if (chunk) this.assets[name] = chunk.fileName;
} }
} }

View File

@@ -161,6 +161,8 @@ export default function extract_css(client_result: CompileResult, components: Pa
// recursively find the chunks this component depends on // recursively find the chunks this component depends on
entry_chunk_dependencies.forEach(chunk => { entry_chunk_dependencies.forEach(chunk => {
if (!chunk) return; // TODO why does this happen?
chunk.imports.forEach(file => { chunk.imports.forEach(file => {
entry_chunk_dependencies.add(lookup.get(file)); entry_chunk_dependencies.add(lookup.get(file));
}); });
@@ -182,7 +184,8 @@ export default function extract_css(client_result: CompileResult, components: Pa
if (!chunk) { if (!chunk) {
// this should never happen! // this should never happen!
throw new Error(`Could not find chunk that owns ${component.file}`); return;
// throw new Error(`Could not find chunk that owns ${component.file}`);
} }
const chunk_dependencies: Set<Chunk> = new Set([chunk]); const chunk_dependencies: Set<Chunk> = new Set([chunk]);
@@ -190,6 +193,8 @@ export default function extract_css(client_result: CompileResult, components: Pa
// recursively find the chunks this component depends on // recursively find the chunks this component depends on
chunk_dependencies.forEach(chunk => { chunk_dependencies.forEach(chunk => {
if (!chunk) return; // TODO why does this happen?
chunk.imports.forEach(file => { chunk.imports.forEach(file => {
chunk_dependencies.add(lookup.get(file)); chunk_dependencies.add(lookup.get(file));
}); });

View File

@@ -151,11 +151,11 @@ function generate_client(
} }
return `// This file is generated by Sapper — do not edit it!\n` + template return `// This file is generated by Sapper — do not edit it!\n` + template
.replace('__ROOT__', stringify(get_file(path_to_routes, manifest_data.root), false)) .replace(/__ROOT__/g, stringify(get_file(path_to_routes, manifest_data.root), false))
.replace('__ERROR__', stringify(posixify(`${path_to_routes}/_error.html`), false)) .replace(/__ERROR__/g, stringify(posixify(`${path_to_routes}/_error.html`), false))
.replace('__IGNORE__', `[${server_routes_to_ignore.map(route => route.pattern).join(', ')}]`) .replace(/__IGNORE__/g, `[${server_routes_to_ignore.map(route => route.pattern).join(', ')}]`)
.replace('__COMPONENTS__', components) .replace(/__COMPONENTS__/g, components)
.replace('__PAGES__', pages) + .replace(/__PAGES__/g, pages) +
footer; footer;
} }
@@ -174,8 +174,8 @@ function generate_server(
manifest_data.server_routes.map(route => manifest_data.server_routes.map(route =>
`import * as __${route.name} from ${stringify(posixify(`${path_to_routes}/${route.file}`))};`), `import * as __${route.name} from ${stringify(posixify(`${path_to_routes}/${route.file}`))};`),
manifest_data.components.map(component => manifest_data.components.map(component =>
`import __${component.name} from ${stringify(get_file(path_to_routes, component))};`), `import __${component.name}, * as __${component.name}_static from ${stringify(get_file(path_to_routes, component))};`),
`import root from ${stringify(get_file(path_to_routes, manifest_data.root))};`, `import root, * as root_static from ${stringify(get_file(path_to_routes, manifest_data.root))};`,
`import error from ${stringify(posixify(`${path_to_routes}/_error.html`))};` `import error from ${stringify(posixify(`${path_to_routes}/_error.html`))};`
); );
@@ -207,7 +207,8 @@ function generate_server(
const props = [ const props = [
`name: "${part.component.name}"`, `name: "${part.component.name}"`,
`file: ${stringify(part.component.file)}`, `file: ${stringify(part.component.file)}`,
`component: __${part.component.name}` `component: __${part.component.name}`,
`preload: __${part.component.name}_static.preload`
]; ];
if (part.params.length > 0) { if (part.params.length > 0) {
@@ -222,6 +223,7 @@ function generate_server(
], ],
root, root,
root_preload: root_static['pre' + 'load'],
error error
};`.replace(/^\t\t/gm, '').trim(); };`.replace(/^\t\t/gm, '').trim();

View File

@@ -1,4 +1,4 @@
import RootComponent from '__ROOT__'; import RootComponent, * as RootComponentStatic from '__ROOT__';
import ErrorComponent from '__ERROR__'; import ErrorComponent from '__ERROR__';
import { import {
Target, Target,
@@ -127,7 +127,7 @@ export function navigate(target: Target, id: number, noscroll?: boolean, hash?:
cid = id; cid = id;
if (root_component) { if (root_component) {
root_component.set({ preloading: true }); root_component.$set({ preloading: true });
} }
const loaded = prefetching && prefetching.href === target.url.href ? const loaded = prefetching && prefetching.href === target.url.href ?
prefetching.promise : prefetching.promise :
@@ -146,12 +146,12 @@ export function navigate(target: Target, id: number, noscroll?: boolean, hash?:
}); });
} }
function render(data: any, nullable_depth: number, scroll: ScrollPosition, noscroll: boolean, hash: string, token: {}) { function render(props: any, nullable_depth: number, scroll: ScrollPosition, noscroll: boolean, hash: string, token: {}) {
if (current_token !== token) return; if (current_token !== token) return;
if (root_component) { if (root_component) {
// first, clear out highest-level root component // first, clear out highest-level root component
let level = data.child; let level = props.child;
for (let i = 0; i < nullable_depth; i += 1) { for (let i = 0; i < nullable_depth; i += 1) {
if (i === nullable_depth) break; if (i === nullable_depth) break;
level = level.props.child; level = level.props.child;
@@ -159,11 +159,12 @@ function render(data: any, nullable_depth: number, scroll: ScrollPosition, noscr
const { component } = level; const { component } = level;
level.component = null; level.component = null;
root_component.set({ child: data.child }); root_component.$set({ child: props.child });
// then render new stuff // then render new stuff
// TODO do we need to call `flush` before doing this?
level.component = component; level.component = component;
root_component.set(data); root_component.$set(props);
} else { } else {
// first load — remove SSR'd <head> contents // first load — remove SSR'd <head> contents
const start = document.querySelector('#sapper-head-start'); const start = document.querySelector('#sapper-head-start');
@@ -175,11 +176,11 @@ function render(data: any, nullable_depth: number, scroll: ScrollPosition, noscr
detach(end); detach(end);
} }
Object.assign(data, root_data); Object.assign(props, root_data);
root_component = new RootComponent({ root_component = new RootComponent({
target, target,
data, props,
store, store,
hydrate: true hydrate: true
}); });
@@ -201,7 +202,7 @@ function render(data: any, nullable_depth: number, scroll: ScrollPosition, noscr
if (scroll) scrollTo(scroll.x, scroll.y); if (scroll) scrollTo(scroll.x, scroll.y);
} }
Object.assign(root_props, data); Object.assign(root_props, props);
ready = true; ready = true;
} }
@@ -238,8 +239,9 @@ export function prepare_page(target: Target): Promise<{
}; };
if (!root_preload) { if (!root_preload) {
root_preload = RootComponent.preload const preload_fn = RootComponentStatic['pre' + 'load']; // Rollup makes us jump through these hoops :(
? initial_data.preloaded[0] || RootComponent.preload.call(preload_context, { root_preload = preload_fn
? initial_data.preloaded[0] || preload_fn.call(preload_context, {
path, path,
query, query,
params: {} params: {}
@@ -251,7 +253,7 @@ export function prepare_page(target: Target): Promise<{
if (i < changed_from) return null; if (i < changed_from) return null;
if (!part) return null; if (!part) return null;
return load_component(components[part.i]).then(Component => { return load_component(components[part.i]).then(({ default: Component, preload }) => {
const req = { const req = {
path, path,
query, query,
@@ -260,8 +262,8 @@ export function prepare_page(target: Target): Promise<{
let preloaded; let preloaded;
if (ready || !initial_data.preloaded[i + 1]) { if (ready || !initial_data.preloaded[i + 1]) {
preloaded = Component.preload preloaded = preload
? Component.preload.call(preload_context, req) ? preload.call(preload_context, req)
: {}; : {};
} else { } else {
preloaded = initial_data.preloaded[i + 1]; preloaded = initial_data.preloaded[i + 1];
@@ -303,7 +305,7 @@ export function prepare_page(target: Target): Promise<{
}; };
return { return {
data: Object.assign({}, props, { props: Object.assign({}, props, {
preloading: false, preloading: false,
child: { child: {
component: ErrorComponent, component: ErrorComponent,
@@ -372,12 +374,15 @@ function load_css(chunk: string) {
}); });
} }
export function load_component(component: ComponentLoader): Promise<ComponentConstructor> { export function load_component(component: ComponentLoader): Promise<{
default: ComponentConstructor,
preload?: (input: any) => any
}> {
// TODO this is temporary — once placeholders are // TODO this is temporary — once placeholders are
// always rewritten, scratch the ternary // always rewritten, scratch the ternary
const promises: Array<Promise<any>> = (typeof component.css === 'string' ? [] : component.css.map(load_css)); const promises: Array<Promise<any>> = (typeof component.css === 'string' ? [] : component.css.map(load_css));
promises.unshift(component.js()); promises.unshift(component.js());
return Promise.all(promises).then(values => values[0].default); return Promise.all(promises).then(values => values[0]);
} }
function detach(node: Node) { function detach(node: Node) {

View File

@@ -16,13 +16,13 @@ export type RootProps = {
}; };
export interface ComponentConstructor { export interface ComponentConstructor {
new (options: { target: Node, data: any, store: Store, hydrate: boolean }): Component; new (options: { target: Node, props: any, store: Store, hydrate: boolean }): Component;
preload: (props: { params: Params, query: Query }) => Promise<any>; preload: (props: { params: Params, query: Query }) => Promise<any>;
}; };
export interface Component { export interface Component {
set: (data: any) => void; $set: (data: any) => void;
destroy: () => void; $destroy: () => void;
} }
export type ComponentLoader = { export type ComponentLoader = {

View File

@@ -135,8 +135,8 @@ export function get_page_handler(
let match; let match;
try { try {
const root_preloaded = manifest.root.preload const root_preloaded = manifest.root_preload
? manifest.root.preload.call(preload_context, { ? manifest.root_preload.call(preload_context, {
path: req.path, path: req.path,
query: req.query, query: req.query,
params: {} params: {}
@@ -148,8 +148,8 @@ export function get_page_handler(
preloaded = await Promise.all([root_preloaded].concat(page.parts.map(part => { preloaded = await Promise.all([root_preloaded].concat(page.parts.map(part => {
if (!part) return null; if (!part) return null;
return part.component.preload return part.preload
? part.component.preload.call(preload_context, { ? part.preload.call(preload_context, {
path: req.path, path: req.path,
query: req.query, query: req.query,
params: part.params ? part.params(match) : {} params: part.params ? part.params(match) : {}