-
Notifications
You must be signed in to change notification settings - Fork 313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ES6, new way to extend YO generator #161
base: master
Are you sure you want to change the base?
Changes from 5 commits
cf5a5e4
48cd24f
d42d788
2ab4e39
a7c082e
15721b8
90bbde5
5ba3067
22bb1a3
efab27d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"extends": ["eslint:recommended"], | ||
"env": { | ||
"node": true, | ||
"es6": true, | ||
"mocha": true | ||
}, | ||
"rules": { | ||
"no-var": "error", | ||
"semi": ["error", "always"] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,168 +1,187 @@ | ||
'use strict'; | ||
var path = require('path'); | ||
var yeoman = require('yeoman-generator'); | ||
var pluralize = require('pluralize'); | ||
var _ = require('lodash'); | ||
var recast = require('recast'); | ||
var reservedWords = require('reserved-words'); | ||
|
||
module.exports = yeoman.Base.extend({ | ||
prompting: function () { | ||
var srcDir = this.config.get('srcDir') || 'src'; | ||
var apiDir = this.config.get('apiDir') || 'api'; | ||
var authMethods = this.config.get('authMethods') || []; | ||
|
||
var methods = [ | ||
{name: 'Create (POST)', value: 'POST'}, | ||
{name: 'Retrieve list (GET)', value: 'GET LIST'}, | ||
{name: 'Retrieve one (GET)', value: 'GET ONE'}, | ||
{name: 'Update (PUT)', value: 'PUT'}, | ||
{name: 'Delete (DELETE)', value: 'DELETE'} | ||
const path = require('path'); | ||
const generator = require('yeoman-generator'); | ||
const pluralize = require('pluralize'); | ||
const { camelCase, findLastIndex, findIndex, upperFirst, lowerCase } = require('lodash'); | ||
const recast = require('recast'); | ||
const reservedWords = require('reserved-words'); | ||
|
||
module.exports = class extends generator { | ||
|
||
prompting () { | ||
const srcDir = this.config.get('srcDir') || 'src'; | ||
const apiDir = this.config.get('apiDir') || 'api'; | ||
const authMethods = this.config.get('authMethods') || []; | ||
|
||
const methods = [ | ||
{ name: 'Create (POST)', value: 'POST' }, | ||
{ name: 'Retrieve list (GET)', value: 'GET LIST' }, | ||
{ name: 'Retrieve one (GET)', value: 'GET ONE' }, | ||
{ name: 'Update (PUT)', value: 'PUT' }, | ||
{ name: 'Delete (DELETE)', value: 'DELETE' } | ||
]; | ||
|
||
var getSelectedMethods = function (props) { | ||
return methods.filter(function (method) { | ||
const getSelectedMethods = (props) => { | ||
return methods.filter((method) => { | ||
return props.methods.indexOf(method.value) !== -1; | ||
}); | ||
}; | ||
|
||
var prompts = [{ | ||
const prompts = [{ | ||
type: 'input', | ||
name: 'kebab', | ||
message: 'What\'s the API name?', | ||
default: 'some-entity' | ||
}, { | ||
}, | ||
{ | ||
type: 'input', | ||
name: 'lowerSuffix', | ||
message: 'Name is a reserved word, add suffix for lowercase identifier', | ||
default: 'Obj', | ||
when: function (props) { | ||
return reservedWords.check(_.lowerCase(props.kebab), 6); | ||
when (props) { | ||
return reservedWords.check(lowerCase(props.kebab), 6); | ||
} | ||
}, { | ||
}, | ||
{ | ||
type: 'input', | ||
name: 'kebabs', | ||
message: 'What\'s the endpoint name?', | ||
default: function (props) { | ||
default (props) { | ||
return pluralize(props.kebab); | ||
} | ||
}, { | ||
}, | ||
{ | ||
type: 'input', | ||
name: 'dir', | ||
message: 'Where to put the code?', | ||
default: srcDir + '/' + apiDir | ||
}, { | ||
}, | ||
{ | ||
type: 'checkbox', | ||
name: 'methods', | ||
message: 'Which methods it will have?', | ||
default: methods.map(function (method) { | ||
default: methods.map((method) => { | ||
return method.value; | ||
}), | ||
choices: methods.map(function (method) { | ||
return _.assign({}, method, {checked: true}); | ||
choices: methods.map((method) => { | ||
return Object.assign({}, method, {checked: true}); | ||
}) | ||
}, { | ||
}, | ||
{ | ||
type: 'checkbox', | ||
name: 'masterMethods', | ||
message: 'Which methods are protected by the master key?', | ||
choices: getSelectedMethods, | ||
default: [], | ||
when: function () { | ||
when () { | ||
return authMethods.length; | ||
} | ||
}, { | ||
}, | ||
{ | ||
type: 'checkbox', | ||
name: 'adminMethods', | ||
message: 'Which methods are only accessible by authenticated admin users?', | ||
default: [], | ||
choices: function (props) { | ||
var choices = getSelectedMethods(props); | ||
return choices.map(function (choice) { | ||
choices (props) { | ||
const choices = getSelectedMethods(props); | ||
return choices.map((choice) => { | ||
if (props.masterMethods.indexOf(choice.value) !== -1) { | ||
return _.assign({}, choice, {disabled: 'Accessible only with master key'}); | ||
return Object.assign({}, choice, {disabled: 'Accessible only with master key'}); | ||
} | ||
return choice; | ||
}); | ||
}, | ||
when: function () { | ||
when () { | ||
return authMethods.length; | ||
} | ||
}, { | ||
}, | ||
{ | ||
type: 'checkbox', | ||
name: 'userMethods', | ||
message: 'Which methods are only accessible by authenticated users?', | ||
default: [], | ||
choices: function (props) { | ||
var choices = getSelectedMethods(props); | ||
return choices.map(function (choice) { | ||
choices (props) { | ||
|
||
const choices = getSelectedMethods(props); | ||
|
||
return choices.map((choice) => { | ||
|
||
if (props.masterMethods.indexOf(choice.value) !== -1) { | ||
return _.assign({}, choice, {disabled: 'Accessible only with master key'}); | ||
} else if (props.adminMethods.indexOf(choice.value) !== -1) { | ||
return _.assign({}, choice, {disabled: 'Accessible only by admin users'}); | ||
return Object.assign({}, choice, {disabled: 'Accessible only with master key'}); | ||
} | ||
else if (props.adminMethods.indexOf(choice.value) !== -1) { | ||
return Object.assign({}, choice, {disabled: 'Accessible only by admin users'}); | ||
} | ||
|
||
return choice; | ||
|
||
}); | ||
}, | ||
when: function () { | ||
when () { | ||
return authMethods.length; | ||
} | ||
}, { | ||
}, | ||
{ | ||
type: 'confirm', | ||
name: 'generateModel', | ||
message: 'Do you want to generate a model?', | ||
default: true | ||
}, { | ||
}, | ||
{ | ||
type: 'input', | ||
name: 'modelFields', | ||
message: 'Which fields the model will have? (comma separated, do not include id)', | ||
when: function (props) { | ||
when (props) { | ||
return props.generateModel; | ||
} | ||
}, { | ||
}, | ||
{ | ||
type: 'confirm', | ||
name: 'storeUser', | ||
default: true, | ||
message: function (props) { | ||
message (props) { | ||
return 'Do you want to store in a field the user who created the ' + props.kebab + '?'; | ||
}, | ||
when: function (props) { | ||
when (props) { | ||
return props.generateModel && props.userMethods && props.userMethods.indexOf('POST') !== -1; | ||
} | ||
}, { | ||
}, | ||
{ | ||
type: 'input', | ||
name: 'userField', | ||
message: 'What\'s the name of the field which will store the user?', | ||
default: 'user', | ||
when: function (props) { | ||
when (props) { | ||
return props.storeUser; | ||
} | ||
}, { | ||
}, | ||
{ | ||
type: 'confirm', | ||
name: 'getList', | ||
message: 'Do you want the retrieve methods (GET) to have the form { rows, count } ?', | ||
default: false, | ||
when: function (props) { | ||
var methods = getSelectedMethods(props); | ||
return methods.find(function (method) { | ||
when (props) { | ||
const methods = getSelectedMethods(props); | ||
|
||
return methods.find((method) => { | ||
return method.value === 'GET LIST'; | ||
}) && props.generateModel; | ||
} | ||
}]; | ||
|
||
return this.prompt(prompts).then(function (props) { | ||
return this.prompt(prompts).then((props) => { | ||
this.props = props; | ||
this.props.camel = _.camelCase(this.props.kebab); | ||
this.props.camels = pluralize(this.props.camel); | ||
this.props.pascal = _.upperFirst(this.props.camel); | ||
this.props.pascals = _.upperFirst(this.props.camels); | ||
this.props.lower = _.lowerCase(this.props.camel); | ||
this.props.lowers = _.lowerCase(this.props.camels); | ||
this.props.start = _.upperFirst(this.props.lower); | ||
this.props.starts = _.upperFirst(this.props.lowers); | ||
this.props.camel = camelCase(this.props.kebab); | ||
this.props.camels = pluralize(this.props.camel); | ||
this.props.pascal = upperFirst(this.props.camel); | ||
this.props.pascals = upperFirst(this.props.camels); | ||
this.props.lower = lowerCase(this.props.camel); | ||
this.props.lowers = lowerCase(this.props.camels); | ||
this.props.start = upperFirst(this.props.lower); | ||
this.props.starts = upperFirst(this.props.lowers); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does that pass eslint? I don't think we need those alignments. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can create our custom rules... feel free to modify it. |
||
|
||
// append suffix so we don't get reserved word clashes | ||
if (this.props.lowerSuffix) { | ||
this.props.camel = _.lowerCase(this.props.camel) + this.props.lowerSuffix; | ||
this.props.camel = lowerCase(this.props.camel) + this.props.lowerSuffix; | ||
} | ||
|
||
this.props.authMethods = authMethods; | ||
|
@@ -171,26 +190,26 @@ module.exports = yeoman.Base.extend({ | |
|
||
this.props.modelFields = this.props.modelFields || ''; | ||
this.props.modelFields = this.props.modelFields ? | ||
this.props.modelFields.split(',').map(function (field) { | ||
return field.trim(); | ||
}) : []; | ||
this.props.modelFields.split(',').map((field) => { | ||
return field.trim(); | ||
}) : []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shorten: |
||
|
||
this.props.getList = props.getList || false; | ||
this.props.storeUser = this.props.storeUser || false; | ||
|
||
if (props.userField && this.props.modelFields.indexOf(props.userField) !== -1) { | ||
this.props.modelFields.splice(this.props.modelFields.indexOf(props.userField), 1); | ||
} | ||
}.bind(this)); | ||
}, | ||
|
||
writing: function () { | ||
var props = this.props; | ||
var routesFile = path.join(props.dir, 'index.js'); | ||
var copyTpl = this.fs.copyTpl.bind(this.fs); | ||
var tPath = this.templatePath.bind(this); | ||
var dPath = this.destinationPath.bind(this); | ||
var filepath = function (filename) { | ||
}); | ||
} | ||
writing () { | ||
|
||
const props = this.props; | ||
const routesFile = path.join(props.dir, 'index.js'); | ||
const copyTpl = this.fs.copyTpl.bind(this.fs); | ||
const tPath = this.templatePath.bind(this); | ||
const dPath = this.destinationPath.bind(this); | ||
const filepath = (filename) => { | ||
return path.join(props.dir, props.kebab, filename); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can make it shorter too: |
||
|
||
|
@@ -204,43 +223,51 @@ module.exports = yeoman.Base.extend({ | |
} | ||
|
||
if (this.fs.exists(routesFile)) { | ||
var ast = recast.parse(this.fs.read(routesFile)); | ||
var body = ast.program.body; | ||
var lastImportIndex = _.findLastIndex(body, function (statement) { | ||
|
||
const ast = recast.parse(this.fs.read(routesFile)); | ||
const body = ast.program.body; | ||
const lastImportIndex = findLastIndex(body, (statement) => { | ||
return statement.type === 'ImportDeclaration'; | ||
}); | ||
var actualImportCode = recast.print(body[lastImportIndex]).code; | ||
var importString = ['import ', props.camel, ' from \'./', props.kebab, '\''].join(''); | ||
const actualImportCode = recast.print(body[lastImportIndex]).code; | ||
const importString = ['import ', props.camel, ' from \'./', props.kebab, '\''].join(''); | ||
|
||
body.splice(lastImportIndex, 1, importString); | ||
body.splice(lastImportIndex, 0, actualImportCode); | ||
|
||
var middlewareString = [ | ||
'router.use(\'/', props.kebabs, '\', ', props.camel, ')' | ||
].join(''); | ||
var lastMiddlewareIndex = _.findLastIndex(body, function (statement) { | ||
const middlewareString = ['router.use(\'/', props.kebabs, '\', ', props.camel, ')'].join(''); | ||
|
||
const lastMiddlewareIndex = findLastIndex(body, function (statement) { | ||
if (!statement.expression || !statement.expression.callee) { | ||
return false; | ||
} | ||
var callee = statement.expression.callee; | ||
|
||
const callee = statement.expression.callee; | ||
return callee.object.name === 'router' && callee.property.name === 'use'; | ||
|
||
}); | ||
|
||
if (lastMiddlewareIndex === -1) { | ||
var exportRouterIndex = _.findIndex(body, function (statement) { | ||
|
||
const exportRouterIndex = findIndex(body, function (statement) { | ||
return statement.type === 'ExportDefaultDeclaration'; | ||
}); | ||
|
||
body.splice(exportRouterIndex, 0, middlewareString); | ||
} else { | ||
var actualMiddlewareCode = recast.print(body[lastMiddlewareIndex]).code; | ||
} | ||
else { | ||
|
||
const actualMiddlewareCode = recast.print(body[lastMiddlewareIndex]).code; | ||
|
||
body.splice(lastMiddlewareIndex, 1, middlewareString); | ||
body.splice(lastMiddlewareIndex, 0, actualMiddlewareCode); | ||
} | ||
|
||
this.fs.write(routesFile, recast.print(ast).code); | ||
} | ||
}, | ||
} | ||
|
||
install: function () { | ||
install () { | ||
|
||
} | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since
generator
is a class, let's make it capitalized:Generator