Socket
Socket
Sign inDemoInstall

react-isomorphic-boilerplate

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-isomorphic-boilerplate - npm Package Compare versions

Comparing version 0.0.0 to 0.1.0

.eslintignore

48

package.json
{
"name": "react-isomorphic-boilerplate",
"version": "0.0.0",
"version": "0.1.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "DEBUG=*,-nodemon*,-express*,-send nodemon dist/server.js",
"start": "DEBUG=*,-nodemon*,-express*,-send nodemon --inspect dist/server.js",
"build:client:dev": "rm -f dist/*-client.js && webpack --env=dev --progress --profile --colors",

@@ -14,4 +14,5 @@ "build:server:dev": "webpack --env=dev --config=webpack.server.js --progress --profile --colors",

"build:server:prod": "webpack --env=prod --config=webpack.server.js --progress --profile --colors",
"eslint": "eslint src",
"test": "npm run eslint"
"eslint": "eslint ./",
"test": "npm run eslint && cross-env NODE_ENV=test nyc ava --verbose",
"report": "cross-env NODE_ENV=test nyc ava && nyc report --reporter=lcov"
},

@@ -21,5 +22,7 @@ "devDependencies": {

"autoprefixer": "^7.1.6",
"ava": "^0.23.0",
"babel-core": "^6.26.0",
"babel-eslint": "^8.0.1",
"babel-loader": "^7.1.2",
"babel-plugin-istanbul": "^4.1.5",
"babel-preset-env": "^1.6.0",

@@ -29,4 +32,7 @@ "babel-preset-es2015": "^6.24.1",

"babel-preset-stage-2": "^6.24.1",
"cross-env": "^5.1.1",
"css-loader": "^0.28.7",
"debug": "^3.1.0",
"enzyme": "^3.2.0",
"enzyme-adapter-react-16": "^1.1.0",
"eslint": "^4.8.0",

@@ -37,9 +43,15 @@ "eslint-loader": "^1.9.0",

"file-loader": "^1.1.5",
"ignore-styles": "^5.0.1",
"immutability-helper": "^2.4.0",
"lodash": "^4.17.4",
"mock-require": "^2.0.2",
"node-sass": "^4.6.1",
"nodemon": "^1.12.1",
"nyc": "^11.3.0",
"postcss-loader": "^2.0.8",
"prop-types": "^15.6.0",
"react-addons-test-utils": "^15.6.2",
"react-redux": "^5.0.6",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"redux": "^3.7.2",

@@ -51,3 +63,6 @@ "redux-logger": "^3.0.6",

"sass-loader": "^6.0.6",
"sinon": "^4.1.2",
"style-loader": "^0.19.0",
"superagent": "^3.8.1",
"supertest": "^3.0.0",
"uglifyjs-webpack-plugin": "^1.0.1",

@@ -61,3 +76,4 @@ "url-loader": "^0.6.2",

"react": "^16.1.1",
"react-dom": "^16.1.1"
"react-dom": "^16.1.1",
"react-helmet": "^5.2.0"
},

