-
Notifications
You must be signed in to change notification settings - Fork 15
Scaffolding
anvil.scaffold
is the generic scaffolding utility built into Anvil. anvil.scaffold
allows you to configure scaffold definitions using a conventional format to generate templated files and directories.
--
With anvil.scaffold
you can create scaffold definitions for generating files and directories. As a simple demo, here is an anvil plugin that will define how additional Anvil plugins could be scaffolded:
anvil.scaffold({
type: 'plugin',
description: 'Creates a new anvil plugin',
prompt: [{
name: 'name',
description: 'Please choose a name for this plugin:',
required: true
}],
output: {
lib: {},
src: {
'index.js': anvil.scaffold.file( __dirname + '/plugin.template.js' )
}
}
});
The contents of the plugin.template.js
is:
module.exports = function (_, anvil) {
return anvil.plugin({
name: '{{name}}',
configure: function (config, command, done) {
done();
},
run: function (done) {
done()
}
});
};
Once this scaffold is executed, it will output lib
and src
directories and an index.js
file with src
that has the name populated from metadata supplied by the user at the command line.
anvil.scaffold( definition );
anvil.scaffold
accepts a single object for defining the scaffold:
anvil.scaffold({
...
});
The following is a list of acceptable properties that can be set on the scaffold definition object.
--
type
The unique identifier to reference this scaffold. This type will be used on the command line to specify which scaffold to invoke.
Examples
type: 'plugin'
type: 'jquery-plugin'
type: 'backbone:model'
--
description
A short description that will be shown on the command line when when a user runs anvil generate list
Examples
description: 'Create an empty Backbone project'
description: 'Create a new Anvil plugin'
--
prompt
This is a value used to solicit additional metadata from the user. This metadata will be passed as a model to all files and directories for templating. prompt
accepts input in any schema defined by the prompt package.
Examples
// will ask for "name" at command line,
// creating a "name" property for all template models
prompt: ['name']
// will require description at command line,
// creating a "name" property for all template models
prompt: [{
name: 'name',
description: 'Please choose a name for this plugin:',
required: true
}]
// will require 2 descriptions at command line,
// creating a "username" and "password" property for all template models,
// hiding the password entered
prompt: [{
name: 'username',
description: 'Enter your username:',
required: true
}, {
name: 'password',
description: 'Enter your password:',
required: true,
hidden: true
}]
Please see prompt for more detailed usage of the prompt
API.
--
output
Define the structure of directories and files to write to the file system. This accepts a map of properties to file and directory names and contents. If a property is an object, a directory will be generated. If a property is a string, a file will be generated. Take the following example:
output: {
destination: {}
}
Since destination
is an object, a directory with that name will be created. It should also be possible to generate directories using a more complex path style:
output: {
'src/scripts/external': {}
}
This output would create an external
directory within src/scripts
, additionally generating those directories as well if they did not already exist. Since the value of the object of this destination is empty, the directory itself will also remain empty. Nesting any structures within the object will create a nested directory tree:
output: {
src: {
scripts: {
external: {}
}
}
}
In order to generate files, the property's value must be a string. Continuing with our previous example:
output: {
src: {
scripts: {
external: {
'file.js': '(function () {})();'
}
}
}
}
This would output the same directory structure as before, except now a file.js
will be created in the external
directory. The contents to use for the file are the string value of the property, or (function () {})();
. You may also choose to store the content of the file in another file and read it as a string, possibly making maintenance of these files easier.
In addition, all keys and string values are passed through Handlebars for templating. This allows you to dynamically generate file contents or even file and directory names themselves:
output: {
src: {
scripts: {
'{{dirType}}': {
'{{scriptName}}.js': '(function ({{lib}}) {})({{lib}});'
}
}
}
}
The data which is used as a model for the templates comes from data optionally supplied in the scaffold or from a user's inputs at the command prompts. This data is automatically merged together for you. The type
will be passed to the view template automatically.
Finally, you can supply a function for any one of the values as long as your function passes a either a string (the contents of a file), or an object for a new directory level. The final merged user input and data
from your scaffold will be passed into the method as its only argument. These functions are evaluated asynchronously and therefore must invoke their done
callback to continue, passing the value into the callback.
This example would generate a different file based on theoretical user input:
output: function ( data, done ) {
if ( data.short ) {
done( { "short.js": fileContentsShort } );
} else {
done( { "normal.js": fileContentsNormal } );
}
}
You can (and should) use this to load files just when you need them instead of loading them every time your plugin is fired up:
output: {
"yourfile.js": function ( data, done ) {
anvil.fs.read( "./templates/yourfile.js", done );
}
}
In fact, having a file loaded and templated when the scaffold is invoked is such a common use case that there is also a helper method for handing this:
output: {
"yourfile.js": anvil.scaffold.file( __dirname + "/templates/yourfile.js" )
}
Using this helper will return a function that will automatically load the file asynchronously and pass the contents back through the done
method.
--
data
An object containing additional data to pass to scaffold templates (e.g. file contents and file and directory names). This data will be automatically merged with other data including properties provided by anvil.scaffold
and from user input prompt responses. Please note that any properties provided in data
that match names provided in prompt
will have their values overwritten by user input.
Examples
data: {
author: 'appendTo',
status: 'l33t'
},
// Now usable within scaffold templates:
output: {
scripts: {
'{{author}}.js': 'console.log("This app is {{status}}");'
}
}
The following is a list of methods that have default functionality, but you can override to provide a greater level of control to your scaffolds:
--
render
All keys and values on your output
object are passed through this method. The default render
method looks like this:
render: function ( data ) {
var template = Handlebars.compile( data.template );
return template( data.data );
}
The data
argument contains relevant properties necessary for rendering a string for file output:
-
mode
: Will either be"name"
or"file"
based on if it's rendering a file/directory name, or the contents of a file -
template
: The contents of either the file/directory name, or the contents of a file -
filename
: If in"file"
mode this will be just the name and extension of the file, otherwisenull
To disable templating entirely, pass false
as the value for render
:
render: false
--
processData
You can override this method to manipulate the data being passed to any templates right before templating occurs, e.g. right after user input has been retrieved. It receives the data as its only parameter, and only what you return will be used as the new template data.
By default it simply returns the data that is passed in.
Example
In this example, the user supplied name is cleaned and prepared to be used as a directory name and key:
prompt: [{
name: 'name',
description: 'Please choose a name for your theme:',
required: true
}],
processData: function ( data ) {
data.key = data.name
.toLowerCase()
.trim()
.replace( /[^a-z0-9_-]/g, '-' )
.replace( /-+/g, '-' );
return data;
}
--
Invoking a scaffold from the command line:
anvil generate <type>
where type
is the name of a scaffold definition. generate
is also aliased to gen
or new
:
anvil gen backbone:model
anvil new jquery-plugin
If you want to list available scaffolds, use the scaffolds
command:
anvil scaffolds