├── docs
└── .gitkeep
├── __tests__
├── .gitkeep
└── index.spec.js
├── src
├── fonts
│ └── .gitkeep
├── images
│ └── .gitkeep
├── javascripts
│ ├── .gitkeep
│ ├── utils
│ │ ├── index.js
│ │ ├── createStore.js
│ │ └── validation.js
│ ├── modules
│ │ ├── Home
│ │ │ ├── actions
│ │ │ │ └── index.js
│ │ │ ├── constants
│ │ │ │ └── index.js
│ │ │ ├── reducers
│ │ │ │ └── index.js
│ │ │ ├── index.js
│ │ │ └── components
│ │ │ │ └── index.jsx
│ │ ├── About
│ │ │ ├── constants
│ │ │ │ └── index.js
│ │ │ ├── actions
│ │ │ │ └── index.js
│ │ │ ├── reducers
│ │ │ │ └── index.js
│ │ │ ├── index.js
│ │ │ └── components
│ │ │ │ └── index.jsx
│ │ ├── Layout
│ │ │ ├── reducers
│ │ │ │ └── index.js
│ │ │ ├── index.js
│ │ │ └── components
│ │ │ │ └── index.jsx
│ │ └── index.js
│ ├── routes.js
│ └── index.jsx
├── stylesheets
│ └── .gitkeep
└── index.html
├── .eslintignore
├── .npmignore
├── .gitignore
├── .babelrc
├── configs
├── webpack
│ ├── index.js
│ ├── build.config.js
│ ├── production.config.js
│ ├── development.config.js
│ └── common.config.js
├── karma
│ ├── karma.entry.js
│ ├── development.config.js
│ ├── production.config.js
│ └── index.js
├── environments
│ ├── dependencies.js
│ └── index.js
└── webpack-dev-server
│ └── index.js
├── .travis.yml
├── .eslintrc
├── .editorconfig
├── gulpfile.js
├── CONTRIBUTING.md
├── README.md
└── package.json
/docs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/__tests__/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/fonts/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/images/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/javascripts/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/stylesheets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 |
2 | /build
3 | /node_modules
4 |
--------------------------------------------------------------------------------
/src/javascripts/utils/index.js:
--------------------------------------------------------------------------------
1 | export const createStore = require('./createStore');
2 |
--------------------------------------------------------------------------------
/src/javascripts/modules/Home/actions/index.js:
--------------------------------------------------------------------------------
1 | import {createAction} from 'redux-actions';
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /configs/karma/build
2 |
3 | /node_modules
4 | /build
5 | /index.html
6 | /lib
7 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "stage": 0,
3 | "optional": ["runtime"],
4 | "plugins": ["object-assign"]
5 | }
--------------------------------------------------------------------------------
/configs/webpack/index.js:
--------------------------------------------------------------------------------
1 | module.exports = exports = [
2 | require('./build.config')(),
3 | ];
4 |
--------------------------------------------------------------------------------
/__tests__/index.spec.js:
--------------------------------------------------------------------------------
1 | describe('Karma', function () {
2 | it('start', function () {
3 | expect(true).toBeTruthy();
4 | });
5 | });
6 |
--------------------------------------------------------------------------------
/src/javascripts/modules/About/constants/index.js:
--------------------------------------------------------------------------------
1 | import keyMirror from 'keymirror';
2 |
3 | module.exports = keyMirror({
4 | ABOUT_DEV_MODE: null,
5 | });
6 |
--------------------------------------------------------------------------------
/configs/karma/karma.entry.js:
--------------------------------------------------------------------------------
1 | const context = require.context('../../__tests__', true, /\.spec\.js$/);
2 | context.keys().forEach(context);
3 | module.exports = context;
4 |
--------------------------------------------------------------------------------
/src/javascripts/modules/Home/constants/index.js:
--------------------------------------------------------------------------------
1 | import keyMirror from 'keymirror';
2 |
3 | export default {
4 | ActionTypes: keyMirror({
5 | HOME_WELCOME_TO: null,
6 | }),
7 | };
8 |
--------------------------------------------------------------------------------
/configs/karma/development.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function karmaDevelopmentConfigModifier(config) {
2 | config.singleRun = false;
3 | config.autoWatch = true;
4 | return config;
5 | };
6 |
--------------------------------------------------------------------------------
/src/javascripts/modules/About/actions/index.js:
--------------------------------------------------------------------------------
1 | import {createAction} from 'redux-actions';
2 | import {ABOUT_DEV_MODE} from '../constants/index';
3 |
4 | export const enableDeveloperMode = createAction(ABOUT_DEV_MODE);
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: false
3 | node_js:
4 | - '4.1.1'
5 | cache:
6 | directories:
7 | - node_modules
8 | branches:
9 | only:
10 | - master
11 | env:
12 | global:
13 | - NODE_ENV=production
14 |
--------------------------------------------------------------------------------
/src/javascripts/modules/Home/reducers/index.js:
--------------------------------------------------------------------------------
1 | import {handleActions} from 'redux-actions';
2 | import Immutable from 'immutable';
3 |
4 | const initialState = Immutable.fromJS({});
5 |
6 | export default handleActions({}, initialState);
7 |
--------------------------------------------------------------------------------
/src/javascripts/modules/Layout/reducers/index.js:
--------------------------------------------------------------------------------
1 | import {handleActions} from 'redux-actions';
2 | import Immutable from 'immutable';
3 |
4 | const initialState = Immutable.fromJS({});
5 |
6 | export default handleActions({}, initialState);
7 |
--------------------------------------------------------------------------------
/configs/webpack/build.config.js:
--------------------------------------------------------------------------------
1 | const env = require('../environments');
2 | const config = require('./common.config')();
3 |
4 | module.exports = function makeClientConfig(type) {
5 | return require('./' + (type || env.NODE_ENV) + '.config')(config);
6 | };
7 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "env": {
4 | "browser": true,
5 | "node": true,
6 | "jasmine": true,
7 | "es6": true
8 | },
9 | "globals": {
10 | "__DEV__": true,
11 | "__PROD__": true,
12 | "__DEBUG__": true
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/configs/karma/production.config.js:
--------------------------------------------------------------------------------
1 | const DIR_DIST = require('../environments').DIR_DIST;
2 |
3 | module.exports = function karmaProductionConfigModifier(config) {
4 | config.singleRun = true;
5 | config.reporters = ['spec', 'coverage'];
6 | config.coverageReporter = {
7 | type: 'html',
8 | dir: DIR_DIST + '/coverage/',
9 | };
10 |
11 | return config;
12 | };
13 |
--------------------------------------------------------------------------------
/src/javascripts/routes.js:
--------------------------------------------------------------------------------
1 | import * as modules from './modules/index';
2 | export default () => {
3 | const {Views: {Layout, Home}, Routes: {About}} = modules;
4 | return {
5 | component: 'div',
6 | childRoutes: [{
7 | path: '/',
8 | component: Layout,
9 | indexRoute: {component: Home},
10 | childRoutes: [
11 | About,
12 | ],
13 | }],
14 | };
15 | };
16 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | React China new Forum Frontend
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/javascripts/modules/Layout/index.js:
--------------------------------------------------------------------------------
1 | import {connect} from 'react-redux';
2 | import {bindActionCreators} from 'redux';
3 |
4 | function mapStateToProps() {
5 | return {};
6 | }
7 |
8 | function mapDispatchToProps(dispatch) {
9 | return bindActionCreators({}, dispatch);
10 | }
11 |
12 | export const reducers = require('./reducers/index');
13 | export const view = connect(mapStateToProps, mapDispatchToProps)(require('./components/index'));
14 |
--------------------------------------------------------------------------------
/src/javascripts/modules/Layout/components/index.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react';
2 | export default class Layout extends Component {
3 | render() {
4 | return (
5 |
6 |
7 | {this.props.children}
8 |
9 |
10 | );
11 | }
12 | }
13 |
14 | Layout.propTypes = {
15 | children: PropTypes.element,
16 | };
17 |
18 |
--------------------------------------------------------------------------------
/src/javascripts/modules/About/reducers/index.js:
--------------------------------------------------------------------------------
1 | import {handleActions} from 'redux-actions';
2 | import Immutable from 'immutable';
3 |
4 | import {ABOUT_DEV_MODE} from '../constants/index';
5 |
6 | const initialState = Immutable.fromJS({
7 | count: 0,
8 | hint: 'You are now a developer!',
9 | });
10 |
11 | export default handleActions({
12 | [ABOUT_DEV_MODE]: state => state.update('count', count => count < 5 ? count + 1 : count),
13 | }, initialState);
14 |
--------------------------------------------------------------------------------
/src/javascripts/modules/Home/index.js:
--------------------------------------------------------------------------------
1 | import {connect} from 'react-redux';
2 | import {bindActionCreators} from 'redux';
3 |
4 | import {pushState} from 'redux-router';
5 |
6 | function mapStateToProps() {
7 | return {};
8 | }
9 |
10 | function mapDispatchToProps(dispatch) {
11 | return bindActionCreators({pushState}, dispatch);
12 | }
13 |
14 | export const reducers = require('./reducers/index');
15 | export const view = connect(mapStateToProps, mapDispatchToProps)(require('./components/index'));
16 |
--------------------------------------------------------------------------------
/src/javascripts/modules/index.js:
--------------------------------------------------------------------------------
1 | import {combineReducers} from 'redux';
2 |
3 | import * as layout from './Layout/index';
4 | import * as home from './Home/index';
5 | import * as about from './About/index';
6 |
7 | export default {
8 | Views: {
9 | Layout: layout.view,
10 | Home: home.view,
11 | },
12 | Routes: {
13 | About: about.route,
14 | },
15 | Reducers: combineReducers({
16 | layout: layout.reducers,
17 | home: home.reducers,
18 | about: about.reducers,
19 | }),
20 | };
21 |
--------------------------------------------------------------------------------
/configs/environments/dependencies.js:
--------------------------------------------------------------------------------
1 | module.exports = exports = {
2 | vendors: [
3 | 'immutable',
4 | 'react',
5 | 'react-redux',
6 | 'react-router',
7 | 'redux',
8 | 'redux-devtools',
9 | 'redux-devtools/lib/react',
10 | ],
11 | aliases: [
12 | 'actions',
13 | 'apis',
14 | 'components',
15 | 'constants',
16 | 'containers',
17 | 'entries',
18 | 'dispatchers',
19 | 'layouts',
20 | 'models',
21 | 'reducers',
22 | 'routes',
23 | 'services',
24 | 'stores',
25 | 'styles',
26 | 'utils',
27 | 'views',
28 | ],
29 | };
30 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # http://editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | charset = utf-8
9 | end_of_line = lf
10 | indent_style = space
11 | insert_final_newline = true
12 | max_line_length = 120
13 | trim_trailing_whitespace = true
14 |
15 | [*.js]
16 | indent_size = 2
17 |
18 | [*.json]
19 | indent_size = 2
20 |
21 | [package.json]
22 | indent_size = 2
23 |
24 | [*.md]
25 | max_line_length = 0
26 | trim_trailing_whitespace = false
27 |
28 | [COMMIT_EDITMSG]
29 | max_line_length = 0
30 |
--------------------------------------------------------------------------------
/src/javascripts/modules/Home/components/index.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react';
2 | export default class Home extends Component {
3 | _handlePushState(event) {
4 | event.preventDefault();
5 |
6 | const {pushState} = this.props;
7 | pushState(null, '/about');
8 | }
9 |
10 | render() {
11 | return (
12 |
13 |
Welcome to React-China
14 |
15 |
16 | );
17 | }
18 | }
19 |
20 | Home.propTypes = {
21 | pushState: PropTypes.func.isRequired,
22 | };
23 |
--------------------------------------------------------------------------------
/src/javascripts/modules/About/index.js:
--------------------------------------------------------------------------------
1 | import {connect} from 'react-redux';
2 | import {bindActionCreators} from 'redux';
3 |
4 | import {enableDeveloperMode} from './actions/index';
5 |
6 | function mapStateToProps(state) {
7 | return {store: state.store.about};
8 | }
9 |
10 | function mapDispatchToProps(dispatch) {
11 | return bindActionCreators({enableDeveloperMode}, dispatch);
12 | }
13 |
14 | export const reducers = require('./reducers/index');
15 | export const route = {
16 | path: 'about',
17 | getComponent(location, callback) {
18 | require.ensure([], (require) => {
19 | callback(null, connect(mapStateToProps, mapDispatchToProps)(require('./components/index')));
20 | });
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/src/javascripts/modules/About/components/index.jsx:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react';
2 | export default class About extends Component {
3 | render() {
4 | const {store, enableDeveloperMode} = this.props;
5 | const {count, hint} = store.toJS();
6 |
7 | const divHint = count > 4 ? {hint}
: null;
8 | return (
9 |
10 |
About
11 | click 5 times, and become a react developer!
12 | {divHint}
13 |
14 | );
15 | }
16 | }
17 |
18 | About.propTypes = {
19 | store: PropTypes.object.isRequired,
20 | enableDeveloperMode: PropTypes.func.isRequired,
21 | };
22 |
--------------------------------------------------------------------------------
/configs/webpack-dev-server/index.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const argv = require('yargs').argv;
3 | const env = require('../environments');
4 | const WebpackDevServer = require('webpack-dev-server');
5 | const makeCompiler = require('../webpack/build.config');
6 |
7 | const QUIET_MODE = !!argv.quiet;
8 |
9 | const server = new WebpackDevServer(webpack(makeCompiler()), {
10 | contentBase: env.inProject(env.DIR_SRC),
11 | hot: true,
12 | quiet: QUIET_MODE,
13 | noInfo: QUIET_MODE,
14 | lazy: false,
15 | stats: {colors: true},
16 | historyApiFallback: true,
17 | });
18 |
19 | server.listen(env.WEBPACK_PORT, 'localhost', () => {
20 | console.log('Webpack dev server running at localhost:' + env.WEBPACK_PORT);
21 | });
22 |
23 | module.exports = exports = server;
24 |
--------------------------------------------------------------------------------
/configs/webpack/production.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
3 |
4 | module.exports = function makeClientProductionConfig(config) {
5 | config.plugins.push(
6 | new ExtractTextPlugin('[name].[contenthash].css'),
7 | new webpack.optimize.UglifyJsPlugin({
8 | sourceMap: false,
9 | output: {
10 | 'comments': false,
11 | },
12 | compress: {
13 | 'unused': true,
14 | 'dead_code': true,
15 | },
16 | })
17 | );
18 |
19 | config.module.loaders = config.module.loaders.map((loader) => {
20 | // Extract CSS to a file
21 | if (/css/.test(loader.test)) {
22 | loader.loader = ExtractTextPlugin.extract(
23 | loader.loaders[0], loader.loaders.slice(1).join('!')
24 | );
25 | delete loader.loaders;
26 | }
27 |
28 | return loader;
29 | });
30 |
31 | return config;
32 | };
33 |
--------------------------------------------------------------------------------
/src/javascripts/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import {Provider} from 'react-redux';
5 | import {ReduxRouter} from 'redux-router';
6 |
7 | import {createStore} from './utils/index';
8 | import createRoutes from './routes';
9 |
10 | import * as modules from './modules/index';
11 |
12 | const store = createStore(modules.Reducers, {});
13 | const routes = createRoutes(store);
14 |
15 | let component = (
16 |
17 |
18 |
19 | );
20 |
21 | if (__DEBUG__) {
22 | const {DevTools, LogMonitor, DebugPanel} = require('redux-devtools/lib/react');
23 | component = (
24 |
25 | {component}
26 |
27 |
28 |
29 |
30 | );
31 | }
32 |
33 | const target = document.getElementById('mount');
34 | ReactDOM.render(component, target);
35 |
--------------------------------------------------------------------------------
/configs/karma/index.js:
--------------------------------------------------------------------------------
1 | const env = require('../environments');
2 | const KARMA_ENTRY_FILE = 'karma.entry.js';
3 |
4 | function makeDefaultConfig() {
5 | const preprocessors = {};
6 |
7 | preprocessors[KARMA_ENTRY_FILE] = ['webpack'];
8 | preprocessors[env.DIR_SRC + '/**/*.js'] = ['webpack'];
9 |
10 | return {
11 | files: [
12 | '../../node_modules/phantomjs-polyfill/bind-polyfill.js',
13 | KARMA_ENTRY_FILE,
14 | ],
15 | frameworks: ['jasmine'],
16 | preprocessors: preprocessors,
17 | reporters: ['dots'],
18 | browsers: ['PhantomJS'],
19 | webpack: (() => require('../webpack/build.config')())(),
20 | webpackMiddleware: {
21 | noInfo: true,
22 | },
23 | plugins: [
24 | require('karma-webpack'),
25 | require('karma-jasmine'),
26 | require('karma-coverage'),
27 | require('karma-phantomjs-launcher'),
28 | require('karma-spec-reporter'),
29 | ],
30 | };
31 | }
32 |
33 | module.exports = (karmaConfig) => {
34 | return karmaConfig.set(
35 | require('./' + env.NODE_ENV + '.config')(makeDefaultConfig())
36 | );
37 | };
38 |
--------------------------------------------------------------------------------
/src/javascripts/utils/createStore.js:
--------------------------------------------------------------------------------
1 | import {compose, applyMiddleware, combineReducers, createStore as _createStore} from 'redux';
2 |
3 | import createBrowserHistory from 'history/lib/createBrowserHistory';
4 |
5 | import thunk from 'redux-thunk';
6 | import promise from 'redux-promise';
7 | import createLogger from 'redux-logger';
8 |
9 | import {reducer as form} from 'redux-form';
10 | import {reduxReactRouter, routerStateReducer as router} from 'redux-router';
11 |
12 | const logger = createLogger();
13 | const middleware = [thunk, promise];
14 |
15 | if (__DEV__) {
16 | middleware.push(logger);
17 | }
18 | const createStoreWithMiddleware = applyMiddleware(...middleware);
19 |
20 | const composes = [createStoreWithMiddleware];
21 | if (__DEBUG__) {
22 | const {devTools, persistState} = require('redux-devtools');
23 | composes.push(devTools(), persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)));
24 | }
25 |
26 | let buildStore = compose(...composes)(_createStore);
27 | buildStore = reduxReactRouter({createHistory: createBrowserHistory})(buildStore);
28 |
29 | export default function createStore(reducers, initialState = {}) {
30 | return buildStore(combineReducers({store: reducers, form, router}), initialState);
31 | }
32 |
--------------------------------------------------------------------------------
/configs/webpack/development.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 |
3 | module.exports = function makeClientDevelopmentConfig(config) {
4 | config.debug = true;
5 | config.displayErrorDetails = true;
6 | config.outputPathinfo = true;
7 | config.devtool = 'eval-source-map';
8 | config.entry.app.push(
9 | 'webpack-dev-server/client?http://localhost:3000',
10 | 'webpack/hot/dev-server'
11 | );
12 |
13 | config.plugins.push(
14 | new webpack.HotModuleReplacementPlugin(),
15 | new webpack.NoErrorsPlugin()
16 | );
17 |
18 | // We need to apply the react-transform HMR plugin to the Babel configuration,
19 | // but _only_ when HMR is enabled. Putting this in the default development
20 | // configuration will break other tasks such as test:unit because Webpack
21 | // HMR is not enabled there, and these transforms require it.
22 | config.module.loaders = config.module.loaders.map((loader) => {
23 | if (/js/.test(loader.test)) {
24 | // loader.loaders.unshift('react-hot');
25 | loader.query.env.development.extra['react-transform'].transforms.push({
26 | transform: 'react-transform-hmr',
27 | imports: ['react'],
28 | locals: ['module'],
29 | });
30 | }
31 | return loader;
32 | });
33 |
34 | return config;
35 | };
36 |
--------------------------------------------------------------------------------
/configs/environments/index.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = (process.env.NODE_ENV || 'development').trim();
2 |
3 | const vendors = require('./dependencies').vendors;
4 | const aliases = require('./dependencies').aliases;
5 | const resolve = require('path').resolve;
6 | const argv = require('yargs').argv;
7 | const _slice = [].slice;
8 |
9 | const DIR_SRC = 'src';
10 | const DIR_DIST = 'build';
11 | const DIR_CONFIG = 'configs';
12 | const PROJECT_PATH = resolve(__dirname, '../../');
13 |
14 | function inProject() {
15 | return resolve.apply(resolve, [PROJECT_PATH].concat(_slice.apply(arguments)));
16 | }
17 |
18 | // ------------------------------------
19 | // Configuration Definition
20 | // ------------------------------------
21 | module.exports = exports = {
22 |
23 | // environment
24 | NODE_ENV: process.env.NODE_ENV,
25 | __PROD__: process.env.NODE_ENV === 'production',
26 | __DEV__: process.env.NODE_ENV === 'development',
27 | __DEBUG__: process.env.NODE_ENV === 'development' && !!argv.debug,
28 |
29 | // path helpers
30 | DIR_SRC: DIR_SRC,
31 | DIR_DIST: DIR_DIST,
32 | DIR_CONFIG: DIR_CONFIG,
33 | PROJECT_PATH: PROJECT_PATH,
34 | inProject: inProject,
35 | inSrc: inProject.bind(undefined, DIR_SRC),
36 | inDist: inProject.bind(undefined, DIR_DIST),
37 |
38 | // build system
39 | VENDOR_DEPENDENCIES: vendors,
40 | ALIAS: aliases,
41 |
42 | // server configuration
43 | WEBPACK_PORT: 3000,
44 | //SERVER_PORT: process.env.PORT || 4000
45 | };
46 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const eslint = require('gulp-eslint');
3 | const webpack = require('webpack');
4 | const del = require('del');
5 | const gutil = require('gulp-util');
6 | const WebpackDevServer = require('webpack-dev-server');
7 |
8 | gulp.task('clean', function (callback) {
9 | del(['build'], callback);
10 | });
11 |
12 | // Note: To have the process exit with an error code (1) on
13 | // lint error, return the stream and pipe to failOnError last.
14 | gulp.task('lint', function gulpLint() {
15 | return gulp.src(['src/**/*.jsx', 'src/**/*.js'])
16 | .pipe(eslint())
17 | .pipe(eslint.format())
18 | .pipe(eslint.failOnError());
19 | });
20 |
21 | gulp.task('dev', ['clean', 'webpack:dev'], function () {
22 | gulp.watch(['src/**/*'], ['webpack:build-dev']);
23 | });
24 |
25 | gulp.task('build', ['clean', 'webpack:build']);
26 |
27 | gulp.task('webpack:dev', function (callback) {
28 | var webpackConfig = require('./configs/webpack/development.config');
29 | webpack(Object.create(webpackConfig)).run(function (err, stats) {
30 | if (err) throw new gutil.PluginError('webpack:dev', err);
31 | gutil.log('[webpack:dev]', stats.toString({
32 | colors: true
33 | }));
34 | callback();
35 | });
36 | });
37 |
38 | gulp.task('webpack:build', function (callback) {
39 | var webpackConfig = Object.create(require('./configs/webpack/production.config'));
40 | webpack(webpackConfig, function (err, stats) {
41 | if (err) throw new gutil.PluginError('webpack:build', err);
42 | gutil.log('[webpack:build]', stats.toString({
43 | colors: true
44 | }));
45 | callback();
46 | });
47 | });
48 |
49 | gulp.task('default', ['lint'], function gulpDefault() {
50 | // This will only run if the lint task is successful...
51 | });
52 |
--------------------------------------------------------------------------------
/src/javascripts/utils/validation.js:
--------------------------------------------------------------------------------
1 | const isEmpty = value => value === undefined || value === null || value === '';
2 | const join = (rules) => value => rules.map(rule => rule(value)).filter(error => !!error)[0 /* first error */];
3 |
4 | export function email(value) {
5 | // Let's not start a debate on email regex. This is just for an example app!
6 | if (!isEmpty(value) && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
7 | return 'Invalid email address';
8 | }
9 | }
10 |
11 | export function required(value) {
12 | if (isEmpty(value)) {
13 | return 'Required';
14 | }
15 | }
16 |
17 | export function minLength(min) {
18 | return value => {
19 | if (!isEmpty(value) && value.length < min) {
20 | return `Must be at least ${min} characters`;
21 | }
22 | };
23 | }
24 |
25 | export function maxLength(max) {
26 | return value => {
27 | if (!isEmpty(value) && value.length > max) {
28 | return `Must be no more than ${max} characters`;
29 | }
30 | };
31 | }
32 |
33 | export function integer(value) {
34 | if (!Number.isInteger(Number(value))) {
35 | return 'Must be an integer';
36 | }
37 | }
38 |
39 | export function oneOf(enumeration) {
40 | return value => {
41 | if (!~enumeration.indexOf(value)) {
42 | return `Must be one of: ${enumeration.join(', ')}`;
43 | }
44 | };
45 | }
46 |
47 | export function createValidator(rules) {
48 | return (data = {}) => {
49 | const errors = {valid: true};
50 | Object.keys(rules).forEach((key) => {
51 | const rule = join([].concat(rules[key])); // concat enables both functions and arrays of functions
52 | const error = rule(data[key]);
53 | if (error) {
54 | errors[key] = error;
55 | errors.valid = false;
56 | }
57 | });
58 | return errors;
59 | };
60 | }
61 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | #Contributing
2 |
3 | So, you want to contribute to this project! That's awesome. However, before
4 | doing so, please read the following simple steps how to contribute. This will
5 | make the life easier and will avoid wasting time on things which are not
6 | requested.
7 |
8 | ## Discuss the changes before doing them
9 | - First of all, open an issue in the repository, using the [bug tracker][1],
10 | describing the contribution you'd like to make, the bug you found or any
11 | other ideas you have. This will help us to get you started on the right
12 | foot.
13 |
14 | - If it makes sense, add the platform and software information (e.g. operating
15 | system, Node.JS version etc), screenshots (so we can see what you're seeing).
16 |
17 | - It's recommended to wait for feedback before continuing to next steps. However,
18 | if the issue is clear (e.g. a typo) and the fix is simple, you can continue
19 | and fix it.
20 |
21 | ## Fixing issues
22 | - Fork the project in your account and create a branch with your fix:
23 | `some-great-feature` or `some-issue-fix`.
24 |
25 | - Commit your changes in that branch, writing the code following the
26 | [code style][2] from [Airbnb][3]. If the project contains tests (generally, the `test`
27 | directory), you are encouraged to add a test as well.
28 |
29 | - If the project contains a `package.json` file add yourself in the
30 | `contributors` array (if it doesn't exist, create it):
31 |
32 | ```json
33 | {
34 | "contributors": [
35 | "Your Name (http://your.website)
36 | ]
37 | }
38 | ```
39 |
40 | ## Creating a pull request
41 |
42 | - Open a pull request, and reference the initial issue in the pull request
43 | message (e.g. *fixes # {
24 | module[name] = env.inSrc('javascripts/' + name);
25 | return module;
26 | }, {}),
27 | },
28 | module: {
29 | preLoaders: [
30 | {test: /\.(js|jsx)$/, loaders: ['eslint-loader'], include: env.inProject(env.DIR_SRC, 'javascripts')},
31 | ],
32 | loaders: [
33 | {
34 | test: /\.(js|jsx)$/,
35 | include: [env.inProject(env.DIR_SRC), env.inProject(env.DIR_CONFIG)],
36 | loader: 'babel',
37 | query: {
38 | stage: 0,
39 | optional: ['runtime'],
40 | env: {
41 | development: {
42 | plugins: ['react-transform'],
43 | extra: {
44 | 'react-transform': {
45 | transforms: [{
46 | transform: 'react-transform-catch-errors',
47 | imports: ['react', 'redbox-react'],
48 | }],
49 | },
50 | },
51 | },
52 | },
53 | },
54 | },
55 | {
56 | test: /\.scss$/,
57 | loaders: [
58 | 'style-loader',
59 | 'css-loader',
60 | 'autoprefixer?browsers=last 2 version',
61 | 'sass-loader?includePaths[]=' + env.inSrc('stylesheets'),
62 | ],
63 | },
64 | ],
65 | },
66 | eslint: {configFile: env.inProject('.eslintrc'), failOnError: env.__PROD__, emitWarning: env.__DEV__},
67 | plugins: [
68 | new webpack.DefinePlugin({
69 | 'process.env': {
70 | 'NODE_ENV': JSON.stringify(env.NODE_ENV),
71 | },
72 | '__DEBUG__': env.__DEBUG__,
73 | '__DEV__': env.__DEV__,
74 | '__PROD__': env.__PROD__,
75 | }),
76 | new webpack.optimize.OccurrenceOrderPlugin(),
77 | new webpack.optimize.DedupePlugin(),
78 | new HtmlWebpackPlugin({
79 | template: env.inSrc('index.html'),
80 | hash: true,
81 | filename: 'index.html',
82 | minify: env.__PROD__,
83 | inject: 'body',
84 | }),
85 | ],
86 | };
87 | }
88 |
89 | module.exports = function makeConfig(configModifier) {
90 | return assign({}, makeDefaultConfig(), configModifier);
91 | };
92 |
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Unmaintained
2 | Sorry everyone, but we are no longer maintaining this project.
3 |
4 | # React China new Forum Frontend
5 |
6 | [![Build Status][travis-ci-image]][travis-ci-url]
7 |
8 | [![Gitter chat][gitter-image]][gitter-url]
9 | [![GitHub license][license-image]][license-url]
10 |
11 | [![dependencies][dependencies-image]][dependencies-url]
12 | [![devDependency][dev-deps-image]][dev-deps-url]
13 | [![peerDependency][peer-deps-image]][peer-deps-url]
14 |
15 | ----
16 |
17 | > React 中文社区 React.js 版前端界面
18 |
19 | ## 状态
20 |
21 | > 正在初始化项目
22 |
23 | ## 计划
24 |
25 | * 完成 JSX 基本开发框架
26 | * 梳理 Discourse API
27 | * 制定 Actions, Store, Router 规范
28 | * 完成顶层组件
29 | * 组件细化
30 |
31 | ## Develop
32 |
33 | ```text
34 | npm i
35 | ```
36 |
37 | You need a static file server for the HTML files. Personally I suggest using Nginx.
38 |
39 | Develop:
40 |
41 | ```bash
42 | gulp html # regenerate index.html
43 | webpack-dev-server --hot # enable live-reloading
44 | ```
45 |
46 | Build (Pack and optimize js, reivision js and add entry in `index.html`):
47 |
48 | ```bash
49 | gulp build
50 | ```
51 |
52 | ## Usage
53 |
54 | #### `npm run clean`
55 | Remove folder `./build`
56 |
57 | #### `npm run dev`
58 | Runs the webpack build system just like in `compile` but enables HMR and react hot-loader. The webpack dev server can be found at `localhost:3000`.
59 |
60 | #### `npm run dev:debug`
61 | Same as `npm run dev` but enables `--debug` flag automatically.
62 |
63 | #### `npm run dev:quiet`
64 | Same as `npm run dev` but disables verbose debugging information.
65 |
66 | #### `npm run compile`
67 | Runs the Webpack build system with your current NODE_ENV and compiles the application to disk (`~/build`).
68 |
69 | #### `npm run test`
70 | Runs all tests for the application.
71 |
72 | #### `npm run test:unit`
73 | Similar to `npm run test`, but only runs unit tests. In development mode this will run in watch mode and re-run individual test files when they change.
74 |
75 | #### `npm run test:e2e`
76 | *TODO*
77 |
78 | ## Contribution
79 |
80 | - If you want to discuss something or just need help, here is our [gitter.im room](https://gitter.im/react-china/forum-frontend).
81 | - Have an idea? Found a bug? See [how to contribute][contributing-url].
82 |
83 | ## License
84 |
85 | MIT (http://www.opensource.org/licenses/mit-license.php)
86 |
87 | [contributing-url]: /CONTRIBUTING.md
88 |
89 | [gitter-url]: https://gitter.im/react-china/forum-frontend
90 | [gitter-image]: https://badges.gitter.im/Join%20Chat.svg
91 |
92 | [license-image]: https://img.shields.io/github/license/mashape/apistatus.svg
93 | [license-url]: http://www.opensource.org/licenses/mit-license.php
94 |
95 | [travis-ci-image]: https://travis-ci.org/react-china/forum-frontend.svg
96 | [travis-ci-url]: https://travis-ci.org/react-china/forum-frontend
97 |
98 | [dependencies-image]: https://david-dm.org/react-china/forum-frontend.svg
99 | [dependencies-url]: https://david-dm.org/react-china/forum-frontend
100 |
101 | [dev-deps-image]: https://david-dm.org/react-china/forum-frontend/dev-status.svg
102 | [dev-deps-url]: https://david-dm.org/react-china/forum-frontend#info=devDependencies
103 |
104 | [peer-deps-image]: https://david-dm.org/react-china/forum-frontend/peer-status.svg
105 | [peer-deps-url]: https://david-dm.org/react-china/forum-frontend#info=peerDependencies
106 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "forum-frontend",
3 | "version": "0.0.1",
4 | "description": "React implemented frontend for react-china.org",
5 | "engines": {
6 | "node": "4.1.2",
7 | "npm": "3.3.5"
8 | },
9 | "keywords": [
10 | "react-china"
11 | ],
12 | "license": "MIT",
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/react-china/forum-frontend.git"
16 | },
17 | "bugs": {
18 | "url": "https://github.com/react-china/forum-frontend/issues"
19 | },
20 | "homepage": "https://github.com/react-china/forum-frontend",
21 | "scripts": {
22 | "clean": "rm -rf build",
23 | "compile": "webpack --config ./configs/webpack",
24 | "start": "npm run dev",
25 | "dev": "node --harmony ./configs/webpack-dev-server",
26 | "dev:debug": "npm run dev -- --debug",
27 | "dev:quiet": "npm run dev -- --quiet",
28 | "test": "npm run test:unit",
29 | "test:unit": "karma start ./configs/karma/index.js"
30 | },
31 | "dependencies": {
32 | "babel-core": "^5.8.25",
33 | "babel-loader": "^5.3.2",
34 | "babel-plugin-object-assign": "^1.2.1",
35 | "babel-runtime": "^5.8.25",
36 | "classnames": "^2.2.0",
37 | "expose-loader": "^0.7.1",
38 | "extract-text-webpack-plugin": "^0.9.1",
39 | "history": "^1.13.0",
40 | "html-webpack-plugin": "^1.6.2",
41 | "immutable": "^3.7.5",
42 | "jasmine": "^2.3.2",
43 | "jasmine-core": "^2.3.4",
44 | "jquery": "^2.1.4",
45 | "karma": "^0.13.15",
46 | "karma-coverage": "^0.5.3",
47 | "karma-jasmine": "^0.3.6",
48 | "karma-phantomjs-launcher": "^0.2.1",
49 | "karma-spec-reporter": "0.0.22",
50 | "karma-webpack": "^1.7.0",
51 | "keymirror": "^0.1.1",
52 | "lodash": "^3.10.1",
53 | "normalizr": "^1.4.0",
54 | "object-assign": "^4.0.1",
55 | "phantomjs": "^1.9.18",
56 | "phantomjs-polyfill": "0.0.1",
57 | "react": "^0.14.2",
58 | "react-document-meta": "^2.0.0",
59 | "react-dom": "^0.14.2",
60 | "react-mixin": "^3.0.3",
61 | "react-redux": "^4.0.0",
62 | "react-router": "^1.0.0",
63 | "redux": "^3.0.4",
64 | "redux-actions": "^0.8.0",
65 | "redux-form": "^3.0.6",
66 | "redux-logger": "^2.0.4",
67 | "redux-promise": "^0.5.0",
68 | "redux-router": "^1.0.0-beta4",
69 | "redux-thunk": "^1.0.0",
70 | "webpack": "^1.12.4",
71 | "yargs": "^3.29.0"
72 | },
73 | "devDependencies": {
74 | "autoprefixer": "^6.1.0",
75 | "autoprefixer-loader": "^3.1.0",
76 | "babel-eslint": "^4.1.5",
77 | "babel-plugin-react-transform": "^1.1.1",
78 | "cirru-script": "^0.5.0-alpha1",
79 | "css-loader": "^0.22.0",
80 | "del": "^2.0.2",
81 | "eslint": "^1.9.0",
82 | "eslint-config-airbnb": "1.0.0",
83 | "eslint-loader": "^1.1.1",
84 | "eslint-plugin-react": "^3.8.0",
85 | "file-loader": "^0.8.4",
86 | "gulp": "^3.9.0",
87 | "gulp-eslint": "^1.1.0",
88 | "gulp-util": "^3.0.7",
89 | "imports-loader": "^0.6.5",
90 | "loader-utils": "^0.2.11",
91 | "node-libs-browser": "^0.5.3",
92 | "node-sass": "^3.4.2",
93 | "react-hot-loader": "^2.0.0-alpha-2",
94 | "react-transform-catch-errors": "^1.0.0",
95 | "react-transform-hmr": "^1.0.1",
96 | "redbox-react": "^1.1.1",
97 | "redux-devtools": "github:tomatau/redux-devtools#e126f799bb81a61e4a2ce69fbc501e09b15b5f5d",
98 | "rsyncwrapper": "^0.4.3",
99 | "run-sequence": "^1.1.4",
100 | "sass-loader": "^3.1.1",
101 | "stir-template": "^0.1.4",
102 | "style-loader": "^0.13.0",
103 | "url-loader": "^0.5.6",
104 | "volubile-ui": "0.0.11",
105 | "webpack-dev-server": "^1.12.1",
106 | "webpack-stream": "^2.1.1"
107 | }
108 | }
109 |
--------------------------------------------------------------------------------