Skip to content

Commit

Permalink
improve CLI UX
Browse files Browse the repository at this point in the history
Adds CLI commands, including a `help` command capable of displaying help
messages for individual commands. This will make it easier for people to
directly install `moon-cli` and work with it without having to reference
the documentation for the previous simple but obscure behavior. There
are now three commands, `moon version`, `moon help`, and `moon create`.
They all have error messages and help messages to guide users through
the process of using them. `moon create` now accepts the template as an
option rather than a positional argument, using the `-t` or `--template`
option instead.
  • Loading branch information
kbrsh committed Oct 13, 2019
1 parent d9cb7a7 commit 71c13a6
Show file tree
Hide file tree
Showing 9 changed files with 329 additions and 51 deletions.
24 changes: 13 additions & 11 deletions build/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ const uglify = require("uglify-js");
const gzipSize = require("gzip-size");
const fs = require("fs");
const path = require("path");
const pkg = require("../package.json");
const SPACES_RE = / /g;
const ENV_RE = /process\.env\.MOON_ENV/g;
const version = require("../package.json").version;
const spacesRE = / /g;
const versionRE = /process\.env\.MOON_VERSION/g;
const envRE = /process\.env\.MOON_ENV/g;

const resolver = {
resolveId(id, origin) {
Expand All @@ -18,11 +19,11 @@ const resolver = {
async function build(package) {
const options = require(`../packages/${package}/config.js`);
const name = options.name;
const exportName = options.exportName;
const nameExport = options.nameExport;
const type = options.type;

const comment = `${type === "executable" ? "#!/usr/bin/env node\n" : ""}/**
* ${name} v${pkg.version}
* ${name} v${version}
* Copyright 2016-2019 Kabir Shah
* Released under the MIT License
* https://kbrsh.github.io/moon
Expand All @@ -36,29 +37,30 @@ async function build(package) {
eslint(),
babel()
],
onwarn: (warning) => {
onwarn: warning => {
if (warning.code !== "CIRCULAR_DEPENDENCY") {
console.warn(`Rollup [Warn]: ${warning}`);
}
}
});

let { output } = await bundle.generate({
name: exportName,
name: nameExport,
format: "iife"
});

output = output[0].code;
output = output.replace(SPACES_RE, "\t");
output = output.replace(spacesRE, "\t");

if (type === "module") {
output = fs.readFileSync("./build/wrapper.js").toString().replace("MODULE_NAME", exportName).replace("MODULE_CONTENT", output.split("\n").slice(1, -3).join("\n"));
output = fs.readFileSync("./build/wrapper.js").toString().replace("MODULE_NAME", nameExport).replace("MODULE_CONTENT", output.split("\n").slice(1, -3).join("\n"));
}

output = output.replace("'use strict'", "\"use strict\"");
output = output.replace(versionRE, `"${version}"`);

const developmentCode = comment + output.replace(ENV_RE, '"development"');
const productionCode = comment + uglify.minify(output.replace(ENV_RE, '"production"'), {
const developmentCode = comment + output.replace(envRE, '"development"');
const productionCode = comment + uglify.minify(output.replace(envRE, '"production"'), {
output: {
ascii_only: true
}
Expand Down
2 changes: 1 addition & 1 deletion packages/moon-browser/config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
name: "Moon Browser",
exportName: "MoonBrowser",
nameExport: "MoonBrowser",
type: "script"
};
2 changes: 1 addition & 1 deletion packages/moon-cli/config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
name: "Moon CLI",
exportName: "MoonCLI",
nameExport: "MoonCLI",
type: "executable"
};
161 changes: 144 additions & 17 deletions packages/moon-cli/dist/moon-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,59 @@

var exec = require("child_process").exec;

var name = process.argv[2];
var repo = process.argv[3] || "kbrsh/moon-template";
var archive = {
method: "GET",
host: "api.github.com",
path: "/repos/" + repo + "/tarball/master",
headers: {
"User-Agent": "Node.js"
var parameterRE = /<\w+>/g;
var optionRE = /\[\w+\]|--?\w+/g;
var help = {
version: {
usage: "moon version",
description: "output Moon CLI version"
},
help: {
usage: "moon help <command>",
description: "output help message for command",
parameters: {
"<command>": "name of Moon CLI command"
}
},
create: {
usage: "moon create <name> [options]",
description: "create application in new directory",
parameters: {
"<name>": "name of application and directory"
},
options: {
"-t, --template <username>/<repository>": "GitHub repository to use as template"
}
}
};
var repo, name;

function highlight(string) {
return string.replace(parameterRE, "\x1b[33m$&\x1b[0m").replace(optionRE, "\x1b[36m$&\x1b[0m");
}

function table(object) {
var keys = Object.keys(object);
var max = Math.max.apply(null, keys.map(function (key) {
return key.length;
}));
return keys.map(function (key) {
return "\t" + key + " ".repeat(max - key.length + 3) + object[key];
}).join("\n");
}

function log(type, message) {
console.log("\x1B[34m" + type + "\x1B[0m " + message);
}

function logHelp(message) {
console.log(highlight(message));
}

function error(message) {
console.log("\x1B[31merror\x1B[0m " + message);
}

function replace(content, sub, subNewString) {
var index = content.indexOf(sub);

Expand Down Expand Up @@ -74,7 +112,7 @@
if (err) throw err;
log("clean", archivePath);
create(targetPath, targetPath);
log("success", "Generated project \"" + name + "\"");
log("success", "Generated application \x1B[36m" + name + "\x1B[0m");
console.log("To start, run:\n\tcd " + name + "\n\tnpm install\n\tnpm run dev");
});
}
Expand All @@ -95,15 +133,104 @@
}
}

log("Moon", "Generating project");
https.get(archive, function (res) {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location !== undefined) {
https.get(res.headers.location, function (redirectRes) {
download(redirectRes);
});
var argv = process.argv.length === 2 ? ["help"] : process.argv.slice(2);
var commandName = argv[0];
var commandArguments = [];
var commandOptions = {};

for (var i = 1; i < argv.length; i++) {
var commandArgument = argv[i];

if (commandArgument[0] === "-") {
for (; i < argv.length; i++) {
var commandOption = argv[i];

if (commandOption[0] === "-") {
commandOptions[commandOption] = true;
} else {
commandOptions[argv[i - 1]] = commandOption;
}
}
} else {
download(res);
commandArguments.push(commandArgument);
}
});
}

switch (commandName) {
case "version":
{
logHelp("Moon CLI v" + "1.0.0-beta.4");
break;
}

case "help":
{
var commandNameHelp = commandArguments[0];

if (commandNameHelp in help) {
var helpCommand = help[commandNameHelp];
logHelp("Usage: " + helpCommand.usage + "\n\t" + helpCommand.description);

if ("parameters" in helpCommand) {
logHelp("\nParameters:\n" + table(helpCommand.parameters));
}

if ("options" in helpCommand) {
logHelp("\nOptions:\n" + table(helpCommand.options));
}
} else {
var tableUsageDescription = {};

for (var command in help) {
var _helpCommand = help[command];
tableUsageDescription[_helpCommand.usage] = _helpCommand.description;
}

logHelp("Usage: moon <command> [options]\n\nCommands:\n" + table(tableUsageDescription));
}

break;
}

case "create":
{
name = commandArguments[0];
repo = commandOptions["-t"] || commandOptions["--template"] || "kbrsh/moon-template";

if (name === undefined || name.length === 0) {
error("Invalid or unknown name.\n\nAttempted to create an application.\n\nReceived an invalid or unknown name.\n\nExpected a valid name. Run \x1B[35mmoon help create\x1B[0m to see usage information.");
}

if (repo === true) {
error("Invalid or unknown template.\n\nAttempted to create an application.\n\nReceived an invalid or unknown template.\n\nExpected a valid template. Run \x1B[35mmoon help create\x1B[0m to see usage information.");
}

var archive = {
method: "GET",
host: "api.github.com",
path: "/repos/" + repo + "/tarball/master",
headers: {
"User-Agent": "Moon"
}
};
log("Moon", "Generating application");
https.get(archive, function (res) {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location !== undefined) {
https.get(res.headers.location, function (redirectRes) {
download(redirectRes);
});
} else {
download(res);
}
});
break;
}

default:
{
error("Unrecognized command.\n\nAttempted to execute a command.\n\nReceived a command that does not exist:\n\t" + commandName + "\n\nExpected a valid command. Run \x1B[35mmoon help\x1B[0m to see valid commands.");
break;
}
}

}());
2 changes: 1 addition & 1 deletion packages/moon-cli/dist/moon-cli.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 71c13a6

Please sign in to comment.