Added changes for version 2.0

This commit is contained in:
Chris
2015-09-21 20:45:57 +02:00
parent 438341c3fe
commit 023d937877
69 changed files with 1149 additions and 18551 deletions
+22
View File
@@ -0,0 +1,22 @@
{
"env": {
"browser": false,
"amd": false,
"es6": true,
"node": true,
"mocha": true
},
"rules": {
"comma-dangle": 1,
"quotes": [ 1, "single" ],
"no-undef": 1,
"global-strict": 0,
"no-extra-semi": 1,
"no-underscore-dangle": 0,
"no-console": 0,
"no-unused-vars": 1,
"no-trailing-spaces": [1, { "skipBlankLines": true }],
"no-unreachable": 1,
"no-alert": 0
}
}
-1
View File
@@ -13,4 +13,3 @@ $RECYCLE.BIN/
node_modules/
npm-debug.log
.idea/
/test/temp-test
-21
View File
@@ -1,21 +0,0 @@
{
"node": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": true,
"strict": true,
"trailing": true,
"smarttabs": true,
"white": true
}
+56 -360
View File
@@ -1,359 +1,76 @@
# generator-react-webpack [![Build Status](https://secure.travis-ci.org/newtriks/generator-react-webpack.png?branch=master)](https://travis-ci.org/newtriks/generator-react-webpack) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com/)
# generator-react-webpack V2.0 [![Build Status](https://secure.travis-ci.org/newtriks/generator-react-webpack.png?branch=master)](https://travis-ci.org/newtriks/generator-react-webpack) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com/)
> Yeoman generator for [ReactJS](http://facebook.github.io/react/) - lets you quickly set up a project including karma test runner and [Webpack](http://webpack.github.io/) module system.
# About
Generator-React-Webpack will help you build new Reactprojects using modern technologies.
## Usage
Out of the box it comes with support for:
- Webpack
- ES2015 via Babel-Loader
- Different supported style languages (sass, scss, less, stylus)
- Automatic code linting via esLint
- Ability to unit test components via Karma and Mocha/Chai
Install `generator-react-webpack`:
```
npm install -g generator-react-webpack
```
## Subgenerators
This generator is also the base project for some other generators that depend on it:
### Generator-React-Webpack-Alt
(https://github.com/weblogixx/generator-react-webpack-alt)
Generator based on generator-react-webpack, providing support for Alt.js.
Make a new directory, and `cd` into it:
```
mkdir my-new-project && cd $_
```
---
Run `yo react-webpack`, optionally passing an app name:
```
yo react-webpack [app-name]
```
Run `grunt build` for building and `grunt serve` for preview in the browser at [localhost](http://localhost:8000).
## Generators
Available generators:
* [react-webpack](#app) (aka [react-webpack:app](#app))
* [react-webpack:component](#component)
and for **Flux** or **Reflux** :
* [react-webpack:action](#action)
* [react-webpack:store](#store)
### App
Sets up a new ReactJS app, generating all the boilerplate you need to get started. The app generator also facilitates the following:
1. Configures a Gruntfile to run the app on a local server.
2. Configures Webpack to modularise the app enabling [loading of various file formats](http://webpack.github.io/docs/loader-list.html) e.g. JSON, CSS, PNG, etc.
3. Configures [Karma](http://karma-runner.github.io) to run all tests.
4. Watches for changes and recompiles JS and refreshes the browser.
Example:
## Installation
```bash
npm install -g yo
npm install generator-react-webpack
```
## Setting up projects
```bash
# Create a new directory, and `cd` into it:
mkdir my-new-project && cd my-new-project
# Run the generator
yo react-webpack
```
### Component
Please make sure to edit your newly generated `package.json` file to set description, author information and the like.
Generates a [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) component in `src/components`, its corresponding test in `test/spec/components` and its style in `src/styles`.
Example:
## Generating new components
```bash
yo react-webpack:component foo //or just: yo react-webpack:c foo
# After setup of course :)
# cd my-new-project
yo react-webpack:component my/namespaced/components/name
```
Produces `src/components/Foo.js` (*javascript - JSX*):
```js
'use strict';
The above command will create a new component, as well as its stylesheet and a basic testcase.
var React = require('react/addons');
require('styles/componentName.css'); //or .sass,.less etc...
var Foo = React.createClass({
render: function () {
return (
<div>
<p>Content for Foo</p>
</div>
)
}
});
module.exports = Foo;
```
And `test/spec/components/Foo.js` (*javascript - jasmine, as seen on http://simonsmith.io/unit-testing-react-components-without-a-dom/*):
```js
'use strict';
// Uncomment the following lines to use the react test utilities
// import React from 'react/addons';
// const TestUtils = React.addons.TestUtils;
import createComponent from 'helpers/createComponent';
import Foo from 'components/Foo.js';
describe('Foo', () => {
let FooComponent;
beforeEach(() => {
FooComponent = createComponent(Foo);
});
it('should have its component name as default className', () => {
expect(FooComponent._store.props.className).toBe('Foo');
});
});
```
And `src/styles/Foo.css` (or .sass, .less etc...) :
```css
.Foo {
border: 1px dashed #f00;
}
```
### rich flag
For all you lazy programmers out there, we've added another shortcut - `rich` flag:
## Usage
The following commands are available in your project:
```bash
yo react-webpack:c foofoo --rich
# Start for development
npm start # or
npm run serve
# Start the dev-server with the dist version
npm run serve:dist
# Just build the dist version and copy static files
npm run dist
# Run unit tests
npm test
# Lint all files in src (also automatically done AFTER tests are run)
npm run lint
# Clean up the dist directory
npm run clean
# Just copy the static assets
npm run copy
```
This will give you all of react component's most common stuff :
```js
var React = require('react/addons');
require('styles/Foofoo.sass');
var Foofoo = React.createClass({
mixins: [],
getInitialState: function() {
return {};
},
getDefaultProps: function() {},
componentWillMount: function() {},
componentDidMount: function() {},
shouldComponentUpdate: function() {},
componentDidUpdate: function() {},
componentWillUnmount: function() {},
render: function () {
return (
<div>
<p>Content for Foofoo</p>
</div>
);
}
});
module.exports = Foofoo;
```
Just remove those you don't need, then fill and space out the rest.
### Action
When using Flux or Reflux architecture, it generates an actionCreator in `src/actions` and it's corresponding test in `src/spec/actions`.
Example:
```bash
yo react-webpack:action bar //or just: yo react-webpack:a bar
```
Will create a file - `src/actions/BarActionCreators.js`
if 'architecture' is **Flux**, it Produces :
```js
'use strict';
var BarActionCreators = {
}
module.exports = BarActionCreators;
```
And if it's **Reflux**:
```js
'use strict';
var Reflux = require('reflux');
var BarActionCreators = Reflux.createActions([
]);
module.exports = BarActionCreators;
```
and same test for both architectures:
```js
'use strict';
describe('BarActionCreators', () => {
let action;
beforeEach(function() {
action = require('actions/BarActionCreators.js');
});
it('should be defined', () => {
expect(action).toBeDefined();
});
});
```
### Store
When using Flux or Reflux architecture, it generates a store in `src/stores` and it's corresponding test in `src/spec/stores`.
Example:
```bash
yo react-webpack:store baz //or just: yo react-webpack:s baz
```
Will create a file - `src/stores/BazStore.js`
if 'architecture' is **Flux**, it Produces :
```js
'use strict';
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var MainAppDispatcher = require('../dispatcher/MainAppDispatcher');
var BazStore = assign({}, EventEmitter.prototype, {
});
BazStore.dispatchToken = MainAppDispatcher.register(function(action) {
switch(action.type) {
default:
}
});
module.exports = BazStore;
```
And if it's **Reflux**:
```js
'use strict';
var Reflux = require('reflux');
//var Actions = require('actions/..');
var BazStore = Reflux.createStore({
listenables: Actions,
});
module.exports = BazStore;
```
and same test for both architectures:
```js
'use strict';
describe('BazStore', () => {
let store;
beforeEach(() => {
store = require('stores/BazStore.js');
});
it('should be defined', () => {
expect(store).toBeDefined();
});
});
```
## Options
Options are available as additional installs to the initial application generation phase.
### [ReactRouter](https://github.com/rackt/react-router)
A complete routing library for React. This option only adds the basic hooks to get started with [react router](https://github.com/rackt/react-router).
### styles language
css, sass, scss, less or stylus
Sets the style file's template and extension
### component suffix
js or jsx
Sets the file suffix for generated components. Defaults to "js". Please note that you need to require files *including* the file ending when using jsx as suffix. Example:
```js
var MyJSComponent = require('./MyJSComponent');
var MyJSX = require('./MyJSX.jsx');
```
### architecture
[flux](https://facebook.github.io/flux/) or [reflux](https://github.com/spoike/refluxjs)
### es6
If you are using `es6`, and want to use its export functionality (and not webpack's), just add `--es6` flag when you create a component, action or store.
## Testing
Running `grunt test` will run the unit tests with karma. Tests are written using [Jasmine](http://jasmine.github.io/) by default.
## Further Information
### Project Structure
The react-webpack generator automates the setup of a [ReactJS](http://facebook.github.io/react/) project using the specific structure detailed below:
```
project
- src
-components
MainApp.js
Foo.js
AnotherComponent.js
//for flux/reflux
-actions
BarActionCreators.js
-stores
BazStore.js
//for flux
-dispatcher
FooAppDispatcher
- styles
main.css
index.html
- test
- spec
- components
MainApp.js
Foo.js
AnotherComponent.js
//for flux/reflux
-actions
BarActionCreators.js
-stores
BazStore.js
- helpers
- react
addons.js
phantomjs-shims.js
Gruntfile.js
karma.conf.js
package.json
webpack.config.js
webpack.dist.config.js
```
I have tried to keep the project structure as simple as possible and understand it may not suit everyone.
### Naming Components
@@ -363,27 +80,6 @@ I have opted to follow [@floydophone](https://twitter.com/floydophone) conventio
Each component is a module and can be required using the [Webpack](http://webpack.github.io/) module system. [Webpack](http://webpack.github.io/) uses [Loaders](http://webpack.github.io/docs/loaders.html) which means you can also require CSS and a host of other file types. Read the [Webpack documentation](http://webpack.github.io/docs/home.html) to find out more.
### Grunt
Out the box the [Gruntfile](http://gruntjs.com/api/grunt.file) is configured with the following:
1. **webpack**: uses the [grunt-webpack](https://github.com/webpack/grunt-webpack) plugin to load all required modules and output to a single JS file `src/main.js`. This is included in the `src/index.html` file by default and will reload in the browser as and when it is recompiled.
2. **webpack-dev-server**: uses the [webpack-dev-server](https://github.com/webpack/webpack-dev-server) to watch for file changes and also serve the webpack app in development.
3. **connect**: uses the [grunt-connect](https://github.com/gruntjs/grunt-contrib-connect) plugin to start a webserver at [localhost](http://localhost:8000).
4. **karma**: uses the [grunt-karma](https://github.com/karma-runner/grunt-karma) plugin to load the Karma configuration file `karma.conf.js` located in the project root. This will run all tests using [PhantomJS](http://phantomjs.org/) by default but supports many other browsers. Please note that karma-launchers other than PhantomJS must be installed separately and configured in `karma.conf.js`.
### CSS
Included in the project is the [normalize.css](http://necolas.github.io/normalize.css/) script. There is also a `src/styles/main.css` script that's required by the core `src/components/App.js` component using Webpack.
### Linting
Webpack is automatically configured to run esLint (http://eslint.org) on every file change or build. The configuration can be found in `PROJECTROOT/.eslintrc`. There are plugins for different editors that use this tool directly:
- linter-eslint for Atom
- Sublime-Linter-eslint for Sublime
You could also use jsxhint, the corresponding rules file is located in `PROJECTROOT/.jshintrc`. However, the support for jsxhint is planned to be dropped in a later release and only available for backwards compatibility.
## Props
Thanks to all who contributed to [generator-angular](https://github.com/yeoman/generator-angular) as the majority of code here has been shamelessy sourced from that repos.
@@ -396,7 +92,7 @@ Contributions are welcomed. When submitting a bugfix, write a test that exposes
### Running Tests
`node node_modules/.bin/mocha`
`npm test` or `node node_modules/.bin/mocha`
## License
-1
View File
@@ -1 +0,0 @@
module.exports = require('../action');
-39
View File
@@ -1,39 +0,0 @@
'use strict';
var util = require('util');
var ScriptBase = require('../script-base.js');
var ActionGenerator = module.exports = function ActionGenerator(args, options, config) {
if (!args[0]) console.log('\n Please specify a name for this action creator \n');
else {
args[0] += 'ActionCreators';
ScriptBase.apply(this, arguments)
}
};
util.inherits(ActionGenerator, ScriptBase);
ActionGenerator.prototype.createActionFile = function createActionFile() {
this.option('es6');
this.es6 = this.options.es6;
var actionTemplate;
switch (this.architecture){
case 'flux':
actionTemplate = 'FluxAction';
break;
case 'reflux':
actionTemplate = 'RefluxAction';
break;
case 'alt':
actionTemplate = 'AltAction';
break;
}
console.log('Creating ' + this.architecture + ' action');
this.generateSourceAndTest(
actionTemplate,
'spec/Action',
'actions'
);
};
-164
View File
@@ -1,164 +0,0 @@
'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');
var generalUtils = require('../util.js');
var ReactWebpackGenerator = module.exports = function ReactWebpackGenerator(args, options, config) {
yeoman.generators.Base.apply(this, arguments);
this.option('es6');
this.argument('appname', { type: String, required: false });
this.appname = this.appname || path.basename(process.cwd());
this.appname = this._.camelize(this._.slugify(this._.humanize(this.appname)));
this.scriptAppName = this._.capitalize(this.appname) + generalUtils.appName(this);
this.config.set('app-name', this.appname);
if (typeof this.options.appPath === 'undefined') {
this.options.appPath = this.options.appPath || 'src';
}
this.appPath = this.options.appPath;
args = [this.scriptAppName];
this.composeWith('react-webpack:common', {
args: args
});
this.composeWith('react-webpack:main', {
args: args
});
this.on('end', function () {
this.installDependencies({ skipInstall: options['skip-install'], bower: false });
});
this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../package.json')));
this.config.save();
};
util.inherits(ReactWebpackGenerator, yeoman.generators.Base);
ReactWebpackGenerator.prototype.welcome = function welcome() {
// welcome message
if (!this.options['skip-welcome-message']) {
console.log(this.yeoman);
console.log(
'Out of the box I include Webpack and some default React components.\n'
);
}
};
ReactWebpackGenerator.prototype.askForReactRouter = function () {
var done = this.async();
this.prompt({
type : 'confirm',
name : 'reactRouter',
message : 'Would you like to include react-router?',
default : true
}, function (props) {
this.env.options.reactRouter = props.reactRouter;
done();
}.bind(this));
};
ReactWebpackGenerator.prototype.askForArchitecture = function() {
var done = this.async();
this.prompt({
type : 'list',
name : 'architecture',
message : 'Would you like to use one of these architectures?',
choices: [
{name:'No need for that, thanks',value:false},
{name:'Flux',value:'flux'},
{name:'ReFlux',value:'reflux'},
{name:'Alt',value:'alt'}
],
default : false
}, function(props) {
this.env.options.architecture = props.architecture;
this.config.set('architecture', props.architecture);
done();
}.bind(this));
};
ReactWebpackGenerator.prototype.askForStylesLanguage = function () {
var done = this.async();
this.prompt({
type : 'list',
name : 'stylesLanguage',
message : 'Which styles language you want to use?',
choices: [
{name: 'CSS', value: 'css'},
{name: 'SASS', value: 'sass'},
{name: 'SCSS', value: 'scss'},
{name: 'LESS', value: 'less'},
{name: 'Stylus', value: 'stylus'}
],
default : 'css'
}, function (props) {
this.env.options.stylesLanguage = props.stylesLanguage;
this.config.set('styles-language', props.stylesLanguage);
done();
}.bind(this));
};
// Allow to set the generated files suffix for the project when using components
// @see https://github.com/newtriks/generator-react-webpack/issues/99
ReactWebpackGenerator.prototype.askForComponentSuffix = function() {
var done = this.async();
this.prompt({
type: 'list',
name: 'componentSuffix',
message: 'Which file suffix do you want to use for components?',
choices: [
{ name: '.js (default)', value: 'js' },
{ name: '.jsx (deprecated)', value: 'jsx' }
],
default: 'js'
}, function (props) {
this.env.options.componentSuffix = props.componentSuffix;
this.config.set('component-suffix', props.componentSuffix);
done();
}.bind(this));
};
ReactWebpackGenerator.prototype.readIndex = function readIndex() {
this.indexFile = this.engine(this.read('../../templates/common/index.html'), this);
};
ReactWebpackGenerator.prototype.createIndexHtml = function createIndexHtml() {
this.indexFile = this.indexFile.replace(/&apos;/g, "'");
this.write(path.join(this.appPath, 'index.html'), this.indexFile);
};
ReactWebpackGenerator.prototype.packageFiles = function () {
this.es6 = this.options.es6;
this.reactRouter = this.env.options.reactRouter;
this.architecture = this.env.options.architecture;
this.stylesLanguage = this.env.options.stylesLanguage;
this.template('../../templates/common/_package.json', 'package.json');
this.template('../../templates/common/_webpack.config.js', 'webpack.config.js');
this.template('../../templates/common/_webpack.dist.config.js', 'webpack.dist.config.js');
this.copy('../../templates/common/Gruntfile.js', 'Gruntfile.js');
this.copy('../../templates/common/gitignore', '.gitignore');
};
ReactWebpackGenerator.prototype.styleFiles = function styleFiles() {
var mainFile = 'main.css';
this.copy('styles/' + mainFile, 'src/styles/' + mainFile);
};
ReactWebpackGenerator.prototype.imageFiles = function () {
this.sourceRoot(path.join(__dirname, 'templates'));
this.directory('images', 'src/images', true);
};
ReactWebpackGenerator.prototype.karmaFiles = function () {
this.copy('../../templates/common/karma.conf.js', 'karma.conf.js');
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

-41
View File
@@ -1,41 +0,0 @@
/* Stiziles */
html, body {
background: #222222;
}
/* main */
.main {
width: 100%;
height: 100%;
background: #222222;
color: #fff;
}
.main img {
width: 103px;
height: 89px;
margin-bottom: 10px;
text-align: center;
}
/* transitions */
.fade-enter {
opacity: 0.01;
transition: opacity .5s ease-in;
}
.fade-enter.fade-enter-active {
opacity: 1;
}
.fade-leave {
opacity: 1;
transition: opacity .5s ease-in;
}
.fade-leave.fade-leave-active {
opacity: 0.01;
}
-1
View File
@@ -1 +0,0 @@
module.exports = require('../component');
-17
View File
@@ -1,17 +0,0 @@
'use strict';
var path = require('path');
var util = require('util');
var yeoman = require('yeoman-generator');
var CommonGenerator = module.exports = function CommonGenerator(args, options, config) {
yeoman.generators.NamedBase.apply(this, arguments);
};
util.inherits(CommonGenerator, yeoman.generators.NamedBase);
CommonGenerator.prototype.setupEnv = function setupEnv() {
// Copies the contents of the generator `templates`
// directory into your users new application path
this.sourceRoot(path.join(__dirname, '../templates/common'));
this.directory('root', '.', true);
};
-25
View File
@@ -1,25 +0,0 @@
'use strict';
var util = require('util');
var ScriptBase = require('../script-base.js');
var ComponentGenerator = module.exports = function ComponentGenerator(args, options, config) {
ScriptBase.apply(this, arguments);
};
util.inherits(ComponentGenerator, ScriptBase);
ComponentGenerator.prototype.createComponentFile = function createComponentFile() {
this.option('es6');
this.option('rich');
this.es6 = this.options.es6;
this.rich = this.options.rich;
this.generateComponentTestAndStyle(
'Component',
'spec/Component',
'styles/Component',
'components',
true
);
};
+124
View File
@@ -0,0 +1,124 @@
'use strict';
let generator = require('yeoman-generator');
let utils = require('../../utils/all');
let prompts = require('./prompts');
let path = require('path');
let fs = require('fs');
// Set the base root directory for our files
let baseRootPath = path.join(__dirname, '../../node_modules/react-webpack-template');
module.exports = generator.Base.extend({
constructor: function() {
generator.Base.apply(this, arguments);
// Make options available
this.option('skip-welcome-message', {
desc: 'Skip the welcome message',
type: Boolean,
defaults: false
});
this.option('skip-install');
// Use our plain template as source
this.sourceRoot(baseRootPath);
this.config.save();
},
initializing: function() {
if(!this.options['skip-welcome-message']) {
this.log(require('yeoman-welcome'));
this.log('Out of the box I include Webpack and some default React components.\n');
}
},
prompting: function() {
let done = this.async();
this.prompt(prompts, function(props) {
// Make sure to get the correct app name if it is not the default
if(props.appName !== utils.yeoman.getAppName()) {
props.appName = utils.yeoman.getAppName(props.appName);
}
// Set needed global vars for yo
this.appName = props.appName;
this.style = props.style;
// Set needed keys into config
this.config.set('appName', this.appName);
this.config.set('appPath', this.appPath);
this.config.set('style', this.style);
done();
}.bind(this));
},
configuring: function() {
// Generate our package.json. Make sure to also include the required dependencies for styles
let defaultSettings = this.fs.readJSON(path.join(baseRootPath, 'package.json'));
let packageSettings = {
name: this.appName,
private: true,
version: '0.0.1',
description: 'YOUR DESCRIPTION - Generated by generator-react-webpack',
main: '',
scripts: defaultSettings.scripts,
repository: '',
keywords: [],
author: 'Your name here',
devDependencies: defaultSettings.devDependencies,
dependencies: defaultSettings.dependencies
};
// Add needed loaders if we have special styles
let styleConfig = utils.config.getChoiceByKey('style', this.style);
if(styleConfig && styleConfig.packages) {
for(let dependency of styleConfig.packages) {
packageSettings.dependencies[dependency.name] = dependency.version;
}
}
this.fs.writeJSON(this.destinationPath('package.json'), packageSettings);
},
writing: function() {
let excludeList = [
'LICENCE',
'README.md',
'node_modules',
'package.json'
];
// Get all files in our repo and copy the ones we should
fs.readdir(this.sourceRoot(), (err, items) => {
for(let item of items) {
// Skip the item if it is in our exclude list
if(excludeList.indexOf(item) !== -1) {
continue;
}
// Copy all items to our root
let fullPath = path.join(baseRootPath, item);
if(fs.lstatSync(fullPath).isDirectory()) {
this.bulkDirectory(item, item);
} else {
this.copy(item, item);
}
}
});
},
install: function() {
if(!this.options['skip-install']) {
this.installDependencies({ bower: false });
}
}
});
+18
View File
@@ -0,0 +1,18 @@
'use strict';
let utils = require('../../utils/all');
module.exports = [
{
type: 'input',
name: 'appName',
message: 'Please choose your application name',
default: utils.yeoman.getAppName()
},
{
type: 'list',
name: 'style',
message: 'Which styles language you want to use?',
choices: utils.config.getChoices('style'),
default: utils.config.getDefaultChoice('style')
}
];
+36
View File
@@ -0,0 +1,36 @@
'use strict';
let generator = require('yeoman-generator');
let utils = require('../../utils/all');
module.exports = generator.NamedBase.extend({
constructor: function() {
generator.NamedBase.apply(this, arguments);
},
writing: function() {
let settings = utils.yeoman.getAllSettingsFromComponentName(this.name, this.config.get('style'));
// Create the style template
this.fs.copyTpl(
this.templatePath(`styles/Component${settings.style.suffix}`),
this.destinationPath(settings.style.path + settings.style.fileName),
settings
);
// Create the component
this.fs.copyTpl(
this.templatePath('components/Base.js'),
this.destinationPath(settings.component.path + settings.component.fileName),
settings
);
// Create the unit test
this.fs.copyTpl(
this.templatePath('tests/Base.js'),
this.destinationPath(settings.test.path + settings.test.fileName),
settings
);
}
});
@@ -0,0 +1,21 @@
'use strict';
import React from 'react/addons';
require('<%= style.webpackPath %>');
class <%= component.className %> extends React.Component {
render() {
return (
<div className="<%= style.className %>">
Please edit <%= component.path %>/<%= component.fileName %> to update this component!
</div>
);
}
}
// Uncomment properties you need
// <%= component.className %>.propTypes = {};
// <%= component.className %>.defaultProps = {};
export default <%= component.className %>;
@@ -1,3 +1,3 @@
.<%= classedName %> {
.<%= style.className %> {
border: 1px dashed #f00;
}
@@ -1,3 +1,3 @@
.<%= classedName %> {
.<%= style.className %> {
border: 1px dashed #f00;
}
@@ -1,2 +1,2 @@
.<%= classedName %>
.<%= style.className %>
border: 1px dashed #f00
@@ -1,3 +1,3 @@
.<%= classedName %> {
.<%= style.className %> {
border: 1px dashed #f00;
}
@@ -1,2 +1,2 @@
.<%= classedName %>
.<%= style.className %>
border 1px dashed #f00
@@ -0,0 +1,23 @@
/*eslint-env node, mocha */
/*global expect */
/*eslint no-console: 0*/
'use strict';
// Uncomment the following lines to use the react test utilities
// import React from 'react/addons';
// const TestUtils = React.addons.TestUtils;
import createComponent from 'helpers/shallowRenderHelper';
import <%= component.className %> from '<%= component.webpackPath %>';
describe('<%= component.className %>', () => {
let component;
beforeEach(() => {
component = createComponent(<%= component.className %>);
});
it('should have its component name as default className', () => {
expect(component._store.props.className).to.equal('<%= style.className %>');
});
});
-35
View File
@@ -1,35 +0,0 @@
'use strict';
var util = require('util');
var ScriptBase = require('../script-base.js');
var MainGenerator = module.exports = function MainGenerator(args, options, config) {
ScriptBase.apply(this, arguments);
};
util.inherits(MainGenerator, ScriptBase);
MainGenerator.prototype.createAppFile = function createAppFile(scriptAppName) {
this.reactRouter = this.env.options.reactRouter;
this.scriptAppName = scriptAppName;
this.appTemplate('App', 'components/' + scriptAppName);
this.testTemplate('spec/App', 'components/' + scriptAppName);
};
MainGenerator.prototype.createMainFile = function createMainFile() {
if(this.env.options.reactRouter) {
this.appTemplate('main', 'components/main');
}
};
MainGenerator.prototype.createDispatcher = function createDispatcher() {
if(this.env.options.architecture=='flux') {
this.appTemplate('Dispatcher', 'dispatcher/' + this.scriptAppName + 'Dispatcher');
}
};
MainGenerator.prototype.createAltjsFile = function createAltjsFile() {
if(this.env.options.architecture=='alt') {
this.appTemplate('alt', 'alt');
}
};
+17 -16
View File
@@ -1,6 +1,6 @@
{
"name": "generator-react-webpack",
"version": "1.2.13",
"version": "1.2.23",
"description": "Yeoman generator for ReactJS and Webpack",
"keywords": [
"yeoman-generator",
@@ -19,7 +19,14 @@
"email": "simon@newtriks.com",
"url": "https://github.com/newtriks"
},
"main": "app/index.js",
"contributors": [
{
"name": "Christian Schilling",
"email": "cs@weblogixx.de",
"url": "https://github.com/weblogixx"
}
],
"main": "generators/app/index.js",
"repository": {
"type": "git",
"url": "git://github.com/newtriks/generator-react-webpack.git"
@@ -28,24 +35,18 @@
"test": "mocha"
},
"dependencies": {
"yeoman-generator": "^0.17.6"
"react-webpack-template": "^0.1.0",
"underscore.string": "^3.2.2",
"yeoman-generator": "^0.20.3",
"yeoman-welcome": "^1.0.1"
},
"devDependencies": {
"mocha": "^1.21.4",
"underscore.string": "^2.3.3",
"grunt": "^0.4.5",
"grunt-cli": "^0.1.13"
},
"peerDependencies": {
"yo": ">=1.2.0"
"chai": "^3.2.0",
"mocha": "^2.3.2"
},
"engines": {
"node": ">=0.10.0",
"node": ">=4.0.0",
"iojs": ">=1.1.0"
},
"licenses": [
{
"type": "MIT"
}
]
"license": "MIT"
}
-1
View File
@@ -1 +0,0 @@
module.exports = require('../store');
-118
View File
@@ -1,118 +0,0 @@
'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');
var generalUtils = require('./util.js');
var Generator = module.exports = function Generator() {
yeoman.generators.NamedBase.apply(this, arguments);
// Add capitalize mixin
this._.mixin({ 'capitalize': generalUtils.capitalize });
this._.mixin({ 'capitalizeFile': generalUtils.capitalizeFile });
this._.mixin({ 'capitalizeClass': generalUtils.capitalizeClass });
this._.mixin({ 'lowercase': generalUtils.lowercase });
this.appname = path.basename(process.cwd());
this.appname = this._.slugify(this._.humanize(this.appname));
this.scriptAppName = this._.camelize(this._.capitalize(this.appname)) + generalUtils.appName(this);
this.classedFileName = this._.capitalizeFile(this.name);
this.classedName = this._.capitalizeClass(this.name);
this.stylesLanguage = this.config.get('styles-language');
this.architecture = this.config.get('architecture');
if (typeof this.options.appPath === 'undefined') {
this.options.appPath = this.options.appPath || 'src';
}
if (typeof this.options.testPath === 'undefined') {
this.options.testPath = this.options.testPath || 'test/spec';
}
if (typeof this.options.stylesPath === 'undefined') {
this.options.stylesPath = this.options.stylesPath || 'src/styles';
}
var sourceRoot = '/templates/';
this.scriptSuffix = '.js';
this.reactSuffix = '.js';
// Add support for generated file legacy fallback
// @see https://github.com/newtriks/generator-react-webpack/issues/99
this.reactComponentSuffix = this.config.get('component-suffix');
switch(this.reactComponentSuffix) {
case 'jsx':
this.reactComponentSuffix = '.jsx';
break;
default:
this.reactComponentSuffix = '.js';
}
this.stylesSuffix = '.css';
switch(this.stylesLanguage) {
case 'sass':
this.stylesSuffix = '.sass';
break;
case 'scss':
this.stylesSuffix = '.scss';
break;
case 'less':
this.stylesSuffix = '.less';
break;
case 'stylus':
this.stylesSuffix = '.styl';
break;
}
this.sourceRoot(path.join(__dirname, sourceRoot));
};
util.inherits(Generator, yeoman.generators.NamedBase);
Generator.prototype.appTemplate = function (src, dest) {
yeoman.generators.Base.prototype.template.apply(this, [
path.join('javascript', src + this.scriptSuffix),
path.join(this.options.appPath, dest) + this.scriptSuffix
]);
};
Generator.prototype.reactComponentTemplate = function (src, dest) {
yeoman.generators.Base.prototype.template.apply(this, [
path.join('javascript', src + this.reactSuffix),
path.join(this.options.appPath, dest) + this.reactComponentSuffix
]);
};
Generator.prototype.testTemplate = function (src, dest) {
yeoman.generators.Base.prototype.template.apply(this, [
src + this.scriptSuffix,
path.join(this.options.testPath, dest) + this.scriptSuffix
]);
};
Generator.prototype.stylesTemplate = function (src, dest) {
yeoman.generators.Base.prototype.template.apply(this, [
src + this.stylesSuffix,
path.join(this.options.stylesPath, dest) + this.stylesSuffix
]);
};
Generator.prototype.htmlTemplate = function (src, dest) {
yeoman.generators.Base.prototype.template.apply(this, [
src,
path.join(this.options.appPath, dest.toLowerCase())
]);
};
Generator.prototype.generateSourceAndTest = function (appTemplate, testTemplate, targetDirectory) {
this.appTemplate(appTemplate, path.join(targetDirectory, this._.capitalizeFile(this.name)));
this.testTemplate(testTemplate, path.join(targetDirectory, this._.capitalizeFile(this.name)));
};
Generator.prototype.generateComponentTestAndStyle = function (componentTemplate, testTemplate, stylesTemplate, targetDirectory) {
this.reactComponentTemplate(componentTemplate, path.join(targetDirectory, this._.capitalizeFile(this.name)));
this.testTemplate(testTemplate, path.join(targetDirectory, this._.capitalizeFile(this.name)));
this.stylesTemplate(stylesTemplate, path.join(this._.capitalizeFile(this.name)));
};
-42
View File
@@ -1,42 +0,0 @@
'use strict';
var util = require('util');
var ScriptBase = require('../script-base.js');
var StoreGenerator = module.exports = function StoreGenerator(args, options, config) {
if (!args[0]) console.log('\n Please specify a name for this store \n');
else {
args[0] += 'Store';
ScriptBase.apply(this, arguments)
}
};
util.inherits(StoreGenerator, ScriptBase);
StoreGenerator .prototype.createStoreFile = function createStoreFile() {
this.option('es6');
this.es6 = this.options.es6;
var storeTemplate;
switch (this.architecture){
case 'flux':
storeTemplate = 'FluxStore';
this.dispatcherName = this._.capitalizeFile(this.config.get('app-name')) + 'AppDispatcher';
break;
case 'reflux':
storeTemplate = 'RefluxStore';
break;
case 'alt':
storeTemplate = 'AltStore';
break;
}
console.log('Creating ' + this.architecture + ' store');
this.generateSourceAndTest(
storeTemplate,
'spec/Store',
'stores'
);
};
-125
View File
@@ -1,125 +0,0 @@
'use strict';
var mountFolder = function (connect, dir) {
return connect.static(require('path').resolve(dir));
};
var webpackDistConfig = require('./webpack.dist.config.js'),
webpackDevConfig = require('./webpack.config.js');
module.exports = function (grunt) {
// Let *load-grunt-tasks* require everything
require('load-grunt-tasks')(grunt);
// Read configuration from package.json
var pkgConfig = grunt.file.readJSON('package.json');
grunt.initConfig({
pkg: pkgConfig,
webpack: {
options: webpackDistConfig,
dist: {
cache: false
}
},
'webpack-dev-server': {
options: {
hot: true,
port: 8000,
webpack: webpackDevConfig,
publicPath: '/assets/',
contentBase: './<%= pkg.src %>/'
},
start: {
keepAlive: true
}
},
connect: {
options: {
port: 8000
},
dist: {
options: {
keepalive: true,
middleware: function (connect) {
return [
mountFolder(connect, pkgConfig.dist)
];
}
}
}
},
open: {
options: {
delay: 500
},
dev: {
path: 'http://localhost:<%= connect.options.port %>/webpack-dev-server/'
},
dist: {
path: 'http://localhost:<%= connect.options.port %>/'
}
},
karma: {
unit: {
configFile: 'karma.conf.js'
}
},
copy: {
dist: {
files: [
// includes files within path
{
flatten: true,
expand: true,
src: ['<%= pkg.src %>/*'],
dest: '<%= pkg.dist %>/',
filter: 'isFile'
},
{
flatten: true,
expand: true,
src: ['<%= pkg.src %>/images/*'],
dest: '<%= pkg.dist %>/images/'
}
]
}
},
clean: {
dist: {
files: [{
dot: true,
src: [
'<%= pkg.dist %>'
]
}]
}
}
});
grunt.registerTask('serve', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'open:dist', 'connect:dist']);
}
grunt.task.run([
'open:dev',
'webpack-dev-server'
]);
});
grunt.registerTask('test', ['karma']);
grunt.registerTask('build', ['clean', 'copy', 'webpack']);
grunt.registerTask('default', []);
};
-52
View File
@@ -1,52 +0,0 @@
{
"name": "<%= _.slugify(appname) %>",
"version": "0.0.0",
"description": "",
"repository": "",
"private": true,
"src": "src",
"test": "test",
"dist": "dist",
"mainInput": "<% if (reactRouter) { %>main<% } else { %><%= scriptAppName %><% } %>",
"mainOutput": "main",
"dependencies": {<% if (reactRouter) { %>
"react-router": "0.13.x",<% } if (architecture === 'flux') { %>
"flux": "^2.0.1",
"events": "^1.0.2",
"object-assign": "^2.0.0", <% } if (architecture === 'reflux') {%>
"reflux": "^0.2.7", <% } if (architecture === 'alt') { %>
"alt": "^0.16.5", <% } %>
"react": "0.13.x",
"normalize.css": "~3.0.3"
},
"devDependencies": {
"babel": "^5.0.0",
"babel-loader": "^5.0.0",
"grunt": "~0.4.5",
"eslint": "^0.21.2",
"eslint-loader": "^0.11.2",
"eslint-plugin-react": "^2.4.0",
"load-grunt-tasks": "~0.6.0",
"grunt-contrib-connect": "~0.8.0",
"grunt-webpack": "~1.0.8",
"jasmine-core": "^2.3.4",
"karma": "~0.13.9",
"karma-jasmine": "^0.3.5",
"karma-phantomjs-launcher": "~0.2.1",
"karma-script-launcher": "~0.1.0",
"karma-webpack": "^1.7.0",
"style-loader": "~0.8.0",
"url-loader": "~0.5.5",
"css-loader": "~0.9.0",
"grunt-karma": "~0.12.1",
"grunt-open": "~0.2.3",
"grunt-contrib-copy": "~0.5.0",
"grunt-contrib-clean": "~0.6.0",<% if (stylesLanguage.match(/s[ac]ss/)) { %>
"sass-loader": "^1.0.1",<% } %><% if (stylesLanguage === 'less') { %>
"less-loader": "^2.0.0",<% } %><% if (stylesLanguage === 'stylus') { %>
"stylus-loader": "^0.5.0",<% } %>
"react-hot-loader": "^1.0.7",
"webpack": "~1.10.0",
"webpack-dev-server": "~1.10.0"
}
}
-75
View File
@@ -1,75 +0,0 @@
/*
* Webpack development server configuration
*
* This file is set up for serving the webpack-dev-server, which will watch for changes and recompile as required if
* the subfolder /webpack-dev-server/ is visited. Visiting the root will not automatically reload.
*/
'use strict';
var webpack = require('webpack');
module.exports = {
output: {
filename: 'main.js',
publicPath: '/assets/'
},
cache: true,
debug: true,
devtool: 'sourcemap',
entry: [
'webpack/hot/only-dev-server',
'./src/components/<% if (reactRouter) { %>main<% } else { %><%= scriptAppName %><% } %>.js'
],
stats: {
colors: true,
reasons: true
},
resolve: {
extensions: ['', '.js', '.jsx'],
alias: {
'styles': __dirname + '/src/styles',
'mixins': __dirname + '/src/mixins',
'components': __dirname + '/src/components/'<% if(architecture==='flux'||architecture=='reflux') { %>,
'stores': __dirname + '/src/stores/',
'actions': __dirname + '/src/actions/'<% } %>
}
},
module: {
preLoaders: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'eslint-loader'
}],
loaders: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'react-hot!babel-loader'
},<% if (stylesLanguage === 'sass') { %> {
test: /\.sass/,
loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded&indentedSyntax'
},<% } %><% if (stylesLanguage === 'scss') { %> {
test: /\.scss/,
loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded'
},<% } %><% if (stylesLanguage === 'less') { %> {
test: /\.less/,
loader: 'style-loader!css-loader!less-loader'
},<% } %><% if (stylesLanguage === 'stylus') { %> {
test: /\.styl/,
loader: 'style-loader!css-loader!stylus-loader'
},<% } %> {
test: /\.css$/,
loader: 'style-loader!css-loader'
}, {
test: /\.(png|jpg|woff|woff2)$/,
loader: 'url-loader?limit=8192'
}]
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
};
-77
View File
@@ -1,77 +0,0 @@
/*
* Webpack distribution configuration
*
* This file is set up for serving the distribution version. It will be compiled to dist/ by default
*/
'use strict';
var webpack = require('webpack');
module.exports = {
output: {
publicPath: '/assets/',
path: 'dist/assets/',
filename: 'main.js'
},
debug: false,
devtool: false,
entry: './src/components/<% if (reactRouter) { %>main<% } else { %><%= scriptAppName %><% } %>.js',
stats: {
colors: true,
reasons: false
},
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.AggressiveMergingPlugin(),
new webpack.NoErrorsPlugin()
],
resolve: {
extensions: ['', '.js', '.jsx'],
alias: {
'styles': __dirname + '/src/styles',
'mixins': __dirname + '/src/mixins',
'components': __dirname + '/src/components/'<% if(architecture==='flux'||architecture=='reflux') { %>,
'stores': __dirname + '/src/stores/',
'actions': __dirname + '/src/actions/'<% } %>
}
},
module: {
preLoaders: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'eslint-loader'
}],
loaders: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader'
}, {
test: /\.css$/,
loader: 'style-loader!css-loader'
},<% if (stylesLanguage === 'sass') { %> {
test: /\.sass/,
loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded&indentedSyntax'
},<% } %><% if (stylesLanguage === 'scss') { %> {
test: /\.scss/,
loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded'
},<% } %><% if (stylesLanguage === 'less') { %> {
test: /\.less/,
loader: 'style-loader!css-loader!less-loader'
},<% } %><% if (stylesLanguage === 'stylus') { %> {
test: /\.styl/,
loader: 'style-loader!css-loader!stylus-loader'
},<% } %> {
test: /\.(png|jpg|woff|woff2)$/,
loader: 'url-loader?limit=8192'
}]
}
};
-33
View File
@@ -1,33 +0,0 @@
### SublimeText ###
*.sublime-workspace
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# App specific
node_modules/
.tmp
dist
/src/main.js
-23
View File
@@ -1,23 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
</head>
<body>
<!--[if lt IE 8]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<div id="content">
<h1>If you can see this, something is broken (or JS is not enabled)!!.</h1>
</div>
<script>
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__
</script>
<script type="text/javascript" src="assets/main.js"></script>
</body>
</html>
-93
View File
@@ -1,93 +0,0 @@
'use strict';
var path = require('path');
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'test/helpers/pack/**/*.js',
'test/helpers/react/**/*.js',
'test/spec/components/**/*.js'<% if(architecture === 'flux'||architecture === 'reflux') { %>,
'test/spec/stores/**/*.js',
'test/spec/actions/**/*.js'<% } %>
],
preprocessors: {
'test/helpers/createComponent.js': ['webpack'],
'test/spec/components/**/*.js': ['webpack'],
'test/spec/components/**/*.jsx': ['webpack']<% if(architecture === 'flux'||architecture === 'reflux') { %>,
'test/spec/stores/**/*.js': ['webpack'],
'test/spec/actions/**/*.js': ['webpack']<% } %>
},
webpack: {
cache: true,
module: {
loaders: [{
test: /\.gif/,
loader: 'url-loader?limit=10000&mimetype=image/gif'
}, {
test: /\.jpg/,
loader: 'url-loader?limit=10000&mimetype=image/jpg'
}, {
test: /\.png/,
loader: 'url-loader?limit=10000&mimetype=image/png'
}, {
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /node_modules/
},<% if (stylesLanguage === 'sass') { %> {
test: /\.sass/,
loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded'
},<% } %><% if (stylesLanguage === 'scss') { %> {
test: /\.scss/,
loader: 'style-loader!css-loader!sass-loader?outputStyle=expanded'
},<% } %><% if (stylesLanguage === 'less') { %> {
test: /\.less/,
loader: 'style-loader!css-loader!less-loader'
},<% } %><% if (stylesLanguage === 'stylus') { %> {
test: /\.styl/,
loader: 'style-loader!css-loader!stylus-loader'
},<% } %> {
test: /\.css$/,
loader: 'style-loader!css-loader'
}, {
test: /\.woff/,
loader: 'url-loader?limit=10000&mimetype=application/font-woff'
}, {
test: /\.woff2/,
loader: 'url-loader?limit=10000&mimetype=application/font-woff2'
}]
},
resolve: {
alias: {
'styles': path.join(process.cwd(), './src/styles/'),
'components': path.join(process.cwd(), './src/components/')<% if(architecture === 'flux'||architecture === 'reflux') { %>,
'stores': '../../../src/stores/',
'actions': '../../../src/actions/'<% } %>,
'helpers': path.join(process.cwd(), './test/helpers/')
}
}
},
webpackMiddleware: {
noInfo: true,
stats: {
colors: true
}
},
exclude: [],
port: 8080,
logLevel: config.LOG_INFO,
colors: true,
autoWatch: false,
browsers: ['PhantomJS'],
reporters: ['dots'],
captureTimeout: 60000,
singleRun: true,
plugins: [
require('karma-webpack'),
require('karma-jasmine'),
require('karma-phantomjs-launcher')
]
});
};
-13
View File
@@ -1,13 +0,0 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
-21
View File
@@ -1,21 +0,0 @@
{
"plugins": [
"react"
],
"ecmaFeatures": {
"jsx": true,
"modules": true
},
"env": {
"browser": true,
"amd": true,
"es6": true
},
"rules": {
"quotes": [ 1, "single" ],
"no-undef": false,
"global-strict": false,
"no-extra-semi": 1,
"no-underscore-dangle": false
}
}
-27
View File
@@ -1,27 +0,0 @@
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": false,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "false",
"regexp": true,
"undef": true,
"unused": false,
"strict": true,
"trailing": true,
"smarttabs": true,
"white": true,
"newcap": false,
"globals": {
"React": true
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

-40
View File
@@ -1,40 +0,0 @@
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": false,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "false",
"regexp": true,
"undef": true,
"unused": false,
"strict": true,
"trailing": true,
"smarttabs": true,
"white": true,
"newcap": false,
"globals": {
"after": false,
"afterEach": false,
"react": false,
"before": false,
"beforeEach": false,
"browser": false,
"describe": false,
"expect": false,
"inject": false,
"it": false,
"spyOn": false,
"jasmine": false,
"spyOnConstructor": false,
"React": true
}
}
@@ -1,27 +0,0 @@
/**
* Function to get the shallow output for a given component
* As we are using phantom.js, we also need to include the fn.proto.bind shim!
*
* @see http://simonsmith.io/unit-testing-react-components-without-a-dom/
* @author somonsmith
*/
// Add missing methods to phantom.js
import './pack/phantomjs-shims';
import React from 'react/addons';
const TestUtils = React.addons.TestUtils;
/**
* Get the shallow rendered component
*
* @param {Object} component The component to return the output for
* @param {Object} props [optional] The components properties
* @param {Mixed} ...children [optional] List of children
* @return {Object} Shallow rendered output
*/
export default function createComponent(component, props = {}, ...children) {
const shallowRenderer = TestUtils.createRenderer();
shallowRenderer.render(React.createElement(component, props, children.length > 1 ? children : children[0]));
return shallowRenderer.getRenderOutput();
}
@@ -1,34 +0,0 @@
(function() {
var Ap = Array.prototype;
var slice = Ap.slice;
var Fp = Function.prototype;
if (!Fp.bind) {
// PhantomJS doesn't support Function.prototype.bind natively, so
// polyfill it whenever this module is required.
Fp.bind = function(context) {
var func = this;
var args = slice.call(arguments, 1);
function bound() {
var invokedAsConstructor = func.prototype && (this instanceof func);
return func.apply(
// Ignore the context parameter when invoking the bound function
// as a constructor. Note that this includes not only constructor
// invocations using the new keyword but also calls to base class
// constructors such as BaseClass.call(this, ...) or super(...).
!invokedAsConstructor && context || this,
args.concat(slice.call(arguments))
);
}
// The bound function must share the .prototype of the unbound
// function so that any object created by one constructor will count
// as an instance of both constructors.
bound.prototype = func.prototype;
return bound;
};
}
})();
File diff suppressed because it is too large Load Diff
-11
View File
@@ -1,11 +0,0 @@
var alt = require('../alt');
<% if (es6) { %>class <%= classedName %> {
}; <% }
else { %>var <%= classedName %> = alt.createActions(function () {
}); <% } %>
<% if (es6) { %>export default alt.createActions(<%= classedName %>); <% }
else { %>module.exports = <%= classedName %>; <% } %>
-21
View File
@@ -1,21 +0,0 @@
var alt = require('../alt');
<% if (es6) { %>class <%= classedName %> {
constructor() {
this.bindListeners({
});
}
} <% }
else { %>var <%= classedName %> = alt.createStore({
bindListeners: {
}
}); <% } %>
<% if (es6) { %>export default alt.createStore(<%= classedName %>, '<%= classedName %>'); <% }
else { %>module.exports = <%= classedName %>; <% } %>
-26
View File
@@ -1,26 +0,0 @@
'use strict';
var React = require('react/addons');
var ReactTransitionGroup = React.addons.TransitionGroup;
// CSS
require('normalize.css');
require('../styles/main.css');
var imageURL = require('../images/yeoman.png');
var <%= scriptAppName %> = React.createClass({
render: function() {
return (
<div className="main">
<ReactTransitionGroup transitionName="fade">
<img src={imageURL} />
</ReactTransitionGroup>
</div>
);
}
});
<% if (!reactRouter) {
%>React.render(<<%= scriptAppName %> />, document.getElementById('content')); // jshint ignore:line
<% } %>
module.exports = <%= scriptAppName %>;
-36
View File
@@ -1,36 +0,0 @@
'use strict';
var React = require('react/addons');<% if(rich && architecture === 'reflux'){%>
var Reflux = require('Reflux');<%}%>
<% if(rich && architecture === 'flux' || architecture === 'reflux'){%>
//var Actions = require('actions/xxx')<%}%>
<% if (stylesLanguage === 'css') { %>require('styles/<%= classedFileName %>.css');<% } %><%
if (stylesLanguage === 'sass') { %>require('styles/<%= classedFileName %>.sass');<% } %><%
if (stylesLanguage === 'scss') { %>require('styles/<%= classedFileName %>.scss');<% } %><%
if (stylesLanguage === 'less') { %>require('styles/<%= classedFileName %>.less');<% } %><%
if (stylesLanguage === 'stylus') { %>require('styles/<%= classedFileName %>.styl');<% } %>
var <%= classedName %> = React.createClass({<% if(rich){%>
mixins: [<% if(architecture === 'reflux'){%>Reflux.ListenerMixin<%}%>],
getInitialState: function() {
return {};
},
getDefaultProps: function() {},
componentWillMount: function() {},
componentDidMount: function() {},
shouldComponentUpdate: function() {},
componentDidUpdate: function() {},
componentWillUnmount: function() {},<%}%>
render: function () {
return (
<div className="<%= classedName %>">
<p>Content for <%= classedName %></p>
</div>
);
}
});
<% if (es6) { %>export default <%= classedName %>;<% }
else { %>module.exports = <%= classedName %>;<% } %>
-3
View File
@@ -1,3 +0,0 @@
var Dispatcher = require('flux').Dispatcher;
module.exports = new Dispatcher();
-8
View File
@@ -1,8 +0,0 @@
'use strict';
var <%= classedName %> = {
};
<% if (es6) { %> export default <%= classedName %>; <% }
else { %>module.exports = <%= classedName %>; <% } %>
-20
View File
@@ -1,20 +0,0 @@
'use strict';
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var <%= dispatcherName %> = require('../dispatcher/<%= dispatcherName %>');
var <%= classedName %> = assign({}, EventEmitter.prototype, {
});
<%= classedName %>.dispatchToken = <%= dispatcherName %>.register(function(action) {
switch(action.type) {
default:
}
});
<% if (es6) { %> export default <%= classedName %>; <% }
else { %>module.exports = <%= classedName %>; <% } %>
-11
View File
@@ -1,11 +0,0 @@
'use strict';
var Reflux = require('reflux');
var <%= classedName %> = Reflux.createActions([
]);
<% if (es6) { %> export default <%= classedName %>; <% }
else { %>module.exports = <%= classedName %>; <% } %>
-14
View File
@@ -1,14 +0,0 @@
'use strict';
var Reflux = require('reflux');
//var Actions = require('actions/..');
var <%= classedName %> = Reflux.createStore({
listenables: Actions,
});
<% if (es6) { %> export default <%= classedName %>; <% }
else { %>module.exports = <%= classedName %>; <% } %>
-4
View File
@@ -1,4 +0,0 @@
var Alt = require('alt');
var alt = new Alt();
module.exports = alt;
-18
View File
@@ -1,18 +0,0 @@
'use strict';
var <%= scriptAppName %> = require('./<%= scriptAppName %>');
var React = require('react');
var Router = require('react-router');
var Route = Router.Route;
var content = document.getElementById('content');
var Routes = (
<Route handler={<%= scriptAppName %>}>
<Route name="/" handler={<%= scriptAppName %>}/>
</Route>
);
Router.run(Routes, function (Handler) {
React.render(<Handler/>, content);
});
-13
View File
@@ -1,13 +0,0 @@
'use strict';
describe('<%= classedName %>', () => {
let action;
beforeEach(() => {
action = require('actions/<%= classedFileName %>.js');
});
it('should be defined', () => {
expect(action).toBeDefined();
});
});
-19
View File
@@ -1,19 +0,0 @@
'use strict';
describe('<%= classedName %>', () => {
let React = require('react/addons');
let <%= scriptAppName %>, component;
beforeEach(() => {
let container = document.createElement('div');
container.id = 'content';
document.body.appendChild(container);
<%= scriptAppName %> = require('components/<%= scriptAppName %>.js');
component = React.createElement(<%= scriptAppName %>);
});
it('should create a new instance of <%= scriptAppName %>', () => {
expect(component).toBeDefined();
});
});
-20
View File
@@ -1,20 +0,0 @@
'use strict';
// Uncomment the following lines to use the react test utilities
// import React from 'react/addons';
// const TestUtils = React.addons.TestUtils;
import createComponent from 'helpers/createComponent';
import <%= classedName %> from 'components/<%= classedFileName %><%= reactComponentSuffix %>';
describe('<%= classedName %>', () => {
let <%= classedName %>Component;
beforeEach(() => {
<%= classedName %>Component = createComponent(<%= classedName %>);
});
it('should have its component name as default className', () => {
expect(<%= classedName %>Component._store.props.className).toBe('<%= classedName %>');
});
});
-13
View File
@@ -1,13 +0,0 @@
'use strict';
describe('<%= classedName %>', () => {
let store;
beforeEach(() => {
store = require('stores/<%= classedFileName %>.js');
});
it('should be defined', () => {
expect(store).toBeDefined();
});
});
-14
View File
@@ -1,14 +0,0 @@
'use strict';
describe('main', () => {
let main, component;
beforeEach(() => {
main = require('components/main.jsx');
component = main();
});
it('should create a new instance of main', () => {
expect(component).toBeDefined();
});
});
+97
View File
@@ -0,0 +1,97 @@
'use strict';
let path = require('path');
let expect = require('chai').expect;
let assert = require('yeoman-generator').assert;
let helpers = require('yeoman-generator').test
describe('react-webpack:app', () => {
let defaultPrompts = require('../../../generators/app/prompts.js');
let prompts = {};
for(let p of defaultPrompts) {
prompts[p.name] = p.default;
}
let generator;
let generatorBase = path.join(__dirname, '../../../generators/app');
before((done) => {
helpers.run(generatorBase)
.inTmpDir()
.withOptions({
'skip-welcome-message': true,
'skip-install': true
})
.withPrompts(prompts)
.on('ready', (instance) => {
generator = instance;
})
.on('end', done);
});
describe('#config', () => {
it('should use "css" as default style language', () => {
expect(generator.config.get('style')).to.equal('css');
});
});
describe('#createFiles', () => {
it('should generate dot files', () => {
assert.file([
'.editorconfig',
'.eslintrc',
'.npmignore',
'.yo-rc.json'
]);
});
it('should generate project configuration files', () => {
assert.file([
'LICENSE',
'package.json'
]);
});
it('should generate the webpack configuration', () => {
assert.file([
'cfg/base.js',
'cfg/dev.js',
'cfg/dist.js',
'cfg/test.js',
'server.js',
'webpack.config.js'
]);
});
it('should generate required source files', () => {
assert.file([
'src/actions/README.md',
'src/components/Main.js',
'src/components/run.js',
'src/favicon.ico',
'src/images/yeoman.png',
'src/index.html',
'src/sources/README.md',
'src/stores/README.md',
'src/styles/App.css'
]);
});
it('should generate test configuration and basic tests', () => {
assert.file([
'karma.conf.js',
'test/components/MainTest.js',
'test/helpers/shallowRenderHelper.js',
'test/loadtests.js'
]);
});
});
});
+283
View File
@@ -0,0 +1,283 @@
'use strict';
let path = require('path');
let assert = require('yeoman-generator').assert;
let helpers = require('yeoman-generator').test
describe('react-webpack:component', () => {
let generatorComponent = path.join(__dirname, '../../../generators/component');
/**
* Return a newly generated component with given name and style
* @param {String} name
* @param {String} styleType
* @param {Function} callback
*/
function createGeneratedComponent(name, styleType, callback) {
helpers.run(generatorComponent)
.withArguments([name])
.on('ready', (instance) => {
instance.config.set('style', styleType);
})
.on('end', callback);
}
describe('When using style type "css"', () => {
describe('Setup', () => {
it('should create the react component, its stylesheet and test file', (done) => {
createGeneratedComponent('mycomponent', 'css', () => {
assert.file([
'src/components/MycomponentComponent.js',
'src/styles/Mycomponent.css',
'test/components/MycomponentComponentTest.js'
]);
done();
});
});
});
describe('Component', () => {
it('should require the created css file', (done) => {
createGeneratedComponent('mycomponent', 'css', () => {
assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.css\');');
done();
});
});
});
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
});
+2
View File
@@ -0,0 +1,2 @@
--reporter spec
--recursive
-342
View File
@@ -1,342 +0,0 @@
/*global describe, beforeEach, it*/
'use strict';
var path = require('path');
var helpers = require('yeoman-generator').test;
var assert = require('yeoman-generator').assert;
var _ = require('underscore.string');
describe('react-webpack generator', function() {
var react;
var expected = [
'src/favicon.ico',
'src/styles/main.css',
'src/index.html',
'Gruntfile.js',
'webpack.config.js',
'karma.conf.js',
'package.json'
];
var mockPrompts = {};
var genOptions = {
'appPath': 'src',
'skip-install': true,
'skip-welcome-message': true,
'skip-message': true
};
var deps = [
'../../app',
'../../common',
'../../component',
'../../main'
];
beforeEach(function(done) {
helpers.testDirectory(path.join(__dirname, 'temp-test'), function(err) {
if (err) {
return done(err);
}
react = helpers.createGenerator('react-webpack:app', deps, false, genOptions);
helpers.mockPrompt(react, mockPrompts);
done();
});
});
describe('App files', function() {
it('should generate dotfiles', function(done) {
react.run({}, function() {
helpers.assertFile([].concat(expected, [
'.yo-rc.json',
'.editorconfig',
'.gitignore',
'.jshintrc'
]));
done();
});
});
it('should generate app files', function(done) {
react.run({}, function() {
// TODO: Hack, no time to work out why generated
// files not present at point of test...
setTimeout(function() {
helpers.assertFile(expected);
done();
});
});
});
it('should generate expected JS files', function(done) {
react.run({}, function() {
setTimeout(function() {
helpers.assertFile([].concat(expected, [
'src/components/TempTestApp.js',
'src/components/main.js'
]));
done();
});
});
});
it('should generate expected test JS files', function(done) {
react.run({}, function() {
// TODO: Hack, no time to work out why generated
// files not present at point of test...
setTimeout(function() {
helpers.assertFile([].concat(expected, [
'test/helpers/pack/phantomjs-shims.js',
'test/helpers/createComponent.js',
'test/helpers/react/addons.js',
'test/spec/components/TempTestApp.js'
]));
done();
});
});
});
it('should use HMR webpack API inside of configs', function (done) {
react.run({}, function() {
assert.fileContent([
['package.json', /react-hot-loader/],
['Gruntfile.js', /hot:\s*true/],
['webpack.config.js', /react-hot/],
['webpack.config.js', /webpack\.HotModuleReplacementPlugin/],
['webpack.config.js', /webpack\/hot\/only-dev-server/]
]);
done();
});
});
it('should generate JS config with aliases', function(done) {
react.run({}, function() {
assert.fileContent([
// style aliases
['webpack.config.js', /resolve[\S\s]+alias[\S\s]+styles/m],
['karma.conf.js', /resolve[\S\s]+alias[\S\s]+styles/m],
['webpack.dist.config.js', /resolve[\S\s]+alias[\S\s]+styles/m],
// script/components aliases
['webpack.config.js', /resolve[\S\s]+alias[\S\s]+components/m],
['karma.conf.js', /resolve[\S\s]+alias[\S\s]+components/m],
['webpack.dist.config.js', /resolve[\S\s]+alias[\S\s]+components/m]
]);
done();
});
});
it('should not have any flux assets configured', function(done) {
react.run({}, function() {
assert.noFileContent([
['package.json', /flux/],
['package.json', /events/],
['package.json', /object-assign/],
['karma.conf.js', /resolve[\S\s]+alias[\S\s]+stores/m],
['webpack.config.js', /resolve[\S\s]+alias[\S\s]+stores/m],
['webpack.dist.config.js', /resolve[\S\s]+alias[\S\s]+stores/m]
]);
assert.noFile('src/dispatcher/TempTestAppDispatcher.js');
done();
});
});
});
describe('Generator', function () {
it('should contain info about used style lang', function (done) {
react.run({}, function() {
assert.ok(react.config.get('styles-language'));
done();
});
});
it('by default should use css style lang', function (done) {
react.run({}, function() {
assert.equal(react.config.get('styles-language'), 'css');
done();
});
});
var assertStyle = function (lang, done) {
helpers.mockPrompt(react, {
stylesLanguage: lang
});
react.run({}, function() {
assert.equal(react.config.get('styles-language'), lang);
done();
});
};
it('should use sass style lang', function (done) {
assertStyle('sass', done);
});
it('should use scss style lang', function (done) {
assertStyle('scss', done);
});
it('should use less style lang', function (done) {
assertStyle('less', done);
});
it('should use stylus style lang', function (done) {
assertStyle('stylus', done);
});
});
describe('When using Flux', function() {
beforeEach(function(done) {
helpers.mockPrompt(react, {
architecture: 'flux'
});
react.run({}, function() {
done();
})
});
it('should add flux, events, and object-assign packages', function(done) {
assert.fileContent([
['package.json', /flux/],
['package.json', /events/],
['package.json', /object-assign/]
]);
done();
});
it('should add stores and actions alias to karma config', function(done) {
assert.fileContent([
['karma.conf.js', /resolve[\S\s]+alias[\S\s]+stores/m]
]);
done();
});
it('should add stores and actions alias to webpack configs', function(done) {
assert.fileContent([
['webpack.config.js', /resolve[\S\s]+alias[\S\s]+stores/m],
['webpack.dist.config.js', /resolve[\S\s]+alias[\S\s]+stores/m]
]);
done();
});
it('should have a Dispatcher generated', function(done) {
setTimeout(function(){
assert.file('src/dispatcher/TempTestAppDispatcher.js');
done();
});
})
});
describe('When generating a Component', function() {
var generatorTest = function(name, generatorType, specType, targetDirectory, scriptNameFn, specNameFn, suffix, done) {
var deps = [path.join('../..', generatorType)];
genOptions.appPath = 'src';
var reactGenerator = helpers.createGenerator('react-webpack:' + generatorType, deps, [name], genOptions);
react.run([], function() {
reactGenerator.run([], function() {
helpers.assertFileContent([
[path.join('src', targetDirectory, name + '.js'), new RegExp('var ' + scriptNameFn(name) + suffix, 'g')],
[path.join('src', targetDirectory, name + '.js'), new RegExp('require\\(\'styles\\/' + name + suffix + '\\.[^\']+' + '\'\\)', 'g')],
[path.join('test/spec', targetDirectory, 'TempTestApp' + '.js'), new RegExp('require\\(\'components\\/' + 'TempTestApp' + suffix + '\\.[^\']+' + '\'\\)', 'g')],
[path.join('test/spec', targetDirectory, name + '.js'), new RegExp('import ' + scriptNameFn(name) + ' from \'components\/Foo', 'g')],
[path.join('test/spec', targetDirectory, name + '.js'), new RegExp('describe\\(\'' + specNameFn(name) + suffix + '\'', 'g')]
]);
done();
});
});
}
it('should generate a new component', function(done) {
react.run({}, function() {
generatorTest('Foo', 'component', 'component', 'components', _.capitalize, _.capitalize, '', done);
});
});
it('should generate a subcomponent', function(done) {
react.run({}, function() {
var subComponentNameFn = function () { return 'Bar'; };
generatorTest('Foo/Bar', 'component', 'component', 'components', subComponentNameFn, subComponentNameFn, '', done);
});
});
});
describe('When generating an Action', function() {
beforeEach(function(done){
helpers.mockPrompt(react, {
architecture: 'flux'
});
react.run({}, function() {
var generator =
helpers.createGenerator(
'react-webpack:action',
[path.join('../../action')],
['Test'],
{ appPath: 'src' }
);
react.run([], function() {
generator.run([], function() {
done();
})
});
});
});
it('should generate a new action with tests', function(done) {
assert.fileContent([
['src/actions/TestActionCreators.js', /var TestActionCreators/g],
['test/spec/actions/TestActionCreators.js', /require\('actions\/TestActionCreators.js'\)/g],
['test/spec/actions/TestActionCreators.js', /describe\('TestActionCreators'/g]
]);
done();
});
});
describe('When generating a Store', function() {
beforeEach(function(done) {
helpers.mockPrompt(react, {
architecture: 'flux'
});
react.run({}, function() {
var generator =
helpers.createGenerator(
'react-webpack:store',
[path.join('../../store')],
['Test'],
{ appPath: 'src' }
);
react.run([], function() {
generator.run([], function() {
done();
});
});
});
});
it('should generate a new store with tests', function(done) {
assert.fileContent([
['src/stores/TestStore.js', /var TestStore/g],
['test/spec/stores/TestStore.js', /require\('stores\/TestStore.js'\)/g],
['test/spec/stores/TestStore.js', /describe\('TestStore'/g]
]);
done();
});
});
});
+65
View File
@@ -0,0 +1,65 @@
'use strict';
let expect = require('chai').expect;
let utils = require('../../utils/config');
let originalSettings = require('../../utils/configopts.json');
describe('Utilities:Config', () => {
describe('#getSetting', () => {
it('should return "null" if the key could not be found', () => {
expect(utils.getSetting('bogus')).to.be.null;
});
it('should return a settings object if it exists', () => {
let result = utils.getSetting('style');
expect(result).to.be.an.object;
expect(result).to.deep.equal(originalSettings.style);
});
});
describe('#getChoices', () => {
it('should return "null" if the key could not be found', () => {
expect(utils.getChoices('bogus')).to.be.null;
});
it('should return an array of choices when queried correctly', () => {
let result = utils.getChoices('style');
expect(result).to.be.an.array;
expect(result).to.deep.equal(originalSettings.style.options);
});
});
describe('#getChoiceByKey', () => {
it('should return "null" if the key or the setting could not be found', () => {
expect(utils.getChoiceByKey('bogus', 'unknown')).to.be.null;
expect(utils.getChoiceByKey('style', 'unknown')).to.be.null;
});
it('should return the configured object when it can be found', () => {
expect(utils.getChoiceByKey('style', 'css')).to.equal(originalSettings.style.options[0]);
expect(utils.getChoiceByKey('style', 'less')).to.equal(originalSettings.style.options[3]);
});
});
describe('#getDefaultChoice', () => {
it('should return "null" if the key could not be found', () => {
expect(utils.getDefaultChoice('bogus')).to.be.null;
});
it('should return the default choice when queried correctly', () => {
let result = utils.getDefaultChoice('style');
expect(result).to.equal(originalSettings.style.default);
});
});
});
+97
View File
@@ -0,0 +1,97 @@
'use strict';
let expect = require('chai').expect;
let path = require('path');
let utils = require('../../utils/yeoman');
let baseDir = path.basename(process.cwd());
describe('Utilities:Yeoman', () => {
describe('#getBaseDir', () => {
it('should return the current run directory', () => {
expect(utils.getBaseDir()).to.equal(baseDir);
});
});
describe('#getCleanedPathName', () => {
it('should return normalized paths', () => {
let tests = {
'my/full œ!/path!': 'my/full/path',
'Test': 'test',
'I am a Test Component!': 'iAmATestComponent',
'A very\^specialChary!@componentName with !!!': 'aVerySpecialCharyComponentNameWith'
};
for(let test in tests) {
expect(utils.getCleanedPathName(test)).to.be.equal(tests[test]);
expect(utils.getCleanedPathName(test, 'suffix')).to.be.equal(tests[test] + 'Suffix');
}
});
});
describe('#getAppName', () => {
it('should return a js friendly application name', () => {
let result = utils.getAppName('this is a test using % special / chars!');
expect(result).to.be.equal('thisIsATestUsingSpecialChars');
});
it('should use the current path for creating the appName if the argument is omitted', () => {
let resultWithoutArgs = utils.getAppName();
let resultWithArgs = utils.getAppName(baseDir);
expect(resultWithoutArgs).to.be.equal(resultWithArgs);
});
});
describe('#getComponentStyleName', () => {
it('should return a components css className', () => {
let tests = {
'my/full œ!/path!': 'path-component',
'Test': 'test-component',
'I am a Test Component!': 'i-am-a-test-component-component',
'A very\^specialChary!@componentName with !!!': 'a-very-specialchary-componentname-with-component'
};
for(let test in tests) {
expect(utils.getComponentStyleName(test)).to.be.equal(tests[test]);
}
});
});
describe('#getAllSettingsFromComponentName', () => {
it('should get all required information for component creation from the components name', () => {
let expection = {
style: {
webpackPath: 'styles/my/component/Test.css',
path: 'src/styles/my/component/',
fileName: 'Test.css',
className: 'test-component',
suffix: '.css'
},
component: {
webpackPath: 'components/my/component/TestComponent.js',
path: 'src/components/my/component/',
fileName: 'TestComponent.js',
className: 'TestComponent',
suffix: '.js'
},
test: {
path: 'test/components/my/component/',
fileName: 'TestComponentTest.js'
}
};
expect(utils.getAllSettingsFromComponentName('my/component/test')).to.deep.equal(expection);
});
});
});
-95
View File
@@ -1,95 +0,0 @@
'use strict';
var path = require('path');
var fs = require('fs');
module.exports = {
rewrite: rewrite,
rewriteFile: rewriteFile,
appName: appName,
capitalize: capitalize,
capitalizeClass: capitalizeClass,
capitalizeFile: capitalizeFile
};
function rewriteFile (args) {
args.path = args.path || process.cwd();
var fullPath = path.join(args.path, args.file);
args.haystack = fs.readFileSync(fullPath, 'utf8');
var body = rewrite(args);
fs.writeFileSync(fullPath, body);
}
function escapeRegExp (str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
}
function rewrite (args) {
// check if splicable is already in the body text
var re = new RegExp(args.splicable.map(function (line) {
return '\s*' + escapeRegExp(line);
}).join('\n'));
if (re.test(args.haystack)) {
return args.haystack;
}
var lines = args.haystack.split('\n');
var otherwiseLineIndex = 0;
lines.forEach(function (line, i) {
if (line.indexOf(args.needle) !== -1) {
otherwiseLineIndex = i;
}
});
var spaces = 0;
while (lines[otherwiseLineIndex].charAt(spaces) === ' ') {
spaces += 1;
}
var spaceStr = '';
while ((spaces -= 1) >= 0) {
spaceStr += ' ';
}
lines.splice(otherwiseLineIndex, 0, args.splicable.map(function (line) {
return spaceStr + line;
}).join('\n'));
return lines.join('\n');
}
function capitalize(str) {
str = String(str);
return str[0].toUpperCase() + str.substr(1, str.length);
}
function capitalizeClass(string) {
var words = string.split('/');
words.push(capitalize(words.pop()));
return words.pop();
}
function capitalizeFile(string) {
var words = string.split('/');
words.push(capitalize(words.pop()));
return words.join('/');
}
function appName(self) {
var counter = 0, suffix = self.options['app-suffix'];
// Have to check this because of generator bug #386
process.argv.forEach(function (val) {
if (val.indexOf('--app-suffix') > -1) {
counter++;
}
});
if (counter === 0 || (typeof suffix === 'boolean' && suffix)) {
suffix = 'App';
}
return suffix ? self._.classify(suffix) : '';
}
+9
View File
@@ -0,0 +1,9 @@
'use strict';
let config = require('./config');
let yeoman = require('./yeoman');
module.exports = {
config: config,
yeoman: yeoman
};
+68
View File
@@ -0,0 +1,68 @@
'use strict';
let opts = require('./configopts.json');
/**
* Get a setting
* @param {String} setting
* @return {Mixed} setting or null if not found
*/
let getSetting = (setting) => {
return opts[setting] !== undefined ? opts[setting] : null;
}
/**
* Get choices for a given setting
* @param {String} setting
* @return {Mixed} Result or null if nothing was found
*/
let getChoices = function getChoices(setting) {
let config = getSetting(setting);
return config && Array.isArray(config.options) ? config.options : null;
}
/**
* Get the wanted choice by key
* @param {String} setting
* @param {String} key
* @return {Object}
*/
let getChoiceByKey = (setting, key) => {
let choices = getChoices(setting);
if(!choices) {
return null;
}
let result = null;
for(let choice of choices) {
if(choice.name === key) {
result = choice;
break;
}
}
return result;
}
/**
* Get the default choice for a config setting
* @param {String} setting
* @return {Mixed}
*/
let getDefaultChoice = (setting) => {
let config = getSetting(setting);
return config && config.default !== undefined && config.default.length > 0 ? config.default : null;
}
// getChoices
// getDefault
module.exports = {
getSetting: getSetting,
getChoices: getChoices,
getChoiceByKey: getChoiceByKey,
getDefaultChoice: getDefaultChoice
};
+84
View File
@@ -0,0 +1,84 @@
{
"path": {
"options": [
{
"name": "base",
"path": "src"
},
{
"name": "action",
"path": "src/actions"
},
{
"name": "component",
"path": "src/components"
},
{
"name": "image",
"path": "src/images"
},
{
"name": "source",
"path": "src/sources"
},
{
"name": "store",
"path": "src/stores"
},
{
"name": "style",
"path": "src/styles"
},
{
"name": "test",
"path": "test"
},
{
"name": "dist",
"path": "dist"
}
]
},
"style": {
"options": [
{
"name": "css",
"value": "css",
"suffix": ".css"
},
{
"name": "sass",
"value": "sass",
"suffix": ".sass",
"packages": [
{ "name": "sass-loader", "version": "^1.0.1" }
]
},
{
"name": "scss",
"value": "scss",
"suffix": ".scss",
"packages": [
{ "name": "sass-loader", "version": "^1.0.1" }
]
},
{
"name": "less",
"value": "less",
"suffix": ".less",
"packages": [
{ "name": "less-loader", "version": "^2.0.0" }
]
},
{
"name": "stylus",
"value": "stylus",
"suffix": ".styl",
"packages": [
{ "name": "stylus-loader", "version": "^0.5.0" }
]
}
],
"default": "css"
}
}
+122
View File
@@ -0,0 +1,122 @@
'use strict';
let path = require('path');
let configUtils = require('./config');
let _ = require('underscore.string');
// Needed directory paths
const baseName = path.basename(process.cwd());
/**
* Get the base directory
* @return {String}
*/
let getBaseDir = () => {
return baseName;
};
/**
* Get all settings (paths and the like) from components name
* @param {String} componentName The components name
* @param {String} style Style language to use [optional]
* @return {Object} Component settings
*/
let getAllSettingsFromComponentName = (componentName, style) => {
if(!style) {
style = 'css';
}
// Clean up the path and pull it to parts
let cleanedPaths = getCleanedPathName(componentName);
let componentParts = cleanedPaths.split('/');
let componentBaseName = _.capitalize(componentParts.pop());
let componentPartPath = componentParts.join('/');
// Configure Styles
let stylePaths = configUtils.getChoiceByKey('path', 'style');
let styleSettings = configUtils.getChoiceByKey('style', style);
// Configure components
let componentPath = configUtils.getChoiceByKey('path', 'component');
// 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`,
suffix: '.js'
},
test: {
path: `${testPath.path}/components/${componentPartPath}/`,
fileName: `${componentBaseName}ComponentTest.js`
}
};
return settings;
};
/**
* Get a cleaned path name for a given path
* @param {String} path
* @param {String} suffix [optional]
* @return {String}
*/
let getCleanedPathName = (path, suffix) => {
if(!suffix) {
suffix = '';
}
// If we have filesystem separators, use them to build the full path
let pathArray = path.split('/');
// Build the full components name
return pathArray.map((path) => {
return _.camelize(_.slugify(_.humanize(path)));
}).join('/') + _.capitalize(suffix);
};
/**
* Get the css/less/whatever style name to use
* @param {String} path
* @return {String}
*/
let getComponentStyleName = (path) => {
let fileName = path.split('/').pop().toLowerCase();
return _.slugify(_.humanize(fileName)) + '-component';
};
/**
* Get a js friendly application name
* @param {String} appName The input application name [optional]
* @return {String}
*/
let getAppName = (appName) => {
// If appName is not given, use the current directory
if(appName === undefined) {
appName = getBaseDir();
}
return _.camelize(_.slugify(_.humanize(appName)));
};
module.exports = {
getBaseDir: getBaseDir,
getAllSettingsFromComponentName: getAllSettingsFromComponentName,
getAppName: getAppName,
getCleanedPathName: getCleanedPathName,
getComponentStyleName: getComponentStyleName
};