From 2c2b772b4699a967095c732b5428f57209af6c72 Mon Sep 17 00:00:00 2001 From: Mark Perkins Date: Mon, 11 Jan 2016 16:25:09 +0000 Subject: [PATCH] CLI component generator first pass --- bin/{fractal.js => fractal} | 4 +- config.json | 2 +- package.json | 9 +- src/application.js | 2 +- src/data.js | 25 +++++ src/errors/exists.js | 13 +++ src/services/generator/generator.js | 45 ++++----- src/services/generator/handlers/component.js | 100 +++++++++++++++++++ src/sources/components.js | 47 +++++++++ 9 files changed, 215 insertions(+), 32 deletions(-) rename bin/{fractal.js => fractal} (98%) mode change 100644 => 100755 create mode 100644 src/errors/exists.js create mode 100644 src/services/generator/handlers/component.js diff --git a/bin/fractal.js b/bin/fractal old mode 100644 new mode 100755 similarity index 98% rename from bin/fractal.js rename to bin/fractal index 0d040b065..8f62a9624 --- a/bin/fractal.js +++ b/bin/fractal @@ -1,3 +1,5 @@ +#! /usr/bin/env node + 'use strict'; /* @@ -164,7 +166,7 @@ checkCommandCount(yargs, argv, 1); function checkCommandCount(yargs, argv, numRequired) { if (argv._.length < numRequired) { yargs.showHelp(); - process.exit(1); + process.exit(0); return false; } return true; diff --git a/config.json b/config.json index cb8b725d6..1dd134815 100644 --- a/config.json +++ b/config.json @@ -23,7 +23,7 @@ }, "generator": { "config": { - "ext": ".js" + "name": "{{name}}.config.js" } }, "components": { diff --git a/package.json b/package.json index 58fb2db56..d26f8564a 100644 --- a/package.json +++ b/package.json @@ -42,14 +42,15 @@ "yargs": "^3.31.0" }, "bin": { - "fractal": "./bin/fractal.js" + "fractal": "./bin/fractal" }, "engines": { "node": ">= 4.0.0" }, "scripts": { - "start": "node ./bin/fractal.js start", - "build": "node ./bin/fractal.js build", - "init": "node ./bin/fractal.js init" + "start": "node ./bin/fractal start", + "build": "node ./bin/fractal build", + "init": "node ./bin/fractal init", + "create": "node ./bin/fractal create" } } diff --git a/src/application.js b/src/application.js index 9de703134..896cf221c 100644 --- a/src/application.js +++ b/src/application.js @@ -120,7 +120,7 @@ app.run = function(argv){ this.runGeneratorService(argv); break; case 'init': - console.log(chalk.magenta('Fractal `init` command is not yet implemented.')); + console.log(chalk.magenta('The `init` command is not yet implemented.')); process.exit(0); break; default: diff --git a/src/data.js b/src/data.js index 582d09dd4..f1f5bfd4f 100644 --- a/src/data.js +++ b/src/data.js @@ -55,6 +55,31 @@ module.exports = { return _.defaultsDeep(data, defaults); }); + }, + + write: function(filePath, data) { + var pathInfo = path.parse(path.resolve(filePath)); + var ext = pathInfo.ext.toLowerCase(); + var content = null; + + switch(ext) { + case ".js": + content = 'module.exports = ' + JSON.stringify(data, null, 4) + ';'; + break; + case ".json": + content = JSON.stringify(data, null, 4); + break; + break; + case ".yml": + case ".yaml": + content = yaml.safeDump(data); + break; + default: + throw new Error('Unknown data file extension: ' + ext); + return; + } + + return fs.writeFileAsync(filePath, content); } }; diff --git a/src/errors/exists.js b/src/errors/exists.js new file mode 100644 index 000000000..f0cbbca9f --- /dev/null +++ b/src/errors/exists.js @@ -0,0 +1,13 @@ +module.exports = ExistsError; + +function ExistsError(message, previous) { + var error = Error.call(this, message); + + this.name = 'ExistsError'; + this.message = error.message; + this.stack = previous ? previous.stack : error.stack; + this.previous = previous; +} + +ExistsError.prototype = Object.create(Error.prototype); +ExistsError.prototype.constructor = ExistsError; diff --git a/src/services/generator/generator.js b/src/services/generator/generator.js index 56f14f56b..4a2c436eb 100644 --- a/src/services/generator/generator.js +++ b/src/services/generator/generator.js @@ -2,38 +2,33 @@ * Module dependencies. */ -var logger = require('winston'); -var Promise = require('bluebird'); -var chalk = require('chalk'); var path = require('path'); -var fs = require('fs'); -var _ = require('lodash'); -var mkdirp = Promise.promisify(require('mkdirp')); -var ncp = Promise.promisify(require('ncp')); -var rimraf = Promise.promisify(require('rimraf')); +var logger = require('winston'); /** * Export the generator function */ -module.exports = function(type, relPath, opts, app){ - var opts = opts || {}; - switch (type) { - case 'component': - var rootPath = app.get('components:path'); - break; - case 'page': - var rootPath = app.get('pages:path'); - break; - default: - logger.error("Entity type '%s' is not recognised. Try 'page' or 'component' instead.", type); - process.exit(1); - return; - } +module.exports = function(argv, app){ - var entityPath = path.join(rootPath, relPath); + var type = argv._[1]; + var relPath = argv._[2].trim('/'); + + try { + var Handler = require('./handlers/' + type); + } catch(e) { + logger.debug(e.message); + logger.error("Entity type '%s' is not recognised. Try 'page' or 'component' instead.", type); + process.exit(1); + return; + } - console.log(entityPath); + var generator = new Handler(app); + generator.generate(relPath, argv).catch(function(e){ + logger.error(e.message); + process.exit(1); + }).finally(function(){ + process.exit(0); + }); - process.exit(0); }; diff --git a/src/services/generator/handlers/component.js b/src/services/generator/handlers/component.js new file mode 100644 index 000000000..053521070 --- /dev/null +++ b/src/services/generator/handlers/component.js @@ -0,0 +1,100 @@ +/** + * Module dependencies. + */ + +var logger = require('winston'); +var Promise = require('bluebird'); +var path = require('path'); +var _ = require('lodash'); +var fs = Promise.promisifyAll(require('fs')); +var mkdirp = Promise.promisify(require('mkdirp')); +var ncp = Promise.promisify(require('ncp')); +var rimraf = Promise.promisify(require('rimraf')); + +var ExistsError = require('../../../errors/exists'); +var utils = require('../../../utils'); + + +module.exports = ComponentGenerator; + +/* + * ComponentGenerator constructor. + * + * @api private + */ + +function ComponentGenerator(app){ + this.app = app; +}; + + +/* + * Run the generator. + * + * @api public + */ + +ComponentGenerator.prototype.generate = function(relPath, opts){ + var self = this; + var fullPath = path.join(self.app.get('components:path'), relPath); + return this.app.getComponents().then(function(components){ + if (components.exists(relPath)) { + throw new ExistsError('The component at ' + relPath +' already exists.'); + } + return mkdirp(fullPath).then(function(){ + return components + }); + }).then(function(components){ + return components.create(relPath) + // var pathParts = path.parse(fullPath); + // var title = utils.titlize(pathParts.name); + // + // var config = { + // handle: pathParts.name, + // label: utils.titlize(pathParts.name) + // context: {} + // }; + // + // var templatePath = path.join(fullPath, pathParts.name + self.app.getComponentViewEngine().ext); + // var configPath = path.join(fullPath, self.app.get('generator:config:name').replace('{{name}}', pathParts.name)); + // + // var writes = [ + // fs.writeFileAsync(templatePath, '

