mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-14 20:14:39 +00:00
Improve internal API
This commit is contained in:
@@ -2,4 +2,4 @@
|
||||
--require ts-node/register
|
||||
--recursive
|
||||
test/unit/*/*.ts
|
||||
test/apps/*/__test__.ts
|
||||
test/apps/*/test.ts
|
||||
15
package-lock.json
generated
15
package-lock.json
generated
@@ -4423,12 +4423,6 @@
|
||||
"error-ex": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"parse-ms": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz",
|
||||
"integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=",
|
||||
"dev": true
|
||||
},
|
||||
"parseurl": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
|
||||
@@ -4599,15 +4593,6 @@
|
||||
"integrity": "sha512-wa5+qGVg9Yt7PB6rYm3kXlKzgzgivYTLRandezh43jjRqgyDyP+9YxfJpJiLs9yKD1WeU8/OvtToWpW7255FtA==",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-ms": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-3.2.0.tgz",
|
||||
"integrity": "sha512-ZypexbfVUGTFxb0v+m1bUyy92DHe5SyYlnyY0msyms5zd3RwyvNgyxZZsXXgoyzlxjx5MiqtXUdhUfvQbe0A2Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"parse-ms": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
"polka": "^0.4.0",
|
||||
"port-authority": "^1.0.5",
|
||||
"pretty-bytes": "^5.0.0",
|
||||
"pretty-ms": "^3.1.0",
|
||||
"puppeteer": "^1.9.0",
|
||||
"require-relative": "^0.8.7",
|
||||
"rimraf": "^2.6.2",
|
||||
@@ -61,7 +60,6 @@
|
||||
"sirv": "^0.2.2",
|
||||
"svelte": "^2.6.3",
|
||||
"svelte-loader": "^2.9.0",
|
||||
"tiny-glob": "^0.2.2",
|
||||
"ts-node": "^7.0.1",
|
||||
"typescript": "^2.8.3",
|
||||
"webpack": "^4.8.3",
|
||||
|
||||
@@ -43,8 +43,8 @@ export default [
|
||||
`src/api.ts`,
|
||||
`src/cli.ts`,
|
||||
`src/core.ts`,
|
||||
`src/rollup.ts`,
|
||||
`src/webpack.ts`
|
||||
`src/config/rollup.ts`,
|
||||
`src/config/webpack.ts`
|
||||
],
|
||||
output: {
|
||||
dir: 'dist',
|
||||
|
||||
10
src/api.ts
10
src/api.ts
@@ -1,6 +1,4 @@
|
||||
import { dev } from './api/dev';
|
||||
import { build } from './api/build';
|
||||
import { exporter } from './api/export';
|
||||
import { find_page } from './api/find_page';
|
||||
|
||||
export { dev, build, exporter, find_page };
|
||||
export { dev } from './api/dev';
|
||||
export { build } from './api/build';
|
||||
export { export } from './api/export';
|
||||
export { find_page } from './api/find_page';
|
||||
@@ -2,44 +2,51 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
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_manifest_data, create_serviceworker_manifest } from '../core';
|
||||
import * as events from './interfaces';
|
||||
import { copy_shimport } from './utils/copy_shimport';
|
||||
import { Dirs } from '../interfaces';
|
||||
import read_template from '../core/read_template';
|
||||
import { CompileResult } from '../core/create_compilers/interfaces';
|
||||
import { noop } from './utils/noop';
|
||||
import validate_bundler from './utils/validate_bundler';
|
||||
|
||||
type Opts = {
|
||||
legacy: boolean;
|
||||
bundler: 'rollup' | 'webpack';
|
||||
cwd?: string;
|
||||
src?: string;
|
||||
routes?: string;
|
||||
dest?: string;
|
||||
output?: string;
|
||||
static_files?: string;
|
||||
legacy?: boolean;
|
||||
bundler?: 'rollup' | 'webpack';
|
||||
oncompile?: ({ type, result }: { type: string, result: CompileResult }) => void;
|
||||
};
|
||||
|
||||
export function build(opts: Opts, dirs: Dirs) {
|
||||
const emitter = new EventEmitter();
|
||||
export async function build({
|
||||
cwd = process.cwd(),
|
||||
src = path.join(cwd, 'src'),
|
||||
routes = path.join(cwd, 'src/routes'),
|
||||
output = path.join(cwd, '__sapper__'),
|
||||
static_files = path.join(cwd, 'static'),
|
||||
dest = path.join(cwd, '__sapper__/build'),
|
||||
|
||||
execute(emitter, opts, dirs).then(
|
||||
() => {
|
||||
emitter.emit('done', <events.DoneEvent>{}); // TODO do we need to pass back any info?
|
||||
},
|
||||
error => {
|
||||
emitter.emit('error', <events.ErrorEvent>{
|
||||
error
|
||||
});
|
||||
}
|
||||
);
|
||||
bundler,
|
||||
legacy = false,
|
||||
oncompile = noop
|
||||
}: Opts = {}) {
|
||||
bundler = validate_bundler(bundler);
|
||||
|
||||
return emitter;
|
||||
}
|
||||
if (legacy && bundler === 'webpack') {
|
||||
throw new Error(`Legacy builds are not supported for projects using webpack`);
|
||||
}
|
||||
|
||||
async function execute(emitter: EventEmitter, opts: Opts, dirs: Dirs) {
|
||||
rimraf.sync(path.join(dirs.dest, '**/*'));
|
||||
mkdirp.sync(`${dirs.dest}/client`);
|
||||
copy_shimport(dirs.dest);
|
||||
rimraf.sync(path.join(dest, '**/*'));
|
||||
mkdirp.sync(`${dest}/client`);
|
||||
copy_shimport(dest);
|
||||
|
||||
// minify src/template.html
|
||||
// TODO compile this to a function? could be quicker than str.replace(...).replace(...).replace(...)
|
||||
const template = read_template();
|
||||
const template = read_template(src);
|
||||
|
||||
// remove this in a future version
|
||||
if (template.indexOf('%sapper.base%') === -1) {
|
||||
@@ -48,47 +55,53 @@ async function execute(emitter: EventEmitter, opts: Opts, dirs: Dirs) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
fs.writeFileSync(`${dirs.dest}/template.html`, minify_html(template));
|
||||
fs.writeFileSync(`${dest}/template.html`, minify_html(template));
|
||||
|
||||
const manifest_data = create_manifest_data();
|
||||
const manifest_data = create_manifest_data(routes);
|
||||
|
||||
// create src/manifest/client.js and src/manifest/server.js
|
||||
create_main_manifests({ bundler: opts.bundler, manifest_data });
|
||||
create_main_manifests({
|
||||
bundler,
|
||||
manifest_data,
|
||||
cwd,
|
||||
src,
|
||||
dest,
|
||||
routes,
|
||||
output,
|
||||
dev: false
|
||||
});
|
||||
|
||||
const { client, server, serviceworker } = await create_compilers(opts.bundler);
|
||||
const { client, server, serviceworker } = await create_compilers(bundler, cwd, src, dest, true);
|
||||
|
||||
const client_result = await client.compile();
|
||||
emitter.emit('build', <events.BuildEvent>{
|
||||
oncompile({
|
||||
type: 'client',
|
||||
// TODO duration/warnings
|
||||
result: client_result
|
||||
});
|
||||
|
||||
const build_info = client_result.to_json(manifest_data, dirs);
|
||||
const build_info = client_result.to_json(manifest_data, { src, routes, dest });
|
||||
|
||||
if (opts.legacy) {
|
||||
if (legacy) {
|
||||
process.env.SAPPER_LEGACY_BUILD = 'true';
|
||||
const { client } = await create_compilers(opts.bundler);
|
||||
const { client } = await create_compilers(bundler, cwd, src, dest, true);
|
||||
|
||||
const client_result = await client.compile();
|
||||
|
||||
emitter.emit('build', <events.BuildEvent>{
|
||||
oncompile({
|
||||
type: 'client (legacy)',
|
||||
// TODO duration/warnings
|
||||
result: client_result
|
||||
});
|
||||
|
||||
client_result.to_json(manifest_data, dirs);
|
||||
client_result.to_json(manifest_data, { src, routes, dest });
|
||||
build_info.legacy_assets = client_result.assets;
|
||||
delete process.env.SAPPER_LEGACY_BUILD;
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join(dirs.dest, 'build.json'), JSON.stringify(build_info));
|
||||
fs.writeFileSync(path.join(dest, 'build.json'), JSON.stringify(build_info));
|
||||
|
||||
const server_stats = await server.compile();
|
||||
emitter.emit('build', <events.BuildEvent>{
|
||||
oncompile({
|
||||
type: 'server',
|
||||
// TODO duration/warnings
|
||||
result: server_stats
|
||||
});
|
||||
|
||||
@@ -97,14 +110,15 @@ async function execute(emitter: EventEmitter, opts: Opts, dirs: Dirs) {
|
||||
if (serviceworker) {
|
||||
create_serviceworker_manifest({
|
||||
manifest_data,
|
||||
client_files: client_result.chunks.map(chunk => `client/${chunk.file}`)
|
||||
output,
|
||||
client_files: client_result.chunks.map(chunk => `client/${chunk.file}`),
|
||||
static_files
|
||||
});
|
||||
|
||||
serviceworker_stats = await serviceworker.compile();
|
||||
|
||||
emitter.emit('build', <events.BuildEvent>{
|
||||
oncompile({
|
||||
type: 'serviceworker',
|
||||
// TODO duration/warnings
|
||||
result: serviceworker_stats
|
||||
});
|
||||
}
|
||||
|
||||
116
src/api/dev.ts
116
src/api/dev.ts
@@ -5,30 +5,45 @@ import * as child_process from 'child_process';
|
||||
import * as ports from 'port-authority';
|
||||
import mkdirp from 'mkdirp';
|
||||
import rimraf from 'rimraf';
|
||||
import { locations } from '../config';
|
||||
import { EventEmitter } from 'events';
|
||||
import { create_manifest_data, create_main_manifests, create_compilers, create_serviceworker_manifest } from '../core';
|
||||
import { Compiler, Compilers } from '../core/create_compilers';
|
||||
import { CompileResult, CompileError } from '../core/create_compilers/interfaces';
|
||||
import { CompileResult } from '../core/create_compilers/interfaces';
|
||||
import Deferred from './utils/Deferred';
|
||||
import * as events from './interfaces';
|
||||
import validate_bundler from '../cli/utils/validate_bundler';
|
||||
import validate_bundler from './utils/validate_bundler';
|
||||
import { copy_shimport } from './utils/copy_shimport';
|
||||
import { ManifestData } from '../interfaces';
|
||||
import { ManifestData, FatalEvent, ErrorEvent, ReadyEvent, InvalidEvent } from '../interfaces';
|
||||
import read_template from '../core/read_template';
|
||||
import { noop } from './utils/noop';
|
||||
|
||||
export function dev(opts) {
|
||||
type Opts = {
|
||||
cwd?: string,
|
||||
src?: string,
|
||||
dest?: string,
|
||||
routes?: string,
|
||||
output?: string,
|
||||
static_files?: string,
|
||||
'dev-port'?: number,
|
||||
live?: boolean,
|
||||
hot?: boolean,
|
||||
'devtools-port'?: number,
|
||||
bundler?: 'rollup' | 'webpack',
|
||||
port?: number
|
||||
};
|
||||
|
||||
export function dev(opts: Opts) {
|
||||
return new Watcher(opts);
|
||||
}
|
||||
|
||||
class Watcher extends EventEmitter {
|
||||
bundler: string;
|
||||
bundler: 'rollup' | 'webpack';
|
||||
dirs: {
|
||||
cwd: string;
|
||||
src: string;
|
||||
dest: string;
|
||||
routes: string;
|
||||
rollup: string;
|
||||
webpack: string;
|
||||
output: string;
|
||||
static_files: string;
|
||||
}
|
||||
port: number;
|
||||
closed: boolean;
|
||||
@@ -54,34 +69,23 @@ class Watcher extends EventEmitter {
|
||||
}
|
||||
|
||||
constructor({
|
||||
src = locations.src(),
|
||||
dest = locations.dest(),
|
||||
routes = locations.routes(),
|
||||
cwd = process.cwd(),
|
||||
src = path.join(cwd, 'src'),
|
||||
routes = path.join(cwd, 'src/routes'),
|
||||
output = path.join(cwd, '__sapper__'),
|
||||
static_files = path.join(cwd, 'static'),
|
||||
dest = path.join(cwd, '__sapper__/dev'),
|
||||
'dev-port': dev_port,
|
||||
live,
|
||||
hot,
|
||||
'devtools-port': devtools_port,
|
||||
bundler,
|
||||
webpack = 'webpack',
|
||||
rollup = 'rollup',
|
||||
port = +process.env.PORT
|
||||
}: {
|
||||
src: string,
|
||||
dest: string,
|
||||
routes: string,
|
||||
'dev-port': number,
|
||||
live: boolean,
|
||||
hot: boolean,
|
||||
'devtools-port': number,
|
||||
bundler?: string,
|
||||
webpack: string,
|
||||
rollup: string,
|
||||
port: number
|
||||
}) {
|
||||
}: Opts) {
|
||||
super();
|
||||
|
||||
this.bundler = validate_bundler(bundler);
|
||||
this.dirs = { src, dest, routes, webpack, rollup };
|
||||
this.dirs = { cwd, src, dest, routes, output, static_files };
|
||||
this.port = port;
|
||||
this.closed = false;
|
||||
|
||||
@@ -101,7 +105,7 @@ class Watcher extends EventEmitter {
|
||||
};
|
||||
|
||||
// remove this in a future version
|
||||
const template = read_template();
|
||||
const template = read_template(src);
|
||||
if (template.indexOf('%sapper.base%') === -1) {
|
||||
const error = new Error(`As of Sapper v0.10, your template.html file must include %sapper.base% in the <head>`);
|
||||
error.code = `missing-sapper-base`;
|
||||
@@ -120,7 +124,7 @@ class Watcher extends EventEmitter {
|
||||
async init() {
|
||||
if (this.port) {
|
||||
if (!await ports.check(this.port)) {
|
||||
this.emit('fatal', <events.FatalEvent>{
|
||||
this.emit('fatal', <FatalEvent>{
|
||||
message: `Port ${this.port} is unavailable`
|
||||
});
|
||||
return;
|
||||
@@ -129,7 +133,7 @@ class Watcher extends EventEmitter {
|
||||
this.port = await ports.find(3000);
|
||||
}
|
||||
|
||||
const { dest } = this.dirs;
|
||||
const { cwd, src, dest, routes, output, static_files } = this.dirs;
|
||||
rimraf.sync(dest);
|
||||
mkdirp.sync(`${dest}/client`);
|
||||
if (this.bundler === 'rollup') copy_shimport(dest);
|
||||
@@ -142,10 +146,16 @@ class Watcher extends EventEmitter {
|
||||
let manifest_data: ManifestData;
|
||||
|
||||
try {
|
||||
manifest_data = create_manifest_data();
|
||||
create_main_manifests({ bundler: this.bundler, manifest_data, dev_port: this.dev_port });
|
||||
manifest_data = create_manifest_data(routes);
|
||||
create_main_manifests({
|
||||
bundler: this.bundler,
|
||||
manifest_data,
|
||||
dev: true,
|
||||
dev_port: this.dev_port,
|
||||
cwd, src, dest, routes, output
|
||||
});
|
||||
} catch (err) {
|
||||
this.emit('fatal', <events.FatalEvent>{
|
||||
this.emit('fatal', <FatalEvent>{
|
||||
message: err.message
|
||||
});
|
||||
return;
|
||||
@@ -155,7 +165,7 @@ class Watcher extends EventEmitter {
|
||||
|
||||
this.filewatchers.push(
|
||||
watch_dir(
|
||||
locations.routes(),
|
||||
routes,
|
||||
({ path: file, stats }) => {
|
||||
if (stats.isDirectory()) {
|
||||
return path.basename(file)[0] !== '_';
|
||||
@@ -164,19 +174,25 @@ class Watcher extends EventEmitter {
|
||||
},
|
||||
() => {
|
||||
try {
|
||||
const new_manifest_data = create_manifest_data();
|
||||
create_main_manifests({ bundler: this.bundler, manifest_data, dev_port: this.dev_port });
|
||||
const new_manifest_data = create_manifest_data(routes);
|
||||
create_main_manifests({
|
||||
bundler: this.bundler,
|
||||
manifest_data, // TODO is this right? not new_manifest_data?
|
||||
dev: true,
|
||||
dev_port: this.dev_port,
|
||||
cwd, src, dest, routes, output
|
||||
});
|
||||
|
||||
manifest_data = new_manifest_data;
|
||||
} catch (err) {
|
||||
this.emit('error', <events.ErrorEvent>{
|
||||
this.emit('error', <ErrorEvent>{
|
||||
message: err.message
|
||||
});
|
||||
}
|
||||
}
|
||||
),
|
||||
|
||||
fs.watch(`${locations.src()}/template.html`, () => {
|
||||
fs.watch(`${src}/template.html`, () => {
|
||||
this.dev_server.send({
|
||||
action: 'reload'
|
||||
});
|
||||
@@ -186,12 +202,12 @@ class Watcher extends EventEmitter {
|
||||
let deferred = new Deferred();
|
||||
|
||||
// TODO watch the configs themselves?
|
||||
const compilers: Compilers = await create_compilers(this.bundler, this.dirs);
|
||||
const compilers: Compilers = await create_compilers(this.bundler, cwd, src, dest, false);
|
||||
|
||||
let log = '';
|
||||
|
||||
const emitFatal = () => {
|
||||
this.emit('fatal', <events.FatalEvent>{
|
||||
this.emit('fatal', <FatalEvent>{
|
||||
message: `Server crashed`,
|
||||
log
|
||||
});
|
||||
@@ -215,7 +231,7 @@ class Watcher extends EventEmitter {
|
||||
|
||||
ports.wait(this.port)
|
||||
.then((() => {
|
||||
this.emit('ready', <events.ReadyEvent>{
|
||||
this.emit('ready', <ReadyEvent>{
|
||||
port: this.port,
|
||||
process: this.proc
|
||||
});
|
||||
@@ -233,7 +249,7 @@ class Watcher extends EventEmitter {
|
||||
.catch(err => {
|
||||
if (this.crashed) return;
|
||||
|
||||
this.emit('fatal', <events.FatalEvent>{
|
||||
this.emit('fatal', <FatalEvent>{
|
||||
message: `Server is not listening on port ${this.port}`,
|
||||
log
|
||||
});
|
||||
@@ -312,7 +328,9 @@ class Watcher extends EventEmitter {
|
||||
|
||||
create_serviceworker_manifest({
|
||||
manifest_data,
|
||||
client_files
|
||||
output,
|
||||
client_files,
|
||||
static_files
|
||||
});
|
||||
|
||||
deferred.fulfil();
|
||||
@@ -361,7 +379,7 @@ class Watcher extends EventEmitter {
|
||||
};
|
||||
|
||||
process.nextTick(() => {
|
||||
this.emit('invalid', <events.InvalidEvent>{
|
||||
this.emit('invalid', <InvalidEvent>{
|
||||
changed: Array.from(this.current_build.changed),
|
||||
invalid: {
|
||||
server: this.current_build.rebuilding.has('server'),
|
||||
@@ -384,7 +402,7 @@ class Watcher extends EventEmitter {
|
||||
|
||||
compiler.watch((err?: Error, result?: CompileResult) => {
|
||||
if (err) {
|
||||
this.emit('error', <events.ErrorEvent>{
|
||||
this.emit('error', <ErrorEvent>{
|
||||
type: name,
|
||||
message: err.message
|
||||
});
|
||||
@@ -455,14 +473,12 @@ class DevServer {
|
||||
}
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
function watch_dir(
|
||||
dir: string,
|
||||
filter: ({ path, stats }: { path: string, stats: fs.Stats }) => boolean,
|
||||
callback: () => void
|
||||
) {
|
||||
let watch;
|
||||
let watch: any;
|
||||
let closed = false;
|
||||
|
||||
import('cheap-watch').then(CheapWatch => {
|
||||
@@ -470,7 +486,7 @@ function watch_dir(
|
||||
|
||||
watch = new CheapWatch({ dir, filter, debounce: 50 });
|
||||
|
||||
watch.on('+', ({ isNew }) => {
|
||||
watch.on('+', ({ isNew }: { isNew: boolean }) => {
|
||||
if (isNew) callback();
|
||||
});
|
||||
|
||||
|
||||
@@ -4,58 +4,52 @@ import * as sander from 'sander';
|
||||
import * as url from 'url';
|
||||
import fetch from 'node-fetch';
|
||||
import * as ports from 'port-authority';
|
||||
import { EventEmitter } from 'events';
|
||||
import clean_html from './utils/clean_html';
|
||||
import minify_html from './utils/minify_html';
|
||||
import Deferred from './utils/Deferred';
|
||||
import * as events from './interfaces';
|
||||
import { noop } from './utils/noop';
|
||||
|
||||
type Opts = {
|
||||
build: string,
|
||||
dest: string,
|
||||
static: string,
|
||||
build_dir?: string,
|
||||
export_dir?: string,
|
||||
cwd?: string,
|
||||
static?: string,
|
||||
basepath?: string,
|
||||
timeout: number | false
|
||||
timeout?: number | false,
|
||||
oninfo?: ({ message }: { message: string }) => void;
|
||||
onfile?: ({ file, size, status }: { file: string, size: number, status: number }) => void;
|
||||
};
|
||||
|
||||
export function exporter(opts: Opts) {
|
||||
const emitter = new EventEmitter();
|
||||
|
||||
execute(emitter, opts).then(
|
||||
() => {
|
||||
emitter.emit('done', <events.DoneEvent>{}); // TODO do we need to pass back any info?
|
||||
},
|
||||
error => {
|
||||
emitter.emit('error', <events.ErrorEvent>{
|
||||
error
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
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) {
|
||||
const export_dir = path.join(opts.dest, opts.basepath);
|
||||
export { _export as export };
|
||||
|
||||
async function _export({
|
||||
cwd = process.cwd(),
|
||||
static: static_files = path.join(cwd, 'static'),
|
||||
build_dir = path.join(cwd, '__sapper__/build'),
|
||||
basepath = '',
|
||||
export_dir = path.join(cwd, '__sapper__/export', basepath),
|
||||
timeout = 5000,
|
||||
oninfo = noop,
|
||||
onfile = noop
|
||||
}: Opts = {}) {
|
||||
// Prep output directory
|
||||
sander.rimrafSync(export_dir);
|
||||
|
||||
sander.copydirSync(opts.static).to(export_dir);
|
||||
sander.copydirSync(opts.build, 'client').to(export_dir, 'client');
|
||||
sander.copydirSync(static_files).to(export_dir);
|
||||
sander.copydirSync(build_dir, 'client').to(export_dir, 'client');
|
||||
|
||||
if (sander.existsSync(opts.build, 'service-worker.js')) {
|
||||
sander.copyFileSync(opts.build, 'service-worker.js').to(export_dir, 'service-worker.js');
|
||||
if (sander.existsSync(build_dir, 'service-worker.js')) {
|
||||
sander.copyFileSync(build_dir, 'service-worker.js').to(export_dir, 'service-worker.js');
|
||||
}
|
||||
|
||||
if (sander.existsSync(opts.build, 'service-worker.js.map')) {
|
||||
sander.copyFileSync(opts.build, 'service-worker.js.map').to(export_dir, 'service-worker.js.map');
|
||||
if (sander.existsSync(build_dir, 'service-worker.js.map')) {
|
||||
sander.copyFileSync(build_dir, 'service-worker.js.map').to(export_dir, 'service-worker.js.map');
|
||||
}
|
||||
|
||||
const port = await ports.find(3000);
|
||||
@@ -64,19 +58,18 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
||||
const host = `localhost:${port}`;
|
||||
const origin = `${protocol}//${host}`;
|
||||
|
||||
const root = resolve(origin, opts.basepath || '');
|
||||
const root = resolve(origin, basepath);
|
||||
if (!root.href.endsWith('/')) root.href += '/';
|
||||
|
||||
emitter.emit('info', {
|
||||
oninfo({
|
||||
message: `Crawling ${root.href}`
|
||||
});
|
||||
|
||||
const proc = child_process.fork(path.resolve(`${opts.build}/server/server.js`), [], {
|
||||
cwd: process.cwd(),
|
||||
const proc = child_process.fork(path.resolve(`${build_dir}/server/server.js`), [], {
|
||||
cwd,
|
||||
env: Object.assign({
|
||||
PORT: port,
|
||||
NODE_ENV: 'production',
|
||||
SAPPER_DEST: opts.build,
|
||||
SAPPER_EXPORT: 'true'
|
||||
}, process.env)
|
||||
});
|
||||
@@ -98,7 +91,7 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
||||
body = minify_html(body);
|
||||
}
|
||||
|
||||
emitter.emit('file', <events.FileEvent>{
|
||||
onfile({
|
||||
file,
|
||||
size: body.length,
|
||||
status
|
||||
@@ -119,9 +112,9 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
||||
seen.add(pathname);
|
||||
|
||||
const timeout_deferred = new Deferred();
|
||||
const timeout = setTimeout(() => {
|
||||
const the_timeout = setTimeout(() => {
|
||||
timeout_deferred.reject(new Error(`Timed out waiting for ${url.href}`));
|
||||
}, opts.timeout);
|
||||
}, timeout);
|
||||
|
||||
const r = await Promise.race([
|
||||
fetch(url.href, {
|
||||
@@ -130,7 +123,7 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
||||
timeout_deferred.promise
|
||||
]);
|
||||
|
||||
clearTimeout(timeout); // prevent it hanging at the end
|
||||
clearTimeout(the_timeout); // prevent it hanging at the end
|
||||
|
||||
let type = r.headers.get('Content-Type');
|
||||
let body = await r.text();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { locations } from '../config';
|
||||
import { create_manifest_data } from '../core';
|
||||
|
||||
export function find_page(pathname: string, cwd = locations.routes()) {
|
||||
export function find_page(pathname: string, cwd = 'src/routes') {
|
||||
const { pages } = create_manifest_data(cwd);
|
||||
|
||||
for (let i = 0; i < pages.length; i += 1) {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import * as child_process from 'child_process';
|
||||
import { CompileResult } from '../core/create_compilers/interfaces';
|
||||
|
||||
export type ReadyEvent = {
|
||||
port: number;
|
||||
process: child_process.ChildProcess;
|
||||
};
|
||||
|
||||
export type ErrorEvent = {
|
||||
type: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type FatalEvent = {
|
||||
message: string;
|
||||
log?: string;
|
||||
};
|
||||
|
||||
export type InvalidEvent = {
|
||||
changed: string[];
|
||||
invalid: {
|
||||
client: boolean;
|
||||
server: boolean;
|
||||
serviceworker: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export type BuildEvent = {
|
||||
type: string;
|
||||
errors: Array<{ file: string, message: string, duplicate: boolean }>;
|
||||
warnings: Array<{ file: string, message: string, duplicate: boolean }>;
|
||||
duration: number;
|
||||
result: CompileResult;
|
||||
}
|
||||
|
||||
export type FileEvent = {
|
||||
file: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export type FailureEvent = {
|
||||
|
||||
}
|
||||
|
||||
export type DoneEvent = {};
|
||||
1
src/api/utils/noop.ts
Normal file
1
src/api/utils/noop.ts
Normal file
@@ -0,0 +1 @@
|
||||
export function noop() {}
|
||||
200
src/cli.ts
200
src/cli.ts
@@ -2,11 +2,21 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import sade from 'sade';
|
||||
import colors from 'kleur';
|
||||
import prettyMs from 'pretty-ms';
|
||||
import * as pkg from '../package.json';
|
||||
import { elapsed, repeat, left_pad, format_milliseconds } from './utils';
|
||||
import { InvalidEvent, ErrorEvent, FatalEvent, BuildEvent, ReadyEvent } from './interfaces';
|
||||
|
||||
const prog = sade('sapper').version(pkg.version);
|
||||
|
||||
if (process.argv[2] === 'start') {
|
||||
// remove this in a future version
|
||||
console.error(colors.bold.red(`'sapper start' has been removed`));
|
||||
console.error(`Use 'node [build_dir]' instead`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const start = Date.now();
|
||||
|
||||
prog.command('dev')
|
||||
.describe('Start a development server')
|
||||
.option('-p, --port', 'Specify a port')
|
||||
@@ -21,10 +31,93 @@ prog.command('dev')
|
||||
'dev-port': number,
|
||||
live: boolean,
|
||||
hot: boolean,
|
||||
bundler?: string
|
||||
bundler?: 'rollup' | 'webpack'
|
||||
}) => {
|
||||
const { dev } = await import('./cli/dev');
|
||||
dev(opts);
|
||||
const cwd = path.resolve(process.env.SAPPER_BASE || '');
|
||||
|
||||
const { dev } = await import('./api/dev');
|
||||
|
||||
try {
|
||||
const watcher = dev({
|
||||
cwd,
|
||||
port: opts.port,
|
||||
'dev-port': opts['dev-port'],
|
||||
live: opts.live,
|
||||
hot: opts.hot,
|
||||
bundler: opts.bundler
|
||||
});
|
||||
|
||||
let first = true;
|
||||
|
||||
watcher.on('ready', async (event: ReadyEvent) => {
|
||||
if (first) {
|
||||
console.log(colors.bold.cyan(`> Listening on http://localhost:${event.port}`));
|
||||
if (opts.open) {
|
||||
const { exec } = await import('child_process');
|
||||
exec(`open http://localhost:${event.port}`);
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
|
||||
// TODO clear screen?
|
||||
|
||||
event.process.stdout.on('data', data => {
|
||||
process.stdout.write(data);
|
||||
});
|
||||
|
||||
event.process.stderr.on('data', data => {
|
||||
process.stderr.write(data);
|
||||
});
|
||||
});
|
||||
|
||||
watcher.on('invalid', (event: InvalidEvent) => {
|
||||
const changed = event.changed.map(filename => path.relative(process.cwd(), filename)).join(', ');
|
||||
console.log(`\n${colors.bold.cyan(changed)} changed. rebuilding...`);
|
||||
});
|
||||
|
||||
watcher.on('error', (event: ErrorEvent) => {
|
||||
console.log(colors.red(`✗ ${event.type}`));
|
||||
console.log(colors.red(event.message));
|
||||
});
|
||||
|
||||
watcher.on('fatal', (event: FatalEvent) => {
|
||||
console.log(colors.bold.red(`> ${event.message}`));
|
||||
if (event.log) console.log(event.log);
|
||||
});
|
||||
|
||||
watcher.on('build', (event: BuildEvent) => {
|
||||
if (event.errors.length) {
|
||||
console.log(colors.bold.red(`✗ ${event.type}`));
|
||||
|
||||
event.errors.filter(e => !e.duplicate).forEach(error => {
|
||||
if (error.file) console.log(colors.bold(error.file));
|
||||
console.log(error.message);
|
||||
});
|
||||
|
||||
const hidden = event.errors.filter(e => e.duplicate).length;
|
||||
if (hidden > 0) {
|
||||
console.log(`${hidden} duplicate ${hidden === 1 ? 'error' : 'errors'} hidden\n`);
|
||||
}
|
||||
} else if (event.warnings.length) {
|
||||
console.log(colors.bold.yellow(`• ${event.type}`));
|
||||
|
||||
event.warnings.filter(e => !e.duplicate).forEach(warning => {
|
||||
if (warning.file) console.log(colors.bold(warning.file));
|
||||
console.log(warning.message);
|
||||
});
|
||||
|
||||
const hidden = event.warnings.filter(e => e.duplicate).length;
|
||||
if (hidden > 0) {
|
||||
console.log(`${hidden} duplicate ${hidden === 1 ? 'warning' : 'warnings'} hidden\n`);
|
||||
}
|
||||
} else {
|
||||
console.log(`${colors.bold.green(`✔ ${event.type}`)} ${colors.gray(`(${format_milliseconds(event.duration)})`)}`);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(colors.bold.red(`> ${err.message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
prog.command('build [dest]')
|
||||
@@ -36,25 +129,20 @@ prog.command('build [dest]')
|
||||
.action(async (dest = '__sapper__/build', opts: {
|
||||
port: string,
|
||||
legacy: boolean,
|
||||
bundler?: string
|
||||
bundler?: 'rollup' | 'webpack'
|
||||
}) => {
|
||||
console.log(`> Building...`);
|
||||
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||
process.env.SAPPER_DEST = dest;
|
||||
|
||||
const start = Date.now();
|
||||
const cwd = path.resolve(process.env.SAPPER_BASE || '');
|
||||
|
||||
try {
|
||||
const { build } = await import('./cli/build');
|
||||
await build(opts);
|
||||
await _build(opts.bundler, opts.legacy, cwd, dest);
|
||||
|
||||
const launcher = path.resolve(dest, 'index.js');
|
||||
|
||||
fs.writeFileSync(launcher, `
|
||||
// generated by sapper build at ${new Date().toISOString()}
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||
process.env.SAPPER_DEST = __dirname;
|
||||
process.env.PORT = process.env.PORT || ${opts.port || 3000};
|
||||
|
||||
console.log('Starting server on port ' + process.env.PORT);
|
||||
@@ -68,15 +156,6 @@ prog.command('build [dest]')
|
||||
}
|
||||
});
|
||||
|
||||
prog.command('start [dir]')
|
||||
.describe('Start your app')
|
||||
.option('-p, --port', 'Specify a port')
|
||||
.option('-o, --open', 'Open a browser window')
|
||||
.action(async (dir = 'build', opts: { port: number, open: boolean }) => {
|
||||
const { start } = await import('./cli/start');
|
||||
start(dir, opts);
|
||||
});
|
||||
|
||||
prog.command('export [dest]')
|
||||
.describe('Export your app as static files (if possible)')
|
||||
.option('--build', '(Re)build app before exporting', true)
|
||||
@@ -88,26 +167,46 @@ prog.command('export [dest]')
|
||||
.action(async (dest = '__sapper__/export', opts: {
|
||||
build: boolean,
|
||||
legacy: boolean,
|
||||
bundler?: string,
|
||||
bundler?: 'rollup' | 'webpack',
|
||||
'build-dir': string,
|
||||
basepath?: string,
|
||||
timeout: number | false
|
||||
}) => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
process.env.SAPPER_DEST = opts['build-dir'];
|
||||
|
||||
const start = Date.now();
|
||||
const cwd = path.resolve(process.env.SAPPER_BASE || '');
|
||||
|
||||
try {
|
||||
if (opts.build) {
|
||||
console.log(`> Building...`);
|
||||
const { build } = await import('./cli/build');
|
||||
await build(opts);
|
||||
await _build(opts.bundler, opts.legacy, cwd, opts['build-dir']);
|
||||
console.error(`\n> Built in ${elapsed(start)}`);
|
||||
}
|
||||
|
||||
const { exporter } = await import('./cli/export');
|
||||
await exporter(dest, opts);
|
||||
const { export: _export } = await import('./api/export');
|
||||
const { default: pb } = await import('pretty-bytes');
|
||||
|
||||
await _export({
|
||||
static: path.resolve(cwd, process.env.SAPPER_STATIC || 'static'),
|
||||
build_dir: opts['build-dir'],
|
||||
export_dir: dest,
|
||||
basepath: opts.basepath,
|
||||
timeout: opts.timeout,
|
||||
|
||||
oninfo: event => {
|
||||
console.log(colors.bold.cyan(`> ${event.message}`));
|
||||
},
|
||||
|
||||
onfile: event => {
|
||||
const size_color = event.size > 150000 ? colors.bold.red : event.size > 50000 ? colors.bold.yellow : colors.bold.gray;
|
||||
const size_label = size_color(left_pad(pb(event.size), 10));
|
||||
|
||||
const file_label = event.status === 200
|
||||
? event.file
|
||||
: colors.bold[event.status >= 400 ? 'red' : 'yellow'](`(${event.status}) ${event.file}`);
|
||||
|
||||
console.log(`${size_label} ${file_label}`);
|
||||
}
|
||||
});
|
||||
|
||||
console.error(`\n> Finished in ${elapsed(start)}. Type ${colors.bold.cyan(`npx serve ${dest}`)} to run the app.`);
|
||||
} catch (err) {
|
||||
console.error(colors.bold.red(`> ${err.message}`));
|
||||
@@ -115,10 +214,41 @@ prog.command('export [dest]')
|
||||
}
|
||||
});
|
||||
|
||||
// TODO upgrade
|
||||
|
||||
prog.parse(process.argv);
|
||||
|
||||
function elapsed(start: number) {
|
||||
return prettyMs(Date.now() - start);
|
||||
}
|
||||
|
||||
async function _build(
|
||||
bundler: 'rollup' | 'webpack',
|
||||
legacy: boolean,
|
||||
cwd: string,
|
||||
dest: string
|
||||
) {
|
||||
const { build } = await import('./api/build');
|
||||
|
||||
await build({
|
||||
bundler,
|
||||
legacy,
|
||||
cwd,
|
||||
src: path.resolve(cwd, process.env.SAPPER_SRC || 'src'),
|
||||
routes: path.resolve(cwd, process.env.SAPPER_ROUTES || 'src/routes'),
|
||||
dest: path.resolve(cwd, dest),
|
||||
|
||||
oncompile: event => {
|
||||
let banner = `built ${event.type}`;
|
||||
let c = colors.cyan;
|
||||
|
||||
const { warnings } = event.result;
|
||||
if (warnings.length > 0) {
|
||||
banner += ` with ${warnings.length} ${warnings.length === 1 ? 'warning' : 'warnings'}`;
|
||||
c = colors.yellow;
|
||||
}
|
||||
|
||||
console.log();
|
||||
console.log(c(`┌─${repeat('─', banner.length)}─┐`));
|
||||
console.log(c(`│ ${colors.bold(banner) } │`));
|
||||
console.log(c(`└─${repeat('─', banner.length)}─┘`));
|
||||
|
||||
console.log(event.result.print());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import { build as _build } from '../api/build';
|
||||
import colors from 'kleur';
|
||||
import { locations } from '../config';
|
||||
import validate_bundler from './utils/validate_bundler';
|
||||
import { repeat } from '../utils';
|
||||
|
||||
export function build(opts: { bundler?: string, legacy?: boolean }) {
|
||||
const bundler = validate_bundler(opts.bundler);
|
||||
|
||||
if (opts.legacy && bundler === 'webpack') {
|
||||
throw new Error(`Legacy builds are not supported for projects using webpack`);
|
||||
}
|
||||
|
||||
return new Promise((fulfil, reject) => {
|
||||
try {
|
||||
const emitter = _build({
|
||||
legacy: opts.legacy,
|
||||
bundler
|
||||
}, {
|
||||
dest: locations.dest(),
|
||||
src: locations.src(),
|
||||
routes: locations.routes()
|
||||
});
|
||||
|
||||
emitter.on('build', event => {
|
||||
let banner = `built ${event.type}`;
|
||||
let c = colors.cyan;
|
||||
|
||||
const { warnings } = event.result;
|
||||
if (warnings.length > 0) {
|
||||
banner += ` with ${warnings.length} ${warnings.length === 1 ? 'warning' : 'warnings'}`;
|
||||
c = colors.yellow;
|
||||
}
|
||||
|
||||
console.log();
|
||||
console.log(c(`┌─${repeat('─', banner.length)}─┐`));
|
||||
console.log(c(`│ ${colors.bold(banner) } │`));
|
||||
console.log(c(`└─${repeat('─', banner.length)}─┘`));
|
||||
|
||||
console.log(event.result.print());
|
||||
});
|
||||
|
||||
emitter.on('error', event => {
|
||||
reject(event.error);
|
||||
});
|
||||
|
||||
emitter.on('done', event => {
|
||||
fulfil();
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import * as path from 'path';
|
||||
import colors from 'kleur';
|
||||
import * as child_process from 'child_process';
|
||||
import prettyMs from 'pretty-ms';
|
||||
import { dev as _dev } from '../api/dev';
|
||||
import * as events from '../api/interfaces';
|
||||
|
||||
export function dev(opts: { port: number, open: boolean, bundler?: string }) {
|
||||
try {
|
||||
const watcher = _dev(opts);
|
||||
|
||||
let first = true;
|
||||
|
||||
watcher.on('ready', (event: events.ReadyEvent) => {
|
||||
if (first) {
|
||||
console.log(colors.bold.cyan(`> Listening on http://localhost:${event.port}`));
|
||||
if (opts.open) child_process.exec(`open http://localhost:${event.port}`);
|
||||
first = false;
|
||||
}
|
||||
|
||||
// TODO clear screen?
|
||||
|
||||
event.process.stdout.on('data', data => {
|
||||
process.stdout.write(data);
|
||||
});
|
||||
|
||||
event.process.stderr.on('data', data => {
|
||||
process.stderr.write(data);
|
||||
});
|
||||
});
|
||||
|
||||
watcher.on('invalid', (event: events.InvalidEvent) => {
|
||||
const changed = event.changed.map(filename => path.relative(process.cwd(), filename)).join(', ');
|
||||
console.log(`\n${colors.bold.cyan(changed)} changed. rebuilding...`);
|
||||
});
|
||||
|
||||
watcher.on('error', (event: events.ErrorEvent) => {
|
||||
console.log(colors.red(`✗ ${event.type}`));
|
||||
console.log(colors.red(event.message));
|
||||
});
|
||||
|
||||
watcher.on('fatal', (event: events.FatalEvent) => {
|
||||
console.log(colors.bold.red(`> ${event.message}`));
|
||||
if (event.log) console.log(event.log);
|
||||
});
|
||||
|
||||
watcher.on('build', (event: events.BuildEvent) => {
|
||||
if (event.errors.length) {
|
||||
console.log(colors.bold.red(`✗ ${event.type}`));
|
||||
|
||||
event.errors.filter(e => !e.duplicate).forEach(error => {
|
||||
if (error.file) console.log(colors.bold(error.file));
|
||||
console.log(error.message);
|
||||
});
|
||||
|
||||
const hidden = event.errors.filter(e => e.duplicate).length;
|
||||
if (hidden > 0) {
|
||||
console.log(`${hidden} duplicate ${hidden === 1 ? 'error' : 'errors'} hidden\n`);
|
||||
}
|
||||
} else if (event.warnings.length) {
|
||||
console.log(colors.bold.yellow(`• ${event.type}`));
|
||||
|
||||
event.warnings.filter(e => !e.duplicate).forEach(warning => {
|
||||
if (warning.file) console.log(colors.bold(warning.file));
|
||||
console.log(warning.message);
|
||||
});
|
||||
|
||||
const hidden = event.warnings.filter(e => e.duplicate).length;
|
||||
if (hidden > 0) {
|
||||
console.log(`${hidden} duplicate ${hidden === 1 ? 'warning' : 'warnings'} hidden\n`);
|
||||
}
|
||||
} else {
|
||||
console.log(`${colors.bold.green(`✔ ${event.type}`)} ${colors.gray(`(${prettyMs(event.duration)})`)}`);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(colors.bold.red(`> ${err.message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { exporter as _exporter } from '../api/export';
|
||||
import colors from 'kleur';
|
||||
import pb from 'pretty-bytes';
|
||||
import { locations } from '../config';
|
||||
import { left_pad } from '../utils';
|
||||
|
||||
export function exporter(export_dir: string, {
|
||||
basepath = '',
|
||||
timeout
|
||||
}: {
|
||||
basepath: string,
|
||||
timeout: number | false
|
||||
}) {
|
||||
return new Promise((fulfil, reject) => {
|
||||
try {
|
||||
const emitter = _exporter({
|
||||
build: locations.dest(),
|
||||
static: locations.static(),
|
||||
dest: export_dir,
|
||||
basepath,
|
||||
timeout
|
||||
});
|
||||
|
||||
emitter.on('file', event => {
|
||||
const size_color = event.size > 150000 ? colors.bold.red : event.size > 50000 ? colors.bold.yellow : colors.bold.gray;
|
||||
const size_label = size_color(left_pad(pb(event.size), 10));
|
||||
|
||||
const file_label = event.status === 200
|
||||
? event.file
|
||||
: colors.bold[event.status >= 400 ? 'red' : 'yellow'](`(${event.status}) ${event.file}`);
|
||||
|
||||
console.log(`${size_label} ${file_label}`);
|
||||
});
|
||||
|
||||
emitter.on('info', event => {
|
||||
console.log(colors.bold.cyan(`> ${event.message}`));
|
||||
});
|
||||
|
||||
emitter.on('error', event => {
|
||||
reject(event.error);
|
||||
});
|
||||
|
||||
emitter.on('done', event => {
|
||||
fulfil();
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(`${colors.bold.red(`> ${err.message}`)}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as child_process from 'child_process';
|
||||
import colors from 'kleur';
|
||||
import * as ports from 'port-authority';
|
||||
|
||||
export async function start(dir: string, opts: { port: number, open: boolean }) {
|
||||
let port = opts.port || +process.env.PORT;
|
||||
|
||||
const resolved = path.resolve(dir);
|
||||
const server = path.resolve(dir, 'server.js');
|
||||
|
||||
if (!fs.existsSync(server)) {
|
||||
console.log(`${colors.bold.red(`> ${dir}/server.js does not exist — type ${colors.bold.cyan(dir === 'build' ? `npx sapper build` : `npx sapper build ${dir}`)} to create it`)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (port) {
|
||||
if (!await ports.check(port)) {
|
||||
console.log(`${colors.bold.red(`> Port ${port} is unavailable`)}`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
port = await ports.find(3000);
|
||||
}
|
||||
|
||||
child_process.fork(server, [], {
|
||||
cwd: process.cwd(),
|
||||
env: Object.assign({
|
||||
NODE_ENV: 'production',
|
||||
PORT: port,
|
||||
SAPPER_DEST: dir
|
||||
}, process.env)
|
||||
});
|
||||
|
||||
await ports.wait(port);
|
||||
console.log(`${colors.bold.cyan(`> Listening on http://localhost:${port}`)}`);
|
||||
if (opts.open) child_process.exec(`open http://localhost:${port}`);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import * as fs from 'fs';
|
||||
import colors from 'kleur';
|
||||
|
||||
export default async function upgrade() {
|
||||
const upgraded = [
|
||||
await upgrade_sapper_main()
|
||||
].filter(Boolean);
|
||||
|
||||
if (upgraded.length === 0) {
|
||||
console.log(`No changes!`);
|
||||
}
|
||||
}
|
||||
|
||||
async function upgrade_sapper_main() {
|
||||
const _2xx = read('templates/2xx.html');
|
||||
const _4xx = read('templates/4xx.html');
|
||||
const _5xx = read('templates/5xx.html');
|
||||
|
||||
const pattern = /<script src='\%sapper\.main\%'><\/script>/;
|
||||
|
||||
let replaced = false;
|
||||
|
||||
['2xx', '4xx', '5xx'].forEach(code => {
|
||||
const file = `templates/${code}.html`
|
||||
const template = read(file);
|
||||
if (!template) return;
|
||||
|
||||
if (/\%sapper\.main\%/.test(template)) {
|
||||
if (!pattern.test(template)) {
|
||||
console.log(`${colors.red(`Could not replace %sapper.main% in ${file}`)}`);
|
||||
} else {
|
||||
write(file, template.replace(pattern, `%sapper.scripts%`));
|
||||
console.log(`${colors.green(`Replaced %sapper.main% in ${file}`)}`);
|
||||
replaced = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
function read(file: string) {
|
||||
try {
|
||||
return fs.readFileSync(file, 'utf-8');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function write(file: string, data: string) {
|
||||
fs.writeFileSync(file, data);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import * as path from 'path';
|
||||
|
||||
export const dev = () => process.env.NODE_ENV !== 'production';
|
||||
|
||||
export const locations = {
|
||||
base: () => path.resolve(process.env.SAPPER_BASE || ''),
|
||||
src: () => path.resolve(process.env.SAPPER_BASE || '', process.env.SAPPER_SRC || 'src'),
|
||||
static: () => path.resolve(process.env.SAPPER_BASE || '', process.env.SAPPER_STATIC || 'static'),
|
||||
routes: () => path.resolve(process.env.SAPPER_BASE || '', process.env.SAPPER_ROUTES || 'src/routes'),
|
||||
dest: () => path.resolve(process.env.SAPPER_BASE || '', process.env.SAPPER_DEST || `__sapper__/${dev() ? 'dev' : 'build'}`)
|
||||
};
|
||||
7
src/config/env.ts
Normal file
7
src/config/env.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export let dev: boolean;
|
||||
export let src: string;
|
||||
export let dest: string;
|
||||
|
||||
export const set_dev = (_: boolean) => dev = _;
|
||||
export const set_src = (_: string) => src = _;
|
||||
export const set_dest = (_: string) => dest = _;
|
||||
@@ -1,15 +1,15 @@
|
||||
import { locations, dev } from './config';
|
||||
import { dev, src, dest } from './env';
|
||||
|
||||
export default {
|
||||
dev: dev(),
|
||||
dev,
|
||||
|
||||
client: {
|
||||
input: () => {
|
||||
return `${locations.src()}/client.js`
|
||||
return `${src}/client.js`
|
||||
},
|
||||
|
||||
output: () => {
|
||||
let dir = `${locations.dest()}/client`;
|
||||
let dir = `${dest}/client`;
|
||||
if (process.env.SAPPER_LEGACY_BUILD) dir += `/legacy`;
|
||||
|
||||
return {
|
||||
@@ -17,7 +17,7 @@ export default {
|
||||
entryFileNames: '[name].[hash].js',
|
||||
chunkFileNames: '[name].[hash].js',
|
||||
format: 'esm',
|
||||
sourcemap: dev()
|
||||
sourcemap: dev
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -25,27 +25,27 @@ export default {
|
||||
server: {
|
||||
input: () => {
|
||||
return {
|
||||
server: `${locations.src()}/server.js`
|
||||
server: `${src}/server.js`
|
||||
};
|
||||
},
|
||||
|
||||
output: () => {
|
||||
return {
|
||||
dir: `${locations.dest()}/server`,
|
||||
dir: `${dest}/server`,
|
||||
format: 'cjs',
|
||||
sourcemap: dev()
|
||||
sourcemap: dev
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
serviceworker: {
|
||||
input: () => {
|
||||
return `${locations.src()}/service-worker.js`;
|
||||
return `${src}/service-worker.js`;
|
||||
},
|
||||
|
||||
output: () => {
|
||||
return {
|
||||
file: `${locations.dest()}/service-worker.js`,
|
||||
file: `${dest}/service-worker.js`,
|
||||
format: 'iife'
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
import { locations, dev } from './config';
|
||||
import { dev, src, dest } from './env';
|
||||
|
||||
export default {
|
||||
dev: dev(),
|
||||
dev,
|
||||
|
||||
client: {
|
||||
entry: () => {
|
||||
return {
|
||||
main: `${locations.src()}/client`
|
||||
main: `${src}/client`
|
||||
};
|
||||
},
|
||||
|
||||
output: () => {
|
||||
return {
|
||||
path: `${locations.dest()}/client`,
|
||||
path: `${dest}/client`,
|
||||
filename: '[hash]/[name].js',
|
||||
chunkFilename: '[hash]/[name].[id].js',
|
||||
publicPath: `client/`
|
||||
@@ -23,13 +23,13 @@ export default {
|
||||
server: {
|
||||
entry: () => {
|
||||
return {
|
||||
server: `${locations.src()}/server`
|
||||
server: `${src}/server`
|
||||
};
|
||||
},
|
||||
|
||||
output: () => {
|
||||
return {
|
||||
path: `${locations.dest()}/server`,
|
||||
path: `${dest}/server`,
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[hash]/[name].[id].js',
|
||||
libraryTarget: 'commonjs2'
|
||||
@@ -40,13 +40,13 @@ export default {
|
||||
serviceworker: {
|
||||
entry: () => {
|
||||
return {
|
||||
'service-worker': `${locations.src()}/service-worker`
|
||||
'service-worker': `${src}/service-worker`
|
||||
};
|
||||
},
|
||||
|
||||
output: () => {
|
||||
return {
|
||||
path: locations.dest(),
|
||||
path: dest,
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[name].[id].[hash].js'
|
||||
}
|
||||
@@ -135,10 +135,10 @@ export default class RollupCompiler {
|
||||
});
|
||||
}
|
||||
|
||||
static async load_config() {
|
||||
if (!rollup) rollup = relative('rollup', process.cwd());
|
||||
static async load_config(cwd: string) {
|
||||
if (!rollup) rollup = relative('rollup', cwd);
|
||||
|
||||
const input = path.resolve('rollup.config.js');
|
||||
const input = path.resolve(cwd, 'rollup.config.js');
|
||||
|
||||
const bundle = await rollup.rollup({
|
||||
input,
|
||||
|
||||
@@ -5,7 +5,7 @@ import RollupCompiler from './RollupCompiler';
|
||||
import extract_css from './extract_css';
|
||||
import { left_pad } from '../../utils';
|
||||
import { CompileResult, BuildInfo, CompileError, Chunk, CssFile } from './interfaces';
|
||||
import { ManifestData, Dirs, PageComponent } from '../../interfaces';
|
||||
import { ManifestData, Dirs } from '../../interfaces';
|
||||
|
||||
export default class RollupResult implements CompileResult {
|
||||
duration: number;
|
||||
|
||||
@@ -4,7 +4,7 @@ import hash from 'string-hash';
|
||||
import * as codec from 'sourcemap-codec';
|
||||
import { PageComponent, Dirs } from '../../interfaces';
|
||||
import { CompileResult } from './interfaces';
|
||||
import { posixify } from '../utils'
|
||||
import { posixify } from '../../utils'
|
||||
|
||||
const inline_sourcemap_header = 'data:application/json;charset=utf-8;base64,';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as path from 'path';
|
||||
import RollupCompiler from './RollupCompiler';
|
||||
import { WebpackCompiler } from './WebpackCompiler';
|
||||
import { set_dev, set_src, set_dest } from '../../config/env';
|
||||
|
||||
export type Compiler = RollupCompiler | WebpackCompiler;
|
||||
|
||||
@@ -10,9 +11,19 @@ export type Compilers = {
|
||||
serviceworker?: Compiler;
|
||||
}
|
||||
|
||||
export default async function create_compilers(bundler: 'rollup' | 'webpack'): Promise<Compilers> {
|
||||
export default async function create_compilers(
|
||||
bundler: 'rollup' | 'webpack',
|
||||
cwd: string,
|
||||
src: string,
|
||||
dest: string,
|
||||
dev: boolean
|
||||
): Promise<Compilers> {
|
||||
set_dev(dev);
|
||||
set_src(src);
|
||||
set_dest(dest);
|
||||
|
||||
if (bundler === 'rollup') {
|
||||
const config = await RollupCompiler.load_config();
|
||||
const config = await RollupCompiler.load_config(cwd);
|
||||
validate_config(config, 'rollup');
|
||||
|
||||
normalize_rollup_config(config.client);
|
||||
@@ -30,7 +41,7 @@ export default async function create_compilers(bundler: 'rollup' | 'webpack'): P
|
||||
}
|
||||
|
||||
if (bundler === 'webpack') {
|
||||
const config = require(path.resolve('webpack.config.js'));
|
||||
const config = require(path.resolve(cwd, 'webpack.config.js'));
|
||||
validate_config(config, 'webpack');
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { locations } from '../config';
|
||||
import { Page, PageComponent, ServerRoute, ManifestData } from '../interfaces';
|
||||
import { posixify, reserved_words } from './utils';
|
||||
import { posixify, reserved_words } from '../utils';
|
||||
|
||||
export default function create_manifest_data(cwd = locations.routes()): ManifestData {
|
||||
export default function create_manifest_data(cwd: string): ManifestData {
|
||||
// TODO remove in a future version
|
||||
if (!fs.existsSync(cwd)) {
|
||||
throw new Error(`As of Sapper 0.21, the routes/ directory should become src/routes/`);
|
||||
|
||||
@@ -1,41 +1,56 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import glob from 'tiny-glob/sync.js';
|
||||
import { posixify, stringify, write_if_changed } from './utils';
|
||||
import { dev, locations } from '../config';
|
||||
import { Page, PageComponent, ServerRoute, ManifestData } from '../interfaces';
|
||||
import { posixify, stringify, walk, write_if_changed } from '../utils';
|
||||
import { Page, PageComponent, ManifestData } from '../interfaces';
|
||||
|
||||
export function create_main_manifests({ bundler, manifest_data, dev_port }: {
|
||||
export function create_main_manifests({
|
||||
bundler,
|
||||
manifest_data,
|
||||
dev_port,
|
||||
dev,
|
||||
cwd,
|
||||
src,
|
||||
dest,
|
||||
routes,
|
||||
output
|
||||
}: {
|
||||
bundler: string,
|
||||
manifest_data: ManifestData;
|
||||
dev_port?: number;
|
||||
dev: boolean;
|
||||
cwd: string;
|
||||
src: string;
|
||||
dest: string;
|
||||
routes: string;
|
||||
output: string
|
||||
}) {
|
||||
const manifest_dir = path.resolve('__sapper__');
|
||||
if (!fs.existsSync(manifest_dir)) fs.mkdirSync(manifest_dir);
|
||||
if (!fs.existsSync(output)) fs.mkdirSync(output);
|
||||
|
||||
const path_to_routes = path.relative(manifest_dir, locations.routes());
|
||||
const path_to_routes = path.relative(output, routes);
|
||||
|
||||
const client_manifest = generate_client(manifest_data, path_to_routes, bundler, dev_port);
|
||||
const server_manifest = generate_server(manifest_data, path_to_routes);
|
||||
const client_manifest = generate_client(manifest_data, path_to_routes, bundler, dev, dev_port);
|
||||
const server_manifest = generate_server(manifest_data, path_to_routes, cwd, src, dest, dev);
|
||||
|
||||
write_if_changed(
|
||||
`${manifest_dir}/_layout.html`,
|
||||
`${output}/_layout.html`,
|
||||
`<svelte:component this={child.component} {...child.props}/>`
|
||||
);
|
||||
write_if_changed(`${manifest_dir}/client.js`, client_manifest);
|
||||
write_if_changed(`${manifest_dir}/server.js`, server_manifest);
|
||||
write_if_changed(`${output}/client.js`, client_manifest);
|
||||
write_if_changed(`${output}/server.js`, server_manifest);
|
||||
}
|
||||
|
||||
export function create_serviceworker_manifest({ manifest_data, client_files }: {
|
||||
export function create_serviceworker_manifest({ manifest_data, output, client_files, static_files }: {
|
||||
manifest_data: ManifestData;
|
||||
output: string;
|
||||
client_files: string[];
|
||||
static_files: string;
|
||||
}) {
|
||||
let files;
|
||||
let files: string[];
|
||||
|
||||
// TODO remove in a future version
|
||||
if (fs.existsSync(locations.static())) {
|
||||
files = glob('**', { cwd: locations.static(), filesOnly: true });
|
||||
if (fs.existsSync(static_files)) {
|
||||
files = walk(static_files);
|
||||
} else {
|
||||
// TODO remove in a future version
|
||||
if (fs.existsSync('assets')) {
|
||||
throw new Error(`As of Sapper 0.21, the assets/ directory should become static/`);
|
||||
}
|
||||
@@ -55,13 +70,14 @@ export function create_serviceworker_manifest({ manifest_data, client_files }: {
|
||||
export const routes = [\n\t${manifest_data.pages.map((r: Page) => `{ pattern: ${r.pattern} }`).join(',\n\t')}\n];
|
||||
`.replace(/^\t\t/gm, '').trim();
|
||||
|
||||
write_if_changed(`__sapper__/service-worker.js`, code);
|
||||
write_if_changed(`${output}/service-worker.js`, code);
|
||||
}
|
||||
|
||||
function generate_client(
|
||||
manifest_data: ManifestData,
|
||||
path_to_routes: string,
|
||||
bundler: string,
|
||||
dev: boolean,
|
||||
dev_port?: number
|
||||
) {
|
||||
const template_file = path.resolve(__dirname, '../templates/client.js');
|
||||
@@ -120,7 +136,7 @@ function generate_client(
|
||||
|
||||
let footer = '';
|
||||
|
||||
if (dev()) {
|
||||
if (dev) {
|
||||
const sapper_dev_client = posixify(
|
||||
path.resolve(__dirname, '../sapper-dev-client.js')
|
||||
);
|
||||
@@ -145,7 +161,11 @@ function generate_client(
|
||||
|
||||
function generate_server(
|
||||
manifest_data: ManifestData,
|
||||
path_to_routes: string
|
||||
path_to_routes: string,
|
||||
cwd: string,
|
||||
src: string,
|
||||
dest: string,
|
||||
dev: boolean
|
||||
) {
|
||||
const template_file = path.resolve(__dirname, '../templates/server.js');
|
||||
const template = fs.readFileSync(template_file, 'utf-8');
|
||||
@@ -206,13 +226,13 @@ function generate_server(
|
||||
error
|
||||
};`.replace(/^\t\t/gm, '').trim();
|
||||
|
||||
const build_dir = path.relative(process.cwd(), locations.dest());
|
||||
const src_dir = path.relative(process.cwd(), locations.src());
|
||||
const build_dir = path.relative(cwd, dest);
|
||||
const src_dir = path.relative(cwd, src);
|
||||
|
||||
return `// This file is generated by Sapper — do not edit it!\n` + template
|
||||
.replace('__BUILD__DIR__', JSON.stringify(build_dir))
|
||||
.replace('__SRC__DIR__', JSON.stringify(src_dir))
|
||||
.replace('__DEV__', dev() ? 'true' : 'false')
|
||||
.replace('__DEV__', dev ? 'true' : 'false')
|
||||
.replace(/const manifest = __MANIFEST__;/, code);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as fs from 'fs';
|
||||
import { locations } from '../config';
|
||||
|
||||
export default function read_template(dir = locations.src()) {
|
||||
export default function read_template(dir: string) {
|
||||
try {
|
||||
return fs.readFileSync(`${dir}/template.html`, 'utf-8');
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
import * as fs from 'fs';
|
||||
|
||||
const previous_contents = new Map();
|
||||
|
||||
export function write_if_changed(file: string, code: string) {
|
||||
if (code !== previous_contents.get(file)) {
|
||||
previous_contents.set(file, code);
|
||||
fs.writeFileSync(file, code);
|
||||
fudge_mtime(file);
|
||||
}
|
||||
}
|
||||
|
||||
export function posixify(file: string) {
|
||||
return file.replace(/[/\\]/g, '/');
|
||||
}
|
||||
|
||||
export function stringify(string: string, includeQuotes: boolean = true) {
|
||||
const quoted = JSON.stringify(string);
|
||||
return includeQuotes ? quoted : quoted.slice(1, -1);
|
||||
}
|
||||
|
||||
export function fudge_mtime(file: string) {
|
||||
// need to fudge the mtime so that webpack doesn't go doolally
|
||||
const { atime, mtime } = fs.statSync(file);
|
||||
fs.utimesSync(
|
||||
file,
|
||||
new Date(atime.getTime() - 999999),
|
||||
new Date(mtime.getTime() - 999999)
|
||||
);
|
||||
}
|
||||
|
||||
export const reserved_words = new Set([
|
||||
'arguments',
|
||||
'await',
|
||||
'break',
|
||||
'case',
|
||||
'catch',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'default',
|
||||
'delete',
|
||||
'do',
|
||||
'else',
|
||||
'enum',
|
||||
'eval',
|
||||
'export',
|
||||
'extends',
|
||||
'false',
|
||||
'finally',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'implements',
|
||||
'import',
|
||||
'in',
|
||||
'instanceof',
|
||||
'interface',
|
||||
'let',
|
||||
'new',
|
||||
'null',
|
||||
'package',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'return',
|
||||
'static',
|
||||
'super',
|
||||
'switch',
|
||||
'this',
|
||||
'throw',
|
||||
'true',
|
||||
'try',
|
||||
'typeof',
|
||||
'var',
|
||||
'void',
|
||||
'while',
|
||||
'with',
|
||||
'yield',
|
||||
]);
|
||||
@@ -1,3 +1,6 @@
|
||||
import * as child_process from 'child_process';
|
||||
import { CompileResult } from './core/create_compilers/interfaces';
|
||||
|
||||
export type Route = {
|
||||
id: string;
|
||||
handlers: {
|
||||
@@ -52,4 +55,47 @@ export type ManifestData = {
|
||||
components: PageComponent[];
|
||||
pages: Page[];
|
||||
server_routes: ServerRoute[];
|
||||
};
|
||||
};
|
||||
|
||||
export type ReadyEvent = {
|
||||
port: number;
|
||||
process: child_process.ChildProcess;
|
||||
};
|
||||
|
||||
export type ErrorEvent = {
|
||||
type: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type FatalEvent = {
|
||||
message: string;
|
||||
log?: string;
|
||||
};
|
||||
|
||||
export type InvalidEvent = {
|
||||
changed: string[];
|
||||
invalid: {
|
||||
client: boolean;
|
||||
server: boolean;
|
||||
serviceworker: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export type BuildEvent = {
|
||||
type: string;
|
||||
errors: Array<{ file: string, message: string, duplicate: boolean }>;
|
||||
warnings: Array<{ file: string, message: string, duplicate: boolean }>;
|
||||
duration: number;
|
||||
result: CompileResult;
|
||||
};
|
||||
|
||||
export type FileEvent = {
|
||||
file: string;
|
||||
size: number;
|
||||
};
|
||||
|
||||
export type FailureEvent = {
|
||||
|
||||
};
|
||||
|
||||
export type DoneEvent = {};
|
||||
111
src/utils.ts
111
src/utils.ts
@@ -1,3 +1,6 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
export function left_pad(str: string, len: number) {
|
||||
while (str.length < len) str = ` ${str}`;
|
||||
return str;
|
||||
@@ -7,4 +10,110 @@ export function repeat(str: string, i: number) {
|
||||
let result = '';
|
||||
while (i--) result += str;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export function format_milliseconds(ms: number) {
|
||||
if (ms < 1000) return `${ms}ms`;
|
||||
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
||||
|
||||
const minutes = ~~(ms / 60000);
|
||||
const seconds = Math.round((ms % 60000) / 1000);
|
||||
return `${minutes}m${seconds < 10 ? '0' : ''}${seconds}s`;
|
||||
}
|
||||
|
||||
export function elapsed(start: number) {
|
||||
return format_milliseconds(Date.now() - start);
|
||||
}
|
||||
|
||||
export function walk(cwd: string, dir = cwd, files: string[] = []) {
|
||||
fs.readdirSync(dir).forEach(file => {
|
||||
const resolved = path.resolve(dir, file);
|
||||
if (fs.statSync(resolved).isDirectory()) {
|
||||
walk(cwd, resolved, files);
|
||||
} else {
|
||||
files.push(posixify(path.relative(cwd, resolved)));
|
||||
}
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
export function posixify(str: string) {
|
||||
return str.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
const previous_contents = new Map();
|
||||
|
||||
export function write_if_changed(file: string, code: string) {
|
||||
if (code !== previous_contents.get(file)) {
|
||||
previous_contents.set(file, code);
|
||||
fs.writeFileSync(file, code);
|
||||
fudge_mtime(file);
|
||||
}
|
||||
}
|
||||
|
||||
export function stringify(string: string, includeQuotes: boolean = true) {
|
||||
const quoted = JSON.stringify(string);
|
||||
return includeQuotes ? quoted : quoted.slice(1, -1);
|
||||
}
|
||||
|
||||
export function fudge_mtime(file: string) {
|
||||
// need to fudge the mtime so that webpack doesn't go doolally
|
||||
const { atime, mtime } = fs.statSync(file);
|
||||
fs.utimesSync(
|
||||
file,
|
||||
new Date(atime.getTime() - 999999),
|
||||
new Date(mtime.getTime() - 999999)
|
||||
);
|
||||
}
|
||||
|
||||
export const reserved_words = new Set([
|
||||
'arguments',
|
||||
'await',
|
||||
'break',
|
||||
'case',
|
||||
'catch',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'default',
|
||||
'delete',
|
||||
'do',
|
||||
'else',
|
||||
'enum',
|
||||
'eval',
|
||||
'export',
|
||||
'extends',
|
||||
'false',
|
||||
'finally',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'implements',
|
||||
'import',
|
||||
'in',
|
||||
'instanceof',
|
||||
'interface',
|
||||
'let',
|
||||
'new',
|
||||
'null',
|
||||
'package',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'return',
|
||||
'static',
|
||||
'super',
|
||||
'switch',
|
||||
'this',
|
||||
'throw',
|
||||
'true',
|
||||
'try',
|
||||
'typeof',
|
||||
'var',
|
||||
'void',
|
||||
'while',
|
||||
'with',
|
||||
'yield',
|
||||
]);
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import * as http from 'http';
|
||||
@@ -23,34 +22,11 @@ describe('basics', function() {
|
||||
let goto: (href: string) => Promise<void>;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const emitter = build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
emitter.on('error', reject);
|
||||
|
||||
emitter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, prefetch, goto } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, prefetch, goto } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
@@ -17,34 +16,11 @@ describe('credentials', function() {
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const emitter = build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
emitter.on('error', reject);
|
||||
|
||||
emitter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
@@ -17,34 +16,11 @@ describe('encoding', function() {
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const emitter = build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
emitter.on('error', reject);
|
||||
|
||||
emitter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
@@ -17,34 +16,11 @@ describe('errors', function() {
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const emitter = build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
emitter.on('error', reject);
|
||||
|
||||
emitter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -1,70 +0,0 @@
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import { walk } from '../../utils';
|
||||
import * as api from '../../../api';
|
||||
|
||||
describe('export', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const builder = api.build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
builder.on('error', reject);
|
||||
builder.on('done', () => {
|
||||
// TODO it'd be nice if build and export returned promises.
|
||||
// not sure how best to combine promise and event emitter
|
||||
const exporter = api.exporter({
|
||||
build: '__sapper__/build',
|
||||
dest: '__sapper__/export',
|
||||
static: 'static',
|
||||
basepath: '',
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
exporter.on('error', (err: Error) => {
|
||||
console.error(err);
|
||||
reject(err);
|
||||
});
|
||||
exporter.on('done', () => fulfil());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('crawls a site', () => {
|
||||
const files = walk('__sapper__/export');
|
||||
|
||||
const client_assets = files.filter(file => file.startsWith('client/'));
|
||||
const non_client_assets = files.filter(file => !file.startsWith('client/')).sort();
|
||||
|
||||
assert.ok(client_assets.length > 0);
|
||||
|
||||
assert.deepEqual(non_client_assets, [
|
||||
'blog.json',
|
||||
'blog/bar.json',
|
||||
'blog/bar/index.html',
|
||||
'blog/baz.json',
|
||||
'blog/baz/index.html',
|
||||
'blog/foo.json',
|
||||
'blog/foo/index.html',
|
||||
'blog/index.html',
|
||||
'global.css',
|
||||
'index.html',
|
||||
'service-worker.js'
|
||||
]);
|
||||
});
|
||||
|
||||
// TODO test timeout, basepath
|
||||
});
|
||||
38
test/apps/export/test.ts
Normal file
38
test/apps/export/test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import * as assert from 'assert';
|
||||
import { walk } from '../../utils';
|
||||
import * as api from '../../../api';
|
||||
|
||||
describe('export', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await api.build({ cwd: __dirname });
|
||||
await api.export({ cwd: __dirname });
|
||||
});
|
||||
|
||||
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();
|
||||
|
||||
assert.ok(client_assets.length > 0);
|
||||
|
||||
assert.deepEqual(non_client_assets, [
|
||||
'blog.json',
|
||||
'blog/bar.json',
|
||||
'blog/bar/index.html',
|
||||
'blog/baz.json',
|
||||
'blog/baz/index.html',
|
||||
'blog/foo.json',
|
||||
'blog/foo/index.html',
|
||||
'blog/index.html',
|
||||
'global.css',
|
||||
'index.html',
|
||||
'service-worker.js'
|
||||
]);
|
||||
});
|
||||
|
||||
// TODO test timeout, basepath
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
@@ -12,34 +11,11 @@ describe('ignore', function() {
|
||||
let base: string;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const emitter = build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
emitter.on('error', reject);
|
||||
|
||||
emitter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
@@ -16,34 +15,11 @@ describe('layout', function() {
|
||||
let start: () => Promise<void>;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const emitter = build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
emitter.on('error', reject);
|
||||
|
||||
emitter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -19,34 +19,11 @@ describe('preloading', function() {
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const emitter = build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
emitter.on('error', reject);
|
||||
|
||||
emitter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
@@ -17,34 +16,11 @@ describe('redirects', function() {
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const emitter = build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
emitter.on('error', reject);
|
||||
|
||||
emitter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as assert from 'assert';
|
||||
import * as path from 'path';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
@@ -17,34 +16,11 @@ describe('scroll', function() {
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const emitter = build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
emitter.on('error', reject);
|
||||
|
||||
emitter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -1,60 +0,0 @@
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
|
||||
describe('store', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const emitter = build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
emitter.on('error', reject);
|
||||
|
||||
emitter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
|
||||
const title = () => page.$eval('h1', node => node.textContent);
|
||||
|
||||
it('renders store props', async () => {
|
||||
await page.goto(`${base}/store`);
|
||||
|
||||
assert.equal(await title(), 'hello world');
|
||||
|
||||
await start();
|
||||
assert.equal(await title(), 'hello world');
|
||||
});
|
||||
});
|
||||
36
test/apps/store/test.ts
Normal file
36
test/apps/store/test.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
|
||||
describe('store', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
|
||||
const title = () => page.$eval('h1', node => node.textContent);
|
||||
|
||||
it('renders store props', async () => {
|
||||
await page.goto(`${base}/store`);
|
||||
|
||||
assert.equal(await title(), 'hello world');
|
||||
|
||||
await start();
|
||||
assert.equal(await title(), 'hello world');
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as path from 'path';
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import * as api from '../../../api';
|
||||
@@ -13,50 +12,16 @@ describe('with-basepath', function() {
|
||||
let base: string;
|
||||
|
||||
// hooks
|
||||
before(() => {
|
||||
return new Promise((fulfil, reject) => {
|
||||
// TODO this is brittle. Make it unnecessary
|
||||
process.chdir(__dirname);
|
||||
process.env.NODE_ENV = 'production';
|
||||
before(async () => {
|
||||
await api.build({ cwd: __dirname });
|
||||
|
||||
// TODO this API isn't great. Rethink it
|
||||
const builder = api.build({
|
||||
bundler: 'rollup'
|
||||
}, {
|
||||
src: path.join(__dirname, 'src'),
|
||||
routes: path.join(__dirname, 'src/routes'),
|
||||
dest: path.join(__dirname, '__sapper__/build')
|
||||
});
|
||||
|
||||
builder.on('error', reject);
|
||||
builder.on('done', () => {
|
||||
// TODO it'd be nice if build and export returned promises.
|
||||
// not sure how best to combine promise and event emitter
|
||||
const exporter = api.exporter({
|
||||
build: '__sapper__/build',
|
||||
dest: '__sapper__/export',
|
||||
static: 'static',
|
||||
basepath: 'custom-basepath',
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
exporter.on('error', (err: Error) => {
|
||||
console.error(err);
|
||||
reject(err);
|
||||
});
|
||||
|
||||
exporter.on('done', async () => {
|
||||
try {
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page } = await runner.start());
|
||||
|
||||
fulfil();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
await api.export({
|
||||
cwd: __dirname,
|
||||
basepath: 'custom-basepath'
|
||||
});
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page } = await runner.start());
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
@@ -81,7 +46,7 @@ describe('with-basepath', function() {
|
||||
});
|
||||
|
||||
it('crawls an exported site with basepath', () => {
|
||||
const files = walk('__sapper__/export');
|
||||
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();
|
||||
Reference in New Issue
Block a user