ft/aurelia_project/generators/element.js
2017-06-09 09:09:06 +01:00

289 lines
7.3 KiB
JavaScript

import {inject} from 'aurelia-dependency-injection';
import {Project, ProjectItem, CLIOptions, UI} from 'aurelia-cli';
import mkdirp from 'mkdirp';
@inject(Project, CLIOptions, UI)
export default class ElementGenerator {
constructor(project, options, ui) {
this.project = project;
this.options = options;
this.ui = ui;
}
execute() {
return this.ui
.ensureAnswer(this.options.args[0], 'What is the name of the element? (in hyphen format. ft- will be prefixed automatically)')
.then(name => {
let area = 'common/';
let subarea = 'elements/';
let fileName = this.project.makeFileName(name);
let className = this.project.makeClassName(name);
// let funcName = this.project.makeFunctionName(name);
let subDir = (subarea) ? area + '/' + subarea : area;
let relDir = 'components/' + subDir + '/ft-' + fileName;
let dir = 'src/' + relDir;
mkdirp.sync(dir);// quick fix incase parent folder doesn't exist.
this.project.locations.push(this.project[dir] = ProjectItem.directory(dir));
this.project[dir].add(
ProjectItem.text(`ft-${fileName}.js`, this.generateComponentJsSource(className)),
ProjectItem.text(`ft-${fileName}.spec.js`, this.generateComponentTestJsSource(className, fileName, relDir)),
ProjectItem.text(`ft-${fileName}.html`, this.generateComponentHtmlSource(fileName)),
ProjectItem.text(`ft-${fileName}.md`, this.generateComponentMdSource('ft-' + fileName)),
);
return this.project.commitChanges()
.then(() => this.ui.log(`Created ${fileName}.`));
});
}
/**
* source for top level component js
* @param className
* @returns {string}
*/
generateComponentJsSource(className) {
return `import {bindable, inject} from 'aurelia-framework';
@inject(Element)
export class Ft${className} {
@bindable nameThisBindable;
constructor(element) {
this.element = element;
}
callBackFunction() {
this.element.innerHTML = this.nameThisBindable;
}
}`;
}
/**
* source for top level component js unit test
* @param className
* @param fileName
* @returns {string}
*/
generateComponentTestJsSource(className, fileName, relDir) {
return `import {StageComponent} from 'aurelia-testing';
import {LogManager} from 'aurelia-framework';
import {bootstrap} from 'aurelia-bootstrapper';
const logger = LogManager.getLogger('${fileName}');
describe('${className}', () => {
let component;
let mockStore = {
dispatch: function(action) {
expect(action.type).toBeDefined();
logger.info('actions should have a type: ' + action.type);
},
subscribe: function(callback) {
logger.info('subscription request to the store');
}
};
let beans = { // see configuration/en-us-retail/beans.js for example
component: {
${className}: []
},
bean: {
}
};
beforeEach(() => {
component = StageComponent
.withResources('${relDir}/ft-${fileName}')
.inView('<ft-${fileName} id="test1" fund-id="817"></ft-${fileName}>');
component.configure = (aurelia) => {
aurelia.container.registerInstance('Store', mockStore);
aurelia.container.registerInstance('Beans', beans);
aurelia.use.standardConfiguration()
.feature('components/common')
.feature('components/products/common');
};
});
it('should render something', done => {
component.create(bootstrap).then(() => {
const testElement = document.getElementById('test1');
expect(testElement.innerText).toMatch(/replace me/);
done();
});
});
afterEach(() => {
component.dispose();
});
});
`;
}
/**
* source for top level component html
* @returns {string}
*/
generateComponentHtmlSource(fileName) {
return `<template>
<div>ft-${fileName}: replace me</div>
</template>
`;
}
/**
* source for reducer
* @param funcName, fileName
*/
generateReducerJsSource(funcName, fileName) {
return `/**
* Data Reducer for ${funcName}
* Takes site specific json data and creates model for components
* after applying business and presentation logic and data mapping
*/
import {LogManager} from 'aurelia-framework';
const logger = LogManager.getLogger('${fileName}');
export function ${funcName}(state, action) {
switch (action.type) {
case 'CHANGE_ME_TO_PROPER_ACTION_NAME_SUCCESS':
logger.debug('Reducing: CHANGE_ME_TO_PROPER_ACTION_NAME_SUCCESS');
return Object.assign({}, state, {
${funcName}: action.data
});
default:
return state;
}
}
`;
}
/**
* source for reducer unit test
* @param funcName
*/
generateReducerTestJsSource(funcName, fileName) {
return `import {${funcName}} from './${fileName}.reducer';
describe('${funcName}', () => {
it('should return unchanged state if action does not apply', done => {
let action = {
type: 'ANOTHER_ACTION'
};
let oldState = {};
let newState = ${funcName}(oldState, action, {});
expect(newState).toBe(oldState);
done();
});
it('should return some stuff', done => {
let action = {
type: 'CHANGE_ME_TO_PROPER_ACTION_NAME_SUCCESS',
data: {
stuff: 'some stuff'
}
};
let newState = ${funcName}({}, action, {});
expect(newState.${funcName}.stuff).toBe('some stuff');
done();
});
});
`;
}
/**
* source for reducer
* @param funcName, fileName
*/
generateAppStateReducerJsSource(funcName, fileName) {
return `/**
* App State Reducer for ${funcName}
* Takes application state data and creates model for components
*/
import {LogManager} from 'aurelia-framework';
const logger = LogManager.getLogger('${fileName}');
export function ${funcName}(state, action) {
switch (action.type) {
case 'SOME_ACTION':
logger.debug('Reducing: SOME_ACTION');
return Object.assign({}, state, {
${funcName}: action.data
});
default:
return state;
}
}
`;
}
/**
* source for reducer unit test
* @param funcName
*/
generateAppStateReducerTestJsSource(funcName, fileName) {
return `import {${funcName}} from './${fileName}.reducer';
describe('${funcName}', () => {
it('should return unchanged state if action does not apply', done => {
let action = {
type: 'ANOTHER_ACTION'
};
let oldState = {};
let newState = ${funcName}(oldState, action, {});
expect(newState).toBe(oldState);
done();
});
it('should return some stuff', done => {
let action = {
type: 'SOME_ACTION',
data: {
stuff: 'some stuff'
}
};
let newState = ${funcName}({}, action, {});
expect(newState.${funcName}.stuff).toBe('some stuff');
done();
});
});
`;
}
/**
* generate markdown stub
* @param fileName
* @returns {string}
*/
generateComponentMdSource(fileName) {
return `# ${ fileName }
## Usage
${'```'}html
<${ fileName} fund-id="817" cid="uniqueId"></${ fileName}>
${'```'}
*The cid is guaranteed to be unique to this page even if multiple instances of the component are added to the same page.*
## Developer notes
`;
}
/**
* generate sass partial stub
* @param fileName
* @returns {string}
*/
generateComponentSassSource(fileName, name) {
return `
// CSS specific to the ${ fileName } component goes here
[data-fti-component="${ name }"] {
}
`;
}
}