Tuesday, September 2, 2014

Better CLI in NPM Generators

Unsure of the best way to deal with existing files in eee-polymer-tests, I opt for a warn / force-overwrite approach. That is, when this NPM package attempts to overwrite existing Polymer test files of the same name, it should warn the developer of the problem, but make no changes. If a developer really wants to overwrite these files, she can either manually move them out of the way or use an option like --force.

So far, I have been able to build eee-polymer-tests with pure Node.js. Both of tonight's needs require their own packages. To warn users, I need to output the messages in yellow so that the problem is as obvious as possible. So far, my generators has accepted a single argument (the name of the Polymer element being tested), but I will need something a little more sophisticated to handle command-line options.

A quick search reveals colors and minimist (not minimalist) as my best bets for coloring strings on the command line and parsing the command line, respectively. So I add them to my package.json:
  "name": "eee-polymer-tests",
  "version": "0.0.1",
  "description": "Sane Polymer test setup (including generator)",
  "scripts": {
    "postinstall" : "./generator.js"
  "homepage": "https://github.com/eee-c/eee-polymer-tests",
  "dependencies": {
    "colors": ">0.0",
    "minimist": ">0.0"
In the generator script, I then require both:
var fs = require('fs');
var readline = require('readline');
var parseArgs = require('minimist');
For each of the files being generated, I add a conditional "ok-to-generate":
function generate() {
  if (okKarmaConf()) generateKarmaConf();
  if (okPolymerSetup()) generatePolymerSetup();
  if (okTestSkeleton()) generateTestSkeleton();
I factor out the check / warn logic common to each of these "ok-to-generate" function into a private _okOverwrite() function that checks for the existence of the file and colorizes a warning if the file already exists:
var KARMA_CONF = 'karma.conf.js';
// ...
function okKarmaConf() {
  return _okOverwrite(KARMA_CONF);

function _okOverwrite(filename) {
  if (!fs.existsSync(filename)) return true;

  var message = '[WARN] File exists: ' + filename + '.' +
                ' (use --force to overwrite)';
  return false;
That, combined with last night's readline generator works quite nicely:

The --force option is easy enough, thanks to minimist. I parse the command as:
function parseCommandLine() {
  if (process.argv.length <= 2) return;
  var argv = parseArgs(process.argv.slice(2), {
    'boolean': 'force'
  if (argv._.length != 1) return;

  element = argv._[0];
  force = argv.force;
If there are no arguments, then the element variable is not set (and last night's readline solution will kick in). But, if there are arguments, I set the element value and, optionally, the force value.

Since I have already consolidated all of the ok-to-overwrite checking in a single place, I only have one place to update to accommodate the force value:
function _okOverwrite(filename) {
  if (!fs.existsSync(filename)) return true;

  if (force) {
    var message = 'Force overwrite: ' + filename + '.';
    return true;

  var message = '[WARN] File exists: ' + filename + '.' +
                ' (use --force to overwrite)';
  return false;
With that, my generator still works as a post-install step and it also works directly from the command-line—optionally overwriting existing files if I really, really want to:

I am reasonably satisfied with that solution, so this may mark the end of initial development on eee-polymer-tests. There is still a minor issue with extremely small test files (like the generated spec) failing on rare occasions. I will have a look at that tomorrow. After that, it is time to finish off any remaining chapters in Patterns in Polymer that lack the kinds of tests that eee-polymer-tests can generate.

Day #171

No comments:

Post a Comment