Created Structure Module

The structure module is a prompt-only module that determines and
sets structural attributes which inform the rest of the project.
With these values, we can automatically generate test configuration
files, sample project layouts, as well as toggle specific tooling
options (such as gulp, karma, and others).

Change-Id: I477f427da121aeda967fadbcbe73d0a75e9d6bed
This commit is contained in:
Michael Krotscheck 2016-06-10 11:06:04 -07:00
parent 57c560e7d5
commit f8c932ea8d
6 changed files with 337 additions and 1 deletions

View File

@ -1,3 +1,4 @@
./dist
.npm
cover
node_modules

3
.gitignore vendored
View File

@ -2,6 +2,7 @@
*.log
*.pid
*.seed
./dist
.DS_Store
.idea
.node_repl_history
@ -16,4 +17,4 @@ npm-debug.log
package
pids
reports
www
www

9
.yo-rc.json Normal file
View File

@ -0,0 +1,9 @@
{
"generator-openstack": {
"srcDir": "./generators",
"distDir": "./dist",
"testDir": "./spec",
"engine": "node",
"language": "es5"
}
}

View File

@ -9,6 +9,7 @@
var gerrit = require('./lib/component/gerrit');
var editorconfig = require('./lib/component/editorconfig');
var license = require('./lib/component/license');
var structure = require('./lib/component/structure');
var eslint = require('./lib/component/eslint');
var gitignore = require('./lib/component/gitignore');
var nsp = require('./lib/component/nsp');
@ -34,6 +35,7 @@
.then(gerrit.init) // Gerrit
.then(editorconfig.init) // Editorconfig
.then(license.init) // Licensing
.then(structure.init) // Project Structure
.then(eslint.init) // Linting
.then(gitignore.init) // Gitignore
.then(nsp.init) // NSP
@ -51,6 +53,7 @@
.then(gerrit.prompt) // Gerrit
.then(editorconfig.prompt) // Editorconfig
.then(license.prompt) // Licensing
.then(structure.prompt) // Project Structure
.then(eslint.prompt) // Linting
.then(gitignore.prompt) // Gitignore
.then(nsp.prompt) // NSP
@ -68,6 +71,7 @@
.then(gerrit.configure) // Gerrit
.then(editorconfig.configure) // Editorconfig
.then(license.configure) // Licensing
.then(structure.configure) // Project Structure
.then(eslint.configure) // Linting
.then(gitignore.configure) // Gitignore
.then(nsp.configure) // NSP

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* This generator module handles questions regarding the project's structure,
* such as engine, common output directories, and language level. It informs
* other generators, such as test framework generation, packaging tools,
* and/or configuration files.
*/
(function () {
'use strict';
var Q = require('q');
var projectBuilder = require('../project_builder');
/**
* Initialize the component by setting configuration defaults. These, or previously set
* versions, will be accessible immediately, however it's good practice not to access them
* until after the prompting phase, as we cannot guarantee that they will be properly set.
*
* @param {generator} generator The currently active generator.
* @returns {generator} The passed generator, for promise chaining.
*/
function initialize (generator) {
// Set our defaults:
generator.config.defaults({
engine: 'browser',
language: 'es5',
srcDir: './src',
distDir: './dist',
testDir: './test'
});
return generator;
}
/**
* If applicable, prompt the user for a project type.
*
* @param {generator} generator The currently active generator.
* @returns {generator} The passed generator, for promise chaining.
*/
function prompt (generator) {
var deferred = Q.defer();
// We default to a node.js project.
if (!generator.options['non-interactive']) {
// Go through the prompts.
generator.prompt(
[{
type: 'list',
name: 'engine',
message: 'Structure- Runtime Engine:',
choices: [
{
name: 'Browser',
value: 'browser'
},
{
name: 'Node.js',
value: 'node'
}
],
default: generator.config.get('engine')
}, {
type: 'list',
name: 'language',
message: 'Structure- Language:',
choices: [
{
name: 'ECMAScript 5',
value: 'es5'
},
{
name: 'ECMAScript 6',
value: 'es6'
}
],
default: generator.config.get('language')
}, {
type: 'input',
name: 'srcDir',
message: 'Structure- Source Directory:',
default: generator.config.get('srcDir')
}, {
type: 'input',
name: 'testDir',
message: 'Structure- Test Directory:',
default: generator.config.get('testDir')
}, {
type: 'input',
name: 'distDir',
message: 'Structure- Dist Directory:',
default: generator.config.get('distDir'),
when: function (answers) {
return answers.engine === 'browser';
}
}],
function (answers) {
generator.config.set(answers);
deferred.resolve(generator);
});
} else {
deferred.resolve(generator);
}
return deferred.promise;
}
/**
* Add any output directories to the ignore files.
*
* @param {generator} generator The currently active generator.
* @returns {generator} The passed generator, for promise chaining.
*/
function configure (generator) {
projectBuilder.ignoreFile(generator.config.get('distDir'));
return generator;
}
module.exports = {
init: initialize,
prompt: prompt,
configure: configure
};
})();