@@ -79,3 +95,23 @@ "description": "This boilerplate would help you build a react/redux/react-router isomorphic/universal web app",

},
"homepage": "https://github.com/ddhp/react-isomorphic-boilerplate#readme"
"homepage": "https://github.com/ddhp/react-isomorphic-boilerplate#readme",
"ava": {
"files": [
"src/**/__tests__/**/*.js",
"!test/fixtures/**/*",
"!test/helpers/**/*"
],
"require": [
"babel-register",
"ignore-styles",
"./src/enzyme-setup.js"
],
"babel": "inherit"
},
"nyc": {
"require": [
"babel-register"
],
"sourceMap": false,
"instrument": false
}
}
# react-isomorphic-boilerplate
[![Build Status](https://img.shields.io/travis/ddhp/react-isomorphic-boilerplate/master.svg?style=flat-square)](https://travis-ci.org/ddhp/react-isomorphic-boilerplate)
[![Dependency Status](https://dependencyci.com/github/ddhp/react-isomorphic-boilerplate/badge)](https://dependencyci.com/github/ddhp/react-isomorphic-boilerplate)
This boilerplate would help you build a react/redux/react-router isomorphic/universal web app
## Feature
- isomorphic: same code runs on server and browser
- SEO: information benefits to search engine would be rendered on server side
- easy to start
- production ready
## Concept
### development
Start 3 process to start developing your app:
1. `npm run build:client:dev:w`: build client side code and watch it's change
2. `npm run build:server:dev:w`: build server side conde and watch it's change
3. `npm start`: nodemon executing dist/server.js, only watches on it's change
### Development
0. `yarn` and run 3 processes to start developing your app:
1. `yarn run build:client:dev:w`: build client side code and watch file change.
2. `yarn run build:server:dev:w`: build server side conde and watch file change.
3. `yarn start`: nodemon executing `dist/server.js`, and only watches on it's change,
[--inspect](https://nodejs.org/en/docs/guides/debugging-getting-started/#enable-inspector) param is given,
you can debug nodejs server on chrome-devtools.
All development code are built with [source map](http://blog.teamtreehouse.com/introduction-source-maps).
### Log
import stdout and define namespace ([example](https://github.com/ddhp/react-isomorphic-boilerplate/blob/master/src/server/pages.js)), then turn on debug message depends on platform:
- browser: allow debug log by type `localStorage.debug = '*'` in console
- server: run node with `DEBUG=*`, see `package.json.scripts.start`.
### Packing code

@@ -19,7 +37,22 @@ - Fonts: font face are set in `src/client/global.scss`

### Style
- [global.scss](https://github.com/ddhp/react-isomorphic-boilerplate/blob/master/src/client/global.scss) imports [reset.css](https://www.npmjs.com/package/reset-css)
- [reset.css](https://www.npmjs.com/package/reset-css) reseting default style imported in [global.scss](https://github.com/ddhp/react-isomorphic-boilerplate/blob/master/src/client/global.scss).
### SEO
- Define `loadData` method in your route to prefetch data needed for SEO. ([example](https://github.com/ddhp/react-isomorphic-boilerplate/blob/master/src/routes/main.js))
- [react-helmet](https://github.com/nfl/react-helmet) help us set head (or specific property) in container and overwrites setting from parent, very handy.
- Define your basic helmet setting in each route file, see [src/routers/main.js](https://github.com/ddhp/react-isomorphic-boilerplate/blob/master/src/routes/main.js),
my idea is - head can be different for different entry of app.
- Overwrites head info in containers. ([example](https://github.com/ddhp/react-isomorphic-boilerplate/blob/master/src/containers/About/index.js))
### Test
- [AVA](https://github.com/avajs/ava) as test runner.
- Don't use [webpack alias](https://webpack.js.org/configuration/resolve/#resolve-alias) in code base
- We use [mock-require](https://github.com/boblauer/mock-require) to mock dependencies to make test as independent as possible.
As it's name says, it only support `require` not import, so if your importing module has some dependencies needs to be mocked,
remember to `require` instead of import them in your test code.
Also append `.default` to get the right reference if your module is defined in es6 way. (see [server test](https://github.com/ddhp/react-isomorphic-boilerplate/blob/master/src/server/__tests__/index.js) for example)
### Production build
1. `npm run build:client:prod`
2. `npm run build:server:prod`
1. `yarn run build:client:prod`
2. `yarn run build:server:prod`

@@ -31,8 +64,12 @@ ## TODOS:

4. ~font / img loader~
5. test on server
6. test on react component
7. apply react router
8. apply logic base on path(seo optimized)
5. ~test on server~
6. ~source map~
7. ~test on react component~
7-1. ~coverage report~
8. ~apply react router~
9. ~apply logic base on path(seo optimized)~
10. ~set head info~
11. ~fetch data from and submit to local api~
## LICENSE
MIT

@@ -1,11 +0,11 @@

import { get as _get } from 'lodash';
// import { get as _get } from 'lodash';
import stdout from '../stdout';
const debug = stdout('action');
import request from 'superagent';
export const ACCUMULATE_COUNT = 'ACCUMULATE_COUNT';
export function accumulateCount() {
return (dispatch, getState) => {
let count = _get(getState(), 'pages.home.count', 0);
count ++;
return (dispatch/*, getState*/) => {
dispatch({
type: ACCUMULATE_COUNT,
payload: count
});

@@ -30,1 +30,36 @@ };

}
export const FETCH_POSTS = 'FETCH_POSTS';
export function fetchPosts() {
return function (dispatch) {
return request
.get('http://localhost:3333/api/post')
.then((res) => {
dispatch({
type: FETCH_POSTS,
payload: JSON.parse(res.text)
});
}, (err) => {
debug(err);
});
};
}
export const ADD_POST = 'ADD_POST';
export function addPost(post) {
return function (dispatch) {
return request
.post('/api/post')
.send(post)
.end((err, res) => {
if (err) {
debug(err);
} else {
dispatch({
type: ADD_POST,
payload: JSON.parse(res.text)
});
}
});
};
}

9

src/client/entry-main.js
import React from 'react';
import { hydrate } from 'react-dom';
import { Provider } from 'react-redux';
import Home from 'Src/containers/Home';
import configureStore from 'Src/configureStore';
import { BrowserRouter as Router, browserHistory } from 'react-router-dom';
import Routes from '../routes/entry-main';
import configureStore from '../configureStore';
import './global.scss';

@@ -22,5 +23,7 @@

<Provider store={store}>
<Home />
<Router history={browserHistory}>
<Routes />
</Router>
</Provider>,
document.getElementById('app-mount-point')
);
import React from 'react';
import { Component } from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
import { connect } from 'react-redux';
import { get as _get } from 'lodash';
import { accumulateCount, updateMeID, updateMe } from '../../actions';
import { Link } from 'react-router-dom';
import { accumulateCount, updateMeID, updateMe, addPost } from '../../actions';
import stdout from '../../stdout';

@@ -11,3 +13,3 @@ const debug = stdout('container/home/index');

import './style.scss';
import logoImg from 'Src/assets/images/react-logo.png';
import logoImg from '../../assets/images/react-logo.png';
debug(logoImg);

@@ -21,3 +23,5 @@

updateMe: PropTypes.func,
me: PropTypes.object
addPost: PropTypes.func,
me: PropTypes.object,
posts: PropTypes.array
}

@@ -27,3 +31,6 @@

super(props);
this.state = {value: ''};
this.state = {
value: '',
postText: ''
};

@@ -33,2 +40,4 @@ this.handleChange = this.handleChange.bind(this);

this.onClick = this.onClick.bind(this);
this.onPostTextChanged = this.onPostTextChanged.bind(this);
this.onPostSubmit = this.onPostSubmit.bind(this);
}

@@ -53,2 +62,15 @@

onPostTextChanged(e) {
this.setState({postText: e.target.value});
}
onPostSubmit(e) {
e.preventDefault();
const { postText } = this.state,
payload = {
text: postText
};
this.props.addPost(payload);
}
componentDidMount() {

@@ -58,19 +80,25 @@ this.props.accumulateCount();

shouldComponentUpdate(nextProps, nextState) {
const { name: thisName, sex: thisSex } = this.props.me,
{ name: nextName, sex: nextSex } = nextProps.me;
if (thisName !== nextName ||
thisSex !== nextSex ||
this.state !== nextState) {
return true;
} else {
return false;
}
}
// shouldComponentUpdate(nextProps, nextState) {
// const { name: thisName, sex: thisSex } = this.props.me,
// { name: nextName, sex: nextSex } = nextProps.me;
// if (thisName !== nextName ||
// thisSex !== nextSex ||
// this.state !== nextState) {
// return true;
// } else {
// return false;
// }
// }
render() {
debug('render method');
const { name, sex } = this.props.me;
const { name, sex } = this.props.me,
{ posts } = this.props;
return (
<div className="page--home">
<Helmet>
<title>Home</title>
<meta name="description" content="home page shows posts" />
<meta name="og:title" content="home page" />
</Helmet>
<h1 className="demo--font">

@@ -80,12 +108,32 @@ Title in Spectral SC

</h1>
<Link to="/about">To About</Link>
<div className="demo--bg"></div>
<img className="demo--img-src" src={logoImg} />
<ul className="posts">
{posts.map((p) => {
return (
<li key={p.id}>
{p.id}, {p.text}
</li>
);
})}
</ul>
<form className="form--post" onSubmit={this.onPostSubmit}>
<label>
TEXT:
<input className="input--post-text" type="text" value={this.state.postText} onChange={this.onPostTextChanged} />
</label>
<input type="submit" value="Submit" />
</form>
counter: {this.props.count}
<div>name: {name}</div>
<div>sex: {sex}</div>
<form onSubmit={this.handleSubmit}>
<form className="form--me" onSubmit={this.handleSubmit}>
<label>
ID:
<input type="text" value={this.state.value} onChange={this.handleChange} />
<input className="input--id" type="text" value={this.state.value} onChange={this.handleChange} />
</label>

@@ -102,7 +150,13 @@ <input type="submit" value="Submit" />

const count = _get(state, 'pages.home.count', 0),
me = _get(state, 'entities.me', {});
entities = _get(state, 'entities'),
me = _get(entities, 'me'),
post = _get(entities, 'post'),
posts = Object.keys(post.byId).map((k) => {
return post.byId[k];
});
return {
count,
me
me,
posts
};

@@ -121,2 +175,5 @@ }

return dispatch(updateMe(me));
},
addPost: (post) => {
return dispatch(addPost(post));
}

@@ -123,0 +180,0 @@ };

import { combineReducers } from 'redux';
import meReducer from './me';
import postReducer from './post';
export default combineReducers({
me: meReducer
me: meReducer,
post: postReducer
});

@@ -6,7 +6,7 @@ const initialState = {

export default function homeReducer(state = initialState, action) {
const payload = action.payload;
// const payload = action.payload;
switch (action.type) {
case 'ACCUMULATE_COUNT': {
return Object.assign({}, state, {
count: payload
count: ++ state.count
});

@@ -16,3 +16,3 @@ // state.count = payload

}
default:

@@ -19,0 +19,0 @@ return state;

@@ -1,11 +0,7 @@

import React from 'react';
import ReactServer from 'react-dom/server';
import Express from 'express';
import { Provider } from 'react-redux';
import Layout from './layout';
import configureStore from 'Src/configureStore';
import Home from 'Src/containers/home';
import stdout from 'Src/stdout';
import stdout from '../stdout';
import pagesMiddleware from './pages';
import apiMiddleware from './api';
const debug = stdout('server-app');
const debug = stdout('app-server');
const app = Express(),

@@ -17,22 +13,5 @@ port = 3333;

app.use('/', (req, res) => {
const store = configureStore(),
reduxState = JSON.stringify(store.getState()).replace(/</g, '\\u003c');
apiMiddleware(app);
pagesMiddleware(app);
const content = (
<Provider store={store}>
<Home />
</Provider>
);
const htmlString = ReactServer.renderToString(
<Layout
content={content}
reduxState={reduxState}
/>
);
res.send(`<!DOCTYPE HTML>${htmlString}`);
});
const server = app.listen(port, function () {

@@ -44,1 +23,3 @@ const host = server.address().address,

});
export default app;

@@ -10,7 +10,8 @@ import React from 'react';

content: PropTypes.object,
reduxState: PropTypes.string
reduxState: PropTypes.string,
head: PropTypes.object
}
render() {
const { content, reduxState } = this.props;
const { content, reduxState, head } = this.props;
return (

@@ -22,7 +23,12 @@ <html>

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<link href={assetsJSON.client.css} rel="stylesheet" />
{head.title.toComponent()}
{head.meta.toComponent()}
<link rel="icon" type="image/x-icon" href="/assets/images/favicon.ico" />
<link rel="apple-touch-icon" href="/assets/images/icon.png" />
<link href={assetsJSON.main.css} rel="stylesheet" />
<script type="text/javascript" charSet="utf-8" dangerouslySetInnerHTML={{__html: `
window.__REDUX_STATE__ = ${reduxState}
`}} />
`}} />
</head>

@@ -34,3 +40,3 @@ <body>

{/* entry script generated by webpack*/}
<script src={assetsJSON.client.js} type="text/javascript" charSet="utf-8"></script>
<script src={assetsJSON.main.js} type="text/javascript" charSet="utf-8"></script>
</body>

@@ -37,0 +43,0 @@ </html>

@@ -18,3 +18,3 @@ const path = require('path');

entry: {
client: path.resolve(__dirname, 'src/client/entry-main')
main: path.resolve(__dirname, 'src/entries/main')
},

@@ -46,6 +46,3 @@ output: {

use: [{
loader: 'babel-loader',
options: {
presets: ['react', 'es2015', 'stage-2']
}
loader: 'babel-loader'
}, {

@@ -95,3 +92,4 @@ loader: 'eslint-loader',

]
}
},
devtool: 'cheap-module-eval-source-map'
};
const webpack = require('webpack');
const path = require('path');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const AssetPlugin = require('assets-webpack-plugin');

@@ -29,6 +28,3 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin');

use: [{
loader: 'babel-loader',
options: {
presets: ['react', 'es2015', 'stage-2']
}
loader: 'babel-loader'
}, {

@@ -75,3 +71,3 @@ loader: 'remove-debug-loader',

}
}
}]
}

@@ -78,0 +74,0 @@ ]

const webpack = require('webpack');
const path = require('path');
const devConfig = require('./webpack.dev.js');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

@@ -52,2 +52,4 @@ const nodeExternals = require('webpack-node-externals');

);
} else {
config.devtool = 'cheap-module-eval-source-map';
}

@@ -54,0 +56,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc