mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-20 14:25:07 +00:00
Add goto function (#46)
* WIP * programmatic navigation * get tests working * wait longer
This commit is contained in:
@@ -187,7 +187,8 @@ function get_route_handler(fn) {
|
|||||||
|
|
||||||
next();
|
next();
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
res.status(500).end(templates.render(500, {
|
res.status(500);
|
||||||
|
res.end(templates.render(500, {
|
||||||
title: (err && err.name) || 'Internal server error',
|
title: (err && err.name) || 'Internal server error',
|
||||||
url,
|
url,
|
||||||
error: escape_html(err && (err.details || err.message || err) || 'Unknown error'),
|
error: escape_html(err && (err.details || err.message || err) || 'Unknown error'),
|
||||||
@@ -201,7 +202,8 @@ function get_not_found_handler(fn) {
|
|||||||
return function handle_not_found(req, res) {
|
return function handle_not_found(req, res) {
|
||||||
const asset_cache = fn();
|
const asset_cache = fn();
|
||||||
|
|
||||||
res.status(404).end(templates.render(404, {
|
res.status(404);
|
||||||
|
res.end(templates.render(404, {
|
||||||
title: 'Not found',
|
title: 'Not found',
|
||||||
status: 404,
|
status: 404,
|
||||||
method: req.method,
|
method: req.method,
|
||||||
|
|||||||
1223
package-lock.json
generated
1223
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
|||||||
"webpack-hot-middleware": "^2.21.0"
|
"webpack-hot-middleware": "^2.21.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"css-loader": "^0.28.7",
|
||||||
"eslint": "^4.13.1",
|
"eslint": "^4.13.1",
|
||||||
"eslint-plugin-import": "^2.8.0",
|
"eslint-plugin-import": "^2.8.0",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
@@ -29,6 +30,9 @@
|
|||||||
"nightmare": "^2.10.0",
|
"nightmare": "^2.10.0",
|
||||||
"node-fetch": "^1.7.3",
|
"node-fetch": "^1.7.3",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
|
"style-loader": "^0.19.1",
|
||||||
|
"svelte": "^1.49.1",
|
||||||
|
"svelte-loader": "^2.3.2",
|
||||||
"wait-on": "^2.0.2"
|
"wait-on": "^2.0.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -6,12 +6,17 @@ export let component;
|
|||||||
let target;
|
let target;
|
||||||
let routes;
|
let routes;
|
||||||
|
|
||||||
|
const history = typeof window !== 'undefined' ? window.history : {
|
||||||
|
pushState: () => {},
|
||||||
|
replaceState: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
const scroll_history = {};
|
const scroll_history = {};
|
||||||
let uid = 1;
|
let uid = 1;
|
||||||
let cid;
|
let cid;
|
||||||
|
|
||||||
if ('scrollRestoration' in history) {
|
if ('scrollRestoration' in history) {
|
||||||
history.scrollRestoration = 'manual'
|
history.scrollRestoration = 'manual';
|
||||||
}
|
}
|
||||||
|
|
||||||
function select_route(url) {
|
function select_route(url) {
|
||||||
@@ -39,7 +44,7 @@ function render(Component, data, scroll) {
|
|||||||
} 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');
|
||||||
let end = document.querySelector('#sapper-head-end');
|
const end = document.querySelector('#sapper-head-end');
|
||||||
|
|
||||||
if (start && end) {
|
if (start && end) {
|
||||||
while (start.nextSibling !== end) detach(start.nextSibling);
|
while (start.nextSibling !== end) detach(start.nextSibling);
|
||||||
@@ -75,8 +80,6 @@ function navigate(url, id) {
|
|||||||
|
|
||||||
id = cid = ++uid;
|
id = cid = ++uid;
|
||||||
scroll_history[cid] = { x: 0, y: 0 };
|
scroll_history[cid] = { x: 0, y: 0 };
|
||||||
|
|
||||||
history.pushState({ id }, '', url.href);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selected.route.load().then(mod => {
|
selected.route.load().then(mod => {
|
||||||
@@ -123,6 +126,7 @@ function handle_click(event) {
|
|||||||
|
|
||||||
if (navigate(url, null)) {
|
if (navigate(url, null)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
history.pushState({ id: cid }, '', url.href);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +177,7 @@ export function init(_target, _routes) {
|
|||||||
inited = true;
|
inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scroll = scroll_history[uid] = scroll_state();
|
scroll_history[uid] = scroll_state();
|
||||||
|
|
||||||
history.replaceState({ id: uid }, '', window.location.href);
|
history.replaceState({ id: uid }, '', window.location.href);
|
||||||
navigate(new URL(window.location), uid);
|
navigate(new URL(window.location), uid);
|
||||||
@@ -189,4 +193,12 @@ function scroll_state() {
|
|||||||
x: window.scrollX,
|
x: window.scrollX,
|
||||||
y: window.scrollY
|
y: window.scrollY
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function goto(href, opts = {}) {
|
||||||
|
if (navigate(new URL(href, window.location.href))) {
|
||||||
|
if (history) history[opts.replaceState ? 'replaceState' : 'pushState']({ id: cid }, '', href);
|
||||||
|
} else {
|
||||||
|
window.location.href = href;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,14 +6,21 @@
|
|||||||
<h1>About this site</h1>
|
<h1>About this site</h1>
|
||||||
|
|
||||||
<p>This is the 'about' page. There's not much here.</p>
|
<p>This is the 'about' page. There's not much here.</p>
|
||||||
|
|
||||||
|
<button on:click='goto("/blog/what-is-sapper")'>What is Sapper?</button>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Layout from './_components/Layout.html';
|
import Layout from './_components/Layout.html';
|
||||||
|
import { goto } from '../../../runtime/app.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Layout
|
Layout
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
goto
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { init } from '__app__';
|
import { init } from '../../../runtime/app.js';
|
||||||
|
|
||||||
// `routes` is an array of route objects injected by Sapper
|
// `routes` is an array of route objects injected by Sapper
|
||||||
init(document.querySelector('#sapper'), __routes__);
|
init(document.querySelector('#sapper'), __routes__);
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
const config = require('../../webpack/config.js');
|
const config = require('../../webpack/config.js');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
|
||||||
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: config.client.entry(),
|
entry: config.client.entry(),
|
||||||
@@ -24,27 +22,18 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
config.dev && {
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
use: [
|
use: [
|
||||||
{ loader: "style-loader" },
|
{ loader: "style-loader" },
|
||||||
{ loader: "css-loader" }
|
{ loader: "css-loader" }
|
||||||
]
|
]
|
||||||
},
|
|
||||||
!config.dev && {
|
|
||||||
test: /\.css$/,
|
|
||||||
use: ExtractTextPlugin.extract({
|
|
||||||
fallback: 'style-loader',
|
|
||||||
use: [{ loader: 'css-loader', options: { sourceMap: config.dev } }]
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
config.dev && new webpack.HotModuleReplacementPlugin(),
|
config.dev && new webpack.HotModuleReplacementPlugin(),
|
||||||
!config.dev && new ExtractTextPlugin('main.css'),
|
!config.dev && new webpack.optimize.ModuleConcatenationPlugin()
|
||||||
!config.dev && new webpack.optimize.ModuleConcatenationPlugin(),
|
|
||||||
!config.dev && new UglifyJSPlugin()
|
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
devtool: config.dev ? 'inline-source-map' : false
|
devtool: config.dev ? 'inline-source-map' : false
|
||||||
};
|
};
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
const config = require('../../webpack/config.js');
|
const config = require('../../webpack/config.js');
|
||||||
const webpack = require('webpack');
|
|
||||||
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
|
||||||
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: config.server.entry(),
|
entry: config.server.entry(),
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
@@ -12,7 +11,7 @@ run('development');
|
|||||||
|
|
||||||
function run(env) {
|
function run(env) {
|
||||||
describe(`env=${env}`, function () {
|
describe(`env=${env}`, function () {
|
||||||
this.timeout(30000);
|
this.timeout(5000);
|
||||||
|
|
||||||
let PORT;
|
let PORT;
|
||||||
let server;
|
let server;
|
||||||
@@ -23,7 +22,7 @@ function run(env) {
|
|||||||
let base;
|
let base;
|
||||||
|
|
||||||
function get(url) {
|
function get(url) {
|
||||||
return new Promise((fulfil, reject) => {
|
return new Promise(fulfil => {
|
||||||
const req = {
|
const req = {
|
||||||
url,
|
url,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
@@ -93,7 +92,7 @@ function run(env) {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
app = express();
|
const app = express();
|
||||||
|
|
||||||
app.use(serve('assets'));
|
app.use(serve('assets'));
|
||||||
|
|
||||||
@@ -128,6 +127,14 @@ function run(env) {
|
|||||||
nightmare.on('console', (type, ...args) => {
|
nightmare.on('console', (type, ...args) => {
|
||||||
console[type](...args);
|
console[type](...args);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
nightmare.on('page', (type, ...args) => {
|
||||||
|
if (type === 'error') {
|
||||||
|
console.error(args[1]);
|
||||||
|
} else {
|
||||||
|
console.warn(type, args);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@@ -177,6 +184,20 @@ function run(env) {
|
|||||||
|
|
||||||
assert.deepEqual(requests.map(r => r.url), []);
|
assert.deepEqual(requests.map(r => r.url), []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('navigates programmatically', async () => {
|
||||||
|
await nightmare
|
||||||
|
.goto(`${base}/about`)
|
||||||
|
.wait(() => window.READY)
|
||||||
|
.click('button')
|
||||||
|
.wait(() => window.location.pathname === '/blog/what-is-sapper')
|
||||||
|
.wait(100);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
await nightmare.evaluate(() => document.title),
|
||||||
|
'What is Sapper?'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('headers', () => {
|
describe('headers', () => {
|
||||||
@@ -188,8 +209,8 @@ function run(env) {
|
|||||||
'text/html'
|
'text/html'
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.ok
|
assert.ok(
|
||||||
(/<\/client\/main.\w+\.js\>;rel="preload";as="script", <\/client\/_.\d+.\w+.js>;rel="preload";as="script"/.test(headers['Link']),
|
/<\/client\/main.\w+\.js>;rel="preload";as="script", <\/client\/_.\d+.\w+.js>;rel="preload";as="script"/.test(headers['Link']),
|
||||||
headers['Link']
|
headers['Link']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -200,10 +221,13 @@ function run(env) {
|
|||||||
function exec(cmd) {
|
function exec(cmd) {
|
||||||
return new Promise((fulfil, reject) => {
|
return new Promise((fulfil, reject) => {
|
||||||
require('child_process').exec(cmd, (err, stdout, stderr) => {
|
require('child_process').exec(cmd, (err, stdout, stderr) => {
|
||||||
if (err) return reject(err);
|
if (err) {
|
||||||
|
process.stdout.write(stdout);
|
||||||
|
process.stderr.write(stderr);
|
||||||
|
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
process.stdout.write(stdout);
|
|
||||||
process.stderr.write(stderr);
|
|
||||||
fulfil();
|
fulfil();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const rimraf = require('rimraf');
|
|
||||||
const child_process = require('child_process');
|
|
||||||
|
|
||||||
// ensure sapper doesn't exist in app/node_modules
|
|
||||||
rimraf.sync(
|
|
||||||
path.join(__dirname, 'app/node_modules/sapper')
|
|
||||||
);
|
|
||||||
|
|
||||||
rimraf.sync(
|
|
||||||
path.join(__dirname, 'app/node_modules/.bin/sapper')
|
|
||||||
);
|
|
||||||
|
|
||||||
// create symlinks
|
|
||||||
fs.symlinkSync(
|
|
||||||
path.join(__dirname, '..'),
|
|
||||||
path.join(__dirname, 'app/node_modules/sapper')
|
|
||||||
);
|
|
||||||
|
|
||||||
fs.symlinkSync(
|
|
||||||
path.join(__dirname, '../cli/index.js'),
|
|
||||||
path.join(__dirname, 'app/node_modules/.bin/sapper')
|
|
||||||
);
|
|
||||||
|
|
||||||
const app_dir = path.join(__dirname, 'app');
|
|
||||||
|
|
||||||
function start_server() {
|
|
||||||
const server = child_process.spawn(process.execPath, ['server.js'], {
|
|
||||||
cwd: app_dir,
|
|
||||||
env: {
|
|
||||||
NODE_ENV: 'development'
|
|
||||||
},
|
|
||||||
stdio: 'pipe'
|
|
||||||
});
|
|
||||||
|
|
||||||
server.stdout.on('data', (data) => {
|
|
||||||
process.stdout.write(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
server.stderr.on('data', (data) => {
|
|
||||||
process.stderr.write(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function launch() {
|
|
||||||
if (process.argv[2] === '--dev') {
|
|
||||||
start_server();
|
|
||||||
} else {
|
|
||||||
child_process.exec(`npm run build`, {
|
|
||||||
cwd: app_dir
|
|
||||||
}, (err, stdout, stderr) => {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
process.stdout.write(stdout);
|
|
||||||
process.stderr.write(stderr);
|
|
||||||
start_server();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is a terrible hack
|
|
||||||
if (process.env.APPVEYOR) {
|
|
||||||
child_process.exec(`npm install`, {
|
|
||||||
cwd: app_dir
|
|
||||||
}, (err, stdout, stderr) => {
|
|
||||||
if (err) throw err;
|
|
||||||
|
|
||||||
process.stdout.write(stdout);
|
|
||||||
process.stderr.write(stderr);
|
|
||||||
launch();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
launch();
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user