mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-15 20:34:44 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
002718b609 | ||
|
|
45d216c64d | ||
|
|
3d69d483d7 | ||
|
|
54da524467 | ||
|
|
ee95240ca6 | ||
|
|
74d5d1f9c0 | ||
|
|
8c2688b1be | ||
|
|
e170e4af9b | ||
|
|
bc31c73c33 | ||
|
|
7798f8f684 | ||
|
|
70fd7038b0 | ||
|
|
c6af2ddfa3 | ||
|
|
65d0172abe | ||
|
|
1e22031765 | ||
|
|
46bf8f2b78 | ||
|
|
553db81b7b | ||
|
|
67cc29ed38 | ||
|
|
36f930f489 | ||
|
|
3b098caa6e |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,22 @@
|
|||||||
# sapper changelog
|
# sapper changelog
|
||||||
|
|
||||||
|
## 0.18.4
|
||||||
|
|
||||||
|
* Handle non-Sapper responses when exporting ([#382](https://github.com/sveltejs/sapper/issues/392))
|
||||||
|
* Add `--dev-port` flag to `sapper dev` ([#381](https://github.com/sveltejs/sapper/issues/381))
|
||||||
|
|
||||||
|
## 0.18.3
|
||||||
|
|
||||||
|
* Fix service worker Rollup build config
|
||||||
|
|
||||||
|
## 0.18.2
|
||||||
|
|
||||||
|
* Update `pkg.files`
|
||||||
|
|
||||||
|
## 0.18.1
|
||||||
|
|
||||||
|
* Add live reloading ([#385](https://github.com/sveltejs/sapper/issues/385))
|
||||||
|
|
||||||
## 0.18.0
|
## 0.18.0
|
||||||
|
|
||||||
* Rollup support ([#379](https://github.com/sveltejs/sapper/pull/379))
|
* Rollup support ([#379](https://github.com/sveltejs/sapper/pull/379))
|
||||||
|
|||||||
24
package-lock.json
generated
24
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sapper",
|
"name": "sapper",
|
||||||
"version": "0.17.1",
|
"version": "0.18.3",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5574,12 +5574,6 @@
|
|||||||
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
|
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"querystringify": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"randomatic": {
|
"randomatic": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz",
|
||||||
@@ -5872,12 +5866,6 @@
|
|||||||
"resolve-from": "^1.0.0"
|
"resolve-from": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"requires-port": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"resolve": {
|
"resolve": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
|
||||||
@@ -7285,16 +7273,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"url-parse": {
|
|
||||||
"version": "1.4.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz",
|
|
||||||
"integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"querystringify": "^2.0.0",
|
|
||||||
"requires-port": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"use": {
|
"use": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sapper",
|
"name": "sapper",
|
||||||
"version": "0.18.0",
|
"version": "0.18.4",
|
||||||
"description": "Military-grade apps, engineered by Svelte",
|
"description": "Military-grade apps, engineered by Svelte",
|
||||||
"main": "dist/middleware.js",
|
"main": "dist/middleware.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"*.js",
|
"*.js",
|
||||||
"runtime",
|
"runtime",
|
||||||
"webpack",
|
"webpack",
|
||||||
|
"config",
|
||||||
"sapper",
|
"sapper",
|
||||||
"components",
|
"components",
|
||||||
"dist"
|
"dist"
|
||||||
@@ -62,7 +63,6 @@
|
|||||||
"tiny-glob": "^0.2.2",
|
"tiny-glob": "^0.2.2",
|
||||||
"ts-node": "^7.0.1",
|
"ts-node": "^7.0.1",
|
||||||
"typescript": "^2.8.3",
|
"typescript": "^2.8.3",
|
||||||
"url-parse": "^1.2.0",
|
|
||||||
"walk-sync": "^0.3.2",
|
"walk-sync": "^0.3.2",
|
||||||
"webpack": "^4.8.3",
|
"webpack": "^4.8.3",
|
||||||
"webpack-format-messages": "^2.0.1"
|
"webpack-format-messages": "^2.0.1"
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ async function execute(emitter: EventEmitter, {
|
|||||||
const route_objects = create_routes();
|
const route_objects = create_routes();
|
||||||
|
|
||||||
// create app/manifest/client.js and app/manifest/server.js
|
// create app/manifest/client.js and app/manifest/server.js
|
||||||
create_main_manifests({ routes: route_objects });
|
create_main_manifests({ bundler, routes: route_objects });
|
||||||
|
|
||||||
const { client, server, serviceworker } = create_compilers(validate_bundler(bundler), { webpack, rollup });
|
const { client, server, serviceworker } = create_compilers(validate_bundler(bundler), { webpack, rollup });
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ async function execute(emitter: EventEmitter, {
|
|||||||
fs.writeFileSync(path.join(dest, 'build.json'), JSON.stringify({
|
fs.writeFileSync(path.join(dest, 'build.json'), JSON.stringify({
|
||||||
bundler,
|
bundler,
|
||||||
shimport: bundler === 'rollup' && require('shimport/package.json').version,
|
shimport: bundler === 'rollup' && require('shimport/package.json').version,
|
||||||
assets: client_result.assetsByChunkName
|
assets: client_result.assets
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const server_stats = await server.compile();
|
const server_stats = await server.compile();
|
||||||
@@ -84,7 +84,7 @@ async function execute(emitter: EventEmitter, {
|
|||||||
if (serviceworker) {
|
if (serviceworker) {
|
||||||
create_serviceworker_manifest({
|
create_serviceworker_manifest({
|
||||||
routes: route_objects,
|
routes: route_objects,
|
||||||
client_files: client_result.assets.map((file: string) => `client/${file}`)
|
client_files: client_result.chunks.map((file: string) => `client/${file}`)
|
||||||
});
|
});
|
||||||
|
|
||||||
serviceworker_stats = await serviceworker.compile();
|
serviceworker_stats = await serviceworker.compile();
|
||||||
|
|||||||
@@ -30,13 +30,14 @@ class Watcher extends EventEmitter {
|
|||||||
port: number;
|
port: number;
|
||||||
closed: boolean;
|
closed: boolean;
|
||||||
|
|
||||||
|
dev_port: number;
|
||||||
|
live: boolean;
|
||||||
|
hot: boolean;
|
||||||
|
|
||||||
dev_server: DevServer;
|
dev_server: DevServer;
|
||||||
proc: child_process.ChildProcess;
|
proc: child_process.ChildProcess;
|
||||||
filewatchers: Array<{ close: () => void }>;
|
filewatchers: Array<{ close: () => void }>;
|
||||||
deferreds: {
|
deferred: Deferred;
|
||||||
client: Deferred;
|
|
||||||
server: Deferred;
|
|
||||||
};
|
|
||||||
|
|
||||||
crashed: boolean;
|
crashed: boolean;
|
||||||
restarting: boolean;
|
restarting: boolean;
|
||||||
@@ -51,6 +52,9 @@ class Watcher extends EventEmitter {
|
|||||||
app = locations.app(),
|
app = locations.app(),
|
||||||
dest = locations.dest(),
|
dest = locations.dest(),
|
||||||
routes = locations.routes(),
|
routes = locations.routes(),
|
||||||
|
'dev-port': dev_port,
|
||||||
|
live,
|
||||||
|
hot,
|
||||||
bundler,
|
bundler,
|
||||||
webpack = 'webpack',
|
webpack = 'webpack',
|
||||||
rollup = 'rollup',
|
rollup = 'rollup',
|
||||||
@@ -59,6 +63,9 @@ class Watcher extends EventEmitter {
|
|||||||
app: string,
|
app: string,
|
||||||
dest: string,
|
dest: string,
|
||||||
routes: string,
|
routes: string,
|
||||||
|
'dev-port': number,
|
||||||
|
live: boolean,
|
||||||
|
hot: boolean,
|
||||||
bundler?: string,
|
bundler?: string,
|
||||||
webpack: string,
|
webpack: string,
|
||||||
rollup: string,
|
rollup: string,
|
||||||
@@ -71,6 +78,10 @@ class Watcher extends EventEmitter {
|
|||||||
this.port = port;
|
this.port = port;
|
||||||
this.closed = false;
|
this.closed = false;
|
||||||
|
|
||||||
|
this.dev_port = dev_port;
|
||||||
|
this.live = live;
|
||||||
|
this.hot = hot;
|
||||||
|
|
||||||
this.filewatchers = [];
|
this.filewatchers = [];
|
||||||
|
|
||||||
this.current_build = {
|
this.current_build = {
|
||||||
@@ -114,11 +125,11 @@ class Watcher extends EventEmitter {
|
|||||||
mkdirp.sync(`${dest}/client`);
|
mkdirp.sync(`${dest}/client`);
|
||||||
if (this.bundler === 'rollup') copy_shimport(dest);
|
if (this.bundler === 'rollup') copy_shimport(dest);
|
||||||
|
|
||||||
const dev_port = await ports.find(10000);
|
if (!this.dev_port) this.dev_port = await ports.find(10000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const routes = create_routes();
|
const routes = create_routes();
|
||||||
create_main_manifests({ routes, dev_port });
|
create_main_manifests({ bundler: this.bundler, routes, dev_port: this.dev_port });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.emit('fatal', <events.FatalEvent>{
|
this.emit('fatal', <events.FatalEvent>{
|
||||||
message: err.message
|
message: err.message
|
||||||
@@ -126,7 +137,7 @@ class Watcher extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dev_server = new DevServer(dev_port);
|
this.dev_server = new DevServer(this.dev_port);
|
||||||
|
|
||||||
this.filewatchers.push(
|
this.filewatchers.push(
|
||||||
watch_dir(
|
watch_dir(
|
||||||
@@ -139,11 +150,11 @@ class Watcher extends EventEmitter {
|
|||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
const routes = create_routes();
|
const routes = create_routes();
|
||||||
create_main_manifests({ routes, dev_port });
|
create_main_manifests({ bundler: this.bundler, routes, dev_port: this.dev_port });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const routes = create_routes();
|
const routes = create_routes();
|
||||||
create_main_manifests({ routes, dev_port });
|
create_main_manifests({ bundler: this.bundler, routes, dev_port: this.dev_port });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.emit('error', <events.ErrorEvent>{
|
this.emit('error', <events.ErrorEvent>{
|
||||||
message: err.message
|
message: err.message
|
||||||
@@ -159,10 +170,7 @@ class Watcher extends EventEmitter {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.deferreds = {
|
let deferred = new Deferred();
|
||||||
server: new Deferred(),
|
|
||||||
client: new Deferred()
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO watch the configs themselves?
|
// TODO watch the configs themselves?
|
||||||
const compilers: Compilers = create_compilers(this.bundler, {
|
const compilers: Compilers = create_compilers(this.bundler, {
|
||||||
@@ -187,11 +195,10 @@ class Watcher extends EventEmitter {
|
|||||||
|
|
||||||
invalid: filename => {
|
invalid: filename => {
|
||||||
this.restart(filename, 'server');
|
this.restart(filename, 'server');
|
||||||
this.deferreds.server = new Deferred();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handle_result: (result: CompileResult) => {
|
handle_result: (result: CompileResult) => {
|
||||||
this.deferreds.client.promise.then(() => {
|
deferred.promise.then(() => {
|
||||||
const restart = () => {
|
const restart = () => {
|
||||||
log = '';
|
log = '';
|
||||||
this.crashed = false;
|
this.crashed = false;
|
||||||
@@ -203,11 +210,15 @@ class Watcher extends EventEmitter {
|
|||||||
process: this.proc
|
process: this.proc
|
||||||
});
|
});
|
||||||
|
|
||||||
this.deferreds.server.fulfil();
|
if (this.hot && this.bundler === 'webpack') {
|
||||||
|
this.dev_server.send({
|
||||||
this.dev_server.send({
|
status: 'completed'
|
||||||
status: 'completed'
|
});
|
||||||
});
|
} else {
|
||||||
|
this.dev_server.send({
|
||||||
|
action: 'reload'
|
||||||
|
});
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (this.crashed) return;
|
if (this.crashed) return;
|
||||||
@@ -263,7 +274,7 @@ class Watcher extends EventEmitter {
|
|||||||
|
|
||||||
invalid: filename => {
|
invalid: filename => {
|
||||||
this.restart(filename, 'client');
|
this.restart(filename, 'client');
|
||||||
this.deferreds.client = new Deferred();
|
deferred = new Deferred();
|
||||||
|
|
||||||
// TODO we should delete old assets. due to a webpack bug
|
// TODO we should delete old assets. due to a webpack bug
|
||||||
// i don't even begin to comprehend, this is apparently
|
// i don't even begin to comprehend, this is apparently
|
||||||
@@ -274,9 +285,8 @@ class Watcher extends EventEmitter {
|
|||||||
fs.writeFileSync(path.join(dest, 'build.json'), JSON.stringify({
|
fs.writeFileSync(path.join(dest, 'build.json'), JSON.stringify({
|
||||||
bundler: this.bundler,
|
bundler: this.bundler,
|
||||||
shimport: this.bundler === 'rollup' && require('shimport/package.json').version,
|
shimport: this.bundler === 'rollup' && require('shimport/package.json').version,
|
||||||
assets: result.assetsByChunkName
|
assets: result.assets
|
||||||
}, null, ' '));
|
}, null, ' '));
|
||||||
this.deferreds.client.fulfil();
|
|
||||||
|
|
||||||
const client_files = result.assets.map((file: string) => `client/${file}`);
|
const client_files = result.assets.map((file: string) => `client/${file}`);
|
||||||
|
|
||||||
@@ -285,6 +295,8 @@ class Watcher extends EventEmitter {
|
|||||||
client_files
|
client_files
|
||||||
});
|
});
|
||||||
|
|
||||||
|
deferred.fulfil();
|
||||||
|
|
||||||
// we need to wait a beat before watching the service
|
// we need to wait a beat before watching the service
|
||||||
// worker, because of some webpack nonsense
|
// worker, because of some webpack nonsense
|
||||||
setTimeout(watch_serviceworker, 100);
|
setTimeout(watch_serviceworker, 100);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as child_process from 'child_process';
|
import * as child_process from 'child_process';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as sander from 'sander';
|
import * as sander from 'sander';
|
||||||
import URL from 'url-parse';
|
import * as url from 'url';
|
||||||
import fetch from 'node-fetch';
|
import fetch from 'node-fetch';
|
||||||
import * as ports from 'port-authority';
|
import * as ports from 'port-authority';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
@@ -34,6 +34,12 @@ export function exporter(opts: Opts) {
|
|||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolve(from: string, to: string) {
|
||||||
|
return url.parse(url.resolve(from, to));
|
||||||
|
}
|
||||||
|
|
||||||
|
type URL = url.UrlWithStringQuery;
|
||||||
|
|
||||||
async function execute(emitter: EventEmitter, opts: Opts) {
|
async function execute(emitter: EventEmitter, opts: Opts) {
|
||||||
const export_dir = path.join(opts.dest, opts.basepath);
|
const export_dir = path.join(opts.dest, opts.basepath);
|
||||||
|
|
||||||
@@ -53,8 +59,12 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
|||||||
|
|
||||||
const port = await ports.find(3000);
|
const port = await ports.find(3000);
|
||||||
|
|
||||||
const origin = `http://localhost:${port}`;
|
const protocol = 'http:';
|
||||||
const root = new URL(opts.basepath || '', origin);
|
const host = `localhost:${port}`;
|
||||||
|
const origin = `${protocol}//${host}`;
|
||||||
|
|
||||||
|
const root = resolve(origin, opts.basepath || '');
|
||||||
|
if (!root.href.endsWith('/')) root.href += '/';
|
||||||
|
|
||||||
emitter.emit('info', {
|
emitter.emit('info', {
|
||||||
message: `Crawling ${root.href}`
|
message: `Crawling ${root.href}`
|
||||||
@@ -72,29 +82,15 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
|||||||
|
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
const saved = new Set();
|
const saved = new Set();
|
||||||
const deferreds = new Map();
|
|
||||||
|
|
||||||
function get_deferred(pathname: string) {
|
function save(path: string, status: number, type: string, body: string) {
|
||||||
pathname = pathname.replace(root.pathname, '');
|
const { pathname } = resolve(origin, path);
|
||||||
|
|
||||||
if (!deferreds.has(pathname)) {
|
|
||||||
deferreds.set(pathname, new Deferred());
|
|
||||||
}
|
|
||||||
|
|
||||||
return deferreds.get(pathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
proc.on('message', message => {
|
|
||||||
if (!message.__sapper__ || message.event !== 'file') return;
|
|
||||||
|
|
||||||
const pathname = new URL(message.url, origin).pathname;
|
|
||||||
let file = pathname.slice(1);
|
let file = pathname.slice(1);
|
||||||
let { body } = message;
|
|
||||||
|
|
||||||
if (saved.has(file)) return;
|
if (saved.has(file)) return;
|
||||||
saved.add(file);
|
saved.add(file);
|
||||||
|
|
||||||
const is_html = message.type === 'text/html';
|
const is_html = type === 'text/html';
|
||||||
|
|
||||||
if (is_html) {
|
if (is_html) {
|
||||||
file = file === '' ? 'index.html' : `${file}/index.html`;
|
file = file === '' ? 'index.html' : `${file}/index.html`;
|
||||||
@@ -104,12 +100,15 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
|||||||
emitter.emit('file', <events.FileEvent>{
|
emitter.emit('file', <events.FileEvent>{
|
||||||
file,
|
file,
|
||||||
size: body.length,
|
size: body.length,
|
||||||
status: message.status
|
status
|
||||||
});
|
});
|
||||||
|
|
||||||
sander.writeFileSync(export_dir, file, body);
|
sander.writeFileSync(export_dir, file, body);
|
||||||
|
}
|
||||||
|
|
||||||
get_deferred(pathname).fulfil();
|
proc.on('message', message => {
|
||||||
|
if (!message.__sapper__ || message.event !== 'file') return;
|
||||||
|
save(message.url, message.status, message.type, message.body);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handle(url: URL) {
|
async function handle(url: URL) {
|
||||||
@@ -118,32 +117,34 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
|||||||
if (seen.has(pathname)) return;
|
if (seen.has(pathname)) return;
|
||||||
seen.add(pathname);
|
seen.add(pathname);
|
||||||
|
|
||||||
const deferred = get_deferred(pathname);
|
|
||||||
|
|
||||||
const timeout_deferred = new Deferred();
|
const timeout_deferred = new Deferred();
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
timeout_deferred.reject(new Error(`Timed out waiting for ${url.href}`));
|
timeout_deferred.reject(new Error(`Timed out waiting for ${url.href}`));
|
||||||
}, opts.timeout);
|
}, opts.timeout);
|
||||||
|
|
||||||
const r = await Promise.race([
|
const r = await Promise.race([
|
||||||
fetch(url.href),
|
fetch(url.href, {
|
||||||
|
redirect: 'manual'
|
||||||
|
}),
|
||||||
timeout_deferred.promise
|
timeout_deferred.promise
|
||||||
]);
|
]);
|
||||||
|
|
||||||
clearTimeout(timeout); // prevent it hanging at the end
|
clearTimeout(timeout); // prevent it hanging at the end
|
||||||
|
|
||||||
|
let type = r.headers.get('Content-Type');
|
||||||
|
let body = await r.text();
|
||||||
|
|
||||||
const range = ~~(r.status / 100);
|
const range = ~~(r.status / 100);
|
||||||
|
|
||||||
if (range === 2) {
|
if (range === 2) {
|
||||||
if (r.headers.get('Content-Type') === 'text/html') {
|
if (type === 'text/html') {
|
||||||
const body = await r.text();
|
|
||||||
const urls: URL[] = [];
|
const urls: URL[] = [];
|
||||||
|
|
||||||
const cleaned = clean_html(body);
|
const cleaned = clean_html(body);
|
||||||
|
|
||||||
const base_match = /<base ([\s\S]+?)>/m.exec(cleaned);
|
const base_match = /<base ([\s\S]+?)>/m.exec(cleaned);
|
||||||
const base_href = base_match && get_href(base_match[1]);
|
const base_href = base_match && get_href(base_match[1]);
|
||||||
const base = new URL(base_href || '/', url.href);
|
const base = resolve(url.href, base_href);
|
||||||
|
|
||||||
let match;
|
let match;
|
||||||
let pattern = /<a ([\s\S]+?)>/gm;
|
let pattern = /<a ([\s\S]+?)>/gm;
|
||||||
@@ -153,8 +154,11 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
|||||||
const href = get_href(attrs);
|
const href = get_href(attrs);
|
||||||
|
|
||||||
if (href) {
|
if (href) {
|
||||||
const url = new URL(href, base.href);
|
const url = resolve(base.href, href);
|
||||||
if (url.origin === origin) urls.push(url);
|
|
||||||
|
if (url.protocol === protocol && url.host === host) {
|
||||||
|
urls.push(url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +166,16 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await deferred.promise;
|
if (range === 3) {
|
||||||
|
const location = r.headers.get('Location');
|
||||||
|
|
||||||
|
type = 'text/html';
|
||||||
|
body = `<script>window.location.href = "${location}"</script>`;
|
||||||
|
|
||||||
|
await handle(resolve(root.href, location));
|
||||||
|
}
|
||||||
|
|
||||||
|
save(pathname, r.status, type, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ports.wait(port)
|
return ports.wait(port)
|
||||||
|
|||||||
12
src/cli.ts
12
src/cli.ts
@@ -11,8 +11,18 @@ prog.command('dev')
|
|||||||
.describe('Start a development server')
|
.describe('Start a development server')
|
||||||
.option('-p, --port', 'Specify a port')
|
.option('-p, --port', 'Specify a port')
|
||||||
.option('-o, --open', 'Open a browser window')
|
.option('-o, --open', 'Open a browser window')
|
||||||
|
.option('--dev-port', 'Specify a port for development server')
|
||||||
|
.option('--hot', 'Use hot module replacement (requires webpack)', true)
|
||||||
|
.option('--live', 'Reload on changes if not using --hot', true)
|
||||||
.option('--bundler', 'Specify a bundler (rollup or webpack)')
|
.option('--bundler', 'Specify a bundler (rollup or webpack)')
|
||||||
.action(async (opts: { port: number, open: boolean, bundler?: string }) => {
|
.action(async (opts: {
|
||||||
|
port: number,
|
||||||
|
open: boolean,
|
||||||
|
'dev-port': number,
|
||||||
|
live: boolean,
|
||||||
|
hot: boolean,
|
||||||
|
bundler?: string
|
||||||
|
}) => {
|
||||||
const { dev } = await import('./cli/dev');
|
const { dev } = await import('./cli/dev');
|
||||||
dev(opts);
|
dev(opts);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ export class CompileResult {
|
|||||||
duration: number;
|
duration: number;
|
||||||
errors: CompileError[];
|
errors: CompileError[];
|
||||||
warnings: CompileError[];
|
warnings: CompileError[];
|
||||||
assets: string[];
|
chunks: string[];
|
||||||
assetsByChunkName: Record<string, string>;
|
assets: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RollupResult extends CompileResult {
|
class RollupResult extends CompileResult {
|
||||||
@@ -32,15 +32,15 @@ class RollupResult extends CompileResult {
|
|||||||
this.errors = compiler.errors.map(munge_rollup_warning_or_error);
|
this.errors = compiler.errors.map(munge_rollup_warning_or_error);
|
||||||
this.warnings = compiler.warnings.map(munge_rollup_warning_or_error); // TODO emit this as they happen
|
this.warnings = compiler.warnings.map(munge_rollup_warning_or_error); // TODO emit this as they happen
|
||||||
|
|
||||||
this.assets = compiler.chunks.map(chunk => chunk.fileName);
|
this.chunks = compiler.chunks.map(chunk => chunk.fileName);
|
||||||
|
|
||||||
// TODO populate this properly. We don't have namedcompiler. chunks, as in
|
// TODO populate this properly. We don't have namedcompiler. chunks, as in
|
||||||
// webpack, but we can have a route -> [chunk] map or something
|
// webpack, but we can have a route -> [chunk] map or something
|
||||||
this.assetsByChunkName = {};
|
this.assets = {};
|
||||||
|
|
||||||
compiler.chunks.forEach(chunk => {
|
compiler.chunks.forEach(chunk => {
|
||||||
if (compiler.input in chunk.modules) {
|
if (compiler.input in chunk.modules) {
|
||||||
this.assetsByChunkName.main = chunk.fileName;
|
this.assets.main = chunk.fileName;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -110,8 +110,8 @@ class WebpackResult extends CompileResult {
|
|||||||
|
|
||||||
this.duration = info.time;
|
this.duration = info.time;
|
||||||
|
|
||||||
this.assets = info.assets.map((chunk: { name: string }) => chunk.name);
|
this.chunks = info.assets.map((chunk: { name: string }) => chunk.name);
|
||||||
this.assetsByChunkName = info.assetsByChunkName;
|
this.assets = info.assetsByChunkName;
|
||||||
}
|
}
|
||||||
|
|
||||||
print() {
|
print() {
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { posixify, write_if_changed } from './utils';
|
|||||||
import { dev, locations } from '../config';
|
import { dev, locations } from '../config';
|
||||||
import { Page, PageComponent, ServerRoute } from '../interfaces';
|
import { Page, PageComponent, ServerRoute } from '../interfaces';
|
||||||
|
|
||||||
export function create_main_manifests({ routes, dev_port }: {
|
export function create_main_manifests({ bundler, routes, dev_port }: {
|
||||||
|
bundler: string,
|
||||||
routes: { components: PageComponent[], pages: Page[], server_routes: ServerRoute[] };
|
routes: { components: PageComponent[], pages: Page[], server_routes: ServerRoute[] };
|
||||||
dev_port?: number;
|
dev_port?: number;
|
||||||
}) {
|
}) {
|
||||||
@@ -14,7 +15,7 @@ export function create_main_manifests({ routes, dev_port }: {
|
|||||||
|
|
||||||
const path_to_routes = path.relative(manifest_dir, locations.routes());
|
const path_to_routes = path.relative(manifest_dir, locations.routes());
|
||||||
|
|
||||||
const client_manifest = generate_client(routes, path_to_routes, dev_port);
|
const client_manifest = generate_client(routes, path_to_routes, bundler, dev_port);
|
||||||
const server_manifest = generate_server(routes, path_to_routes);
|
const server_manifest = generate_server(routes, path_to_routes);
|
||||||
|
|
||||||
write_if_changed(
|
write_if_changed(
|
||||||
@@ -48,6 +49,7 @@ export function create_serviceworker_manifest({ routes, client_files }: {
|
|||||||
function generate_client(
|
function generate_client(
|
||||||
routes: { root: PageComponent, components: PageComponent[], pages: Page[], server_routes: ServerRoute[] },
|
routes: { root: PageComponent, components: PageComponent[], pages: Page[], server_routes: ServerRoute[] },
|
||||||
path_to_routes: string,
|
path_to_routes: string,
|
||||||
|
bundler: string,
|
||||||
dev_port?: number
|
dev_port?: number
|
||||||
) {
|
) {
|
||||||
const page_ids = new Set(routes.pages.map(page =>
|
const page_ids = new Set(routes.pages.map(page =>
|
||||||
@@ -61,10 +63,15 @@ function generate_client(
|
|||||||
import root from '${get_file(path_to_routes, routes.root)}';
|
import root from '${get_file(path_to_routes, routes.root)}';
|
||||||
import error from '${posixify(`${path_to_routes}/_error.html`)}';
|
import error from '${posixify(`${path_to_routes}/_error.html`)}';
|
||||||
|
|
||||||
${routes.components.map(component =>
|
${routes.components.map(component => {
|
||||||
`const ${component.name} = () =>
|
const annotation = bundler === 'webpack'
|
||||||
import(/* webpackChunkName: "${component.name}" */ '${get_file(path_to_routes, component)}');`)
|
? `/* webpackChunkName: "${component.name}" */ `
|
||||||
.join('\n')}
|
: '';
|
||||||
|
|
||||||
|
const source = get_file(path_to_routes, component);
|
||||||
|
|
||||||
|
return `const ${component.name} = () => import(${annotation}'${source}');`;
|
||||||
|
}).join('\n')}
|
||||||
|
|
||||||
export const manifest = {
|
export const manifest = {
|
||||||
ignore: [${server_routes_to_ignore.map(route => route.pattern).join(', ')}],
|
ignore: [${server_routes_to_ignore.map(route => route.pattern).join(', ')}],
|
||||||
|
|||||||
@@ -128,12 +128,12 @@ export default function create_routes(cwd = locations.routes()) {
|
|||||||
components.push(component);
|
components.push(component);
|
||||||
if (item.basename === 'index.html') {
|
if (item.basename === 'index.html') {
|
||||||
pages.push({
|
pages.push({
|
||||||
pattern: get_pattern(parent_segments),
|
pattern: get_pattern(parent_segments, true),
|
||||||
parts
|
parts
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
pages.push({
|
pages.push({
|
||||||
pattern: get_pattern(segments),
|
pattern: get_pattern(segments, true),
|
||||||
parts
|
parts
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,7 @@ export default function create_routes(cwd = locations.routes()) {
|
|||||||
else {
|
else {
|
||||||
server_routes.push({
|
server_routes.push({
|
||||||
name: `route_${get_slug(item.file)}`,
|
name: `route_${get_slug(item.file)}`,
|
||||||
pattern: get_pattern(segments),
|
pattern: get_pattern(segments, false),
|
||||||
file: item.file,
|
file: item.file,
|
||||||
params: params
|
params: params
|
||||||
});
|
});
|
||||||
@@ -276,7 +276,7 @@ function get_slug(file: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_pattern(segments: Part[][]) {
|
function get_pattern(segments: Part[][], add_trailing_slash: boolean) {
|
||||||
return new RegExp(
|
return new RegExp(
|
||||||
`^` +
|
`^` +
|
||||||
segments.map(segment => {
|
segments.map(segment => {
|
||||||
@@ -290,6 +290,6 @@ function get_pattern(segments: Part[][]) {
|
|||||||
.replace(/%5D/g, ']');
|
.replace(/%5D/g, ']');
|
||||||
}).join('');
|
}).join('');
|
||||||
}).join('') +
|
}).join('') +
|
||||||
'\\\/?$'
|
(add_trailing_slash ? '\\\/?$' : '$')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ export default function middleware(opts: {
|
|||||||
|
|
||||||
serve({
|
serve({
|
||||||
prefix: '/client/',
|
prefix: '/client/',
|
||||||
cache_control: 'max-age=31536000'
|
cache_control: dev() ? 'no-cache' : 'max-age=31536000'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
get_server_route_handler(manifest.server_routes),
|
get_server_route_handler(manifest.server_routes),
|
||||||
@@ -414,18 +414,6 @@ function get_page_handler(
|
|||||||
res.setHeader('Location', location);
|
res.setHeader('Location', location);
|
||||||
res.end();
|
res.end();
|
||||||
|
|
||||||
if (process.send) {
|
|
||||||
process.send({
|
|
||||||
__sapper__: true,
|
|
||||||
event: 'file',
|
|
||||||
url: req.url,
|
|
||||||
method: req.method,
|
|
||||||
status: redirect.statusCode,
|
|
||||||
type: 'text/html',
|
|
||||||
body: `<script>window.location.href = "${location}"</script>`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,18 +500,6 @@ function get_page_handler(
|
|||||||
|
|
||||||
res.statusCode = status;
|
res.statusCode = status;
|
||||||
res.end(body);
|
res.end(body);
|
||||||
|
|
||||||
if (process.send) {
|
|
||||||
process.send({
|
|
||||||
__sapper__: true,
|
|
||||||
event: 'file',
|
|
||||||
url: req.url,
|
|
||||||
method: req.method,
|
|
||||||
status,
|
|
||||||
type: 'text/html',
|
|
||||||
body
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
if (error) {
|
if (error) {
|
||||||
// we encountered an error while rendering the error page — oops
|
// we encountered an error while rendering the error page — oops
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default {
|
|||||||
|
|
||||||
output: () => {
|
output: () => {
|
||||||
return {
|
return {
|
||||||
dir: locations.dest(),
|
file: `${locations.dest()}/service-worker.js`,
|
||||||
format: 'iife'
|
format: 'iife'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,13 @@ const middlewares = [
|
|||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
app.get(`${BASEPATH}/non-sapper-redirect-from`, (req, res) => {
|
||||||
|
res.writeHead(301, {
|
||||||
|
Location: `${BASEPATH}/non-sapper-redirect-to`
|
||||||
|
});
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
|
||||||
if (BASEPATH) {
|
if (BASEPATH) {
|
||||||
app.use(BASEPATH, ...middlewares);
|
app.use(BASEPATH, ...middlewares);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<a href='.'>home</a>
|
<a href='.'>home</a>
|
||||||
<a href='about'>about</a>
|
<a href='about'>about</a>
|
||||||
<a href='slow-preload'>slow preload</a>
|
<a href='slow-preload'>slow preload</a>
|
||||||
|
<a href='non-sapper-redirect-from'>redirect</a>
|
||||||
<a href='redirect-from'>redirect</a>
|
<a href='redirect-from'>redirect</a>
|
||||||
<a href='redirect-root'>redirect (root)</a>
|
<a href='redirect-root'>redirect (root)</a>
|
||||||
<a href='blog/nope'>broken link</a>
|
<a href='blog/nope'>broken link</a>
|
||||||
|
|||||||
1
test/app/routes/non-sapper-redirect-to.html
Normal file
1
test/app/routes/non-sapper-redirect-to.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<h1>redirected</h1>
|
||||||
@@ -2,9 +2,7 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const Nightmare = require('nightmare');
|
const Nightmare = require('nightmare');
|
||||||
const serve = require('serve-static');
|
|
||||||
const walkSync = require('walk-sync');
|
const walkSync = require('walk-sync');
|
||||||
const fetch = require('node-fetch');
|
|
||||||
const rimraf = require('rimraf');
|
const rimraf = require('rimraf');
|
||||||
const ports = require('port-authority');
|
const ports = require('port-authority');
|
||||||
|
|
||||||
@@ -83,6 +81,11 @@ function testExport({ basepath = '' }) {
|
|||||||
'about/index.html',
|
'about/index.html',
|
||||||
'slow-preload/index.html',
|
'slow-preload/index.html',
|
||||||
|
|
||||||
|
'redirect-from/index.html',
|
||||||
|
'redirect-to/index.html',
|
||||||
|
'non-sapper-redirect-from/index.html',
|
||||||
|
'non-sapper-redirect-to/index.html',
|
||||||
|
|
||||||
'blog/index.html',
|
'blog/index.html',
|
||||||
'blog/a-very-long-post/index.html',
|
'blog/a-very-long-post/index.html',
|
||||||
'blog/how-can-i-get-involved/index.html',
|
'blog/how-can-i-get-involved/index.html',
|
||||||
|
|||||||
@@ -53,14 +53,14 @@ describe('create_routes', () => {
|
|||||||
assert.deepEqual(server_routes, [
|
assert.deepEqual(server_routes, [
|
||||||
{
|
{
|
||||||
name: 'route_blog_json',
|
name: 'route_blog_json',
|
||||||
pattern: /^\/blog.json\/?$/,
|
pattern: /^\/blog.json$/,
|
||||||
file: 'blog/index.json.js',
|
file: 'blog/index.json.js',
|
||||||
params: []
|
params: []
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'route_blog_$slug_json',
|
name: 'route_blog_$slug_json',
|
||||||
pattern: /^\/blog\/([^\/]+?).json\/?$/,
|
pattern: /^\/blog\/([^\/]+?).json$/,
|
||||||
file: 'blog/[slug].json.js',
|
file: 'blog/[slug].json.js',
|
||||||
params: ['slug']
|
params: ['slug']
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user