Compare commits

...
14 Commits
Author SHA1 Message Date
Chris ad6135e48d 2.2.5 2015-10-19 08:11:34 +02:00
Chris 63f1b74fe7 Added ability to create stateless components
Cleaned up unittests
2015-10-19 08:11:22 +02:00
Chris 432d5d00de 2.2.4 2015-10-15 16:09:41 +02:00
Chris 685f6a75ce Added automatic generation of components displayName property. 2015-10-15 16:09:23 +02:00
Chris 82a080f586 2.2.3 2015-10-14 07:20:38 +02:00
Chris 6c41164d4a Updated changeling for 2.2.3 2015-10-14 07:20:23 +02:00
Chris 9eedc6fd2f Merge pull request #152 from VovanR/feature/gitignore
Add .gitignore file to generated application
2015-10-14 07:13:59 +02:00
Vladimir Rodkin 83d47bddd8 Add gitignore 2015-10-13 20:40:06 +03:00
Vladimir Rodkin 31e478f28d Add gitignore 2015-10-13 17:54:47 +03:00
Chris b95fc4344a 2.2.2 2015-10-12 16:35:14 +02:00
Chris 66b85f48d8 Fixed missing packages when using sass/less 2015-10-12 16:34:54 +02:00
Chris bf2df82159 2.2.1 2015-10-12 08:42:28 +02:00
Chris d729c3caa8 Added changelog to ignored files. 2015-10-12 08:41:47 +02:00
Chris 4f99a5a9db Added (mis-)spelling fix... again :( 2015-10-12 08:31:40 +02:00
12 changed files with 226 additions and 261 deletions
+17
View File
@@ -1,5 +1,22 @@
# generator-react-webpack - Changelog # generator-react-webpack - Changelog
## 2.2.5
___Upgrades:___
1. Added ability to create stateless components
2. Updated README with new installation instructions (need to install globally)
3. Added new tests for components (should be easier to handle in the future)
## 2.2.4
___Upgrades:___
1. Added automatic generation of components displayName property. This makes it easier to keep track of components that reside in deep subfolders (like src/components/my/example/components/indexComponent) would become "index" as a displayName, but should instead be MyExampleComponentsIndexComponent instead.
## 2.2.3
___Fixes:___
1. Fixed .gitignore renaming (Patch provided by [VovanR](https://github.com/VovanR))
## 2.2.0: ## 2.2.0:
___Upgrades:___ ___Upgrades:___
+12 -2
View File
@@ -12,7 +12,7 @@ Out of the box it comes with support for:
- Ability to unit test components via Karma and Mocha/Chai - Ability to unit test components via Karma and Mocha/Chai
## Changes in Version 2.0 ## Changes in Version 2.0
Tthis generator is written in ES2015. This means it is ___not compatible with node.js versions before 4.0___. This generator is written in ES2015. This means it is ___not compatible with node.js versions before 4.0___.
It also does __NOT__ include support for Flux-Frameworks anymore. Instead, we will use it as a base for other generators to build upon. This will make the base generator easier to use and update. It also does __NOT__ include support for Flux-Frameworks anymore. Instead, we will use it as a base for other generators to build upon. This will make the base generator easier to use and update.
@@ -27,8 +27,9 @@ If you have built a generator using generator-react-webpack, tell us and we will
## Installation ## Installation
```bash ```bash
# Make sure both is installed globally
npm install -g yo npm install -g yo
npm install generator-react-webpack npm install -g generator-react-webpack
``` ```
## Setting up projects ## Setting up projects
@@ -51,6 +52,15 @@ yo react-webpack:component my/namespaced/components/name
The above command will create a new component, as well as its stylesheet and a basic testcase. The above command will create a new component, as well as its stylesheet and a basic testcase.
## Generating new stateless functional components
```
yo react-webpack:component:stateless my/namespaced/components/name
```
Stateless functional components where introduced in React v0.14. They have a much shorter syntax than regular ones and no state or lifecycle methods at all. Please read the [React 0.14 release notes](https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html) to get more information about those components.
___Note___: You will still be able to set properties for stateless components!
## Usage ## Usage
The following commands are available in your project: The following commands are available in your project:
```bash ```bash
+4
View File
@@ -91,6 +91,7 @@ module.exports = generator.Base.extend({
let excludeList = [ let excludeList = [
'LICENCE', 'LICENCE',
'README.md', 'README.md',
'CHANGELOG.md',
'node_modules', 'node_modules',
'package.json' 'package.json'
]; ];
@@ -110,6 +111,9 @@ module.exports = generator.Base.extend({
if(fs.lstatSync(fullPath).isDirectory()) { if(fs.lstatSync(fullPath).isDirectory()) {
this.bulkDirectory(item, item); this.bulkDirectory(item, item);
} else { } else {
if (item === '.npmignore') {
this.copy(item, '.gitignore');
}
this.copy(item, item); this.copy(item, item);
} }
} }
+7 -1
View File
@@ -6,11 +6,17 @@ module.exports = generator.NamedBase.extend({
constructor: function() { constructor: function() {
generator.NamedBase.apply(this, arguments); generator.NamedBase.apply(this, arguments);
this.option('stateless', {
desc: 'Create a stateless component instead of a full one',
defaults: false
});
}, },
writing: function() { writing: function() {
let settings = utils.yeoman.getAllSettingsFromComponentName(this.name, this.config.get('style')); let settings = utils.yeoman.getAllSettingsFromComponentName(this.name, this.config.get('style'));
let componentType = this.options.stateless ? 'Stateless' : 'Base';
// Create the style template // Create the style template
this.fs.copyTpl( this.fs.copyTpl(
@@ -21,7 +27,7 @@ module.exports = generator.NamedBase.extend({
// Create the component // Create the component
this.fs.copyTpl( this.fs.copyTpl(
this.templatePath('components/Base.js'), this.templatePath(`components/${componentType}.js`),
this.destinationPath(settings.component.path + settings.component.fileName), this.destinationPath(settings.component.path + settings.component.fileName),
settings settings
); );
@@ -14,6 +14,8 @@ class <%= component.className %> extends React.Component {
} }
} }
<%= component.className %>.displayName = '<%= component.displayName %>';
// Uncomment properties you need // Uncomment properties you need
// <%= component.className %>.propTypes = {}; // <%= component.className %>.propTypes = {};
// <%= component.className %>.defaultProps = {}; // <%= component.className %>.defaultProps = {};
@@ -0,0 +1,19 @@
'use strict';
import React from 'react';
require('<%= style.webpackPath %>');
let <%= component.className %> = (props) => (
<div className="<%= style.className %>">
Please edit <%= component.path %>/<%= component.fileName %> to update this component!
</div>
);
<%= component.className %>.displayName = '<%= component.displayName %>';
// Uncomment properties you need
// <%= component.className %>.propTypes = {};
// <%= component.className %>.defaultProps = {};
export default <%= component.className %>;
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "generator-react-webpack", "name": "generator-react-webpack",
"version": "2.2.0", "version": "2.2.5",
"description": "Yeoman generator for using React with Webpack via Babel", "description": "Yeoman generator for using React with Webpack via Babel",
"keywords": [ "keywords": [
"yeoman-generator", "yeoman-generator",
+1
View File
@@ -44,6 +44,7 @@ describe('react-webpack:app', () => {
assert.file([ assert.file([
'.editorconfig', '.editorconfig',
'.eslintrc', '.eslintrc',
'.gitignore',
'.npmignore', '.npmignore',
'.yo-rc.json' '.yo-rc.json'
]); ]);
+152 -254
View File
@@ -7,277 +7,175 @@ describe('react-webpack:component', () => {
let generatorComponent = path.join(__dirname, '../../../generators/component'); let generatorComponent = path.join(__dirname, '../../../generators/component');
// 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',
assertions: {
componentImport: 'require(\'styles//Mycomponent.css\');',
styleContent: '.mycomponent-component'
}
},
sass: {
type: 'sass',
fileName: 'src/styles/Mycomponent.sass',
expandedFileName: 'src/styles/my/littleSpecial/Test.sass',
assertions: {
componentImport: 'require(\'styles//Mycomponent.sass\');',
styleContent: '.mycomponent-component'
}
},
scss: {
type: 'scss',
fileName: 'src/styles/Mycomponent.scss',
expandedFileName: 'src/styles/my/littleSpecial/Test.scss',
assertions: {
componentImport: 'require(\'styles//Mycomponent.scss\');',
styleContent: '.mycomponent-component'
}
},
less: {
type: 'less',
fileName: 'src/styles/Mycomponent.less',
expandedFileName: 'src/styles/my/littleSpecial/Test.less',
assertions: {
componentImport: 'require(\'styles//Mycomponent.less\');',
styleContent: '.mycomponent-component'
}
},
stylus: {
type: 'stylus',
fileName: 'src/styles/Mycomponent.styl',
expandedFileName: 'src/styles/my/littleSpecial/Test.styl',
assertions: {
componentImport: 'require(\'styles//Mycomponent.styl\');',
styleContent: '.mycomponent-component'
}
}
};
/** /**
* Return a newly generated component with given name and style * Return a newly generated component with given name and style
* @param {String} name * @param {String} name Name of the component
* @param {String} styleType * @param {String} styleType Styling language to use
* @param {Function} callback * @param {Object} options Options to use for the generator
* @param {Function} callback Test callback to run
*/ */
function createGeneratedComponent(name, styleType, callback) { function createGeneratedComponent(name, styleType, options, callback) {
helpers.run(generatorComponent) helpers.run(generatorComponent)
.withArguments([name]) .withArguments([name])
.withOptions(options)
.on('ready', (instance) => { .on('ready', (instance) => {
instance.config.set('style', styleType); instance.config.set('style', styleType);
}) })
.on('end', callback); .on('end', callback);
} }
describe('When using style type "css"', () => { /**
* Test a component with styling applied
* @param {Object} style The style to apply (see styleTypes above)
* @param {Object} options Options to use [optional]
*/
function testComponentWithStyle(style, options) {
describe('Setup', () => { // Make sure we always have options
if(!options) {
options = {};
}
it('should create the react component, its stylesheet and test file', (done) => { describe(`When using style type "${style.type}"`, () => {
createGeneratedComponent('mycomponent', 'css', () => {
assert.file([ describe('When writing is called', () => {
'src/components/MycomponentComponent.js',
'src/styles/Mycomponent.css', it(`should create the react component, its ${style.type}-stylesheet and test file`, (done) => {
'test/components/MycomponentComponentTest.js' createGeneratedComponent('mycomponent', style.type, options, () => {
]);
done(); assert.file([
'src/components/MycomponentComponent.js',
style.fileName,
'test/components/MycomponentComponentTest.js'
]);
done();
});
});
});
describe('When creating a component', () => {
it('should always import REACT', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('src/components/MycomponentComponent.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);
done();
});
});
it('should have its displayName set per default', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('src/components/MycomponentComponent.js', `displayName = 'MycomponentComponent';`);
done();
});
});
it('should export the created component', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('src/components/MycomponentComponent.js', `export default MycomponentComponent`);
done();
});
});
it(`should be possible to create components in a subfolder`, (done) => {
createGeneratedComponent('my/little !special/test', style.type, options, () => {
assert.file([
'src/components/my/littleSpecial/TestComponent.js',
style.expandedFileName,
'test/components/my/littleSpecial/TestComponentTest.js'
]);
done();
});
});
});
describe('Style', () => {
it(`should add the components ${style.type} class to the stylesheet`, (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent(style.fileName, style.assertions.styleContent);
done();
});
});
});
describe('Test', () => {
it('should import the react component', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';');
done();
});
}); });
}); });
}); });
}
describe('Component', () => { // Run all tests for all available style types.
testComponentWithStyle(styleTypes.css);
it('should require the created css file', (done) => { testComponentWithStyle(styleTypes.sass);
createGeneratedComponent('mycomponent', 'css', () => { testComponentWithStyle(styleTypes.scss);
assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.css\');'); testComponentWithStyle(styleTypes.less);
done(); testComponentWithStyle(styleTypes.stylus);
});
});
});
describe('Style', () => {
it('should add the components css class to the stylesheet', (done) => {
createGeneratedComponent('mycomponent', 'css', () => {
assert.fileContent('src/styles/Mycomponent.css', '.mycomponent-component');
done();
});
});
});
describe('Test', () => {
it('should import the react component', (done) => {
createGeneratedComponent('mycomponent', 'css', () => {
assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';');
done();
});
});
});
}); // End css
describe('When creating a component in a subfolder', () => {
describe('Setup', () => {
it('should create the react component, its stylesheet and test file', (done) => {
createGeneratedComponent('my/little !special/test', 'css', () => {
assert.file([
'src/components/my/littleSpecial/TestComponent.js',
'src/styles/my/littleSpecial/Test.css',
'test/components/my/littleSpecial/TestComponentTest.js'
]);
done();
});
});
});
}); // End css (subfolder)
describe('When using style type "sass"', () => {
describe('Setup', () => {
it('should create the react component, its stylesheet and test file', (done) => {
createGeneratedComponent('mycomponent', 'sass', () => {
assert.file([
'src/components/MycomponentComponent.js',
'src/styles/Mycomponent.sass',
'test/components/MycomponentComponentTest.js'
]);
done();
});
});
});
describe('Component', () => {
it('should require the created sass file', (done) => {
createGeneratedComponent('mycomponent', 'sass', () => {
assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.sass\');');
done();
});
});
});
describe('Style', () => {
it('should add the components sass class to the stylesheet', (done) => {
createGeneratedComponent('mycomponent', 'sass', () => {
assert.fileContent('src/styles/Mycomponent.sass', '.mycomponent-component');
done();
});
});
});
describe('Test', () => {
it('should import the react component', (done) => {
createGeneratedComponent('mycomponent', 'sass', () => {
assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';');
done();
});
});
});
}); // End sass
describe('When using style type "scss"', () => {
describe('Setup', () => {
it('should create the react component, its stylesheet and test file', (done) => {
createGeneratedComponent('mycomponent', 'scss', () => {
assert.file([
'src/components/MycomponentComponent.js',
'src/styles/Mycomponent.scss',
'test/components/MycomponentComponentTest.js'
]);
done();
});
});
});
describe('Component', () => {
it('should require the created scss file', (done) => {
createGeneratedComponent('mycomponent', 'scss', () => {
assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.scss\');');
done();
});
});
});
describe('Style', () => {
it('should add the components scss class to the stylesheet', (done) => {
createGeneratedComponent('mycomponent', 'scss', () => {
assert.fileContent('src/styles/Mycomponent.scss', '.mycomponent-component');
done();
});
});
});
describe('Test', () => {
it('should import the react component', (done) => {
createGeneratedComponent('mycomponent', 'scss', () => {
assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';');
done();
});
});
});
}); // End scss
describe('When using style type "less"', () => {
describe('Setup', () => {
it('should create the react component, its stylesheet and test file', (done) => {
createGeneratedComponent('mycomponent', 'less', () => {
assert.file([
'src/components/MycomponentComponent.js',
'src/styles/Mycomponent.less',
'test/components/MycomponentComponentTest.js'
]);
done();
});
});
});
describe('Component', () => {
it('should require the created less file', (done) => {
createGeneratedComponent('mycomponent', 'less', () => {
assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.less\');');
done();
});
});
});
describe('Style', () => {
it('should add the components less class to the stylesheet', (done) => {
createGeneratedComponent('mycomponent', 'less', () => {
assert.fileContent('src/styles/Mycomponent.less', '.mycomponent-component');
done();
});
});
});
describe('Test', () => {
it('should import the react component', (done) => {
createGeneratedComponent('mycomponent', 'less', () => {
assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';');
done();
});
});
});
}); // End less
describe('When using style type "stylus"', () => {
describe('Setup', () => {
it('should create the react component, its stylesheet and test file', (done) => {
createGeneratedComponent('mycomponent', 'stylus', () => {
assert.file([
'src/components/MycomponentComponent.js',
'src/styles/Mycomponent.styl',
'test/components/MycomponentComponentTest.js'
]);
done();
});
});
});
describe('Component', () => {
it('should require the created stylus file', (done) => {
createGeneratedComponent('mycomponent', 'stylus', () => {
assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.styl\');');
done();
});
});
});
describe('Style', () => {
it('should add the components stylus class to the stylesheet', (done) => {
createGeneratedComponent('mycomponent', 'stylus', () => {
assert.fileContent('src/styles/Mycomponent.styl', '.mycomponent-component');
done();
});
});
});
describe('Test', () => {
it('should import the react component', (done) => {
createGeneratedComponent('mycomponent', 'less', () => {
assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';');
done();
});
});
});
}); // End stylus
// Test stateless components (should be enough when testing with defaults)
testComponentWithStyle(styleTypes.css, { stateless: true });
}); });
+1
View File
@@ -82,6 +82,7 @@ describe('Utilities:Yeoman', () => {
path: 'src/components/my/component/', path: 'src/components/my/component/',
fileName: 'TestComponent.js', fileName: 'TestComponent.js',
className: 'TestComponent', className: 'TestComponent',
displayName: 'MyComponentTestComponent',
suffix: '.js' suffix: '.js'
}, },
test: { test: {
+6 -3
View File
@@ -51,7 +51,8 @@
"value": "sass", "value": "sass",
"suffix": ".sass", "suffix": ".sass",
"packages": [ "packages": [
{ "name": "sass-loader", "version": "^1.0.1" } { "name": "sass-loader", "version": "^1.0.1" },
{ "name": "node-sass", "version": "^3.3.3" }
] ]
}, },
{ {
@@ -59,7 +60,8 @@
"value": "scss", "value": "scss",
"suffix": ".scss", "suffix": ".scss",
"packages": [ "packages": [
{ "name": "sass-loader", "version": "^1.0.1" } { "name": "sass-loader", "version": "^1.0.1" },
{ "name": "node-sass", "version": "^3.3.3" }
] ]
}, },
{ {
@@ -67,7 +69,8 @@
"value": "less", "value": "less",
"suffix": ".less", "suffix": ".less",
"packages": [ "packages": [
{ "name": "less-loader", "version": "^2.0.0" } { "name": "less-loader", "version": "^2.0.0" },
{ "name": "less", "version": "^2.5.3" }
] ]
}, },
{ {
+4
View File
@@ -33,6 +33,9 @@ let getAllSettingsFromComponentName = (componentName, style) => {
let componentBaseName = _.capitalize(componentParts.pop()); let componentBaseName = _.capitalize(componentParts.pop());
let componentPartPath = componentParts.join('/'); let componentPartPath = componentParts.join('/');
// Get the components displayName property
let componentFullName = _.classify(_.replaceAll(componentName, '/', '_'));
// Configure Styles // Configure Styles
let stylePaths = configUtils.getChoiceByKey('path', 'style'); let stylePaths = configUtils.getChoiceByKey('path', 'style');
let styleSettings = configUtils.getChoiceByKey('style', style); let styleSettings = configUtils.getChoiceByKey('style', style);
@@ -56,6 +59,7 @@ let getAllSettingsFromComponentName = (componentName, style) => {
path: `${componentPath.path}/${componentPartPath}/`, path: `${componentPath.path}/${componentPartPath}/`,
fileName: `${componentBaseName}Component.js`, fileName: `${componentBaseName}Component.js`,
className: `${componentBaseName}Component`, className: `${componentBaseName}Component`,
displayName: `${componentFullName}Component`,
suffix: '.js' suffix: '.js'
}, },
test: { test: {