mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-23 15:41:32 +00:00
first crack at context-driven store
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -12,4 +12,5 @@ sapper
|
|||||||
runtime.js
|
runtime.js
|
||||||
dist
|
dist
|
||||||
!rollup.config.js
|
!rollup.config.js
|
||||||
templates/*.mjs
|
templates/app.mjs
|
||||||
|
templates/server.mjs
|
||||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -5713,9 +5713,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"svelte": {
|
"svelte": {
|
||||||
"version": "3.0.0-alpha25",
|
"version": "3.0.0-alpha26",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.0.0-alpha25.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.0.0-alpha26.tgz",
|
||||||
"integrity": "sha512-Qbziusyrhy2eeonijfpgq1s0CyzsMrwU5hz3+o+bASFjk5kKFCmKRGYLWRU5JnYFTphZyLw4jPdbBKWFDOMmng==",
|
"integrity": "sha512-8++B3/arwhWggcvBYZnGDd9xKsYchqw4Os2haA38v6BSIcSSY706puNK01+PAdVvLCUJH/R4U6T8uV/fhjwVVw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"svelte-dev-helper": {
|
"svelte-dev-helper": {
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
"config",
|
"config",
|
||||||
"sapper",
|
"sapper",
|
||||||
"dist/*.js",
|
"dist/*.js",
|
||||||
"templates/*.js"
|
"templates/*.js",
|
||||||
|
"templates/*.html"
|
||||||
],
|
],
|
||||||
"directories": {
|
"directories": {
|
||||||
"test": "test"
|
"test": "test"
|
||||||
@@ -58,7 +59,7 @@
|
|||||||
"sade": "^1.4.1",
|
"sade": "^1.4.1",
|
||||||
"sander": "^0.6.0",
|
"sander": "^0.6.0",
|
||||||
"sirv": "^0.2.2",
|
"sirv": "^0.2.2",
|
||||||
"svelte": "^3.0.0-alpha25",
|
"svelte": "^3.0.0-alpha26",
|
||||||
"svelte-loader": "^2.11.0",
|
"svelte-loader": "^2.11.0",
|
||||||
"ts-node": "^8.0.2",
|
"ts-node": "^8.0.2",
|
||||||
"typescript": "^3.1.3",
|
"typescript": "^3.1.3",
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ function template(kind, external, target) {
|
|||||||
input: `templates/src/${kind}/index.ts`,
|
input: `templates/src/${kind}/index.ts`,
|
||||||
output: {
|
output: {
|
||||||
file: `templates/${kind}.mjs`,
|
file: `templates/${kind}.mjs`,
|
||||||
format: 'es'
|
format: 'es',
|
||||||
|
paths: id => id.replace('@sapper', '.')
|
||||||
},
|
},
|
||||||
external,
|
external,
|
||||||
plugins: [
|
plugins: [
|
||||||
@@ -35,7 +36,7 @@ function template(kind, external, target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
template('client', ['__ROOT__', '__ERROR__'], 'ES2017'),
|
template('app', ['__ROOT__', '__ERROR__', 'svelte', '@sapper/App.html'], 'ES2017'),
|
||||||
template('server', builtinModules, 'ES2015'),
|
template('server', builtinModules, 'ES2015'),
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export async function build({
|
|||||||
|
|
||||||
const manifest_data = create_manifest_data(routes);
|
const manifest_data = create_manifest_data(routes);
|
||||||
|
|
||||||
// create src/manifest/client.js and src/manifest/server.js
|
// create src/node_modules/@sapper/app.mjs and server.mjs
|
||||||
create_main_manifests({
|
create_main_manifests({
|
||||||
bundler,
|
bundler,
|
||||||
manifest_data,
|
manifest_data,
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class Watcher extends EventEmitter {
|
|||||||
cwd = '.',
|
cwd = '.',
|
||||||
src = 'src',
|
src = 'src',
|
||||||
routes = 'src/routes',
|
routes = 'src/routes',
|
||||||
output = '__sapper__',
|
output = 'src/node_modules/@sapper',
|
||||||
static: static_files = 'static',
|
static: static_files = 'static',
|
||||||
dest = '__sapper__/dev',
|
dest = '__sapper__/dev',
|
||||||
'dev-port': dev_port,
|
'dev-port': dev_port,
|
||||||
@@ -144,6 +144,10 @@ class Watcher extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { cwd, src, dest, routes, output, static: static_files } = this.dirs;
|
const { cwd, src, dest, routes, output, static: static_files } = this.dirs;
|
||||||
|
|
||||||
|
rimraf.sync(path.join(output, '**/*'));
|
||||||
|
mkdirp.sync(output);
|
||||||
|
|
||||||
rimraf.sync(dest);
|
rimraf.sync(dest);
|
||||||
mkdirp.sync(`${dest}/client`);
|
mkdirp.sync(`${dest}/client`);
|
||||||
if (this.bundler === 'rollup') copy_shimport(dest);
|
if (this.bundler === 'rollup') copy_shimport(dest);
|
||||||
|
|||||||
36
src/cli.ts
36
src/cli.ts
@@ -10,7 +10,7 @@ const prog = sade('sapper').version(pkg.version);
|
|||||||
|
|
||||||
if (process.argv[2] === 'start') {
|
if (process.argv[2] === 'start') {
|
||||||
// remove this in a future version
|
// remove this in a future version
|
||||||
console.error(colors.bold.red(`'sapper start' has been removed`));
|
console.error(colors.bold().red(`'sapper start' has been removed`));
|
||||||
console.error(`Use 'node [build_dir]' instead`);
|
console.error(`Use 'node [build_dir]' instead`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ prog.command('dev')
|
|||||||
|
|
||||||
watcher.on('ready', async (event: ReadyEvent) => {
|
watcher.on('ready', async (event: ReadyEvent) => {
|
||||||
if (first) {
|
if (first) {
|
||||||
console.log(colors.bold.cyan(`> Listening on http://localhost:${event.port}`));
|
console.log(colors.bold().cyan(`> Listening on http://localhost:${event.port}`));
|
||||||
if (opts.open) {
|
if (opts.open) {
|
||||||
const { exec } = await import('child_process');
|
const { exec } = await import('child_process');
|
||||||
exec(`open http://localhost:${event.port}`);
|
exec(`open http://localhost:${event.port}`);
|
||||||
@@ -85,7 +85,7 @@ prog.command('dev')
|
|||||||
|
|
||||||
watcher.on('invalid', (event: InvalidEvent) => {
|
watcher.on('invalid', (event: InvalidEvent) => {
|
||||||
const changed = event.changed.map(filename => path.relative(process.cwd(), filename)).join(', ');
|
const changed = event.changed.map(filename => path.relative(process.cwd(), filename)).join(', ');
|
||||||
console.log(`\n${colors.bold.cyan(changed)} changed. rebuilding...`);
|
console.log(`\n${colors.bold().cyan(changed)} changed. rebuilding...`);
|
||||||
});
|
});
|
||||||
|
|
||||||
watcher.on('error', (event: ErrorEvent) => {
|
watcher.on('error', (event: ErrorEvent) => {
|
||||||
@@ -94,16 +94,16 @@ prog.command('dev')
|
|||||||
});
|
});
|
||||||
|
|
||||||
watcher.on('fatal', (event: FatalEvent) => {
|
watcher.on('fatal', (event: FatalEvent) => {
|
||||||
console.log(colors.bold.red(`> ${event.message}`));
|
console.log(colors.bold().red(`> ${event.message}`));
|
||||||
if (event.log) console.log(event.log);
|
if (event.log) console.log(event.log);
|
||||||
});
|
});
|
||||||
|
|
||||||
watcher.on('build', (event: BuildEvent) => {
|
watcher.on('build', (event: BuildEvent) => {
|
||||||
if (event.errors.length) {
|
if (event.errors.length) {
|
||||||
console.log(colors.bold.red(`✗ ${event.type}`));
|
console.log(colors.bold().red(`✗ ${event.type}`));
|
||||||
|
|
||||||
event.errors.filter(e => !e.duplicate).forEach(error => {
|
event.errors.filter(e => !e.duplicate).forEach(error => {
|
||||||
if (error.file) console.log(colors.bold(error.file));
|
if (error.file) console.log(colors.bold()(error.file));
|
||||||
console.log(error.message);
|
console.log(error.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -112,10 +112,10 @@ prog.command('dev')
|
|||||||
console.log(`${hidden} duplicate ${hidden === 1 ? 'error' : 'errors'} hidden\n`);
|
console.log(`${hidden} duplicate ${hidden === 1 ? 'error' : 'errors'} hidden\n`);
|
||||||
}
|
}
|
||||||
} else if (event.warnings.length) {
|
} else if (event.warnings.length) {
|
||||||
console.log(colors.bold.yellow(`• ${event.type}`));
|
console.log(colors.bold().yellow(`• ${event.type}`));
|
||||||
|
|
||||||
event.warnings.filter(e => !e.duplicate).forEach(warning => {
|
event.warnings.filter(e => !e.duplicate).forEach(warning => {
|
||||||
if (warning.file) console.log(colors.bold(warning.file));
|
if (warning.file) console.log(colors.bold()(warning.file));
|
||||||
console.log(warning.message);
|
console.log(warning.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -124,11 +124,11 @@ prog.command('dev')
|
|||||||
console.log(`${hidden} duplicate ${hidden === 1 ? 'warning' : 'warnings'} hidden\n`);
|
console.log(`${hidden} duplicate ${hidden === 1 ? 'warning' : 'warnings'} hidden\n`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`${colors.bold.green(`✔ ${event.type}`)} ${colors.gray(`(${format_milliseconds(event.duration)})`)}`);
|
console.log(`${colors.bold().green(`✔ ${event.type}`)} ${colors.gray(`(${format_milliseconds(event.duration)})`)}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(colors.bold.red(`> ${err.message}`));
|
console.log(colors.bold().red(`> ${err.message}`));
|
||||||
console.log(colors.gray(err.stack));
|
console.log(colors.gray(err.stack));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@@ -169,9 +169,9 @@ prog.command('build [dest]')
|
|||||||
require('./server/server.js');
|
require('./server/server.js');
|
||||||
`.replace(/^\t+/gm, '').trim());
|
`.replace(/^\t+/gm, '').trim());
|
||||||
|
|
||||||
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));
|
console.log(colors.gray(err.stack));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@@ -222,24 +222,24 @@ prog.command('export [dest]')
|
|||||||
timeout: opts.timeout,
|
timeout: opts.timeout,
|
||||||
|
|
||||||
oninfo: event => {
|
oninfo: event => {
|
||||||
console.log(colors.bold.cyan(`> ${event.message}`));
|
console.log(colors.bold().cyan(`> ${event.message}`));
|
||||||
},
|
},
|
||||||
|
|
||||||
onfile: event => {
|
onfile: event => {
|
||||||
const size_color = event.size > 150000 ? colors.bold().red : event.size > 50000 ? colors.bold().yellow : colors.bold().gray;
|
const size_color = event.size > 150000 ? colors.bold()().red : event.size > 50000 ? colors.bold()().yellow : colors.bold()().gray;
|
||||||
const size_label = size_color(left_pad(pb(event.size), 10));
|
const size_label = size_color(left_pad(pb(event.size), 10));
|
||||||
|
|
||||||
const file_label = event.status === 200
|
const file_label = event.status === 200
|
||||||
? event.file
|
? event.file
|
||||||
: colors.bold[event.status >= 400 ? 'red' : 'yellow'](`(${event.status}) ${event.file}`);
|
: colors.bold()[event.status >= 400 ? 'red' : 'yellow'](`(${event.status}) ${event.file}`);
|
||||||
|
|
||||||
console.log(`${size_label} ${file_label}`);
|
console.log(`${size_label} ${file_label}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.error(`\n> Finished in ${elapsed(start)}. Type ${colors.bold.cyan(`npx serve ${dest}`)} to run the app.`);
|
console.error(`\n> Finished in ${elapsed(start)}. Type ${colors.bold().cyan(`npx serve ${dest}`)} to run the app.`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(colors.bold.red(`> ${err.message}`));
|
console.error(colors.bold().red(`> ${err.message}`));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -278,7 +278,7 @@ async function _build(
|
|||||||
|
|
||||||
console.log();
|
console.log();
|
||||||
console.log(c(`┌─${repeat('─', banner.length)}─┐`));
|
console.log(c(`┌─${repeat('─', banner.length)}─┐`));
|
||||||
console.log(c(`│ ${colors.bold(banner) } │`));
|
console.log(c(`│ ${colors.bold()(banner) } │`));
|
||||||
console.log(c(`└─${repeat('─', banner.length)}─┘`));
|
console.log(c(`└─${repeat('─', banner.length)}─┘`));
|
||||||
|
|
||||||
console.log(event.result.print());
|
console.log(event.result.print());
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export default function extract_css(client_result: CompileResult, components: Pa
|
|||||||
chunks_with_css.add(chunk);
|
chunks_with_css.add(chunk);
|
||||||
});
|
});
|
||||||
|
|
||||||
const entry = path.resolve(dirs.src, 'client.js');
|
const entry = path.resolve(dirs.src, 'app.mjs');
|
||||||
const entry_chunk = client_result.chunks.find(chunk => chunk.modules.indexOf(entry) !== -1);
|
const entry_chunk = client_result.chunks.find(chunk => chunk.modules.indexOf(entry) !== -1);
|
||||||
|
|
||||||
const entry_chunk_dependencies: Set<Chunk> = new Set([entry_chunk]);
|
const entry_chunk_dependencies: Set<Chunk> = new Set([entry_chunk]);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import * as path from 'path';
|
|||||||
import { posixify, stringify, walk, write_if_changed } from '../utils';
|
import { posixify, stringify, walk, write_if_changed } from '../utils';
|
||||||
import { Page, PageComponent, ManifestData } from '../interfaces';
|
import { Page, PageComponent, ManifestData } from '../interfaces';
|
||||||
|
|
||||||
|
const app = fs.readFileSync(path.resolve(__dirname, '../templates/App.html'), 'utf-8');
|
||||||
|
const internal = fs.readFileSync(path.resolve(__dirname, '../templates/internal.mjs'), 'utf-8');
|
||||||
|
const layout = `<svelte:component this={child.component} {...child.props}/>`;
|
||||||
|
|
||||||
export function create_main_manifests({
|
export function create_main_manifests({
|
||||||
bundler,
|
bundler,
|
||||||
manifest_data,
|
manifest_data,
|
||||||
@@ -31,12 +35,11 @@ export function create_main_manifests({
|
|||||||
const client_manifest = generate_client(manifest_data, path_to_routes, bundler, dev, dev_port);
|
const client_manifest = generate_client(manifest_data, path_to_routes, bundler, dev, dev_port);
|
||||||
const server_manifest = generate_server(manifest_data, path_to_routes, cwd, src, dest, dev);
|
const server_manifest = generate_server(manifest_data, path_to_routes, cwd, src, dest, dev);
|
||||||
|
|
||||||
write_if_changed(
|
write_if_changed(`${output}/_layout.html`, layout);
|
||||||
`${output}/_layout.html`,
|
write_if_changed(`${output}/internal.mjs`, internal);
|
||||||
`<svelte:component this={child.component} {...child.props}/>`
|
write_if_changed(`${output}/App.html`, app);
|
||||||
);
|
write_if_changed(`${output}/app.mjs`, client_manifest);
|
||||||
write_if_changed(`${output}/client.js`, client_manifest);
|
write_if_changed(`${output}/server.mjs`, server_manifest);
|
||||||
write_if_changed(`${output}/server.js`, server_manifest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function create_serviceworker_manifest({ manifest_data, output, client_files, static_files }: {
|
export function create_serviceworker_manifest({ manifest_data, output, client_files, static_files }: {
|
||||||
@@ -80,7 +83,7 @@ function generate_client(
|
|||||||
dev: boolean,
|
dev: boolean,
|
||||||
dev_port?: number
|
dev_port?: number
|
||||||
) {
|
) {
|
||||||
const template_file = path.resolve(__dirname, '../templates/client.js');
|
const template_file = path.resolve(__dirname, '../templates/app.mjs');
|
||||||
const template = fs.readFileSync(template_file, 'utf-8');
|
const template = fs.readFileSync(template_file, 'utf-8');
|
||||||
|
|
||||||
const page_ids = new Set(manifest_data.pages.map(page =>
|
const page_ids = new Set(manifest_data.pages.map(page =>
|
||||||
@@ -167,7 +170,7 @@ function generate_server(
|
|||||||
dest: string,
|
dest: string,
|
||||||
dev: boolean
|
dev: boolean
|
||||||
) {
|
) {
|
||||||
const template_file = path.resolve(__dirname, '../templates/server.js');
|
const template_file = path.resolve(__dirname, '../templates/server.mjs');
|
||||||
const template = fs.readFileSync(template_file, 'utf-8');
|
const template = fs.readFileSync(template_file, 'utf-8');
|
||||||
|
|
||||||
const imports = [].concat(
|
const imports = [].concat(
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ export type Template = {
|
|||||||
stream: (req, res, data: Record<string, string | Promise<string>>) => void;
|
stream: (req, res, data: Record<string, string | Promise<string>>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Store = {
|
export type WritableStore<T> = {
|
||||||
get: () => any;
|
set: (value: T) => void;
|
||||||
|
update: (fn: (value: T) => T) => void;
|
||||||
|
subscribe: (fn: (T: any) => void) => () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PageComponent = {
|
export type PageComponent = {
|
||||||
|
|||||||
13
templates/App.html
Normal file
13
templates/App.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<script>
|
||||||
|
import { setContext } from 'svelte';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
import { CONTEXT_KEY } from './internal';
|
||||||
|
|
||||||
|
export let Root;
|
||||||
|
export let props;
|
||||||
|
export let session;
|
||||||
|
|
||||||
|
setContext(CONTEXT_KEY, writable(session));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Root {...props}/>
|
||||||
1
templates/internal.mjs
Normal file
1
templates/internal.mjs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const CONTEXT_KEY = {};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { tick } from 'svelte';
|
import App from '@sapper/App.html';
|
||||||
import RootComponent, * as RootComponentStatic from '__ROOT__';
|
import { preloading, page } from '../shared/stores';
|
||||||
|
import Root, * as RootStatic from '__ROOT__';
|
||||||
import ErrorComponent from '__ERROR__';
|
import ErrorComponent from '__ERROR__';
|
||||||
import {
|
import {
|
||||||
Target,
|
Target,
|
||||||
@@ -128,7 +129,9 @@ 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 });
|
preloading.set({
|
||||||
|
// TODO path, params, query
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const loaded = prefetching && prefetching.href === target.url.href ?
|
const loaded = prefetching && prefetching.href === target.url.href ?
|
||||||
prefetching.promise :
|
prefetching.promise :
|
||||||
@@ -150,6 +153,8 @@ export function navigate(target: Target, id: number, noscroll?: boolean, hash?:
|
|||||||
async function render(props: any, nullable_depth: number, scroll: ScrollPosition, noscroll: boolean, hash: string, token: {}) {
|
async function render(props: any, nullable_depth: number, scroll: ScrollPosition, noscroll: boolean, hash: string, token: {}) {
|
||||||
if (current_token !== token) return;
|
if (current_token !== token) return;
|
||||||
|
|
||||||
|
preloading.set(null);
|
||||||
|
|
||||||
if (root_component) {
|
if (root_component) {
|
||||||
// first, clear out highest-level root component
|
// first, clear out highest-level root component
|
||||||
let level = props.child;
|
let level = props.child;
|
||||||
@@ -160,18 +165,12 @@ async function render(props: any, nullable_depth: number, scroll: ScrollPosition
|
|||||||
|
|
||||||
const { component } = level;
|
const { component } = level;
|
||||||
level.component = null;
|
level.component = null;
|
||||||
root_component.$set({ child: props.child });
|
root_component.props = props;
|
||||||
|
|
||||||
await tick();
|
|
||||||
|
|
||||||
// then render new stuff
|
// then render new stuff
|
||||||
// TODO do we need to call `flush` before doing this?
|
// TODO do we need to call `flush` before doing this?
|
||||||
level.component = component;
|
level.component = component;
|
||||||
root_component.$set(props);
|
root_component.props = props;
|
||||||
|
|
||||||
// if we need to scroll to a deep link, we need to
|
|
||||||
// wait for the current update to happen first
|
|
||||||
if (!noscroll && hash) await tick();
|
|
||||||
} 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');
|
||||||
@@ -185,10 +184,13 @@ async function render(props: any, nullable_depth: number, scroll: ScrollPosition
|
|||||||
|
|
||||||
Object.assign(props, root_data);
|
Object.assign(props, root_data);
|
||||||
|
|
||||||
root_component = new RootComponent({
|
root_component = new App({
|
||||||
target,
|
target,
|
||||||
props,
|
props: {
|
||||||
store,
|
Root,
|
||||||
|
props,
|
||||||
|
session: __SAPPER__.session
|
||||||
|
},
|
||||||
hydrate: true
|
hydrate: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -247,7 +249,7 @@ export function prepare_page(target: Target): Promise<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!root_preload) {
|
if (!root_preload) {
|
||||||
const preload_fn = RootComponentStatic['pre' + 'load']; // Rollup makes us jump through these hoops :(
|
const preload_fn = RootStatic['pre' + 'load']; // Rollup makes us jump through these hoops :(
|
||||||
root_preload = preload_fn
|
root_preload = preload_fn
|
||||||
? initial_data.preloaded[0] || preload_fn.call(preload_context, {
|
? initial_data.preloaded[0] || preload_fn.call(preload_context, {
|
||||||
path,
|
path,
|
||||||
@@ -315,7 +317,6 @@ export function prepare_page(target: Target): Promise<{
|
|||||||
return {
|
return {
|
||||||
nullable_depth: 0,
|
nullable_depth: 0,
|
||||||
data: Object.assign({}, props, {
|
data: Object.assign({}, props, {
|
||||||
preloading: false,
|
|
||||||
child: {
|
child: {
|
||||||
component: ErrorComponent,
|
component: ErrorComponent,
|
||||||
props
|
props
|
||||||
@@ -327,7 +328,6 @@ export function prepare_page(target: Target): Promise<{
|
|||||||
const props = { path, query, error: null, status: null };
|
const props = { path, query, error: null, status: null };
|
||||||
const data = {
|
const data = {
|
||||||
path,
|
path,
|
||||||
preloading: false,
|
|
||||||
child: Object.assign({}, root_props.child, {
|
child: Object.assign({}, root_props.child, {
|
||||||
segment: segments[0]
|
segment: segments[0]
|
||||||
})
|
})
|
||||||
13
templates/src/app/index.ts
Normal file
13
templates/src/app/index.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { getContext } from 'svelte';
|
||||||
|
import { CONTEXT_KEY } from '@sapper/internal';
|
||||||
|
import * as stores from '../shared/stores';
|
||||||
|
|
||||||
|
export const preloading = { subscribe: stores.preloading.subscribe };
|
||||||
|
export const page = { subscribe: stores.page.subscribe };
|
||||||
|
|
||||||
|
export const getSession = () => getContext(CONTEXT_KEY);
|
||||||
|
|
||||||
|
export { default as start } from './start/index';
|
||||||
|
export { default as goto } from './goto/index';
|
||||||
|
export { default as prefetch } from './prefetch/index';
|
||||||
|
export { default as prefetchRoutes } from './prefetchRoutes/index';
|
||||||
@@ -6,25 +6,21 @@ import {
|
|||||||
scroll_history,
|
scroll_history,
|
||||||
scroll_state,
|
scroll_state,
|
||||||
select_route,
|
select_route,
|
||||||
set_store,
|
|
||||||
set_target,
|
set_target,
|
||||||
uid,
|
uid,
|
||||||
set_uid,
|
set_uid,
|
||||||
set_cid
|
set_cid
|
||||||
} from '../app';
|
} from '../app';
|
||||||
import prefetch from '../prefetch/index';
|
import prefetch from '../prefetch/index';
|
||||||
import { Store, ScrollPosition } from '../types';
|
|
||||||
|
|
||||||
export default function start(opts: {
|
export default function start(opts: {
|
||||||
target: Node,
|
target: Node
|
||||||
store?: (data: any) => Store
|
|
||||||
}) {
|
}) {
|
||||||
if ('scrollRestoration' in history) {
|
if ('scrollRestoration' in history) {
|
||||||
history.scrollRestoration = 'manual';
|
history.scrollRestoration = 'manual';
|
||||||
}
|
}
|
||||||
|
|
||||||
set_target(opts.target);
|
set_target(opts.target);
|
||||||
if (opts.store) set_store(opts.store);
|
|
||||||
|
|
||||||
addEventListener('click', handle_click);
|
addEventListener('click', handle_click);
|
||||||
addEventListener('popstate', handle_popstate);
|
addEventListener('popstate', handle_popstate);
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export { default as start } from './start/index';
|
|
||||||
export { default as goto } from './goto/index';
|
|
||||||
export { default as prefetch } from './prefetch/index';
|
|
||||||
export { default as prefetchRoutes } from './prefetchRoutes/index';
|
|
||||||
@@ -4,12 +4,14 @@ import cookie from 'cookie';
|
|||||||
import devalue from 'devalue';
|
import devalue from 'devalue';
|
||||||
import fetch from 'node-fetch';
|
import fetch from 'node-fetch';
|
||||||
import { URL, resolve } from 'url';
|
import { URL, resolve } from 'url';
|
||||||
|
import * as stores from '../../shared/stores';
|
||||||
import { build_dir, dev, src_dir, IGNORE } from '../placeholders';
|
import { build_dir, dev, src_dir, IGNORE } from '../placeholders';
|
||||||
import { Manifest, Page, Props, Req, Res, Store } from './types';
|
import { Manifest, Page, Props, Req, Res } from './types';
|
||||||
|
import App from '@sapper/App.html';
|
||||||
|
|
||||||
export function get_page_handler(
|
export function get_page_handler(
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
store_getter: (req: Req, res: Res) => Store
|
session_getter: (req: Req, res: Res) => any
|
||||||
) {
|
) {
|
||||||
const get_build_info = dev
|
const get_build_info = dev
|
||||||
? () => JSON.parse(fs.readFileSync(path.join(build_dir, 'build.json'), 'utf-8'))
|
? () => JSON.parse(fs.readFileSync(path.join(build_dir, 'build.json'), 'utf-8'))
|
||||||
@@ -76,7 +78,7 @@ export function get_page_handler(
|
|||||||
res.setHeader('Link', link);
|
res.setHeader('Link', link);
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = store_getter ? store_getter(req, res) : null;
|
const session = session_getter(req, res);
|
||||||
|
|
||||||
let redirect: { statusCode: number, location: string };
|
let redirect: { statusCode: number, location: string };
|
||||||
let preload_error: { statusCode: number, message: Error | string };
|
let preload_error: { statusCode: number, message: Error | string };
|
||||||
@@ -127,8 +129,7 @@ export function get_page_handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return fetch(parsed.href, opts);
|
return fetch(parsed.href, opts);
|
||||||
},
|
}
|
||||||
store
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let preloaded;
|
let preloaded;
|
||||||
@@ -177,11 +178,6 @@ export function get_page_handler(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const serialized = {
|
|
||||||
preloaded: `[${preloaded.map(data => try_serialize(data)).join(',')}]`,
|
|
||||||
store: store && try_serialize(store.get())
|
|
||||||
};
|
|
||||||
|
|
||||||
const segments = req.path.split('/').filter(Boolean);
|
const segments = req.path.split('/').filter(Boolean);
|
||||||
|
|
||||||
const props: Props = {
|
const props: Props = {
|
||||||
@@ -223,15 +219,30 @@ export function get_page_handler(
|
|||||||
level = level.props.child;
|
level = level.props.child;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { html, head, css } = manifest.root.render(data, {
|
stores.page.set({
|
||||||
store
|
path: req.path,
|
||||||
|
query: req.query,
|
||||||
|
params: req.params
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { html, head, css } = App.render({
|
||||||
|
Root: manifest.root,
|
||||||
|
props: data,
|
||||||
|
session
|
||||||
|
});
|
||||||
|
|
||||||
|
const serialized = {
|
||||||
|
preloaded: `[${preloaded.map(data => try_serialize(data)).join(',')}]`,
|
||||||
|
session: session && try_serialize(session, err => {
|
||||||
|
throw new Error(`Failed to serialize session data: ${err.message}`);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
let script = `__SAPPER__={${[
|
let script = `__SAPPER__={${[
|
||||||
error && `error:1`,
|
error && `error:1`,
|
||||||
`baseUrl:"${req.baseUrl}"`,
|
`baseUrl:"${req.baseUrl}"`,
|
||||||
serialized.preloaded && `preloaded:${serialized.preloaded}`,
|
serialized.preloaded && `preloaded:${serialized.preloaded}`,
|
||||||
serialized.store && `store:${serialized.store}`
|
serialized.session && `session:${serialized.session}`
|
||||||
].filter(Boolean).join(',')}};`;
|
].filter(Boolean).join(',')}};`;
|
||||||
|
|
||||||
if (has_service_worker) {
|
if (has_service_worker) {
|
||||||
@@ -320,10 +331,11 @@ function read_template(dir = build_dir) {
|
|||||||
return fs.readFileSync(`${dir}/template.html`, 'utf-8');
|
return fs.readFileSync(`${dir}/template.html`, 'utf-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
function try_serialize(data: any) {
|
function try_serialize(data: any, fail?: (err) => void) {
|
||||||
try {
|
try {
|
||||||
return devalue(data);
|
return devalue(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (fail) fail(err);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import { get_page_handler } from './get_page_handler';
|
|||||||
import { lookup } from './mime';
|
import { lookup } from './mime';
|
||||||
|
|
||||||
export default function middleware(opts: {
|
export default function middleware(opts: {
|
||||||
store?: (req: Req, res: Res) => Store,
|
session?: (req: Req, res: Res) => any,
|
||||||
ignore?: any
|
ignore?: any
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const { store, ignore } = opts;
|
const { session, ignore } = opts;
|
||||||
|
|
||||||
let emitted_basepath = false;
|
let emitted_basepath = false;
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ export default function middleware(opts: {
|
|||||||
|
|
||||||
get_server_route_handler(manifest.server_routes),
|
get_server_route_handler(manifest.server_routes),
|
||||||
|
|
||||||
get_page_handler(manifest, store)
|
get_page_handler(manifest, session || noop)
|
||||||
].filter(Boolean));
|
].filter(Boolean));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,4 +140,6 @@ export function serve({ prefix, pathname, cache_control }: {
|
|||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function noop(){}
|
||||||
4
templates/src/shared/stores.ts
Normal file
4
templates/src/shared/stores.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export const preloading = writable(null);
|
||||||
|
export const page = writable(null);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -7,12 +7,13 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export let preloading;
|
import { preloading } from '@sapper/app';
|
||||||
|
|
||||||
export let child;
|
export let child;
|
||||||
export let rootPreloadFunctionRan;
|
export let rootPreloadFunctionRan;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if preloading}
|
{#if $preloading}
|
||||||
<progress class='preloading-progress' value=0.5/>
|
<progress class='preloading-progress' value=0.5/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import * as sapper from '@sapper/client';
|
import { getSession } from '@sapper/app';
|
||||||
const session = sapper.session();
|
const session = getSession();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1>{$session.title}</h1>
|
<h1>{$session.title}</h1>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as sapper from '@sapper/client';
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
window.start = () => sapper.start({
|
window.start = () => sapper.start({
|
||||||
target: document.querySelector('#sapper')
|
target: document.querySelector('#sapper')
|
||||||
|
|||||||
Reference in New Issue
Block a user