Compare commits

..
85 Commits
Author SHA1 Message Date
Chris 35ef93138c 3.3.0 2016-03-29 23:24:14 +02:00
Chris 4e698c51b4 Added new changelog for 3.3.0 2016-03-29 23:09:07 +02:00
Chris 832907f2fd Added minor es2015 tweaks 2016-03-29 23:01:42 +02:00
Chris 6344984d65 Added ability to save generator version for future updates 2016-03-29 23:00:55 +02:00
Chris 6b9e24c85a 3.2.4 2016-03-02 11:56:34 +01:00
Chris 561e072a9c Adjusted generated unit tests 2016-03-02 11:56:12 +01:00
Chris 56d6b9cd48 3.2.3 2016-02-23 22:14:34 +01:00
Chris 43f3bce786 Added changelog for 3.2.3 (adjusted postcss config to work with new react-webpack-template). 2016-02-23 22:14:02 +01:00
Chris f6327e1904 Merge pull request #188 from thewtex/grammar
Grammar improvement to the style prompt.
2016-01-25 08:33:00 +01:00
Matt McCormick bd5748bb89 Grammar improvement to the style prompt. 2016-01-24 10:14:47 -05:00
Chris 782a38b685 3.2.2 2016-01-13 07:57:00 +01:00
Chris 946775a22f Fix: Set correct indention for generated unit tests 2016-01-13 07:56:49 +01:00
Chris 165b4ae956 3.2.1 2015-12-28 08:21:27 +01:00
Chris cb08986a6d Updated tests for new version of react-webpack-template 2015-12-28 08:21:00 +01:00
Chris 52e7528cd2 Merge pull request #179 from stylesuxx/master
Added link to generator-react-webpack-redux to the extending section
2015-12-22 19:52:34 +01:00
stylesuxx 85c0f7f343 Added link to generator-react-webpack-redux to the extending section 2015-12-22 16:47:01 +01:00
Chris 8ee10ffd52 3.2.0 2015-12-18 08:44:52 +01:00
Chris 60bf2aae3a Adjusted generator for updated yeoman-generator to 0.22.x 2015-12-18 08:44:42 +01:00
Chris cb98c7e528 Updated sass dependencies to new versions (fixes https://github.com/newtriks/generator-react-webpack/issues/171) 2015-12-01 07:48:31 +01:00
Chris 498c1032f2 3.1.1 2015-11-24 08:42:31 +01:00
Chris 20cbcee3fe Added missing dependencies for stylus (fixes https://github.com/newtriks/generator-react-webpack/issues/170), moved additional deps to devDependencies when installing 2015-11-24 08:42:21 +01:00
Chris e74b89d8ea Fixed tests for current version (uses style-loader now) 2015-11-23 08:58:05 +01:00
Chris 4e2650f244 Updated travis configuration 2015-11-23 08:51:15 +01:00
Chris c99d3c6682 Updated travis configuration 2015-11-23 08:50:13 +01:00
Chris 9c40789c96 3.1.0 2015-11-23 08:44:18 +01:00
Chris fe4d214be9 Prepare 3.1.0 2015-11-23 08:44:01 +01:00
Chris 492d2c6947 Merge pull request #168 from stylesuxx/feature/postcss
Add basic support for PostCSS (provided by @stylesuxx)
2015-11-23 08:33:32 +01:00
stylesuxx b214930f8e Updated wording 2015-11-22 03:48:47 +01:00
stylesuxx 4596462f29 Test: Check that the base config is rewritten if PostCSS is enabled - #163 2015-11-22 03:45:27 +01:00
stylesuxx 0e3b554855 PostCSS loader has to be the third item in the pipe 2015-11-22 03:31:05 +01:00
stylesuxx 98ffadd612 Test: Do not enable PostCSS by default - #163. 2015-11-21 16:49:17 +01:00
stylesuxx 629ce71db5 Added some information about the usage of the postcss functionality - #163 2015-11-21 16:31:53 +01:00
stylesuxx 3a10311c47 css is a dilakt itself - got rid of one condition 2015-11-21 16:06:14 +01:00
stylesuxx 255d7b94d8 Refactored postcss functionality in it's own file - #163 2015-11-21 15:59:39 +01:00
stylesuxx ac6eb05171 Added dependencies for reading, traversing and converting AST 2015-11-18 18:47:05 +01:00
stylesuxx 9f427d9332 Refactored regex in favor of traversing via AST 2015-11-18 18:46:39 +01:00
Chris 003f8ce8e6 Merge pull request #167 from praseetht/master
Added support for Visual Studio Code from @praseetht
2015-11-18 08:30:23 +01:00
Chris aa468d529d 3.0.1 2015-11-18 08:20:41 +01:00
Chris 62b36813b3 Prepare 3.0.1 2015-11-18 08:20:28 +01:00
Chris e7501dccbd Removed licence from copied files
Removed unneeded .npmignore copy
Adjusted unittests to include .babelrc
2015-11-18 08:19:12 +01:00
stylesuxx f70a1eed8f Parse base config and add postcss related stuff 2015-11-18 04:33:58 +01:00
stylesuxx 312c88b0bc Added dependencies for postcss 2015-11-18 03:11:04 +01:00
Chris 7d01c86522 3.0.0 2015-11-17 07:49:16 +01:00
Chris fffd03bfa3 Added new publish shortcut scripts 2015-11-17 07:48:58 +01:00
Chris d2992321e8 Updated react-webpack-template dependency to include babel 6 support 2015-11-17 07:47:30 +01:00
Praseeth Thuvassery c897085128 Support for Visual Studio Code 2015-11-17 12:14:15 +05:30
Chris 5eee756f3e 2.2.7 2015-10-23 09:49:07 +02:00
Chris 91ac75db71 Updated software versions, added badges 2015-10-23 09:40:29 +02:00
Chris 25c67ffd33 2.2.6 2015-10-22 12:50:08 +02:00
Chris 8cdcc2c432 Prepare V2.2.6 2015-10-22 12:49:59 +02:00
Chris cee53ad6c1 Fixed documentation for stateless react components 2015-10-19 10:25:41 +02:00
Chris ad6135e48d 2.2.5 2015-10-19 08:11:34 +02:00
Chris 63f1b74fe7 Added ability to create stateless components
Cleaned up unittests
2015-10-19 08:11:22 +02:00
Chris 432d5d00de 2.2.4 2015-10-15 16:09:41 +02:00
Chris 685f6a75ce Added automatic generation of components displayName property. 2015-10-15 16:09:23 +02:00
Chris 82a080f586 2.2.3 2015-10-14 07:20:38 +02:00
Chris 6c41164d4a Updated changeling for 2.2.3 2015-10-14 07:20:23 +02:00
Chris 9eedc6fd2f Merge pull request #152 from VovanR/feature/gitignore
Add .gitignore file to generated application
2015-10-14 07:13:59 +02:00
Vladimir Rodkin 83d47bddd8 Add gitignore 2015-10-13 20:40:06 +03:00
Vladimir Rodkin 31e478f28d Add gitignore 2015-10-13 17:54:47 +03:00
Chris b95fc4344a 2.2.2 2015-10-12 16:35:14 +02:00
Chris 66b85f48d8 Fixed missing packages when using sass/less 2015-10-12 16:34:54 +02:00
Chris bf2df82159 2.2.1 2015-10-12 08:42:28 +02:00
Chris d729c3caa8 Added changelog to ignored files. 2015-10-12 08:41:47 +02:00
Chris 4f99a5a9db Added (mis-)spelling fix... again :( 2015-10-12 08:31:40 +02:00
Chris 190cfdac06 2.2.0 2015-10-12 08:29:03 +02:00
Chris ca3f923bf0 Added new version of react-webpack-template as dependency. Now using React 0.14 as base. 2015-10-12 08:28:43 +02:00
Chris 57a2ac9325 Merge pull request #150 from spencer48/patch-1
Made a few grammar and punctuation corrections
2015-10-11 11:15:58 +02:00
Spencer Handley de5cca9866 Made a few grammar and punctuation corrections
I noticed a few errors in the README. I hope you don't mind my corrections. Great repo!
2015-10-09 21:44:05 -07:00
Chris 4e8d7af872 Added new keywords to make generator findeable 2015-10-07 08:58:04 +02:00
Chris 85470145f5 Adjusted readme to include generator-react-webpack-alt 2015-10-02 08:33:09 +02:00
Chris 520db61333 Removed grunt badge. We don't use it anymore 2015-10-01 11:12:31 +02:00
Simon Bailey 260758f93b refactor: ignore temp test dir
Signed-off-by: Simon Bailey <simon@newtriks.com>
2015-10-01 09:17:02 +01:00
Chris 361ea7409d Added another utility #getDestinationClassName to get the final classname of a given path 2015-09-29 14:11:28 +02:00
Chris 47f1273828 2.1.0 2015-09-29 13:53:05 +02:00
Chris 56ff5c6281 Added new helper method #getDestinationPath to make path management in subcomponents easier 2015-09-29 13:52:58 +02:00
Chris 7269a0a315 2.0.2 2015-09-25 11:38:31 +02:00
Chris 6a3da576fa Added changes to make pr compatible with coding guidelines. 2015-09-25 11:38:24 +02:00
Chris e7bbcb1fe7 Merge pull request #146 from halkeye/patch-1
update baseRootPath to be happier with npm@3
2015-09-25 11:34:27 +02:00
Gavin Mogan 8a2fc142ba update baseRootPath to be happier with npm@3
npm@3 now flattens node_modules directory
This should be happy no matter where the module ends up
2015-09-24 23:45:12 -07:00
Chris 76f0e8e672 Added correct node version for travis 2015-09-21 21:13:28 +02:00
Chris a5c4ea4c74 2.0.1 2015-09-21 20:58:11 +02:00
Chris 9c61be0c24 Added updated readme 2015-09-21 20:58:00 +02:00
Chris 6516861e79 2.0.0 2015-09-21 20:46:20 +02:00
Chris 023d937877 Added changes for version 2.0 2015-09-21 20:45:57 +02:00
74 changed files with 1474 additions and 18556 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 -1
View File
@@ -13,4 +13,4 @@ $RECYCLE.BIN/
node_modules/ node_modules/
npm-debug.log npm-debug.log
.idea/ .idea/
/test/temp-test 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
}
+2 -2
View File
@@ -1,7 +1,7 @@
language: node_js language: node_js
sudo: false
node_js: node_js:
- '0.10' - '4.2.2'
before_install: before_install:
- currentfolder=${PWD##*/} - currentfolder=${PWD##*/}
- if [ "$currentfolder" != 'generator-react-webpack' ]; then cd .. && eval "mv $currentfolder generator-react-webpack" && cd generator-react-webpack; fi - if [ "$currentfolder" != 'generator-react-webpack' ]; then cd .. && eval "mv $currentfolder generator-react-webpack" && cd generator-react-webpack; fi
+70
View File
@@ -0,0 +1,70 @@
# generator-react-webpack - Changelog
## 3.3.0
1. Added new yeoman key "generatedWithVersion". Will be used for backwards compatibility of new major releases.
## 3.2.4
1. Added whitespaces to generated unit tests (made problems when using it with various eslint rules)
## 3.2.3
1. Adjusted postcss to work with react-webpack-template 1.4 upwards
2. Improved styling output (provided by [thewtex](https://github.com/thewtex))
## 3.2.2
1. Cleaned up formatting of unit tests
## 3.2.1
1. Updated tests for new version of react-webpack-template
## 3.2.0
1. Updated yeoman-generator package to new version 0.22.1 (some methods like generator.NamedBase are now deprecated!)
## 3.1.1
1. Added bugfix for https://github.com/newtriks/generator-react-webpack/issues/170
## 3.1.0
1. Added support for postcss (Patch provided by [stylesuxx](https://github.com/stylesuxx))
## 3.0.1
1. Unneeded files (License, .npmignore) are not copied anymore
2. Existence of .babelrc is now checked in unit tests
## 3.0.0
1. Updated react-webpack-template to 1.0.0 to include support for babel 6.
## 2.2.7
1. Updated yeoman to 0.21
2. Added some badges for the readme
## 2.2.6
1. Added new version of ```react-webpack-template``` (provides new features for continuous testing and better dist build support)
## 2.2.5
1. Added ability to create stateless components
2. Updated README with new installation instructions (need to install globally)
3. Added new tests for components (should be easier to handle in the future)
## 2.2.4
1. Added automatic generation of components displayName property. This makes it easier to keep track of components that reside in deep subfolders (like src/components/my/example/components/indexComponent) would become "index" as a displayName, but should instead be MyExampleComponentsIndexComponent instead.
## 2.2.3
1. Fixed .gitignore renaming (Patch provided by [VovanR](https://github.com/VovanR))
## 2.2.0:
1. Added new version of react-webpack-template, including support for React 0.14.
+89 -361
View File
@@ -1,405 +1,133 @@
# 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) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/newtriks/generator-react-webpack/trend.png)](https://bitdeli.com/free "Bitdeli Badge") ![Amount of Downloads per month](https://img.shields.io/npm/dm/generator-react-webpack.svg "Amount of Downloads") ![Dependency Tracker](https://img.shields.io/david/newtriks/generator-react-webpack.svg "Dependency Tracker") ![Dependency Tracker](https://img.shields.io/david/dev/newtriks/generator-react-webpack.svg "Dependency Tracker") ![Node Version](https://img.shields.io/node/v/generator-react-webpack.svg "Node Version")
> 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. > 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 React projects 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)
- Style transformations via PostCSS
- Automatic code linting via esLint
- Ability to unit test components via Karma and Mocha/Chai
Install `generator-react-webpack`: ## Changes in Version 2.0
``` This generator is written in ES2015. This means it is ___not compatible with node.js versions before 4.0___.
It also does __NOT__ include support for Flux-Frameworks anymore. Instead, we will use it as a base for other generators to build upon. This will make the base generator easier to use and update.
If you are interested, feel free to write your own generator and use generator-react-webpack as a base (via composition).
If you have built a generator using generator-react-webpack, tell us and we will add a link to our README.
## Generators that extend generator-react-webpack
- [Generator-React-Webpack-Alt](https://github.com/weblogixx/generator-react-webpack-alt) (Adds ability to create actions, stores and sources for [alt.js](http://alt.js.org))
- [Generator-React-Webpack-Redux](https://github.com/stylesuxx/generator-react-webpack-redux) (Adds ability to create actions and reducers for [Redux](https://github.com/rackt/redux))
---
## Installation
```bash
# Make sure both is installed globally
npm install -g yo
npm install -g generator-react-webpack npm install -g generator-react-webpack
``` ```
Make a new directory, and `cd` into it: ## Setting up projects
```
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:
```bash ```bash
# Create a new directory, and `cd` into it:
mkdir my-new-project && cd my-new-project
# Run the generator
yo react-webpack 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`. ## Generating new components
Example:
```bash ```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*): The above command will create a new component, as well as its stylesheet and a basic testcase.
```js
'use strict';
var React = require('react/addons'); ## Generating new stateless functional components
```
require('styles/componentName.css'); //or .sass,.less etc... yo react-webpack:component my/namespaced/components/name --stateless
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/*): Stateless functional components where introduced in React v0.14. They have a much shorter syntax than regular ones and no state or lifecycle methods at all. Please read the [React 0.14 release notes](https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html) to get more information about those components.
```js
'use strict';
// Uncomment the following lines to use the react test utilities ___Note___: You will still be able to set properties for stateless components!
// import React from 'react/addons';
// const TestUtils = React.addons.TestUtils;
import createComponent from 'helpers/createComponent'; ## Adding PostCSS plugins
import Foo from 'components/Foo.js'; If you have enabled [PostCSS](https://github.com/postcss/postcss) at generation time, install your PostCSS plugins via npm and *require* it in **postcss** function in *cfg/base.js*.
describe('Foo', () => { Example for autoprefixer:
```bash
let FooComponent; cd my-new-project
npm install autoprefixer
beforeEach(() => {
FooComponent = createComponent(Foo);
});
it('should have its component name as default className', () => {
expect(FooComponent._store.props.className).toBe('Foo');
});
});
``` ```
Require in *cfg/base.js*
And `src/styles/Foo.css` (or .sass, .less etc...) : ```JavaScript
```css ...
.Foo { postcss: function () {
border: 1px dashed #f00; return [
require('autoprefixer')({
browsers: ['last 2 versions', 'ie >= 8']
})
];
} }
...
``` ```
### rich flag ## Usage
The following commands are available in your project:
For all you lazy programmers out there, we've added another shortcut - `rich` flag:
```bash ```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 ### Naming Components
We have opted to follow [@floydophone](https://twitter.com/floydophone) convention of uppercase for component file naming e.g. [Component.js](https://github.com/petehunt/ReactHack/tree/master/src/components). I am open to suggestions if there is a general objection to this decision.
I have opted to follow [@floydophone](https://twitter.com/floydophone) convention of uppercase for component file naming e.g. [Component.js](https://github.com/petehunt/ReactHack/tree/master/src/components). I am open to suggestions if there is a general objection to this decision.
### Modules ### Modules
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. 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 ## Props
Thanks to [Edd Hannay](https://github.com/eddhannay) for his Webpack optimisations, my local merge and testing meant his additions lost his signature (my fault, sorry). So, big thanks Edd.
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.
Thanks to [Edd Hannay](https://github.com/eddhannay) for his Webpack optimisations, my local merge and testing meant his additions lost his signature (my fault sorry) so big thanks Edd.
## Contribute ## Contribute
Contributions are welcomed. When submitting a bugfix, write a test that exposes the bug and fails before applying your fix. Submit the test alongside the fix. Contributions are welcomed. When submitting a bugfix, write a test that exposes the bug and fails before applying your fix. Submit the test alongside the fix.
### Running Tests ### Running Tests
`npm test` or `node node_modules/.bin/mocha`
`node node_modules/.bin/mocha`
## License ## License
[BSD license](http://opensource.org/licenses/bsd-license.php) [BSD license](http://opensource.org/licenses/bsd-license.php)
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/newtriks/generator-react-webpack/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
-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
);
};
+151
View File
@@ -0,0 +1,151 @@
'use strict';
let generator = require('yeoman-generator');
let utils = require('../../utils/all');
let prompts = require('./prompts');
let path = require('path');
let fs = require('fs');
const packageInfo = require('../../package.json');
// Set the base root directory for our files
let baseRootPath = path.dirname(require.resolve('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;
this.postcss = props.postcss
this.generatedWithVersion = packageInfo.version.split('.').unshift();
// Set needed keys into config
this.config.set('appName', this.appName);
this.config.set('appPath', this.appPath);
this.config.set('style', this.style);
this.config.set('postcss', this.postcss);
this.config.set('generatedWithVersion', this.generatedWithVersion);
this.config.save();
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.devDependencies[dependency.name] = dependency.version;
}
}
// Add postcss module if enabled
let postcssConfig = utils.config.getChoiceByKey('postcss', 'postcss');
if(this.postcss && postcssConfig && postcssConfig.packages) {
for(let dependency of postcssConfig.packages) {
packageSettings.devDependencies[dependency.name] = dependency.version;
}
}
this.fs.writeJSON(this.destinationPath('package.json'), packageSettings);
},
writing: function() {
let excludeList = [
'LICENSE',
'README.md',
'CHANGELOG.md',
'node_modules',
'package.json',
'.travis.yml'
];
// 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 {
if (item === '.npmignore') {
this.copy(item, '.gitignore');
} else {
this.copy(item, item);
}
}
}
});
},
install: function() {
if(this.postcss) {
let postcss = require('./postcss');
postcss.write(path.join(this.destinationRoot(), 'cfg/defaults.js'));
}
if(!this.options['skip-install']) {
this.installDependencies({ bower: false });
}
}
});
+39
View File
@@ -0,0 +1,39 @@
'use strict';
let fs = require('fs');
let esprima = require('esprima');
let walk = require('esprima-walk');
let escodegen = require('escodegen');
module.exports = {
write: function(path) {
let baseConfigPath = path;
let cssDialects = ['/\\.css$/', '/\\.sass/', '/\\.scss/', '/\\.less/', '/\\.styl/'];
let postcss = 'var postcss = { postcss: function() { return []; } }';
postcss = esprima.parse(postcss);
postcss = postcss.body[0].declarations[0].init.properties[0];
let data = fs.readFileSync(baseConfigPath, 'utf8');
let parsed = esprima.parse(data);
walk.walkAddParent(parsed, function(node) {
if(node.type === 'AssignmentExpression' && node.left.object.name === 'module') {
node.right.properties.push(postcss);
}
if(node.type === 'Property' && node.key.name === 'test') {
if(cssDialects.indexOf(node.value.raw) > -1) {
let current = node.parent.properties[1].value.value;
let position = current.split('!', 2).join('!').length;
current = [current.slice(0, position), '!postcss-loader', current.slice(position)].join('');
node.parent.properties[1].value.value = current;
}
}
});
let options = { format: { indent: { style: ' ' } } };
let code = escodegen.generate(parsed, options);
fs.writeFileSync(baseConfigPath, code, 'utf8');
}
}
+24
View File
@@ -0,0 +1,24 @@
'use strict';
const 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 style language do you want to use?',
choices: utils.config.getChoices('style'),
default: utils.config.getDefaultChoice('style')
},
{
type: 'confirm',
name: 'postcss',
message: 'Enable postcss?',
default: false
}
];
+42
View File
@@ -0,0 +1,42 @@
'use strict';
let generator = require('yeoman-generator');
let utils = require('../../utils/all');
module.exports = generator.Base.extend({
constructor: function() {
generator.Base.apply(this, arguments);
this.argument('name', { type: String, required: true });
this.option('stateless', {
desc: 'Create a stateless component instead of a full one',
defaults: false
});
},
writing: function() {
let settings = utils.yeoman.getAllSettingsFromComponentName(this.name, this.config.get('style'));
let componentType = this.options.stateless ? 'Stateless' : 'Base';
// 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/${componentType}.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,23 @@
'use strict';
import React from 'react';
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>
);
}
}
<%= component.className %>.displayName = '<%= component.displayName %>';
// Uncomment properties you need
// <%= component.className %>.propTypes = {};
// <%= component.className %>.defaultProps = {};
export default <%= component.className %>;
@@ -0,0 +1,19 @@
'use strict';
import React from 'react';
require('<%= style.webpackPath %>');
let <%= component.className %> = (props) => (
<div className="<%= style.className %>">
Please edit <%= component.path %>/<%= component.fileName %> to update this component!
</div>
);
<%= component.className %>.displayName = '<%= component.displayName %>';
// Uncomment properties you need
// <%= component.className %>.propTypes = {};
// <%= component.className %>.defaultProps = {};
export default <%= component.className %>;
@@ -1,3 +1,3 @@
.<%= classedName %> { .<%= style.className %> {
border: 1px dashed #f00; border: 1px dashed #f00;
} }
@@ -1,3 +1,3 @@
.<%= classedName %> { .<%= style.className %> {
border: 1px dashed #f00; border: 1px dashed #f00;
} }
@@ -1,2 +1,2 @@
.<%= classedName %> .<%= style.className %>
border: 1px dashed #f00 border: 1px dashed #f00
@@ -1,3 +1,3 @@
.<%= classedName %> { .<%= style.className %> {
border: 1px dashed #f00; border: 1px dashed #f00;
} }
@@ -1,2 +1,2 @@
.<%= classedName %> .<%= style.className %>
border 1px dashed #f00 border 1px dashed #f00
@@ -0,0 +1,22 @@
/* eslint-env node, mocha */
/* global expect */
/* eslint no-console: 0 */
'use strict';
// Uncomment the following lines to use the react test utilities
// import TestUtils from 'react-addons-test-utils';
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.props.className).to.equal('<%= style.className %>');
});
});
+7
View File
@@ -0,0 +1,7 @@
//Support for Visual Studio Code https://code.visualstudio.com/docs/languages/javascript
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs"
}
}
-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');
}
};
+29 -18
View File
@@ -1,14 +1,16 @@
{ {
"name": "generator-react-webpack", "name": "generator-react-webpack",
"version": "1.2.13", "version": "3.3.0",
"description": "Yeoman generator for ReactJS and Webpack", "description": "Yeoman generator for using React with Webpack via Babel",
"keywords": [ "keywords": [
"yeoman-generator", "yeoman-generator",
"react",
"reactjs", "reactjs",
"webpack", "webpack",
"scaffold", "scaffold",
"framework", "framework",
"component", "component",
"frontend",
"front-end", "front-end",
"app" "app"
], ],
@@ -19,33 +21,42 @@
"email": "simon@newtriks.com", "email": "simon@newtriks.com",
"url": "https://github.com/newtriks" "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": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/newtriks/generator-react-webpack.git" "url": "git://github.com/newtriks/generator-react-webpack.git"
}, },
"scripts": { "scripts": {
"test": "mocha" "test": "mocha",
"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"
}, },
"dependencies": { "dependencies": {
"yeoman-generator": "^0.17.6" "react-webpack-template": "^1.0.0",
"underscore.string": "^3.2.2",
"yeoman-generator": "^0.22.0",
"yeoman-welcome": "^1.0.1",
"esprima": "^2.7.0",
"esprima-walk": "^0.1.0",
"escodegen": "^1.7.0"
}, },
"devDependencies": { "devDependencies": {
"mocha": "^1.21.4", "chai": "^3.2.0",
"underscore.string": "^2.3.3", "mocha": "^2.3.2",
"grunt": "^0.4.5", "yeoman-assert": "^2.1.1",
"grunt-cli": "^0.1.13" "yeoman-test": "^1.0.0"
},
"peerDependencies": {
"yo": ">=1.2.0"
}, },
"engines": { "engines": {
"node": ">=0.10.0", "node": ">=4.0.0",
"iojs": ">=1.1.0" "iojs": ">=1.1.0"
}, },
"licenses": [ "license": "MIT"
{
"type": "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();
});
});
+218
View File
@@ -0,0 +1,218 @@
'use strict';
let path = require('path');
let expect = require('chai').expect;
let assert = require('yeoman-assert');
let helpers = require('yeoman-test');
// Default globals, used in all tests
const defaultPrompts = require('../../../generators/app/prompts.js');
let generator;
const generatorBase = path.join(__dirname, '../../../generators/app');
/**
* Global before load function. Run in the before callbacks
* @param {Object} prompts List of prompts to use
* @param {Function} done Done callback
*/
const beforeLoad = (prompts, done) => {
helpers.run(generatorBase)
.inTmpDir()
.withOptions({
'skip-welcome-message': true,
'skip-install': true
})
.withPrompts(prompts)
.on('ready', (instance) => {
generator = instance;
})
.on('end', done);
};
describe('react-webpack:app', () => {
let prompts = {};
for(let p of defaultPrompts) {
prompts[p.name] = p.default;
}
before((done) => {
beforeLoad(prompts, done);
});
describe('#config', () => {
it('should set the generatedWith key to the current generator major version', () => {
expect(generator.config.get('generatedWithVersion')).to.equal(3);
});
it('should use "css" as default style language', () => {
expect(generator.config.get('style')).to.equal('css');
});
it('should not enable "PostCSS" by default', () => {
expect(generator.config.get('postcss')).to.equal(false);
});
});
describe('#createFiles', () => {
it('should generate dot files', () => {
assert.file([
'.babelrc',
'.editorconfig',
'.eslintrc',
'.gitignore',
'.yo-rc.json'
]);
});
it('should generate project configuration files', () => {
assert.file([
'package.json'
]);
});
it('should generate the webpack configuration', () => {
assert.file([
'cfg/base.js',
'cfg/defaults.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/index.js',
'src/components/Main.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'
]);
});
});
});
describe('react-webpack:app with PostCSS support', () => {
let prompts = {};
for(let p of defaultPrompts) {
prompts[p.name] = p.default;
}
prompts.postcss = true;
before((done) => {
beforeLoad(prompts, done);
});
describe('#config', () => {
it('should set the generatedWith key to the current generator major version', () => {
expect(generator.config.get('generatedWithVersion')).to.equal(3);
});
it('should use "css" as default style language', () => {
expect(generator.config.get('style')).to.equal('css');
});
it('should enable "PostCSS"', () => {
expect(generator.config.get('postcss')).to.equal(true);
});
});
describe('#createFiles', () => {
it('should generate dot files', () => {
assert.file([
'.babelrc',
'.editorconfig',
'.eslintrc',
'.gitignore',
'.yo-rc.json'
]);
});
it('should generate project configuration files', () => {
assert.file([
'package.json'
]);
});
it('should generate the webpack configuration', () => {
assert.file([
'cfg/base.js',
'cfg/defaults.js',
'cfg/dev.js',
'cfg/dist.js',
'cfg/test.js',
'server.js',
'webpack.config.js'
]);
});
it('should insert the postcss loader into the style pipes', () => {
assert.fileContent('cfg/defaults.js', 'loader: \'style-loader!css-loader!postcss-loader\'');
assert.fileContent('cfg/defaults.js', 'loader: \'style-loader!css-loader!postcss-loader!sass-loader?outputStyle=expanded&indentedSyntax\'');
assert.fileContent('cfg/defaults.js', 'loader: \'style-loader!css-loader!postcss-loader!sass-loader?outputStyle=expanded\'');
assert.fileContent('cfg/defaults.js', 'loader: \'style-loader!css-loader!postcss-loader!less-loader\'');
assert.fileContent('cfg/defaults.js', 'loader: \'style-loader!css-loader!postcss-loader!stylus-loader\'');
});
it('should append the postcss function to the base config', () => {
assert.fileContent('cfg/defaults.js', ',\n postcss: function () {\n return [];\n }');
});
it('should generate required source files', () => {
assert.file([
'src/actions/README.md',
'src/index.js',
'src/components/Main.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'
]);
});
});
});
+181
View File
@@ -0,0 +1,181 @@
'use strict';
let path = require('path');
let assert = require('yeoman-assert');
let helpers = require('yeoman-test');
describe('react-webpack:component', () => {
let generatorComponent = path.join(__dirname, '../../../generators/component');
// List of available style types. Please add a line that says
// testComponentWithStyle(styleTypes.KEY); to the bottom of the file
// to run all unit tests for this filetype.
const styleTypes = {
css: {
type: 'css',
fileName: 'src/styles/Mycomponent.css',
expandedFileName: 'src/styles/my/littleSpecial/Test.css',
assertions: {
componentImport: 'require(\'styles//Mycomponent.css\');',
styleContent: '.mycomponent-component'
}
},
sass: {
type: 'sass',
fileName: 'src/styles/Mycomponent.sass',
expandedFileName: 'src/styles/my/littleSpecial/Test.sass',
assertions: {
componentImport: 'require(\'styles//Mycomponent.sass\');',
styleContent: '.mycomponent-component'
}
},
scss: {
type: 'scss',
fileName: 'src/styles/Mycomponent.scss',
expandedFileName: 'src/styles/my/littleSpecial/Test.scss',
assertions: {
componentImport: 'require(\'styles//Mycomponent.scss\');',
styleContent: '.mycomponent-component'
}
},
less: {
type: 'less',
fileName: 'src/styles/Mycomponent.less',
expandedFileName: 'src/styles/my/littleSpecial/Test.less',
assertions: {
componentImport: 'require(\'styles//Mycomponent.less\');',
styleContent: '.mycomponent-component'
}
},
stylus: {
type: 'stylus',
fileName: 'src/styles/Mycomponent.styl',
expandedFileName: 'src/styles/my/littleSpecial/Test.styl',
assertions: {
componentImport: 'require(\'styles//Mycomponent.styl\');',
styleContent: '.mycomponent-component'
}
}
};
/**
* Return a newly generated component with given name and style
* @param {String} name Name of the component
* @param {String} styleType Styling language to use
* @param {Object} options Options to use for the generator
* @param {Function} callback Test callback to run
*/
function createGeneratedComponent(name, styleType, options, callback) {
helpers.run(generatorComponent)
.withArguments([name])
.withOptions(options)
.on('ready', (instance) => {
instance.config.set('style', styleType);
})
.on('end', callback);
}
/**
* Test a component with styling applied
* @param {Object} style The style to apply (see styleTypes above)
* @param {Object} options Options to use [optional]
*/
function testComponentWithStyle(style, options) {
// Make sure we always have options
if(!options) {
options = {};
}
describe(`When using style type "${style.type}"`, () => {
describe('When writing is called', () => {
it(`should create the react component, its ${style.type}-stylesheet and test file`, (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.file([
'src/components/MycomponentComponent.js',
style.fileName,
'test/components/MycomponentComponentTest.js'
]);
done();
});
});
});
describe('When creating a component', () => {
it('should always import REACT', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('src/components/MycomponentComponent.js', `import React from 'react';`);
done();
});
});
it(`should require the created ${style.type} file`, (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('src/components/MycomponentComponent.js', style.assertions.componentImport);
done();
});
});
it('should have its displayName set per default', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('src/components/MycomponentComponent.js', `displayName = 'MycomponentComponent';`);
done();
});
});
it('should export the created component', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('src/components/MycomponentComponent.js', `export default MycomponentComponent`);
done();
});
});
it(`should be possible to create components in a subfolder`, (done) => {
createGeneratedComponent('my/little !special/test', style.type, options, () => {
assert.file([
'src/components/my/littleSpecial/TestComponent.js',
style.expandedFileName,
'test/components/my/littleSpecial/TestComponentTest.js'
]);
done();
});
});
});
describe('Style', () => {
it(`should add the components ${style.type} class to the stylesheet`, (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent(style.fileName, style.assertions.styleContent);
done();
});
});
});
describe('Test', () => {
it('should import the react component', (done) => {
createGeneratedComponent('mycomponent', style.type, options, () => {
assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';');
done();
});
});
});
});
}
// 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 });
});
+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);
});
});
});
+121
View File
@@ -0,0 +1,121 @@
'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',
displayName: 'MyComponentTestComponent',
suffix: '.js'
},
test: {
path: 'test/components/my/component/',
fileName: 'TestComponentTest.js'
}
};
expect(utils.getAllSettingsFromComponentName('my/component/test')).to.deep.equal(expection);
});
});
describe('#getDestinationPath', () => {
it('should return the correct filesystem path for all components', () => {
expect(utils.getDestinationPath('test', 'action', 'Actions')).to.equal('src/actions/TestActions.js');
expect(utils.getDestinationPath('subfolder/test', 'action', 'Actions')).to.equal('src/actions/subfolder/TestActions.js');
expect(utils.getDestinationPath('test', 'source', 'Source')).to.equal('src/sources/TestSource.js');
expect(utils.getDestinationPath('subfolder/test', 'source', 'Source')).to.equal('src/sources/subfolder/TestSource.js');
expect(utils.getDestinationPath('test', 'store', 'Store')).to.equal('src/stores/TestStore.js');
expect(utils.getDestinationPath('subfolder/test', 'store', 'Store')).to.equal('src/stores/subfolder/TestStore.js');
});
});
describe('#getDestinationClassName', () => {
it('should return the correct javascript class name for the given paths', () => {
expect(utils.getDestinationClassName('test', 'action', 'Actions')).to.equal('TestActions');
expect(utils.getDestinationClassName('test', 'source', 'Source')).to.equal('TestSource');
expect(utils.getDestinationClassName('test', 'store', 'Store')).to.equal('TestStore');
});
});
});
-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';
const config = require('./config');
const yeoman = require('./yeoman');
module.exports = {
config,
yeoman
};
+65
View File
@@ -0,0 +1,65 @@
'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;
}
module.exports = {
getSetting,
getChoices,
getChoiceByKey,
getDefaultChoice
};
+101
View File
@@ -0,0 +1,101 @@
{
"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"
}
]
},
"postcss": {
"options": [
{
"name": "postcss",
"value": "postcss",
"packages": [
{ "name": "postcss", "version": "^5.0.11" },
{ "name": "postcss-loader", "version": "^0.8.0" }
]
}
]
},
"style": {
"options": [
{
"name": "css",
"value": "css",
"suffix": ".css"
},
{
"name": "sass",
"value": "sass",
"suffix": ".sass",
"packages": [
{ "name": "sass-loader", "version": "^3.1.2" },
{ "name": "node-sass", "version": "^3.4.2" }
]
},
{
"name": "scss",
"value": "scss",
"suffix": ".scss",
"packages": [
{ "name": "sass-loader", "version": "^3.1.2" },
{ "name": "node-sass", "version": "^3.4.2" }
]
},
{
"name": "less",
"value": "less",
"suffix": ".less",
"packages": [
{ "name": "less-loader", "version": "^2.0.0" },
{ "name": "less", "version": "^2.5.3" }
]
},
{
"name": "stylus",
"value": "stylus",
"suffix": ".styl",
"packages": [
{ "name": "stylus-loader", "version": "^0.5.0" },
{ "name": "stylus", "version": "~0.49.2" },
{ "name": "nib", "version": "~1.0.4" }
]
}
],
"default": "css"
}
}
+167
View File
@@ -0,0 +1,167 @@
'use strict';
const path = require('path');
const configUtils = require('./config');
const _ = 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('/');
// Get the components displayName property
let componentFullName = _.classify(_.replaceAll(componentName, '/', '_'));
// 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`,
displayName: `${componentFullName}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)));
};
/**
* Get the wanted destination path
* @param {String} name Name of the file
* @param {String} type The type to use (e.g. action, store, ...)
* @param {Suffix} suffix The suffix to use for the file (e.g. Store, Actions, ...)
* @return {String} Final path
*/
let getDestinationPath = (name, type, suffix) => {
let cleanedPaths = getCleanedPathName(name, suffix);
let fsParts = cleanedPaths.split('/');
let actionBaseName = _.capitalize(fsParts.pop());
let partPath = fsParts.join('/');
let fsPath = configUtils.getChoiceByKey('path', type).path;
let parts = [ fsPath ];
if(partPath.length > 0) {
parts.push(partPath);
}
parts.push(actionBaseName);
let fullPath = parts.join('/');
return `${fullPath}.js`;
};
/**
* Get the destinations class name
* @param {String} name Name of the file
* @param {String} type The type to use (e.g. action, store, ...)
* @param {Suffix} suffix The suffix to use for the file (e.g. Store, Actions, ...)
* @return {String} The javascript class name to use
*/
let getDestinationClassName = (name, type, suffix) => {
let fixedName = getDestinationPath(name, type, suffix);
return _.capitalize(fixedName.split('/').pop().split('.js')[0]);
};
module.exports = {
getBaseDir,
getAllSettingsFromComponentName,
getAppName,
getCleanedPathName,
getComponentStyleName,
getDestinationPath,
getDestinationClassName
};