mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-13 11:35:28 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d13d6209ed | ||
|
|
05aef8de71 | ||
|
|
429f44e3a6 | ||
|
|
f8e853c02b | ||
|
|
bedbcb834b | ||
|
|
5c07080207 | ||
|
|
c2ed73f103 | ||
|
|
133ac07ed2 | ||
|
|
7417de101e | ||
|
|
2792b7c5d1 | ||
|
|
cf8d5ee717 | ||
|
|
8e4517a1ad | ||
|
|
91894722ee | ||
|
|
933b3b76a6 | ||
|
|
3ed4d1d887 | ||
|
|
dda936e53b | ||
|
|
ac4eb84f3d | ||
|
|
9e70e68c0c | ||
|
|
22389eab99 | ||
|
|
fe6b7976ef | ||
|
|
54e92c3b99 | ||
|
|
e6c1a54164 | ||
|
|
629b5601c8 | ||
|
|
8bdd363a19 | ||
|
|
8fcc27d44f | ||
|
|
f6e72a0432 | ||
|
|
f886a12bd7 | ||
|
|
6c03cfd46a | ||
|
|
78480fe5e8 | ||
|
|
5cba40b7e0 | ||
|
|
c99b787632 | ||
|
|
99a25308fc | ||
|
|
c0ada5c52f | ||
|
|
d51e1a0af8 | ||
|
|
5e5a8c4c69 | ||
|
|
49f8b2c4bd | ||
|
|
2754ba0ee4 | ||
|
|
1ad27573c6 | ||
|
|
f10b941c4e | ||
|
|
6ca869a3b1 | ||
|
|
a2204a9d2e |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -3,11 +3,7 @@ yarn.lock
|
||||
yarn-error.log
|
||||
node_modules
|
||||
cypress/screenshots
|
||||
test/app/.sapper
|
||||
test/app/src/manifest
|
||||
__sapper__
|
||||
test/app/export
|
||||
test/app/build
|
||||
sapper
|
||||
runtime.js
|
||||
dist
|
||||
|
||||
@@ -3,7 +3,7 @@ sudo: false
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "11"
|
||||
- "stable"
|
||||
|
||||
env:
|
||||
global:
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# sapper changelog
|
||||
|
||||
## 0.27.0
|
||||
|
||||
* Change license from LIL to MIT ([#652](https://github.com/sveltejs/sapper/pull/652))
|
||||
* Fix index server route mapping ([#624](https://github.com/sveltejs/sapper/issues/624))
|
||||
|
||||
## 0.26.1
|
||||
|
||||
* Handle skipped segments ([#663](https://github.com/sveltejs/sapper/pull/663))
|
||||
|
||||
## 0.26.0
|
||||
|
||||
* Update to Svelte 3
|
||||
|
||||
10
LICENSE
10
LICENSE
@@ -1,9 +1,7 @@
|
||||
Copyright (c) 2017 [these people](https://github.com/sveltejs/sapper/graphs/contributors).
|
||||
Copyright (c) 2016-19 [these people](https://github.com/sveltejs/sapper/graphs/contributors)
|
||||
|
||||
Permission is hereby granted by the authors of this software, to any person, to use the software for any purpose, free of charge, including the rights to run, read, copy, change, distribute and sell it, and including usage rights to any patents the authors may hold on it, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
This license, or a link to its text, must be included with all copies of the software and any derivative works.
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
Any modification to the software submitted to the authors may be incorporated into the software under the terms of this license.
|
||||
|
||||
The software is provided "as is", without warranty of any kind, including but not limited to the warranties of title, fitness, merchantability and non-infringement. The authors have no obligation to provide support or updates for the software, and may not be held liable for any damages, claims or other liability arising from its use.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# sapper
|
||||
|
||||
[Military-grade progressive web apps, powered by Svelte.](https://sapper.svelte.technology)
|
||||
[Military-grade progressive web apps, powered by Svelte.](https://sapper.svelte.dev)
|
||||
|
||||
|
||||
## What is Sapper?
|
||||
|
||||
Sapper is a framework for building high-performance universal web apps. [Read the guide](https://sapper.svelte.technology/guide) or the [introductory blog post](https://svelte.technology/blog/sapper-towards-the-ideal-web-app-framework) to learn more.
|
||||
Sapper is a framework for building high-performance universal web apps. [Read the guide](https://sapper.svelte.dev/docs) or the [introductory blog post](https://svelte.dev/blog/sapper-towards-the-ideal-web-app-framework) to learn more.
|
||||
|
||||
|
||||
## Get started
|
||||
@@ -74,4 +74,4 @@ npm run test
|
||||
|
||||
## License
|
||||
|
||||
[LIL](LICENSE)
|
||||
[LIL](LICENSE)
|
||||
|
||||
1525
package-lock.json
generated
1525
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
50
package.json
50
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sapper",
|
||||
"version": "0.26.0",
|
||||
"version": "0.27.0",
|
||||
"description": "Military-grade apps, engineered by Svelte",
|
||||
"bin": {
|
||||
"sapper": "./sapper"
|
||||
@@ -18,50 +18,50 @@
|
||||
"test": "test"
|
||||
},
|
||||
"dependencies": {
|
||||
"html-minifier": "^3.5.21",
|
||||
"html-minifier": "^4.0.0",
|
||||
"http-link-header": "^1.0.2",
|
||||
"shimport": "^1.0.0",
|
||||
"sourcemap-codec": "^1.4.4",
|
||||
"string-hash": "^1.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^5.2.5",
|
||||
"@types/node": "^10.12.21",
|
||||
"@types/puppeteer": "^1.11.3",
|
||||
"@types/mocha": "^5.2.6",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/puppeteer": "^1.12.4",
|
||||
"agadoo": "^1.0.1",
|
||||
"cheap-watch": "^1.0.2",
|
||||
"cookie": "^0.3.1",
|
||||
"devalue": "^1.1.0",
|
||||
"eslint": "^5.12.1",
|
||||
"eslint-plugin-import": "^2.16.0",
|
||||
"kleur": "^3.0.1",
|
||||
"mocha": "^5.2.0",
|
||||
"node-fetch": "^2.3.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-import": "^2.17.2",
|
||||
"kleur": "^3.0.3",
|
||||
"mocha": "^6.1.4",
|
||||
"node-fetch": "^2.5.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"polka": "^0.5.1",
|
||||
"polka": "^0.5.2",
|
||||
"port-authority": "^1.0.5",
|
||||
"pretty-bytes": "^5.1.0",
|
||||
"puppeteer": "^1.12.0",
|
||||
"pretty-bytes": "^5.2.0",
|
||||
"puppeteer": "^1.15.0",
|
||||
"require-relative": "^0.8.7",
|
||||
"rollup": "^1.1.2",
|
||||
"rollup-plugin-commonjs": "^9.2.0",
|
||||
"rollup-plugin-json": "^3.1.0",
|
||||
"rollup-plugin-node-resolve": "^4.0.0",
|
||||
"rollup-plugin-replace": "^2.1.0",
|
||||
"rollup-plugin-string": "^2.0.2",
|
||||
"rollup": "^1.11.3",
|
||||
"rollup-plugin-commonjs": "^9.3.4",
|
||||
"rollup-plugin-json": "^4.0.0",
|
||||
"rollup-plugin-node-resolve": "^4.2.3",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
"rollup-plugin-sucrase": "^2.1.0",
|
||||
"rollup-plugin-svelte": "^5.0.3",
|
||||
"sade": "^1.4.2",
|
||||
"sirv": "^0.2.2",
|
||||
"sucrase": "^3.9.5",
|
||||
"svelte": "^3.0.0-beta.11",
|
||||
"sirv": "^0.4.2",
|
||||
"sucrase": "^3.10.1",
|
||||
"svelte": "^3.2.2",
|
||||
"svelte-loader": "^2.13.3",
|
||||
"webpack": "^4.29.0",
|
||||
"webpack": "^4.31.0",
|
||||
"webpack-format-messages": "^2.0.5",
|
||||
"yootils": "0.0.15"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.0.0"
|
||||
"svelte": "^3.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha --opts mocha.opts",
|
||||
@@ -81,7 +81,7 @@
|
||||
"express"
|
||||
],
|
||||
"author": "Rich Harris",
|
||||
"license": "LIL",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/sveltejs/sapper/issues"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import sucrase from 'rollup-plugin-sucrase';
|
||||
import string from 'rollup-plugin-string';
|
||||
import { string } from 'rollup-plugin-string';
|
||||
import json from 'rollup-plugin-json';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
@@ -24,8 +24,9 @@ function template(kind, external) {
|
||||
external,
|
||||
plugins: [
|
||||
resolve({
|
||||
extensions: ['.mjs', '.js', '.ts']
|
||||
extensions: ['.mjs', '.js', '.json', '.ts']
|
||||
}),
|
||||
json(),
|
||||
commonjs(),
|
||||
string({
|
||||
include: '**/*.md'
|
||||
@@ -52,7 +53,8 @@ export default [
|
||||
output: {
|
||||
dir: 'dist',
|
||||
format: 'cjs',
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
chunkFileNames: '[name].js'
|
||||
},
|
||||
external,
|
||||
plugins: [
|
||||
|
||||
@@ -101,7 +101,11 @@ export function select_target(url: URL): Target {
|
||||
if (url.origin !== location.origin) return null;
|
||||
if (!url.pathname.startsWith(initial_data.baseUrl)) return null;
|
||||
|
||||
const path = url.pathname.slice(initial_data.baseUrl.length);
|
||||
let path = url.pathname.slice(initial_data.baseUrl.length);
|
||||
|
||||
if (path === '') {
|
||||
path = '/';
|
||||
}
|
||||
|
||||
// avoid accidental clashes between server routes and page routes
|
||||
if (ignore.some(pattern => pattern.test(path))) return;
|
||||
@@ -288,14 +292,22 @@ export async function hydrate_target(target: Target): Promise<{
|
||||
let l = 1;
|
||||
|
||||
try {
|
||||
let segment_dirty = false;
|
||||
branch = await Promise.all(route.parts.map(async (part, i) => {
|
||||
const segment = segments[i];
|
||||
|
||||
if (current_branch[i] && current_branch[i].segment !== segment) segment_dirty = true;
|
||||
|
||||
props.segments[l] = segments[i + 1]; // TODO make this less confusing
|
||||
if (!part) return null;
|
||||
if (!part) return { segment };
|
||||
|
||||
const j = l++;
|
||||
|
||||
const segment = segments[i];
|
||||
if (!session_dirty && current_branch[i] && current_branch[i].segment === segment && current_branch[i].part === part.i) return current_branch[i];
|
||||
if (!session_dirty && !segment_dirty && current_branch[i] && current_branch[i].part === part.i) {
|
||||
return current_branch[i];
|
||||
}
|
||||
|
||||
segment_dirty = false;
|
||||
|
||||
const { default: component, preload } = await load_component(components[part.i]);
|
||||
|
||||
@@ -324,7 +336,7 @@ export async function hydrate_target(target: Target): Promise<{
|
||||
}
|
||||
|
||||
function load_css(chunk: string) {
|
||||
const href = `client/${chunk}`;
|
||||
const href = `sapper/${chunk}`;
|
||||
if (document.querySelector(`link[href="${href}"]`)) return;
|
||||
|
||||
return new Promise((fulfil, reject) => {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export const IGNORE = '__SAPPER__IGNORE__';
|
||||
@@ -1 +1,17 @@
|
||||
export { default as middleware } from './middleware/index';
|
||||
import http from 'http';
|
||||
import middleware from './middleware/index';
|
||||
import { StartOptions, MiddlewareOptions, Handler } from './types';
|
||||
|
||||
export { middleware };
|
||||
|
||||
export function server(opts: MiddlewareOptions) {
|
||||
const handler = middleware(opts) as Handler;
|
||||
return http.createServer(handler as http.ServerOptions);
|
||||
}
|
||||
|
||||
export function start(opts: StartOptions = {}) {
|
||||
const s = server(opts);
|
||||
s.listen(opts.port || process.env.PORT);
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -5,10 +5,8 @@ import cookie from 'cookie';
|
||||
import devalue from 'devalue';
|
||||
import fetch from 'node-fetch';
|
||||
import URL from 'url';
|
||||
import { IGNORE } from '../constants';
|
||||
import { Manifest, Page, Props, Req, Res } from './types';
|
||||
import { Manifest, Page, Req, Res } from '../types';
|
||||
import { build_dir, dev, src_dir } from '@sapper/internal/manifest-server';
|
||||
import { stores } from '@sapper/internal/shared';
|
||||
import App from '@sapper/internal/App.svelte';
|
||||
|
||||
export function get_page_handler(
|
||||
@@ -23,9 +21,8 @@ export function get_page_handler(
|
||||
? () => read_template(src_dir)
|
||||
: (str => () => str)(read_template(build_dir));
|
||||
|
||||
const has_service_worker = fs.existsSync(path.join(build_dir, 'service-worker.js'));
|
||||
const has_service_worker = fs.existsSync(path.join(build_dir, 'service-worker/service-worker.js'));
|
||||
|
||||
const { server_routes, pages } = manifest;
|
||||
const error_route = manifest.error;
|
||||
|
||||
function handle_error(req: Req, res: Res, statusCode: number, error: Error | string) {
|
||||
@@ -65,7 +62,7 @@ export function get_page_handler(
|
||||
// TODO add dependencies and CSS
|
||||
const link = preloaded_chunks
|
||||
.filter(file => file && !file.match(/\.map$/))
|
||||
.map(file => `<${req.baseUrl}/client/${file}>;rel="modulepreload"`)
|
||||
.map(file => `<${req.baseUrl}/sapper/${file}>;rel="modulepreload"`)
|
||||
.join(', ');
|
||||
|
||||
res.setHeader('Link', link);
|
||||
@@ -74,7 +71,7 @@ export function get_page_handler(
|
||||
.filter(file => file && !file.match(/\.map$/))
|
||||
.map((file) => {
|
||||
const as = /\.css$/.test(file) ? 'style' : 'script';
|
||||
return `<${req.baseUrl}/client/${file}>;rel="preload";as="${as}"`;
|
||||
return `<${req.baseUrl}/sapper/${file}>;rel="preload";as="${as}"`;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
@@ -265,14 +262,14 @@ export function get_page_handler(
|
||||
}
|
||||
|
||||
const file = [].concat(build_info.assets.main).filter(file => file && /\.js$/.test(file))[0];
|
||||
const main = `${req.baseUrl}/client/${file}`;
|
||||
const main = `${req.baseUrl}/sapper/${file}`;
|
||||
|
||||
if (build_info.bundler === 'rollup') {
|
||||
if (build_info.legacy_assets) {
|
||||
const legacy_main = `${req.baseUrl}/client/legacy/${build_info.legacy_assets.main}`;
|
||||
script += `(function(){try{eval("async function x(){}");var main="${main}"}catch(e){main="${legacy_main}"};var s=document.createElement("script");try{new Function("if(0)import('')")();s.src=main;s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="${req.baseUrl}/client/shimport@${build_info.shimport}.js";s.setAttribute("data-main",main);}document.head.appendChild(s);}());`;
|
||||
const legacy_main = `${req.baseUrl}/sapper/legacy/${build_info.legacy_assets.main}`;
|
||||
script += `(function(){try{eval("async function x(){}");var main="${main}"}catch(e){main="${legacy_main}"};var s=document.createElement("script");try{new Function("if(0)import('')")();s.src=main;s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="${req.baseUrl}/sapper/shimport@${build_info.shimport}.js";s.setAttribute("data-main",main);}document.head.appendChild(s);}());`;
|
||||
} else {
|
||||
script += `var s=document.createElement("script");try{new Function("if(0)import('')")();s.src="${main}";s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="${req.baseUrl}/client/shimport@${build_info.shimport}.js";s.setAttribute("data-main","${main}")}document.head.appendChild(s)`;
|
||||
script += `var s=document.createElement("script");try{new Function("if(0)import('')")();s.src="${main}";s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="${req.baseUrl}/sapper/shimport@${build_info.shimport}.js";s.setAttribute("data-main","${main}")}document.head.appendChild(s)`;
|
||||
}
|
||||
} else {
|
||||
script += `</script><script src="${main}">`;
|
||||
@@ -297,7 +294,7 @@ export function get_page_handler(
|
||||
});
|
||||
|
||||
styles = Array.from(css_chunks)
|
||||
.map(href => `<link rel="stylesheet" href="client/${href}">`)
|
||||
.map(href => `<link rel="stylesheet" href="sapper/${href}">`)
|
||||
.join('')
|
||||
} else {
|
||||
styles = (css && css.code ? `<style>${css.code}</style>` : '');
|
||||
@@ -328,15 +325,13 @@ export function get_page_handler(
|
||||
}
|
||||
|
||||
return function find_route(req: Req, res: Res, next: () => void) {
|
||||
if (req[IGNORE]) return next();
|
||||
|
||||
if (req.path === '/service-worker-index.html') {
|
||||
const homePage = pages.find(page => page.pattern.test('/'));
|
||||
const homePage = manifest.pages.find(page => page.pattern.test('/'));
|
||||
handle_page(homePage, req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const page of pages) {
|
||||
for (const page of manifest.pages) {
|
||||
if (page.pattern.test(req.path)) {
|
||||
handle_page(page, req, res);
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { IGNORE } from '../constants';
|
||||
import { Req, Res, ServerRoute } from './types';
|
||||
import { Req, Res, ServerRoute } from '../types';
|
||||
|
||||
export function get_server_route_handler(routes: ServerRoute[]) {
|
||||
async function handle_route(route: ServerRoute, req: Req, res: Res, next: () => void) {
|
||||
@@ -64,8 +63,6 @@ export function get_server_route_handler(routes: ServerRoute[]) {
|
||||
}
|
||||
|
||||
return function find_route(req: Req, res: Res, next: () => void) {
|
||||
if (req[IGNORE]) return next();
|
||||
|
||||
for (const route of routes) {
|
||||
if (route.pattern.test(req.path)) {
|
||||
handle_route(route, req, res, next);
|
||||
@@ -75,4 +72,4 @@ export function get_server_route_handler(routes: ServerRoute[]) {
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,42 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import querystring from 'querystring';
|
||||
import sirv from 'sirv';
|
||||
import { build_dir, dev, manifest } from '@sapper/internal/manifest-server';
|
||||
import { Handler, Req, Res } from './types';
|
||||
import { Handler, Req, Res, MiddlewareOptions } from '../types';
|
||||
import { get_server_route_handler } from './get_server_route_handler';
|
||||
import { get_page_handler } from './get_page_handler';
|
||||
import { lookup } from './mime';
|
||||
import { IGNORE } from '../constants';
|
||||
|
||||
export default function middleware(opts: {
|
||||
session?: (req: Req, res: Res) => any,
|
||||
ignore?: any
|
||||
} = {}) {
|
||||
export default function middleware(opts: MiddlewareOptions = {}) {
|
||||
const { session, ignore } = opts;
|
||||
|
||||
let emitted_basepath = false;
|
||||
|
||||
return compose_handlers([
|
||||
ignore && ((req: Req, res: Res, next: () => void) => {
|
||||
req[IGNORE] = should_ignore(req.path, ignore);
|
||||
next();
|
||||
return compose_handlers(ignore, [
|
||||
fs.existsSync('static') && sirv('static', {
|
||||
dev,
|
||||
setHeaders: opts.static && opts.static.headers && ((res: Response, pathname: string, stats: fs.Stats) => {
|
||||
const headers = opts.static.headers(pathname, stats);
|
||||
for (const k in headers) res.setHeader(k, headers[k]);
|
||||
})
|
||||
}),
|
||||
|
||||
(req: Req, res: Res, next: () => void) => {
|
||||
if (req[IGNORE]) return next();
|
||||
sirv(`${build_dir}/client`, {
|
||||
dev,
|
||||
maxAge: 31536000,
|
||||
immutable: true
|
||||
}),
|
||||
|
||||
sirv(`${build_dir}/service-worker`, {
|
||||
dev,
|
||||
maxAge: 300
|
||||
}),
|
||||
|
||||
function condition_request(req: Req, res: Res, next: () => void) {
|
||||
const qi = req.url.indexOf('?');
|
||||
req.query = ~qi ? querystring.parse(req.url.slice(qi + 1)) : {}
|
||||
|
||||
if (req.baseUrl === undefined) {
|
||||
let { originalUrl } = req;
|
||||
let originalUrl = req.originalUrl || req.url;
|
||||
if (req.url === '/' && originalUrl[originalUrl.length - 1] !== '/') {
|
||||
originalUrl += '/';
|
||||
}
|
||||
@@ -52,45 +63,32 @@ export default function middleware(opts: {
|
||||
next();
|
||||
},
|
||||
|
||||
fs.existsSync(path.join(build_dir, 'service-worker.js')) && serve({
|
||||
pathname: '/service-worker.js',
|
||||
cache_control: 'no-cache, no-store, must-revalidate'
|
||||
}),
|
||||
|
||||
fs.existsSync(path.join(build_dir, 'service-worker.js.map')) && serve({
|
||||
pathname: '/service-worker.js.map',
|
||||
cache_control: 'no-cache, no-store, must-revalidate'
|
||||
}),
|
||||
|
||||
serve({
|
||||
prefix: '/client/',
|
||||
cache_control: dev ? 'no-cache' : 'max-age=31536000, immutable'
|
||||
}),
|
||||
|
||||
get_server_route_handler(manifest.server_routes),
|
||||
|
||||
get_page_handler(manifest, session || noop)
|
||||
].filter(Boolean));
|
||||
}
|
||||
|
||||
export function compose_handlers(handlers: Handler[]) {
|
||||
return (req: Req, res: Res, next: () => void) => {
|
||||
let i = 0;
|
||||
function go() {
|
||||
const handler = handlers[i];
|
||||
export function compose_handlers(ignore: any, handlers: Handler[]): Handler {
|
||||
const total = handlers.length;
|
||||
|
||||
if (handler) {
|
||||
handler(req, res, () => {
|
||||
i += 1;
|
||||
go();
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
function nth_handler(n: number, req: Req, res: Res, next: () => void) {
|
||||
if (n >= total) {
|
||||
return next();
|
||||
}
|
||||
|
||||
go();
|
||||
};
|
||||
handlers[n](req, res, () => nth_handler(n+1, req, res, next));
|
||||
}
|
||||
|
||||
return !ignore
|
||||
? (req, res, next) => nth_handler(0, req, res, next)
|
||||
: (req, res, next) => {
|
||||
if (should_ignore(req.path, ignore)) {
|
||||
next();
|
||||
} else {
|
||||
nth_handler(0, req, res, next);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function should_ignore(uri: string, val: any) {
|
||||
@@ -100,42 +98,4 @@ export function should_ignore(uri: string, val: any) {
|
||||
return uri.startsWith(val.charCodeAt(0) === 47 ? val : `/${val}`);
|
||||
}
|
||||
|
||||
export function serve({ prefix, pathname, cache_control }: {
|
||||
prefix?: string,
|
||||
pathname?: string,
|
||||
cache_control: string
|
||||
}) {
|
||||
const filter = pathname
|
||||
? (req: Req) => req.path === pathname
|
||||
: (req: Req) => req.path.startsWith(prefix);
|
||||
|
||||
const cache: Map<string, Buffer> = new Map();
|
||||
|
||||
const read = dev
|
||||
? (file: string) => fs.readFileSync(path.resolve(build_dir, file))
|
||||
: (file: string) => (cache.has(file) ? cache : cache.set(file, fs.readFileSync(path.resolve(build_dir, file)))).get(file)
|
||||
|
||||
return (req: Req, res: Res, next: () => void) => {
|
||||
if (req[IGNORE]) return next();
|
||||
|
||||
if (filter(req)) {
|
||||
const type = lookup(req.path);
|
||||
|
||||
try {
|
||||
const file = decodeURIComponent(req.path.slice(1));
|
||||
const data = read(file);
|
||||
|
||||
res.setHeader('Content-Type', type);
|
||||
res.setHeader('Cache-Control', cache_control);
|
||||
res.end(data);
|
||||
} catch (err) {
|
||||
res.statusCode = 404;
|
||||
res.end('not found');
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function noop(){}
|
||||
|
||||
@@ -1,767 +0,0 @@
|
||||
application/andrew-inset ez
|
||||
application/applixware aw
|
||||
application/atom+xml atom
|
||||
application/atomcat+xml atomcat
|
||||
application/atomsvc+xml atomsvc
|
||||
application/ccxml+xml ccxml
|
||||
application/cdmi-capability cdmia
|
||||
application/cdmi-container cdmic
|
||||
application/cdmi-domain cdmid
|
||||
application/cdmi-object cdmio
|
||||
application/cdmi-queue cdmiq
|
||||
application/cu-seeme cu
|
||||
application/davmount+xml davmount
|
||||
application/docbook+xml dbk
|
||||
application/dssc+der dssc
|
||||
application/dssc+xml xdssc
|
||||
application/ecmascript ecma
|
||||
application/emma+xml emma
|
||||
application/epub+zip epub
|
||||
application/exi exi
|
||||
application/font-tdpfr pfr
|
||||
application/gml+xml gml
|
||||
application/gpx+xml gpx
|
||||
application/gxf gxf
|
||||
application/hyperstudio stk
|
||||
application/inkml+xml ink inkml
|
||||
application/ipfix ipfix
|
||||
application/java-archive jar
|
||||
application/java-serialized-object ser
|
||||
application/java-vm class
|
||||
application/javascript js
|
||||
application/json json map
|
||||
application/jsonml+json jsonml
|
||||
application/lost+xml lostxml
|
||||
application/mac-binhex40 hqx
|
||||
application/mac-compactpro cpt
|
||||
application/mads+xml mads
|
||||
application/marc mrc
|
||||
application/marcxml+xml mrcx
|
||||
application/mathematica ma nb mb
|
||||
application/mathml+xml mathml
|
||||
application/mbox mbox
|
||||
application/mediaservercontrol+xml mscml
|
||||
application/metalink+xml metalink
|
||||
application/metalink4+xml meta4
|
||||
application/mets+xml mets
|
||||
application/mods+xml mods
|
||||
application/mp21 m21 mp21
|
||||
application/mp4 mp4s
|
||||
application/msword doc dot
|
||||
application/mxf mxf
|
||||
application/octet-stream bin dms lrf mar so dist distz pkg bpk dump elc deploy
|
||||
application/oda oda
|
||||
application/oebps-package+xml opf
|
||||
application/ogg ogx
|
||||
application/omdoc+xml omdoc
|
||||
application/onenote onetoc onetoc2 onetmp onepkg
|
||||
application/oxps oxps
|
||||
application/patch-ops-error+xml xer
|
||||
application/pdf pdf
|
||||
application/pgp-encrypted pgp
|
||||
application/pgp-signature asc sig
|
||||
application/pics-rules prf
|
||||
application/pkcs10 p10
|
||||
application/pkcs7-mime p7m p7c
|
||||
application/pkcs7-signature p7s
|
||||
application/pkcs8 p8
|
||||
application/pkix-attr-cert ac
|
||||
application/pkix-cert cer
|
||||
application/pkix-crl crl
|
||||
application/pkix-pkipath pkipath
|
||||
application/pkixcmp pki
|
||||
application/pls+xml pls
|
||||
application/postscript ai eps ps
|
||||
application/prs.cww cww
|
||||
application/pskc+xml pskcxml
|
||||
application/rdf+xml rdf
|
||||
application/reginfo+xml rif
|
||||
application/relax-ng-compact-syntax rnc
|
||||
application/resource-lists+xml rl
|
||||
application/resource-lists-diff+xml rld
|
||||
application/rls-services+xml rs
|
||||
application/rpki-ghostbusters gbr
|
||||
application/rpki-manifest mft
|
||||
application/rpki-roa roa
|
||||
application/rsd+xml rsd
|
||||
application/rss+xml rss
|
||||
application/rtf rtf
|
||||
application/sbml+xml sbml
|
||||
application/scvp-cv-request scq
|
||||
application/scvp-cv-response scs
|
||||
application/scvp-vp-request spq
|
||||
application/scvp-vp-response spp
|
||||
application/sdp sdp
|
||||
application/set-payment-initiation setpay
|
||||
application/set-registration-initiation setreg
|
||||
application/shf+xml shf
|
||||
application/smil+xml smi smil
|
||||
application/sparql-query rq
|
||||
application/sparql-results+xml srx
|
||||
application/srgs gram
|
||||
application/srgs+xml grxml
|
||||
application/sru+xml sru
|
||||
application/ssdl+xml ssdl
|
||||
application/ssml+xml ssml
|
||||
application/tei+xml tei teicorpus
|
||||
application/thraud+xml tfi
|
||||
application/timestamped-data tsd
|
||||
application/vnd.3gpp.pic-bw-large plb
|
||||
application/vnd.3gpp.pic-bw-small psb
|
||||
application/vnd.3gpp.pic-bw-var pvb
|
||||
application/vnd.3gpp2.tcap tcap
|
||||
application/vnd.3m.post-it-notes pwn
|
||||
application/vnd.accpac.simply.aso aso
|
||||
application/vnd.accpac.simply.imp imp
|
||||
application/vnd.acucobol acu
|
||||
application/vnd.acucorp atc acutc
|
||||
application/vnd.adobe.air-application-installer-package+zip air
|
||||
application/vnd.adobe.formscentral.fcdt fcdt
|
||||
application/vnd.adobe.fxp fxp fxpl
|
||||
application/vnd.adobe.xdp+xml xdp
|
||||
application/vnd.adobe.xfdf xfdf
|
||||
application/vnd.ahead.space ahead
|
||||
application/vnd.airzip.filesecure.azf azf
|
||||
application/vnd.airzip.filesecure.azs azs
|
||||
application/vnd.amazon.ebook azw
|
||||
application/vnd.americandynamics.acc acc
|
||||
application/vnd.amiga.ami ami
|
||||
application/vnd.android.package-archive apk
|
||||
application/vnd.anser-web-certificate-issue-initiation cii
|
||||
application/vnd.anser-web-funds-transfer-initiation fti
|
||||
application/vnd.antix.game-component atx
|
||||
application/vnd.apple.installer+xml mpkg
|
||||
application/vnd.apple.mpegurl m3u8
|
||||
application/vnd.aristanetworks.swi swi
|
||||
application/vnd.astraea-software.iota iota
|
||||
application/vnd.audiograph aep
|
||||
application/vnd.blueice.multipass mpm
|
||||
application/vnd.bmi bmi
|
||||
application/vnd.businessobjects rep
|
||||
application/vnd.chemdraw+xml cdxml
|
||||
application/vnd.chipnuts.karaoke-mmd mmd
|
||||
application/vnd.cinderella cdy
|
||||
application/vnd.claymore cla
|
||||
application/vnd.cloanto.rp9 rp9
|
||||
application/vnd.clonk.c4group c4g c4d c4f c4p c4u
|
||||
application/vnd.cluetrust.cartomobile-config c11amc
|
||||
application/vnd.cluetrust.cartomobile-config-pkg c11amz
|
||||
application/vnd.commonspace csp
|
||||
application/vnd.contact.cmsg cdbcmsg
|
||||
application/vnd.cosmocaller cmc
|
||||
application/vnd.crick.clicker clkx
|
||||
application/vnd.crick.clicker.keyboard clkk
|
||||
application/vnd.crick.clicker.palette clkp
|
||||
application/vnd.crick.clicker.template clkt
|
||||
application/vnd.crick.clicker.wordbank clkw
|
||||
application/vnd.criticaltools.wbs+xml wbs
|
||||
application/vnd.ctc-posml pml
|
||||
application/vnd.cups-ppd ppd
|
||||
application/vnd.curl.car car
|
||||
application/vnd.curl.pcurl pcurl
|
||||
application/vnd.dart dart
|
||||
application/vnd.data-vision.rdz rdz
|
||||
application/vnd.dece.data uvf uvvf uvd uvvd
|
||||
application/vnd.dece.ttml+xml uvt uvvt
|
||||
application/vnd.dece.unspecified uvx uvvx
|
||||
application/vnd.dece.zip uvz uvvz
|
||||
application/vnd.denovo.fcselayout-link fe_launch
|
||||
application/vnd.dna dna
|
||||
application/vnd.dolby.mlp mlp
|
||||
application/vnd.dpgraph dpg
|
||||
application/vnd.dreamfactory dfac
|
||||
application/vnd.ds-keypoint kpxx
|
||||
application/vnd.dvb.ait ait
|
||||
application/vnd.dvb.service svc
|
||||
application/vnd.dynageo geo
|
||||
application/vnd.ecowin.chart mag
|
||||
application/vnd.enliven nml
|
||||
application/vnd.epson.esf esf
|
||||
application/vnd.epson.msf msf
|
||||
application/vnd.epson.quickanime qam
|
||||
application/vnd.epson.salt slt
|
||||
application/vnd.epson.ssf ssf
|
||||
application/vnd.eszigno3+xml es3 et3
|
||||
application/vnd.ezpix-album ez2
|
||||
application/vnd.ezpix-package ez3
|
||||
application/vnd.fdf fdf
|
||||
application/vnd.fdsn.mseed mseed
|
||||
application/vnd.fdsn.seed seed dataless
|
||||
application/vnd.flographit gph
|
||||
application/vnd.fluxtime.clip ftc
|
||||
application/vnd.framemaker fm frame maker book
|
||||
application/vnd.frogans.fnc fnc
|
||||
application/vnd.frogans.ltf ltf
|
||||
application/vnd.fsc.weblaunch fsc
|
||||
application/vnd.fujitsu.oasys oas
|
||||
application/vnd.fujitsu.oasys2 oa2
|
||||
application/vnd.fujitsu.oasys3 oa3
|
||||
application/vnd.fujitsu.oasysgp fg5
|
||||
application/vnd.fujitsu.oasysprs bh2
|
||||
application/vnd.fujixerox.ddd ddd
|
||||
application/vnd.fujixerox.docuworks xdw
|
||||
application/vnd.fujixerox.docuworks.binder xbd
|
||||
application/vnd.fuzzysheet fzs
|
||||
application/vnd.genomatix.tuxedo txd
|
||||
application/vnd.geogebra.file ggb
|
||||
application/vnd.geogebra.tool ggt
|
||||
application/vnd.geometry-explorer gex gre
|
||||
application/vnd.geonext gxt
|
||||
application/vnd.geoplan g2w
|
||||
application/vnd.geospace g3w
|
||||
application/vnd.gmx gmx
|
||||
application/vnd.google-earth.kml+xml kml
|
||||
application/vnd.google-earth.kmz kmz
|
||||
application/vnd.grafeq gqf gqs
|
||||
application/vnd.groove-account gac
|
||||
application/vnd.groove-help ghf
|
||||
application/vnd.groove-identity-message gim
|
||||
application/vnd.groove-injector grv
|
||||
application/vnd.groove-tool-message gtm
|
||||
application/vnd.groove-tool-template tpl
|
||||
application/vnd.groove-vcard vcg
|
||||
application/vnd.hal+xml hal
|
||||
application/vnd.handheld-entertainment+xml zmm
|
||||
application/vnd.hbci hbci
|
||||
application/vnd.hhe.lesson-player les
|
||||
application/vnd.hp-hpgl hpgl
|
||||
application/vnd.hp-hpid hpid
|
||||
application/vnd.hp-hps hps
|
||||
application/vnd.hp-jlyt jlt
|
||||
application/vnd.hp-pcl pcl
|
||||
application/vnd.hp-pclxl pclxl
|
||||
application/vnd.hydrostatix.sof-data sfd-hdstx
|
||||
application/vnd.ibm.minipay mpy
|
||||
application/vnd.ibm.modcap afp listafp list3820
|
||||
application/vnd.ibm.rights-management irm
|
||||
application/vnd.ibm.secure-container sc
|
||||
application/vnd.iccprofile icc icm
|
||||
application/vnd.igloader igl
|
||||
application/vnd.immervision-ivp ivp
|
||||
application/vnd.immervision-ivu ivu
|
||||
application/vnd.insors.igm igm
|
||||
application/vnd.intercon.formnet xpw xpx
|
||||
application/vnd.intergeo i2g
|
||||
application/vnd.intu.qbo qbo
|
||||
application/vnd.intu.qfx qfx
|
||||
application/vnd.ipunplugged.rcprofile rcprofile
|
||||
application/vnd.irepository.package+xml irp
|
||||
application/vnd.is-xpr xpr
|
||||
application/vnd.isac.fcs fcs
|
||||
application/vnd.jam jam
|
||||
application/vnd.jcp.javame.midlet-rms rms
|
||||
application/vnd.jisp jisp
|
||||
application/vnd.joost.joda-archive joda
|
||||
application/vnd.kahootz ktz ktr
|
||||
application/vnd.kde.karbon karbon
|
||||
application/vnd.kde.kchart chrt
|
||||
application/vnd.kde.kformula kfo
|
||||
application/vnd.kde.kivio flw
|
||||
application/vnd.kde.kontour kon
|
||||
application/vnd.kde.kpresenter kpr kpt
|
||||
application/vnd.kde.kspread ksp
|
||||
application/vnd.kde.kword kwd kwt
|
||||
application/vnd.kenameaapp htke
|
||||
application/vnd.kidspiration kia
|
||||
application/vnd.kinar kne knp
|
||||
application/vnd.koan skp skd skt skm
|
||||
application/vnd.kodak-descriptor sse
|
||||
application/vnd.las.las+xml lasxml
|
||||
application/vnd.llamagraphics.life-balance.desktop lbd
|
||||
application/vnd.llamagraphics.life-balance.exchange+xml lbe
|
||||
application/vnd.lotus-1-2-3 123
|
||||
application/vnd.lotus-approach apr
|
||||
application/vnd.lotus-freelance pre
|
||||
application/vnd.lotus-notes nsf
|
||||
application/vnd.lotus-organizer org
|
||||
application/vnd.lotus-screencam scm
|
||||
application/vnd.lotus-wordpro lwp
|
||||
application/vnd.macports.portpkg portpkg
|
||||
application/vnd.mcd mcd
|
||||
application/vnd.medcalcdata mc1
|
||||
application/vnd.mediastation.cdkey cdkey
|
||||
application/vnd.mfer mwf
|
||||
application/vnd.mfmp mfm
|
||||
application/vnd.micrografx.flo flo
|
||||
application/vnd.micrografx.igx igx
|
||||
application/vnd.mif mif
|
||||
application/vnd.mobius.daf daf
|
||||
application/vnd.mobius.dis dis
|
||||
application/vnd.mobius.mbk mbk
|
||||
application/vnd.mobius.mqy mqy
|
||||
application/vnd.mobius.msl msl
|
||||
application/vnd.mobius.plc plc
|
||||
application/vnd.mobius.txf txf
|
||||
application/vnd.mophun.application mpn
|
||||
application/vnd.mophun.certificate mpc
|
||||
application/vnd.mozilla.xul+xml xul
|
||||
application/vnd.ms-artgalry cil
|
||||
application/vnd.ms-cab-compressed cab
|
||||
application/vnd.ms-excel xls xlm xla xlc xlt xlw
|
||||
application/vnd.ms-excel.addin.macroenabled.12 xlam
|
||||
application/vnd.ms-excel.sheet.binary.macroenabled.12 xlsb
|
||||
application/vnd.ms-excel.sheet.macroenabled.12 xlsm
|
||||
application/vnd.ms-excel.template.macroenabled.12 xltm
|
||||
application/vnd.ms-fontobject eot
|
||||
application/vnd.ms-htmlhelp chm
|
||||
application/vnd.ms-ims ims
|
||||
application/vnd.ms-lrm lrm
|
||||
application/vnd.ms-officetheme thmx
|
||||
application/vnd.ms-pki.seccat cat
|
||||
application/vnd.ms-pki.stl stl
|
||||
application/vnd.ms-powerpoint ppt pps pot
|
||||
application/vnd.ms-powerpoint.addin.macroenabled.12 ppam
|
||||
application/vnd.ms-powerpoint.presentation.macroenabled.12 pptm
|
||||
application/vnd.ms-powerpoint.slide.macroenabled.12 sldm
|
||||
application/vnd.ms-powerpoint.slideshow.macroenabled.12 ppsm
|
||||
application/vnd.ms-powerpoint.template.macroenabled.12 potm
|
||||
application/vnd.ms-project mpp mpt
|
||||
application/vnd.ms-word.document.macroenabled.12 docm
|
||||
application/vnd.ms-word.template.macroenabled.12 dotm
|
||||
application/vnd.ms-works wps wks wcm wdb
|
||||
application/vnd.ms-wpl wpl
|
||||
application/vnd.ms-xpsdocument xps
|
||||
application/vnd.mseq mseq
|
||||
application/vnd.musician mus
|
||||
application/vnd.muvee.style msty
|
||||
application/vnd.mynfc taglet
|
||||
application/vnd.neurolanguage.nlu nlu
|
||||
application/vnd.nitf ntf nitf
|
||||
application/vnd.noblenet-directory nnd
|
||||
application/vnd.noblenet-sealer nns
|
||||
application/vnd.noblenet-web nnw
|
||||
application/vnd.nokia.n-gage.data ngdat
|
||||
application/vnd.nokia.n-gage.symbian.install n-gage
|
||||
application/vnd.nokia.radio-preset rpst
|
||||
application/vnd.nokia.radio-presets rpss
|
||||
application/vnd.novadigm.edm edm
|
||||
application/vnd.novadigm.edx edx
|
||||
application/vnd.novadigm.ext ext
|
||||
application/vnd.oasis.opendocument.chart odc
|
||||
application/vnd.oasis.opendocument.chart-template otc
|
||||
application/vnd.oasis.opendocument.database odb
|
||||
application/vnd.oasis.opendocument.formula odf
|
||||
application/vnd.oasis.opendocument.formula-template odft
|
||||
application/vnd.oasis.opendocument.graphics odg
|
||||
application/vnd.oasis.opendocument.graphics-template otg
|
||||
application/vnd.oasis.opendocument.image odi
|
||||
application/vnd.oasis.opendocument.image-template oti
|
||||
application/vnd.oasis.opendocument.presentation odp
|
||||
application/vnd.oasis.opendocument.presentation-template otp
|
||||
application/vnd.oasis.opendocument.spreadsheet ods
|
||||
application/vnd.oasis.opendocument.spreadsheet-template ots
|
||||
application/vnd.oasis.opendocument.text odt
|
||||
application/vnd.oasis.opendocument.text-master odm
|
||||
application/vnd.oasis.opendocument.text-template ott
|
||||
application/vnd.oasis.opendocument.text-web oth
|
||||
application/vnd.olpc-sugar xo
|
||||
application/vnd.oma.dd2+xml dd2
|
||||
application/vnd.openofficeorg.extension oxt
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx
|
||||
application/vnd.openxmlformats-officedocument.presentationml.slide sldx
|
||||
application/vnd.openxmlformats-officedocument.presentationml.slideshow ppsx
|
||||
application/vnd.openxmlformats-officedocument.presentationml.template potx
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.template xltx
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.template dotx
|
||||
application/vnd.osgeo.mapguide.package mgp
|
||||
application/vnd.osgi.dp dp
|
||||
application/vnd.osgi.subsystem esa
|
||||
application/vnd.palm pdb pqa oprc
|
||||
application/vnd.pawaafile paw
|
||||
application/vnd.pg.format str
|
||||
application/vnd.pg.osasli ei6
|
||||
application/vnd.picsel efif
|
||||
application/vnd.pmi.widget wg
|
||||
application/vnd.pocketlearn plf
|
||||
application/vnd.powerbuilder6 pbd
|
||||
application/vnd.previewsystems.box box
|
||||
application/vnd.proteus.magazine mgz
|
||||
application/vnd.publishare-delta-tree qps
|
||||
application/vnd.pvi.ptid1 ptid
|
||||
application/vnd.quark.quarkxpress qxd qxt qwd qwt qxl qxb
|
||||
application/vnd.realvnc.bed bed
|
||||
application/vnd.recordare.musicxml mxl
|
||||
application/vnd.recordare.musicxml+xml musicxml
|
||||
application/vnd.rig.cryptonote cryptonote
|
||||
application/vnd.rim.cod cod
|
||||
application/vnd.rn-realmedia rm
|
||||
application/vnd.rn-realmedia-vbr rmvb
|
||||
application/vnd.route66.link66+xml link66
|
||||
application/vnd.sailingtracker.track st
|
||||
application/vnd.seemail see
|
||||
application/vnd.sema sema
|
||||
application/vnd.semd semd
|
||||
application/vnd.semf semf
|
||||
application/vnd.shana.informed.formdata ifm
|
||||
application/vnd.shana.informed.formtemplate itp
|
||||
application/vnd.shana.informed.interchange iif
|
||||
application/vnd.shana.informed.package ipk
|
||||
application/vnd.simtech-mindmapper twd twds
|
||||
application/vnd.smaf mmf
|
||||
application/vnd.smart.teacher teacher
|
||||
application/vnd.solent.sdkm+xml sdkm sdkd
|
||||
application/vnd.spotfire.dxp dxp
|
||||
application/vnd.spotfire.sfs sfs
|
||||
application/vnd.stardivision.calc sdc
|
||||
application/vnd.stardivision.draw sda
|
||||
application/vnd.stardivision.impress sdd
|
||||
application/vnd.stardivision.math smf
|
||||
application/vnd.stardivision.writer sdw vor
|
||||
application/vnd.stardivision.writer-global sgl
|
||||
application/vnd.stepmania.package smzip
|
||||
application/vnd.stepmania.stepchart sm
|
||||
application/vnd.sun.xml.calc sxc
|
||||
application/vnd.sun.xml.calc.template stc
|
||||
application/vnd.sun.xml.draw sxd
|
||||
application/vnd.sun.xml.draw.template std
|
||||
application/vnd.sun.xml.impress sxi
|
||||
application/vnd.sun.xml.impress.template sti
|
||||
application/vnd.sun.xml.math sxm
|
||||
application/vnd.sun.xml.writer sxw
|
||||
application/vnd.sun.xml.writer.global sxg
|
||||
application/vnd.sun.xml.writer.template stw
|
||||
application/vnd.sus-calendar sus susp
|
||||
application/vnd.svd svd
|
||||
application/vnd.symbian.install sis sisx
|
||||
application/vnd.syncml+xml xsm
|
||||
application/vnd.syncml.dm+wbxml bdm
|
||||
application/vnd.syncml.dm+xml xdm
|
||||
application/vnd.tao.intent-module-archive tao
|
||||
application/vnd.tcpdump.pcap pcap cap dmp
|
||||
application/vnd.tmobile-livetv tmo
|
||||
application/vnd.trid.tpt tpt
|
||||
application/vnd.triscape.mxs mxs
|
||||
application/vnd.trueapp tra
|
||||
application/vnd.ufdl ufd ufdl
|
||||
application/vnd.uiq.theme utz
|
||||
application/vnd.umajin umj
|
||||
application/vnd.unity unityweb
|
||||
application/vnd.uoml+xml uoml
|
||||
application/vnd.vcx vcx
|
||||
application/vnd.visio vsd vst vss vsw
|
||||
application/vnd.visionary vis
|
||||
application/vnd.vsf vsf
|
||||
application/vnd.wap.wbxml wbxml
|
||||
application/vnd.wap.wmlc wmlc
|
||||
application/vnd.wap.wmlscriptc wmlsc
|
||||
application/vnd.webturbo wtb
|
||||
application/vnd.wolfram.player nbp
|
||||
application/vnd.wordperfect wpd
|
||||
application/vnd.wqd wqd
|
||||
application/vnd.wt.stf stf
|
||||
application/vnd.xara xar
|
||||
application/vnd.xfdl xfdl
|
||||
application/vnd.yamaha.hv-dic hvd
|
||||
application/vnd.yamaha.hv-script hvs
|
||||
application/vnd.yamaha.hv-voice hvp
|
||||
application/vnd.yamaha.openscoreformat osf
|
||||
application/vnd.yamaha.openscoreformat.osfpvg+xml osfpvg
|
||||
application/vnd.yamaha.smaf-audio saf
|
||||
application/vnd.yamaha.smaf-phrase spf
|
||||
application/vnd.yellowriver-custom-menu cmp
|
||||
application/vnd.zul zir zirz
|
||||
application/vnd.zzazz.deck+xml zaz
|
||||
application/voicexml+xml vxml
|
||||
application/widget wgt
|
||||
application/winhlp hlp
|
||||
application/wsdl+xml wsdl
|
||||
application/wspolicy+xml wspolicy
|
||||
application/x-7z-compressed 7z
|
||||
application/x-abiword abw
|
||||
application/x-ace-compressed ace
|
||||
application/x-apple-diskimage dmg
|
||||
application/x-authorware-bin aab x32 u32 vox
|
||||
application/x-authorware-map aam
|
||||
application/x-authorware-seg aas
|
||||
application/x-bcpio bcpio
|
||||
application/x-bittorrent torrent
|
||||
application/x-blorb blb blorb
|
||||
application/x-bzip bz
|
||||
application/x-bzip2 bz2 boz
|
||||
application/x-cbr cbr cba cbt cbz cb7
|
||||
application/x-cdlink vcd
|
||||
application/x-cfs-compressed cfs
|
||||
application/x-chat chat
|
||||
application/x-chess-pgn pgn
|
||||
application/x-conference nsc
|
||||
application/x-cpio cpio
|
||||
application/x-csh csh
|
||||
application/x-debian-package deb udeb
|
||||
application/x-dgc-compressed dgc
|
||||
application/x-director dir dcr dxr cst cct cxt w3d fgd swa
|
||||
application/x-doom wad
|
||||
application/x-dtbncx+xml ncx
|
||||
application/x-dtbook+xml dtb
|
||||
application/x-dtbresource+xml res
|
||||
application/x-dvi dvi
|
||||
application/x-envoy evy
|
||||
application/x-eva eva
|
||||
application/x-font-bdf bdf
|
||||
application/x-font-ghostscript gsf
|
||||
application/x-font-linux-psf psf
|
||||
application/x-font-pcf pcf
|
||||
application/x-font-snf snf
|
||||
application/x-font-type1 pfa pfb pfm afm
|
||||
application/x-freearc arc
|
||||
application/x-futuresplash spl
|
||||
application/x-gca-compressed gca
|
||||
application/x-glulx ulx
|
||||
application/x-gnumeric gnumeric
|
||||
application/x-gramps-xml gramps
|
||||
application/x-gtar gtar
|
||||
application/x-hdf hdf
|
||||
application/x-install-instructions install
|
||||
application/x-iso9660-image iso
|
||||
application/x-java-jnlp-file jnlp
|
||||
application/x-latex latex
|
||||
application/x-lzh-compressed lzh lha
|
||||
application/x-mie mie
|
||||
application/x-mobipocket-ebook prc mobi
|
||||
application/x-ms-application application
|
||||
application/x-ms-shortcut lnk
|
||||
application/x-ms-wmd wmd
|
||||
application/x-ms-wmz wmz
|
||||
application/x-ms-xbap xbap
|
||||
application/x-msaccess mdb
|
||||
application/x-msbinder obd
|
||||
application/x-mscardfile crd
|
||||
application/x-msclip clp
|
||||
application/x-msdownload exe dll com bat msi
|
||||
application/x-msmediaview mvb m13 m14
|
||||
application/x-msmetafile wmf wmz emf emz
|
||||
application/x-msmoney mny
|
||||
application/x-mspublisher pub
|
||||
application/x-msschedule scd
|
||||
application/x-msterminal trm
|
||||
application/x-mswrite wri
|
||||
application/x-netcdf nc cdf
|
||||
application/x-nzb nzb
|
||||
application/x-pkcs12 p12 pfx
|
||||
application/x-pkcs7-certificates p7b spc
|
||||
application/x-pkcs7-certreqresp p7r
|
||||
application/x-rar-compressed rar
|
||||
application/x-research-info-systems ris
|
||||
application/x-sh sh
|
||||
application/x-shar shar
|
||||
application/x-shockwave-flash swf
|
||||
application/x-silverlight-app xap
|
||||
application/x-sql sql
|
||||
application/x-stuffit sit
|
||||
application/x-stuffitx sitx
|
||||
application/x-subrip srt
|
||||
application/x-sv4cpio sv4cpio
|
||||
application/x-sv4crc sv4crc
|
||||
application/x-t3vm-image t3
|
||||
application/x-tads gam
|
||||
application/x-tar tar
|
||||
application/x-tcl tcl
|
||||
application/x-tex tex
|
||||
application/x-tex-tfm tfm
|
||||
application/x-texinfo texinfo texi
|
||||
application/x-tgif obj
|
||||
application/x-ustar ustar
|
||||
application/x-wais-source src
|
||||
application/x-x509-ca-cert der crt
|
||||
application/x-xfig fig
|
||||
application/x-xliff+xml xlf
|
||||
application/x-xpinstall xpi
|
||||
application/x-xz xz
|
||||
application/x-zmachine z1 z2 z3 z4 z5 z6 z7 z8
|
||||
application/xaml+xml xaml
|
||||
application/xcap-diff+xml xdf
|
||||
application/xenc+xml xenc
|
||||
application/xhtml+xml xhtml xht
|
||||
application/xml xml xsl
|
||||
application/xml-dtd dtd
|
||||
application/xop+xml xop
|
||||
application/xproc+xml xpl
|
||||
application/xslt+xml xslt
|
||||
application/xspf+xml xspf
|
||||
application/xv+xml mxml xhvml xvml xvm
|
||||
application/yang yang
|
||||
application/yin+xml yin
|
||||
application/zip zip
|
||||
audio/adpcm adp
|
||||
audio/basic au snd
|
||||
audio/midi mid midi kar rmi
|
||||
audio/mp4 m4a mp4a
|
||||
audio/mpeg mpga mp2 mp2a mp3 m2a m3a
|
||||
audio/ogg oga ogg spx
|
||||
audio/s3m s3m
|
||||
audio/silk sil
|
||||
audio/vnd.dece.audio uva uvva
|
||||
audio/vnd.digital-winds eol
|
||||
audio/vnd.dra dra
|
||||
audio/vnd.dts dts
|
||||
audio/vnd.dts.hd dtshd
|
||||
audio/vnd.lucent.voice lvp
|
||||
audio/vnd.ms-playready.media.pya pya
|
||||
audio/vnd.nuera.ecelp4800 ecelp4800
|
||||
audio/vnd.nuera.ecelp7470 ecelp7470
|
||||
audio/vnd.nuera.ecelp9600 ecelp9600
|
||||
audio/vnd.rip rip
|
||||
audio/webm weba
|
||||
audio/x-aac aac
|
||||
audio/x-aiff aif aiff aifc
|
||||
audio/x-caf caf
|
||||
audio/x-flac flac
|
||||
audio/x-matroska mka
|
||||
audio/x-mpegurl m3u
|
||||
audio/x-ms-wax wax
|
||||
audio/x-ms-wma wma
|
||||
audio/x-pn-realaudio ram ra
|
||||
audio/x-pn-realaudio-plugin rmp
|
||||
audio/x-wav wav
|
||||
audio/xm xm
|
||||
chemical/x-cdx cdx
|
||||
chemical/x-cif cif
|
||||
chemical/x-cmdf cmdf
|
||||
chemical/x-cml cml
|
||||
chemical/x-csml csml
|
||||
chemical/x-xyz xyz
|
||||
font/collection ttc
|
||||
font/otf otf
|
||||
font/ttf ttf
|
||||
font/woff woff
|
||||
font/woff2 woff2
|
||||
image/bmp bmp
|
||||
image/cgm cgm
|
||||
image/g3fax g3
|
||||
image/gif gif
|
||||
image/ief ief
|
||||
image/jpeg jpeg jpg jpe
|
||||
image/ktx ktx
|
||||
image/png png
|
||||
image/prs.btif btif
|
||||
image/sgi sgi
|
||||
image/svg+xml svg svgz
|
||||
image/tiff tiff tif
|
||||
image/vnd.adobe.photoshop psd
|
||||
image/vnd.dece.graphic uvi uvvi uvg uvvg
|
||||
image/vnd.djvu djvu djv
|
||||
image/vnd.dvb.subtitle sub
|
||||
image/vnd.dwg dwg
|
||||
image/vnd.dxf dxf
|
||||
image/vnd.fastbidsheet fbs
|
||||
image/vnd.fpx fpx
|
||||
image/vnd.fst fst
|
||||
image/vnd.fujixerox.edmics-mmr mmr
|
||||
image/vnd.fujixerox.edmics-rlc rlc
|
||||
image/vnd.ms-modi mdi
|
||||
image/vnd.ms-photo wdp
|
||||
image/vnd.net-fpx npx
|
||||
image/vnd.wap.wbmp wbmp
|
||||
image/vnd.xiff xif
|
||||
image/webp webp
|
||||
image/x-3ds 3ds
|
||||
image/x-cmu-raster ras
|
||||
image/x-cmx cmx
|
||||
image/x-freehand fh fhc fh4 fh5 fh7
|
||||
image/x-icon ico
|
||||
image/x-mrsid-image sid
|
||||
image/x-pcx pcx
|
||||
image/x-pict pic pct
|
||||
image/x-portable-anymap pnm
|
||||
image/x-portable-bitmap pbm
|
||||
image/x-portable-graymap pgm
|
||||
image/x-portable-pixmap ppm
|
||||
image/x-rgb rgb
|
||||
image/x-tga tga
|
||||
image/x-xbitmap xbm
|
||||
image/x-xpixmap xpm
|
||||
image/x-xwindowdump xwd
|
||||
message/rfc822 eml mime
|
||||
model/iges igs iges
|
||||
model/mesh msh mesh silo
|
||||
model/vnd.collada+xml dae
|
||||
model/vnd.dwf dwf
|
||||
model/vnd.gdl gdl
|
||||
model/vnd.gtw gtw
|
||||
model/vnd.mts mts
|
||||
model/vnd.vtu vtu
|
||||
model/vrml wrl vrml
|
||||
model/x3d+binary x3db x3dbz
|
||||
model/x3d+vrml x3dv x3dvz
|
||||
model/x3d+xml x3d x3dz
|
||||
text/cache-manifest appcache
|
||||
text/calendar ics ifb
|
||||
text/css css
|
||||
text/csv csv
|
||||
text/html html htm
|
||||
text/n3 n3
|
||||
text/plain txt text conf def list log in
|
||||
text/prs.lines.tag dsc
|
||||
text/richtext rtx
|
||||
text/sgml sgml sgm
|
||||
text/tab-separated-values tsv
|
||||
text/troff t tr roff man me ms
|
||||
text/turtle ttl
|
||||
text/uri-list uri uris urls
|
||||
text/vcard vcard
|
||||
text/vnd.curl curl
|
||||
text/vnd.curl.dcurl dcurl
|
||||
text/vnd.curl.mcurl mcurl
|
||||
text/vnd.curl.scurl scurl
|
||||
text/vnd.dvb.subtitle sub
|
||||
text/vnd.fly fly
|
||||
text/vnd.fmi.flexstor flx
|
||||
text/vnd.graphviz gv
|
||||
text/vnd.in3d.3dml 3dml
|
||||
text/vnd.in3d.spot spot
|
||||
text/vnd.sun.j2me.app-descriptor jad
|
||||
text/vnd.wap.wml wml
|
||||
text/vnd.wap.wmlscript wmls
|
||||
text/x-asm s asm
|
||||
text/x-c c cc cxx cpp h hh dic
|
||||
text/x-fortran f for f77 f90
|
||||
text/x-java-source java
|
||||
text/x-nfo nfo
|
||||
text/x-opml opml
|
||||
text/x-pascal p pas
|
||||
text/x-setext etx
|
||||
text/x-sfv sfv
|
||||
text/x-uuencode uu
|
||||
text/x-vcalendar vcs
|
||||
text/x-vcard vcf
|
||||
video/3gpp 3gp
|
||||
video/3gpp2 3g2
|
||||
video/h261 h261
|
||||
video/h263 h263
|
||||
video/h264 h264
|
||||
video/jpeg jpgv
|
||||
video/jpm jpm jpgm
|
||||
video/mj2 mj2 mjp2
|
||||
video/mp4 mp4 mp4v mpg4
|
||||
video/mpeg mpeg mpg mpe m1v m2v
|
||||
video/ogg ogv
|
||||
video/quicktime qt mov
|
||||
video/vnd.dece.hd uvh uvvh
|
||||
video/vnd.dece.mobile uvm uvvm
|
||||
video/vnd.dece.pd uvp uvvp
|
||||
video/vnd.dece.sd uvs uvvs
|
||||
video/vnd.dece.video uvv uvvv
|
||||
video/vnd.dvb.file dvb
|
||||
video/vnd.fvt fvt
|
||||
video/vnd.mpegurl mxu m4u
|
||||
video/vnd.ms-playready.media.pyv pyv
|
||||
video/vnd.uvvu.mp4 uvu uvvu
|
||||
video/vnd.vivo viv
|
||||
video/webm webm
|
||||
video/x-f4v f4v
|
||||
video/x-fli fli
|
||||
video/x-flv flv
|
||||
video/x-m4v m4v
|
||||
video/x-matroska mkv mk3d mks
|
||||
video/x-mng mng
|
||||
video/x-ms-asf asf asx
|
||||
video/x-ms-vob vob
|
||||
video/x-ms-wm wm
|
||||
video/x-ms-wmv wmv
|
||||
video/x-ms-wmx wmx
|
||||
video/x-ms-wvx wvx
|
||||
video/x-msvideo avi
|
||||
video/x-sgi-movie movie
|
||||
video/x-smv smv
|
||||
x-conference/x-cooltalk ice
|
||||
@@ -1,20 +0,0 @@
|
||||
import mime_raw from './mime-types.md';
|
||||
|
||||
const map: Map<string, string> = new Map();
|
||||
|
||||
mime_raw.split('\n').forEach((row: string) => {
|
||||
const match = /(.+?)\t+(.+)/.exec(row);
|
||||
if (!match) return;
|
||||
|
||||
const type = match[1];
|
||||
const extensions = match[2].split(' ');
|
||||
|
||||
extensions.forEach(ext => {
|
||||
map.set(ext, type);
|
||||
});
|
||||
});
|
||||
|
||||
export function lookup(file: string) {
|
||||
const match = /\.([^\.]+)$/.exec(file);
|
||||
return match && map.get(match[1]);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Stats } from 'fs';
|
||||
import { ClientRequest, ServerResponse } from 'http';
|
||||
|
||||
export type ServerRoute = {
|
||||
@@ -37,6 +38,18 @@ export type Props = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export interface MiddlewareOptions {
|
||||
static?: {
|
||||
headers?: (pathname: string, stats: Stats) => Record<string, string>
|
||||
},
|
||||
session?: (req: Req, res: Res) => any,
|
||||
ignore?: any
|
||||
}
|
||||
|
||||
export interface StartOptions extends MiddlewareOptions {
|
||||
port?: number;
|
||||
}
|
||||
|
||||
export interface Req extends ClientRequest {
|
||||
url: string;
|
||||
baseUrl: string;
|
||||
@@ -44,7 +57,7 @@ export interface Req extends ClientRequest {
|
||||
method: string;
|
||||
path: string;
|
||||
params: Record<string, string>;
|
||||
query: Record<string, string>;
|
||||
query: Record<string, string | string[]>;
|
||||
headers: Record<string, string>;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ You can also add a script to your package.json...
|
||||
|
||||
When you run `sapper export`, Sapper first builds a production version of your app, as though you had run `sapper build`, and copies the contents of your `assets` folder to the destination. It then starts the server, and navigates to the root of your app. From there, it follows any `<a>` elements it finds, and captures any data served by the app.
|
||||
|
||||
Because of this, any pages you want to be included in the exported site must be reachable by `<a>` elements. Additionally, any non-page routes should be requested in `preload`, *not* in `oncreate` or elsewhere.
|
||||
Because of this, any pages you want to be included in the exported site must be reachable by `<a>` elements. Additionally, any non-page routes should be requested in `preload`, *not* in `onMount` or elsewhere.
|
||||
|
||||
|
||||
### When not to export
|
||||
@@ -60,4 +60,4 @@ Because `sapper export` writes to the filesystem, it isn't possible to have two
|
||||
|
||||
The solution is to rename one of the routes to avoid conflict — for example, `src/routes/foo-bar.js`. (Note that you would also need to update any code that fetches data from `/foo/bar` to reference `/foo-bar` instead.)
|
||||
|
||||
For *pages*, we skirt around this problem by writing `export/foo/index.html` instead of `export/foo`.
|
||||
For *pages*, we skirt around this problem by writing `export/foo/index.html` instead of `export/foo`.
|
||||
|
||||
737
site/package-lock.json
generated
737
site/package-lock.json
generated
@@ -846,9 +846,9 @@
|
||||
}
|
||||
},
|
||||
"@sveltejs/site-kit": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-1.0.1.tgz",
|
||||
"integrity": "sha512-S5mZG2PkWGDXGZaIJQh5JtF7RLG2uIO36f9juUkUHhjqlz6FaBl2D4zyofmvGergSd6m9NgQ+iXLiFIK9WNmBg==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/site-kit/-/site-kit-1.0.4.tgz",
|
||||
"integrity": "sha512-BaQhIL1iPhCF+iDXfy9psDvRdFzfyMPkWnoZHfVz+INpHsU2aJmRZOPl9rykXmPyiPo+AwTTNK5vjIvmtwHLPQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sindresorhus/slugify": "^0.9.1",
|
||||
@@ -862,9 +862,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "11.13.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.8.tgz",
|
||||
"integrity": "sha512-szA3x/3miL90ZJxUCzx9haNbK5/zmPieGraZEe4WI+3srN0eGLiT22NXeMHmyhNEopn+IrxqMc7wdVwvPl8meg==",
|
||||
"version": "11.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.10.tgz",
|
||||
"integrity": "sha512-leUNzbFTMX94TWaIKz8N15Chu55F9QSH+INKayQr5xpkasBQBRF3qQXfo3/dOnMU/dEIit+Y/SU8HyOjq++GwA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/resolve": {
|
||||
@@ -900,27 +900,6 @@
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
|
||||
"integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"micromatch": "^3.1.4",
|
||||
"normalize-path": "^2.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"normalize-path": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
|
||||
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"remove-trailing-separator": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"arr-diff": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
||||
@@ -969,12 +948,6 @@
|
||||
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
|
||||
"dev": true
|
||||
},
|
||||
"async-each": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
|
||||
"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
|
||||
"dev": true
|
||||
},
|
||||
"atob": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
||||
@@ -1042,12 +1015,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
|
||||
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@@ -1088,14 +1055,14 @@
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.5.5",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.5.tgz",
|
||||
"integrity": "sha512-0QFO1r/2c792Ohkit5XI8Cm8pDtZxgNl2H6HU4mHrpYz7314pEYcsAVVatM0l/YmxPnEzh9VygXouj4gkFUTKA==",
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.5.6.tgz",
|
||||
"integrity": "sha512-o/hPOtbU9oX507lIqon+UvPYqpx3mHc8cV3QemSBTXwkG8gSQSK6UKvXcE/DcleU3+A59XTUHyCvZ5qGy8xVAg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30000960",
|
||||
"electron-to-chromium": "^1.3.124",
|
||||
"node-releases": "^1.1.14"
|
||||
"caniuse-lite": "^1.0.30000963",
|
||||
"electron-to-chromium": "^1.3.127",
|
||||
"node-releases": "^1.1.17"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
@@ -1143,9 +1110,9 @@
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30000963",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000963.tgz",
|
||||
"integrity": "sha512-n4HUiullc7Lw0LyzpeLa2ffP8KxFBGdxqD/8G3bSL6oB758hZ2UE2CVK+tQN958tJIi0/tfpjAc67aAtoHgnrQ==",
|
||||
"version": "1.0.30000967",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000967.tgz",
|
||||
"integrity": "sha512-rUBIbap+VJfxTzrM4akJ00lkvVb5/n5v3EGXfWzSH5zT8aJmGzjA8HWhJ4U6kCpzxozUSnB+yvAYDRPY6mRpgQ==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
@@ -1159,26 +1126,6 @@
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz",
|
||||
"integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"anymatch": "^2.0.0",
|
||||
"async-each": "^1.0.1",
|
||||
"braces": "^2.3.2",
|
||||
"fsevents": "^1.2.7",
|
||||
"glob-parent": "^3.1.0",
|
||||
"inherits": "^2.0.3",
|
||||
"is-binary-path": "^1.0.0",
|
||||
"is-glob": "^4.0.0",
|
||||
"normalize-path": "^3.0.0",
|
||||
"path-is-absolute": "^1.0.0",
|
||||
"readdirp": "^2.2.1",
|
||||
"upath": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"class-utils": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
||||
@@ -1432,9 +1379,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.127",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.127.tgz",
|
||||
"integrity": "sha512-1o25iFRf/dbgauTWalEzmD1EmRN3a2CzP/K7UVpYLEBduk96LF0FyUdCcf4Ry2mAWJ1VxyblFjC93q6qlLwA2A==",
|
||||
"version": "1.3.133",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.133.tgz",
|
||||
"integrity": "sha512-lyoC8aoqbbDqsprb6aPdt9n3DpOZZzdz/T4IZKsR0/dkZIxnJVUjjcpOSwA66jPRIOyDAamCTAUqweU05kKNSg==",
|
||||
"dev": true
|
||||
},
|
||||
"error-ex": {
|
||||
@@ -1648,535 +1595,6 @@
|
||||
"map-cache": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "1.2.9",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
|
||||
"integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "^2.12.1",
|
||||
"node-pre-gyp": "^0.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"abbrev": {
|
||||
"version": "1.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"delegates": "^1.0.0",
|
||||
"readable-stream": "^2.0.6"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"fs-minipass": {
|
||||
"version": "1.2.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minipass": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"aproba": "^1.0.3",
|
||||
"console-control-strings": "^1.0.0",
|
||||
"has-unicode": "^2.0.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"signal-exit": "^3.0.0",
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1",
|
||||
"wide-align": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
},
|
||||
"ignore-walk": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimatch": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"minizlib": {
|
||||
"version": "1.2.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minipass": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"needle": {
|
||||
"version": "2.3.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.0",
|
||||
"iconv-lite": "^0.4.4",
|
||||
"sax": "^1.2.4"
|
||||
}
|
||||
},
|
||||
"node-pre-gyp": {
|
||||
"version": "0.12.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"detect-libc": "^1.0.2",
|
||||
"mkdirp": "^0.5.1",
|
||||
"needle": "^2.2.1",
|
||||
"nopt": "^4.0.1",
|
||||
"npm-packlist": "^1.1.6",
|
||||
"npmlog": "^4.0.2",
|
||||
"rc": "^1.2.7",
|
||||
"rimraf": "^2.6.1",
|
||||
"semver": "^5.3.0",
|
||||
"tar": "^4"
|
||||
}
|
||||
},
|
||||
"nopt": {
|
||||
"version": "4.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"abbrev": "1",
|
||||
"osenv": "^0.1.4"
|
||||
}
|
||||
},
|
||||
"npm-bundled": {
|
||||
"version": "1.0.6",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"npm-packlist": {
|
||||
"version": "1.4.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ignore-walk": "^3.0.1",
|
||||
"npm-bundled": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"are-we-there-yet": "~1.1.2",
|
||||
"console-control-strings": "~1.1.0",
|
||||
"gauge": "~2.7.3",
|
||||
"set-blocking": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"os-homedir": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"osenv": {
|
||||
"version": "0.1.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"os-homedir": "^1.0.0",
|
||||
"os-tmpdir": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"deep-extend": "^0.6.0",
|
||||
"ini": "~1.3.0",
|
||||
"minimist": "^1.2.0",
|
||||
"strip-json-comments": "~2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
"strip-ansi": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"fs-minipass": "^1.2.5",
|
||||
"minipass": "^2.3.4",
|
||||
"minizlib": "^1.1.1",
|
||||
"mkdirp": "^0.5.0",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"string-width": "^1.0.2 || 2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
@@ -2189,27 +1607,6 @@
|
||||
"integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
|
||||
"dev": true
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
||||
"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-glob": "^3.1.0",
|
||||
"path-dirname": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-glob": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
|
||||
"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extglob": "^2.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
@@ -2377,15 +1774,6 @@
|
||||
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
|
||||
"dev": true
|
||||
},
|
||||
"is-binary-path": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
|
||||
"integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"binary-extensions": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
@@ -2449,21 +1837,6 @@
|
||||
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
|
||||
"dev": true
|
||||
},
|
||||
"is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||
"dev": true
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
|
||||
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extglob": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"is-module": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||
@@ -2778,13 +2151,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.13.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
|
||||
"integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"nanomatch": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
|
||||
@@ -2825,9 +2191,9 @@
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.17",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.17.tgz",
|
||||
"integrity": "sha512-/SCjetyta1m7YXLgtACZGDYJdCSIBAWorDWkGCGZlydP2Ll7J48l7j/JxNYZ+xsgSPbWfdulVS/aY+GdjUsQ7Q==",
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.18.tgz",
|
||||
"integrity": "sha512-/mnVgm6u/8OwlIsoyRXtTI0RfQcxZoAZbdwyXap0EeWwcOpDDymyCHM2/aR9XKmHXrvizHoPAOs0pcbiJ6RUaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"semver": "^5.3.0"
|
||||
@@ -2845,12 +2211,6 @@
|
||||
"validate-npm-package-license": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true
|
||||
},
|
||||
"npm-run-all": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
|
||||
@@ -2953,18 +2313,6 @@
|
||||
"integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
|
||||
"dev": true
|
||||
},
|
||||
"path-dirname": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
|
||||
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
|
||||
"dev": true
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"path-key": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
|
||||
@@ -3059,17 +2407,6 @@
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"readdirp": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
|
||||
"integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.11",
|
||||
"micromatch": "^3.1.10",
|
||||
"readable-stream": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"regenerate": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
|
||||
@@ -3159,12 +2496,6 @@
|
||||
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
|
||||
"dev": true
|
||||
},
|
||||
"remove-trailing-separator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
|
||||
"dev": true
|
||||
},
|
||||
"repeat-element": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
|
||||
@@ -3205,13 +2536,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"rollup": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-1.10.1.tgz",
|
||||
"integrity": "sha512-pW353tmBE7QP622ITkGxtqF0d5gSRCVPD9xqM+fcPjudeZfoXMFW2sCzsTe2TU/zU1xamIjiS9xuFCPVT9fESw==",
|
||||
"version": "1.11.3",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-1.11.3.tgz",
|
||||
"integrity": "sha512-81MR7alHcFKxgWzGfG7jSdv+JQxSOIOD/Fa3iNUmpzbd7p+V19e1l9uffqT8/7YAHgGOzmoPGN3Fx3L2ptOf5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/estree": "0.0.39",
|
||||
"@types/node": "^11.13.5",
|
||||
"@types/node": "^11.13.9",
|
||||
"acorn": "^6.1.1"
|
||||
}
|
||||
},
|
||||
@@ -3394,9 +2725,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"sirv": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-0.2.5.tgz",
|
||||
"integrity": "sha512-q7F1VElkj/WPrXwWsdHK9gqi2rd94oaMQc3VtN8N1TNHrKxlsd7hGsPFiOIodRxre8eHCV1XK1iqC1BDaZ+IKA==",
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-0.4.2.tgz",
|
||||
"integrity": "sha512-dQbZnsMaIiTQPZmbGmktz+c74zt/hyrJEB4tdp2Jj0RNv9J6B/OWR5RyrZEvIn9fyh9Zlg2OlE2XzKz6wMKGAw==",
|
||||
"requires": {
|
||||
"@polka/url": "^0.5.0",
|
||||
"mime": "^2.3.1"
|
||||
@@ -3662,9 +2993,9 @@
|
||||
}
|
||||
},
|
||||
"svelte": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.1.0.tgz",
|
||||
"integrity": "sha512-b5TyzV7Dx1ijN4QPNarhKq5rX98QHDmi18nF0G8KV3d5KX3Jj98Yu4+tzM97ktnXcfoVJmvONvPaX1ZI0mr8Dw==",
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.2.2.tgz",
|
||||
"integrity": "sha512-B6ePpPodCQodVFDwv/uByaWs0KvXojaXZiB/db1x7WXbyxgLJ8xZF3j1lk5M83Ox0AWlX0fQnCTAaZmfT6xqdw==",
|
||||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
@@ -3881,12 +3212,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"upath": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz",
|
||||
"integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"upper-case": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
|
||||
|
||||
@@ -15,30 +15,29 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@polka/send": "^0.4.0",
|
||||
"compression": "^1.7.1",
|
||||
"compression": "^1.7.4",
|
||||
"highlight.js": "^9.15.6",
|
||||
"marked": "^0.6.2",
|
||||
"polka": "^0.5.2",
|
||||
"prismjs": "^1.16.0",
|
||||
"sirv": "^0.2.0"
|
||||
"sirv": "^0.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/plugin-transform-runtime": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"@sveltejs/site-kit": "^1.0.1",
|
||||
"chokidar": "^2.0.4",
|
||||
"@babel/core": "^7.4.4",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/plugin-transform-runtime": "^7.4.4",
|
||||
"@babel/preset-env": "^7.4.4",
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@sveltejs/site-kit": "^1.0.4",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rollup": "^1.0.0",
|
||||
"rollup-plugin-babel": "^4.0.2",
|
||||
"rollup-plugin-commonjs": "^9.1.6",
|
||||
"rollup-plugin-node-resolve": "^4.0.0",
|
||||
"rollup-plugin-replace": "^2.0.0",
|
||||
"rollup-plugin-svelte": "^5.0.1",
|
||||
"rollup": "^1.11.3",
|
||||
"rollup-plugin-babel": "^4.3.2",
|
||||
"rollup-plugin-commonjs": "^9.3.4",
|
||||
"rollup-plugin-node-resolve": "^4.2.3",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"rollup-plugin-svelte": "^5.0.3",
|
||||
"rollup-plugin-terser": "^4.0.4",
|
||||
"sapper": "alpha",
|
||||
"svelte": "^3.1.0"
|
||||
"svelte": "^3.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,13 +49,16 @@
|
||||
|
||||
<div style="grid-area: start; display: flex; flex-direction: column; min-width: 0" slot="how">
|
||||
<pre class="language-bash" style="margin: 0 0 1em 0; min-width: 0; min-height: 0">
|
||||
npx degit sveltejs/template my-svelte-project
|
||||
cd my-svelte-project
|
||||
# for Rollup
|
||||
npx degit sveltejs/sapper-template#rollup my-app
|
||||
# for webpack
|
||||
npx degit sveltejs/sapper-template#webpack my-app
|
||||
cd my-app
|
||||
|
||||
npm install
|
||||
npm run dev & open http://localhost:5000
|
||||
npm run dev & open http://localhost:3000
|
||||
</pre>
|
||||
|
||||
<p class="cta"><a rel="prefetch" href="docs">Learn Sapper</a></p>
|
||||
</div>
|
||||
</Blurb>
|
||||
</Blurb>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<html lang='en' class="theme-default typo-default">
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width;initial-scale=1.0'>
|
||||
<meta name='theme-color' content='#ff3e00'>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1.0'>
|
||||
<meta name='theme-color' content='#159794'>
|
||||
|
||||
%sapper.base%
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#ff3e00",
|
||||
"theme_color": "#159794",
|
||||
"name": "Sapper",
|
||||
"short_name": "Sapper",
|
||||
"display": "minimal-ui",
|
||||
|
||||
@@ -122,7 +122,7 @@ export async function build({
|
||||
|
||||
const client_files = client_result.chunks
|
||||
.filter(chunk => !chunk.file.endsWith('.map')) // SW does not need to cache sourcemap files
|
||||
.map(chunk => `client/${chunk.file}`);
|
||||
.map(chunk => `sapper/${chunk.file}`);
|
||||
|
||||
create_serviceworker_manifest({
|
||||
manifest_data,
|
||||
|
||||
@@ -333,7 +333,7 @@ class Watcher extends EventEmitter {
|
||||
JSON.stringify(result.to_json(manifest_data, this.dirs), null, ' ')
|
||||
);
|
||||
|
||||
const client_files = result.chunks.map(chunk => `client/${chunk.file}`);
|
||||
const client_files = result.chunks.map(chunk => `sapper/${chunk.file}`);
|
||||
|
||||
create_serviceworker_manifest({
|
||||
manifest_data,
|
||||
|
||||
@@ -60,9 +60,8 @@ async function _export({
|
||||
rimraf(export_dir);
|
||||
|
||||
copy(static_files, export_dir);
|
||||
copy(path.join(build_dir, 'client'), path.join(export_dir, 'client'));
|
||||
copy(path.join(build_dir, 'service-worker.js'), path.join(export_dir, 'service-worker.js'));
|
||||
copy(path.join(build_dir, 'service-worker.js.map'), path.join(export_dir, 'service-worker.js.map'));
|
||||
copy(path.join(build_dir, 'client'), export_dir);
|
||||
copy(path.join(build_dir, 'service-worker'), export_dir);
|
||||
|
||||
const defaultPort = process.env.PORT ? parseInt(process.env.PORT) : 3000;
|
||||
const port = await ports.find(defaultPort);
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import * as fs from 'fs';
|
||||
import { mkdirp } from './fs_utils';
|
||||
|
||||
export function copy_shimport(dest: string) {
|
||||
mkdirp(`${dest}/client/sapper`);
|
||||
|
||||
const shimport_version = require('shimport/package.json').version;
|
||||
|
||||
fs.writeFileSync(
|
||||
`${dest}/client/shimport@${shimport_version}.js`,
|
||||
`${dest}/client/sapper/shimport@${shimport_version}.js`,
|
||||
fs.readFileSync(require.resolve('shimport/index.js'))
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export default {
|
||||
},
|
||||
|
||||
output: () => {
|
||||
let dir = `${dest}/client`;
|
||||
let dir = `${dest}/client/sapper`;
|
||||
if (process.env.SAPPER_LEGACY_BUILD) dir += `/legacy`;
|
||||
|
||||
return {
|
||||
@@ -45,7 +45,7 @@ export default {
|
||||
|
||||
output: () => {
|
||||
return {
|
||||
file: `${dest}/service-worker.js`,
|
||||
file: `${dest}/service-worker/service-worker.js`,
|
||||
format: 'iife'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ export default {
|
||||
|
||||
output: () => {
|
||||
return {
|
||||
path: `${dest}/client`,
|
||||
path: `${dest}/client/sapper`,
|
||||
filename: '[hash]/[name].js',
|
||||
chunkFilename: '[hash]/[name].[id].js',
|
||||
publicPath: `client/`
|
||||
publicPath: `sapper/`
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -46,7 +46,7 @@ export default {
|
||||
|
||||
output: () => {
|
||||
return {
|
||||
path: dest,
|
||||
path: `${dest}/service-worker`,
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[name].[id].[hash].js'
|
||||
}
|
||||
|
||||
@@ -251,11 +251,11 @@ function generate_app(manifest_data: ManifestData, path_to_routes: string) {
|
||||
|
||||
let l = max_depth;
|
||||
|
||||
let pyramid = `<svelte:component this={level${l}.component} {...level${l}.props}/>`;
|
||||
let pyramid = `<svelte:component this="{level${l}.component}" {...level${l}.props}/>`;
|
||||
|
||||
while (l-- > 1) {
|
||||
pyramid = `
|
||||
<svelte:component this={level${l}.component} segment={segments[${l}]} {...level${l}.props}>
|
||||
<svelte:component this="{level${l}.component}" segment="{segments[${l}]}" {...level${l}.props}>
|
||||
{#if level${l + 1}}
|
||||
${pyramid.replace(/\n/g, '\n\t\t\t\t\t')}
|
||||
{/if}
|
||||
@@ -281,7 +281,7 @@ function generate_app(manifest_data: ManifestData, path_to_routes: string) {
|
||||
setContext(CONTEXT_KEY, stores);
|
||||
</script>
|
||||
|
||||
<Layout segment={segments[0]} {...level0.props}>
|
||||
<Layout segment="{segments[0]}" {...level0.props}>
|
||||
{#if error}
|
||||
<Error {error} {status}/>
|
||||
{:else}
|
||||
|
||||
@@ -116,7 +116,7 @@ export default function extract_css(client_result: CompileResult, components: Pa
|
||||
|
||||
if (!client_result.css_files) return; // Rollup-only for now
|
||||
|
||||
let asset_dir = `${dirs.dest}/client`;
|
||||
let asset_dir = `${dirs.dest}/client/sapper`;
|
||||
if (process.env.SAPPER_LEGACY_BUILD) asset_dir += '/legacy';
|
||||
|
||||
const unclaimed = new Set(client_result.css_files.map(x => x.id));
|
||||
@@ -238,7 +238,7 @@ export default function extract_css(client_result: CompileResult, components: Pa
|
||||
map.file = output_file_name;
|
||||
map.sources = map.sources.map(source => path.relative(asset_dir, source));
|
||||
|
||||
fs.writeFileSync(`${asset_dir}/${output_file_name}`, `${code}\n/* sourceMappingURL=client/${output_file_name}.map */`);
|
||||
fs.writeFileSync(`${asset_dir}/${output_file_name}`, `${code}\n/* sourceMappingURL=sapper/${output_file_name}.map */`);
|
||||
fs.writeFileSync(`${asset_dir}/${output_file_name}.map`, JSON.stringify(map, null, ' '));
|
||||
|
||||
result.main = output_file_name;
|
||||
|
||||
@@ -25,6 +25,19 @@ export default function create_manifest_data(cwd: string): ManifestData {
|
||||
return false;
|
||||
}
|
||||
|
||||
function find_layout(file_name: string, component_name: string, dir: string = '') {
|
||||
const ext = component_extensions.find((ext) => fs.existsSync(path.join(cwd, dir, `${file_name}${ext}`)));
|
||||
const file = posixify(path.join(dir, `${file_name}${ext}`))
|
||||
|
||||
return ext
|
||||
? {
|
||||
name: component_name,
|
||||
file: file,
|
||||
has_preload: has_preload(file)
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
const components: PageComponent[] = [];
|
||||
const pages: Page[] = [];
|
||||
const server_routes: ServerRoute[] = [];
|
||||
@@ -61,15 +74,19 @@ export default function create_manifest_data(cwd: string): ManifestData {
|
||||
const is_dir = fs.statSync(resolved).isDirectory();
|
||||
|
||||
const ext = path.extname(basename);
|
||||
|
||||
if (basename[0] === '_') return null;
|
||||
if (basename[0] === '.' && basename !== '.well-known') return null;
|
||||
if (!is_dir && !/^\.[a-z]+$/i.test(ext)) return null; // filter out tmp files etc
|
||||
|
||||
const segment = is_dir
|
||||
? basename
|
||||
: basename.slice(0, -path.extname(basename).length);
|
||||
: basename.slice(0, -ext.length);
|
||||
|
||||
const parts = get_parts(segment);
|
||||
const is_index = is_dir ? false : basename.startsWith('index.');
|
||||
const is_page = component_extensions.indexOf(ext) !== -1;
|
||||
const route_suffix = basename.slice(basename.indexOf('.'), -ext.length);
|
||||
|
||||
parts.forEach(part => {
|
||||
if (/\]\[/.test(part.content)) {
|
||||
@@ -88,39 +105,35 @@ export default function create_manifest_data(cwd: string): ManifestData {
|
||||
file: posixify(file),
|
||||
is_dir,
|
||||
is_index,
|
||||
is_page
|
||||
is_page,
|
||||
route_suffix
|
||||
};
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort(comparator);
|
||||
|
||||
items.forEach(item => {
|
||||
if (item.basename[0] === '_') return;
|
||||
|
||||
if (item.basename[0] === '.') {
|
||||
if (item.file !== '.well-known') return;
|
||||
}
|
||||
|
||||
const segments = parent_segments.slice();
|
||||
|
||||
if (item.is_index && segments.length > 0) {
|
||||
const last_segment = segments[segments.length - 1].slice();
|
||||
const suffix = item.basename
|
||||
.slice(0, -path.extname(item.basename).length).
|
||||
replace('index', '');
|
||||
if (item.is_index) {
|
||||
if (item.route_suffix) {
|
||||
if (segments.length > 0) {
|
||||
const last_segment = segments[segments.length - 1].slice();
|
||||
const last_part = last_segment[last_segment.length - 1];
|
||||
|
||||
if (suffix) {
|
||||
const last_part = last_segment[last_segment.length - 1];
|
||||
if (last_part.dynamic) {
|
||||
last_segment.push({ dynamic: false, content: suffix });
|
||||
if (last_part.dynamic) {
|
||||
last_segment.push({ dynamic: false, content: item.route_suffix });
|
||||
} else {
|
||||
last_segment[last_segment.length - 1] = {
|
||||
dynamic: false,
|
||||
content: `${last_part.content}${item.route_suffix}`
|
||||
};
|
||||
}
|
||||
|
||||
segments[segments.length - 1] = last_segment;
|
||||
} else {
|
||||
last_segment[last_segment.length - 1] = {
|
||||
dynamic: false,
|
||||
content: `${last_part.content}${suffix}`
|
||||
};
|
||||
segments.push(item.parts);
|
||||
}
|
||||
|
||||
segments[segments.length - 1] = last_segment;
|
||||
}
|
||||
} else {
|
||||
segments.push(item.parts);
|
||||
@@ -130,16 +143,7 @@ export default function create_manifest_data(cwd: string): ManifestData {
|
||||
params.push(...item.parts.filter(p => p.dynamic).map(p => p.content));
|
||||
|
||||
if (item.is_dir) {
|
||||
const ext = component_extensions.find((ext: string) => {
|
||||
const index = path.join(dir, item.basename, `_layout${ext}`);
|
||||
return fs.existsSync(index);
|
||||
});
|
||||
|
||||
const component = ext && {
|
||||
name: `${get_slug(item.file)}__layout`,
|
||||
file: `${item.file}/_layout${ext}`,
|
||||
has_preload: has_preload(`${item.file}/_layout${ext}`)
|
||||
};
|
||||
const component = find_layout('_layout', `${get_slug(item.file)}__layout`, item.file);
|
||||
|
||||
if (component) components.push(component);
|
||||
|
||||
@@ -154,8 +158,6 @@ export default function create_manifest_data(cwd: string): ManifestData {
|
||||
}
|
||||
|
||||
else if (item.is_page) {
|
||||
const is_index = item.basename === `index${item.ext}`;
|
||||
|
||||
const component = {
|
||||
name: get_slug(item.file),
|
||||
file: item.file,
|
||||
@@ -164,22 +166,20 @@ export default function create_manifest_data(cwd: string): ManifestData {
|
||||
|
||||
components.push(component);
|
||||
|
||||
const parts = (is_index && stack[stack.length - 1] === null)
|
||||
const parts = (item.is_index && stack[stack.length - 1] === null)
|
||||
? stack.slice(0, -1).concat({ component, params })
|
||||
: stack.concat({ component, params })
|
||||
|
||||
const page = {
|
||||
pattern: get_pattern(is_index ? parent_segments : segments, true),
|
||||
pages.push({
|
||||
pattern: get_pattern(segments, true),
|
||||
parts
|
||||
};
|
||||
|
||||
pages.push(page);
|
||||
});
|
||||
}
|
||||
|
||||
else {
|
||||
server_routes.push({
|
||||
name: `route_${get_slug(item.file)}`,
|
||||
pattern: get_pattern(segments, false),
|
||||
pattern: get_pattern(segments, !item.route_suffix),
|
||||
file: item.file,
|
||||
params: params
|
||||
});
|
||||
@@ -187,23 +187,8 @@ export default function create_manifest_data(cwd: string): ManifestData {
|
||||
});
|
||||
}
|
||||
|
||||
const root_ext = component_extensions.find(ext => fs.existsSync(path.join(cwd, `_layout${ext}`)));
|
||||
const root = root_ext
|
||||
? {
|
||||
name: 'main',
|
||||
file: `_layout${root_ext}`,
|
||||
has_preload: has_preload(`_layout${root_ext}`)
|
||||
}
|
||||
: default_layout;
|
||||
|
||||
const error_ext = component_extensions.find(ext => fs.existsSync(path.join(cwd, `_error${ext}`)));
|
||||
const error = error_ext
|
||||
? {
|
||||
name: 'error',
|
||||
file: `_error${error_ext}`,
|
||||
has_preload: has_preload(`_error${error_ext}`)
|
||||
}
|
||||
: default_error;
|
||||
const root = find_layout('_layout', 'main') || default_layout;
|
||||
const error = find_layout('_error', 'error') || default_error;
|
||||
|
||||
walk(cwd, [], [], []);
|
||||
|
||||
@@ -249,7 +234,7 @@ type Part = {
|
||||
spread?: boolean;
|
||||
};
|
||||
|
||||
function is_spead(path: string) {
|
||||
function is_spread(path: string) {
|
||||
const spread_pattern = /\[\.{3}/g;
|
||||
return spread_pattern.test(path)
|
||||
}
|
||||
@@ -259,9 +244,9 @@ function comparator(
|
||||
b: { basename: string, parts: Part[], file: string, is_index: boolean }
|
||||
) {
|
||||
if (a.is_index !== b.is_index) {
|
||||
if (a.is_index) return is_spead(a.file) ? 1 : -1;
|
||||
if (a.is_index) return is_spread(a.file) ? 1 : -1;
|
||||
|
||||
return is_spead(b.file) ? -1 : 1;
|
||||
return is_spread(b.file) ? -1 : 1;
|
||||
}
|
||||
|
||||
const max = Math.max(a.parts.length, b.parts.length);
|
||||
@@ -334,7 +319,6 @@ function get_parts(part: string): Part[] {
|
||||
function get_slug(file: string) {
|
||||
let name = file
|
||||
.replace(/[\\\/]index/, '')
|
||||
.replace(/_default([\/\\index])?\.html$/, 'index')
|
||||
.replace(/[\/\\]/g, '_')
|
||||
.replace(/\.\w+$/, '')
|
||||
.replace(/\[([^(]+)(?:\([^(]+\))?\]/, '$$$1')
|
||||
@@ -347,19 +331,19 @@ function get_slug(file: string) {
|
||||
}
|
||||
|
||||
function get_pattern(segments: Part[][], add_trailing_slash: boolean) {
|
||||
return new RegExp(
|
||||
`^` +
|
||||
segments.map(segment => {
|
||||
return '\\/' + segment.map(part => {
|
||||
return part.dynamic
|
||||
? part.qualifier || part.spread ? '(.+)' : '([^\\/]+?)'
|
||||
: encodeURI(part.content.normalize())
|
||||
.replace(/\?/g, '%3F')
|
||||
.replace(/#/g, '%23')
|
||||
.replace(/%5B/g, '[')
|
||||
.replace(/%5D/g, ']');
|
||||
}).join('');
|
||||
}).join('') +
|
||||
(add_trailing_slash ? '\\\/?$' : '$')
|
||||
);
|
||||
const path = segments.map(segment => {
|
||||
return segment.map(part => {
|
||||
return part.dynamic
|
||||
? part.qualifier || part.spread ? '(.+)' : '([^\\/]+?)'
|
||||
: encodeURI(part.content.normalize())
|
||||
.replace(/\?/g, '%3F')
|
||||
.replace(/#/g, '%23')
|
||||
.replace(/%5B/g, '[')
|
||||
.replace(/%5D/g, ']');
|
||||
}).join('');
|
||||
}).join('\\/');
|
||||
|
||||
const trailing = add_trailing_slash && segments.length ? '\\/?$' : '$';
|
||||
|
||||
return new RegExp(`^\\/${path}${trailing}`);
|
||||
}
|
||||
|
||||
@@ -1,49 +1,77 @@
|
||||
import * as path from 'path';
|
||||
import puppeteer from 'puppeteer';
|
||||
import * as ports from 'port-authority';
|
||||
import { fork, ChildProcess } from 'child_process';
|
||||
import { AddressInfo } from 'net';
|
||||
|
||||
import { wait } from '../utils'
|
||||
|
||||
const DEFAULT_ENTRY = '__sapper__/build/server/server.js';
|
||||
const DELAY = parseInt(process.env.SAPPER_TEST_DELAY) || 50;
|
||||
|
||||
declare const start: () => Promise<void>;
|
||||
declare const prefetchRoutes: () => Promise<void>;
|
||||
declare const prefetch: (href: string) => Promise<void>;
|
||||
declare const goto: (href: string) => Promise<void>;
|
||||
|
||||
type StartOpts = {
|
||||
requestInterceptor?: (interceptedRequst: puppeteer.Request) => any
|
||||
};
|
||||
|
||||
export class AppRunner {
|
||||
cwd: string;
|
||||
entry: string;
|
||||
port: number;
|
||||
proc: ChildProcess;
|
||||
exiting: boolean;
|
||||
terminate: Promise<any>;
|
||||
|
||||
server: ChildProcess;
|
||||
address: AddressInfo;
|
||||
base: string;
|
||||
messages: any[];
|
||||
errors: Error[];
|
||||
|
||||
browser: puppeteer.Browser;
|
||||
page: puppeteer.Page;
|
||||
|
||||
constructor(cwd: string, entry: string) {
|
||||
this.cwd = cwd;
|
||||
this.entry = path.join(cwd, entry);
|
||||
sapper = {
|
||||
start: () => this.page.evaluate(() => start()).then(() => void 0),
|
||||
prefetchRoutes: () => this.page.evaluate(() => prefetchRoutes()).then(() => void 0),
|
||||
prefetch: (href: string) => this.page.evaluate((href: string) => prefetch(href), href).then(() => void 0),
|
||||
goto: (href: string) => this.page.evaluate((href: string) => goto(href), href).then(() => void 0)
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.messages = [];
|
||||
this.errors = [];
|
||||
}
|
||||
|
||||
async start({ requestInterceptor }: StartOpts = {}) {
|
||||
this.port = await ports.find(3000);
|
||||
async start(cwd: string, entry: string = DEFAULT_ENTRY) {
|
||||
const server_listening = deferred();
|
||||
const server_closed = deferred();
|
||||
const browser_closed = deferred();
|
||||
|
||||
this.proc = fork(this.entry, [], {
|
||||
cwd: this.cwd,
|
||||
env: {
|
||||
PORT: String(this.port)
|
||||
this.terminate = Promise.all([server_closed, browser_closed]);
|
||||
|
||||
this.server = fork(path.join(cwd, entry), [], { cwd });
|
||||
this.server.on('exit', () => {
|
||||
server_listening.reject();
|
||||
server_closed.settle(this.exiting);
|
||||
});
|
||||
this.server.on('message', message => {
|
||||
if (!message.__sapper__) return;
|
||||
|
||||
switch (message.event) {
|
||||
case 'listening':
|
||||
this.address = message.address;
|
||||
this.base = `http://localhost:${this.address.port}`;
|
||||
|
||||
server_listening.resolve();
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
this.errors.push(Object.assign(new Error(), message.error));
|
||||
break;
|
||||
|
||||
default:
|
||||
this.messages.push(message);
|
||||
}
|
||||
});
|
||||
|
||||
this.proc.on('message', message => {
|
||||
if (!message.__sapper__) return;
|
||||
this.messages.push(message);
|
||||
});
|
||||
|
||||
this.browser = await puppeteer.launch({ args: ['--no-sandbox'] });
|
||||
this.browser.on('disconnected', () => browser_closed.settle(this.exiting));
|
||||
|
||||
this.page = await this.browser.newPage();
|
||||
this.page.on('console', msg => {
|
||||
@@ -54,25 +82,28 @@ export class AppRunner {
|
||||
}
|
||||
});
|
||||
|
||||
if (requestInterceptor) {
|
||||
await this.page.setRequestInterception(true);
|
||||
this.page.on('request', requestInterceptor);
|
||||
}
|
||||
await server_listening;
|
||||
|
||||
return {
|
||||
page: this.page,
|
||||
base: `http://localhost:${this.port}`,
|
||||
|
||||
// helpers
|
||||
start: () => this.page.evaluate(() => start()).then(() => void 0),
|
||||
prefetchRoutes: () => this.page.evaluate(() => prefetchRoutes()).then(() => void 0),
|
||||
prefetch: (href: string) => this.page.evaluate((href: string) => prefetch(href), href).then(() => void 0),
|
||||
goto: (href: string) => this.page.evaluate((href: string) => goto(href), href).then(() => void 0),
|
||||
title: () => this.page.$eval('h1', node => node.textContent).then(serializable => String(serializable))
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
capture(fn: () => any): Promise<string[]> {
|
||||
load(url: string) {
|
||||
if (url[0] === '/') {
|
||||
url = `${this.base}${url}`;
|
||||
}
|
||||
|
||||
return this.page.goto(url);
|
||||
}
|
||||
|
||||
text(selector: string) {
|
||||
return this.page.$eval(selector, node => node.textContent);
|
||||
}
|
||||
|
||||
wait(extra_ms: number = 0) {
|
||||
return wait(DELAY + extra_ms);
|
||||
}
|
||||
|
||||
capture_requests(fn: () => any): Promise<string[]> {
|
||||
return new Promise((fulfil, reject) => {
|
||||
const requests: string[] = [];
|
||||
const pending: Set<string> = new Set();
|
||||
@@ -120,13 +151,55 @@ export class AppRunner {
|
||||
});
|
||||
}
|
||||
|
||||
end() {
|
||||
return Promise.all([
|
||||
this.browser.close(),
|
||||
new Promise(fulfil => {
|
||||
this.proc.once('exit', fulfil);
|
||||
this.proc.kill();
|
||||
})
|
||||
]);
|
||||
async intercept_requests(interceptor: (request: puppeteer.Request) => void, fn: () => any): Promise<void> {
|
||||
const unique_interceptor = request => interceptor(request);
|
||||
|
||||
this.page.prependListener('request', unique_interceptor);
|
||||
await this.page.setRequestInterception(true);
|
||||
|
||||
const result = await Promise.resolve(fn());
|
||||
|
||||
await this.page.setRequestInterception(false);
|
||||
this.page.removeListener('request', unique_interceptor);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
end() {
|
||||
this.exiting = true;
|
||||
|
||||
this.server.kill();
|
||||
this.browser.close();
|
||||
|
||||
return this.terminate;
|
||||
}
|
||||
}
|
||||
|
||||
interface Deferred<T> extends Promise<T> {
|
||||
resolve: (value?: T | PromiseLike<T>) => void;
|
||||
reject: (reason?: any) => void;
|
||||
settle: (result?: boolean) => void;
|
||||
}
|
||||
|
||||
function settle<T>(this: Deferred<T>, result: boolean) {
|
||||
if (result) {
|
||||
this.resolve();
|
||||
} else {
|
||||
this.reject();
|
||||
}
|
||||
}
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve, reject;
|
||||
|
||||
const deferred = new Promise((_resolve, _reject) => {
|
||||
resolve = _resolve;
|
||||
reject = _reject;
|
||||
}) as Deferred<T>;
|
||||
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
deferred.settle = settle;
|
||||
|
||||
return deferred;
|
||||
}
|
||||
|
||||
14
test/apps/basics/src/routes/skipped/[one]/[two].svelte
Normal file
14
test/apps/basics/src/routes/skipped/[one]/[two].svelte
Normal file
@@ -0,0 +1,14 @@
|
||||
<script context="module">
|
||||
export function preload({ params }) {
|
||||
return params;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export let one;
|
||||
export let two;
|
||||
</script>
|
||||
|
||||
<h1>{one}:{two}</h1>
|
||||
|
||||
<a href="skipped/y/1">skipped/y/1</a>
|
||||
@@ -1,8 +1,6 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT } = process.env;
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
start(server);
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import * as http from 'http';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
declare let deleted: { id: number };
|
||||
declare let el: any;
|
||||
|
||||
function get(url: string, opts?: any): Promise<{ headers: Record<string, string>, body: string }> {
|
||||
type Response = { headers: http.IncomingHttpHeaders, body: string };
|
||||
|
||||
function get(url: string, opts: http.RequestOptions = {}): Promise<Response> {
|
||||
return new Promise((fulfil, reject) => {
|
||||
const req = http.get(url, opts || {}, res => {
|
||||
const req = http.get(url, opts, res => {
|
||||
res.on('error', reject);
|
||||
|
||||
let body = '';
|
||||
res.on('data', chunk => body += chunk);
|
||||
res.on('end', () => {
|
||||
fulfil({
|
||||
headers: res.headers as Record<string, string>,
|
||||
headers: res.headers,
|
||||
body
|
||||
});
|
||||
});
|
||||
@@ -30,114 +30,104 @@ function get(url: string, opts?: any): Promise<{ headers: Record<string, string>
|
||||
describe('basics', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let prefetch: (href: string) => Promise<void>;
|
||||
let goto: (href: string) => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, prefetch, goto, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('serves /', async () => {
|
||||
await page.goto(base);
|
||||
await r.load('/');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'Great success!'
|
||||
);
|
||||
});
|
||||
|
||||
it('serves /?', async () => {
|
||||
await page.goto(`${base}?`);
|
||||
await r.load('/?');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'Great success!'
|
||||
);
|
||||
});
|
||||
|
||||
it('serves static route', async () => {
|
||||
await page.goto(`${base}/a`);
|
||||
await r.load('/a');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'a'
|
||||
);
|
||||
});
|
||||
|
||||
it('serves static route from dir/index.html file', async () => {
|
||||
await page.goto(`${base}/b`);
|
||||
await r.load('/b');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'b'
|
||||
);
|
||||
});
|
||||
|
||||
it('serves dynamic route', async () => {
|
||||
await page.goto(`${base}/test-slug`);
|
||||
await r.load('/test-slug');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'TEST-SLUG'
|
||||
);
|
||||
});
|
||||
|
||||
it('navigates to a new page without reloading', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
const requests: string[] = await runner.capture(async () => {
|
||||
await page.click('a[href="a"]');
|
||||
const requests: string[] = await r.capture_requests(async () => {
|
||||
await r.page.click('a[href="a"]');
|
||||
await r.wait();
|
||||
});
|
||||
|
||||
assert.deepEqual(requests, []);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'a'
|
||||
);
|
||||
});
|
||||
|
||||
it('navigates programmatically', async () => {
|
||||
await page.goto(`${base}/a`);
|
||||
await start();
|
||||
|
||||
await goto('b');
|
||||
await r.load('/a');
|
||||
await r.sapper.start();
|
||||
await r.sapper.goto('b');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'b'
|
||||
);
|
||||
});
|
||||
|
||||
it('prefetches programmatically', async () => {
|
||||
await page.goto(`${base}/a`);
|
||||
await start();
|
||||
await r.load(`/a`);
|
||||
await r.sapper.start();
|
||||
|
||||
const requests = await runner.capture(() => prefetch('b'));
|
||||
const requests = await r.capture_requests(() => r.sapper.prefetch('b'));
|
||||
|
||||
assert.equal(requests.length, 2);
|
||||
assert.equal(requests[1], `${base}/b.json`);
|
||||
assert.equal(requests[1], `${r.base}/b.json`);
|
||||
});
|
||||
|
||||
// TODO equivalent test for a webpack app
|
||||
it('sets Content-Type, Link...modulepreload, and Cache-Control headers', async () => {
|
||||
const { headers } = await get(base);
|
||||
const { headers } = await get(r.base);
|
||||
|
||||
assert.equal(
|
||||
headers['content-type'],
|
||||
@@ -150,169 +140,170 @@ describe('basics', function() {
|
||||
);
|
||||
|
||||
// TODO preload more than just the entry point
|
||||
const regex = /<\/client\/client\.\w+\.js>;rel="modulepreload"/;
|
||||
const regex = /<\/sapper\/client\.\w+\.js>;rel="modulepreload"/;
|
||||
const link = <string>headers['link'];
|
||||
|
||||
assert.ok(regex.test(link), link);
|
||||
});
|
||||
|
||||
it('calls a delete handler', async () => {
|
||||
await page.goto(`${base}/delete-test`);
|
||||
await start();
|
||||
await r.load('/delete-test');
|
||||
await r.sapper.start();
|
||||
|
||||
await page.click('.del');
|
||||
await page.waitForFunction(() => deleted);
|
||||
await r.page.click('.del');
|
||||
await r.page.waitForFunction(() => deleted);
|
||||
|
||||
assert.equal(await page.evaluate(() => deleted.id), 42);
|
||||
assert.equal(await r.page.evaluate(() => deleted.id), 42);
|
||||
});
|
||||
|
||||
it('hydrates initial route', async () => {
|
||||
await page.goto(base);
|
||||
await r.load('/');
|
||||
|
||||
await page.evaluate(() => {
|
||||
await r.page.evaluate(() => {
|
||||
el = document.querySelector('.hydrate-test');
|
||||
});
|
||||
|
||||
await start();
|
||||
await r.sapper.start();
|
||||
|
||||
assert.ok(await page.evaluate(() => {
|
||||
assert.ok(await r.page.evaluate(() => {
|
||||
return document.querySelector('.hydrate-test') === el;
|
||||
}));
|
||||
});
|
||||
|
||||
it('does not attempt client-side navigation to server routes', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click(`[href="ambiguous/ok.json"]`);
|
||||
await wait(50);
|
||||
await r.page.click('[href="ambiguous/ok.json"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.body.textContent),
|
||||
await r.text('body'),
|
||||
'ok'
|
||||
);
|
||||
});
|
||||
|
||||
it('allows reserved words as route names', async () => {
|
||||
await page.goto(`${base}/const`);
|
||||
await start();
|
||||
await r.load('/const');
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'reserved words are okay as routes'
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts value-less query string parameter on server', async () => {
|
||||
await page.goto(`${base}/echo-query?message`);
|
||||
await r.load('/echo-query?message');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'{"message":""}'
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts value-less query string parameter on client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a[href="echo-query?message"]')
|
||||
await r.page.click('a[href="echo-query?message"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'{"message":""}'
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts duplicated query string parameter on server', async () => {
|
||||
await page.goto(`${base}/echo-query?p=one&p=two`);
|
||||
await r.load('/echo-query?p=one&p=two');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'{"p":["one","two"]}'
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts duplicated query string parameter on client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a[href="echo-query?p=one&p=two"]')
|
||||
await r.page.click('a[href="echo-query?p=one&p=two"]')
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'{"p":["one","two"]}'
|
||||
);
|
||||
});
|
||||
|
||||
// skipped because Nightmare doesn't seem to focus the <a> correctly
|
||||
it('resets the active element after navigation', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="a"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="a"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.activeElement.nodeName),
|
||||
await r.page.evaluate(() => document.activeElement.nodeName),
|
||||
'BODY'
|
||||
);
|
||||
});
|
||||
|
||||
it('replaces %sapper.xxx% tags safely', async () => {
|
||||
await page.goto(`${base}/unsafe-replacement`);
|
||||
await start();
|
||||
await r.load('/unsafe-replacement');
|
||||
await r.sapper.start();
|
||||
|
||||
const html = String(await page.evaluate(() => document.body.innerHTML));
|
||||
const html = String(await r.page.evaluate(() => document.body.innerHTML));
|
||||
assert.equal(html.indexOf('%sapper'), -1);
|
||||
});
|
||||
|
||||
it('navigates between routes with empty parts', async () => {
|
||||
await page.goto(`${base}/dirs/foo`);
|
||||
await start();
|
||||
await r.load('/dirs/foo');
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(await title(), 'foo');
|
||||
assert.equal(await r.text('h1'), 'foo');
|
||||
|
||||
await page.click('[href="dirs/bar"]');
|
||||
await wait(50);
|
||||
assert.equal(await title(), 'bar');
|
||||
await r.page.click('[href="dirs/bar"]');
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), 'bar');
|
||||
});
|
||||
|
||||
it('navigates to ...rest', async () => {
|
||||
await page.goto(`${base}/abc/xyz`);
|
||||
await start();
|
||||
it('navigates to ...rest', async () => {
|
||||
await r.load('/abc/xyz');
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(await title(), 'abc,xyz');
|
||||
assert.equal(await r.text('h1'), 'abc,xyz');
|
||||
|
||||
await page.click('[href="xyz/abc/deep"]');
|
||||
await wait(50);
|
||||
assert.equal(await title(), 'xyz,abc');
|
||||
await r.page.click('[href="xyz/abc/deep"]');
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), 'xyz,abc');
|
||||
|
||||
await page.click(`[href="xyz/abc/qwe/deep.json"]`);
|
||||
await wait(50);
|
||||
await r.page.click('[href="xyz/abc/qwe/deep.json"]');
|
||||
await r.wait();
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.body.textContent),
|
||||
await r.text('body'),
|
||||
'xyz,abc,qwe'
|
||||
);
|
||||
});
|
||||
|
||||
it('navigates between dynamic routes with same segments', async () => {
|
||||
await page.goto(`${base}/dirs/bar/xyz`);
|
||||
await start();
|
||||
|
||||
assert.equal(await title(), 'A page');
|
||||
|
||||
await page.click('[href="dirs/foo/xyz"]');
|
||||
await wait(50);
|
||||
assert.equal(await title(), 'B page');
|
||||
});
|
||||
|
||||
|
||||
it('navigates between dynamic routes with same segments', async () => {
|
||||
await r.load('/dirs/bar/xyz');
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(await r.text('h1'), 'A page');
|
||||
|
||||
await r.page.click('[href="dirs/foo/xyz"]');
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), 'B page');
|
||||
});
|
||||
|
||||
it('runs server route handlers before page handlers, if they match', async () => {
|
||||
const json = await get(`${base}/middleware`, {
|
||||
const json = await get(`${r.base}/middleware`, {
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
@@ -320,8 +311,26 @@ describe('basics', function() {
|
||||
|
||||
assert.equal(json.body, '{"json":true}');
|
||||
|
||||
const html = await get(`${base}/middleware`);
|
||||
const html = await get(`${r.base}/middleware`);
|
||||
|
||||
assert.ok(html.body.indexOf('<h1>HTML</h1>') !== -1);
|
||||
});
|
||||
|
||||
it('invalidates page when a segment is skipped', async () => {
|
||||
await r.load('/skipped/x/1');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await r.page.click('a[href="skipped/y/1"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await r.text('h1'),
|
||||
'y:1'
|
||||
);
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
43
test/apps/common.js
Normal file
43
test/apps/common.js
Normal file
@@ -0,0 +1,43 @@
|
||||
const { NODE_ENV, PORT } = process.env;
|
||||
|
||||
export const dev = NODE_ENV === 'development';
|
||||
|
||||
export function start(server) {
|
||||
const port = parseInt(PORT) || 0;
|
||||
|
||||
server.listen(port, () => {
|
||||
server = server.server || server;
|
||||
const address = server.address();
|
||||
|
||||
process.env.PORT = address.port;
|
||||
|
||||
send({
|
||||
__sapper__: true,
|
||||
event: 'listening',
|
||||
address
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const properties = ['name', 'message', 'stack', 'code', 'lineNumber', 'fileName'];
|
||||
|
||||
function send(message) {
|
||||
process.send && process.send(message);
|
||||
}
|
||||
|
||||
function send_error(error) {
|
||||
send({
|
||||
__sapper__: true,
|
||||
event: 'error',
|
||||
error: properties.reduce((object, key) => ({...object, [key]: error[key]}), {})
|
||||
})
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
send_error(reason);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', err => {
|
||||
send_error(err);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -1,13 +1,14 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
const app = polka()
|
||||
.use((req, res, next) => {
|
||||
// set test cookie
|
||||
res.setHeader('Set-Cookie', ['a=1; Max-Age=3600', 'b=2; Max-Age=3600']);
|
||||
next();
|
||||
})
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,59 +1,54 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { wait } from '../../utils';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
|
||||
describe('credentials', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('sends cookies when using this.fetch with credentials: "include"', async () => {
|
||||
await page.goto(`${base}/credentials?creds=include`);
|
||||
await r.load('/credentials?creds=include');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'a: 1, b: 2, max-age: undefined'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not send cookies when using this.fetch without credentials', async () => {
|
||||
await page.goto(`${base}/credentials`);
|
||||
await r.load('/credentials');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'unauthorized'
|
||||
);
|
||||
});
|
||||
|
||||
it('delegates to fetch on the client', async () => {
|
||||
await page.goto(base)
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/')
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="credentials?creds=include"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="credentials?creds=include"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'a: 1, b: 2, max-age: undefined'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT } = process.env;
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
start(server);
|
||||
|
||||
@@ -1,78 +1,61 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
describe('css', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let prefetch: (href: string) => Promise<void>;
|
||||
let goto: (href: string) => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, prefetch, goto, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('includes critical CSS with server render', async () => {
|
||||
await page.goto(base);
|
||||
await r.load('/');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => {
|
||||
const h1 = document.querySelector('h1');
|
||||
return getComputedStyle(h1).color;
|
||||
}),
|
||||
await r.page.$eval('h1', node => getComputedStyle(node).color),
|
||||
'rgb(255, 0, 0)'
|
||||
);
|
||||
});
|
||||
|
||||
it('loads CSS when navigating client-side', async () => {
|
||||
await page.goto(base);
|
||||
await r.load('/');
|
||||
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click(`[href="foo"]`);
|
||||
await wait(50);
|
||||
await r.page.click(`[href="foo"]`);
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => {
|
||||
const h1 = document.querySelector('h1');
|
||||
return getComputedStyle(h1).color;
|
||||
}),
|
||||
await r.page.$eval('h1', node => getComputedStyle(node).color),
|
||||
'rgb(0, 0, 255)'
|
||||
);
|
||||
});
|
||||
|
||||
it('loads CSS for a lazily-rendered component', async () => {
|
||||
await page.goto(base);
|
||||
await r.load('/');
|
||||
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click(`[href="bar"]`);
|
||||
await wait(50);
|
||||
await r.page.click(`[href="bar"]`);
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => {
|
||||
const h1 = document.querySelector('h1');
|
||||
return getComputedStyle(h1).color;
|
||||
}),
|
||||
await r.page.$eval('h1', node => getComputedStyle(node).color),
|
||||
'rgb(0, 128, 0)'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT } = process.env;
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
start(server);
|
||||
|
||||
@@ -1,68 +1,63 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
describe('encoding', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('encodes routes', async () => {
|
||||
await page.goto(`${base}/fünke`);
|
||||
await r.load('/fünke');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
`I'm afraid I just blue myself`
|
||||
);
|
||||
});
|
||||
|
||||
it('encodes req.params and req.query for server-rendered pages', async () => {
|
||||
await page.goto(`${base}/echo/page/encöded?message=hëllö+wörld&föo=bar&=baz&tel=%2B123456789`);
|
||||
await r.load('/echo/page/encöded?message=hëllö+wörld&föo=bar&=baz&tel=%2B123456789');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'encöded {"message":"hëllö wörld","föo":"bar","":"baz","tel":"+123456789"}'
|
||||
);
|
||||
});
|
||||
|
||||
it('encodes req.params and req.query for client-rendered pages', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a');
|
||||
await wait(50);
|
||||
await r.page.click('a');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'encöded {"message":"hëllö wörld","föo":"bar","":"baz","tel":"+123456789"}'
|
||||
);
|
||||
});
|
||||
|
||||
it('encodes req.params for server routes', async () => {
|
||||
await page.goto(`${base}/echo/server-route/encöded`);
|
||||
await r.load('/echo/server-route/encöded');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'encöded'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
<h1>{error ? error.message : 'No error here'}</h1>
|
||||
|
||||
<script>
|
||||
export let error;
|
||||
</script>
|
||||
|
||||
<h1>{error ? error.message : 'No error here'}</h1>
|
||||
|
||||
<a href="enhance-your-calm">Enhance your calm</a>
|
||||
@@ -1,8 +1,6 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT } = process.env;
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
start(server);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
@@ -7,142 +6,137 @@ import { wait } from '../../utils';
|
||||
describe('errors', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('handles missing route on server', async () => {
|
||||
await page.goto(`${base}/nope`);
|
||||
await r.load('/nope');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'404'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles missing route on client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
|
||||
await page.click('[href="nope"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="nope"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'404'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles explicit 4xx on server', async () => {
|
||||
await page.goto(`${base}/blog/nope`);
|
||||
await r.load('/blog/nope');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'404'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles explicit 4xx on client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="blog/nope"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="blog/nope"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'404'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles error on server', async () => {
|
||||
await page.goto(`${base}/throw`);
|
||||
await r.load('/throw');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'500'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles error on client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="throw"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="throw"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'500'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not serve error page for explicit non-page errors', async () => {
|
||||
await page.goto(`${base}/nope.json`);
|
||||
await r.load('/nope.json');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.body.textContent),
|
||||
await r.text('body'),
|
||||
'nope'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not serve error page for thrown non-page errors', async () => {
|
||||
await page.goto(`${base}/throw.json`);
|
||||
await r.load('/throw.json');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.body.textContent),
|
||||
await r.text('body'),
|
||||
'oops'
|
||||
);
|
||||
});
|
||||
|
||||
it('execute error page hooks', async () => {
|
||||
await page.goto(`${base}/some-throw-page`);
|
||||
await start();
|
||||
await wait(50);
|
||||
await r.load('/some-throw-page');
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h2', node => node.textContent),
|
||||
await r.text('h2'),
|
||||
'success'
|
||||
);
|
||||
})
|
||||
|
||||
it('does not serve error page for async non-page error', async () => {
|
||||
await page.goto(`${base}/async-throw.json`);
|
||||
await r.load('/async-throw.json');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.body.textContent),
|
||||
await r.text('body'),
|
||||
'oops'
|
||||
);
|
||||
});
|
||||
|
||||
it('clears props.error on successful render', async () => {
|
||||
await page.goto(`${base}/no-error`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/no-error');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="enhance-your-calm"]');
|
||||
await wait(50);
|
||||
assert.equal(await title(), '420');
|
||||
await r.page.click('[href="enhance-your-calm"]');
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), '420');
|
||||
|
||||
await page.goBack();
|
||||
await wait(50);
|
||||
assert.equal(await title(), 'No error here');
|
||||
await r.page.goBack();
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), 'No error here');
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import sirv from 'sirv';
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT, NODE_ENV } = process.env;
|
||||
const dev = NODE_ENV === 'development';
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(
|
||||
sirv('static', { dev }),
|
||||
sapper.middleware()
|
||||
)
|
||||
.listen(PORT, err => {
|
||||
if (err) console.log('error', err);
|
||||
});
|
||||
start(server);
|
||||
|
||||
@@ -6,14 +6,12 @@ describe('export-webpack', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await api.build({ cwd: __dirname, bundler: 'webpack' });
|
||||
await api.export({ cwd: __dirname, bundler: 'webpack' });
|
||||
});
|
||||
before('build app', () => api.build({ cwd: __dirname, bundler: 'webpack' }));
|
||||
before('export app', () => api.export({ cwd: __dirname }));
|
||||
|
||||
// tests
|
||||
it('injects <link rel=preload> tags', () => {
|
||||
const index = fs.readFileSync(`${__dirname}/__sapper__/export/index.html`, 'utf8');
|
||||
assert.ok(/rel=preload/.test(index));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import sirv from 'sirv';
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT, NODE_ENV } = process.env;
|
||||
const dev = NODE_ENV === 'development';
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(
|
||||
sirv('static', { dev }),
|
||||
sapper.middleware()
|
||||
)
|
||||
.listen(PORT, err => {
|
||||
if (err) console.log('error', err);
|
||||
});
|
||||
start(server);
|
||||
|
||||
@@ -6,16 +6,15 @@ describe('export', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await api.build({ cwd: __dirname });
|
||||
await api.export({ cwd: __dirname });
|
||||
});
|
||||
before('build app', () => api.build({ cwd: __dirname }));
|
||||
before('export app', () => api.export({ cwd: __dirname }));
|
||||
|
||||
// tests
|
||||
it('crawls a site', () => {
|
||||
const files = walk(`${__dirname}/__sapper__/export`);
|
||||
|
||||
const client_assets = files.filter(file => file.startsWith('client/'));
|
||||
const non_client_assets = files.filter(file => !file.startsWith('client/')).sort();
|
||||
const client_assets = files.filter(file => file.startsWith('sapper/'));
|
||||
const non_client_assets = files.filter(file => !file.startsWith('sapper/')).sort();
|
||||
|
||||
assert.ok(client_assets.length > 0);
|
||||
|
||||
@@ -45,4 +44,4 @@ describe('export', function() {
|
||||
});
|
||||
|
||||
// TODO test timeout, basepath
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
<h1>{status}</h1>
|
||||
|
||||
<script>
|
||||
export let status;
|
||||
export let error;
|
||||
</script>
|
||||
|
||||
<h1>{status}</h1>
|
||||
|
||||
<p>{error.message}</p>
|
||||
@@ -1,7 +1,7 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const app = polka().use(sapper.middleware({
|
||||
ignore: [
|
||||
@@ -16,4 +16,4 @@ const app = polka().use(sapper.middleware({
|
||||
app.get('/'+uri, (req, res) => res.end(uri));
|
||||
});
|
||||
|
||||
app.listen(PORT);
|
||||
start(app);
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
|
||||
describe('ignore', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('respects `options.ignore` values (RegExp)', async () => {
|
||||
await page.goto(`${base}/foobar`);
|
||||
await r.load('/foobar');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.documentElement.textContent),
|
||||
await r.text('body'),
|
||||
'foobar'
|
||||
);
|
||||
});
|
||||
|
||||
it('respects `options.ignore` values (String #1)', async () => {
|
||||
await page.goto(`${base}/buzz`);
|
||||
await r.load('/buzz');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.documentElement.textContent),
|
||||
await r.text('body'),
|
||||
'buzz'
|
||||
);
|
||||
});
|
||||
|
||||
it('respects `options.ignore` values (String #2)', async () => {
|
||||
await page.goto(`${base}/fizzer`);
|
||||
await r.load('/fizzer');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.documentElement.textContent),
|
||||
await r.text('body'),
|
||||
'fizzer'
|
||||
);
|
||||
});
|
||||
|
||||
it('respects `options.ignore` values (Function)', async () => {
|
||||
await page.goto(`${base}/hello`);
|
||||
await r.load('/hello');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.documentElement.textContent),
|
||||
await r.text('body'),
|
||||
'hello'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT } = process.env;
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
start(server);
|
||||
|
||||
@@ -1,33 +1,25 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
describe('layout', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('only recreates components when necessary', async () => {
|
||||
await page.goto(`${base}/foo/bar/baz`);
|
||||
await r.load('/foo/bar/baz');
|
||||
|
||||
const text1 = String(await page.evaluate(() => document.querySelector('#sapper').textContent));
|
||||
const text1 = await r.text('#sapper');
|
||||
assert.deepEqual(text1.split('\n').map(str => str.trim()).filter(Boolean), [
|
||||
'y: bar 1',
|
||||
'z: baz 1',
|
||||
@@ -35,8 +27,8 @@ describe('layout', function() {
|
||||
'child segment: baz'
|
||||
]);
|
||||
|
||||
await start();
|
||||
const text2 = String(await page.evaluate(() => document.querySelector('#sapper').textContent));
|
||||
await r.sapper.start();
|
||||
const text2 = await r.text('#sapper');
|
||||
assert.deepEqual(text2.split('\n').map(str => str.trim()).filter(Boolean), [
|
||||
'y: bar 1',
|
||||
'z: baz 1',
|
||||
@@ -44,10 +36,10 @@ describe('layout', function() {
|
||||
'child segment: baz'
|
||||
]);
|
||||
|
||||
await page.click('[href="foo/bar/qux"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="foo/bar/qux"]');
|
||||
await r.wait();
|
||||
|
||||
const text3 = String(await page.evaluate(() => document.querySelector('#sapper').textContent));
|
||||
const text3 = await r.text('#sapper');
|
||||
assert.deepEqual(text3.split('\n').map(str => str.trim()).filter(Boolean), [
|
||||
'y: bar 1',
|
||||
'z: qux 2',
|
||||
@@ -55,4 +47,8 @@ describe('layout', function() {
|
||||
'child segment: qux'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT } = process.env;
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
start(server);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { wait } from '../../utils';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
|
||||
declare const fulfil: () => Promise<void>;
|
||||
@@ -9,112 +7,106 @@ declare const fulfil: () => Promise<void>;
|
||||
describe('preloading', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('serializes Set objects returned from preload', async () => {
|
||||
await page.goto(`${base}/preload-values/set`);
|
||||
await r.load('/preload-values/set');
|
||||
|
||||
assert.equal(await title(), 'true');
|
||||
assert.equal(await r.text('h1'), 'true');
|
||||
|
||||
await start();
|
||||
assert.equal(await title(), 'true');
|
||||
await r.sapper.start();
|
||||
assert.equal(await r.text('h1'), 'true');
|
||||
});
|
||||
|
||||
it('prevent crash if preload return nothing', async () => {
|
||||
await page.goto(`${base}/preload-nothing`);
|
||||
await r.load('/preload-nothing');
|
||||
|
||||
await start();
|
||||
await wait(50);
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(await title(), 'Page loaded');
|
||||
assert.equal(await r.text('h1'), 'Page loaded');
|
||||
});
|
||||
|
||||
it('bails on custom classes returned from preload', async () => {
|
||||
await page.goto(`${base}/preload-values/custom-class`);
|
||||
await r.load('/preload-values/custom-class');
|
||||
|
||||
assert.equal(await title(), '42');
|
||||
assert.equal(await r.text('h1'), '42');
|
||||
|
||||
await start();
|
||||
assert.equal(await title(), '42');
|
||||
await r.sapper.start();
|
||||
assert.equal(await r.text('h1'), '42');
|
||||
});
|
||||
|
||||
it('sets preloading true when appropriate', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a[href="slow-preload"]');
|
||||
await r.page.click('a[href="slow-preload"]');
|
||||
|
||||
assert.ok(await page.evaluate(() => !!document.querySelector('progress')));
|
||||
assert.ok(await r.page.evaluate(() => !!document.querySelector('progress')));
|
||||
|
||||
await page.evaluate(() => fulfil());
|
||||
assert.ok(await page.evaluate(() => !document.querySelector('progress')));
|
||||
await r.page.evaluate(() => fulfil());
|
||||
assert.ok(await r.page.evaluate(() => !document.querySelector('progress')));
|
||||
});
|
||||
|
||||
it('runs preload in root component', async () => {
|
||||
await page.goto(`${base}/preload-root`);
|
||||
assert.equal(await title(), 'root preload function ran: true');
|
||||
await r.load('/preload-root');
|
||||
assert.equal(await r.text('h1'), 'root preload function ran: true');
|
||||
});
|
||||
|
||||
it('cancels navigation if subsequent navigation occurs during preload', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a[href="slow-preload"]');
|
||||
await wait(100);
|
||||
await page.click('a[href="foo"]');
|
||||
await r.page.click('a[href="slow-preload"]');
|
||||
await r.wait();
|
||||
await r.page.click('a[href="foo"]');
|
||||
|
||||
assert.equal(page.url(), `${base}/foo`);
|
||||
assert.equal(await title(), 'foo');
|
||||
assert.equal(r.page.url(), `${r.base}/foo`);
|
||||
assert.equal(await r.text('h1'), 'foo');
|
||||
|
||||
await page.evaluate(() => fulfil());
|
||||
await wait(100);
|
||||
assert.equal(page.url(), `${base}/foo`);
|
||||
assert.equal(await title(), 'foo');
|
||||
await r.page.evaluate(() => fulfil());
|
||||
await r.wait();
|
||||
assert.equal(r.page.url(), `${r.base}/foo`);
|
||||
assert.equal(await r.text('h1'), 'foo');
|
||||
});
|
||||
|
||||
it('navigates to prefetched urls', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.hover('a[href="prefetch/qwe"]');
|
||||
await wait(100);
|
||||
await page.hover('a[href="prefetch/xyz"]');
|
||||
await wait(100);
|
||||
await r.page.hover('a[href="prefetch/qwe"]');
|
||||
await r.wait(50);
|
||||
await r.page.hover('a[href="prefetch/xyz"]');
|
||||
await r.wait(50);
|
||||
|
||||
await page.click('a[href="prefetch/qwe"]');
|
||||
await wait(50);
|
||||
await r.page.click('a[href="prefetch/qwe"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'qwe'
|
||||
);
|
||||
|
||||
await page.goto(`${base}/prefetch`);
|
||||
await wait(50);
|
||||
await r.load('/prefetch');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'prefetch'
|
||||
);
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT } = process.env;
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
start(server);
|
||||
|
||||
@@ -2,138 +2,137 @@ import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
describe('redirects', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, title } = await runner.start({
|
||||
requestInterceptor: (interceptedRequest) => {
|
||||
if (/example\.com/.test(interceptedRequest.url())) {
|
||||
interceptedRequest.respond({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
body: `<h1>external</h1>`
|
||||
});
|
||||
} else {
|
||||
interceptedRequest.continue();
|
||||
}
|
||||
}
|
||||
}));
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('redirects on server', async () => {
|
||||
await page.goto(`${base}/redirect-from`);
|
||||
await r.load('/redirect-from');
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/redirect-to`
|
||||
r.page.url(),
|
||||
`${r.base}/redirect-to`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'redirected'
|
||||
);
|
||||
});
|
||||
|
||||
it('redirects in client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="redirect-from"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="redirect-from"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/redirect-to`
|
||||
r.page.url(),
|
||||
`${r.base}/redirect-to`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'redirected'
|
||||
);
|
||||
});
|
||||
|
||||
it('redirects to root on server', async () => {
|
||||
await page.goto(`${base}/redirect-to-root`);
|
||||
await r.load('/redirect-to-root');
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/`
|
||||
r.page.url(),
|
||||
`${r.base}/`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'root'
|
||||
);
|
||||
});
|
||||
|
||||
it('redirects to root in client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="redirect-to-root"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="redirect-to-root"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/`
|
||||
r.page.url(),
|
||||
`${r.base}/`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'root'
|
||||
);
|
||||
});
|
||||
|
||||
const interceptor = (request: puppeteer.Request) => {
|
||||
if (/example\.com/.test(request.url())) {
|
||||
request.respond({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
body: `<h1>external</h1>`
|
||||
});
|
||||
} else {
|
||||
request.continue();
|
||||
}
|
||||
};
|
||||
|
||||
it('redirects to external URL on server', async () => {
|
||||
await page.goto(`${base}/redirect-to-external`);
|
||||
await r.intercept_requests(interceptor, async () => {
|
||||
await r.load('/redirect-to-external');
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
r.page.url(),
|
||||
`https://example.com/`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'external'
|
||||
);
|
||||
});
|
||||
|
||||
it('redirects to external URL in client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="redirect-to-external"]');
|
||||
await wait(50);
|
||||
await r.intercept_requests(interceptor, async () => {
|
||||
await r.page.click('[href="redirect-to-external"]');
|
||||
await r.wait();
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
r.page.url(),
|
||||
`https://example.com/`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'external'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT } = process.env;
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
start(server);
|
||||
|
||||
@@ -1,93 +1,87 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
describe('scroll', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('scrolls to active deeplink', async () => {
|
||||
await page.goto(`${base}/tall-page#foo`);
|
||||
await start();
|
||||
await r.load('/tall-page#foo');
|
||||
await r.sapper.start();
|
||||
|
||||
const scrollY = await page.evaluate(() => window.scrollY);
|
||||
const scrollY = await r.page.evaluate(() => window.scrollY);
|
||||
assert.ok(scrollY > 0, String(scrollY));
|
||||
});
|
||||
|
||||
it('scrolls to any deeplink if it was already active', async () => {
|
||||
await page.goto(`${base}/tall-page#foo`);
|
||||
await start();
|
||||
await r.load('/tall-page#foo');
|
||||
await r.sapper.start();
|
||||
|
||||
let scrollY = await page.evaluate(() => window.scrollY);
|
||||
let scrollY = await r.page.evaluate(() => window.scrollY);
|
||||
assert.ok(scrollY > 0, String(scrollY));
|
||||
|
||||
scrollY = await page.evaluate(() => {
|
||||
scrollY = await r.page.evaluate(() => {
|
||||
window.scrollTo(0, 0)
|
||||
return window.scrollY
|
||||
});
|
||||
assert.ok(scrollY === 0, String(scrollY));
|
||||
|
||||
await page.click('[href="tall-page#foo"]');
|
||||
scrollY = await page.evaluate(() => window.scrollY);
|
||||
await r.page.click('[href="tall-page#foo"]');
|
||||
scrollY = await r.page.evaluate(() => window.scrollY);
|
||||
assert.ok(scrollY > 0, String(scrollY));
|
||||
});
|
||||
|
||||
it('resets scroll when a link is clicked', async () => {
|
||||
await page.goto(`${base}/tall-page#foo`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/tall-page#foo');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="another-tall-page"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="another-tall-page"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => window.scrollY),
|
||||
await r.page.evaluate(() => window.scrollY),
|
||||
0
|
||||
);
|
||||
});
|
||||
|
||||
it('preserves scroll when a link with sapper-noscroll is clicked', async () => {
|
||||
await page.goto(`${base}/tall-page#foo`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/tall-page#foo');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="another-tall-page"][sapper-noscroll]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="another-tall-page"][sapper-noscroll]');
|
||||
await r.wait();
|
||||
|
||||
const scrollY = await page.evaluate(() => window.scrollY);
|
||||
const scrollY = await r.page.evaluate(() => window.scrollY);
|
||||
|
||||
assert.ok(scrollY > 0);
|
||||
});
|
||||
|
||||
it('scrolls into a deeplink on a new page', async () => {
|
||||
await page.goto(`${base}/tall-page#foo`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/tall-page#foo');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="another-tall-page#bar"]');
|
||||
await wait(50);
|
||||
assert.equal(await title(), 'Another tall page');
|
||||
const scrollY = await page.evaluate(() => window.scrollY);
|
||||
await r.page.click('[href="another-tall-page#bar"]');
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), 'Another tall page');
|
||||
const scrollY = await r.page.evaluate(() => window.scrollY);
|
||||
assert.ok(scrollY > 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
const app = polka()
|
||||
.use((req, res, next) => {
|
||||
req.hello = 'hello';
|
||||
res.locals = { name: 'world' };
|
||||
@@ -15,5 +15,6 @@ polka()
|
||||
title: `${req.hello} ${res.locals.name}`
|
||||
})
|
||||
})
|
||||
)
|
||||
.listen(PORT);
|
||||
);
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,50 +1,46 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
|
||||
describe('session', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('renders session props', async () => {
|
||||
await page.goto(`${base}/session`);
|
||||
await r.load('/session');
|
||||
|
||||
assert.equal(await title(), 'hello world');
|
||||
assert.equal(await r.text('h1'), 'hello world');
|
||||
|
||||
await start();
|
||||
assert.equal(await title(), 'hello world');
|
||||
await r.sapper.start();
|
||||
assert.equal(await r.text('h1'), 'hello world');
|
||||
|
||||
await page.click('button');
|
||||
assert.equal(await title(), 'changed');
|
||||
await r.page.click('button');
|
||||
assert.equal(await r.text('h1'), 'changed');
|
||||
});
|
||||
|
||||
it('preloads session props', async () => {
|
||||
await page.goto(`${base}/preloaded`);
|
||||
await r.load('/preloaded');
|
||||
|
||||
assert.equal(await title(), 'hello world');
|
||||
assert.equal(await r.text('h1'), 'hello world');
|
||||
|
||||
await start();
|
||||
assert.equal(await title(), 'hello world');
|
||||
await r.sapper.start();
|
||||
assert.equal(await r.text('h1'), 'hello world');
|
||||
|
||||
await page.click('button');
|
||||
assert.equal(await title(), 'changed');
|
||||
await r.page.click('button');
|
||||
assert.equal(await r.text('h1'), 'changed');
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,15 +2,14 @@ import sirv from 'sirv';
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT, NODE_ENV } = process.env;
|
||||
const dev = NODE_ENV === 'development';
|
||||
import { start, dev } from '../../common.js';
|
||||
|
||||
polka()
|
||||
const app = polka()
|
||||
.use(
|
||||
'custom-basepath',
|
||||
sirv('static', { dev }),
|
||||
sapper.middleware()
|
||||
)
|
||||
.listen(PORT, err => {
|
||||
if (err) console.log('error', err);
|
||||
});
|
||||
);
|
||||
|
||||
start(app);
|
||||
|
||||
|
||||
@@ -1,51 +1,36 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import * as api from '../../../api';
|
||||
import { walk } from '../../utils';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
|
||||
describe('with-basepath', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await api.build({ cwd: __dirname });
|
||||
|
||||
await api.export({
|
||||
cwd: __dirname,
|
||||
basepath: '/custom-basepath'
|
||||
});
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, start, page, prefetchRoutes, title } = await runner.start());
|
||||
before('build app', () => api.build({ cwd: __dirname }));
|
||||
before('export app', () => api.export({ cwd: __dirname, basepath: '/custom-basepath' }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('serves /custom-basepath', async () => {
|
||||
await page.goto(`${base}/custom-basepath`);
|
||||
await r.load('/custom-basepath');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'Great success!'
|
||||
);
|
||||
});
|
||||
|
||||
it('emits a basepath message', async () => {
|
||||
await page.goto(`${base}/custom-basepath`);
|
||||
await r.load('/custom-basepath');
|
||||
|
||||
assert.deepEqual(runner.messages, [{
|
||||
assert.deepEqual(r.messages, [{
|
||||
__sapper__: true,
|
||||
event: 'basepath',
|
||||
basepath: '/custom-basepath'
|
||||
@@ -55,8 +40,8 @@ describe('with-basepath', function() {
|
||||
it('crawls an exported site with basepath', () => {
|
||||
const files = walk(`${__dirname}/__sapper__/export`);
|
||||
|
||||
const client_assets = files.filter(file => file.startsWith('custom-basepath/client/'));
|
||||
const non_client_assets = files.filter(file => !file.startsWith('custom-basepath/client/')).sort();
|
||||
const client_assets = files.filter(file => file.startsWith('custom-basepath/sapper/'));
|
||||
const non_client_assets = files.filter(file => !file.startsWith('custom-basepath/sapper/')).sort();
|
||||
|
||||
assert.ok(client_assets.length > 0);
|
||||
|
||||
@@ -71,35 +56,39 @@ describe('with-basepath', function() {
|
||||
});
|
||||
|
||||
it('redirects on server', async () => {
|
||||
await page.goto(`${base}/custom-basepath/redirect-from`);
|
||||
await r.load('/custom-basepath/redirect-from');
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/custom-basepath/redirect-to`
|
||||
r.page.url(),
|
||||
`${r.base}/custom-basepath/redirect-to`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'redirected'
|
||||
);
|
||||
});
|
||||
|
||||
it('redirects in client', async () => {
|
||||
await page.goto(`${base}/custom-basepath`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/custom-basepath');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="redirect-from"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="redirect-from"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/custom-basepath/redirect-to`
|
||||
r.page.url(),
|
||||
`${r.base}/custom-basepath/redirect-to`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'redirected'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT } = process.env;
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
start(server);
|
||||
|
||||
@@ -7,10 +7,9 @@ describe('with-sourcemaps-webpack', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname, bundler: 'webpack' });
|
||||
});
|
||||
before('build app', () => build({ cwd: __dirname, bundler: 'webpack' }));
|
||||
|
||||
// tests
|
||||
it('does not put sourcemap files in service worker shell', async () => {
|
||||
const service_worker_source = fs.readFileSync(`${__dirname}/src/node_modules/@sapper/service-worker.js`, 'utf-8');
|
||||
const shell_source = /shell = (\[[\s\S]+?\])/.exec(service_worker_source)[1];
|
||||
@@ -19,8 +18,8 @@ describe('with-sourcemaps-webpack', function() {
|
||||
assert.equal(shell.filter(_ => _.endsWith('.map')).length, 0,
|
||||
'sourcemap files are not cached in SW');
|
||||
|
||||
const clientShellDir = path.resolve(`${__dirname}/__sapper__/build`, path.dirname(shell[0]));
|
||||
const sourcemapFiles = fs.readdirSync(clientShellDir).filter(_ => _.endsWith('.map'));
|
||||
assert.ok(sourcemapFiles.length > 0, 'sourcemap files exist');
|
||||
const client_shell_dir = path.resolve(`${__dirname}/__sapper__/build/client`, path.dirname(shell[0]));
|
||||
const sourcemap_files = fs.readdirSync(client_shell_dir).filter(_ => _.endsWith('.map'));
|
||||
assert.ok(sourcemap_files.length > 0, 'sourcemap files exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const { PORT } = process.env;
|
||||
const server = sapper.server();
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
start(server);
|
||||
|
||||
@@ -7,10 +7,9 @@ describe('with-sourcemaps', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
});
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
|
||||
// tests
|
||||
it('does not put sourcemap files in service worker shell', async () => {
|
||||
const service_worker_source = fs.readFileSync(`${__dirname}/src/node_modules/@sapper/service-worker.js`, 'utf-8');
|
||||
const shell_source = /shell = (\[[\s\S]+?\])/.exec(service_worker_source)[1];
|
||||
@@ -19,8 +18,8 @@ describe('with-sourcemaps', function() {
|
||||
assert.equal(shell.filter(_ => _.endsWith('.map')).length, 0,
|
||||
'sourcemap files are not cached in SW');
|
||||
|
||||
const clientShellDir = path.resolve(`${__dirname}/__sapper__/build`, path.dirname(shell[0]));
|
||||
const sourcemapFiles = fs.readdirSync(clientShellDir).filter(_ => _.endsWith('.map'));
|
||||
assert.ok(sourcemapFiles.length > 0, 'sourcemap files exist');
|
||||
const client_shell_dir = path.resolve(`${__dirname}/__sapper__/build/client`, path.dirname(shell[0]));
|
||||
const sourcemap_files = fs.readdirSync(client_shell_dir).filter(_ => _.endsWith('.map'));
|
||||
assert.ok(sourcemap_files.length > 0, 'sourcemap files exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('manifest_data', () => {
|
||||
|
||||
assert.deepEqual(pages, [
|
||||
{
|
||||
pattern: /^\/?$/,
|
||||
pattern: /^\/$/,
|
||||
parts: [
|
||||
{ component: index, params: [] }
|
||||
]
|
||||
@@ -50,6 +50,13 @@ describe('manifest_data', () => {
|
||||
]);
|
||||
|
||||
assert.deepEqual(server_routes, [
|
||||
{
|
||||
name: 'route_index',
|
||||
pattern: /^\/$/,
|
||||
file: 'index.js',
|
||||
params: []
|
||||
},
|
||||
|
||||
{
|
||||
name: 'route_blog_json',
|
||||
pattern: /^\/blog.json$/,
|
||||
@@ -167,7 +174,7 @@ describe('manifest_data', () => {
|
||||
file: 'foo.js',
|
||||
name: 'route_foo',
|
||||
params: [],
|
||||
pattern: /^\/foo$/
|
||||
pattern: /^\/foo\/?$/
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user