Skip to content

Commit

Permalink
Merge remote-tracking branch 'Hashnode/master' into jsx
Browse files Browse the repository at this point in the history
# Conflicts:
#	.gitignore
  • Loading branch information
beaucharman committed Feb 19, 2016
2 parents 0b4330d + d857d2f commit aba035f
Show file tree
Hide file tree
Showing 22 changed files with 157 additions and 134 deletions.
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"presets": ["react", "es2015"],
}
}
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
webpack.config.dev.js
webpack.config.prod.js
webpack.config.prod.js
9 changes: 7 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
"env": {
"browser": true,
"node": true,
"mocha": true
"mocha": true,
},
"ecmaFeatures": {
"jsx": true,
"es6": true,
"classes": true
"classes": true,
},
"rules": {
"comma-dangle": [1, "always-multiline"],
"max-len": [1, 180, 4],
"arrow-body-style": [0],
},
}
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ node_js:
- '5'
- '4'
before_install:
- 'npm install -g npm@latest'
- 'npm install -g npm@latest'
39 changes: 19 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ MERN is a scaffolding tool which makes it easy to build isomorphic apps using Mo
- [Website](http://mern.io)
- [Documentation](http://mern.io/documentation.html)

##Quickstart
## Quickstart

```
npm install -g mern-cli
Expand All @@ -19,29 +19,29 @@ MERN is a scaffolding tool which makes it easy to build isomorphic apps using Mo
npm start
```

**Note : Please make sure your MongoDB is running and install cross-env globally before running `npm start`.**
**Note : Please make sure your MongoDB is running and cross-env is installed globally before running `npm start`.** . For MongoDB installation guide see [this](https://docs.mongodb.org/v3.0/installation/).

##File Structure
## File Structure

###Webpack Configs
### Webpack Configs

MERN uses Webpack for bundling modules. There are two types of webpack configs provided `webpack.config.dev.js` (for development) and `webpack.config.prod.js` (for production).
MERN uses Webpack for bundling modules. There are two types of Webpack configs provided `webpack.config.dev.js` (for development) and `webpack.config.prod.js` (for production).

The webpack configuration is minimal and beginner-friendly. You can customize and add more features to it for production build.
The Webpack configuration is minimal and beginner-friendly. You can customize and add more features to it for production build.

###Server
### Server

MERN uses express web framework. Our app sits in server.js where we check for NODE_ENV.

If NODE_ENV is development we apply webpack middlewares for bundling and Hot Module Replacement.
If NODE_ENV is development we apply Webpack middlewares for bundling and Hot Module Replacement.

####Server Side Rendering
#### Server Side Rendering

We use react-router's match function for handling all page requests so that browser history works.
We use React Router's match function for handling all page requests so that browser history works.

All the routes are defined in shared/routes.js. React router renders components according to route requested.
All the routes are defined in `shared/routes.js`. React Router renders components according to route requested.

```
```js
// Server Side Rendering based on routes matched by React-router.
app.use((req, res) => {
match({
Expand All @@ -64,14 +64,14 @@ app.use((req, res) => {
const store = configureStore(initialState);

fetchComponentData(store.dispatch, renderProps.components, renderProps.params).then(() => {
const initialView = renderToString(
const initialView = renderToString(
<Provider store = {store} >
<RouterContext {...renderProps}/>
<RouterContext {...renderProps}/>
</Provider>
);

const finalState = store.getState();

res.status(200).end(renderFullPage(initialView, finalState));
}).catch(() => {
res.end(renderFullPage('Error', {}));
Expand All @@ -82,18 +82,17 @@ app.use((req, res) => {

`match` takes two parameters, first is an object that contains routes, location and history and second is a callback function which is called when routes have been matched to a location.

If there's an error in matching we return 500 status code, if no matches are found we return 404 status code. If a match is found then we need to create a new redux store instance.
If there's an error in matching we return 500 status code, if no matches are found we return 404 status code. If a match is found then we need to create a new Redux Store instance.

**Note:** A new Redux Store is populated afresh on every request.

`fetchComponentData` is the key function. It takes three params : first is a dispatch function of redux store, second is an array of components that should be rendered in current route and third is the route params. `fetchComponentData` collects all the needs (need is an array of actions that are required to be dispatched before rendering the component) of components in the current route. It returns a promise when all the required actions are dispatched. We render the page and send data to client for client-side rendering in `window.__INITIAL_STATE__`.

`fetchComponentData` is the key function. It takes three params : first is a dispatch function of Redux store, second is an array of components that should be rendered in current route and third is the route params. `fetchComponentData` collects all the needs (need is an array of actions that are required to be dispatched before rendering the component) of components in the current route. It returns a promise when all the required actions are dispatched. We render the page and send data to client for client-side rendering in `window.__INITIAL_STATE__`.

###Shared
### Shared

Shared directory contains all the components, routes, actions and reducers.

###Client
### Client

Index.js simply does client side rendering using the data provided from window.__INITIAL_STATE__.

Expand Down
22 changes: 11 additions & 11 deletions server/controllers/post.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Post from '../models/post';
import cuid from 'cuid';
import slug from 'slug';

var sanitizeHtml = require('sanitize-html');
const sanitizeHtml = require('sanitize-html');

export function getPosts(req, res) {
Post.find().sort('-dateAdded').exec((err, posts) => {
Expand All @@ -18,13 +18,13 @@ export function addPost(req, res) {
return res.status(403).end();
}

var newPost = new Post(req.body.post);
//Let's sanitize inputs
const newPost = new Post(req.body.post);

// Let's sanitize inputs
newPost.title = sanitizeHtml(newPost.title);
newPost.name = sanitizeHtml(newPost.name);
newPost.content = sanitizeHtml(newPost.content);

newPost.slug = slug(newPost.title.toLowerCase(), { lowercase: true });
newPost.cuid = cuid();
newPost.save((err, saved) => {
Expand All @@ -36,9 +36,9 @@ export function addPost(req, res) {
}

export function getPost(req, res) {
var slug = req.query.slug.split('-');
var cuid = slug[slug.length - 1];
Post.findOne({ cuid: cuid }).exec((err, post) => {
const newSlug = req.query.slug.split('-');
const newCuid = newSlug[newSlug.length - 1];
Post.findOne({ cuid: newCuid }).exec((err, post) => {
if (err) {
return res.status(500).send(err);
}
Expand All @@ -47,14 +47,14 @@ export function getPost(req, res) {
}

export function deletePost(req, res) {
var postId = req.body.postId;
const postId = req.body.postId;
Post.findById(postId).exec((err, post) => {
if (err) {
return res.status(500).send(err);
}

post.remove(function () {
post.remove(() => {
res.status(200).end();
});
});
}
}
19 changes: 8 additions & 11 deletions server/dummyData.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import Post from './models/post';

export default function () {

Post.count().exec(function (err, count) {

Post.count().exec((err, count) => {
if (count > 0) {
return;
}

var content1 = `Sed ut perspiciatis unde omnis iste natus error
const content1 = `Sed ut perspiciatis unde omnis iste natus error
sit voluptatem accusantium doloremque laudantium, totam rem aperiam,
eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae
vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit
Expand All @@ -22,7 +20,7 @@ export default function () {
occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
est laborum`;

var content2 = `Lorem ipsum dolor sit amet, consectetur adipiscing elit,
const content2 = `Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit
Expand All @@ -36,14 +34,13 @@ export default function () {
qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem
ipsum quia dolor sit amet.`;

var post1 = new Post({ name: 'Admin', title: 'Hello MERN', slug: 'hello-mern', cuid: 'cikqgkv4q01ck7453ualdn3hd', content: content1 });
var post2 = new Post({ name: 'Admin', title: 'Lorem Ipsum', slug: 'lorem-ipsum', cuid: 'cikqgkv4q01ck7453ualdn3hf', content: content2 });
const post1 = new Post({ name: 'Admin', title: 'Hello MERN', slug: 'hello-mern', cuid: 'cikqgkv4q01ck7453ualdn3hd', content: content1 });
const post2 = new Post({ name: 'Admin', title: 'Lorem Ipsum', slug: 'lorem-ipsum', cuid: 'cikqgkv4q01ck7453ualdn3hf', content: content2 });

Post.create([post1, post2], function (err, saved) {
if (!err) {
//console.log('ready to go....');
Post.create([post1, post2], (error, saved) => {
if (!error) {
// console.log('ready to go....');
}
});
});

}
12 changes: 6 additions & 6 deletions server/models/post.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

var postSchema = new Schema({
const postSchema = new Schema({
name: { type: 'String', required: true },
title: { type: 'String', required: true },
content: { type: 'String', required: true },
slug : { type: 'String', required: true },
slug: { type: 'String', required: true },
cuid: { type: 'String', required: true },
dateAdded : { type: 'Date', default: Date.now, required: true },
dateAdded: { type: 'Date', default: Date.now, required: true },
});

var Post = mongoose.model('Post', postSchema);
const Post = mongoose.model('Post', postSchema);

module.exports = Post;
14 changes: 7 additions & 7 deletions server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import dummyData from './dummyData';
import serverConfig from './config';

// MongoDB Connection
mongoose.connect(serverConfig.mongoURL, function (err, connection) {
if (err) {
throw err;
mongoose.connect(serverConfig.mongoURL, (error, connection) => {
if (error) {
throw error;
}

// feed some dummy data in DB.
Expand Down Expand Up @@ -93,11 +93,11 @@ app.use((req, res) => {
.then(() => {
const initialView = renderToString(
<Provider store={store}>
<RouterContext {...renderProps} />
<RouterContext {...renderProps} />
</Provider>
);
const finalState = store.getState();

res.status(200).end(renderFullPage(initialView, finalState));
})
.catch(() => {
Expand All @@ -109,8 +109,8 @@ app.use((req, res) => {
// start app
app.listen(serverConfig.port, (error) => {
if (!error) {
console.log('MERN is running on port: '+serverConfig.port+'! Build something amazing!');
console.log(`MERN is running on port: ${serverConfig.port}! Build something amazing!`); // eslint-disable-line
}
});

module.exports = app;
module.exports = app;
6 changes: 2 additions & 4 deletions server/tests/post.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,13 @@ describe('GET /api/getPost', function () {
post.save(function (err, saved) {
done();
});

});

});

afterEach(function (done) {
dropDB(done);
});

it('Should send correct data when queried against a title', function (done) {

request(app)
Expand Down Expand Up @@ -124,4 +122,4 @@ describe('POST /api/addPost', function () {
});
});

});
});
4 changes: 2 additions & 2 deletions shared/components/Footer/Footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import React from 'react';
function Footer(props, context) {
return (
<div className="footer">
<p>&copy; 2016 &middot; Hashnode &middot; LinearBytes Inc.</p>
<p>We are on Twitter : <a href="https://twitter.com/@mern_io" target="_Blank">@mern_io</a></p>
<p>&copy; 2016 &middot; Hashnode &middot; LinearBytes Inc.</p>
<p>We are on Twitter : <a href="https://twitter.com/@mern_io" target="_Blank">@mern_io</a></p>
</div>
);
}
Expand Down
3 changes: 2 additions & 1 deletion shared/components/PostCreateView/PostCreateView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class PostCreateView extends Component {
<textarea placeholder="Post Content" className="form-field" ref="content"></textarea>
<a className="post-submit-button align-right" href="#" onClick={this.addPost}>Submit</a>
</div>
</div>);
</div>
);
}
}

Expand Down
14 changes: 7 additions & 7 deletions shared/components/PostListItem/PostListItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import { Link } from 'react-router';

function PostListItem(props, context) {
return (
<div className="single-post">
<h3 className="post-title ">
<Link to={'/post/' + props.post.slug + '-' + props.post.cuid} onClick={props.onClick}>
<div className="single-post">
<h3 className="post-title ">
<Link to={'/post/' + props.post.slug + '-' + props.post.cuid} onClick={props.onClick}>
{props.post.title}
</Link>
</h3>
</Link>
</h3>
<p className="author-name">By {props.post.name}</p>
<p className="post-desc">{props.post.content}</p>
<p className="post-action"><a href="#" onClick={props.onDelete}>Delete Post</a></p>
<hr className="divider"/>
</div>
);
</div>
);
}

PostListItem.propTypes = {
Expand Down
2 changes: 1 addition & 1 deletion shared/container/PostContainer/PostContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class PostContainer extends Component {
}
}

PostContainer.need = [function () { return Actions.fetchPosts(); }];
PostContainer.need = [() => { return Actions.fetchPosts(); }];
PostContainer.contextTypes = {
router: React.PropTypes.object,
};
Expand Down
4 changes: 2 additions & 2 deletions shared/container/PostDetailView/PostDetailView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ class PostDetailView extends Component {
</div>
<Footer />
</div>
);
);
}
}

PostDetailView.need = [function (params) {
PostDetailView.need = [(params) => {
return Actions.getPostRequest.bind(null, params.slug)();
}];

Expand Down
Loading

0 comments on commit aba035f

Please sign in to comment.