Skip to content

Commit

Permalink
first version complete with handlebar and prompt support
Browse files Browse the repository at this point in the history
  • Loading branch information
amwmedia committed Oct 9, 2014
1 parent 1bb0ee4 commit 90ff353
Show file tree
Hide file tree
Showing 11 changed files with 450 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
*.sublime-*
82 changes: 82 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
// Settings
"maxerr" : 50, // Maximum error before stopping.
"passfail" : false, // Stop on first error.


// Predefined globals whom JSHint will ignore.
"browser" : true, // Standard browser globals e.g. `window`, `document`.
"jquery" : false, // Globals exposed by the jQuery JavaScript library.
"node" : true, // Globals available when your code is running inside of the Node runtime environment.


// Custom globals.
"globals": {
"after" : false,
"afterEach" : false,
"angular" : false,
"before" : false,
"beforeEach": false,
"browser" : false,
"describe" : false,
"expect" : false,
"inject" : false,
"it" : false,
"jasmine" : false,
"spyOn" : false,
"require" : false,
"define" : false
},


// Development.
"debug" : false, // Allow debugger statements e.g. browser breakpoints.
"devel" : false, // Allow developments statements e.g. `console.log();`.


// EcmaScript.
"esnext" : false, // Allow EcmaScript 6 syntax.
"globalstrict" : true, // Allow global "use strict" (also enables 'strict').
"strict" : true, // Require `use strict` pragma in every file.


// The Good Parts.
"asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons).
"bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.).
"boss" : true, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
"curly" : true, // Require {} for every new block or scope.
"eqeqeq" : true, // Require triple equals i.e. `===`.
"eqnull" : true, // Tolerate use of `== null`.
"evil" : false, // Tolerate use of `eval`.
"expr" : false, // Tolerate `ExpressionStatement` as Programs.
"forin" : false, // Tolerate `for in` loops without `hasOwnPrototype`.
"immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"latedef" : true, // Prohibit variable use before definition.
"laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
"loopfunc" : false, // Allow functions to be defined within loops.
"noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"regexdash" : true, // Tolerate unescaped last dash i.e. `[-...]`.
"regexp" : true, // Prohibit `.` and `[^...]` in regular expressions.
"scripturl" : false, // Tolerate script-targeted URLs.
"shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`.
"smarttabs" : true, // Suppresses warnings about mixed tabs and spaces when the latter are used for alignmnent only.
"supernew" : false, // Tolerate `new function () { ... };` and `new Object;`.
"undef" : true, // Require all non-global variables be declared before they are used.


