mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-23 15:41:32 +00:00
Improve internal API
This commit is contained in:
@@ -2,4 +2,4 @@
|
|||||||
--require ts-node/register
|
--require ts-node/register
|
||||||
--recursive
|
--recursive
|
||||||
test/unit/*/*.ts
|
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"
|
"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": {
|
"parseurl": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
|
||||||
@@ -4599,15 +4593,6 @@
|
|||||||
"integrity": "sha512-wa5+qGVg9Yt7PB6rYm3kXlKzgzgivYTLRandezh43jjRqgyDyP+9YxfJpJiLs9yKD1WeU8/OvtToWpW7255FtA==",
|
"integrity": "sha512-wa5+qGVg9Yt7PB6rYm3kXlKzgzgivYTLRandezh43jjRqgyDyP+9YxfJpJiLs9yKD1WeU8/OvtToWpW7255FtA==",
|
||||||
"dev": true
|
"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": {
|
"process": {
|
||||||
"version": "0.11.10",
|
"version": "0.11.10",
|
||||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
"polka": "^0.4.0",
|
"polka": "^0.4.0",
|
||||||
"port-authority": "^1.0.5",
|
"port-authority": "^1.0.5",
|
||||||
"pretty-bytes": "^5.0.0",
|
"pretty-bytes": "^5.0.0",
|
||||||
"pretty-ms": "^3.1.0",
|
|
||||||
"puppeteer": "^1.9.0",
|
"puppeteer": "^1.9.0",
|
||||||
"require-relative": "^0.8.7",
|
"require-relative": "^0.8.7",
|
||||||
"rimraf": "^2.6.2",
|
"rimraf": "^2.6.2",
|
||||||
@@ -61,7 +60,6 @@
|
|||||||
"sirv": "^0.2.2",
|
"sirv": "^0.2.2",
|
||||||
"svelte": "^2.6.3",
|
"svelte": "^2.6.3",
|
||||||
"svelte-loader": "^2.9.0",
|
"svelte-loader": "^2.9.0",
|
||||||
"tiny-glob": "^0.2.2",
|
|
||||||
"ts-node": "^7.0.1",
|
"ts-node": "^7.0.1",
|
||||||
"typescript": "^2.8.3",
|
"typescript": "^2.8.3",
|
||||||
"webpack": "^4.8.3",
|
"webpack": "^4.8.3",
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ export default [
|
|||||||
`src/api.ts`,
|
`src/api.ts`,
|
||||||
`src/cli.ts`,
|
`src/cli.ts`,
|
||||||
`src/core.ts`,
|
`src/core.ts`,
|
||||||
`src/rollup.ts`,
|
`src/config/rollup.ts`,
|
||||||
`src/webpack.ts`
|
`src/config/webpack.ts`
|
||||||
],
|
],
|
||||||
output: {
|
output: {
|
||||||
dir: 'dist',
|
dir: 'dist',
|
||||||
|
|||||||
10
src/api.ts
10
src/api.ts
@@ -1,6 +1,4 @@
|
|||||||
import { dev } from './api/dev';
|
export { dev } from './api/dev';
|
||||||
import { build } from './api/build';
|
export { build } from './api/build';
|
||||||
import { exporter } from './api/export';
|
export { export } from './api/export';
|
||||||
import { find_page } from './api/find_page';
|
export { find_page } from './api/find_page';
|
||||||
|
|
||||||
export { dev, build, exporter, find_page };
|
|
||||||
@@ -2,44 +2,51 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from 'mkdirp';
|
||||||
import rimraf from 'rimraf';
|
import rimraf from 'rimraf';
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
import minify_html from './utils/minify_html';
|
import minify_html from './utils/minify_html';
|
||||||
import { create_compilers, create_main_manifests, create_manifest_data, create_serviceworker_manifest } from '../core';
|
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 { copy_shimport } from './utils/copy_shimport';
|
||||||
import { Dirs } from '../interfaces';
|
|
||||||
import read_template from '../core/read_template';
|
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 = {
|
type Opts = {
|
||||||
legacy: boolean;
|
cwd?: string;
|
||||||
bundler: 'rollup' | 'webpack';
|
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) {
|
export async function build({
|
||||||
const emitter = new EventEmitter();
|
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(
|
bundler,
|
||||||
() => {
|
legacy = false,
|
||||||
emitter.emit('done', <events.DoneEvent>{}); // TODO do we need to pass back any info?
|
oncompile = noop
|
||||||
},
|
}: Opts = {}) {
|
||||||
error => {
|
bundler = validate_bundler(bundler);
|
||||||
emitter.emit('error', <events.ErrorEvent>{
|
|
||||||
error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
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(dest, '**/*'));
|
||||||
rimraf.sync(path.join(dirs.dest, '**/*'));
|
mkdirp.sync(`${dest}/client`);
|
||||||
mkdirp.sync(`${dirs.dest}/client`);
|
copy_shimport(dest);
|
||||||
copy_shimport(dirs.dest);
|
|
||||||
|
|
||||||
// minify src/template.html
|
// minify src/template.html
|
||||||
// TODO compile this to a function? could be quicker than str.replace(...).replace(...).replace(...)
|
// 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
|
// remove this in a future version
|
||||||
if (template.indexOf('%sapper.base%') === -1) {
|
if (template.indexOf('%sapper.base%') === -1) {
|
||||||
@@ -48,47 +55,53 @@ async function execute(emitter: EventEmitter, opts: Opts, dirs: Dirs) {
|
|||||||
throw error;
|
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 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();
|
const client_result = await client.compile();
|
||||||
emitter.emit('build', <events.BuildEvent>{
|
oncompile({
|
||||||
type: 'client',
|
type: 'client',
|
||||||
// TODO duration/warnings
|
|
||||||
result: client_result
|
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';
|
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();
|
const client_result = await client.compile();
|
||||||
|
|
||||||
emitter.emit('build', <events.BuildEvent>{
|
oncompile({
|
||||||
type: 'client (legacy)',
|
type: 'client (legacy)',
|
||||||
// TODO duration/warnings
|
|
||||||
result: client_result
|
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;
|
build_info.legacy_assets = client_result.assets;
|
||||||
delete process.env.SAPPER_LEGACY_BUILD;
|
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();
|
const server_stats = await server.compile();
|
||||||
emitter.emit('build', <events.BuildEvent>{
|
oncompile({
|
||||||
type: 'server',
|
type: 'server',
|
||||||
// TODO duration/warnings
|
|
||||||
result: server_stats
|
result: server_stats
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -97,14 +110,15 @@ async function execute(emitter: EventEmitter, opts: Opts, dirs: Dirs) {
|
|||||||
if (serviceworker) {
|
if (serviceworker) {
|
||||||
create_serviceworker_manifest({
|
create_serviceworker_manifest({
|
||||||
manifest_data,
|
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();
|
serviceworker_stats = await serviceworker.compile();
|
||||||
|
|
||||||
emitter.emit('build', <events.BuildEvent>{
|
oncompile({
|
||||||
type: 'serviceworker',
|
type: 'serviceworker',
|
||||||
// TODO duration/warnings
|
|
||||||
result: serviceworker_stats
|
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 * as ports from 'port-authority';
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from 'mkdirp';
|
||||||
import rimraf from 'rimraf';
|
import rimraf from 'rimraf';
|
||||||
import { locations } from '../config';
|
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { create_manifest_data, create_main_manifests, create_compilers, create_serviceworker_manifest } from '../core';
|
import { create_manifest_data, create_main_manifests, create_compilers, create_serviceworker_manifest } from '../core';
|
||||||
import { Compiler, Compilers } from '../core/create_compilers';
|
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 Deferred from './utils/Deferred';
|
||||||
import * as events from './interfaces';
|
import validate_bundler from './utils/validate_bundler';
|
||||||
import validate_bundler from '../cli/utils/validate_bundler';
|
|
||||||
import { copy_shimport } from './utils/copy_shimport';
|
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 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);
|
return new Watcher(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Watcher extends EventEmitter {
|
class Watcher extends EventEmitter {
|
||||||
bundler: string;
|
bundler: 'rollup' | 'webpack';
|
||||||
dirs: {
|
dirs: {
|
||||||
|
cwd: string;
|
||||||
src: string;
|
src: string;
|
||||||
dest: string;
|
dest: string;
|
||||||
routes: string;
|
routes: string;
|
||||||
rollup: string;
|
output: string;
|
||||||
webpack: string;
|
static_files: string;
|
||||||
}
|
}
|
||||||
port: number;
|
port: number;
|
||||||
closed: boolean;
|
closed: boolean;
|
||||||
@@ -54,34 +69,23 @@ class Watcher extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
src = locations.src(),
|
cwd = process.cwd(),
|
||||||
dest = locations.dest(),
|
src = path.join(cwd, 'src'),
|
||||||
routes = locations.routes(),
|
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,
|
'dev-port': dev_port,
|
||||||
live,
|
live,
|
||||||
hot,
|
hot,
|
||||||
'devtools-port': devtools_port,
|
'devtools-port': devtools_port,
|
||||||
bundler,
|
bundler,
|
||||||
webpack = 'webpack',
|
|
||||||
rollup = 'rollup',
|
|
||||||
port = +process.env.PORT
|
port = +process.env.PORT
|
||||||
}: {
|
}: Opts) {
|
||||||
src: string,
|
|
||||||
dest: string,
|
|
||||||
routes: string,
|
|
||||||
'dev-port': number,
|
|
||||||
live: boolean,
|
|
||||||
hot: boolean,
|
|
||||||
'devtools-port': number,
|
|
||||||
bundler?: string,
|
|
||||||
webpack: string,
|
|
||||||
rollup: string,
|
|
||||||
port: number
|
|
||||||
}) {
|
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.bundler = validate_bundler(bundler);
|
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.port = port;
|
||||||
this.closed = false;
|
this.closed = false;
|
||||||
|
|
||||||
@@ -101,7 +105,7 @@ class Watcher extends EventEmitter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// remove this in a future version
|
// remove this in a future version
|
||||||
const template = read_template();
|
const template = read_template(src);
|
||||||
if (template.indexOf('%sapper.base%') === -1) {
|
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>`);
|
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`;
|
error.code = `missing-sapper-base`;
|
||||||
@@ -120,7 +124,7 @@ class Watcher extends EventEmitter {
|
|||||||
async init() {
|
async init() {
|
||||||
if (this.port) {
|
if (this.port) {
|
||||||
if (!await ports.check(this.port)) {
|
if (!await ports.check(this.port)) {
|
||||||
this.emit('fatal', <events.FatalEvent>{
|
this.emit('fatal', <FatalEvent>{
|
||||||
message: `Port ${this.port} is unavailable`
|
message: `Port ${this.port} is unavailable`
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -129,7 +133,7 @@ class Watcher extends EventEmitter {
|
|||||||
this.port = await ports.find(3000);
|
this.port = await ports.find(3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { dest } = this.dirs;
|
const { cwd, src, dest, routes, output, static_files } = this.dirs;
|
||||||
rimraf.sync(dest);
|
rimraf.sync(dest);
|
||||||
mkdirp.sync(`${dest}/client`);
|
mkdirp.sync(`${dest}/client`);
|
||||||
if (this.bundler === 'rollup') copy_shimport(dest);
|
if (this.bundler === 'rollup') copy_shimport(dest);
|
||||||
@@ -142,10 +146,16 @@ class Watcher extends EventEmitter {
|
|||||||
let manifest_data: ManifestData;
|
let manifest_data: ManifestData;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
manifest_data = create_manifest_data();
|
manifest_data = create_manifest_data(routes);
|
||||||
create_main_manifests({ bundler: this.bundler, manifest_data, dev_port: this.dev_port });
|
create_main_manifests({
|
||||||
|
bundler: this.bundler,
|
||||||
|
manifest_data,
|
||||||
|
dev: true,
|
||||||
|
dev_port: this.dev_port,
|
||||||
|
cwd, src, dest, routes, output
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.emit('fatal', <events.FatalEvent>{
|
this.emit('fatal', <FatalEvent>{
|
||||||
message: err.message
|
message: err.message
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -155,7 +165,7 @@ class Watcher extends EventEmitter {
|
|||||||
|
|
||||||
this.filewatchers.push(
|
this.filewatchers.push(
|
||||||
watch_dir(
|
watch_dir(
|
||||||
locations.routes(),
|
routes,
|
||||||
({ path: file, stats }) => {
|
({ path: file, stats }) => {
|
||||||
if (stats.isDirectory()) {
|
if (stats.isDirectory()) {
|
||||||
return path.basename(file)[0] !== '_';
|
return path.basename(file)[0] !== '_';
|
||||||
@@ -164,19 +174,25 @@ class Watcher extends EventEmitter {
|
|||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
try {
|
try {
|
||||||
const new_manifest_data = create_manifest_data();
|
const new_manifest_data = create_manifest_data(routes);
|
||||||
create_main_manifests({ bundler: this.bundler, manifest_data, dev_port: this.dev_port });
|
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;
|
manifest_data = new_manifest_data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.emit('error', <events.ErrorEvent>{
|
this.emit('error', <ErrorEvent>{
|
||||||
message: err.message
|
message: err.message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
||||||
fs.watch(`${locations.src()}/template.html`, () => {
|
fs.watch(`${src}/template.html`, () => {
|
||||||
this.dev_server.send({
|
this.dev_server.send({
|
||||||
action: 'reload'
|
action: 'reload'
|
||||||
});
|
});
|
||||||
@@ -186,12 +202,12 @@ class Watcher extends EventEmitter {
|
|||||||
let deferred = new Deferred();
|
let deferred = new Deferred();
|
||||||
|
|
||||||
// TODO watch the configs themselves?
|
// 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 = '';
|
let log = '';
|
||||||
|
|
||||||
const emitFatal = () => {
|
const emitFatal = () => {
|
||||||
this.emit('fatal', <events.FatalEvent>{
|
this.emit('fatal', <FatalEvent>{
|
||||||
message: `Server crashed`,
|
message: `Server crashed`,
|
||||||
log
|
log
|
||||||
});
|
});
|
||||||
@@ -215,7 +231,7 @@ class Watcher extends EventEmitter {
|
|||||||
|
|
||||||
ports.wait(this.port)
|
ports.wait(this.port)
|
||||||
.then((() => {
|
.then((() => {
|
||||||
this.emit('ready', <events.ReadyEvent>{
|
this.emit('ready', <ReadyEvent>{
|
||||||
port: this.port,
|
port: this.port,
|
||||||
process: this.proc
|
process: this.proc
|
||||||
});
|
});
|
||||||
@@ -233,7 +249,7 @@ class Watcher extends EventEmitter {
|
|||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (this.crashed) return;
|
if (this.crashed) return;
|
||||||
|
|
||||||
this.emit('fatal', <events.FatalEvent>{
|
this.emit('fatal', <FatalEvent>{
|
||||||
message: `Server is not listening on port ${this.port}`,
|
message: `Server is not listening on port ${this.port}`,
|
||||||
log
|
log
|
||||||
});
|
});
|
||||||
@@ -312,7 +328,9 @@ class Watcher extends EventEmitter {
|
|||||||
|
|
||||||
create_serviceworker_manifest({
|
create_serviceworker_manifest({
|
||||||
manifest_data,
|
manifest_data,
|
||||||
client_files
|
output,
|
||||||
|
client_files,
|
||||||
|
static_files
|
||||||
});
|
});
|
||||||
|
|
||||||
deferred.fulfil();
|
deferred.fulfil();
|
||||||
@@ -361,7 +379,7 @@ class Watcher extends EventEmitter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
this.emit('invalid', <events.InvalidEvent>{
|
this.emit('invalid', <InvalidEvent>{
|
||||||
changed: Array.from(this.current_build.changed),
|
changed: Array.from(this.current_build.changed),
|
||||||
invalid: {
|
invalid: {
|
||||||
server: this.current_build.rebuilding.has('server'),
|
server: this.current_build.rebuilding.has('server'),
|
||||||
@@ -384,7 +402,7 @@ class Watcher extends EventEmitter {
|
|||||||
|
|
||||||
compiler.watch((err?: Error, result?: CompileResult) => {
|
compiler.watch((err?: Error, result?: CompileResult) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.emit('error', <events.ErrorEvent>{
|
this.emit('error', <ErrorEvent>{
|
||||||
type: name,
|
type: name,
|
||||||
message: err.message
|
message: err.message
|
||||||
});
|
});
|
||||||
@@ -455,14 +473,12 @@ class DevServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function noop() {}
|
|
||||||
|
|
||||||
function watch_dir(
|
function watch_dir(
|
||||||
dir: string,
|
dir: string,
|
||||||
filter: ({ path, stats }: { path: string, stats: fs.Stats }) => boolean,
|
filter: ({ path, stats }: { path: string, stats: fs.Stats }) => boolean,
|
||||||
callback: () => void
|
callback: () => void
|
||||||
) {
|
) {
|
||||||
let watch;
|
let watch: any;
|
||||||
let closed = false;
|
let closed = false;
|
||||||
|
|
||||||
import('cheap-watch').then(CheapWatch => {
|
import('cheap-watch').then(CheapWatch => {
|
||||||
@@ -470,7 +486,7 @@ function watch_dir(
|
|||||||
|
|
||||||
watch = new CheapWatch({ dir, filter, debounce: 50 });
|
watch = new CheapWatch({ dir, filter, debounce: 50 });
|
||||||
|
|
||||||
watch.on('+', ({ isNew }) => {
|
watch.on('+', ({ isNew }: { isNew: boolean }) => {
|
||||||
if (isNew) callback();
|
if (isNew) callback();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,58 +4,52 @@ import * as sander from 'sander';
|
|||||||
import * as url from 'url';
|
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 clean_html from './utils/clean_html';
|
import clean_html from './utils/clean_html';
|
||||||
import minify_html from './utils/minify_html';
|
import minify_html from './utils/minify_html';
|
||||||
import Deferred from './utils/Deferred';
|
import Deferred from './utils/Deferred';
|
||||||
import * as events from './interfaces';
|
import { noop } from './utils/noop';
|
||||||
|
|
||||||
type Opts = {
|
type Opts = {
|
||||||
build: string,
|
build_dir?: string,
|
||||||
dest: string,
|
export_dir?: string,
|
||||||
static: string,
|
cwd?: string,
|
||||||
|
static?: string,
|
||||||
basepath?: 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) {
|
function resolve(from: string, to: string) {
|
||||||
return url.parse(url.resolve(from, to));
|
return url.parse(url.resolve(from, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
type URL = url.UrlWithStringQuery;
|
type URL = url.UrlWithStringQuery;
|
||||||
|
|
||||||
async function execute(emitter: EventEmitter, opts: Opts) {
|
export { _export as export };
|
||||||
const export_dir = path.join(opts.dest, opts.basepath);
|
|
||||||
|
|
||||||
|
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
|
// Prep output directory
|
||||||
sander.rimrafSync(export_dir);
|
sander.rimrafSync(export_dir);
|
||||||
|
|
||||||
sander.copydirSync(opts.static).to(export_dir);
|
sander.copydirSync(static_files).to(export_dir);
|
||||||
sander.copydirSync(opts.build, 'client').to(export_dir, 'client');
|
sander.copydirSync(build_dir, 'client').to(export_dir, 'client');
|
||||||
|
|
||||||
if (sander.existsSync(opts.build, 'service-worker.js')) {
|
if (sander.existsSync(build_dir, 'service-worker.js')) {
|
||||||
sander.copyFileSync(opts.build, 'service-worker.js').to(export_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')) {
|
if (sander.existsSync(build_dir, 'service-worker.js.map')) {
|
||||||
sander.copyFileSync(opts.build, 'service-worker.js.map').to(export_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);
|
const port = await ports.find(3000);
|
||||||
@@ -64,19 +58,18 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
|||||||
const host = `localhost:${port}`;
|
const host = `localhost:${port}`;
|
||||||
const origin = `${protocol}//${host}`;
|
const origin = `${protocol}//${host}`;
|
||||||
|
|
||||||
const root = resolve(origin, opts.basepath || '');
|
const root = resolve(origin, basepath);
|
||||||
if (!root.href.endsWith('/')) root.href += '/';
|
if (!root.href.endsWith('/')) root.href += '/';
|
||||||
|
|
||||||
emitter.emit('info', {
|
oninfo({
|
||||||
message: `Crawling ${root.href}`
|
message: `Crawling ${root.href}`
|
||||||
});
|
});
|
||||||
|
|
||||||
const proc = child_process.fork(path.resolve(`${opts.build}/server/server.js`), [], {
|
const proc = child_process.fork(path.resolve(`${build_dir}/server/server.js`), [], {
|
||||||
cwd: process.cwd(),
|
cwd,
|
||||||
env: Object.assign({
|
env: Object.assign({
|
||||||
PORT: port,
|
PORT: port,
|
||||||
NODE_ENV: 'production',
|
NODE_ENV: 'production',
|
||||||
SAPPER_DEST: opts.build,
|
|
||||||
SAPPER_EXPORT: 'true'
|
SAPPER_EXPORT: 'true'
|
||||||
}, process.env)
|
}, process.env)
|
||||||
});
|
});
|
||||||
@@ -98,7 +91,7 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
|||||||
body = minify_html(body);
|
body = minify_html(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
emitter.emit('file', <events.FileEvent>{
|
onfile({
|
||||||
file,
|
file,
|
||||||
size: body.length,
|
size: body.length,
|
||||||
status
|
status
|
||||||
@@ -119,9 +112,9 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
|||||||
seen.add(pathname);
|
seen.add(pathname);
|
||||||
|
|
||||||
const timeout_deferred = new Deferred();
|
const timeout_deferred = new Deferred();
|
||||||
const timeout = setTimeout(() => {
|
const the_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);
|
}, timeout);
|
||||||
|
|
||||||
const r = await Promise.race([
|
const r = await Promise.race([
|
||||||
fetch(url.href, {
|
fetch(url.href, {
|
||||||
@@ -130,7 +123,7 @@ async function execute(emitter: EventEmitter, opts: Opts) {
|
|||||||
timeout_deferred.promise
|
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 type = r.headers.get('Content-Type');
|
||||||
let body = await r.text();
|
let body = await r.text();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { locations } from '../config';
|
|
||||||
import { create_manifest_data } from '../core';
|
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);
|
const { pages } = create_manifest_data(cwd);
|
||||||
|
|
||||||
for (let i = 0; i < pages.length; i += 1) {
|
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() {}
|
||||||
198
src/cli.ts
198
src/cli.ts
@@ -2,11 +2,21 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import sade from 'sade';
|
import sade from 'sade';
|
||||||
import colors from 'kleur';
|
import colors from 'kleur';
|
||||||
import prettyMs from 'pretty-ms';
|
|
||||||
import * as pkg from '../package.json';
|
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);
|
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')
|
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')
|
||||||
@@ -21,10 +31,93 @@ prog.command('dev')
|
|||||||
'dev-port': number,
|
'dev-port': number,
|
||||||
live: boolean,
|
live: boolean,
|
||||||
hot: boolean,
|
hot: boolean,
|
||||||
bundler?: string
|
bundler?: 'rollup' | 'webpack'
|
||||||
}) => {
|
}) => {
|
||||||
const { dev } = await import('./cli/dev');
|
const cwd = path.resolve(process.env.SAPPER_BASE || '');
|
||||||
dev(opts);
|
|
||||||
|
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]')
|
prog.command('build [dest]')
|
||||||
@@ -36,25 +129,20 @@ prog.command('build [dest]')
|
|||||||
.action(async (dest = '__sapper__/build', opts: {
|
.action(async (dest = '__sapper__/build', opts: {
|
||||||
port: string,
|
port: string,
|
||||||
legacy: boolean,
|
legacy: boolean,
|
||||||
bundler?: string
|
bundler?: 'rollup' | 'webpack'
|
||||||
}) => {
|
}) => {
|
||||||
console.log(`> Building...`);
|
console.log(`> Building...`);
|
||||||
|
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
const cwd = path.resolve(process.env.SAPPER_BASE || '');
|
||||||
process.env.SAPPER_DEST = dest;
|
|
||||||
|
|
||||||
const start = Date.now();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { build } = await import('./cli/build');
|
await _build(opts.bundler, opts.legacy, cwd, dest);
|
||||||
await build(opts);
|
|
||||||
|
|
||||||
const launcher = path.resolve(dest, 'index.js');
|
const launcher = path.resolve(dest, 'index.js');
|
||||||
|
|
||||||
fs.writeFileSync(launcher, `
|
fs.writeFileSync(launcher, `
|
||||||
// generated by sapper build at ${new Date().toISOString()}
|
// generated by sapper build at ${new Date().toISOString()}
|
||||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||||
process.env.SAPPER_DEST = __dirname;
|
|
||||||
process.env.PORT = process.env.PORT || ${opts.port || 3000};
|
process.env.PORT = process.env.PORT || ${opts.port || 3000};
|
||||||
|
|
||||||
console.log('Starting server on port ' + process.env.PORT);
|
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]')
|
prog.command('export [dest]')
|
||||||
.describe('Export your app as static files (if possible)')
|
.describe('Export your app as static files (if possible)')
|
||||||
.option('--build', '(Re)build app before exporting', true)
|
.option('--build', '(Re)build app before exporting', true)
|
||||||
@@ -88,26 +167,46 @@ prog.command('export [dest]')
|
|||||||
.action(async (dest = '__sapper__/export', opts: {
|
.action(async (dest = '__sapper__/export', opts: {
|
||||||
build: boolean,
|
build: boolean,
|
||||||
legacy: boolean,
|
legacy: boolean,
|
||||||
bundler?: string,
|
bundler?: 'rollup' | 'webpack',
|
||||||
'build-dir': string,
|
'build-dir': string,
|
||||||
basepath?: string,
|
basepath?: string,
|
||||||
timeout: number | false
|
timeout: number | false
|
||||||
}) => {
|
}) => {
|
||||||
process.env.NODE_ENV = 'production';
|
const cwd = path.resolve(process.env.SAPPER_BASE || '');
|
||||||
process.env.SAPPER_DEST = opts['build-dir'];
|
|
||||||
|
|
||||||
const start = Date.now();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (opts.build) {
|
if (opts.build) {
|
||||||
console.log(`> Building...`);
|
console.log(`> Building...`);
|
||||||
const { build } = await import('./cli/build');
|
await _build(opts.bundler, opts.legacy, cwd, opts['build-dir']);
|
||||||
await build(opts);
|
|
||||||
console.error(`\n> Built in ${elapsed(start)}`);
|
console.error(`\n> Built in ${elapsed(start)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { exporter } = await import('./cli/export');
|
const { export: _export } = await import('./api/export');
|
||||||
await exporter(dest, opts);
|
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.`);
|
console.error(`\n> Finished in ${elapsed(start)}. Type ${colors.bold.cyan(`npx serve ${dest}`)} to run the app.`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(colors.bold.red(`> ${err.message}`));
|
console.error(colors.bold.red(`> ${err.message}`));
|
||||||
@@ -115,10 +214,41 @@ prog.command('export [dest]')
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO upgrade
|
|
||||||
|
|
||||||
prog.parse(process.argv);
|
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 {
|
export default {
|
||||||
dev: dev(),
|
dev,
|
||||||
|
|
||||||
client: {
|
client: {
|
||||||
input: () => {
|
input: () => {
|
||||||
return `${locations.src()}/client.js`
|
return `${src}/client.js`
|
||||||
},
|
},
|
||||||
|
|
||||||
output: () => {
|
output: () => {
|
||||||
let dir = `${locations.dest()}/client`;
|
let dir = `${dest}/client`;
|
||||||
if (process.env.SAPPER_LEGACY_BUILD) dir += `/legacy`;
|
if (process.env.SAPPER_LEGACY_BUILD) dir += `/legacy`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -17,7 +17,7 @@ export default {
|
|||||||
entryFileNames: '[name].[hash].js',
|
entryFileNames: '[name].[hash].js',
|
||||||
chunkFileNames: '[name].[hash].js',
|
chunkFileNames: '[name].[hash].js',
|
||||||
format: 'esm',
|
format: 'esm',
|
||||||
sourcemap: dev()
|
sourcemap: dev
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -25,27 +25,27 @@ export default {
|
|||||||
server: {
|
server: {
|
||||||
input: () => {
|
input: () => {
|
||||||
return {
|
return {
|
||||||
server: `${locations.src()}/server.js`
|
server: `${src}/server.js`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
output: () => {
|
output: () => {
|
||||||
return {
|
return {
|
||||||
dir: `${locations.dest()}/server`,
|
dir: `${dest}/server`,
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
sourcemap: dev()
|
sourcemap: dev
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
serviceworker: {
|
serviceworker: {
|
||||||
input: () => {
|
input: () => {
|
||||||
return `${locations.src()}/service-worker.js`;
|
return `${src}/service-worker.js`;
|
||||||
},
|
},
|
||||||
|
|
||||||
output: () => {
|
output: () => {
|
||||||
return {
|
return {
|
||||||
file: `${locations.dest()}/service-worker.js`,
|
file: `${dest}/service-worker.js`,
|
||||||
format: 'iife'
|
format: 'iife'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
import { locations, dev } from './config';
|
import { dev, src, dest } from './env';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
dev: dev(),
|
dev,
|
||||||
|
|
||||||
client: {
|
client: {
|
||||||
entry: () => {
|
entry: () => {
|
||||||
return {
|
return {
|
||||||
main: `${locations.src()}/client`
|
main: `${src}/client`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
output: () => {
|
output: () => {
|
||||||
return {
|
return {
|
||||||
path: `${locations.dest()}/client`,
|
path: `${dest}/client`,
|
||||||
filename: '[hash]/[name].js',
|
filename: '[hash]/[name].js',
|
||||||
chunkFilename: '[hash]/[name].[id].js',
|
chunkFilename: '[hash]/[name].[id].js',
|
||||||
publicPath: `client/`
|
publicPath: `client/`
|
||||||
@@ -23,13 +23,13 @@ export default {
|
|||||||
server: {
|
server: {
|
||||||
entry: () => {
|
entry: () => {
|
||||||
return {
|
return {
|
||||||
server: `${locations.src()}/server`
|
server: `${src}/server`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
output: () => {
|
output: () => {
|
||||||
return {
|
return {
|
||||||
path: `${locations.dest()}/server`,
|
path: `${dest}/server`,
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
chunkFilename: '[hash]/[name].[id].js',
|
chunkFilename: '[hash]/[name].[id].js',
|
||||||
libraryTarget: 'commonjs2'
|
libraryTarget: 'commonjs2'
|
||||||
@@ -40,13 +40,13 @@ export default {
|
|||||||
serviceworker: {
|
serviceworker: {
|
||||||
entry: () => {
|
entry: () => {
|
||||||
return {
|
return {
|
||||||
'service-worker': `${locations.src()}/service-worker`
|
'service-worker': `${src}/service-worker`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
output: () => {
|
output: () => {
|
||||||
return {
|
return {
|
||||||
path: locations.dest(),
|
path: dest,
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
chunkFilename: '[name].[id].[hash].js'
|
chunkFilename: '[name].[id].[hash].js'
|
||||||
}
|
}
|
||||||
@@ -135,10 +135,10 @@ export default class RollupCompiler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static async load_config() {
|
static async load_config(cwd: string) {
|
||||||
if (!rollup) rollup = relative('rollup', process.cwd());
|
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({
|
const bundle = await rollup.rollup({
|
||||||
input,
|
input,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import RollupCompiler from './RollupCompiler';
|
|||||||
import extract_css from './extract_css';
|
import extract_css from './extract_css';
|
||||||
import { left_pad } from '../../utils';
|
import { left_pad } from '../../utils';
|
||||||
import { CompileResult, BuildInfo, CompileError, Chunk, CssFile } from './interfaces';
|
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 {
|
export default class RollupResult implements CompileResult {
|
||||||
duration: number;
|
duration: number;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import hash from 'string-hash';
|
|||||||
import * as codec from 'sourcemap-codec';
|
import * as codec from 'sourcemap-codec';
|
||||||
import { PageComponent, Dirs } from '../../interfaces';
|
import { PageComponent, Dirs } from '../../interfaces';
|
||||||
import { CompileResult } 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,';
|
const inline_sourcemap_header = 'data:application/json;charset=utf-8;base64,';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import RollupCompiler from './RollupCompiler';
|
import RollupCompiler from './RollupCompiler';
|
||||||
import { WebpackCompiler } from './WebpackCompiler';
|
import { WebpackCompiler } from './WebpackCompiler';
|
||||||
|
import { set_dev, set_src, set_dest } from '../../config/env';
|
||||||
|
|
||||||
export type Compiler = RollupCompiler | WebpackCompiler;
|
export type Compiler = RollupCompiler | WebpackCompiler;
|
||||||
|
|
||||||
@@ -10,9 +11,19 @@ export type Compilers = {
|
|||||||
serviceworker?: Compiler;
|
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') {
|
if (bundler === 'rollup') {
|
||||||
const config = await RollupCompiler.load_config();
|
const config = await RollupCompiler.load_config(cwd);
|
||||||
validate_config(config, 'rollup');
|
validate_config(config, 'rollup');
|
||||||
|
|
||||||
normalize_rollup_config(config.client);
|
normalize_rollup_config(config.client);
|
||||||
@@ -30,7 +41,7 @@ export default async function create_compilers(bundler: 'rollup' | 'webpack'): P
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bundler === 'webpack') {
|
if (bundler === 'webpack') {
|
||||||
const config = require(path.resolve('webpack.config.js'));
|
const config = require(path.resolve(cwd, 'webpack.config.js'));
|
||||||
validate_config(config, 'webpack');
|
validate_config(config, 'webpack');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { locations } from '../config';
|
|
||||||
import { Page, PageComponent, ServerRoute, ManifestData } from '../interfaces';
|
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
|
// TODO remove in a future version
|
||||||
if (!fs.existsSync(cwd)) {
|
if (!fs.existsSync(cwd)) {
|
||||||
throw new Error(`As of Sapper 0.21, the routes/ directory should become src/routes/`);
|
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 fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import glob from 'tiny-glob/sync.js';
|
import { posixify, stringify, walk, write_if_changed } from '../utils';
|
||||||
import { posixify, stringify, write_if_changed } from './utils';
|
import { Page, PageComponent, ManifestData } from '../interfaces';
|
||||||
import { dev, locations } from '../config';
|
|
||||||
import { Page, PageComponent, ServerRoute, 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,
|
bundler: string,
|
||||||
manifest_data: ManifestData;
|
manifest_data: ManifestData;
|
||||||
dev_port?: number;
|
dev_port?: number;
|
||||||
|
dev: boolean;
|
||||||
|
cwd: string;
|
||||||
|
src: string;
|
||||||
|
dest: string;
|
||||||
|
routes: string;
|
||||||
|
output: string
|
||||||
}) {
|
}) {
|
||||||
const manifest_dir = path.resolve('__sapper__');
|
if (!fs.existsSync(output)) fs.mkdirSync(output);
|
||||||
if (!fs.existsSync(manifest_dir)) fs.mkdirSync(manifest_dir);
|
|
||||||
|
|
||||||
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 client_manifest = generate_client(manifest_data, path_to_routes, bundler, dev, dev_port);
|
||||||
const server_manifest = generate_server(manifest_data, path_to_routes);
|
const server_manifest = generate_server(manifest_data, path_to_routes, cwd, src, dest, dev);
|
||||||
|
|
||||||
write_if_changed(
|
write_if_changed(
|
||||||
`${manifest_dir}/_layout.html`,
|
`${output}/_layout.html`,
|
||||||
`<svelte:component this={child.component} {...child.props}/>`
|
`<svelte:component this={child.component} {...child.props}/>`
|
||||||
);
|
);
|
||||||
write_if_changed(`${manifest_dir}/client.js`, client_manifest);
|
write_if_changed(`${output}/client.js`, client_manifest);
|
||||||
write_if_changed(`${manifest_dir}/server.js`, server_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;
|
manifest_data: ManifestData;
|
||||||
|
output: string;
|
||||||
client_files: string[];
|
client_files: string[];
|
||||||
|
static_files: string;
|
||||||
}) {
|
}) {
|
||||||
let files;
|
let files: string[];
|
||||||
|
|
||||||
// TODO remove in a future version
|
if (fs.existsSync(static_files)) {
|
||||||
if (fs.existsSync(locations.static())) {
|
files = walk(static_files);
|
||||||
files = glob('**', { cwd: locations.static(), filesOnly: true });
|
|
||||||
} else {
|
} else {
|
||||||
|
// TODO remove in a future version
|
||||||
if (fs.existsSync('assets')) {
|
if (fs.existsSync('assets')) {
|
||||||
throw new Error(`As of Sapper 0.21, the assets/ directory should become static/`);
|
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];
|
export const routes = [\n\t${manifest_data.pages.map((r: Page) => `{ pattern: ${r.pattern} }`).join(',\n\t')}\n];
|
||||||
`.replace(/^\t\t/gm, '').trim();
|
`.replace(/^\t\t/gm, '').trim();
|
||||||
|
|
||||||
write_if_changed(`__sapper__/service-worker.js`, code);
|
write_if_changed(`${output}/service-worker.js`, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
function generate_client(
|
function generate_client(
|
||||||
manifest_data: ManifestData,
|
manifest_data: ManifestData,
|
||||||
path_to_routes: string,
|
path_to_routes: string,
|
||||||
bundler: string,
|
bundler: string,
|
||||||
|
dev: boolean,
|
||||||
dev_port?: number
|
dev_port?: number
|
||||||
) {
|
) {
|
||||||
const template_file = path.resolve(__dirname, '../templates/client.js');
|
const template_file = path.resolve(__dirname, '../templates/client.js');
|
||||||
@@ -120,7 +136,7 @@ function generate_client(
|
|||||||
|
|
||||||
let footer = '';
|
let footer = '';
|
||||||
|
|
||||||
if (dev()) {
|
if (dev) {
|
||||||
const sapper_dev_client = posixify(
|
const sapper_dev_client = posixify(
|
||||||
path.resolve(__dirname, '../sapper-dev-client.js')
|
path.resolve(__dirname, '../sapper-dev-client.js')
|
||||||
);
|
);
|
||||||
@@ -145,7 +161,11 @@ function generate_client(
|
|||||||
|
|
||||||
function generate_server(
|
function generate_server(
|
||||||
manifest_data: ManifestData,
|
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_file = path.resolve(__dirname, '../templates/server.js');
|
||||||
const template = fs.readFileSync(template_file, 'utf-8');
|
const template = fs.readFileSync(template_file, 'utf-8');
|
||||||
@@ -206,13 +226,13 @@ function generate_server(
|
|||||||
error
|
error
|
||||||
};`.replace(/^\t\t/gm, '').trim();
|
};`.replace(/^\t\t/gm, '').trim();
|
||||||
|
|
||||||
const build_dir = path.relative(process.cwd(), locations.dest());
|
const build_dir = path.relative(cwd, dest);
|
||||||
const src_dir = path.relative(process.cwd(), locations.src());
|
const src_dir = path.relative(cwd, src);
|
||||||
|
|
||||||
return `// This file is generated by Sapper — do not edit it!\n` + template
|
return `// This file is generated by Sapper — do not edit it!\n` + template
|
||||||
.replace('__BUILD__DIR__', JSON.stringify(build_dir))
|
.replace('__BUILD__DIR__', JSON.stringify(build_dir))
|
||||||
.replace('__SRC__DIR__', JSON.stringify(src_dir))
|
.replace('__SRC__DIR__', JSON.stringify(src_dir))
|
||||||
.replace('__DEV__', dev() ? 'true' : 'false')
|
.replace('__DEV__', dev ? 'true' : 'false')
|
||||||
.replace(/const manifest = __MANIFEST__;/, code);
|
.replace(/const manifest = __MANIFEST__;/, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as fs from 'fs';
|
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 {
|
try {
|
||||||
return fs.readFileSync(`${dir}/template.html`, 'utf-8');
|
return fs.readFileSync(`${dir}/template.html`, 'utf-8');
|
||||||
} catch (err) {
|
} 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 = {
|
export type Route = {
|
||||||
id: string;
|
id: string;
|
||||||
handlers: {
|
handlers: {
|
||||||
@@ -53,3 +56,46 @@ export type ManifestData = {
|
|||||||
pages: Page[];
|
pages: Page[];
|
||||||
server_routes: ServerRoute[];
|
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 = {};
|
||||||
109
src/utils.ts
109
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) {
|
export function left_pad(str: string, len: number) {
|
||||||
while (str.length < len) str = ` ${str}`;
|
while (str.length < len) str = ` ${str}`;
|
||||||
return str;
|
return str;
|
||||||
@@ -8,3 +11,109 @@ export function repeat(str: string, i: number) {
|
|||||||
while (i--) result += str;
|
while (i--) result += str;
|
||||||
return result;
|
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 assert from 'assert';
|
||||||
import * as puppeteer from 'puppeteer';
|
import * as puppeteer from 'puppeteer';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
@@ -23,34 +22,11 @@ describe('basics', function() {
|
|||||||
let goto: (href: string) => Promise<void>;
|
let goto: (href: string) => Promise<void>;
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
before(() => {
|
before(async () => {
|
||||||
return new Promise((fulfil, reject) => {
|
await build({ cwd: __dirname });
|
||||||
// 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');
|
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||||
({ base, page, start, prefetchRoutes, prefetch, goto } = await runner.start());
|
({ base, page, start, prefetchRoutes, prefetch, goto } = await runner.start());
|
||||||
|
|
||||||
fulfil();
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => runner.end());
|
after(() => runner.end());
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as path from 'path';
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import * as puppeteer from 'puppeteer';
|
import * as puppeteer from 'puppeteer';
|
||||||
import { build } from '../../../api';
|
import { build } from '../../../api';
|
||||||
@@ -17,34 +16,11 @@ describe('credentials', function() {
|
|||||||
let prefetchRoutes: () => Promise<void>;
|
let prefetchRoutes: () => Promise<void>;
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
before(() => {
|
before(async () => {
|
||||||
return new Promise((fulfil, reject) => {
|
await build({ cwd: __dirname });
|
||||||
// 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');
|
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||||
|
|
||||||
fulfil();
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => runner.end());
|
after(() => runner.end());
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as path from 'path';
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import * as puppeteer from 'puppeteer';
|
import * as puppeteer from 'puppeteer';
|
||||||
import { build } from '../../../api';
|
import { build } from '../../../api';
|
||||||
@@ -17,34 +16,11 @@ describe('encoding', function() {
|
|||||||
let prefetchRoutes: () => Promise<void>;
|
let prefetchRoutes: () => Promise<void>;
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
before(() => {
|
before(async () => {
|
||||||
return new Promise((fulfil, reject) => {
|
await build({ cwd: __dirname });
|
||||||
// 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');
|
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||||
|
|
||||||
fulfil();
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => runner.end());
|
after(() => runner.end());
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as path from 'path';
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import * as puppeteer from 'puppeteer';
|
import * as puppeteer from 'puppeteer';
|
||||||
import { build } from '../../../api';
|
import { build } from '../../../api';
|
||||||
@@ -17,34 +16,11 @@ describe('errors', function() {
|
|||||||
let prefetchRoutes: () => Promise<void>;
|
let prefetchRoutes: () => Promise<void>;
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
before(() => {
|
before(async () => {
|
||||||
return new Promise((fulfil, reject) => {
|
await build({ cwd: __dirname });
|
||||||
// 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');
|
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||||
|
|
||||||
fulfil();
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => runner.end());
|
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 assert from 'assert';
|
||||||
import * as puppeteer from 'puppeteer';
|
import * as puppeteer from 'puppeteer';
|
||||||
import { build } from '../../../api';
|
import { build } from '../../../api';
|
||||||
@@ -12,34 +11,11 @@ describe('ignore', function() {
|
|||||||
let base: string;
|
let base: string;
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
before(() => {
|
before(async () => {
|
||||||
return new Promise((fulfil, reject) => {
|
await build({ cwd: __dirname });
|
||||||
// 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');
|
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||||
({ base, page } = await runner.start());
|
({ base, page } = await runner.start());
|
||||||
|
|
||||||
fulfil();
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => runner.end());
|
after(() => runner.end());
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as path from 'path';
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import * as puppeteer from 'puppeteer';
|
import * as puppeteer from 'puppeteer';
|
||||||
import { build } from '../../../api';
|
import { build } from '../../../api';
|
||||||
@@ -16,34 +15,11 @@ describe('layout', function() {
|
|||||||
let start: () => Promise<void>;
|
let start: () => Promise<void>;
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
before(() => {
|
before(async () => {
|
||||||
return new Promise((fulfil, reject) => {
|
await build({ cwd: __dirname });
|
||||||
// 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');
|
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||||
({ base, page, start } = await runner.start());
|
({ base, page, start } = await runner.start());
|
||||||
|
|
||||||
fulfil();
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => runner.end());
|
after(() => runner.end());
|
||||||
@@ -19,34 +19,11 @@ describe('preloading', function() {
|
|||||||
let prefetchRoutes: () => Promise<void>;
|
let prefetchRoutes: () => Promise<void>;
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
before(() => {
|
before(async () => {
|
||||||
return new Promise((fulfil, reject) => {
|
await build({ cwd: __dirname });
|
||||||
// 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');
|
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||||
|
|
||||||
fulfil();
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => runner.end());
|
after(() => runner.end());
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as path from 'path';
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import * as puppeteer from 'puppeteer';
|
import * as puppeteer from 'puppeteer';
|
||||||
import { build } from '../../../api';
|
import { build } from '../../../api';
|
||||||
@@ -17,34 +16,11 @@ describe('redirects', function() {
|
|||||||
let prefetchRoutes: () => Promise<void>;
|
let prefetchRoutes: () => Promise<void>;
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
before(() => {
|
before(async () => {
|
||||||
return new Promise((fulfil, reject) => {
|
await build({ cwd: __dirname });
|
||||||
// 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');
|
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||||
|
|
||||||
fulfil();
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => runner.end());
|
after(() => runner.end());
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import * as path from 'path';
|
|
||||||
import * as puppeteer from 'puppeteer';
|
import * as puppeteer from 'puppeteer';
|
||||||
import { build } from '../../../api';
|
import { build } from '../../../api';
|
||||||
import { AppRunner } from '../AppRunner';
|
import { AppRunner } from '../AppRunner';
|
||||||
@@ -17,34 +16,11 @@ describe('scroll', function() {
|
|||||||
let prefetchRoutes: () => Promise<void>;
|
let prefetchRoutes: () => Promise<void>;
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
before(() => {
|
before(async () => {
|
||||||
return new Promise((fulfil, reject) => {
|
await build({ cwd: __dirname });
|
||||||
// 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');
|
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||||
|
|
||||||
fulfil();
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => runner.end());
|
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 assert from 'assert';
|
||||||
import * as puppeteer from 'puppeteer';
|
import * as puppeteer from 'puppeteer';
|
||||||
import * as api from '../../../api';
|
import * as api from '../../../api';
|
||||||
@@ -13,50 +12,16 @@ describe('with-basepath', function() {
|
|||||||
let base: string;
|
let base: string;
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
before(() => {
|
before(async () => {
|
||||||
return new Promise((fulfil, reject) => {
|
await api.build({ cwd: __dirname });
|
||||||
// TODO this is brittle. Make it unnecessary
|
|
||||||
process.chdir(__dirname);
|
|
||||||
process.env.NODE_ENV = 'production';
|
|
||||||
|
|
||||||
// TODO this API isn't great. Rethink it
|
await api.export({
|
||||||
const builder = api.build({
|
cwd: __dirname,
|
||||||
bundler: 'rollup'
|
basepath: 'custom-basepath'
|
||||||
}, {
|
|
||||||
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');
|
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||||
({ base, page } = await runner.start());
|
({ base, page } = await runner.start());
|
||||||
|
|
||||||
fulfil();
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => runner.end());
|
after(() => runner.end());
|
||||||
@@ -81,7 +46,7 @@ describe('with-basepath', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('crawls an exported site with basepath', () => {
|
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 client_assets = files.filter(file => file.startsWith('custom-basepath/client/'));
|
||||||
const non_client_assets = files.filter(file => !file.startsWith('custom-basepath/client/')).sort();
|
const non_client_assets = files.filter(file => !file.startsWith('custom-basepath/client/')).sort();
|
||||||
Reference in New Issue
Block a user