Eslint support
Eslint configuration and ignore file are now generated, with simple writeback support.
This commit is contained in:
@@ -1,3 +1,2 @@
|
||||
coverage
|
||||
node_modules
|
||||
|
||||
node_modules
|
||||
@@ -1,4 +1,4 @@
|
||||
(function () {
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var yeoman = require('yeoman-generator');
|
||||
@@ -7,17 +7,18 @@
|
||||
var gerrit = require('./lib/component/gerrit');
|
||||
var editorconfig = require('./lib/component/editorconfig');
|
||||
var license = require('./lib/component/license');
|
||||
var eslint = require('./lib/component/eslint');
|
||||
|
||||
module.exports = yeoman.generators.Base.extend({
|
||||
|
||||
constructor: function () {
|
||||
constructor: function() {
|
||||
yeoman.generators.Base.apply(this, arguments);
|
||||
|
||||
// Add support for a `--non-interactive` flag
|
||||
this.option('non-interactive');
|
||||
},
|
||||
|
||||
initializing: function () {
|
||||
initializing: function() {
|
||||
// Set our own defaults.
|
||||
this.config.defaults({
|
||||
projectName: this.appname
|
||||
@@ -27,41 +28,51 @@
|
||||
gerrit.init(this); // Gerrit
|
||||
editorconfig.init(this); // Editorconfig
|
||||
license.init(this); // Licensing
|
||||
eslint.init(this); // Linting
|
||||
},
|
||||
|
||||
prompting: function () {
|
||||
prompting: function() {
|
||||
if (!this.options['non-interactive']) {
|
||||
// Prompt components.
|
||||
gerrit.prompt(this); // Gerrit
|
||||
editorconfig.prompt(this); // Editorconfig
|
||||
license.prompt(this); // Licensing
|
||||
eslint.prompt(this); // Linting
|
||||
}
|
||||
},
|
||||
|
||||
configuring: function () {
|
||||
configuring: function() {
|
||||
// Configure components.
|
||||
gerrit.configure(this); // Gerrit
|
||||
editorconfig.configure(this); // Editorconfig
|
||||
license.configure(this); // Licensing
|
||||
eslint.configure(this); // Linting
|
||||
},
|
||||
|
||||
writing: function () {
|
||||
writing: function() {
|
||||
var self = this;
|
||||
var config = self.config.getAll();
|
||||
var included = projectBuilder.getIncludedFiles();
|
||||
var excluded = projectBuilder.getExcludedFiles();
|
||||
|
||||
// Write out all files included in the project builder.
|
||||
included.forEach(function (fileRef) {
|
||||
self.fs.copyTpl(
|
||||
self.templatePath(fileRef.from),
|
||||
self.destinationPath(fileRef.to),
|
||||
config
|
||||
);
|
||||
included.forEach(function(fileRef) {
|
||||
if (fileRef.hasOwnProperty('content')) {
|
||||
var content = typeof fileRef.content === 'function'
|
||||
? "" + fileRef.content()
|
||||
: "" + fileRef.content;
|
||||
self.fs.write(fileRef.to, content);
|
||||
} else {
|
||||
self.fs.copyTpl(
|
||||
self.templatePath(fileRef.from),
|
||||
self.destinationPath(fileRef.to),
|
||||
config
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Delete all files explicitly excluded in the project builder.
|
||||
excluded.forEach(function (path) {
|
||||
excluded.forEach(function(path) {
|
||||
self.fs.delete(self.destinationPath(path));
|
||||
});
|
||||
}
|
||||
|
||||
84
generators/app/lib/component/eslint.js
Normal file
84
generators/app/lib/component/eslint.js
Normal file
@@ -0,0 +1,84 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var projectBuilder = require('../project_builder');
|
||||
var yaml = require('js-yaml');
|
||||
|
||||
var excludedPaths = [];
|
||||
var ignoreFile = '.eslintignore';
|
||||
var rcFile = '.eslintrc';
|
||||
var eslintrc = {extends: 'openstack'};
|
||||
|
||||
/**
|
||||
* No-op placeholder method, for handlers we don't need.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function noop () {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the existing .eslintrc and .eslintignore files, and populate our initial configuration
|
||||
* with them.
|
||||
*
|
||||
* @param {*} generator The currently active yeoman generator.
|
||||
* @returns {void}
|
||||
*/
|
||||
function initializeEslint (generator) {
|
||||
var fs = generator.fs;
|
||||
|
||||
// Read .eslintignore.
|
||||
if (fs.exists(ignoreFile)) {
|
||||
excludedPaths = fs.read(ignoreFile)
|
||||
.split('\n')
|
||||
.filter(function(item) {
|
||||
// Remove empty lines.
|
||||
return item.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
// Read .eslintrc
|
||||
if (fs.exists(rcFile)) {
|
||||
eslintrc = yaml.safeLoad(fs.read(rcFile));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the project by adding required files.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function configureEslint () {
|
||||
if (buildEslintIgnore().length === 0) {
|
||||
projectBuilder.removeFile('.eslintignore');
|
||||
} else {
|
||||
projectBuilder.writeFile('.eslintignore', buildEslintIgnore);
|
||||
}
|
||||
projectBuilder.writeFile('.eslintrc', buildEslintRc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the content of our .eslintignore file from the configured list of excluded paths.
|
||||
*
|
||||
* @returns {string} The content of the .eslintignore file.
|
||||
*/
|
||||
function buildEslintIgnore () {
|
||||
return excludedPaths.sort().join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the content of our .eslintrc file from the current configuration.
|
||||
*
|
||||
* @returns {string} The content of the .eslintrc file.
|
||||
*/
|
||||
function buildEslintRc () {
|
||||
return yaml.safeDump(eslintrc);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: initializeEslint,
|
||||
prompt: noop,
|
||||
configure: configureEslint
|
||||
};
|
||||
})();
|
||||
@@ -25,6 +25,17 @@
|
||||
includedFiles.push({from: sourcePath, to: destinationPath || sourcePath});
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a file to the project.
|
||||
*
|
||||
* @param {String} destinationPath The destination for the file.
|
||||
* @param {String|Function} content A string of content, or method that returns one.
|
||||
* @returns {void}
|
||||
*/
|
||||
function writeFile (destinationPath, content) {
|
||||
includedFiles.push({to: destinationPath, content: content});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all files that are to be included.
|
||||
*
|
||||
@@ -55,6 +66,7 @@
|
||||
|
||||
module.exports = {
|
||||
addFile: addFile,
|
||||
writeFile: writeFile,
|
||||
removeFile: removeFile,
|
||||
getIncludedFiles: getIncludedFiles,
|
||||
getExcludedFiles: getExcludedFiles,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"yo": "^1.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"js-yaml": "^3.5.5",
|
||||
"yeoman-generator": "^0.21.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(function () {
|
||||
(function() {
|
||||
'use strict';
|
||||
var path = require('path');
|
||||
var assert = require('yeoman-assert');
|
||||
@@ -8,16 +8,16 @@
|
||||
var modules = ['gerrit', 'license', 'editorconfig'];
|
||||
var projectBuilder = require('../../generators/app/lib/project_builder');
|
||||
|
||||
describe('generator-openstack:app', function () {
|
||||
describe('generator-openstack:app', function() {
|
||||
|
||||
beforeEach(function () {
|
||||
beforeEach(function() {
|
||||
projectBuilder.clear();
|
||||
});
|
||||
|
||||
it('should call all module lifecycle prompts',
|
||||
function (done) {
|
||||
function(done) {
|
||||
var spies = [];
|
||||
modules.forEach(function (name) {
|
||||
modules.forEach(function(name) {
|
||||
var module = require('../../generators/app/lib/component/' + name);
|
||||
spies.push(spyOn(module, 'init').and.callThrough());
|
||||
spies.push(spyOn(module, 'prompt').and.callThrough());
|
||||
@@ -25,8 +25,8 @@
|
||||
});
|
||||
|
||||
helpers.run(generator)
|
||||
.on('end', function () {
|
||||
spies.forEach(function (spy) {
|
||||
.on('end', function() {
|
||||
spies.forEach(function(spy) {
|
||||
expect(spy.calls.any()).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -35,10 +35,10 @@
|
||||
});
|
||||
|
||||
it('should call no module prompts with the --non-interactive flag',
|
||||
function (done) {
|
||||
function(done) {
|
||||
var promptSpies = [];
|
||||
var regularSpies = [];
|
||||
modules.forEach(function (name) {
|
||||
modules.forEach(function(name) {
|
||||
var module = require('../../generators/app/lib/component/' + name);
|
||||
regularSpies.push(spyOn(module, 'init').and.callThrough());
|
||||
promptSpies.push(spyOn(module, 'prompt').and.callThrough());
|
||||
@@ -47,11 +47,11 @@
|
||||
|
||||
helpers.run(generator)
|
||||
.withOptions({'non-interactive': true})
|
||||
.on('end', function () {
|
||||
promptSpies.forEach(function (spy) {
|
||||
.on('end', function() {
|
||||
promptSpies.forEach(function(spy) {
|
||||
expect(spy.calls.any()).toBeFalsy();
|
||||
});
|
||||
regularSpies.forEach(function (spy) {
|
||||
regularSpies.forEach(function(spy) {
|
||||
expect(spy.calls.any()).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -59,24 +59,48 @@
|
||||
});
|
||||
});
|
||||
|
||||
it('should create all files created in the project builder',
|
||||
function (done) {
|
||||
helpers.run(generator)
|
||||
.on('end', function () {
|
||||
assert.file(['.gitreview']); // We'll just use a file we know about.
|
||||
done();
|
||||
});
|
||||
});
|
||||
describe('writing()', function() {
|
||||
it('should create all files created in the project builder',
|
||||
function(done) {
|
||||
helpers.run(generator)
|
||||
.on('end', function() {
|
||||
assert.file(['.gitreview']); // We'll just use a file we know about.
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete all files flagged in the project builder',
|
||||
function (done) {
|
||||
projectBuilder.removeFile('test.json');
|
||||
|
||||
helpers.run(generator)
|
||||
.on('end', function () {
|
||||
assert.noFile(['test.json']);
|
||||
done();
|
||||
it('should write any files provided to the content builder',
|
||||
function(done) {
|
||||
projectBuilder.writeFile('test.json', function() {
|
||||
return 'foo';
|
||||
});
|
||||
});
|
||||
projectBuilder.writeFile('test_null.json', function() {
|
||||
// do nothing.
|
||||
});
|
||||
projectBuilder.writeFile('test_empty.json', function() {
|
||||
return '';
|
||||
});
|
||||
projectBuilder.writeFile('test_static.json', 'static_content');
|
||||
projectBuilder.writeFile('test_undefined.json');
|
||||
|
||||
helpers.run(generator)
|
||||
.on('end', function() {
|
||||
assert.file(['test.json', 'test_static.json','test_empty.json', 'test_null.json',
|
||||
'test_undefined.json']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete all files flagged in the project builder',
|
||||
function(done) {
|
||||
projectBuilder.removeFile('test.json');
|
||||
|
||||
helpers.run(generator)
|
||||
.on('end', function() {
|
||||
assert.noFile(['test.json']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
168
spec/app/lib/component/eslint.js
Normal file
168
spec/app/lib/component/eslint.js
Normal file
@@ -0,0 +1,168 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
var libDir = '../../../../generators/app/lib';
|
||||
var mockGenerator;
|
||||
var mockEslintIgnore = ['node_modules', 'bower_components', 'dist'];
|
||||
|
||||
var eslint = require(libDir + '/component/eslint');
|
||||
var projectBuilder = require(libDir + '/project_builder');
|
||||
var mocks = require('../../../helpers/mocks');
|
||||
var yaml = require('js-yaml');
|
||||
|
||||
describe('generator-openstack:lib/component/eslint', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
mockGenerator = mocks.buildGenerator();
|
||||
mockGenerator.fs.write('.eslintignore', mockEslintIgnore.join('\n'));
|
||||
projectBuilder.clear();
|
||||
});
|
||||
|
||||
it('should define init, prompt, and configure',
|
||||
function() {
|
||||
expect(typeof eslint.init).toBe('function');
|
||||
expect(typeof eslint.prompt).toBe('function');
|
||||
expect(typeof eslint.configure).toBe('function');
|
||||
});
|
||||
|
||||
describe('init()', function() {
|
||||
it('should not interact with config',
|
||||
function() {
|
||||
var spy = spyOn(mockGenerator.config, 'defaults');
|
||||
eslint.init(mockGenerator);
|
||||
expect(spy.calls.any()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('prompt()', function() {
|
||||
it('should do nothing',
|
||||
function() {
|
||||
var spy = spyOn(mockGenerator, 'prompt');
|
||||
eslint.prompt(mockGenerator);
|
||||
expect(spy.calls.any()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('configure()', function() {
|
||||
it('should add .eslintrc and .eslintignore to the project files.',
|
||||
function() {
|
||||
eslint.configure(mockGenerator);
|
||||
|
||||
var files = projectBuilder.getIncludedFiles();
|
||||
expect(files.length).toBe(2);
|
||||
expect(files[0].to).toBe('.eslintignore');
|
||||
expect(files[1].to).toBe('.eslintrc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.eslintrc management', function() {
|
||||
var mockEslintRc = {
|
||||
extends: 'openstack',
|
||||
plugins: ['angular']
|
||||
};
|
||||
|
||||
it('should write a .eslintrc file as valid .yaml',
|
||||
function() {
|
||||
eslint.init(mockGenerator);
|
||||
eslint.configure(mockGenerator);
|
||||
|
||||
var files = projectBuilder.getIncludedFiles();
|
||||
var eslintRcRef = files[1];
|
||||
expect(eslintRcRef.to).toBe('.eslintrc');
|
||||
expect(yaml.safeLoad(eslintRcRef.content()))
|
||||
.toEqual({extends: 'openstack'});
|
||||
});
|
||||
|
||||
it('should echo back existing .eslintrc',
|
||||
function() {
|
||||
var yamlContent = yaml.safeDump(mockEslintRc);
|
||||
mockGenerator.fs.write('.eslintrc', yamlContent);
|
||||
|
||||
eslint.init(mockGenerator);
|
||||
eslint.configure(mockGenerator);
|
||||
|
||||
var files = projectBuilder.getIncludedFiles();
|
||||
var eslintRcRef = files[1];
|
||||
var eslintContent = yaml.safeLoad(eslintRcRef.content());
|
||||
expect(mockEslintRc).toEqual(eslintContent);
|
||||
});
|
||||
|
||||
it('should convert a json .eslintrc to yaml',
|
||||
function() {
|
||||
mockGenerator.fs.write('.eslintrc', JSON.stringify(mockEslintRc));
|
||||
|
||||
eslint.init(mockGenerator);
|
||||
eslint.configure(mockGenerator);
|
||||
|
||||
var files = projectBuilder.getIncludedFiles();
|
||||
var eslintRcRef = files[1];
|
||||
var eslintContent = yaml.safeLoad(eslintRcRef.content());
|
||||
expect(mockEslintRc).toEqual(eslintContent);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.eslintignore management', function() {
|
||||
|
||||
it('should echo back existing .eslintignore',
|
||||
function() {
|
||||
mockGenerator.fs.write('.eslintignore', mockEslintIgnore.join('\n'));
|
||||
|
||||
eslint.init(mockGenerator);
|
||||
eslint.configure(mockGenerator);
|
||||
|
||||
var files = projectBuilder.getIncludedFiles();
|
||||
var ignoreRef = files[0];
|
||||
var ignoreContent = ignoreRef.content().split('\n');
|
||||
expect(ignoreContent.length).toBe(mockEslintIgnore.length);
|
||||
|
||||
ignoreContent.forEach(function(item) {
|
||||
expect(mockEslintIgnore.indexOf(item)).not.toBe(-1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort the ignored files.',
|
||||
function() {
|
||||
mockGenerator.fs.write('.eslintignore', mockEslintIgnore.join('\n'));
|
||||
|
||||
eslint.init(mockGenerator);
|
||||
eslint.configure(mockGenerator);
|
||||
|
||||
var files = projectBuilder.getIncludedFiles();
|
||||
var ignoreRef = files[0];
|
||||
var ignoreContent = ignoreRef.content().split('\n');
|
||||
expect(ignoreContent[0]).toBe('bower_components');
|
||||
expect(ignoreContent[1]).toBe('dist');
|
||||
expect(ignoreContent[2]).toBe('node_modules');
|
||||
});
|
||||
|
||||
it('should remove any whitespace from the existing .eslintignore',
|
||||
function() {
|
||||
mockGenerator.fs.write('.eslintignore', ['1_one', '', '2_two', ''].join('\n'));
|
||||
|
||||
eslint.init(mockGenerator);
|
||||
eslint.configure(mockGenerator);
|
||||
|
||||
var files = projectBuilder.getIncludedFiles();
|
||||
var ignoreRef = files[0];
|
||||
var ignoreContent = ignoreRef.content().split('\n');
|
||||
expect(ignoreContent.length).toBe(2);
|
||||
expect(ignoreContent[0]).toBe('1_one');
|
||||
expect(ignoreContent[1]).toBe('2_two');
|
||||
});
|
||||
|
||||
it('should delete the file if there\'s nothing to ignore', function() {
|
||||
mockGenerator.fs.write('.eslintignore', '');
|
||||
|
||||
eslint.init(mockGenerator);
|
||||
eslint.configure(mockGenerator);
|
||||
|
||||
var files = projectBuilder.getIncludedFiles();
|
||||
expect(files.length).toBe(1);
|
||||
expect(files[0].to).not.toBe('.eslintignore');
|
||||
|
||||
var rmFiles = projectBuilder.getExcludedFiles();
|
||||
expect(rmFiles.length).toBe(1);
|
||||
expect(rmFiles[0]).toBe('.eslintignore');
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1,29 +1,34 @@
|
||||
(function () {
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function buildMockGenerator (config, mockAnswers, mockOptions) {
|
||||
var configDefaults = {};
|
||||
var memFs = require('mem-fs');
|
||||
var editor = require('mem-fs-editor');
|
||||
var store = memFs.create();
|
||||
|
||||
config = config || {};
|
||||
mockAnswers = mockAnswers || {};
|
||||
|
||||
return {
|
||||
fs: editor.create(store),
|
||||
appname: 'generator-openstack',
|
||||
async: function () {
|
||||
return function () {
|
||||
async: function() {
|
||||
return function() {
|
||||
};
|
||||
},
|
||||
config: {
|
||||
defaults: function (values) {
|
||||
Object.keys(values).forEach(function (key) {
|
||||
defaults: function(values) {
|
||||
Object.keys(values).forEach(function(key) {
|
||||
configDefaults[key] = values[key];
|
||||
});
|
||||
},
|
||||
get: function (value) {
|
||||
get: function(value) {
|
||||
return config[value] || configDefaults[value];
|
||||
},
|
||||
set: function (key, value) {
|
||||
set: function(key, value) {
|
||||
if (typeof key === 'object') {
|
||||
Object.keys(key).forEach(function (index) {
|
||||
Object.keys(key).forEach(function(index) {
|
||||
config[index] = key[index];
|
||||
});
|
||||
} else {
|
||||
@@ -31,9 +36,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
prompt: function (params, callback) {
|
||||
prompt: function(params, callback) {
|
||||
var answers = {};
|
||||
params.forEach(function (param) {
|
||||
params.forEach(function(param) {
|
||||
|
||||
if (param.when && !param.when(answers)) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user