golf things down a bit

This commit is contained in:
Rich Harris
2018-09-30 18:11:40 -04:00
parent a43764a971
commit 8530d06d00
7 changed files with 87 additions and 76 deletions

View File

@@ -35,7 +35,7 @@ function template(kind, external) {
} }
export default [ export default [
template('client', []), template('client', ['__ROOT__', '__ERROR__']),
template('server', builtinModules), template('server', builtinModules),
{ {

View File

@@ -75,62 +75,57 @@ function generate_client(
const component_indexes: Record<string, number> = {}; const component_indexes: Record<string, number> = {};
let code = ` const components = `[
import root from ${stringify(get_file(path_to_routes, manifest_data.root))}; ${manifest_data.components.map((component, i) => {
import error from ${stringify(posixify(`${path_to_routes}/_error.html`))}; const annotation = bundler === 'webpack'
? `/* webpackChunkName: "${component.name}" */ `
: '';
const d = decodeURIComponent; const source = get_file(path_to_routes, component);
const components = [ component_indexes[component.name] = i;
${manifest_data.components.map((component, i) => {
const annotation = bundler === 'webpack'
? `/* webpackChunkName: "${component.name}" */ `
: '';
const source = get_file(path_to_routes, component); return `{
js: () => import(${annotation}${stringify(source)}),
css: "__SAPPER_CSS_PLACEHOLDER:${stringify(component.file, false)}__"
}`;
}).join(',\n\t\t')}
]`.replace(/^\t/gm, '').trim();
component_indexes[component.name] = i; let needs_decode = false;
return `{ let pages = `[
js: () => import(${annotation}${stringify(source)}), ${manifest_data.pages.map(page => `{
css: "__SAPPER_CSS_PLACEHOLDER:${stringify(component.file, false)}__" // ${page.parts[page.parts.length - 1].component.file}
}`; pattern: ${page.pattern},
}).join(',\n\t\t\t')} parts: [
]; ${page.parts.map(part => {
if (part === null) return 'null';
const manifest = { if (part.params.length > 0) {
ignore: [${server_routes_to_ignore.map(route => route.pattern).join(', ')}], needs_decode = true;
const props = part.params.map((param, i) => `${param}: d(match[${i + 1}])`);
return `{ i: ${component_indexes[part.component.name]}, params: match => ({ ${props.join(', ')} }) }`;
}
pages: [ return `{ i: ${component_indexes[part.component.name]} }`;
${manifest_data.pages.map(page => `{ }).join(',\n\t\t\t\t')}
// ${page.parts[page.parts.length - 1].component.file} ]
pattern: ${page.pattern}, }`).join(',\n\n\t\t')}
parts: [ ]`.replace(/^\t/gm, '').trim();
${page.parts.map(part => {
if (part === null) return 'null';
if (part.params.length > 0) { if (needs_decode) {
const props = part.params.map((param, i) => `${param}: d(match[${i + 1}])`); pages = `(d => ${pages})(decodeURIComponent)`
return `{ component: components[${component_indexes[part.component.name]}], params: match => ({ ${props.join(', ')} }) }`; }
}
return `{ component: components[${component_indexes[part.component.name]}] }`; let footer = '';
}).join(',\n\t\t\t\t\t\t')}
]
}`).join(',\n\n\t\t\t\t')}
],
root,
error
};`.replace(/^\t\t/gm, '').trim();
if (dev()) { if (dev()) {
const sapper_dev_client = posixify( const sapper_dev_client = posixify(
path.resolve(__dirname, '../sapper-dev-client.js') path.resolve(__dirname, '../sapper-dev-client.js')
); );
code += ` footer = `
import(${stringify(sapper_dev_client)}).then(client => { import(${stringify(sapper_dev_client)}).then(client => {
client.connect(${dev_port}); client.connect(${dev_port});
@@ -138,7 +133,12 @@ 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(/const manifest = __MANIFEST__;/, code); .replace('__ROOT__', stringify(get_file(path_to_routes, manifest_data.root), false))
.replace('__ERROR__', stringify(posixify(`${path_to_routes}/_error.html`), false))
.replace('__IGNORE__', `[${server_routes_to_ignore.map(route => route.pattern).join(', ')}]`)
.replace('__COMPONENTS__', components)
.replace('__PAGES__', pages) +
footer;
} }
function generate_server( function generate_server(

View File

@@ -1,7 +1,20 @@
import { Manifest, Target, ScrollPosition, Component, Redirect, ComponentLoader, ComponentConstructor, RootProps } from './types'; import RootComponent from '__ROOT__';
import ErrorComponent from '__ERROR__';
import {
Target,
ScrollPosition,
Component,
Redirect,
ComponentLoader,
ComponentConstructor,
RootProps,
Page
} from './types';
import goto from './goto'; import goto from './goto';
export const manifest: Manifest = __MANIFEST__; const ignore = __IGNORE__;
export const components: ComponentLoader[] = __COMPONENTS__;
export const pages: Page[] = __PAGES__;
let ready = false; let ready = false;
let root_component: Component; let root_component: Component;
@@ -61,16 +74,16 @@ export { _history as history };
export const scroll_history: Record<string, ScrollPosition> = {}; export const scroll_history: Record<string, ScrollPosition> = {};
export function select_route(url: URL): Target { export function select_route(url: URL): Target {
if (url.origin !== window.location.origin) return null; if (url.origin !== location.origin) return null;
if (!url.pathname.startsWith(initial_data.baseUrl)) return null; if (!url.pathname.startsWith(initial_data.baseUrl)) return null;
const path = url.pathname.slice(initial_data.baseUrl.length); const path = url.pathname.slice(initial_data.baseUrl.length);
// avoid accidental clashes between server routes and pages // avoid accidental clashes between server routes and pages
if (manifest.ignore.some(pattern => pattern.test(path))) return; if (ignore.some(pattern => pattern.test(path))) return;
for (let i = 0; i < manifest.pages.length; i += 1) { for (let i = 0; i < pages.length; i += 1) {
const page = manifest.pages[i]; const page = pages[i];
const match = page.pattern.exec(path); const match = page.pattern.exec(path);
if (match) { if (match) {
@@ -88,8 +101,8 @@ export function select_route(url: URL): Target {
export function scroll_state() { export function scroll_state() {
return { return {
x: window.scrollX, x: scrollX,
y: window.scrollY y: scrollY
}; };
} }
@@ -159,7 +172,7 @@ function render(data: any, nullable_depth: number, scroll: ScrollPosition, token
Object.assign(data, root_data); Object.assign(data, root_data);
root_component = new manifest.root({ root_component = new RootComponent({
target, target,
data, data,
store, store,
@@ -168,7 +181,7 @@ function render(data: any, nullable_depth: number, scroll: ScrollPosition, token
} }
if (scroll) { if (scroll) {
window.scrollTo(scroll.x, scroll.y); scrollTo(scroll.x, scroll.y);
} }
Object.assign(root_props, data); Object.assign(root_props, data);
@@ -195,7 +208,7 @@ export function prepare_page(target: Target): Promise<{
const preload_context = { const preload_context = {
store, store,
fetch: (url: string, opts?: any) => window.fetch(url, opts), fetch: (url: string, opts?: any) => fetch(url, opts),
redirect: (statusCode: number, location: string) => { redirect: (statusCode: number, location: string) => {
if (redirect && (redirect.statusCode !== statusCode || redirect.location !== location)) { if (redirect && (redirect.statusCode !== statusCode || redirect.location !== location)) {
throw new Error(`Conflicting redirects`); throw new Error(`Conflicting redirects`);
@@ -208,8 +221,8 @@ export function prepare_page(target: Target): Promise<{
}; };
if (!root_preload) { if (!root_preload) {
root_preload = manifest.root.preload root_preload = RootComponent.preload
? initial_data.preloaded[0] || manifest.root.preload.call(preload_context, { ? initial_data.preloaded[0] || RootComponent.preload.call(preload_context, {
path, path,
query, query,
params: {} params: {}
@@ -221,7 +234,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(part.component).then(Component => { return load_component(components[part.i]).then(Component => {
const req = { const req = {
path, path,
query, query,
@@ -276,7 +289,7 @@ export function prepare_page(target: Target): Promise<{
data: Object.assign({}, props, { data: Object.assign({}, props, {
preloading: false, preloading: false,
child: { child: {
component: manifest.error, component: ErrorComponent,
props props
} }
}) })

View File

@@ -8,7 +8,7 @@ export default function goto(href: string, opts = { replaceState: false }) {
promise = navigate(target, null).then(() => {}); promise = navigate(target, null).then(() => {});
if (history) history[opts.replaceState ? 'replaceState' : 'pushState']({ id: cid }, '', href); if (history) history[opts.replaceState ? 'replaceState' : 'pushState']({ id: cid }, '', href);
} else { } else {
window.location.href = href; location.href = href;
promise = new Promise(f => {}); // never resolves promise = new Promise(f => {}); // never resolves
} }

View File

@@ -1,14 +1,12 @@
import { manifest, load_component } from "../app"; import { components, pages, load_component } from "../app";
export default function prefetchRoutes(pathnames: string[]) { export default function prefetchRoutes(pathnames: string[]) {
if (!manifest) throw new Error(`You must call init() first`); return pages
return manifest.pages
.filter(route => { .filter(route => {
if (!pathnames) return true; if (!pathnames) return true;
return pathnames.some(pathname => route.pattern.test(pathname)); return pathnames.some(pathname => route.pattern.test(pathname));
}) })
.reduce((promise: Promise<any>, route) => promise.then(() => { .reduce((promise: Promise<any>, route) => promise.then(() => {
return Promise.all(route.parts.map(part => part && load_component(part.component))); return Promise.all(route.parts.map(part => part && load_component(components[part.i])));
}), Promise.resolve()); }), Promise.resolve());
} }

View File

@@ -26,15 +26,15 @@ export default function start(opts: {
set_target(opts.target); set_target(opts.target);
if (opts.store) set_store(opts.store); if (opts.store) set_store(opts.store);
window.addEventListener('click', handle_click); addEventListener('click', handle_click);
window.addEventListener('popstate', handle_popstate); addEventListener('popstate', handle_popstate);
// prefetch // prefetch
window.addEventListener('touchstart', trigger_prefetch); addEventListener('touchstart', trigger_prefetch);
window.addEventListener('mousemove', handle_mousemove); addEventListener('mousemove', handle_mousemove);
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
const { hash, href } = window.location; const { hash, href } = location;
const deep_linked = hash && document.getElementById(hash.slice(1)); const deep_linked = hash && document.getElementById(hash.slice(1));
scroll_history[uid] = deep_linked ? scroll_history[uid] = deep_linked ?
@@ -44,7 +44,7 @@ export default function start(opts: {
history.replaceState({ id: uid }, '', href); history.replaceState({ id: uid }, '', href);
if (!initial_data.error) { if (!initial_data.error) {
const target = select_route(new URL(window.location.href)); const target = select_route(new URL(location.href));
if (target) return navigate(target, uid); if (target) return navigate(target, uid);
} }
}); });
@@ -83,7 +83,7 @@ function handle_click(event: MouseEvent) {
const svg = typeof a.href === 'object' && a.href.constructor.name === 'SVGAnimatedString'; const svg = typeof a.href === 'object' && a.href.constructor.name === 'SVGAnimatedString';
const href = String(svg ? (<SVGAElement>a).href.baseVal : a.href); const href = String(svg ? (<SVGAElement>a).href.baseVal : a.href);
if (href === window.location.href) { if (href === location.href) {
event.preventDefault(); event.preventDefault();
return; return;
} }
@@ -99,7 +99,7 @@ function handle_click(event: MouseEvent) {
const url = new URL(href); const url = new URL(href);
// Don't handle hash changes // Don't handle hash changes
if (url.pathname === window.location.pathname && url.search === window.location.search) return; if (url.pathname === location.pathname && url.search === location.search) return;
const target = select_route(url); const target = select_route(url);
if (target) { if (target) {
@@ -122,17 +122,17 @@ function handle_popstate(event: PopStateEvent) {
scroll_history[cid] = scroll_state(); scroll_history[cid] = scroll_state();
if (event.state) { if (event.state) {
const url = new URL(window.location.href); const url = new URL(location.href);
const target = select_route(url); const target = select_route(url);
if (target) { if (target) {
navigate(target, event.state.id); navigate(target, event.state.id);
} else { } else {
window.location.href = window.location.href; location.href = location.href;
} }
} else { } else {
// hashchange // hashchange
set_uid(uid + 1); set_uid(uid + 1);
set_cid(uid); set_cid(uid);
history.replaceState({ id: cid }, '', window.location.href); history.replaceState({ id: cid }, '', location.href);
} }
} }

View File

@@ -33,7 +33,7 @@ export type ComponentLoader = {
export type Page = { export type Page = {
pattern: RegExp; pattern: RegExp;
parts: Array<{ parts: Array<{
component: ComponentLoader; i: number;
params?: (match: RegExpExecArray) => Record<string, string>; params?: (match: RegExpExecArray) => Record<string, string>;
}>; }>;
}; };