├── .editorconfig
├── .eslintrc
├── .gitattributes
├── .gitignore
├── .jshintrc
├── .travis.yml
├── .yo-rc.json
├── README.md
├── app
├── index.js
└── templates
│ ├── LICENSE.md
│ ├── README.md
│ ├── buildConfig.js
│ ├── editorconfig
│ ├── esdoc.json
│ ├── eslintrc
│ ├── gitignore
│ ├── gulp
│ ├── assets.js
│ ├── build.js
│ ├── custom-task.js
│ ├── debug.js
│ ├── index.js
│ └── utils
│ │ └── define-plugin.js
│ ├── gulpfile.js
│ ├── index.html
│ ├── package.json
│ ├── src
│ ├── app.jsx
│ ├── app
│ │ └── index.js
│ ├── components
│ │ ├── chrome
│ │ │ ├── index.js
│ │ │ └── template.jsx
│ │ └── navigation
│ │ │ ├── index.js
│ │ │ └── template.jsx
│ ├── index.js
│ ├── pages
│ │ ├── index
│ │ │ ├── index.js
│ │ │ ├── page.js
│ │ │ └── template.jsx
│ │ ├── notfound
│ │ │ ├── index.js
│ │ │ ├── page.js
│ │ │ └── template.jsx
│ │ └── other
│ │ │ ├── index.js
│ │ │ ├── page.js
│ │ │ └── template.jsx
│ └── routes.js
│ ├── style
│ └── main.less
│ ├── test
│ ├── app.jsx
│ ├── environment.js
│ └── index.js
│ ├── webpack.config.js
│ └── webpack.config.prod.js
├── common
└── util
│ ├── alias.js
│ └── header.js
├── component
├── index.js
└── templates
│ ├── index.js
│ ├── store-postal.js
│ ├── store-rxmq.js
│ ├── style.less
│ ├── template.jsx
│ └── tests
│ └── test.jsx
├── docker-deploy
├── index.js
└── templates
│ └── Dockerfile
├── docker
├── index.js
└── templates
│ ├── Dockerfile
│ └── dockerignore
├── package.json
├── page
├── index.js
└── templates
│ ├── index.js
│ ├── page.js
│ ├── template.jsx
│ └── tests
│ └── test.jsx
└── test
├── docker-deploy.js
├── test-app.js
├── test-component.js
├── test-docker.js
└── test-page.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig: http://EditorConfig.org
2 | # all files defaults
3 | [**]
4 | # Unix-style newlines with a newline ending
5 | end_of_line = lf
6 | insert_final_newline = true
7 | # Set default charset
8 | charset = utf-8
9 | # 4 space indentation
10 | indent_style = space
11 | indent_size = 2
12 | # trim whitespaces
13 | trim_trailing_whitespace = true
14 | # always insert final newline
15 | insert_final_newline = true
16 |
17 | # 2 spaces indent for config files
18 | [{package.json}]
19 | indent_style = space
20 | indent_size = 2
21 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | // I want to use babel-eslint for parsing!
3 | "parser": "babel-eslint",
4 | "env": {
5 | // I write for browser
6 | "browser": true,
7 | // in CommonJS
8 | "node": true,
9 | // use ES6
10 | "es6": true
11 | },
12 | "ecmaProperties": {
13 | // enable JSX support
14 | "jsx": true
15 | },
16 | "plugins": [
17 | // enable react plugin
18 | "react"
19 | ],
20 | // To give you an idea how to override rule options
21 | "rules": {
22 | // Possible Errors
23 | "comma-dangle": 0,
24 | "no-console": 2,
25 | "no-debugger": 2,
26 | "no-dupe-keys": 2,
27 | "no-dupe-args": 2,
28 | "no-empty": 2,
29 | "no-extra-boolean-cast": 2,
30 | "no-extra-semi": 2,
31 | "no-invalid-regexp": 2,
32 | "no-irregular-whitespace": 2,
33 | "no-reserved-keys": 0,
34 | "no-sparse-arrays": 2,
35 | "no-unreachable": 2,
36 | "use-isnan": 2,
37 | "valid-jsdoc": 2,
38 | "valid-typeof": 2,
39 | // Best Practices
40 | "consistent-return": 1,
41 | "curly": 2,
42 | "default-case": 2,
43 | "dot-notation": 2,
44 | "eqeqeq": 2,
45 | "no-alert": 2,
46 | "no-caller": 2,
47 | "no-else-return": 2,
48 | "no-eq-null": 2,
49 | "no-eval": 2,
50 | "no-extend-native": 2,
51 | "no-floating-decimal": 2,
52 | "no-implied-eval": 2,
53 | "no-iterator": 2,
54 | "no-labels": 2,
55 | "no-loop-func": 1,
56 | "no-lone-blocks": 2,
57 | "no-multi-spaces": 2,
58 | "no-native-reassign": 2,
59 | "no-new": 2,
60 | "no-new-func": 2,
61 | "no-new-wrappers": 2,
62 | "no-proto": 2,
63 | "no-redeclare": 2,
64 | "no-return-assign": 2,
65 | "no-script-url": 2,
66 | "no-self-compare": 2,
67 | "no-sequences": 2,
68 | "no-throw-literal": 2,
69 | "no-unused-expressions": 2,
70 | "no-void": 2,
71 | "radix": 2,
72 | "yoda": 0,
73 | // Strict Mode
74 | "strict": 0,
75 | // Variables
76 | "no-catch-shadow": 2,
77 | "no-delete-var": 2,
78 | "no-shadow": 2,
79 | "no-shadow-restricted-names": 2,
80 | "no-undef": 2,
81 | "no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
82 | "no-use-before-define": 2,
83 | // Node
84 | "handle-callback-err": 2,
85 | "no-new-require": 2,
86 | "no-path-concat": 2,
87 | // Stylistic Issues
88 | "indent": [2, 2], // 2 spaces
89 | "camelcase": 2,
90 | "comma-spacing": [2, {"before": false, "after": true}],
91 | "comma-style": [2, "last"],
92 | "eol-last": 2,
93 | "func-style": [2, "expression"],
94 | "max-nested-callbacks": [2, 3],
95 | "no-array-constructor": 2,
96 | "no-mixed-spaces-and-tabs": 2,
97 | "no-multiple-empty-lines": [1, {"max": 2}],
98 | "no-nested-ternary": 2,
99 | "no-new-object": 2,
100 | "no-space-before-semi": 2,
101 | "no-spaced-func": 2,
102 | "no-trailing-spaces": 2,
103 | "no-underscore-dangle": 2,
104 | "no-wrap-func": 2,
105 | "quote-props": [1, "as-needed"],
106 | "quotes": [1, "single"],
107 | "semi": [2, "always"],
108 | "semi-spacing": [2, {"before": false, "after": true}],
109 | "space-after-function-name": [1, "never"],
110 | "space-after-keywords": [1, "always"],
111 | "space-before-blocks": [1, "always"],
112 | "space-before-function-parentheses": [2, "always"],
113 | "space-in-brackets": [1, "never"],
114 | "space-in-parens": [1, "never"],
115 | "space-infix-ops": 2,
116 | // complexity rules
117 | "max-depth": [2, 2],
118 | "max-statements": [2, 8],
119 | "complexity": [2, 5],
120 | "max-len": [2, 120],
121 | "max-params": [2, 2],
122 | "max-nested-callbacks": [2, 1],
123 | // jsx rules
124 | "react/jsx-quotes": 1,
125 | "react/jsx-no-undef": 1,
126 | "react/jsx-uses-react": 1,
127 | "react/jsx-uses-vars": 1,
128 | "react/no-did-mount-set-state": 1,
129 | "react/no-did-update-set-state": 1,
130 | "react/no-multi-comp": 1,
131 | "react/react-in-jsx-scope": 1,
132 | "react/self-closing-comp": 1,
133 | "react/wrap-multilines": 1
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "indent": 2,
10 | "latedef": true,
11 | "newcap": true,
12 | "noarg": true,
13 | "quotmark": "single",
14 | "undef": true,
15 | "unused": true,
16 | "strict": true
17 | }
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - 'iojs'
5 | - '0.12'
6 | - '0.10'
7 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-generator": {}
3 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # generator-turris
2 |
3 | [](http://unmaintained.tech/)
4 |
5 | > [Yeoman](http://yeoman.io) generator for ES6 React web applications
6 |
7 | ## About Turris.js
8 |
9 | Turris.js is a combination of existing libraries and tools that allows fast and simple creation of single page web applications using [ES6](http://www.ecma-international.org/publications/standards/Ecma-262.htm).
10 |
11 | Turris.js is built using [React.js](https://facebook.github.io/react/) with [react-router](https://github.com/rackt/react-router) and [Twitter Bootstrap CSS](http://getbootstrap.com/).
12 | All of the application code is managed and compiled by [Webpack](http://webpack.github.io/) and minified using [Uglify.js](https://github.com/mishoo/UglifyJS2) (when not in debug mode).
13 | [NPM](https://npmjs.org/) is used for the dependency management.
14 |
15 | [Gulp.js](http://gulpjs.com/) is used as a build-tool.
16 | Testing is done using [eslint](http://eslint.org/) for linting and code-style and [mocha.js](http://visionmedia.github.io/mocha/) with [should.js](https://github.com/visionmedia/should.js/) for automated testing.
17 |
18 | Standalone reusable components for your Turris application can be generated using separate [Turris-component](https://github.com/turrisjs/generator-turris-component) generator.
19 |
20 | ## Getting Started
21 |
22 | ### Requirements
23 |
24 | For Turris.js to function properly, you'll need to have following things installed:
25 |
26 | * [io.js](https://iojs.org/) v1.8+
27 | * [NPM](https://npmjs.org/) v2.8+
28 |
29 | Alternatively you can use [docker](https://www.docker.com/) environment provided by `turris:docker` generator.
30 |
31 | ### Installation
32 |
33 | ```
34 | $ npm install -g yo generator-turris
35 | ```
36 |
37 | ### Usage
38 |
39 | ```
40 | $ yo turris
41 | ```
42 |
43 | ### API
44 |
45 | `$ yo turris`
46 | Creates a new turris.js application.
47 |
48 | `$ yo turris:component myName`
49 | Generates a new inlined React component named *myName*.
50 | If you want to generate standalone/self-contained and more sophisticated components, look into using [Turris component generator](https://github.com/turrisjs/generator-turris-component).
51 |
52 | `$ yo turris:page myName`
53 | Generates a new page named *myName* and injects it into main app.
54 | The only difference between page and component is that page generates and exports a route.
55 |
56 | `$ yo turris:docker`
57 | Generates a [docker](https://www.docker.com/) environment for the project with all the requirements included.
58 |
59 | `$ yo turris:docker-deploy`
60 | Generates a separate [docker](https://www.docker.com/) environment for the project statical deployment via nginx.
61 | To deploy the app using it, you will need to:
62 | 1. Compile your app using `npm run deploy` command
63 | 2. Package it into docker container using `docker build -t app-name -f Dockerfile-deploy .` command
64 | 3. Run it on docker and forward port 80
65 |
66 |
67 | ## Learning Your Way Around
68 |
69 | Once installed, you can create a basic application by following the prompts.
70 |
71 | ```shell
72 | $ mkdir HelloWorld
73 | $ cd HelloWorld
74 | $ yo turris
75 |
76 | _-----_
77 | | | .--------------------------.
78 | |--(o)--| | Welcome to the |
79 | `---------´ | breathtaking Turris |
80 | ( _´U`_ ) | generator! |
81 | /___A___\ '--------------------------'
82 | | ~ |
83 | __'.___.'__
84 | ´ ` |° ´ Y `
85 |
86 | ? Your project name: (HelloWorld)
87 | ...
88 | ```
89 |
90 | To run your application, just type `npm start`.
91 |
92 | ### Project Structure
93 |
94 | Client:
95 | - **/style/** - LESS files
96 | - **/src/index.js** - App entry point
97 | - **/src/app.jsx** - React app bootstrapper
98 | - **/src/routes.js** - Routes for react-router
99 | - **/src/app/** - React app definition with react-router setup
100 | - **/src/components/** - React components folder
101 | - **/src/components/chrome** - Application chrome React component
102 | - **/src/components/navigation** - Application navigation bar React component
103 | - **/src/pages/** - React pages folder
104 | - **/src/pages/index** - Index page that's loaded by default
105 | - **/src/pages/other** - Other page that's added for convenience
106 |
107 | Components (can be generated with `turris:component`):
108 | - **/src/components/COMPONENT/index.js** - React component definition
109 | - **/src/components/COMPONENT/template.jsx** - React template
110 |
111 | Pages (can be generated with `turris:page`):
112 | - **/src/pages/PAGE/index.js** - React component and route definition
113 | - **/src/pages/PAGE/template.jsx** - React template
114 |
115 | Misc:
116 | - **/gulp/** - Custom Gulp tasks
117 | - **/tests/** - Unit and functional tests
118 | - **/buildConfig.js** - Build config
119 | - **/gulpfile.js** - Gulp bootstrapper
120 | - **/webpack.config.js** - Webpack config for development
121 | - **/webpack.config.prod.js** - Webpack config for production
122 |
123 | ### Docker
124 |
125 | You can as well quickly setup a [docker](https://www.docker.com/) container that includes all the required packages for Turris.js app to work (assuming you have docker installed).
126 | To do so, follow the steps below:
127 |
128 | 1. Run `yo turris:docker` to generate all the required docker files
129 | 2. Run `docker build -t appname .` to generate new docker container
130 | 3. Run `docker run -it -p 8080:8080 appname` to launch generated container
131 |
132 | ### TODO
133 |
134 | Add more docs, subgenerators and a proper test suite.
135 |
136 | ## License
137 |
138 | [MIT](http://opensource.org/licenses/MIT)
139 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 | var chalk = require('chalk');
4 | var yosay = require('yosay');
5 |
6 | module.exports = yeoman.generators.Base.extend({
7 | initializing: function () {
8 | this.pkg = require('../package.json');
9 | },
10 |
11 | prompting: function () {
12 | var done = this.async();
13 |
14 | // Have Yeoman greet the user.
15 | this.log(yosay(
16 | 'Welcome to the breathtaking ' + chalk.red('Turris') + ' generator!'
17 | ));
18 |
19 | var prompts = [{
20 | type: 'input',
21 | name: 'name',
22 | message: 'Your project name',
23 | default: this.appname,
24 | }, {
25 | type: 'input',
26 | name: 'description',
27 | message: 'Your project description',
28 | default: 'No description yet',
29 | }, {
30 | type: 'list',
31 | name: 'style',
32 | message: 'Do you want to use less, scss, styl or plain css?',
33 | choices: ['less', 'scss', 'styl', 'css'],
34 | default: 'less',
35 | store: true, // save for future
36 | }];
37 |
38 | this.prompt(prompts, function (props) {
39 | this.props = props;
40 | done();
41 | }.bind(this));
42 | },
43 |
44 | writing: {
45 | app: function () {
46 | this.fs.copyTpl(
47 | this.templatePath('package.json'),
48 | this.destinationPath('package.json'),
49 | this.props
50 | );
51 | this.fs.copyTpl(
52 | this.templatePath('README.md'),
53 | this.destinationPath('README.md'),
54 | this.props
55 | );
56 | },
57 |
58 | projectfiles: function () {
59 | this.fs.copy(
60 | this.templatePath('editorconfig'),
61 | this.destinationPath('.editorconfig')
62 | );
63 | this.fs.copy(
64 | this.templatePath('eslintrc'),
65 | this.destinationPath('.eslintrc')
66 | );
67 | this.fs.copy(
68 | this.templatePath('gitignore'),
69 | this.destinationPath('.gitignore')
70 | );
71 | this.fs.copy(
72 | this.templatePath('buildConfig.js'),
73 | this.destinationPath('buildConfig.js')
74 | );
75 | this.fs.copy(
76 | this.templatePath('esdoc.json'),
77 | this.destinationPath('esdoc.json')
78 | );
79 | this.fs.copy(
80 | this.templatePath('gulpfile.js'),
81 | this.destinationPath('gulpfile.js')
82 | );
83 | this.fs.copyTpl(
84 | this.templatePath('index.html'),
85 | this.destinationPath('index.html'),
86 | this.props
87 | );
88 | this.fs.copy(
89 | this.templatePath('webpack.config.js'),
90 | this.destinationPath('webpack.config.js')
91 | );
92 | this.fs.copy(
93 | this.templatePath('webpack.config.prod.js'),
94 | this.destinationPath('webpack.config.prod.js')
95 | );
96 |
97 | // folders
98 | this.fs.copy(
99 | this.templatePath('gulp'),
100 | this.destinationPath('gulp')
101 | );
102 | this.fs.copyTpl(
103 | this.templatePath('src'),
104 | this.destinationPath('src'),
105 | this.props
106 | );
107 | this.fs.copy(
108 | this.templatePath('style/main.less'),
109 | this.destinationPath('style/main.' + this.props.style)
110 | );
111 | this.fs.copy(
112 | this.templatePath('test'),
113 | this.destinationPath('test')
114 | );
115 | this.fs.copy(
116 | this.templatePath('LICENSE.md'),
117 | this.destinationPath('LICENSE.md')
118 | );
119 | }
120 | },
121 |
122 | install: function () {
123 | this.npmInstall();
124 | }
125 | });
126 |
--------------------------------------------------------------------------------
/app/templates/LICENSE.md:
--------------------------------------------------------------------------------
1 | Your license text here
2 |
--------------------------------------------------------------------------------
/app/templates/README.md:
--------------------------------------------------------------------------------
1 | # Awesome <%= name %> application
2 |
3 | <%= description %>
4 |
5 | Made with [Turris.js](https://github.com/turrisjs)
6 |
--------------------------------------------------------------------------------
/app/templates/buildConfig.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | webpackConfig: {
3 | debug: require('./webpack.config.js'),
4 | production: require('./webpack.config.prod.js'),
5 | },
6 | devServer: {
7 | // settings for webpack-dev-server
8 | proxy: {
9 | // put your custom proxy routes here, e.g.:
10 | // '/api/': 'http://localhost:8081'
11 | },
12 | headers: {
13 | // put your custom headers here, e.g.:
14 | // 'X-TEST': 1,
15 | },
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/app/templates/editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig: http://EditorConfig.org
2 | # all files defaults
3 | [**]
4 | # Unix-style newlines with a newline ending
5 | end_of_line = lf
6 | insert_final_newline = true
7 | # Set default charset
8 | charset = utf-8
9 | # 4 space indentation
10 | indent_style = space
11 | indent_size = 4
12 | # trim whitespaces
13 | trim_trailing_whitespace = true
14 | # always insert final newline
15 | insert_final_newline = true
16 |
17 | # 2 spaces indent for config files
18 | [{package.json}]
19 | indent_style = space
20 | indent_size = 2
21 |
--------------------------------------------------------------------------------
/app/templates/esdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "source": "./src",
3 | "destination": "./esdoc",
4 | "coverage": true
5 | }
6 |
--------------------------------------------------------------------------------
/app/templates/eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | // I want to use babel-eslint for parsing!
3 | "parser": "babel-eslint",
4 | "env": {
5 | // I write for browser
6 | "browser": true,
7 | // in CommonJS
8 | "node": true,
9 | // use ES6
10 | "es6": true
11 | },
12 | "ecmaProperties": {
13 | // enable JSX support
14 | "jsx": true
15 | },
16 | "plugins": [
17 | // enable react plugin
18 | "react"
19 | ],
20 | "globals": {
21 | "__WEBPACK__": true
22 | },
23 | // To give you an idea how to override rule options
24 | "rules": {
25 | // Possible Errors
26 | "comma-dangle": 0,
27 | "no-console": 2,
28 | "no-debugger": 2,
29 | "no-dupe-keys": 2,
30 | "no-dupe-args": 2,
31 | "no-empty": 2,
32 | "no-extra-boolean-cast": 2,
33 | "no-extra-semi": 2,
34 | "no-invalid-regexp": 2,
35 | "no-irregular-whitespace": 2,
36 | "quote-props": [2, "consistent-as-needed", {"keywords": true}],
37 | "no-sparse-arrays": 2,
38 | "no-unreachable": 2,
39 | "use-isnan": 2,
40 | "valid-jsdoc": 2,
41 | "valid-typeof": 2,
42 | // Best Practices
43 | "consistent-return": 1,
44 | "curly": 2,
45 | "default-case": 2,
46 | "dot-notation": 2,
47 | "eqeqeq": 2,
48 | "no-alert": 2,
49 | "no-caller": 2,
50 | "no-else-return": 2,
51 | "no-eq-null": 2,
52 | "no-eval": 2,
53 | "no-extend-native": 2,
54 | "no-floating-decimal": 2,
55 | "no-implied-eval": 2,
56 | "no-iterator": 2,
57 | "no-labels": 2,
58 | "no-loop-func": 1,
59 | "no-lone-blocks": 2,
60 | "no-multi-spaces": 2,
61 | "no-native-reassign": 2,
62 | "no-new": 2,
63 | "no-new-func": 2,
64 | "no-new-wrappers": 2,
65 | "no-proto": 2,
66 | "no-redeclare": 2,
67 | "no-return-assign": 2,
68 | "no-script-url": 2,
69 | "no-self-compare": 2,
70 | "no-sequences": 2,
71 | "no-throw-literal": 2,
72 | "no-unused-expressions": 2,
73 | "no-void": 2,
74 | "radix": 2,
75 | "yoda": 0,
76 | // Strict Mode
77 | "strict": 0,
78 | // Variables
79 | "no-catch-shadow": 2,
80 | "no-delete-var": 2,
81 | "no-shadow": 2,
82 | "no-shadow-restricted-names": 2,
83 | "no-undef": 2,
84 | "no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
85 | "no-use-before-define": 2,
86 | // Node
87 | "handle-callback-err": 2,
88 | "no-new-require": 2,
89 | "no-path-concat": 2,
90 | // Stylistic Issues
91 | "indent": 2, // 4 spaces
92 | "camelcase": 2,
93 | "comma-spacing": [2, {"before": false, "after": true}],
94 | "comma-style": [2, "last"],
95 | "eol-last": 2,
96 | "func-style": [2, "expression"],
97 | "max-nested-callbacks": [2, 3],
98 | "no-array-constructor": 2,
99 | "no-mixed-spaces-and-tabs": 2,
100 | "no-multiple-empty-lines": [1, {"max": 2}],
101 | "no-nested-ternary": 2,
102 | "no-new-object": 2,
103 | "semi-spacing": [2, {"before": false, "after": true}],
104 | "no-spaced-func": 2,
105 | "no-trailing-spaces": 2,
106 | "no-underscore-dangle": 2,
107 | "no-extra-parens": [2, "functions"],
108 | "quote-props": [1, "as-needed"],
109 | "quotes": [1, "single"],
110 | "semi": [2, "always"],
111 | "semi-spacing": [2, {"before": false, "after": true}],
112 | "space-before-function-paren": [1, {"anonymous": "never", "named": "never"}],
113 | "space-after-keywords": [1, "always"],
114 | "space-before-blocks": [1, "always"],
115 | "object-curly-spacing": [1, "never"],
116 | "array-bracket-spacing": [1, "never"],
117 | "computed-property-spacing": [1, "never"],
118 | "space-in-parens": [1, "never"],
119 | "key-spacing": [1, {"beforeColon": false, "afterColon": true}],
120 | "object-curly-spacing": [1, "never"],
121 | "space-infix-ops": 2,
122 | // complexity rules
123 | "max-depth": [2, 3],
124 | "max-statements": [1, 20],
125 | "complexity": [1, 3],
126 | "max-len": [2, 120],
127 | "max-params": [2, 2],
128 | // jsx rules
129 | "react/jsx-quotes": 1,
130 | "react/jsx-no-undef": 1,
131 | "react/jsx-uses-react": 1,
132 | "react/jsx-uses-vars": 1,
133 | "react/no-did-mount-set-state": 1,
134 | "react/no-did-update-set-state": 1,
135 | "react/no-multi-comp": 1,
136 | "react/react-in-jsx-scope": 1,
137 | "react/self-closing-comp": 1,
138 | "react/wrap-multilines": 1,
139 | // ES6
140 | "prefer-const": 2,
141 | "object-shorthand": [2, "always"],
142 | "no-var": 2
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/app/templates/gitignore:
--------------------------------------------------------------------------------
1 | # ignore node deps
2 | node_modules/
3 |
4 | # ignore compiled
5 | dist/
6 |
7 | # ignore coverage report
8 | coverage/
9 |
10 | # ignore docs
11 | esdoc/
12 |
--------------------------------------------------------------------------------
/app/templates/gulp/assets.js:
--------------------------------------------------------------------------------
1 | export default (gulp) => {
2 | gulp.task('assets', function() {
3 | return gulp.src('./index.html').pipe(gulp.dest('./dist'));
4 | });
5 | };
6 |
--------------------------------------------------------------------------------
/app/templates/gulp/build.js:
--------------------------------------------------------------------------------
1 | import gutil from 'gulp-util';
2 | import webpack from 'webpack';
3 | import definePlugin from './utils/define-plugin.js';
4 |
5 | // create optimizations array
6 | const optimizations = [
7 | definePlugin,
8 | new webpack.optimize.DedupePlugin(),
9 | new webpack.optimize.UglifyJsPlugin({
10 | output: {
11 | comments: false,
12 | },
13 | compress: {
14 | warnings: false,
15 | },
16 | }),
17 | ];
18 |
19 | // get build config
20 | const buildConfig = require('../buildConfig');
21 | // get webpack config
22 | const wpConfig = buildConfig.webpackConfig.production;
23 |
24 | // export initilizer
25 | export default (gulp) => {
26 | gulp.task('build', function(callback) {
27 | if (wpConfig.plugins) {
28 | wpConfig.plugins = wpConfig.plugins.concat(optimizations);
29 | } else {
30 | wpConfig.plugins = optimizations;
31 | }
32 | // run webpack
33 | webpack(wpConfig, function(err, stats) {
34 | if (err) {
35 | throw new gutil.PluginError('webpack', err);
36 | }
37 | // only log when errors
38 | gutil.log('[webpack]: ', stats.toString({
39 | chunks: false,
40 | modules: false,
41 | colors: true,
42 | }));
43 | callback();
44 | });
45 | });
46 | };
47 |
--------------------------------------------------------------------------------
/app/templates/gulp/custom-task.js:
--------------------------------------------------------------------------------
1 | module.exports = function(gulp) {
2 | gulp.task('custom-task', function() {
3 | console.log('custom task done!');
4 | });
5 | };
6 |
--------------------------------------------------------------------------------
/app/templates/gulp/debug.js:
--------------------------------------------------------------------------------
1 | /* eslint no-console: 0 */
2 | import webpack from 'webpack';
3 | import WebpackDevServer from 'webpack-dev-server';
4 | import definePlugin from './utils/define-plugin.js';
5 |
6 | // create optimizations array
7 | const optimizations = [definePlugin];
8 |
9 | // get build config
10 | const buildConfig = require('../buildConfig');
11 | // get webpack config
12 | const wpConfig = buildConfig.webpackConfig.debug;
13 |
14 | // export initilizer
15 | export default (gulp) => {
16 | gulp.task('debug', function() {
17 | // append our define plugin
18 | wpConfig.plugins = wpConfig.plugins ? wpConfig.plugins.concat(optimizations) : optimizations;
19 | // get server config
20 | let proxy = {};
21 | if (buildConfig.devServer && buildConfig.devServer.proxy) {
22 | proxy = buildConfig.devServer.proxy;
23 | }
24 | let headers = {};
25 | if (buildConfig.devServer && buildConfig.devServer.headers) {
26 | headers = buildConfig.devServer.headers;
27 | }
28 | // run webpack
29 | const compiler = webpack(wpConfig);
30 | const server = new WebpackDevServer(compiler, {
31 | // webpack-dev-server options
32 | contentBase: wpConfig.context,
33 |
34 | hot: true,
35 | // Enable special support for Hot Module Replacement
36 | // Page is no longer updated, but a "webpackHotUpdate" message is send to the content
37 | // Use "webpack/hot/dev-server" as additional module in your entry point
38 | // Note: this does _not_ add the `HotModuleReplacementPlugin` like the CLI option does.
39 |
40 | // webpack-dev-middleware options
41 | quiet: false,
42 | noInfo: false,
43 | watchOptions: {
44 | aggregateTimeout: 300,
45 | poll: true
46 | },
47 | headers,
48 | stats: {
49 | chunks: false,
50 | colors: true,
51 | },
52 | // Set this as true if you want to access dev server from arbitrary url.
53 | // This is handy if you are using a html5 router.
54 | historyApiFallback: true,
55 | // Set this if you want webpack-dev-server to delegate a single path to an arbitrary server.
56 | // Use "*" to proxy all paths to the specified server.
57 | // This is useful if you want to get rid of 'http://localhost:8080/' in script[src],
58 | // and has many other use cases (see https://github.com/webpack/webpack-dev-server/pull/127 ).
59 | proxy,
60 | });
61 | server.listen(8080, 'localhost', function() {
62 | console.log('Webpack-Dev-Server: started on port 8080');
63 | });
64 | });
65 | };
66 |
--------------------------------------------------------------------------------
/app/templates/gulp/index.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import gulp from 'gulp';
3 |
4 | // blacklist self and utils folder
5 | const blacklist = ['index.js', 'utils'];
6 | // get local files
7 | const files = fs.readdirSync('./gulp').filter(f => !blacklist.includes(f));
8 |
9 | // load custom tasks
10 | files.forEach(function(file) {
11 | require('./' + file)(gulp);
12 | });
13 |
14 | gulp.task('default', ['debug']);
15 | gulp.task('deploy', ['build', 'assets']);
16 |
--------------------------------------------------------------------------------
/app/templates/gulp/utils/define-plugin.js:
--------------------------------------------------------------------------------
1 | import webpack from 'webpack';
2 |
3 | // definePlugin takes raw strings and inserts them, so you can put strings of JS if you want.
4 | const definePlugin = new webpack.DefinePlugin({
5 | __WEBPACK__: true, // say we're the webpack
6 | __DEV__: process.env.BUILD_DEV, // dev environment indication
7 | });
8 |
9 | export default definePlugin;
10 |
--------------------------------------------------------------------------------
/app/templates/gulpfile.js:
--------------------------------------------------------------------------------
1 | // enable babel
2 | require('babel/register');
3 | // require gulp entry
4 | require('./gulp');
5 |
--------------------------------------------------------------------------------
/app/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= name %> app
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/templates/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "<%= _.camelCase(name) %>",
3 | "version": "1.0.0",
4 | "description": "<%= description %>",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "tape ./test | faucet",
8 | "cover": "istanbul cover tape ./test",
9 | "docs": "esdoc -c esdoc.json",
10 | "start": "gulp",
11 | "build": "gulp build",
12 | "deploy": "gulp deploy"
13 | },
14 | "author": "",
15 | "license": "MIT",
16 | "dependencies": {
17 | "bootstrap": "^3.3.4",
18 | "react": "^0.13.2",
19 | "react-router": "^0.13.3"
20 | },
21 | "devDependencies": {
22 | "babel": "^5.8.19",
23 | "babel-core": "^5.8.19",
24 | "babel-eslint": "^4.0.5",
25 | "babel-loader": "^5.3.2",
26 | "css-loader": "^0.17.0",
27 | "eslint": "^1.1.0",
28 | "eslint-loader": "^1.0.0",
29 | "eslint-plugin-react": "^3.1.0",
30 | "extract-text-webpack-plugin": "^0.8.2",
31 | "faucet": "0.0.1",
32 | "file-loader": "^0.8.4",
33 | "gulp": "^3.9.0",
34 | "gulp-util": "^3.0.6",
35 | "istanbul": "^0.3.17",
36 | "jsdom": "^6.3.0",<% if (style === 'less') { %>
37 | "less-loader": "^2.2.0",<% } %>
38 | "localStorage": "^1.0.3",
39 | "react-hot-loader": "^1.3.0",
40 | "style-loader": "^0.12.3",<% if (style === 'scss') { %>
41 | "sass-loader": "^1.0.4",<% } else if (style === 'styl') { %>
42 | "stylus-loader": "^1.2.1",<% } %>
43 | "tape": "^4.0.1",
44 | "url-loader": "^0.5.6",
45 | "webpack": "^1.10.5",
46 | "webpack-dev-server": "^1.10.1"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/templates/src/app.jsx:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import ReactRouter from 'react-router';
4 | // app core
5 | import App from './app/index.js';
6 | // user routes
7 | import routes from './routes.js';
8 |
9 | const appInstance = (
10 |
11 | {routes}
12 |
13 | );
14 |
15 | const Bootstrapper = {
16 | start() {
17 | ReactRouter.run(appInstance, ReactRouter.HistoryLocation, function(Handler) {
18 | React.render(, document.getElementById('mainContainer'));
19 | });
20 | },
21 | };
22 |
23 | export default Bootstrapper;
24 |
--------------------------------------------------------------------------------
/app/templates/src/app/index.js:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import {RouteHandler} from 'react-router';
4 |
5 | // main class
6 | const App = React.createClass({
7 | render() {
8 | return ;
9 | },
10 | });
11 |
12 | export default App;
13 |
--------------------------------------------------------------------------------
/app/templates/src/components/chrome/index.js:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import Template from './template.jsx';
4 |
5 | const Chrome = React.createClass({
6 | render: Template,
7 | });
8 |
9 | export default Chrome;
10 |
--------------------------------------------------------------------------------
/app/templates/src/components/chrome/template.jsx:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import Navigation from '../navigation';
4 |
5 | const render = function() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | {this.props.children}
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | export default render;
20 |
--------------------------------------------------------------------------------
/app/templates/src/components/navigation/index.js:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import Template from './template.jsx';
4 |
5 | const Navbar = React.createClass({
6 | render: Template,
7 | });
8 |
9 | export default Navbar;
10 |
--------------------------------------------------------------------------------
/app/templates/src/components/navigation/template.jsx:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import ReactRouter from 'react-router';
4 |
5 | const render = function() {
6 | return (
7 |
8 |
9 |
10 |
16 | <%= name %>
17 |
18 |
19 |
20 |
21 | - Home
22 | - Other
23 |
24 |
25 |
26 |
27 | );
28 | };
29 |
30 | export default render;
31 |
--------------------------------------------------------------------------------
/app/templates/src/index.js:
--------------------------------------------------------------------------------
1 | /* istanbul ignore if */
2 | if (__WEBPACK__) {
3 | require('bootstrap/dist/css/bootstrap.css');
4 | require('../style/main.<%= style %>');
5 | }
6 | import App from './app.jsx';
7 |
8 | App.start();
9 |
--------------------------------------------------------------------------------
/app/templates/src/pages/index/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {DefaultRoute} from 'react-router';
3 | import IndexPage from './page';
4 |
5 | const IndexRoute = React.createElement(DefaultRoute, {name: 'home', key: 'route_default', handler: IndexPage});
6 |
7 | export default IndexRoute;
8 |
--------------------------------------------------------------------------------
/app/templates/src/pages/index/page.js:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import Template from './template.jsx';
4 |
5 | const IndexPage = React.createClass({
6 | getInitialState() {
7 | // init state
8 | return {
9 | // your stuff here
10 | };
11 | },
12 | render: Template,
13 | });
14 |
15 | export default IndexPage;
16 |
--------------------------------------------------------------------------------
/app/templates/src/pages/index/template.jsx:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import Chrome from '../../components/chrome/index.js';
4 |
5 | const render = function() {
6 | return (
7 |
8 | I am index page
9 |
10 | );
11 | };
12 |
13 | export default render;
14 |
--------------------------------------------------------------------------------
/app/templates/src/pages/notfound/index.js:
--------------------------------------------------------------------------------
1 | // imports
2 | import React from 'react';
3 | import {NotFoundRoute} from 'react-router';
4 | import NotFoundPage from './page';
5 |
6 | const route = React.createElement(NotFoundRoute, {name: 'notfound', key: 'route_notfound', handler: NotFoundPage});
7 |
8 | export default route;
9 |
--------------------------------------------------------------------------------
/app/templates/src/pages/notfound/page.js:
--------------------------------------------------------------------------------
1 | // imports
2 | import React from 'react';
3 | import Template from './template.jsx';
4 |
5 | const NotFoundPage = React.createClass({
6 | render: Template,
7 | });
8 |
9 | export default NotFoundPage;
10 |
--------------------------------------------------------------------------------
/app/templates/src/pages/notfound/template.jsx:
--------------------------------------------------------------------------------
1 | // imports
2 | import React from 'react';
3 | import Chrome from '../../components/chrome/index.js';
4 |
5 | const render = function() {
6 | return (
7 |
8 | 404 - page not found!
9 |
10 | );
11 | };
12 |
13 | export default render;
14 |
--------------------------------------------------------------------------------
/app/templates/src/pages/other/index.js:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import {Route} from 'react-router';
4 | import OtherPage from './page';
5 |
6 | const route = React.createElement(Route, {name: 'other', key: 'route_other', handler: OtherPage});
7 |
8 | export default route;
9 |
--------------------------------------------------------------------------------
/app/templates/src/pages/other/page.js:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import Template from './template.jsx';
4 |
5 | const OtherPage = React.createClass({
6 | render: Template,
7 | });
8 |
9 | export default OtherPage;
10 |
--------------------------------------------------------------------------------
/app/templates/src/pages/other/template.jsx:
--------------------------------------------------------------------------------
1 | // react
2 | import React from 'react';
3 | import Chrome from '../../components/chrome/index.js';
4 |
5 | const render = function() {
6 | return (
7 |
8 | I am other page
9 |
10 | );
11 | };
12 |
13 | export default render;
14 |
--------------------------------------------------------------------------------
/app/templates/src/routes.js:
--------------------------------------------------------------------------------
1 | // import all routes
2 | import Index from './pages/index/index.js';
3 | import Other from './pages/other/index.js';
4 | import NotFound from './pages/notfound/index.js';
5 |
6 | // create route array
7 | const routes = [
8 | Index,
9 | Other,
10 | NotFound,
11 | ];
12 |
13 | export default routes;
14 |
--------------------------------------------------------------------------------
/app/templates/style/main.less:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 50px;
3 | }
4 |
--------------------------------------------------------------------------------
/app/templates/test/app.jsx:
--------------------------------------------------------------------------------
1 | // import helpers
2 | import test from 'tape';
3 |
4 | // import app
5 | import App from '../src/app.jsx';
6 |
7 | test('App suite', function(it) {
8 | it.test('# should render', function(t) {
9 | // render
10 | App.start();
11 | // verify it exists
12 | t.equal(1, document.getElementById('mainContainer').children.length);
13 | t.end();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/app/templates/test/environment.js:
--------------------------------------------------------------------------------
1 | import localStorage from 'localStorage';
2 | import {jsdom} from 'jsdom';
3 |
4 | // say we're not in webpack environment
5 | // this is required to skip including styles
6 | global.__WEBPACK__ = false; // eslint-disable-line no-underscore-dangle
7 |
8 | // init jsdom
9 | global.document = jsdom('');
10 | global.window = global.document.defaultView;
11 | global.navigator = global.window.navigator;
12 |
13 | // mock location
14 | global.window.location.href = 'http://localhost/';
15 |
16 | // local storage polyfill
17 | global.window.localStorage = localStorage;
18 | global.localStorage = localStorage;
19 |
--------------------------------------------------------------------------------
/app/templates/test/index.js:
--------------------------------------------------------------------------------
1 | require('babel/register');
2 | require('./environment');
3 | require('./app.jsx');
4 |
--------------------------------------------------------------------------------
/app/templates/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'inline-source-map',
6 | debug: true,
7 | context: path.resolve(__dirname),
8 | entry: [
9 | 'webpack-dev-server/client?http://0.0.0.0:8080', // WebpackDevServer host and port
10 | 'webpack/hot/only-dev-server', // "only" prevents reload on syntax errors
11 | './src/index.js',
12 | ],
13 | output: {
14 | path: path.resolve(__dirname, 'dist'),
15 | publicPath: '/',
16 | filename: 'app.min.js',
17 | },
18 | resolve: {
19 | root: path.resolve(__dirname),
20 | extensions: ['', '.js', '.jsx'],
21 | modulesDirectories: ['node_modules'],
22 | },
23 | node: {
24 | fs: 'empty',
25 | },
26 | eslint: {
27 | configFile: path.join(__dirname, '.eslintrc'),
28 | },
29 | module: {
30 | preLoaders: [
31 | {
32 | test: /\.jsx?$/,
33 | exclude: /node_modules/,
34 | loader: 'eslint'
35 | },
36 | ],
37 | loaders: [
38 | {
39 | test: /\.css$/,
40 | loaders: ['style', 'css'],
41 | },
42 | {
43 | test: /\.json$/,
44 | loader: 'json',
45 | },
46 | {
47 | test: /\.less$/,
48 | loaders: ['style', 'css', 'less'],
49 | },
50 | {
51 | test: /\.scss$/,
52 | loaders: ['style', 'css', 'sass'],
53 | },
54 | {
55 | test: /\.styl$/,
56 | loaders: ['style', 'css', 'stylus'],
57 | },
58 | {
59 | test: /\.jsx?$/,
60 | exclude: /node_modules/,
61 | loaders: ['react-hot', 'babel'],
62 | },
63 | {
64 | test: /\.woff\d?(\?.+)?$/,
65 | loader: 'url?limit=10000&minetype=application/font-woff',
66 | },
67 | {
68 | test: /\.ttf(\?.+)?$/,
69 | loader: 'url?limit=10000&minetype=application/octet-stream',
70 | },
71 | {
72 | test: /\.eot(\?.+)?$/,
73 | loader: 'url?limit=10000',
74 | },
75 | {
76 | test: /\.svg(\?.+)?$/,
77 | loader: 'url?limit=10000&minetype=image/svg+xml',
78 | },
79 | {
80 | test: /\.png$/,
81 | loader: 'url?limit=10000&mimetype=image/png',
82 | },
83 | {
84 | test: /\.gif$/,
85 | loader: 'url?limit=10000&mimetype=image/gif'
86 | }
87 | ],
88 | },
89 | plugins: [
90 | new webpack.HotModuleReplacementPlugin(),
91 | ],
92 | };
93 |
--------------------------------------------------------------------------------
/app/templates/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
3 |
4 | module.exports = {
5 | context: path.resolve(__dirname),
6 | entry: './src/index.js',
7 | output: {
8 | path: path.join(__dirname, 'dist'),
9 | filename: 'app.min.js',
10 | },
11 | resolve: {
12 | root: path.resolve(__dirname),
13 | packageMains: ['style', 'main'],
14 | extensions: ['', '.js', '.jsx'],
15 | modulesDirectories: ['node_modules'],
16 | },
17 | node: {
18 | fs: 'empty',
19 | },
20 | module: {
21 | loaders: [
22 | {
23 | test: /\.css$/,
24 | loader: ExtractTextPlugin.extract('style', 'css'),
25 | },
26 | {
27 | test: /\.json$/,
28 | loader: 'json',
29 | },
30 | {
31 | test: /\.less$/,
32 | loader: ExtractTextPlugin.extract('style', 'css!less'),
33 | },
34 | {
35 | test: /\.scss$/,
36 | loader: ExtractTextPlugin.extract('style', 'css!sass'),
37 | },
38 | {
39 | test: /\.styl$/,
40 | loader: ExtractTextPlugin.extract('style', 'css!stylus'),
41 | },
42 | {
43 | test: /\.jsx?$/,
44 | exclude: /node_modules/,
45 | loader: 'babel',
46 | },
47 | {
48 | test: /\.woff\d?(\?.+)?$/,
49 | loader: 'url?limit=1000&minetype=application/font-woff',
50 | },
51 | {
52 | test: /\.ttf(\?.+)?$/,
53 | loader: 'url?limit=1000&minetype=application/octet-stream',
54 | },
55 | {
56 | test: /\.eot(\?.+)?$/,
57 | loader: 'url?limit=1000',
58 | },
59 | {
60 | test: /\.svg(\?.+)?$/,
61 | loader: 'url?limit=1000&minetype=image/svg+xml',
62 | },
63 | {
64 | test: /\.png$/,
65 | loader: 'url?limit=1000&mimetype=image/png',
66 | },
67 | {
68 | test: /\.gif$/,
69 | loader: 'url?limit=1000&mimetype=image/gif'
70 | }
71 | ],
72 | },
73 | plugins: [
74 | new ExtractTextPlugin('style.css', {allChunks: true}),
75 | ],
76 | };
77 |
--------------------------------------------------------------------------------
/common/util/alias.js:
--------------------------------------------------------------------------------
1 | var rxNode = {
2 | type: 'Property',
3 | key: {
4 | type: 'Identifier',
5 | name: 'rx'
6 | },
7 | computed: false,
8 | value: {
9 | type: 'Literal',
10 | value: 'rx/dist/rx.lite.js'
11 | },
12 | kind: 'init',
13 | method: false,
14 | shorthand: false,
15 | };
16 |
17 | var aliasNode = {
18 | type: 'Property',
19 | key: {
20 | type: 'Identifier',
21 | name: 'alias'
22 | },
23 | computed: false,
24 | value: {
25 | type: 'ObjectExpression',
26 | properties: [rxNode]
27 | },
28 | kind: 'init',
29 | method: false,
30 | shorthand: false,
31 | };
32 |
33 | module.exports = function(data) {
34 | var exists = false;
35 | var added = false;
36 |
37 | data.body.forEach(function(it) {
38 | if (it.type === 'ExpressionStatement') {
39 | var props = it.expression.right.properties;
40 | props.forEach(function(prop) {
41 | if (prop.key.name === 'resolve' && prop.value.properties) {
42 | prop.value.properties.forEach(function(resolve) {
43 | if (resolve.key.name === 'alias' && resolve.value.properties) {
44 | resolve.value.properties.forEach(function(res) {
45 | if (res.key.name === 'rx') {
46 | exists = true;
47 | }
48 | });
49 |
50 | if (!exists) {
51 | if (resolve.value.properties) {
52 | resolve.value.properties.push(rxNode);
53 | } else {
54 | resolve.value.properties = [rxNode];
55 | }
56 | added = true;
57 | }
58 | }
59 | });
60 |
61 | if (!exists && !added) {
62 | prop.value.properties.push(aliasNode);
63 | added = true;
64 | }
65 | }
66 | });
67 | }
68 | });
69 |
70 | return data;
71 | };
72 |
--------------------------------------------------------------------------------
/common/util/header.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 |
3 | var getHeader = function (license) {
4 | if (license.toLowerCase() === 'no' || !license || !license.length) {
5 | return '';
6 | }
7 |
8 | if (license.toLowerCase() === 'yes') {
9 | return fs.readFileSync('./LICENSE.md', 'utf8');
10 | }
11 |
12 | return fs.readFileSync(license, 'utf8');
13 | };
14 |
15 | module.exports = {
16 | getHeader: getHeader,
17 | };
18 |
--------------------------------------------------------------------------------
/component/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 | var _ = require('lodash');
4 | var esprima = require('esprima');
5 | var escodegen = require('escodegen');
6 | var addAlias = require('../common/util/alias');
7 | var getHeader = require('../common/util/header').getHeader;
8 |
9 | module.exports = yeoman.generators.Base.extend({
10 | initializing: function () {
11 | var done = this.async();
12 |
13 | this.argument('name', {
14 | required: true,
15 | type: String,
16 | desc: 'The subgenerator name'
17 | });
18 |
19 | this.log('You called the Turris component subgenerator with the argument ' + this.name + '.');
20 |
21 | var prompts = [{
22 | type: 'input',
23 | name: 'license',
24 | message: 'Add header license? - no / yes (use LICENSE.md) / your file path',
25 | default: 'no',
26 | store: true, // save for future
27 | }, {
28 | type: 'list',
29 | name: 'store',
30 | message: 'Do you want to include postal, rxmq or no store?',
31 | choices: ['postal', 'rxmq', 'none'],
32 | default: 'none',
33 | store: true,
34 | }, {
35 | type: 'list',
36 | name: 'style',
37 | message: 'Do you want to use less, scss, styl or plain css?',
38 | choices: ['less', 'scss', 'styl', 'css'],
39 | default: 'less',
40 | store: true, // save for future
41 | }];
42 |
43 | this.prompt(prompts, function (props) {
44 | this.props = props;
45 | done();
46 | }.bind(this));
47 | },
48 |
49 | writing: function () {
50 | var header = getHeader(this.props.license);
51 | var cleanedName = _.kebabCase(this.name);
52 | this.fs.copyTpl(
53 | this.templatePath('index.js'),
54 | this.destinationPath('src/components/' + cleanedName + '/index.js'),
55 | {name: this.name, addStore: this.props.store, header: header, style: this.props.style}
56 | );
57 | this.fs.copyTpl(
58 | this.templatePath('template.jsx'),
59 | this.destinationPath('src/components/' + cleanedName + '/template.jsx'),
60 | {name: this.name, header: header}
61 | );
62 | this.fs.copyTpl(
63 | this.templatePath('style.less'),
64 | this.destinationPath('src/components/' + cleanedName + '/style.' + this.props.style),
65 | {name: this.name, header: header}
66 | );
67 |
68 | // add tests
69 | this.fs.copyTpl(
70 | this.templatePath('tests/test.jsx'),
71 | this.destinationPath('test/component-' + cleanedName + '.jsx'),
72 | {name: this.name, header: header}
73 | );
74 | // update test entry point
75 | var path = this.destinationPath('test/index.js');
76 | this.fs.copy(path, path, {
77 | process: function (content) {
78 | return content +
79 | 'require(\'./component-' + cleanedName + '.jsx\');' +
80 | '\n';
81 | }
82 | });
83 |
84 | if (this.props.store && this.props.store !== 'none') {
85 | this.npmInstall([this.props.store], {save: true});
86 | this.fs.copyTpl(
87 | this.templatePath('store-' + this.props.store + '.js'),
88 | this.destinationPath('src/components/' + cleanedName + '/store.js'),
89 | {name: this.name, header: header}
90 | );
91 |
92 | // if we're using Rx, patch up webpack config
93 | if (this.props.store === 'rxmq') {
94 | // update routes
95 | var degugPath = this.destinationPath('webpack.config.js');
96 | var prodPath = this.destinationPath('webpack.config.prod.js');
97 | var updateWpConfig = function (content) {
98 | // check if it's already there
99 | content = content.toString();
100 | // load as module for simpler parsing
101 | var data = esprima.parse(content);
102 | // append rx resolution
103 | data = addAlias(data);
104 | // generate new text
105 | var newContent = escodegen.generate(data);
106 | // return
107 | return newContent;
108 | };
109 | this.fs.copy(degugPath, degugPath, {process: updateWpConfig});
110 | this.fs.copy(prodPath, prodPath, {process: updateWpConfig});
111 | }
112 | }
113 | }
114 | });
115 |
--------------------------------------------------------------------------------
/component/templates/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | <%= header %>
3 | */
4 | import React from 'react';
5 | import Template from './template.jsx';<% if (addStore !== 'none') { %>
6 | import <%= _.camelCase(name) %>Channel from './store.js';
7 | <% } %>
8 | // only load style when using webpack
9 | /* istanbul ignore if */
10 | if (__WEBPACK__) {
11 | require('./style.<%= style %>');
12 | }
13 |
14 | const <%= _.capitalize(_.camelCase(name)) %>Component = React.createClass({
15 | render: Template,
16 | });
17 |
18 | export default <%= _.capitalize(_.camelCase(name)) %>Component;
19 |
--------------------------------------------------------------------------------
/component/templates/store-postal.js:
--------------------------------------------------------------------------------
1 | /*
2 | <%= header %>
3 | */
4 | import postal from 'postal';
5 |
6 | const <%= _.camelCase(name) %>Channel = postal.channel('<%= _.camelCase(name) %>');
7 |
8 | <%= _.camelCase(name) %>Channel.subscribe('action', ({option}, envelope) => {
9 | // ...
10 | });
11 |
12 | export default <%= _.camelCase(name) %>Channel;
13 |
--------------------------------------------------------------------------------
/component/templates/store-rxmq.js:
--------------------------------------------------------------------------------
1 | /*
2 | <%= header %>
3 | */
4 | import rxmq from 'rxmq';
5 |
6 | const <%= _.camelCase(name) %>Channel = rxmq.channel('<%= _.camelCase(name) %>');
7 |
8 | <%= _.camelCase(name) %>Channel.subject('action').subscribe(({option}, envelope) => {
9 | // ...
10 | });
11 |
12 | export default <%= _.camelCase(name) %>Channel;
13 |
--------------------------------------------------------------------------------
/component/templates/style.less:
--------------------------------------------------------------------------------
1 | /*
2 | <%= header %>
3 | */
4 | .<%= _.kebabCase(name) %>-component {
5 | // put you styles here
6 | }
7 |
--------------------------------------------------------------------------------
/component/templates/template.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | <%= header %>
3 | */
4 | import React from 'react';
5 |
6 | const render = function() {
7 | return (
8 |
9 | I am new <%= _.camelCase(name) %> component
10 |
11 | );
12 | };
13 |
14 | export default render;
15 |
--------------------------------------------------------------------------------
/component/templates/tests/test.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | <%= header %>
3 | */
4 | /* global describe, it */
5 | // import helpers
6 | import test from 'tape';
7 | import React from 'React/addons';
8 | const {TestUtils} = React.addons;
9 |
10 | // import page
11 | import Component from '../src/components/<%= _.camelCase(name) %>/index.js';
12 |
13 | test('<%= _.capitalize(name) %> component suite', function(it) {
14 | it.test('# should render', function(t) {
15 | // render
16 | const comp = TestUtils.renderIntoDocument();
17 |
18 | // check if link and name are correct
19 | const divs = TestUtils.scryRenderedDOMComponentsWithTag(comp, 'div');
20 | t.equal(1, divs.length);
21 | t.end();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/docker-deploy/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 |
4 | module.exports = yeoman.generators.Base.extend({
5 | initializing: function () {
6 | this.log('You called the Turris docker-deploy subgenerator.');
7 | },
8 |
9 | writing: function () {
10 | this.fs.copy(
11 | this.templatePath('Dockerfile'),
12 | this.destinationPath('Dockerfile-deploy')
13 | );
14 | }
15 | });
16 |
--------------------------------------------------------------------------------
/docker-deploy/templates/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx
2 |
3 | # add compiled files
4 | COPY dist /usr/share/nginx/html
5 |
--------------------------------------------------------------------------------
/docker/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 |
4 | module.exports = yeoman.generators.Base.extend({
5 | initializing: function () {
6 | this.log('You called the Turris docker subgenerator.');
7 | },
8 |
9 | writing: function () {
10 | this.fs.copy(
11 | this.templatePath('Dockerfile'),
12 | this.destinationPath('Dockerfile')
13 | );
14 | this.fs.copy(
15 | this.templatePath('dockerignore'),
16 | this.destinationPath('.dockerignore')
17 | );
18 | }
19 | });
20 |
--------------------------------------------------------------------------------
/docker/templates/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM iojs
2 |
3 | # pre-cache node_modules
4 | RUN mkdir /opt/tmp
5 | ADD package.json /opt/tmp/package.json
6 | WORKDIR /opt/tmp
7 | RUN npm install --silent
8 |
9 | # add app files
10 | ADD . /opt/application
11 | WORKDIR /opt/application
12 | RUN cp -R /opt/tmp/node_modules /opt/application/
13 |
14 | # run
15 | CMD ["npm", "start"]
16 |
--------------------------------------------------------------------------------
/docker/templates/dockerignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-turris",
3 | "version": "0.9.0",
4 | "description": "Yeoman generator for Turris.js applications done with React and ES6",
5 | "license": "MIT",
6 | "main": "app/index.js",
7 | "repository": "turrisjs/generator-turris",
8 | "author": {
9 | "name": "Tim Ermilov",
10 | "email": "yamalight@gmail.com",
11 | "url": "https://github.com/yamalight"
12 | },
13 | "scripts": {
14 | "test": "mocha"
15 | },
16 | "files": [
17 | "app",
18 | "page",
19 | "component",
20 | "docker",
21 | "common"
22 | ],
23 | "keywords": [
24 | "yeoman-generator",
25 | "turrisjs",
26 | "react",
27 | "react-router",
28 | "es6",
29 | "babel"
30 | ],
31 | "dependencies": {
32 | "chalk": "^1.0.0",
33 | "escodegen": "^1.6.1",
34 | "esprima": "^2.5.0",
35 | "lodash": "^3.8.0",
36 | "yeoman-generator": "^0.19.0",
37 | "yosay": "^1.0.2"
38 | },
39 | "devDependencies": {
40 | "mocha": "*"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/page/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var yeoman = require('yeoman-generator');
3 | var _ = require('lodash');
4 | var getHeader = require('../common/util/header').getHeader;
5 |
6 | module.exports = yeoman.generators.Base.extend({
7 | initializing: function () {
8 | var done = this.async();
9 |
10 | this.argument('name', {
11 | required: true,
12 | type: String,
13 | desc: 'The subgenerator name'
14 | });
15 |
16 | this.log('You called the Turris page subgenerator with the argument ' + this.name + '.');
17 |
18 | var prompts = [{
19 | type: 'input',
20 | name: 'license',
21 | message: 'Add header license? - no / yes (use LICENSE.md) / your file path',
22 | default: 'no',
23 | store: true, // save for future
24 | }];
25 |
26 | this.prompt(prompts, function (props) {
27 | this.props = props;
28 | done();
29 | }.bind(this));
30 | },
31 |
32 | writing: function () {
33 | var header = getHeader(this.props.license);
34 | var camelcaseName = _.camelCase(this.name);
35 | this.fs.copyTpl(
36 | this.templatePath('index.js'),
37 | this.destinationPath('src/pages/' + camelcaseName + '/index.js'),
38 | {name: this.name, header: header}
39 | );
40 | this.fs.copyTpl(
41 | this.templatePath('page.js'),
42 | this.destinationPath('src/pages/' + camelcaseName + '/page.js'),
43 | {name: this.name, header: header}
44 | );
45 | this.fs.copyTpl(
46 | this.templatePath('template.jsx'),
47 | this.destinationPath('src/pages/' + camelcaseName + '/template.jsx'),
48 | {name: this.name, header: header}
49 | );
50 | // update routes
51 | var path = this.destinationPath('src/routes.js');
52 | var capitalizedName = _.capitalize(camelcaseName);
53 | this.fs.copy(path, path, {
54 | process: function (content) {
55 | // add new import
56 | var re = /import(.+?);/;
57 | var newString = 'import ' + capitalizedName;
58 | newString += ' from \'./pages/' + camelcaseName + '/index.js\';\nimport$1;';
59 | var newContent = content.toString().replace(re, newString);
60 | // add new route to array
61 | newString = 'const routes = [\n ' + capitalizedName + ',';
62 | re = /const routes = \[/;
63 | newContent = newContent.replace(re, newString);
64 | // return new file
65 | return newContent;
66 | }
67 | });
68 |
69 | // add tests
70 | this.fs.copyTpl(
71 | this.templatePath('tests/test.jsx'),
72 | this.destinationPath('test/page-' + camelcaseName + '.jsx'),
73 | {name: this.name, header: header}
74 | );
75 | // update test entry point
76 | var testPath = this.destinationPath('test/index.js');
77 | this.fs.copy(testPath, testPath, {
78 | process: function (content) {
79 | return content +
80 | 'require(\'./page-' + camelcaseName + '.jsx\');' +
81 | '\n';
82 | }
83 | });
84 | }
85 | });
86 |
--------------------------------------------------------------------------------
/page/templates/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | <%= header %>
3 | */
4 | import React from 'react';
5 | import {Route} from 'react-router';
6 | import <%= _.capitalize(_.camelCase(name)) %>Page from './page.js';
7 |
8 | const route = React.createElement(Route, {name: '<%= _.camelCase(name) %>', key: 'route_<%= _.camelCase(name) %>', handler: <%= _.capitalize(_.camelCase(name)) %>Page});
9 |
10 | export default route;
11 |
--------------------------------------------------------------------------------
/page/templates/page.js:
--------------------------------------------------------------------------------
1 | /*
2 | <%= header %>
3 | */
4 | import React from 'react';
5 | import Template from './template.jsx';
6 |
7 | const <%= _.capitalize(_.camelCase(name)) %>Page = React.createClass({
8 | render: Template,
9 | });
10 |
11 | export default <%= _.capitalize(_.camelCase(name)) %>Page;
12 |
--------------------------------------------------------------------------------
/page/templates/template.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | <%= header %>
3 | */
4 | import React from 'react';
5 |
6 | const render = function() {
7 | return (
8 | I am new <%= _.camelCase(name) %> page
9 | );
10 | };
11 |
12 | export default render;
13 |
--------------------------------------------------------------------------------
/page/templates/tests/test.jsx:
--------------------------------------------------------------------------------
1 | /*
2 | <%= header %>
3 | */
4 | /* global describe, it */
5 | // import helpers
6 | import test from 'tape';
7 | import React from 'React/addons';
8 | const {TestUtils} = React.addons;
9 |
10 | // import page
11 | import Page from '../src/pages/<%= _.camelCase(name) %>/page.js';
12 |
13 | test('<%= _.capitalize(name) %> page suite', function(it) {
14 | it.test('# should render', function(t) {
15 | // render
16 | const comp = TestUtils.renderIntoDocument();
17 |
18 | // check if link and name are correct
19 | const divs = TestUtils.scryRenderedDOMComponentsWithTag(comp, 'h1');
20 | t.equal(1, divs.length);
21 | t.end();
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/docker-deploy.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var assert = require('yeoman-generator').assert;
5 | var helpers = require('yeoman-generator').test;
6 |
7 | describe('Turris:docker-deploy', function () {
8 | before(function (done) {
9 | helpers.run(path.join(__dirname, '../docker-deploy'))
10 | .withArguments('name')
11 | .withOptions({ skipInstall: true, force: true })
12 | .on('end', done);
13 | });
14 |
15 | it('creates files', function () {
16 | assert.file([
17 | 'Dockerfile-deploy'
18 | ]);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/test-app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var assert = require('yeoman-generator').assert;
5 | var helpers = require('yeoman-generator').test;
6 | var os = require('os');
7 |
8 | describe('turris:app', function () {
9 | before(function (done) {
10 | helpers.run(path.join(__dirname, '../app'))
11 | .withOptions({ skipInstall: true })
12 | .withPrompts({ someOption: true })
13 | .on('end', done);
14 | });
15 |
16 | it('creates files', function () {
17 | assert.file([
18 | 'package.json',
19 | '.editorconfig',
20 | '.gitignore'
21 | ]);
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/test-component.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var assert = require('yeoman-generator').assert;
5 | var helpers = require('yeoman-generator').test;
6 |
7 | describe('Turris:component', function () {
8 | before(function (done) {
9 | helpers.run(path.join(__dirname, '../component'))
10 | .withArguments('name')
11 | .withOptions({ skipInstall: true, force: true })
12 | .on('end', done);
13 | });
14 |
15 | it('creates files', function () {
16 | assert.file([
17 | 'somefile.js'
18 | ]);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/test-docker.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var assert = require('yeoman-generator').assert;
5 | var helpers = require('yeoman-generator').test;
6 |
7 | describe('Turris:docker', function () {
8 | before(function (done) {
9 | helpers.run(path.join(__dirname, '../docker'))
10 | .withArguments('name')
11 | .withOptions({ skipInstall: true, force: true })
12 | .on('end', done);
13 | });
14 |
15 | it('creates files', function () {
16 | assert.file([
17 | 'somefile.js'
18 | ]);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/test-page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var assert = require('yeoman-generator').assert;
5 | var helpers = require('yeoman-generator').test;
6 |
7 | describe('Turris:page', function () {
8 | before(function (done) {
9 | helpers.run(path.join(__dirname, '../page'))
10 | .withArguments('name')
11 | .withOptions({ skipInstall: true, force: true })
12 | .on('end', done);
13 | });
14 |
15 | it('creates files', function () {
16 | assert.file([
17 | 'somefile.js'
18 | ]);
19 | });
20 | });
21 |
--------------------------------------------------------------------------------