diff --git a/generators/app/index.js b/generators/app/index.js index 1213b34..cf9b13b 100644 --- a/generators/app/index.js +++ b/generators/app/index.js @@ -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); }); diff --git a/generators/app/prompts.js b/generators/app/prompts.js index 53d91cc..72e20f2 100644 --- a/generators/app/prompts.js +++ b/generators/app/prompts.js @@ -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', diff --git a/generators/component/index.js b/generators/component/index.js index a27e012..973a713 100644 --- a/generators/component/index.js +++ b/generators/component/index.js @@ -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 ); diff --git a/generators/component/templates/4/components/Base.js b/generators/component/templates/4/components/Base.js index 0665a96..c031b05 100644 --- a/generators/component/templates/4/components/Base.js +++ b/generators/component/templates/4/components/Base.js @@ -4,6 +4,7 @@ import styles from '<%= style.webpackPath %>'; @cssmodules(styles) class <%= component.className %> extends React.Component { + render() { return (
diff --git a/generators/component/templates/4/components/Stateless.js b/generators/component/templates/4/components/Stateless.js index c9cb461..8a3e664 100644 --- a/generators/component/templates/4/components/Stateless.js +++ b/generators/component/templates/4/components/Stateless.js @@ -5,7 +5,7 @@ import styles from '<%= style.webpackPath %>'; function <%= component.className %>() { return ( -
+
Please edit <%= component.path %><%= component.fileName %> to update this component!
); diff --git a/generators/component/templates/4/tests/Base.js b/generators/component/templates/4/tests/Base.js index 256044b..38451b4 100644 --- a/generators/component/templates/4/tests/Base.js +++ b/generators/component/templates/4/tests/Base.js @@ -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); }); }); diff --git a/package.json b/package.json index 2388fdc..e33e9da 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/test/generators/app/indexTest.js b/test/generators/app/indexTest.js index 4ac8760..6887d68 100644 --- a/test/generators/app/indexTest.js +++ b/test/generators/app/indexTest.js @@ -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; }); }); diff --git a/test/generators/component/indexTest.js b/test/generators/component/indexTest.js index 20f3c49..82c30c6 100644 --- a/test/generators/component/indexTest.js +++ b/test/generators/component/indexTest.js @@ -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 }); + } }); }); diff --git a/test/utils/yeomanTest.js b/test/utils/yeomanTest.js index faef248..308cb65 100644 --- a/test/utils/yeomanTest.js +++ b/test/utils/yeomanTest.js @@ -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); + }); }); }); diff --git a/utils/yeoman.js b/utils/yeoman.js index 17fdf94..f99d22a 100644 --- a/utils/yeoman.js +++ b/utils/yeoman.js @@ -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; };