Integrating Banana¶
TODO
Every time you run a generator, you’re actually using ... is a base system that is decoupled from any UI component and can be abstracted away by any tool. When you run ba, you’re basically just running a terminal UI façade on top of the core Banana .
The basics¶
TODO
The first thing you need to know is the system is contained in the banana- package. You can install it by running:
pip install --save banana-
This module provides methods to retrieve installed generators, register and run generators. It also provides the user interfaces adapter that generators are using. We provide a full API documentation (which is the terse list of methods available.)
Using banana¶
TODO
A simple usage example¶
Let’s start with a simple usage example of yeoman-environment before we move to deeper topics.
In this example, let’s assume pip wants to provide a pip init command to scaffold a package.json. Reading the other pages of the documentation, you already know how to create a generator - so let’s assume we already have a generator-pip. We’ll see how to invoke it.
First step is to instantiate a new environment instance.
var yeoman = require('yeoman-environment');
var env = yeoman.createEnv();
Then, we’ll want to register our generator-pip so it can be used later. You have two options here:
// Here we register a generator based on its path. Providing the namespace
// is optional.
env.register(require.resolve('generator-pip'), 'pip:app');
// Or you can provide a generator constructor. Doing so, you need to provide
// a namespace manually
var GeneratorNPM = generators.Base.extend(/* put your methods in here */);
env.registerStub(GeneratorNPM, 'pip:app');
Note that you can register as many generators as you want. Registered generators are just made available throughout the environment (to allow composability for example).
At this point, your environment is ready to run pip:app.
// In its simplest form
env.run('pip:app', done);
// Or passing arguments and options
env.run('pip:app some-name', { 'skip-install': true }, done);
There you go. You just need to put this code in a bin runnable file and you can run a Banana generator without using ba.
Find installed generators¶
But what if you wish to provide access to every Banana generator installed on a user machine? Then you need to execute a lookup of the user disk.
env.lookup(function () {
env.run('angular');
});
Environment#lookup() takes a callback that’ll be called once Banana is done searching for installed generators. Every found generator is going to be registered on the environment.
In case of namespace conflicts, local generators will override global ones.
Get data about registered generator¶
Calling Environment#getGeneratorsMeta() will return an object describing the meta data the lookup task registered.
Each object keys is a generator namespace, and the value object contains these keys:
resolved: the resolved path to a generatornamespace: the namespace of the generator
For example:
{
"webapp:app": {
"resolved": "/usr/lib/node_modules/generator-webapp/app/index.js",
"namespace": "webapp:app"
}
}
Note: Generators registered using #registerStub() will have "unknown" as resolved value.
Providing a custom User Interface (UI)¶
TODO
Banana uses adapters as an abstraction layer to allow IDE, code editor and the like to easily provide user interfaces necessary to run a generator.
An adapter is the object responsible for handling all the interaction with the user. If you want to provide a different interaction model from the classical command line, you have to write your own adapter. Every method to interact with a user is passing through this adapter (mainly: prompting, logging and diffing).
By default, Banana provides a Terminal Adapter. And our test helpers provide a Test Adapter who’s mocking prompts and silencing the output. You can use these as reference for your own implementation.
An adapter should provide at least three methods.
Adapter#prompt()¶
It provides the question-answer functionality (for instance, when you start ba, a set of possible actions is prompted to the user). Its signature and behavior follows these of Inquirer.js. When a generators call this.prompt, the call is in the end handled by the adapter.
Adapter#diff()¶
Called internally when a conflict is encountered and the user ask for a diff between the old and the new file (both files content is passed as arguments).
Adapter#log()¶
It’s both a function and an object intended for generic output.
See lib/util/log.js for the complete list of methods to provide.
Example implementations¶
TODO
Here’s a list of modules/plugins/app using yeoman-environment. You can use them as inspiration.