' + + ' component

'), + // fs.writeFileAsync(configPath, JSON.stringify(config, null, 4)) + // ]; + // + // return Promise.all(writes); + }); +}; + +// /** +// * Export the generator function +// */ +// +// module.exports = function(argv, app){ +// +// var type = argv._[1]; +// var relPath = argv._[2]; +// +// switch (type) { +// case 'component': +// var rootPath = app.get('components:path'); +// break; +// case 'page': +// var rootPath = app.get('pages:path'); +// break; +// default: +// logger.error("Entity type '%s' is not recognised. Try 'page' or 'component' instead.", type); +// process.exit(1); +// return; +// } +// +// var components = app.getComponents(); +// .findByPath(relPath.trim('/')); +// +// // var entityPath = path.join(rootPath, relPath); +// +// console.log(entityPath); +// +// process.exit(0); +// }; diff --git a/src/sources/components.js b/src/sources/components.js index 0c3953c2b..766415cda 100644 --- a/src/sources/components.js +++ b/src/sources/components.js @@ -5,6 +5,9 @@ var Promise = require('bluebird'); var _ = require('lodash'); var logger = require('winston'); +var path = require('path'); +var fs = Promise.promisifyAll(require('fs')); +var mkdirp = Promise.promisify(require('mkdirp')); var Directory = require('../filesystem/directory'); var Component = require('./entities/component'); @@ -12,6 +15,7 @@ var Group = require('./entities/group'); var mixin = require('./source'); var data = require('../data'); var NotFoundError = require('../errors/notfound') +var utils = require('../utils'); /* * Export the component source. @@ -196,6 +200,49 @@ ComponentSource.prototype.filter = function(key, value){ return new ComponentSource(filter(this.components), this.app).init(); }; +/* + * Checks if a component exists + * + * @api public + */ + +ComponentSource.prototype.exists = function(str){ + try { + var component = this.resolve(str); + return component; + } catch(e){ + return false; + } +}; + +/* + * Creates a new component + * + * @api public + */ + +ComponentSource.prototype.create = function(relPath, opts){ + + var fullPath = path.join(this.app.get('components:path'), relPath); + var pathParts = path.parse(fullPath); + var title = utils.titlize(pathParts.name); + + var config = { + handle: pathParts.name, + label: title + }; + + var templatePath = pathParts.name + this.app.getComponentViewEngine().ext; + var configPath = this.app.get('generator:config:name').replace('{{name}}', pathParts.name); + + var writes = [ + fs.writeFileAsync(path.join(fullPath, templatePath), '

' + title + ' component

'), + data.write(path.join(fullPath, configPath), config) + ]; + + return Promise.all(writes); +}; + /* * Returns a JSON representation of all the components *