Better/faster exporting

* add --build and --build-dir options to sapper export (#325)

* tweak export logging, update port-authority to prevent timeout bug

* better logging of export progress

* handle case where linked resource is already fetched

* default to .sapper/dev instead of .sapper

* handle query params and redirects

* dont write server_info.json either - second half of #318

* update changelog

* update lockfile

* try to track down ci test failures

* err wut

* curiouser and curiouser

* ok, seems to work now
This commit is contained in:
Rich Harris
2018-08-03 00:10:58 -04:00
committed by GitHub
parent 31327b3780
commit eae8351f77
11 changed files with 2734 additions and 472 deletions

View File

@@ -4,8 +4,7 @@ import mkdirp from 'mkdirp';
import rimraf from 'rimraf';
import { EventEmitter } from 'events';
import { minify_html } from './utils/minify_html';
import { create_compilers, create_main_manifests, create_routes, create_serviceworker_manifest } from '../core'
import { locations } from '../config';
import { create_compilers, create_main_manifests, create_routes, create_serviceworker_manifest } from '../core';
import * as events from './interfaces';
export function build(opts: {}) {

View File

@@ -9,6 +9,7 @@ import format_messages from 'webpack-format-messages';
import { locations } from '../config';
import { EventEmitter } from 'events';
import { create_routes, create_main_manifests, create_compilers, create_serviceworker_manifest } from '../core';
import Deferred from './utils/Deferred';
import * as events from './interfaces';
export function dev(opts) {
@@ -168,8 +169,6 @@ class Watcher extends EventEmitter {
},
result: info => {
fs.writeFileSync(path.join(dest, 'server_info.json'), JSON.stringify(info, null, ' '));
this.deferreds.client.promise.then(() => {
const restart = () => {
log = '';
@@ -400,19 +399,6 @@ function mungeWebpackError(message: string, duplicate: boolean) {
};
}
class Deferred {
promise: Promise<any>;
fulfil: (value?: any) => void;
reject: (error: Error) => void;
constructor() {
this.promise = new Promise((fulfil, reject) => {
this.fulfil = fulfil;
this.reject = reject;
});
}
}
const INTERVAL = 10000;
class DevServer {

View File

@@ -7,7 +7,7 @@ import fetch from 'node-fetch';
import * as ports from 'port-authority';
import { EventEmitter } from 'events';
import { minify_html } from './utils/minify_html';
import { locations } from '../config';
import Deferred from './utils/Deferred';
import * as events from './interfaces';
export function exporter(opts: {}) {
@@ -52,6 +52,10 @@ async function execute(emitter: EventEmitter, {
const origin = `http://localhost:${port}`;
emitter.emit('info', {
message: `Crawling ${origin}`
});
const proc = child_process.fork(path.resolve(`${build}/server.js`), [], {
cwd: process.cwd(),
env: Object.assign({
@@ -64,11 +68,21 @@ async function execute(emitter: EventEmitter, {
const seen = new Set();
const saved = new Set();
const deferreds = new Map();
function get_deferred(pathname: string) {
if (!deferreds.has(pathname)) {
deferreds.set(pathname, new Deferred()) ;
}
return deferreds.get(pathname);
}
proc.on('message', message => {
if (!message.__sapper__ || message.event !== 'file') return;
let file = new URL(message.url, origin).pathname.slice(1);
const pathname = new URL(message.url, origin).pathname;
let file = pathname.slice(1);
let { body } = message;
if (saved.has(file)) return;
@@ -83,24 +97,26 @@ async function execute(emitter: EventEmitter, {
emitter.emit('file', <events.FileEvent>{
file,
size: body.length
size: body.length,
status: message.status
});
sander.writeFileSync(export_dir, file, body);
get_deferred(pathname).fulfil();
});
async function handle(url: URL) {
const pathname = url.pathname || '/';
if (seen.has(pathname)) return;
seen.add(pathname);
const deferred = get_deferred(pathname);
const r = await fetch(url.href);
const range = ~~(r.status / 100);
if (range >= 4) {
emitter.emit('failure', <events.FailureEvent>{
status: r.status,
pathname: url.pathname
});
return;
}
if (range === 2) {
if (r.headers.get('Content-Type') === 'text/html') {
const body = await r.text();
@@ -111,16 +127,14 @@ async function execute(emitter: EventEmitter, {
$('a[href]').each((i: number, $a) => {
const url = new URL($a.attribs.href, base.href);
if (url.origin === origin && !seen.has(url.pathname)) {
seen.add(url.pathname);
urls.push(url);
}
if (url.origin === origin) urls.push(url);
});
await Promise.all(urls.map(handle));
}
}
await deferred.promise;
}
return ports.wait(port)

12
src/api/utils/Deferred.ts Normal file
View File

@@ -0,0 +1,12 @@
export default class Deferred {
promise: Promise<any>;
fulfil: (value?: any) => void;
reject: (error: Error) => void;
constructor() {
this.promise = new Promise((fulfil, reject) => {
this.fulfil = fulfil;
this.reject = reject;
});
}
}