Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

better CLI #170

Merged
merged 7 commits into from
Mar 5, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"code-frame": "^5.0.0",
"escape-html": "^1.0.3",
"express": "^4.16.2",
"get-port": "^3.2.0",
"glob": "^7.1.2",
"locate-character": "^2.0.5",
"mkdirp": "^0.5.1",
Expand All @@ -35,10 +34,11 @@
"relative": "^3.0.2",
"require-relative": "^0.8.7",
"rimraf": "^2.6.2",
"sade": "^1.4.0",
"sander": "^0.6.0",
"serialize-javascript": "^1.4.0",
"source-map-support": "^0.5.3",
"tslib": "^1.8.1",
"tslib": "^1.9.0",
"url-parse": "^1.2.0",
"walk-sync": "^0.3.2",
"webpack-format-messages": "^1.0.1"
Expand All @@ -53,6 +53,7 @@
"electron": "^1.8.2",
"eslint": "^4.13.1",
"eslint-plugin-import": "^2.8.0",
"get-port": "^3.2.0",
"mocha": "^4.0.1",
"nightmare": "^2.10.0",
"npm-run-all": "^4.1.2",
Expand Down
13 changes: 11 additions & 2 deletions src/cli/build.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as fs from 'fs';
import * as path from 'path';
import chalk from 'chalk';
import mkdirp from 'mkdirp';
import rimraf from 'rimraf';
import { create_compilers, create_app, create_routes, create_serviceworker } from 'sapper/core.js'
Expand All @@ -19,9 +20,15 @@ export default async function build() {
const { client, server, serviceworker } = create_compilers();

const client_stats = await compile(client);
console.log(chalk.inverse(`\nbuilt client`));
console.log(client_stats.toString({ colors: true }));
fs.writeFileSync(path.join(output, 'client_info.json'), JSON.stringify(client_stats.toJson()));

await compile(server);
const server_stats = await compile(server);
console.log(chalk.inverse(`\nbuilt server`));
console.log(server_stats.toString({ colors: true }));

let serviceworker_stats;

if (serviceworker) {
create_serviceworker({
Expand All @@ -30,7 +37,9 @@ export default async function build() {
src
});

await compile(serviceworker);
serviceworker_stats = await compile(serviceworker);
console.log(chalk.inverse(`\nbuilt service worker`));
console.log(serviceworker_stats.toString({ colors: true }));
}
}

Expand Down
15 changes: 8 additions & 7 deletions src/cli/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import mkdirp from 'mkdirp';
import rimraf from 'rimraf';
import format_messages from 'webpack-format-messages';
import prettyMs from 'pretty-ms';
import { wait_for_port } from './utils';
import * as port_utils from './port-utils';
import { dest } from '../config';
import { create_compilers, create_app, create_routes, create_serviceworker } from 'sapper/core.js';

Expand Down Expand Up @@ -70,15 +70,14 @@ function create_hot_update_server(port: number, interval = 10000) {
return { send };
}

export default async function dev() {
export default async function dev(port: number) {
process.env.NODE_ENV = 'development';

const dir = dest();
rimraf.sync(dir);
mkdirp.sync(dir);

// initial build
const dev_port = await require('get-port')(10000);
const dev_port = await port_utils.find(10000);

const routes = create_routes();
create_app({ routes, dev_port });
Expand Down Expand Up @@ -109,7 +108,7 @@ export default async function dev() {
unique_errors: new Set()
};

function restart_build(filename) {
function restart_build(filename: string) {
if (restarting) return;

restarting = true;
Expand Down Expand Up @@ -206,7 +205,7 @@ export default async function dev() {

deferreds.client.promise.then(() => {
function restart() {
wait_for_port(3000).then(deferreds.server.fulfil); // TODO control port
port_utils.wait(3000).then(deferreds.server.fulfil); // TODO control port
}

if (proc) {
Expand All @@ -218,7 +217,9 @@ export default async function dev() {

proc = child_process.fork(`${dir}/server.js`, [], {
cwd: process.cwd(),
env: Object.assign({}, process.env)
env: Object.assign({
PORT: port
}, process.env)
});
});
}
Expand Down
6 changes: 3 additions & 3 deletions src/cli/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import express from 'express';
import cheerio from 'cheerio';
import URL from 'url-parse';
import fetch from 'node-fetch';
import { wait_for_port } from './utils';
import * as port_utils from './port-utils';
import { dest } from '../config';

const app = express();
Expand All @@ -27,7 +27,7 @@ export default async function exporter(export_dir: string) {
sander.copyFileSync(build_dir, 'service-worker.js').to(export_dir, 'service-worker.js');
}

const port = await require('get-port')(3000);
const port = await port_utils.find(3000);

const origin = `http://localhost:${port}`;

Expand Down Expand Up @@ -88,7 +88,7 @@ export default async function exporter(export_dir: string) {
});
}

wait_for_port(port)
return port_utils.wait(port)
.then(() => handle(new URL(origin))) // TODO all static routes
.then(() => proc.kill());
}
20 changes: 0 additions & 20 deletions src/cli/help.md

This file was deleted.

125 changes: 75 additions & 50 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -1,89 +1,114 @@
import * as path from 'path';
import * as child_process from 'child_process';
import sade from 'sade';
import mri from 'mri';
import chalk from 'chalk';
import prettyMs from 'pretty-ms';
import help from './help.md';
import build from './build';
import exporter from './export';
import dev from './dev';
import upgrade from './upgrade';
import * as port_utils from './port-utils';
import { exists } from '../utils';
import * as pkg from '../../package.json';

const opts = mri(process.argv.slice(2), {
alias: {
h: 'help'
}
});
const prog = sade('sapper');

if (opts.help) {
const rendered = help
.replace('<@version@>', pkg.version)
.replace(/^(.+)/gm, (m: string, $1: string) => /[#>]/.test(m) ? $1 : ` ${$1}`)
.replace(/^# (.+)/gm, (m: string, $1: string) => chalk.bold.underline($1))
.replace(/^> (.+)/gm, (m: string, $1: string) => chalk.cyan($1));
prog.version(pkg.version);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely personal preference, but I like to recommend assigning all global aspects into the initial assignment. New commands get their own blocks, and that consistent spacing helps separate things with larger CLIs.

const prog = sade('sapper').version(pkg.version);`

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nice. I didn't realise it was chainable, I suppose that makes sense!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aye. You can chain the whole thing if you want, but that'll get messy


console.log(`\n${rendered}\n`);
process.exit(0);
}
prog.command('dev')
.describe('Start a development server')
.option('-p, --port', 'Specify a port')
.action(async ({ port }: { port: number }) => {
if (port) {
if (!await port_utils.check(port)) {
console.log(chalk.bold.red(`> Port ${port} is unavailable`));
return;
}
} else {
port = await port_utils.find(3000);
}

const [cmd] = opts._;
dev(port);
});

const start = Date.now();
prog.command('build [dest]')
.describe('Create a production-ready version of your app')
.action((dest = 'build') => {
console.log(`> Building...`);

switch (cmd) {
case 'build':
process.env.NODE_ENV = 'production';
process.env.SAPPER_DEST = opts._[1] || 'build';
process.env.SAPPER_DEST = dest;

const start = Date.now();

build()
.then(() => {
const elapsed = Date.now() - start;
console.error(`built in ${elapsed}ms`); // TODO beautify this, e.g. 'built in 4.7 seconds'
console.error(`\n> Finished in ${prettyMs(elapsed)}. Type ${chalk.bold.cyan(dest === 'build' ? 'npx sapper start' : `npx sapper start ${dest}`)} to run the app.`);
})
.catch(err => {
console.error(err ? err.details || err.stack || err.message || err : 'Unknown error');
});
});

prog.command('start [dir]')
.describe('Start your app')
.option('-p, --port', 'Specify a port')
.action(async (dir = 'build', { port }: { port: number }) => {
const resolved = path.resolve(dir);
const server = path.resolve(dir, 'server.js');

if (!exists(server)) {
console.log(chalk.bold.red(`> ${dir}/server.js does not exist — type ${chalk.bold.cyan(dir === 'build' ? `npx sapper build` : `npx sapper build ${dir}`)} to create it`));
return;
}

if (port) {
if (!await port_utils.check(port)) {
console.log(chalk.bold.red(`> Port ${port} is unavailable`));
return;
}
} else {
port = await port_utils.find(3000);
}

child_process.fork(server, [], {
cwd: process.cwd(),
env: Object.assign({
NODE_ENV: 'production',
PORT: port,
SAPPER_DEST: dir
}, process.env)
});
});

break;
prog.command('export [dest]')
.describe('Export your app as static files (if possible)')
.action((dest = 'export') => {
console.log(`> Building...`);

case 'export':
process.env.NODE_ENV = 'production';
process.env.SAPPER_DEST = '.sapper/.export';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More for my own curiosity, but why not set SAPPER_DEST from within export()? Especially since the dest is variable

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used by build first — it sends the webpack output to .sapper/.export, and then sapper export runs the code therein to generate the export directory. So it has to be set before either thing happens


const export_dir = opts._[1] || 'export';
const start = Date.now();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use process.hrtime if you want to be a little more exact. Roughly the same though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when pretty-ms is involved I don't think it'll make any difference. I generally avoid process.hrtime because, well, the signature is batshit


build()
.then(() => exporter(export_dir))
.then(() => {
const elapsed = Date.now() - start;
console.error(`extracted in ${elapsed}ms`); // TODO beautify this, e.g. 'built in 4.7 seconds'
console.error(`\n> Built in ${prettyMs(elapsed)}. Exporting...`);
})
.then(() => exporter(dest))
.then(() => {
const elapsed = Date.now() - start;
console.error(`\n> Finished in ${prettyMs(elapsed)}. Type ${chalk.bold.cyan(`npx serve ${dest}`)} to run the app.`);
})
.catch(err => {
console.error(err ? err.details || err.stack || err.message || err : 'Unknown error');
});
});

break;

case 'dev':
dev();
break;

case 'upgrade':
upgrade();
break;

case 'start':
const dir = path.resolve(opts._[1] || 'build');

child_process.fork(`${dir}/server.js`, [], {
cwd: process.cwd(),
env: Object.assign({
NODE_ENV: 'production',
SAPPER_DEST: dir
}, process.env)
});

break;
// TODO upgrade

default:
console.log(`unrecognized command ${cmd} — try \`sapper --help\` for more information`);
}
prog.parse(process.argv);
Loading