View File

@ -0,0 +1,182 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company, LP
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
(function () {
'use strict';
var libDir = '../../../../generators/app/lib';
var structure = require(libDir + '/component/structure');
var projectBuilder = require(libDir + '/project_builder');
var mocks = require('../../../helpers/mocks');
var mockGenerator;
var expectedDefaults = {
engine: 'browser',
language: 'es5',
srcDir: './src',
distDir: './dist',
testDir: './test'
};
describe('generator-openstack:lib/component/structure', function () {
beforeEach(function () {
mockGenerator = mocks.buildGenerator();
jasmine.clock().install();
});
afterEach(function () {
jasmine.clock().uninstall();
});
it('should define init, prompt, and configure',
function () {
expect(typeof structure.init).toBe('function');
expect(typeof structure.prompt).toBe('function');
expect(typeof structure.configure).toBe('function');
});
describe('init()', function () {
it('should return a generator',
function () {
var outputGenerator = structure.init(mockGenerator);
expect(outputGenerator).toEqual(mockGenerator);
});
it('should set configuration defaults',
function () {
var spy = spyOn(mockGenerator.config, 'defaults');
structure.init(mockGenerator);
expect(spy).toHaveBeenCalledWith(expectedDefaults);
});
});
describe('prompt()', function () {
it('should return a promise that resolves with a generator',
function () {
var generator = mocks.buildGenerator();
var outputPromise = structure.prompt(generator);
outputPromise.then(function (outputGenerator) {
expect(outputGenerator).toEqual(generator);
});
});
it('should revert to config defaults if no answers provided',
function () {
var config = {};
var mockAnswers = {};
var generator = mocks.buildGenerator(config, mockAnswers);
// Call the component
structure.init(generator);
structure.prompt(generator);
structure.configure(generator);
Object.keys(expectedDefaults).forEach(function (key) {
expect(generator.config.get(key)).toEqual(expectedDefaults[key]);
});
});
it('should not show a prompt if non-interactive is set',
function () {
var generator = mocks.buildGenerator(null, null, {'non-interactive': true});
var promptSpy = spyOn(generator, 'prompt');
structure.init(generator);
structure.prompt(generator);
expect(promptSpy.calls.any()).toBeFalsy();
});
it('should configure answers if answers provided',
function () {
var config = {};
var mockAnswers = {
language: 'es6',
srcDir: './dir',
distDir: './foo',
testDir: './bar'
};
var generator = mocks.buildGenerator(config, mockAnswers);
// Set defaults
structure.init(generator);
structure.prompt(generator);
structure.configure(generator);
Object.keys(mockAnswers).forEach(function (key) {
expect(generator.config.get(key)).toEqual(mockAnswers[key]);
});
});
it('should not configure the dist directory for a node project',
function () {
var config = {};
var mockAnswers = {
engine: 'node',
distDir: './foo' // This answer should never be read.
};
var generator = mocks.buildGenerator(config, mockAnswers);
// Set defaults
structure.init(generator);
structure.prompt(generator);
structure.configure(generator);
expect(generator.config.get('distDir')).not.toBe(mockAnswers.distDir);
});
it('should configure the dist directory for a browser project',
function () {
var config = {};
var mockAnswers = {
engine: 'browser',
distDir: './foo' // This answer should never be read.
};
var generator = mocks.buildGenerator(config, mockAnswers);
// Set defaults
structure.init(generator);
structure.prompt(generator);
structure.configure(generator);
expect(generator.config.get('distDir')).toBe(mockAnswers.distDir);
});
});
describe('configure()', function () {
it('should return a generator',
function () {
var outputGenerator = structure.configure(mockGenerator);
expect(outputGenerator).toEqual(mockGenerator);
});
it('should add the dist directory to the ignoreFile.',
function () {
var ignoreSpy = spyOn(projectBuilder, 'ignoreFile');
var generator = mocks.buildGenerator();
structure.init(generator);
structure.prompt(generator);
structure.configure(generator);
expect(ignoreSpy).toHaveBeenCalledWith('./dist');
});
});
});
})();