mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-14 20:14:39 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
687071902d | ||
|
|
cd3fcfdf3c | ||
|
|
dad48e4abd | ||
|
|
37d3d57694 | ||
|
|
9a5d273590 | ||
|
|
3816fe71ad | ||
|
|
69f5b9cac7 | ||
|
|
ad14320dc3 | ||
|
|
43563bd8e5 | ||
|
|
02d558b97c | ||
|
|
866286c95e | ||
|
|
e1b5e336dc | ||
|
|
1d71b86c0f | ||
|
|
bdc248f09a | ||
|
|
be63ea7c96 | ||
|
|
819ec0b776 | ||
|
|
d22d37fb18 | ||
|
|
8ec433581a | ||
|
|
0d0e4d664e |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,5 +1,21 @@
|
|||||||
# sapper changelog
|
# sapper changelog
|
||||||
|
|
||||||
|
## 0.9.5
|
||||||
|
|
||||||
|
* Stringify clorox output ([#197](https://github.com/sveltejs/sapper/pull/197))
|
||||||
|
|
||||||
|
## 0.9.4
|
||||||
|
|
||||||
|
* Add `SAPPER_BASE` and `SAPPER_APP` environment variables ([#181](https://github.com/sveltejs/sapper/issues/181))
|
||||||
|
* Minify template in `sapper build` ([#15](https://github.com/sveltejs/sapper/issues/15))
|
||||||
|
* Minify all HTML files in `sapper export` ([#172](https://github.com/sveltejs/sapper/issues/172))
|
||||||
|
* Log exported files ([#195](https://github.com/sveltejs/sapper/pull/195))
|
||||||
|
* Add `--open`/`-o` flag to `sapper dev` and `sapper start` ([#186](https://github.com/sveltejs/sapper/issues/186))
|
||||||
|
|
||||||
|
## 0.9.3
|
||||||
|
|
||||||
|
* Fix path to `sapper-dev-client`
|
||||||
|
|
||||||
## 0.9.2
|
## 0.9.2
|
||||||
|
|
||||||
* Include `dist` files in package
|
* Include `dist` files in package
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
// TODO write to this file, instead of middleware.ts.js
|
|
||||||
module.exports = require('./dist/middleware.ts.js');
|
|
||||||
12
package.json
12
package.json
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "sapper",
|
"name": "sapper",
|
||||||
"version": "0.9.2",
|
"version": "0.9.5",
|
||||||
"description": "Military-grade apps, engineered by Svelte",
|
"description": "Military-grade apps, engineered by Svelte",
|
||||||
"main": "middleware.js",
|
"main": "dist/middleware.ts.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"sapper": "./sapper"
|
"sapper": "./sapper"
|
||||||
},
|
},
|
||||||
@@ -23,17 +23,17 @@
|
|||||||
"clorox": "^1.0.3",
|
"clorox": "^1.0.3",
|
||||||
"devalue": "^1.0.1",
|
"devalue": "^1.0.1",
|
||||||
"glob": "^7.1.2",
|
"glob": "^7.1.2",
|
||||||
|
"html-minifier": "^3.5.10",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"node-fetch": "^1.7.3",
|
"node-fetch": "^1.7.3",
|
||||||
"polka": "^0.3.4",
|
"port-authority": "^1.0.2",
|
||||||
"port-authority": "^1.0.0",
|
"pretty-bytes": "^4.0.2",
|
||||||
"pretty-ms": "^3.1.0",
|
"pretty-ms": "^3.1.0",
|
||||||
"require-relative": "^0.8.7",
|
"require-relative": "^0.8.7",
|
||||||
"rimraf": "^2.6.2",
|
"rimraf": "^2.6.2",
|
||||||
"sade": "^1.4.0",
|
"sade": "^1.4.0",
|
||||||
"sander": "^0.6.0",
|
"sander": "^0.6.0",
|
||||||
"source-map-support": "^0.5.3",
|
"source-map-support": "^0.5.3",
|
||||||
"tslib": "^1.9.0",
|
|
||||||
"url-parse": "^1.2.0",
|
"url-parse": "^1.2.0",
|
||||||
"walk-sync": "^0.3.2",
|
"walk-sync": "^0.3.2",
|
||||||
"webpack-format-messages": "^1.0.1"
|
"webpack-format-messages": "^1.0.1"
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
"mocha": "^4.0.1",
|
"mocha": "^4.0.1",
|
||||||
"nightmare": "^2.10.0",
|
"nightmare": "^2.10.0",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
|
"polka": "^0.3.4",
|
||||||
"rollup": "^0.56.5",
|
"rollup": "^0.56.5",
|
||||||
"rollup-plugin-commonjs": "^8.3.0",
|
"rollup-plugin-commonjs": "^8.3.0",
|
||||||
"rollup-plugin-json": "^2.3.0",
|
"rollup-plugin-json": "^2.3.0",
|
||||||
@@ -58,6 +59,7 @@
|
|||||||
"serve-static": "^1.13.2",
|
"serve-static": "^1.13.2",
|
||||||
"svelte": "^1.49.1",
|
"svelte": "^1.49.1",
|
||||||
"svelte-loader": "^2.3.2",
|
"svelte-loader": "^2.3.2",
|
||||||
|
"tslib": "^1.9.0",
|
||||||
"ts-node": "^4.1.0",
|
"ts-node": "^4.1.0",
|
||||||
"typescript": "^2.6.2",
|
"typescript": "^2.6.2",
|
||||||
"webpack": "^4.1.0"
|
"webpack": "^4.1.0"
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ const prog = sade('sapper').version(pkg.version);
|
|||||||
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')
|
||||||
.action(async (opts: { port: number }) => {
|
.option('-o, --open', 'Open a browser window')
|
||||||
|
.action(async (opts: { port: number, open: boolean }) => {
|
||||||
const { dev } = await import('./cli/dev');
|
const { dev } = await import('./cli/dev');
|
||||||
dev(opts);
|
dev(opts);
|
||||||
});
|
});
|
||||||
@@ -40,7 +41,8 @@ prog.command('build [dest]')
|
|||||||
prog.command('start [dir]')
|
prog.command('start [dir]')
|
||||||
.describe('Start your app')
|
.describe('Start your app')
|
||||||
.option('-p, --port', 'Specify a port')
|
.option('-p, --port', 'Specify a port')
|
||||||
.action(async (dir = 'build', opts: { port: number }) => {
|
.option('-o, --open', 'Open a browser window')
|
||||||
|
.action(async (dir = 'build', opts: { port: number, open: boolean }) => {
|
||||||
const { start } = await import('./cli/start');
|
const { start } = await import('./cli/start');
|
||||||
start(dir, opts);
|
start(dir, opts);
|
||||||
});
|
});
|
||||||
@@ -58,7 +60,7 @@ prog.command('export [dest]')
|
|||||||
try {
|
try {
|
||||||
const { build } = await import('./cli/build');
|
const { build } = await import('./cli/build');
|
||||||
await build();
|
await build();
|
||||||
console.error(`\n> Built in ${elapsed(start)}. Exporting...`);
|
console.error(`\n> Built in ${elapsed(start)}. Crawling site...`);
|
||||||
|
|
||||||
const { exporter } = await import('./cli/export');
|
const { exporter } = await import('./cli/export');
|
||||||
await exporter(dest);
|
await exporter(dest);
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import * as path from 'path';
|
|||||||
import * as clorox from 'clorox';
|
import * as clorox from 'clorox';
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from 'mkdirp';
|
||||||
import rimraf from 'rimraf';
|
import rimraf from 'rimraf';
|
||||||
import { create_compilers, create_app, create_routes, create_serviceworker } from '../core'
|
import { minify_html } from './utils/minify_html';
|
||||||
import { src, dest, dev } from '../config';
|
import { create_compilers, create_main_manifests, create_routes, create_serviceworker_manifest } from '../core'
|
||||||
|
import { locations } from '../config';
|
||||||
|
|
||||||
export async function build() {
|
export async function build() {
|
||||||
const output = dest();
|
const output = locations.dest();
|
||||||
|
|
||||||
mkdirp.sync(output);
|
mkdirp.sync(output);
|
||||||
rimraf.sync(path.join(output, '**/*'));
|
rimraf.sync(path.join(output, '**/*'));
|
||||||
@@ -15,32 +16,36 @@ export async function build() {
|
|||||||
const routes = create_routes();
|
const routes = create_routes();
|
||||||
|
|
||||||
// create app/manifest/client.js and app/manifest/server.js
|
// create app/manifest/client.js and app/manifest/server.js
|
||||||
create_app({ routes, src, dev });
|
create_main_manifests({ routes });
|
||||||
|
|
||||||
const { client, server, serviceworker } = create_compilers();
|
const { client, server, serviceworker } = create_compilers();
|
||||||
|
|
||||||
const client_stats = await compile(client);
|
const client_stats = await compile(client);
|
||||||
console.log(clorox.inverse(`\nbuilt client`).toString());
|
console.log(`${clorox.inverse(`\nbuilt client`)}`);
|
||||||
console.log(client_stats.toString({ colors: true }));
|
console.log(client_stats.toString({ colors: true }));
|
||||||
fs.writeFileSync(path.join(output, 'client_info.json'), JSON.stringify(client_stats.toJson()));
|
fs.writeFileSync(path.join(output, 'client_info.json'), JSON.stringify(client_stats.toJson()));
|
||||||
|
|
||||||
const server_stats = await compile(server);
|
const server_stats = await compile(server);
|
||||||
console.log(clorox.inverse(`\nbuilt server`).toString());
|
console.log(`${clorox.inverse(`\nbuilt server`)}`);
|
||||||
console.log(server_stats.toString({ colors: true }));
|
console.log(server_stats.toString({ colors: true }));
|
||||||
|
|
||||||
let serviceworker_stats;
|
let serviceworker_stats;
|
||||||
|
|
||||||
if (serviceworker) {
|
if (serviceworker) {
|
||||||
create_serviceworker({
|
create_serviceworker_manifest({
|
||||||
routes,
|
routes,
|
||||||
client_files: client_stats.toJson().assets.map((chunk: { name: string }) => `/client/${chunk.name}`),
|
client_files: client_stats.toJson().assets.map((chunk: { name: string }) => `/client/${chunk.name}`)
|
||||||
src
|
|
||||||
});
|
});
|
||||||
|
|
||||||
serviceworker_stats = await compile(serviceworker);
|
serviceworker_stats = await compile(serviceworker);
|
||||||
console.log(clorox.inverse(`\nbuilt service worker`).toString());
|
console.log(`${clorox.inverse(`\nbuilt service worker`)}`);
|
||||||
console.log(serviceworker_stats.toString({ colors: true }));
|
console.log(serviceworker_stats.toString({ colors: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// minify app/template.html
|
||||||
|
// TODO compile this to a function? could be quicker than str.replace(...).replace(...).replace(...)
|
||||||
|
const template = fs.readFileSync(`${locations.app()}/template.html`, 'utf-8');
|
||||||
|
fs.writeFileSync(`${output}/template.html`, minify_html(template));
|
||||||
}
|
}
|
||||||
|
|
||||||
function compile(compiler: any) {
|
function compile(compiler: any) {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import rimraf from 'rimraf';
|
|||||||
import format_messages from 'webpack-format-messages';
|
import format_messages from 'webpack-format-messages';
|
||||||
import prettyMs from 'pretty-ms';
|
import prettyMs from 'pretty-ms';
|
||||||
import * as ports from 'port-authority';
|
import * as ports from 'port-authority';
|
||||||
import { dest } from '../config';
|
import { locations } from '../config';
|
||||||
import { create_compilers, create_app, create_routes, create_serviceworker } from '../core';
|
import { create_compilers, create_main_manifests, create_routes, create_serviceworker_manifest } from '../core';
|
||||||
|
|
||||||
type Deferred = {
|
type Deferred = {
|
||||||
promise?: Promise<any>;
|
promise?: Promise<any>;
|
||||||
@@ -70,37 +70,37 @@ function create_hot_update_server(port: number, interval = 10000) {
|
|||||||
return { send };
|
return { send };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function dev(opts: { port: number }) {
|
export async function dev(opts: { port: number, open: boolean }) {
|
||||||
process.env.NODE_ENV = 'development';
|
process.env.NODE_ENV = 'development';
|
||||||
|
|
||||||
let port = opts.port || +process.env.PORT;
|
let port = opts.port || +process.env.PORT;
|
||||||
|
|
||||||
if (port) {
|
if (port) {
|
||||||
if (!await ports.check(port)) {
|
if (!await ports.check(port)) {
|
||||||
console.log(clorox.bold.red(`> Port ${port} is unavailable`));
|
console.log(`${clorox.bold.red(`> Port ${port} is unavailable`)}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
port = await ports.find(3000);
|
port = await ports.find(3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dir = dest();
|
const dir = locations.dest();
|
||||||
rimraf.sync(dir);
|
rimraf.sync(dir);
|
||||||
mkdirp.sync(dir);
|
mkdirp.sync(dir);
|
||||||
|
|
||||||
const dev_port = await ports.find(10000);
|
const dev_port = await ports.find(10000);
|
||||||
|
|
||||||
const routes = create_routes();
|
const routes = create_routes();
|
||||||
create_app({ routes, dev_port });
|
create_main_manifests({ routes, dev_port });
|
||||||
|
|
||||||
const hot_update_server = create_hot_update_server(dev_port);
|
const hot_update_server = create_hot_update_server(dev_port);
|
||||||
|
|
||||||
watch_files('routes/**/*', ['add', 'unlink'], () => {
|
watch_files(`${locations.routes()}/**/*`, ['add', 'unlink'], () => {
|
||||||
const routes = create_routes();
|
const routes = create_routes();
|
||||||
create_app({ routes, dev_port });
|
create_main_manifests({ routes, dev_port });
|
||||||
});
|
});
|
||||||
|
|
||||||
watch_files('app/template.html', ['change'], () => {
|
watch_files(`${locations.app()}/template.html`, ['change'], () => {
|
||||||
hot_update_server.send({
|
hot_update_server.send({
|
||||||
action: 'reload'
|
action: 'reload'
|
||||||
});
|
});
|
||||||
@@ -155,15 +155,15 @@ export async function dev(opts: { port: number }) {
|
|||||||
|
|
||||||
compiler.watch({}, (err: Error, stats: any) => {
|
compiler.watch({}, (err: Error, stats: any) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(clorox.red(`✗ ${name}`));
|
console.log(`${clorox.red(`✗ ${name}`)}`);
|
||||||
console.error(clorox.red(err.message));
|
console.log(`${clorox.red(err.message)}`);
|
||||||
error(err);
|
error(err);
|
||||||
} else {
|
} else {
|
||||||
const messages = format_messages(stats);
|
const messages = format_messages(stats);
|
||||||
const info = stats.toJson();
|
const info = stats.toJson();
|
||||||
|
|
||||||
if (messages.errors.length > 0) {
|
if (messages.errors.length > 0) {
|
||||||
console.log(clorox.bold.red(`✗ ${name}`));
|
console.log(`${clorox.bold.red(`✗ ${name}`)}`);
|
||||||
|
|
||||||
const filtered = messages.errors.filter((message: string) => {
|
const filtered = messages.errors.filter((message: string) => {
|
||||||
return !build.unique_errors.has(message);
|
return !build.unique_errors.has(message);
|
||||||
@@ -180,7 +180,7 @@ export async function dev(opts: { port: number }) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (messages.warnings.length > 0) {
|
if (messages.warnings.length > 0) {
|
||||||
console.log(clorox.bold.yellow(`• ${name}`));
|
console.log(`${clorox.bold.yellow(`• ${name}`)}`);
|
||||||
|
|
||||||
const filtered = messages.warnings.filter((message: string) => {
|
const filtered = messages.warnings.filter((message: string) => {
|
||||||
return !build.unique_warnings.has(message);
|
return !build.unique_warnings.has(message);
|
||||||
@@ -241,6 +241,8 @@ export async function dev(opts: { port: number }) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let first = true;
|
||||||
|
|
||||||
watch(compilers.client, {
|
watch(compilers.client, {
|
||||||
name: 'client',
|
name: 'client',
|
||||||
|
|
||||||
@@ -263,9 +265,15 @@ export async function dev(opts: { port: number }) {
|
|||||||
hot_update_server.send({
|
hot_update_server.send({
|
||||||
status: 'completed'
|
status: 'completed'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
console.log(`${clorox.bold.cyan(`> Listening on localhost:${port}`)}`);
|
||||||
|
if (opts.open) child_process.exec(`open http://localhost:${port}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
create_serviceworker({
|
create_serviceworker_manifest({
|
||||||
routes: create_routes(),
|
routes: create_routes(),
|
||||||
client_files
|
client_files
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import * as child_process from 'child_process';
|
import * as child_process from 'child_process';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as sander from 'sander';
|
import * as sander from 'sander';
|
||||||
|
import * as clorox from 'clorox';
|
||||||
import cheerio from 'cheerio';
|
import cheerio from 'cheerio';
|
||||||
import URL from 'url-parse';
|
import URL from 'url-parse';
|
||||||
import fetch from 'node-fetch';
|
import fetch from 'node-fetch';
|
||||||
import * as ports from 'port-authority';
|
import * as ports from 'port-authority';
|
||||||
import { dest } from '../config';
|
import prettyBytes from 'pretty-bytes';
|
||||||
|
import { minify_html } from './utils/minify_html';
|
||||||
|
import { locations } from '../config';
|
||||||
|
|
||||||
export async function exporter(export_dir: string) {
|
export async function exporter(export_dir: string) {
|
||||||
const build_dir = dest();
|
const build_dir = locations.dest();
|
||||||
|
|
||||||
// Prep output directory
|
// Prep output directory
|
||||||
sander.rimrafSync(export_dir);
|
sander.rimrafSync(export_dir);
|
||||||
@@ -40,18 +43,22 @@ export async function exporter(export_dir: string) {
|
|||||||
proc.on('message', message => {
|
proc.on('message', message => {
|
||||||
if (!message.__sapper__) return;
|
if (!message.__sapper__) return;
|
||||||
|
|
||||||
const url = new URL(message.url, origin);
|
let file = new URL(message.url, origin).pathname.slice(1);
|
||||||
|
let { body } = message;
|
||||||
|
|
||||||
if (saved.has(url.pathname)) return;
|
if (saved.has(file)) return;
|
||||||
saved.add(url.pathname);
|
saved.add(file);
|
||||||
|
|
||||||
if (message.type === 'text/html') {
|
const is_html = message.type === 'text/html';
|
||||||
const file = `${export_dir}/${url.pathname}/index.html`;
|
|
||||||
sander.writeFileSync(file, message.body);
|
if (is_html) {
|
||||||
} else {
|
file = file === '' ? 'index.html' : `${file}/index.html`;
|
||||||
const file = `${export_dir}/${url.pathname}`;
|
body = minify_html(body);
|
||||||
sander.writeFileSync(file, message.body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`${clorox.bold.cyan(file)} ${clorox.gray(`(${prettyBytes(body.length)})`)}`);
|
||||||
|
|
||||||
|
sander.writeFileSync(`${export_dir}/${file}`, body);
|
||||||
});
|
});
|
||||||
|
|
||||||
function handle(url: URL) {
|
function handle(url: URL) {
|
||||||
@@ -78,7 +85,7 @@ export async function exporter(export_dir: string) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err: Error) => {
|
.catch((err: Error) => {
|
||||||
console.error(`Error rendering ${url.pathname}: ${err.message}`);
|
console.log(`${clorox.red(`> Error rendering ${url.pathname}: ${err.message}`)}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import * as child_process from 'child_process';
|
|||||||
import * as clorox from 'clorox';
|
import * as clorox from 'clorox';
|
||||||
import * as ports from 'port-authority';
|
import * as ports from 'port-authority';
|
||||||
|
|
||||||
export async function start(dir: string, opts: { port: number }) {
|
export async function start(dir: string, opts: { port: number, open: boolean }) {
|
||||||
let port = opts.port || +process.env.PORT;
|
let port = opts.port || +process.env.PORT;
|
||||||
|
|
||||||
const resolved = path.resolve(dir);
|
const resolved = path.resolve(dir);
|
||||||
@@ -32,4 +32,8 @@ export async function start(dir: string, opts: { port: number }) {
|
|||||||
SAPPER_DEST: dir
|
SAPPER_DEST: dir
|
||||||
}, process.env)
|
}, process.env)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await ports.wait(port);
|
||||||
|
console.log(`${clorox.bold.cyan(`> Listening on localhost:${port}`)}`);
|
||||||
|
if (opts.open) child_process.exec(`open http://localhost:${port}`);
|
||||||
}
|
}
|
||||||
@@ -27,10 +27,10 @@ async function upgrade_sapper_main() {
|
|||||||
|
|
||||||
if (/\%sapper\.main\%/.test(template)) {
|
if (/\%sapper\.main\%/.test(template)) {
|
||||||
if (!pattern.test(template)) {
|
if (!pattern.test(template)) {
|
||||||
console.log(clorox.red(`Could not replace %sapper.main% in ${file}`));
|
console.log(`${clorox.red(`Could not replace %sapper.main% in ${file}`)}`);
|
||||||
} else {
|
} else {
|
||||||
write(file, template.replace(pattern, `%sapper.scripts%`));
|
write(file, template.replace(pattern, `%sapper.scripts%`));
|
||||||
console.log(clorox.green(`Replaced %sapper.main% in ${file}`));
|
console.log(`${clorox.green(`Replaced %sapper.main% in ${file}`)}`);
|
||||||
replaced = true;
|
replaced = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/cli/utils/minify_html.ts
Normal file
21
src/cli/utils/minify_html.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { minify } from 'html-minifier';
|
||||||
|
|
||||||
|
export function minify_html(html: string) {
|
||||||
|
return minify(html, {
|
||||||
|
collapseBooleanAttributes: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
conservativeCollapse: true,
|
||||||
|
decodeEntities: true,
|
||||||
|
html5: true,
|
||||||
|
minifyCSS: true,
|
||||||
|
minifyJS: true,
|
||||||
|
removeAttributeQuotes: true,
|
||||||
|
removeComments: true,
|
||||||
|
removeOptionalTags: true,
|
||||||
|
removeRedundantAttributes: true,
|
||||||
|
removeScriptTypeAttributes: true,
|
||||||
|
removeStyleLinkTypeAttributes: true,
|
||||||
|
sortAttributes: true,
|
||||||
|
sortClassName: true
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
export const dev = () => process.env.NODE_ENV !== 'production';
|
export const dev = () => process.env.NODE_ENV !== 'production';
|
||||||
export const src = () => path.resolve(process.env.SAPPER_ROUTES || 'routes');
|
|
||||||
export const dest = () => path.resolve(process.env.SAPPER_DEST || '.sapper');
|
export const locations = {
|
||||||
|
base: () => path.resolve(process.env.SAPPER_BASE || ''),
|
||||||
|
app: () => path.resolve(process.env.SAPPER_BASE || '', process.env.SAPPER_APP || 'app'),
|
||||||
|
routes: () => path.resolve(process.env.SAPPER_BASE || '', process.env.SAPPER_ROUTES || 'routes'),
|
||||||
|
dest: () => path.resolve(process.env.SAPPER_BASE || '', process.env.SAPPER_DEST || '.sapper')
|
||||||
|
};
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
export { default as create_app } from './core/create_app';
|
export * from './core/create_manifests';
|
||||||
export { default as create_serviceworker } from './core/create_serviceworker';
|
|
||||||
export { default as create_compilers } from './core/create_compilers';
|
export { default as create_compilers } from './core/create_compilers';
|
||||||
export { default as create_routes } from './core/create_routes';
|
export { default as create_routes } from './core/create_routes';
|
||||||
@@ -1,36 +1,45 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import mkdirp from 'mkdirp';
|
import * as glob from 'glob';
|
||||||
import create_routes from './create_routes';
|
import create_routes from './create_routes';
|
||||||
import { fudge_mtime, posixify, write } from './utils';
|
import { posixify, write_if_changed } from './utils';
|
||||||
import { dev } from '../config';
|
import { dev, locations } from '../config';
|
||||||
import { Route } from '../interfaces';
|
import { Route } from '../interfaces';
|
||||||
|
|
||||||
// in dev mode, we avoid touching the fs unnecessarily
|
export function create_main_manifests({ routes, dev_port }: {
|
||||||
let last_client_manifest: string = null;
|
|
||||||
let last_server_manifest: string = null;
|
|
||||||
|
|
||||||
export default function create_app({ routes, dev_port }: {
|
|
||||||
routes: Route[];
|
routes: Route[];
|
||||||
dev_port: number;
|
dev_port?: number;
|
||||||
}) {
|
}) {
|
||||||
mkdirp.sync('app/manifest');
|
const path_to_routes = path.relative(`${locations.app()}/manifest`, locations.routes());
|
||||||
|
|
||||||
const client_manifest = generate_client(routes, dev_port);
|
const client_manifest = generate_client(routes, path_to_routes, dev_port);
|
||||||
const server_manifest = generate_server(routes);
|
const server_manifest = generate_server(routes, path_to_routes);
|
||||||
|
|
||||||
if (client_manifest !== last_client_manifest) {
|
write_if_changed(`${locations.app()}/manifest/client.js`, client_manifest);
|
||||||
write(`app/manifest/client.js`, client_manifest);
|
write_if_changed(`${locations.app()}/manifest/server.js`, server_manifest);
|
||||||
last_client_manifest = client_manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server_manifest !== last_server_manifest) {
|
|
||||||
write(`app/manifest/server.js`, server_manifest);
|
|
||||||
last_server_manifest = server_manifest;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generate_client(routes: Route[], dev_port?: number) {
|
export function create_serviceworker_manifest({ routes, client_files }: {
|
||||||
|
routes: Route[];
|
||||||
|
client_files: string[];
|
||||||
|
}) {
|
||||||
|
const assets = glob.sync('**', { cwd: 'assets', nodir: true });
|
||||||
|
|
||||||
|
let code = `
|
||||||
|
// This file is generated by Sapper — do not edit it!
|
||||||
|
export const timestamp = ${Date.now()};
|
||||||
|
|
||||||
|
export const assets = [\n\t${assets.map((x: string) => `"${x}"`).join(',\n\t')}\n];
|
||||||
|
|
||||||
|
export const shell = [\n\t${client_files.map((x: string) => `"${x}"`).join(',\n\t')}\n];
|
||||||
|
|
||||||
|
export const routes = [\n\t${routes.filter((r: Route) => r.type === 'page' && !/^_[45]xx$/.test(r.id)).map((r: Route) => `{ pattern: ${r.pattern} }`).join(',\n\t')}\n];
|
||||||
|
`.replace(/^\t\t/gm, '').trim();
|
||||||
|
|
||||||
|
write_if_changed(`${locations.app()}/manifest/service-worker.js`, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate_client(routes: Route[], path_to_routes: string, dev_port?: number) {
|
||||||
let code = `
|
let code = `
|
||||||
// This file is generated by Sapper — do not edit it!
|
// This file is generated by Sapper — do not edit it!
|
||||||
export const routes = [
|
export const routes = [
|
||||||
@@ -40,7 +49,7 @@ function generate_client(routes: Route[], dev_port?: number) {
|
|||||||
return `{ pattern: ${route.pattern}, ignore: true }`;
|
return `{ pattern: ${route.pattern}, ignore: true }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = posixify(`../../routes/${route.file}`);
|
const file = posixify(`${path_to_routes}/${route.file}`);
|
||||||
|
|
||||||
if (route.id === '_4xx' || route.id === '_5xx') {
|
if (route.id === '_4xx' || route.id === '_5xx') {
|
||||||
return `{ error: '${route.id.slice(1)}', load: () => import(/* webpackChunkName: "${route.id}" */ '${file}') }`;
|
return `{ error: '${route.id.slice(1)}', load: () => import(/* webpackChunkName: "${route.id}" */ '${file}') }`;
|
||||||
@@ -57,7 +66,7 @@ function generate_client(routes: Route[], dev_port?: number) {
|
|||||||
|
|
||||||
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')
|
||||||
);
|
);
|
||||||
|
|
||||||
code += `
|
code += `
|
||||||
@@ -72,12 +81,12 @@ function generate_client(routes: Route[], dev_port?: number) {
|
|||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generate_server(routes: Route[]) {
|
function generate_server(routes: Route[], path_to_routes: string) {
|
||||||
let code = `
|
let code = `
|
||||||
// This file is generated by Sapper — do not edit it!
|
// This file is generated by Sapper — do not edit it!
|
||||||
${routes
|
${routes
|
||||||
.map(route => {
|
.map(route => {
|
||||||
const file = posixify(`../../routes/${route.file}`);
|
const file = posixify(`${path_to_routes}/${route.file}`);
|
||||||
return route.type === 'page'
|
return route.type === 'page'
|
||||||
? `import ${route.id} from '${file}';`
|
? `import ${route.id} from '${file}';`
|
||||||
: `import * as ${route.id} from '${file}';`;
|
: `import * as ${route.id} from '${file}';`;
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import glob from 'glob';
|
import glob from 'glob';
|
||||||
import { src } from '../config';
|
import { locations } from '../config';
|
||||||
import { Route } from '../interfaces';
|
import { Route } from '../interfaces';
|
||||||
|
|
||||||
export default function create_routes({ files } = { files: glob.sync('**/*.*', { cwd: src(), nodir: true }) }) {
|
export default function create_routes({ files } = { files: glob.sync('**/*.*', { cwd: locations.routes(), nodir: true }) }) {
|
||||||
const routes: Route[] = files
|
const routes: Route[] = files
|
||||||
.map((file: string) => {
|
.map((file: string) => {
|
||||||
if (/(^|\/|\\)_/.test(file)) return;
|
if (/(^|\/|\\)_/.test(file)) return;
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
import glob from 'glob';
|
|
||||||
import create_routes from './create_routes';
|
|
||||||
import { fudge_mtime, posixify, write } from './utils';
|
|
||||||
import { Route } from '../interfaces';
|
|
||||||
|
|
||||||
export default function create_serviceworker({ routes, client_files }: {
|
|
||||||
routes: Route[];
|
|
||||||
client_files: string[];
|
|
||||||
}) {
|
|
||||||
const assets = glob.sync('**', { cwd: 'assets', nodir: true });
|
|
||||||
|
|
||||||
let code = `
|
|
||||||
// This file is generated by Sapper — do not edit it!
|
|
||||||
export const timestamp = ${Date.now()};
|
|
||||||
|
|
||||||
export const assets = [\n\t${assets.map((x: string) => `"${x}"`).join(',\n\t')}\n];
|
|
||||||
|
|
||||||
export const shell = [\n\t${client_files.map((x: string) => `"${x}"`).join(',\n\t')}\n];
|
|
||||||
|
|
||||||
export const routes = [\n\t${routes.filter((r: Route) => r.type === 'page' && !/^_[45]xx$/.test(r.id)).map((r: Route) => `{ pattern: ${r.pattern} }`).join(',\n\t')}\n];
|
|
||||||
`.replace(/^\t\t/gm, '').trim();
|
|
||||||
|
|
||||||
write('app/manifest/service-worker.js', code);
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
import * as fs from 'fs';
|
import * as sander from 'sander';
|
||||||
|
|
||||||
export function write(file: string, code: string) {
|
const previous_contents = new Map();
|
||||||
fs.writeFileSync(file, code);
|
|
||||||
fudge_mtime(file);
|
export function write_if_changed(file: string, code: string) {
|
||||||
|
if (code !== previous_contents.get(file)) {
|
||||||
|
previous_contents.set(file, code);
|
||||||
|
sander.writeFileSync(file, code);
|
||||||
|
fudge_mtime(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function posixify(file: string) {
|
export function posixify(file: string) {
|
||||||
@@ -11,8 +16,8 @@ export function posixify(file: string) {
|
|||||||
|
|
||||||
export function fudge_mtime(file: string) {
|
export function fudge_mtime(file: string) {
|
||||||
// need to fudge the mtime so that webpack doesn't go doolally
|
// need to fudge the mtime so that webpack doesn't go doolally
|
||||||
const { atime, mtime } = fs.statSync(file);
|
const { atime, mtime } = sander.statSync(file);
|
||||||
fs.utimesSync(
|
sander.utimesSync(
|
||||||
file,
|
file,
|
||||||
new Date(atime.getTime() - 999999),
|
new Date(atime.getTime() - 999999),
|
||||||
new Date(mtime.getTime() - 999999)
|
new Date(mtime.getTime() - 999999)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import mkdirp from 'mkdirp';
|
|||||||
import rimraf from 'rimraf';
|
import rimraf from 'rimraf';
|
||||||
import devalue from 'devalue';
|
import devalue from 'devalue';
|
||||||
import { lookup } from './middleware/mime';
|
import { lookup } from './middleware/mime';
|
||||||
import { create_routes, templates, create_compilers } from './core/index';
|
import { create_routes, create_compilers } from './core';
|
||||||
import { dest, dev } from './config';
|
import { locations, dev } from './config';
|
||||||
import { Route, Template } from './interfaces';
|
import { Route, Template } from './interfaces';
|
||||||
import sourceMapSupport from 'source-map-support';
|
import sourceMapSupport from 'source-map-support';
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ interface Req extends ClientRequest {
|
|||||||
export default function middleware({ routes }: {
|
export default function middleware({ routes }: {
|
||||||
routes: RouteObject[]
|
routes: RouteObject[]
|
||||||
}) {
|
}) {
|
||||||
const output = dest();
|
const output = locations.dest();
|
||||||
|
|
||||||
const client_info = JSON.parse(fs.readFileSync(path.join(output, 'client_info.json'), 'utf-8'));
|
const client_info = JSON.parse(fs.readFileSync(path.join(output, 'client_info.json'), 'utf-8'));
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ function serve({ prefix, pathname, cache_control }: {
|
|||||||
? (req: Req) => req.pathname === pathname
|
? (req: Req) => req.pathname === pathname
|
||||||
: (req: Req) => req.pathname.startsWith(prefix);
|
: (req: Req) => req.pathname.startsWith(prefix);
|
||||||
|
|
||||||
const output = dest();
|
const output = locations.dest();
|
||||||
|
|
||||||
const cache: Map<string, Buffer> = new Map();
|
const cache: Map<string, Buffer> = new Map();
|
||||||
|
|
||||||
@@ -112,8 +112,8 @@ const resolved = Promise.resolve();
|
|||||||
|
|
||||||
function get_route_handler(chunks: Record<string, string>, routes: RouteObject[]) {
|
function get_route_handler(chunks: Record<string, string>, routes: RouteObject[]) {
|
||||||
const template = dev()
|
const template = dev()
|
||||||
? () => fs.readFileSync('app/template.html', 'utf-8')
|
? () => fs.readFileSync(`${locations.app()}/template.html`, 'utf-8')
|
||||||
: (str => () => str)(fs.readFileSync('app/template.html', 'utf-8'));
|
: (str => () => str)(fs.readFileSync(`${locations.dest()}/template.html`, 'utf-8'));
|
||||||
|
|
||||||
function handle_route(route: RouteObject, req: Req, res: ServerResponse) {
|
function handle_route(route: RouteObject, req: Req, res: ServerResponse) {
|
||||||
req.params = route.params(route.pattern.exec(req.pathname));
|
req.params = route.params(route.pattern.exec(req.pathname));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { dest, dev } from './config';
|
import { locations, dev } from './config';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
dev: dev(),
|
dev: dev(),
|
||||||
@@ -6,13 +6,13 @@ export default {
|
|||||||
client: {
|
client: {
|
||||||
entry: () => {
|
entry: () => {
|
||||||
return {
|
return {
|
||||||
main: './app/client'
|
main: `${locations.app()}/client`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
output: () => {
|
output: () => {
|
||||||
return {
|
return {
|
||||||
path: `${dest()}/client`,
|
path: `${locations.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: './app/server'
|
server: `${locations.app()}/server`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
output: () => {
|
output: () => {
|
||||||
return {
|
return {
|
||||||
path: dest(),
|
path: locations.dest(),
|
||||||
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': './app/service-worker'
|
'service-worker': `${locations.app()}/service-worker`
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
output: () => {
|
output: () => {
|
||||||
return {
|
return {
|
||||||
path: dest(),
|
path: locations.dest(),
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
chunkFilename: '[name].[id].[hash].js'
|
chunkFilename: '[name].[id].[hash].js'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import fs from 'fs';
|
|||||||
import polka from 'polka';
|
import polka from 'polka';
|
||||||
import compression from 'compression';
|
import compression from 'compression';
|
||||||
import serve from 'serve-static';
|
import serve from 'serve-static';
|
||||||
import sapper from '../../../middleware';
|
import sapper from '../../../dist/middleware.ts.js';
|
||||||
import { routes } from './manifest/server.js';
|
import { routes } from './manifest/server.js';
|
||||||
|
|
||||||
let pending;
|
let pending;
|
||||||
|
|||||||
Reference in New Issue
Block a user