Added new directory options for generated components (styles are now also generated in the components folder)

Added directory options fallbacks for version 3 and below
Prepared for inclusion of css modules
This commit is contained in:
Chris 2016-06-15 15:34:26 +02:00
parent ed4686edd0
commit 892f771899
11 changed files with 171 additions and 91 deletions

View File

@ -54,6 +54,7 @@ class AppGenerator extends Generators.Base {
// Set needed global vars for yo
this.appName = answers.appName;
this.style = answers.style;
this.cssmodules = answers.cssmodules;
this.postcss = answers.postcss;
this.generatedWithVersion = parseInt(packageInfo.version.split('.').shift(), 10);
@ -61,6 +62,7 @@ class AppGenerator extends Generators.Base {
this.config.set('appName', this.appName);
this.config.set('appPath', this.appPath);
this.config.set('style', this.style);
this.config.set('cssmodules', this.cssmodules);
this.config.set('postcss', this.postcss);
this.config.set('generatedWithVersion', this.generatedWithVersion);
});

View File

@ -15,6 +15,12 @@ module.exports = [
choices: utils.config.getChoices('style'),
default: utils.config.getDefaultChoice('style')
},
{
type: 'confirm',
name: 'cssmodules',
message: 'Enable css module support? See https://github.com/gajus/react-css-modules for further info',
default: true
},
{
type: 'confirm',
name: 'postcss',

View File

@ -16,34 +16,34 @@ class ComponentGenerator extends Generators.Base {
writing() {
let settings = utils.yeoman.getAllSettingsFromComponentName(this.name, this.config.get('style'));
let componentType = this.options.stateless ? 'Stateless' : 'Base';
// Set the template base. If it cannot be guessed,
// use files from the default directory. If it cannot be
// guessed, assume we have something REALLY REALLY old here...
let templateBase = this.config.get('generatedWithVersion');
if(!templateBase) {
templateBase = 3;
// use files from the default directory. In this case,
// assume we have something REALLY REALLY old here...
let generatorVersion = this.config.get('generatedWithVersion');
if(!generatorVersion) {
generatorVersion = 3;
}
const settings = utils.yeoman.getAllSettingsFromComponentName(this.name, this.config.get('style'), generatorVersion);
const componentType = this.options.stateless ? 'Stateless' : 'Base';
// Create the style template
this.fs.copyTpl(
this.templatePath(`${templateBase}/styles/Component${settings.style.suffix}`),
this.templatePath(`${generatorVersion}/styles/Component${settings.style.suffix}`),
this.destinationPath(settings.style.path + settings.style.fileName),
settings
);
// Create the component
this.fs.copyTpl(
this.templatePath(`${templateBase}/components/${componentType}.js`),
this.templatePath(`${generatorVersion}/components/${componentType}.js`),
this.destinationPath(settings.component.path + settings.component.fileName),
settings
);
// Create the unit test
this.fs.copyTpl(
this.templatePath(`${templateBase}/tests/Base.js`),
this.templatePath(`${generatorVersion}/tests/Base.js`),
this.destinationPath(settings.test.path + settings.test.fileName),
settings
);

View File

@ -4,6 +4,7 @@ import styles from '<%= style.webpackPath %>';
@cssmodules(styles)
class <%= component.className %> extends React.Component {
render() {
return (
<div className="<%= style.className %>" styleName="<%= style.className %>">

View File

@ -5,7 +5,7 @@ import styles from '<%= style.webpackPath %>';
function <%= component.className %>() {
return (
<div className="<%= style.className %>">
<div className="<%= style.className %>" styleName="<%= style.className %>">
Please edit <%= component.path %><%= component.fileName %> to update this component!
</div>
);

View File

@ -6,12 +6,12 @@ describe('<<%= component.className %> />', () => {
let component;
beforeEach(() => {
component = shallow(<%= component.className %>);
component = shallow(<<%= component.className %> />);
});
describe('when rendering the component', () => {
it('should have a className of "index"', () => {
it('should have a className of "<%= style.className %>"', () => {
expect(component.hasClass('<%= style.className %>')).to.equal(true);
});
});

View File

@ -40,9 +40,9 @@
"scripts": {
"test": "mocha",
"test:watch": "mocha -w",
"release:major": "npm version major && npm publish && git push --follow-tags",
"release:minor": "npm version minor && npm publish && git push --follow-tags",
"release:patch": "npm version patch && npm publish && git push --follow-tags"
"release:major": "npm version major && npm publish && git push --follow-tags --tag beta",
"release:minor": "npm version minor && npm publish && git push --follow-tags --tag beta",
"release:patch": "npm version patch && npm publish && git push --follow-tags --tag beta"
},
"dependencies": {
"escodegen": "^1.7.0",

View File

@ -52,8 +52,12 @@ describe('react-webpack:app', () => {
expect(generator.config.get('style')).to.equal('css');
});
it('should use "css modules" per default', () => {
expect(generator.config.get('cssmodules')).to.be.true;
});
it('should not enable "PostCSS" by default', () => {
expect(generator.config.get('postcss')).to.equal(false);
expect(generator.config.get('postcss')).to.be.false;
});
});

View File

@ -173,75 +173,72 @@ describe('react-webpack:component', () => {
}
// Run all tests for all available style types.
testComponentWithStyle(styleTypes.css);
testComponentWithStyle(styleTypes.sass);
testComponentWithStyle(styleTypes.scss);
testComponentWithStyle(styleTypes.less);
testComponentWithStyle(styleTypes.stylus);
// Test stateless components (should be enough when testing with defaults)
testComponentWithStyle(styleTypes.css, { stateless: true });
// Stateless components will also be tested!
for(const style in styleTypes) {
testComponentWithStyle(styleTypes[style]);
testComponentWithStyle(styleTypes[style], { stateless: true });
}
});
describe('when using version 4 of the generator', () => {
/**
* @var {yeoman.generator} generator
* Global generator instance, set by createGeneratedComponent
*/
let generator;
// List of available style types. Please add a line that says
// testComponentWithStyle(styleTypes.KEY); to the bottom of the file
// to run all unit tests for this filetype.
const styleTypes = {
css: {
type: 'css',
fileName: 'src/styles/Mycomponent.css',
expandedFileName: 'src/styles/my/littleSpecial/Test.css',
fileName: 'src/components/mycomponent.cssmodule.css',
expandedFileName: 'src/components/my/littleSpecial/test.cssmodule.css',
assertions: {
componentImport: 'import styles from \'styles//Mycomponent.css\';',
componentImport: 'import styles from \'./mycomponent.cssmodule.css\';',
styleContent: '.mycomponent-component'
}
},
sass: {
type: 'sass',
fileName: 'src/styles/Mycomponent.sass',
expandedFileName: 'src/styles/my/littleSpecial/Test.sass',
fileName: 'src/components/Mycomponent.cssmodule.sass',
expandedFileName: 'src/components/my/littleSpecial/test.cssmodule.sass',
assertions: {
componentImport: 'import styles from \'styles//Mycomponent.sass\';',
componentImport: 'import styles from \'./mycomponent.cssmodule.sass\';',
styleContent: '.mycomponent-component'
}
},
scss: {
type: 'scss',
fileName: 'src/styles/Mycomponent.scss',
expandedFileName: 'src/styles/my/littleSpecial/Test.scss',
fileName: 'src/components/mycomponent.cssmodule.scss',
expandedFileName: 'src/components/my/littleSpecial/test.cssmodule.scss',
assertions: {
componentImport: 'import styles from \'styles//Mycomponent.scss\';',
componentImport: 'import styles from \'./mycomponent.cssmodule.scss\';',
styleContent: '.mycomponent-component'
}
},
less: {
type: 'less',
fileName: 'src/styles/Mycomponent.less',
expandedFileName: 'src/styles/my/littleSpecial/Test.less',
fileName: 'src/components/mycomponent.cssmodule.less',
expandedFileName: 'src/components/my/littleSpecial/test.cssmodule.less',
assertions: {
componentImport: 'import styles from \'styles//Mycomponent.less\';',
componentImport: 'import styles from \'./mycomponent.cssmodule.less\';',
styleContent: '.mycomponent-component'
}
},
stylus: {
type: 'stylus',
fileName: 'src/styles/Mycomponent.styl',
expandedFileName: 'src/styles/my/littleSpecial/Test.styl',
fileName: 'src/components/mycomponent.cssmodule.styl',
expandedFileName: 'src/components/my/littleSpecial/test.cssmodule.styl',
assertions: {
componentImport: 'import styles from \'styles//Mycomponent.styl\';',
componentImport: 'import styles from \'./mycomponent.cssmodule.styl\';',
styleContent: '.mycomponent-component'
}
}
};
/**
* @var {yeoman.generator} generator
* Global generator instance, set by createGeneratedComponent
*/
let generator;
/**
* Return a newly generated component with given name and style
* @param {String} name Name of the component
@ -281,9 +278,9 @@ describe('react-webpack:component', () => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.file([
'src/components/MycomponentComponent.js',
'src/components/Mycomponent.js',
style.fileName,
'test/components/MycomponentComponentTest.js'
'test/components/MycomponentTest.js'
]);
done();
});
@ -294,21 +291,21 @@ describe('react-webpack:component', () => {
it('should always import REACT', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('src/components/MycomponentComponent.js', 'import React from \'react\';');
assert.fileContent('src/components/Mycomponent.js', 'import React from \'react\';');
done();
});
});
it(`should require the created ${style.type} file`, (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('src/components/MycomponentComponent.js', style.assertions.componentImport);
assert.fileContent('src/components/Mycomponent.js', style.assertions.componentImport);
done();
});
});
it('should have its displayName set per default', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('src/components/MycomponentComponent.js', 'displayName = \'MycomponentComponent\';');
assert.fileContent('src/components/Mycomponent.js', 'Mycomponent.displayName = \'Mycomponent\';');
done();
});
});
@ -318,11 +315,11 @@ describe('react-webpack:component', () => {
let exportAssertion;
if(generator.options.stateless) {
exportAssertion = 'export default cssmodules(MycomponentComponent, styles);';
exportAssertion = 'export default cssmodules(Mycomponent, styles);';
} else {
exportAssertion = 'export default MycomponentComponent';
exportAssertion = 'export default Mycomponent';
}
assert.fileContent('src/components/MycomponentComponent.js', exportAssertion);
assert.fileContent('src/components/Mycomponent.js', exportAssertion);
done();
});
});
@ -331,9 +328,9 @@ describe('react-webpack:component', () => {
createGeneratedComponent('my/little !special/test', style.type, options, () => {
assert.file([
'src/components/my/littleSpecial/TestComponent.js',
'src/components/my/littleSpecial/Test.js',
style.expandedFileName,
'test/components/my/littleSpecial/TestComponentTest.js'
'test/components/my/littleSpecial/TestTest.js'
]);
done();
});
@ -354,7 +351,7 @@ describe('react-webpack:component', () => {
it('should import the react component', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';');
assert.fileContent('test/components/MycomponentTest.js', 'import Mycomponent from \'components//Mycomponent.js\';');
done();
});
});
@ -363,14 +360,11 @@ describe('react-webpack:component', () => {
}
// Run all tests for all available style types.
testComponentWithStyle(styleTypes.css);
testComponentWithStyle(styleTypes.sass);
testComponentWithStyle(styleTypes.scss);
testComponentWithStyle(styleTypes.less);
testComponentWithStyle(styleTypes.stylus);
// Test stateless components (should be enough when testing with defaults)
testComponentWithStyle(styleTypes.css, { stateless: true });
// Stateless components will also be tested!
for(const style in styleTypes) {
testComponentWithStyle(styleTypes[style]);
testComponentWithStyle(styleTypes[style], { stateless: true });
}
});
});

View File

@ -67,9 +67,38 @@ describe('Utilities:Yeoman', () => {
describe('#getAllSettingsFromComponentName', () => {
it('should get all required information for component creation from the components name', () => {
describe('when the generator version is set to 4', () => {
let expection = {
const expection = {
style: {
webpackPath: './test.cssmodule.css',
path: 'src/components/my/component/',
fileName: 'test.cssmodule.css',
className: 'test-component',
suffix: '.css'
},
component: {
webpackPath: 'components/my/component/Test.js',
path: 'src/components/my/component/',
fileName: 'Test.js',
className: 'Test',
displayName: 'MyComponentTest',
suffix: '.js'
},
test: {
path: 'test/components/my/component/',
fileName: 'TestTest.js'
}
};
it('should get all required information for component creation from the components name', () => {
expect(utils.getAllSettingsFromComponentName('my/component/test', 'css', 4)).to.deep.equal(expection);
});
});
describe('when the generator version is set to 3 (or not set at all)', () => {
const expection = {
style: {
webpackPath: 'styles/my/component/Test.css',
path: 'src/styles/my/component/',
@ -91,7 +120,10 @@ describe('Utilities:Yeoman', () => {
}
};
expect(utils.getAllSettingsFromComponentName('my/component/test')).to.deep.equal(expection);
it('should get all required information for component creation from the components name', () => {
expect(utils.getAllSettingsFromComponentName('my/component/test')).to.deep.equal(expection);
expect(utils.getAllSettingsFromComponentName('my/component/test', 'css', 3)).to.deep.equal(expection);
});
});
});

View File

@ -19,14 +19,21 @@ let getBaseDir = () => {
* Get all settings (paths and the like) from components name
* @param {String} componentName The components name
* @param {String} style Style language to use [optional]
* @param {String|Number} generatorVersion The version of the generator [optional]
* @return {Object} Component settings
*/
let getAllSettingsFromComponentName = (componentName, style) => {
let getAllSettingsFromComponentName = (componentName, style, generatorVersion) => {
// Use css per default
if(!style) {
style = 'css';
}
// Use version 3 fallback as default for projects
if(!generatorVersion) {
generatorVersion = 3;
}
// Clean up the path and pull it to parts
let cleanedPaths = getCleanedPathName(componentName);
let componentParts = cleanedPaths.split('/');
@ -46,27 +53,61 @@ let getAllSettingsFromComponentName = (componentName, style) => {
// Configure tests
let testPath = configUtils.getChoiceByKey('path', 'test');
let settings = {
style: {
webpackPath: `styles/${componentPartPath}/${componentBaseName}${styleSettings.suffix}`,
path: `${stylePaths.path}/${componentPartPath}/`,
fileName: `${componentBaseName}${styleSettings.suffix}`,
className: getComponentStyleName(componentBaseName),
suffix: styleSettings.suffix
},
component: {
webpackPath: `components/${componentPartPath}/${componentBaseName}Component.js`,
path: `${componentPath.path}/${componentPartPath}/`,
fileName: `${componentBaseName}Component.js`,
className: `${componentBaseName}Component`,
displayName: `${componentFullName}Component`,
suffix: '.js'
},
test: {
path: `${testPath.path}/components/${componentPartPath}/`,
fileName: `${componentBaseName}ComponentTest.js`
}
};
let settings;
switch(generatorVersion) {
case 4:
settings = {
style: {
webpackPath: `./${componentBaseName.toLowerCase()}.cssmodule${styleSettings.suffix}`,
path: `${componentPath.path}/${componentPartPath}/`,
fileName: `${componentBaseName.toLowerCase()}.cssmodule${styleSettings.suffix}`,
className: getComponentStyleName(componentBaseName),
suffix: styleSettings.suffix
},
component: {
webpackPath: `components/${componentPartPath}/${componentBaseName}.js`,
path: `${componentPath.path}/${componentPartPath}/`,
fileName: `${componentBaseName}.js`,
className: `${componentBaseName}`,
displayName: `${componentFullName}`,
suffix: '.js'
},
test: {
path: `${testPath.path}/components/${componentPartPath}/`,
fileName: `${componentBaseName}Test.js`
}
};
break;
// Use version 3 style for the defaults and fallback
// @deprecated
case 3:
default:
settings = {
style: {
webpackPath: `styles/${componentPartPath}/${componentBaseName}${styleSettings.suffix}`,
path: `${stylePaths.path}/${componentPartPath}/`,
fileName: `${componentBaseName}${styleSettings.suffix}`,
className: getComponentStyleName(componentBaseName),
suffix: styleSettings.suffix
},
component: {
webpackPath: `components/${componentPartPath}/${componentBaseName}Component.js`,
path: `${componentPath.path}/${componentPartPath}/`,
fileName: `${componentBaseName}Component.js`,
className: `${componentBaseName}Component`,
displayName: `${componentFullName}Component`,
suffix: '.js'
},
test: {
path: `${testPath.path}/components/${componentPartPath}/`,
fileName: `${componentBaseName}ComponentTest.js`
}
};
break;
}
return settings;
};