├── .gitignore
├── client
└── src
│ ├── js
│ ├── constants
│ │ ├── MessageTypes.js
│ │ └── ActionTypes.js
│ ├── reducers
│ │ ├── index.js
│ │ └── MessageReducer.js
│ ├── pages
│ │ └── index.js
│ ├── containers
│ │ ├── SignInContainer.js
│ │ └── ChatContainer.js
│ ├── socket.js
│ ├── routes
│ │ └── index.js
│ ├── actions
│ │ └── actions.js
│ └── components
│ │ ├── chat
│ │ ├── MessageInput.js
│ │ ├── MessageItem.js
│ │ └── Chat.js
│ │ └── signIn
│ │ └── SignIn.js
│ ├── html
│ └── index.html
│ └── less
│ ├── main.less
│ ├── signIn.less
│ └── chat.less
├── README.md
├── package.json
├── server
└── server.js
├── webpack.config.js
└── webpack.plugins.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | *.iml
3 | .idea/*
4 | client/build/*
--------------------------------------------------------------------------------
/client/src/js/constants/MessageTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/29/16.
3 | */
4 |
5 | export const USER_MESSAGE = 'USER_MESSAGE';
6 | export const SYSTEM_MESSAGE = 'SYSTEM_MESSAGE';
--------------------------------------------------------------------------------
/client/src/js/constants/ActionTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/24/16.
3 | */
4 |
5 | // message
6 | export const SEND_MESSAGE = 'SEND_MESSAGE';
7 | export const RECEIVE_MESSAGE = 'RECEIVE_MESSAGE';
8 |
9 | // user
10 | export const USER_JOINED = 'USER_JOINED';
11 | export const USER_LEFT = 'USER_LEFT';
--------------------------------------------------------------------------------
/client/src/js/reducers/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/24/16.
3 | */
4 |
5 | import { combineReducers } from 'redux'
6 | import messages from 'js/reducers/MessageReducer'
7 |
8 | // const reducer = combineReducers({
9 | // messages
10 | // })
11 |
12 | const reducer = messages
13 |
14 | export default reducer
15 |
--------------------------------------------------------------------------------
/client/src/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Chatting Room
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/client/src/js/pages/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/20/16.
3 | */
4 |
5 | import $ from "jquery"
6 | import React from 'react'
7 | import { render } from 'react-dom'
8 | import { createStore } from 'redux'
9 | import { Provider } from 'react-redux'
10 | import reducer from 'js/reducers'
11 | import Routes from 'js/routes'
12 |
13 | let store = createStore(reducer)
14 |
15 | import 'css/main.less'
16 |
17 | render(
18 |
19 | {Routes}
20 | ,
21 | $('.main')[0]
22 | );
--------------------------------------------------------------------------------
/client/src/less/main.less:
--------------------------------------------------------------------------------
1 | /* Fix user-agent */
2 |
3 | * {
4 | box-sizing: border-box;
5 | }
6 |
7 | html {
8 | font-weight: 300;
9 | -webkit-font-smoothing: antialiased;
10 | }
11 |
12 | html, input {
13 | font-family:
14 | "HelveticaNeue-Light",
15 | "Helvetica Neue Light",
16 | "Helvetica Neue",
17 | Helvetica,
18 | Arial,
19 | "Lucida Grande",
20 | sans-serif;
21 | }
22 |
23 | html, body {
24 | height: 100%;
25 | margin: 0;
26 | padding: 0;
27 | }
--------------------------------------------------------------------------------
/client/src/js/containers/SignInContainer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/24/16.
3 | */
4 |
5 | import React, { Component, PropTypes } from 'react';
6 | import { connect } from 'react-redux';
7 | import {bindActionCreators} from 'redux';
8 |
9 | import SignIn from 'js/components/signIn/SignIn';
10 | import * as actions from 'js/actions/actions';
11 |
12 | class SignInContainer extends Component {
13 | render() {
14 | return (
15 |
16 | );
17 | }
18 | }
19 |
20 | export default connect()(SignInContainer)
21 |
--------------------------------------------------------------------------------
/client/src/js/socket.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/29/16.
3 | */
4 |
5 | const Singleton = (function () {
6 | let instance;
7 |
8 | function createInstance() {
9 | const io = require('socket.io-client')
10 | const socket = io.connect();
11 | return socket;
12 | }
13 |
14 | return {
15 | getInstance: function () {
16 | if (!instance) {
17 | instance = createInstance();
18 | }
19 | return instance;
20 | }
21 | };
22 | })();
23 |
24 | export default Singleton;
--------------------------------------------------------------------------------
/client/src/js/routes/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/24/16.
3 | */
4 |
5 | import React from 'react'
6 | import ChatContainer from 'js/containers/ChatContainer';
7 | import SignInContainer from 'js/containers/SignInContainer';
8 |
9 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
10 |
11 | const Routes = (
12 |
13 |
14 |
15 |
16 | );
17 |
18 | export default Routes;
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ChattingRoom
2 |
3 | ## Online demo:
4 |
5 | [view it on my personal server](http://hshen.pw:3000)
6 |
7 | or the link below if my server doesn't work (๑•́₃ •̀๑) :
8 |
9 | [view it on heroku](https://murmuring-brook-22426.herokuapp.com)
10 |
11 |
12 | ## Run it locally:
13 |
14 | ###For production:
15 | ```
16 | npm install
17 | npm run build
18 | node server/server.js
19 | ```
20 | And then point your browser to `localhost:3000`
21 |
22 |
23 | ###For debug:
24 | ```
25 | npm install
26 | npm run dev
27 | node server/server.js
28 | ```
29 | And then point your browser to `localhost:3000`
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/client/src/js/actions/actions.js:
--------------------------------------------------------------------------------
1 | import * as types from 'js/constants/ActionTypes';
2 |
3 | /*
4 | * action creator
5 | */
6 | export function sendMessage(message) {
7 | return {
8 | type: types.SEND_MESSAGE,
9 | message
10 | }
11 | }
12 |
13 | export function receiveMessage(message) {
14 | return {
15 | type: types.RECEIVE_MESSAGE,
16 | message
17 | }
18 | }
19 |
20 | export function userJoined(data) {
21 | return {
22 | type: types.USER_JOINED,
23 | data
24 | }
25 | }
26 |
27 | export function userLeft(data) {
28 | return {
29 | type: types.USER_LEFT,
30 | data
31 | }
32 | }
--------------------------------------------------------------------------------
/client/src/less/signIn.less:
--------------------------------------------------------------------------------
1 | .sign-in {
2 | background-color: #000;
3 | height: 100%;
4 | position: absolute;
5 | width: 100%;
6 | }
7 |
8 | .sign-in .form {
9 | height: 100px;
10 | margin-top: -100px;
11 | position: absolute;
12 | text-align: center;
13 | top: 50%;
14 | width: 100%;
15 | }
16 |
17 | .sign-in .form .username-input {
18 | background-color: transparent;
19 | border: none;
20 | border-bottom: 2px solid #fff;
21 | outline: none;
22 | padding-bottom: 15px;
23 | text-align: center;
24 | width: 400px;
25 | font-size: 200%;
26 | letter-spacing: 3px;
27 | }
28 |
29 | .sign-in .title {
30 | font-size: 200%;
31 | }
32 |
33 | .sign-in .title, .sign-in .username-input {
34 | color: #fff;
35 | font-weight: 100;
36 | }
37 |
--------------------------------------------------------------------------------
/client/src/js/components/chat/MessageInput.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/24/16.
3 | */
4 |
5 | import React, { Component, PropTypes } from 'react';
6 | import { Input } from 'react-bootstrap';
7 |
8 | export default class MessageComposer extends Component {
9 | constructor(props, context) {
10 | super(props, context);
11 | }
12 |
13 | handleSubmit(event) {
14 | if (event.which === 13) {
15 | event.preventDefault();
16 | const text = this.refs.input.value.trim();
17 | if (text.length > 0) {
18 | this.props.sendMessage(text);
19 | this.refs.input.value = '';
20 | }
21 | }
22 | }
23 |
24 | render() {
25 | return (
26 |
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/client/src/js/containers/ChatContainer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/24/16.
3 | */
4 |
5 | import React, { Component, PropTypes } from 'react';
6 | import { connect } from 'react-redux';
7 | import {bindActionCreators} from 'redux';
8 |
9 | import Chat from 'js/components/chat/Chat';
10 | import * as actions from 'js/actions/actions';
11 |
12 |
13 | class ChatContainer extends Component {
14 | render() {
15 | return (
16 |
17 | );
18 | }
19 | }
20 |
21 | const mapStateToProps = (state, ownProps) => {
22 | return {
23 | messages: state.messages,
24 | // userName: state.user.userName
25 | }
26 | }
27 |
28 | const mapDispatchToProps = (dispatch, ownProps) => {
29 | return bindActionCreators({
30 | receiveMessage: actions.receiveMessage,
31 | sendMessage: actions.sendMessage,
32 | userJoined: actions.userJoined,
33 | userLeft: actions.userLeft
34 | },dispatch);
35 | }
36 |
37 | export default connect(mapStateToProps, mapDispatchToProps)(ChatContainer)
38 |
--------------------------------------------------------------------------------
/client/src/less/chat.less:
--------------------------------------------------------------------------------
1 | .chat {
2 | height: 100%;
3 | position: absolute;
4 | width: 100%;
5 | }
6 |
7 | /* Font */
8 |
9 | .messages {
10 | font-size: 150%;
11 | }
12 |
13 | .input-message {
14 | font-size: 200%;
15 | }
16 |
17 | .log {
18 | color: gray;
19 | font-size: 70%;
20 | margin: 5px;
21 | text-align: center;
22 | }
23 |
24 | /* Messages */
25 |
26 | ul {
27 | list-style: none;
28 | word-wrap: break-word;
29 | }
30 |
31 | .chat-area {
32 | height: 100%;
33 | padding-bottom: 60px;
34 | }
35 |
36 | .messages {
37 | height: 100%;
38 | margin: 0;
39 | overflow-y: scroll;
40 | padding: 10px 20px 10px 20px;
41 | }
42 |
43 | .system-message {
44 | color: gray;
45 | text-align: center;
46 |
47 | }
48 |
49 | .user-name {
50 | font-weight: 700;
51 | overflow: hidden;
52 | padding-right: 15px;
53 | text-align: right;
54 | }
55 |
56 | /* Input */
57 |
58 | .input-message {
59 | border: 13px solid #66d1f1;
60 | bottom: 0;
61 | height: 100px;
62 | left: 0;
63 | outline: none;
64 | padding-left: 10px;
65 | position: absolute;
66 | right: 0;
67 | width: 100%;
68 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ChattingRoom",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "build": "webpack",
7 | "dev": "webpack"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "ejs": "^2.5.2",
14 | "express": "^4.14.0",
15 | "jquery": "^3.1.0",
16 | "material-ui": "^0.15.4",
17 | "moment": "^2.15.1",
18 | "node-uuid": "^1.4.7",
19 | "react": "^15.3.2",
20 | "react-bootstrap": "^0.30.3",
21 | "react-dom": "^15.3.2",
22 | "react-redux": "^4.4.5",
23 | "react-router": "^2.8.1",
24 | "redux": "^3.6.0",
25 | "redux-auth": "0.0.5-beta5",
26 | "redux-thunk": "^2.1.0",
27 | "socket.io": "^1.4.8",
28 | "socket.io-client": "^1.4.8"
29 | },
30 | "devDependencies": {
31 | "babel-core": "^6.9.1",
32 | "babel-eslint": "^6.1.2",
33 | "babel-loader": "^6.2.4",
34 | "babel-preset-es2015": "^6.9.0",
35 | "babel-preset-react": "^6.11.1",
36 | "bootstrap": "^3.3.6",
37 | "clean-webpack-plugin": "^0.1.10",
38 | "copy-webpack-plugin": "^3.0.1",
39 | "css-loader": "^0.23.1",
40 | "extract-text-webpack-plugin": "^1.0.1",
41 | "less": "^2.7.1",
42 | "less-loader": "^2.2.3",
43 | "redux-devtools": "^3.3.1",
44 | "style-loader": "^0.13.1",
45 | "webpack": "^1.13.1",
46 | "webpack-merge": "^0.14.1"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/client/src/js/components/signIn/SignIn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/27/16.
3 | */
4 | /**
5 | * Created by hshen on 9/24/16.
6 | */
7 |
8 | import React, { Component, PropTypes } from 'react';
9 | import { browserHistory } from 'react-router';
10 | import Singleton from 'js/socket'
11 |
12 | import 'css/signIn.less'
13 |
14 | export default class SignIn extends Component {
15 |
16 | constructor(props, context) {
17 | super(props, context);
18 | this.socket = Singleton.getInstance();
19 | }
20 |
21 | handleSignIn(event) {
22 | if (event.which === 13) {
23 | event.preventDefault();
24 | const userName = event.target.value.trim();
25 | this.socket.emit('signIn', userName);
26 | browserHistory.push('/chat');
27 | }
28 | }
29 |
30 | render() {
31 | const { messages} = this.props;
32 | return (
33 |
34 |
35 |
Who are you?
36 |
41 |
42 |
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/client/src/js/components/chat/MessageItem.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react';
2 | import * as messageTypes from 'js/constants/MessageTypes'
3 |
4 | export default class MessageItem extends React.Component {
5 |
6 |
7 | render() {
8 | const { message } = this.props;
9 | switch (message.type) {
10 | case messageTypes.USER_MESSAGE:
11 | const userNameColor = this._getUserNameColor(message.userName);
12 | return (
13 |
14 | {message.userName}
15 | {message.text}
16 |
17 | );
18 | case messageTypes.SYSTEM_MESSAGE:
19 | return (
20 |
21 | {message.text}
22 |
23 | );
24 | }
25 | }
26 |
27 | _getUserNameColor(userName) {
28 | const COLORS = [
29 | '#e21400', '#91580f', '#f8a700', '#f78b00',
30 | '#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
31 | '#3b88eb', '#3824aa', '#a700ff', '#d300e7'
32 | ];
33 | // Compute hash code
34 | let hash = 7;
35 | for (let i = 0; i < userName.length; i++) {
36 | hash = userName.charCodeAt(i) + (hash << 5) - hash;
37 | }
38 | // Calculate color
39 | const index = Math.abs(hash % COLORS.length);
40 | return COLORS[index];
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/client/src/js/reducers/MessageReducer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/24/16.
3 | */
4 |
5 | import React from 'react'
6 | import * as actionTypes from 'js/constants/ActionTypes'
7 | import * as messageTypes from 'js/constants/MessageTypes'
8 |
9 |
10 | const initialState = {
11 | messages: [{type: messageTypes.SYSTEM_MESSAGE,
12 | text: 'Welcome to our chatting room!'}],
13 | };
14 |
15 | export default function messages(state = initialState, action) {
16 | switch (action.type) {
17 | case actionTypes.RECEIVE_MESSAGE:
18 | const message = action.message;
19 | return {...state,
20 | messages: [...state.messages, {
21 | type: messageTypes.USER_MESSAGE,
22 | text: message.text,
23 | userName: message.userName
24 | }]
25 | }
26 | case actionTypes.USER_JOINED:
27 | return {...state,
28 | messages: [...state.messages, {
29 | type: messageTypes.SYSTEM_MESSAGE,
30 | text: `${action.data.userName} joined! Now ${action.data.userNumber} participants.`
31 | }]
32 | }
33 | case actionTypes.USER_LEFT:
34 | return {...state,
35 | messages: [...state.messages, {
36 | type: messageTypes.SYSTEM_MESSAGE,
37 | text: `${action.data.userName} left! Now ${action.data.userNumber} participants.`
38 | }]
39 | }
40 | case actionTypes.SEND_MESSAGE:
41 | return state;
42 | default:
43 | return state;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/23/16.
3 | */
4 |
5 | // Import modules
6 | var express = require('express');
7 | var path = require('path');
8 | var ejs = require('ejs');
9 |
10 | // Create server
11 | var app = express()
12 | , server = require('http').createServer(app)
13 | , io = require('socket.io').listen(server);
14 | var port = process.env.PORT || 3000;
15 | server.listen(port);
16 |
17 | // Return index.html for '/'
18 | app.get('/', function (req, res) {
19 | res.render('index');
20 | });
21 |
22 | // Set path for views and static resources
23 | app.set('views', './client/src/html');
24 | app.set('view engine', 'html');
25 | app.engine('html', ejs.renderFile);
26 | app.use('/static', express.static('./client/build'));
27 |
28 | var userNumber = 0;
29 |
30 | io.sockets.on('connection', function (socket) {
31 | var signedIn = false;
32 |
33 | socket.on('newMessage', function (text) {
34 | io.sockets.emit('newMessage',{
35 | userName: socket.userName,
36 | text: text
37 | })
38 | });
39 |
40 | socket.on('signIn', function (userName) {
41 | if (signedIn) return;
42 |
43 | // we store the username in the socket session for this client
44 | socket.userName = userName;
45 | ++userNumber;
46 | signedIn = true;
47 |
48 | io.sockets.emit('userJoined', {
49 | userName: userName,
50 | userNumber: userNumber
51 | });
52 | });
53 |
54 | socket.on('disconnect', function () {
55 | if (signedIn) {
56 | --userNumber;
57 |
58 | io.sockets.emit('userLeft', {
59 | userName: socket.userName,
60 | userNumber: userNumber
61 | });
62 | }
63 | });
64 |
65 | });
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/20/16.
3 | */
4 |
5 | // Define paths
6 | const path = require('path');
7 | const PATHS = {
8 | app: path.join(__dirname, 'client', 'src'),
9 | build: path.join(__dirname, 'client', 'build')
10 | };
11 |
12 | // A tool to merge config object
13 | const merge = require('webpack-merge');
14 |
15 | // Configuration for plugins
16 | const plugins = require('./webpack.plugins');
17 |
18 | // Common configuration for production and dev
19 | const common = merge(
20 | {
21 | entry: {
22 | index: path.join(PATHS.app,'js','pages','index.js'),
23 | },
24 | output: {
25 | path: path.join(PATHS.build,'js'),
26 | filename: '[name].js',
27 | chunkFilename: '[chunkhash].js'
28 | },
29 | resolve: {
30 | root: [
31 | PATHS.app
32 | ],
33 | alias: {
34 | js: 'js',
35 | css: 'less'
36 | },
37 | extensions: ['', '.js', '.jsx']
38 | }
39 | },
40 | plugins.clean(PATHS.build),
41 | plugins.copy(),
42 | plugins.extractCommon('common.js'),
43 | plugins.less(),
44 | plugins.babel()
45 | );
46 |
47 | var config = null;
48 |
49 | // Detect the branch where npm is running on
50 | switch(process.env.npm_lifecycle_event) {
51 | case 'build':
52 | config = merge(
53 | common,
54 | plugins.minify()
55 | );
56 | break;
57 |
58 | case 'dev':
59 | default:
60 | config = merge(
61 | common,
62 | // Set source map for debug
63 | {
64 | devtool: 'source-map'
65 | }
66 | );
67 | break;
68 | }
69 |
70 | module.exports = config;
71 |
--------------------------------------------------------------------------------
/client/src/js/components/chat/Chat.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/24/16.
3 | */
4 |
5 | import React, { Component, PropTypes } from 'react';
6 | import MessageInput from 'js/components/chat/MessageInput';
7 | import MessageItem from 'js/components/chat/MessageItem';
8 | import Singleton from 'js/socket'
9 |
10 | import 'css/chat.less'
11 |
12 | export default class Chat extends Component {
13 |
14 | constructor(props, context) {
15 | super(props, context);
16 | this.socket = Singleton.getInstance();
17 | }
18 |
19 | componentWillMount() {
20 | const { receiveMessage, userJoined, userLeft } = this.props;
21 | this.socket.on('newMessage',function (msg) {
22 | receiveMessage(msg);
23 | });
24 | this.socket.on('userJoined',function (data) {
25 | console.log(data);
26 | userJoined(data);
27 | });
28 | this.socket.on('userLeft',function (data) {
29 | console.log(data);
30 | userLeft(data);
31 | });
32 | }
33 |
34 | sendMessage(newMessage) {
35 | const { sendMessage, userName } = this.props;
36 | if (newMessage.length !== 0) {
37 | sendMessage(newMessage);
38 | this.socket.emit('newMessage', newMessage);
39 | }
40 | }
41 |
42 | render() {
43 | const { messages} = this.props;
44 | return (
45 |
46 |
47 |
48 | {messages.map( (message, index) =>
49 |
50 | )}
51 |
52 |
53 |
54 |
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/webpack.plugins.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by hshen on 9/20/16.
3 | */
4 | const webpack = require('webpack');
5 |
6 | exports.minify = function () {
7 | return {
8 | plugins: [
9 | new webpack.optimize.UglifyJsPlugin({
10 | // Don't beautify output (enable for neater output)
11 | beautify: false,
12 | // Eliminate comments
13 | comments: false,
14 | compress: {
15 | warnings: false,
16 | // Drop `console` statements
17 | drop_console: true
18 | }
19 | })
20 | ]
21 | };
22 | }
23 |
24 | // Clean a specific folder
25 | exports.clean = function (path) {
26 | const CleanWebpackPlugin = require('clean-webpack-plugin');
27 |
28 | return {
29 | plugins: [
30 | new CleanWebpackPlugin([path], {
31 | // Without `root` CleanWebpackPlugin won't point to our
32 | // project and will fail to work.
33 | root: process.cwd()
34 | })
35 | ]
36 | };
37 | }
38 |
39 | exports.extractCommon = function (name) {
40 | return {
41 | plugins: [
42 | new webpack.optimize.CommonsChunkPlugin(name)
43 | ]
44 | };
45 | }
46 |
47 | exports.copy = function () {
48 | const path = require('path');
49 | const PATHS = {
50 | app: path.join(__dirname, 'src'),
51 | build: path.join(__dirname, 'build')
52 | };
53 | const CopyWebpackPlugin = require('copy-webpack-plugin');
54 |
55 | return {
56 | plugins: [
57 | new CopyWebpackPlugin([
58 | { from: path.join(PATHS.app,'html'), to: path.join(PATHS.build,'html')},
59 | ], {
60 | ignore: [
61 |
62 | ],
63 | // By default, we only copy modified files during
64 | // a watch or webpack-dev-server build. Setting this
65 | // to `true` copies all files.
66 | copyUnmodified: true
67 | })
68 | ]
69 | };
70 | }
71 |
72 | exports.less = function () {
73 | return {
74 | module: {
75 | loaders: [
76 | {
77 | test: /\.less$/,
78 | exclude: /node_modules/,
79 | loader: "style!css!less"
80 | }
81 | ]
82 | }
83 | };
84 | };
85 |
86 | exports.babel = function () {
87 | return {
88 | module: {
89 | loaders: [
90 | {
91 | test: /\.jsx?$/,
92 | exclude: /(node_modules)/,
93 | loader: 'babel',
94 | query: {
95 | presets: ['es2015', 'react'],
96 | plugins: ['transform-object-rest-spread']
97 | }
98 | }
99 | ]
100 | }
101 | };
102 | };
103 |
--------------------------------------------------------------------------------