// Personal styling prefrences.
"camelcase" : true, // Force all variable names to use either camelCase style or UPPER_CASE with underscores.
"indent" : 4, // Enforces specific tab width for your code.
"newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`.
"noempty" : true, // Prohipit use of empty blocks.
"nomen" : false, // Prohibit use of initial or trailing underbars in names.
"nonew" : false, // Prohibit use of constructors for side-effects.
"onevar" : false, // Allow only one `var` statement per function.
"plusplus" : false, // Prohibit use of `++` & `--`.
"quotmark" : "single", // Enforces the consistency of quotation marks used throughout your code.
"sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
"trailing" : true, // Prohibit trailing whitespaces.
"undef" : true, // Prohibits the use of explicitly undeclared variables.
"unused" : "vars", // Warns when you define and never use your variables.
"white" : false // Check against strict whitespace and indentation rules.
}
8 changes: 8 additions & 0 deletions example/plopfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = function (plop) {
'use strict';
plop.helpers.dashAround = function (text) {
return '---- ' + text + ' ----';
};

// plop.setPlopFolder('generators');
};
16 changes: 16 additions & 0 deletions example/plops/test/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"modify": {

},
"prompts": {
"name": {
"description": "What is your name?",
"message": "Name is required"
},
"age": {
"description": "How old are you?",
"pattern": "[0-9]+",
"message": "Age must be a number."
}
}
}
2 changes: 2 additions & 0 deletions example/plops/test/folder/{{dashCase name}}.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{dashAround name}}
Hi, my name is {{name}} and I am {{age}}.
11 changes: 11 additions & 0 deletions mod/console-out.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = (function () {
'use strict';
function listOptions(plopList) {
console.log('#### AVAILABLE OPTIONS ####\n -', plopList.join('\n - '));
process.exit(0);
}

return {
listOptions: listOptions
};
})();
104 changes: 104 additions & 0 deletions mod/fs-promise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
module.exports = (function () {
'use strict';

var q = require('q'),
fs = require('fs'),
mkdirp = require('mkdirp'),
path = require('path');

var readFile = q.denodeify(fs.readFile),
writeFile = q.denodeify(fs.writeFile),
readDir = q.denodeify(fs.readdir),
makeDir = q.denodeify(mkdirp),
stat = q.denodeify(fs.stat);


function getFile(path) {
return readFile(path, 'utf8');
}

function setFile(path, data) {
return writeFile(path, data, 'utf8');
}

function getJson(path) {
return getFile(path).then(function (data) {
return JSON.parse(data);
});
}

function setDir(dir) {
return makeDir(dir).then(function () { return dir; });
}

function getFolderList(dir) {
var _d = q.defer();

readDir(dir).then(function (list) {
list = list || [];
var pending = list.length || 0,
results = [];

if (!pending) { _d.reject(new Error('Nothing to list: ' + dir)); }

list.forEach(function(filePath) {
var path = dir + '/' + filePath;
stat(path).then(function(dstat) {
if (dstat && dstat.isDirectory()) {
results.push(filePath);
}

if (!--pending) { _d.resolve(results); }
});
});
}, function () {
_d.reject(new Error('Folder not found: ' + dir));
});

return _d.promise;
}

function fileListRecusive(dir) {
var _d = q.defer(),
results = [];

readDir(dir)
.then(function(list) {

var pending = list.length;
if (!pending) { _d.resolve(results); return; }

list.forEach(function(file) {
file = path.join(dir, file);
stat(file)
.then(function(stat) {
if (stat && stat.isDirectory()) {
fileListRecusive(file).then(function(res) {
results = results.concat(res);
if (!--pending) { _d.resolve(results); return; }
});
} else {
// omit dotfiles
if (!file.split('/').pop().match(/^\./)) {
results.push(file);
}

if (!--pending) { _d.resolve(results); return; }
}
});
});
})
.fail(_d.reject);

return _d.promise;
}

return {
readFile: getFile,
writeFile: setFile,
json: getJson,
folderList: getFolderList,
makeDir: setDir,
fileListRecusive: fileListRecusive
};
})();
99 changes: 99 additions & 0 deletions mod/logic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
module.exports = (function () {
'use strict';

var prompt = require('prompt'),
q = require('q'),
path = require('path'),
colors = require('colors');

var plop = require('./plop-base'),
fs = require('./fs-promise');

var genName = '',
genPath = '',
plopFolderName = '',
config = {};

function getPlopData(gName) {
var _d = q.defer(),
configPath;

genName = gName;
genPath = path.join(plop.getPlopFolderPath(), gName);
plopFolderName = plop.getPlopFolder();
configPath = path.join(genPath, 'config.json');

fs.json(configPath)
.then(function (cfg) {
var prompts = cfg.prompts,
key;

config = cfg;

for (key in prompts) {
if (!prompts.hasOwnProperty(key)) { continue; }
if (prompts[key].pattern) {
prompts[key].pattern = new RegExp(prompts[key].pattern);
}
}

prompt.message = colors.green(gName);
prompt.start();
prompt.get({properties: prompts}, function (err, result) {
if (err) { _d.reject(err); }
_d.resolve(result);
});
})
.fail(_d.reject);

return _d.promise;
}

function executePlop(data) {
var _d = q.defer(),
newFiles = [],
progress;

fs.fileListRecusive(genPath)
.then(function (list) {
// remove the config file from the list of files to create
list = list.filter(function (v, i , a) {
return !(new RegExp('[\\\\/]{1,2}' + plopFolderName + '[\\\\/]{1,2}' + genName + '[\\\\/]{1,2}config.json')).test(v);
});

// keep track of how many files we need to process
// so we know when everything is complete
progress = list.length;
list.forEach(function (filePath) {
// copy the filePath and remove the generator folder
var newFilePath = filePath.replace(new RegExp('[\\\\/]{1}' + plopFolderName + '[\\\\/]{1}' + genName + '([\\\\/]{1})', 'gi'), '$1');
// escape bad handlebar stuff in path
newFilePath = newFilePath.replace(/\\{{/g, '\\\\{{');
// render the template for the filename
newFilePath = plop.renderString(newFilePath, data);
newFiles.push(newFilePath);

fs.makeDir(path.dirname(newFilePath))
.then(function () { return fs.readFile(filePath); })
.then(function (fileData) {
return fs.writeFile(newFilePath, plop.renderString(fileData, data));
})
.then(function () {
if (!--progress) {
_d.resolve(newFiles);
}
});
});

_d.resolve(list);
});
// .then();

return _d.promise;
}

return {
getPlopData: getPlopData,
executePlop: executePlop
};
})();
47 changes: 47 additions & 0 deletions mod/plop-base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module.exports = (function () {
'use strict';
var changeCase = require('change-case'),
path = require('path'),
handlebars = require('handlebars');

var basePath = '',
plopFolderName = 'plops',
helpers = {
camelCase: changeCase.camel,
snakeCase: changeCase.snake,
dashCase: changeCase.param,
dotCase: changeCase.dot,
pathCase: changeCase.path,
properCase: changeCase.pascalCase
};


function addHelper(name, fn) { helpers[name] = fn; }
function setBasePath(p) { basePath = p; }
function setPlopFolder(pf) { plopFolderName = pf; }
function getPlopFolder() { return plopFolderName; }

function getPlopFolderPath() { return path.join(basePath, plopFolderName); }

function renderString(template, data) {
var t = template,
h;

for (h in helpers) {
if (!helpers.hasOwnProperty(h)) { continue; }
handlebars.registerHelper(h, helpers[h]);
}

return handlebars.compile(t)(data);
}

return {
renderString: renderString,
helpers: helpers,
setBasePath: setBasePath,
setPlopFolder: setPlopFolder,
getPlopFolder: getPlopFolder,
getPlopFolderPath: getPlopFolderPath,
addHelper: addHelper
};
})();
Loading

0 comments on commit 90ff353

Please sign in to comment.