Skip to content

jstrimpel/builder-init

 
 

Repository files navigation

Travis Status Coverage Status

Builder Initializer

Initialize projects from builder archetypes.

Installation

Install this package as a global dependency.

$ npm install -g builder-init

Although we generally disfavor global installs, this tool creates new projects from scratch, so you have to start somewhere...

Usage

TODO: Add usage, documentation.
https://github.com/FormidableLabs/builder-init/issues/6

Templates

Archetype Data

Archetypes provide data for template expansion via an init.js file in the root of the archetype. The structure of the file is:

module.exports = {
  prompts: // Questions and responses for the user
  derived: // Other fields derived from the data provided by the user
};

User Prompts

User prompts and responses are ingested using inquirer. The prompts field of the init.js object can either be an array or object of inquirer question objects. For example:

module.exports = {
  prompts: [
    {
      name: "name",
      message: "What is your name?",
      validate: function (val) {
        // Validate functions return `true` if valid.
        // If invalid, return `false` or an error message.
        return !!val.trim() || "Must enter a name!";
      }
    },
    {
      name: "quest",
      message: "What is your quest?"
    }
  ]
};

builder-init provides a short-cut of placing the name field as the key value for a prompts object instead of an array:

module.exports = {
  prompts: {
    name: {
      message: "What is your name?",
      validate: function (val) { return !!val.trim() || "Must enter a name!"; }
    },
    quest: {
      message: "What is your quest?"
    }
  }
};

Note - Async: Inquirer has some nice features, one of which is enabling functions like validate to become async by using this.async(). For example:

name: {
  message: "What is your name?",
  validate: function (val) {
    var done = this.async();

    // Let's wait a second.
    setTimeout(function () {
      done(!!val.trim() || "Must enter a name!")
    }, 1000);
  }
}

Derived Data

Archetype authors may not wish to expose all data for user input. Thus, builder-init supports a simple bespoke scheme for taking the existing user data and adding derived fields.

The derived field of the init.js object is an object of functions with the signature:

derived: {
  // - `data`     All existing data from user prompts.
  // - `callback` Callback of form `(error, derivedData)`
  upperName: function (data, cb) {
    // Uppercase the existing `name` data.
    cb(null, data.name.toUpperCase());
  }
}

Application

Presently, all files in the init/ directory of an archetype are parsed as templates. We will reconsider this over time if escaping the template syntax becomes problematic.

Templates Directory Ingestion

builder-init mostly just walks the init/ directory of an archetype looking for any files with the following features:

  • An empty / non-existent init/ directory is allowed, although nothing will be written out.
  • If an init/.gitignore file is found, the files matched in the templates directory will be filtered to ignore any .gitignore glob matches. This filtering is done at load time before file name template strings are expanded (in case that matters).

Template Parsing

builder-init uses Lodash templates, with the following customizations:

  • ERB-style templates are the only supported format. The new ES-style template strings are disabled because the underlying processed code is likely to include JS code with ES templates.
  • HTML escaping by default is disabled so that we can easily process <, >, etc. symbols in JS.

The Lodash templates documentation can be found at: https://github.com/lodash/lodash/blob/master/lodash.js#L12302-L12365

And, here's a quick refresher:

Variables

var compiled = _.template("Hi <%= user %>!");
console.log(compiled({ user: "Bob" }));
// => "Hi Bob!"
var compiled = _.template(
  "Hi <%= _.map(users, function (u) { return u.toUpperCase(); }).join(\", \") %>!");
console.log(compiled({ users: ["Bob", "Sally"] }));
// => Hi BOB, SALLY!

JavaScript Interpolation

var compiled = _.template(
  "Hi <% _.each(users, function (u, i) { %>" +
    "<%- i === 0 ? '' : ', ' %>" +
    "<%- u.toUpperCase() %>" +
  "<% }); %>!");
console.log(compiled({ users: ["Bob", "Sally"] }));
// => Hi BOB, SALLY!

File Name Parsing

In addition file content, builder-init also interpolates and parses file names using an alternate template parsing scheme, inspired by Mustache templates. (The rationale for this is that ERB syntax is not file-system compliant on all OSes).

So, if we have data: packageName: "whiz-bang-component" and want to create a file-system path:

src/components/whiz-bang-component.jsx

The source archetype should contain a full file path like:

init/src/components/{{packageName}}.jsx

builder-init will validate the expanded file tokens to detect clashes with other static file names provided by the generator.

About

A project generator for builder archetypes.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 100.0%