|
|
|
@ -1,11 +1,14 @@
|
|
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
|
|
const colors = require('ansi-colors');
|
|
|
|
|
const chalk = require('chalk');
|
|
|
|
|
const clipboardy = require('clipboardy');
|
|
|
|
|
const compileHtml = require('./build/compile-html.js');
|
|
|
|
|
const fsp = require('./build/fsp.js');
|
|
|
|
|
const i18n = require('./build/i18n.js');
|
|
|
|
|
const Koa = require('koa');
|
|
|
|
|
const koaStatic = require('koa-static');
|
|
|
|
|
|
|
|
|
|
const polka = require('polka');
|
|
|
|
|
const dhost = require('dhost');
|
|
|
|
|
|
|
|
|
|
const log = require('fancy-log');
|
|
|
|
|
const path = require('path');
|
|
|
|
|
|
|
|
|
@ -15,8 +18,8 @@ const yargs = require('yargs')
|
|
|
|
|
.option('port', {
|
|
|
|
|
alias: 'p',
|
|
|
|
|
type: 'number',
|
|
|
|
|
default: process.env.PORT || 5000,
|
|
|
|
|
describe: 'Serving port (+1 for prod)',
|
|
|
|
|
default: process.env.PORT || 8000,
|
|
|
|
|
describe: 'Static port',
|
|
|
|
|
})
|
|
|
|
|
.option('lang', {
|
|
|
|
|
type: 'string',
|
|
|
|
@ -26,17 +29,55 @@ const yargs = require('yargs')
|
|
|
|
|
.option('compile', {
|
|
|
|
|
type: 'boolean',
|
|
|
|
|
default: false,
|
|
|
|
|
describe: 'Compile dependencies',
|
|
|
|
|
describe: 'Compile complex dependencies',
|
|
|
|
|
})
|
|
|
|
|
.argv;
|
|
|
|
|
|
|
|
|
|
function listen(server, port) {
|
|
|
|
|
return new Promise((resolve) => server.listen(port, resolve));
|
|
|
|
|
return new Promise((r) => server.listen(port, 'localhost', r));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function clipboardCopy(v) {
|
|
|
|
|
try {
|
|
|
|
|
clipboardy.writeSync(v);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return e;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const messages = i18n(yargs.lang);
|
|
|
|
|
log(messages('santatracker'));
|
|
|
|
|
|
|
|
|
|
async function prod(req, res, next) {
|
|
|
|
|
let servePath = 'index.html';
|
|
|
|
|
|
|
|
|
|
const simplePathMatch = /^\/(\w+)\.html$/.exec(req.path);
|
|
|
|
|
if (simplePathMatch) {
|
|
|
|
|
const cand = `${simplePathMatch[1]}.html`;
|
|
|
|
|
const exists = await fsp.exists(path.join('prod', cand));
|
|
|
|
|
if (exists) {
|
|
|
|
|
// load the top-level path if the file doesn't already exist (e.g. error/upgrade/cast)
|
|
|
|
|
servePath = cand;
|
|
|
|
|
}
|
|
|
|
|
} else if (res.path !== '/') {
|
|
|
|
|
return next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// compile the HTML locally to include i18n and static URL
|
|
|
|
|
const filename = path.join('prod', servePath);
|
|
|
|
|
const options = {
|
|
|
|
|
compile: yargs.compile,
|
|
|
|
|
messages,
|
|
|
|
|
body: {
|
|
|
|
|
static: staticURL,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
|
|
|
|
|
res.end(await compileHtml(filename, options));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function serve() {
|
|
|
|
|
const loader = require('./loader.js')({
|
|
|
|
|
compile: yargs.compile,
|
|
|
|
@ -45,48 +86,24 @@ async function serve() {
|
|
|
|
|
});
|
|
|
|
|
const loaderTransform = require('./loader-transform.js');
|
|
|
|
|
|
|
|
|
|
const server = new Koa();
|
|
|
|
|
server.use(loaderTransform(loader));
|
|
|
|
|
server.use(koaStatic('.'));
|
|
|
|
|
|
|
|
|
|
await listen(server, yargs.port);
|
|
|
|
|
log(`=> ${colors.blue(`http://localhost:${yargs.port}`)}`);
|
|
|
|
|
|
|
|
|
|
const prod = new Koa();
|
|
|
|
|
prod.use(async (ctx, next) => {
|
|
|
|
|
const simplePathMatch = /^\/(\w+)\.html$/.exec(ctx.path);
|
|
|
|
|
if (simplePathMatch) {
|
|
|
|
|
const sceneName = simplePathMatch[1];
|
|
|
|
|
const exists = await fsp.exists(path.join('prod', `${sceneName}.html`));
|
|
|
|
|
if (!exists) {
|
|
|
|
|
// load the top-level path if the file doesn't already exist (e.g. error/upgrade/cast)
|
|
|
|
|
ctx.url = '/index.html';
|
|
|
|
|
}
|
|
|
|
|
} else if (ctx.path === '/') {
|
|
|
|
|
ctx.url = '/index.html';
|
|
|
|
|
}
|
|
|
|
|
const staticServer = polka();
|
|
|
|
|
staticServer.use(loaderTransform(loader));
|
|
|
|
|
staticServer.use('static', dhost({path: 'static', cors: true, listing: true}));
|
|
|
|
|
|
|
|
|
|
if (path.extname(ctx.path) !== '.html') {
|
|
|
|
|
return next();
|
|
|
|
|
}
|
|
|
|
|
await listen(staticServer, yargs.port + 1000);
|
|
|
|
|
const staticURL = `http://127.0.0.1:${yargs.port + 1000}/static`;
|
|
|
|
|
log('Static', chalk.green(staticURL));
|
|
|
|
|
|
|
|
|
|
// compile the HTML locally to include i18n and static URL
|
|
|
|
|
const filename = path.join('prod', ctx.path);
|
|
|
|
|
const options = {
|
|
|
|
|
compile: yargs.compile,
|
|
|
|
|
messages,
|
|
|
|
|
body: {
|
|
|
|
|
static: `http://localhost:${yargs.port}/index.html`,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
ctx.response.body = await compileHtml(filename, options);
|
|
|
|
|
ctx.response.type = 'text/html';
|
|
|
|
|
});
|
|
|
|
|
prod.use(koaStatic('prod'));
|
|
|
|
|
const prodServer = polka();
|
|
|
|
|
prodServer.use(prod);
|
|
|
|
|
prodServer.use(dhost({path: 'prod', listing: false}));
|
|
|
|
|
|
|
|
|
|
// advertise 127.0.0.1, not localhost, as this emulates-ish CORS for Chrome
|
|
|
|
|
await listen(prod, yargs.port + 1);
|
|
|
|
|
log(`=> ${colors.blue(`http://127.0.0.1:${yargs.port + 1}`)} prod`);
|
|
|
|
|
// listen, copy and announce prod URL
|
|
|
|
|
await listen(prodServer, yargs.port);
|
|
|
|
|
const prodURL = `http://localhost:${yargs.port}`;
|
|
|
|
|
const clipboardError = clipboardCopy(prodURL);
|
|
|
|
|
const suffix = clipboardError ? chalk.red('(could not copy to clipboard)') : chalk.dim('(on your clipboard!)');
|
|
|
|
|
log('Prod', chalk.greenBright(`http://localhost:${yargs.port}`), suffix);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
serve().catch((err) => {
|
|
|
|
|