├── .gitignore
├── README.md
├── basic-deku
├── .babelrc
├── README.md
├── index.html
├── package.json
├── server.js
├── serverDev.js
├── src
│ ├── Counter
│ │ ├── Counter.js
│ │ └── style.css
│ ├── app.css
│ └── index.js
├── webpack.config.dev.js
└── webpack.config.js
├── basic-react
├── .babelrc
├── README.md
├── index.html
├── package.json
├── server.js
├── serverDev.js
├── src
│ ├── App.js
│ ├── Counter
│ │ ├── Counter.js
│ │ └── style.css
│ ├── app.css
│ └── index.js
├── webpack.config.dev.js
└── webpack.config.js
├── post-css
├── .babelrc
├── README.md
├── index.html
├── package.json
├── server.js
├── serverDev.js
├── src
│ ├── App.js
│ ├── Grid
│ │ ├── Grid.js
│ │ └── style.css
│ ├── app.css
│ └── index.js
├── webpack.config.dev.js
└── webpack.config.js
├── redux-deku
├── .babelrc
├── README.md
├── index.html
├── package.json
├── server.js
├── serverDev.js
├── src
│ ├── actions
│ │ └── draft.js
│ ├── api.js
│ ├── components
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── Compose.css
│ │ ├── Compose.js
│ │ ├── Inbox.js
│ │ ├── InboxItem.js
│ │ ├── InboxList.css
│ │ ├── InboxList.js
│ │ ├── Main.js
│ │ ├── Message.css
│ │ ├── Message.js
│ │ ├── Nav.js
│ │ └── NotFound.js
│ ├── create-router.js
│ ├── index.js
│ ├── reducers
│ │ ├── draft.js
│ │ └── emails.js
│ └── store.js
├── webpack.config.dev.js
└── webpack.config.js
└── redux-react
├── .babelrc
├── README.md
├── index.html
├── package.json
├── server.js
├── serverDev.js
├── src
├── actions
│ └── FriendsActions.js
├── components
│ ├── AddFriendInput.css
│ ├── AddFriendInput.js
│ ├── FriendList.css
│ ├── FriendList.js
│ ├── FriendListItem.css
│ ├── FriendListItem.js
│ └── NotFound.js
├── constants
│ └── ActionTypes.js
├── containers
│ ├── FriendListContainer.css
│ ├── FriendListContainer.js
│ └── Main.js
├── create-router.js
├── index.js
├── reducers
│ └── friendlist.js
└── store.js
├── webpack.config.dev.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | npm-debug.log
4 | public
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # webpack-css-loader-examples
2 |
3 | Some examples of using [css-loader](https://github.com/webpack/css-loader) with react and deku.
4 |
5 | #### basic use of css-loader
6 |
7 | * [basic react](https://github.com/StevenIseki/webpack-css-loader-examples/tree/master/basic-react)
8 | * [basic deku](https://github.com/StevenIseki/webpack-css-loader-examples/tree/master/basic-deku)
9 | * [post-css](https://github.com/StevenIseki/webpack-css-loader-examples/tree/master/post-css)
10 |
11 | #### css module examples
12 |
13 | * [redux react](https://github.com/StevenIseki/webpack-css-loader-examples/tree/master/redux-react)
14 | * [redux deku](https://github.com/StevenIseki/webpack-css-loader-examples/tree/master/redux-deku)
15 |
16 | ## Dependancies
17 |
18 | Here are the latest dependancies:
19 |
20 | [](https://badge.fury.io/js/babel-core)
21 | babel-core
22 |
23 | [](https://badge.fury.io/js/babel-loader)
24 | babel-loader
25 |
26 | [](https://badge.fury.io/js/css-loader)
27 | css-loader
28 |
29 | [](https://badge.fury.io/js/deku)
30 | deku
31 |
32 | [](https://badge.fury.io/js/react)
33 | react
34 |
35 | [](https://badge.fury.io/js/redux)
36 | redux
37 |
38 | [](https://badge.fury.io/js/router5)
39 | router5
40 |
41 | [](https://badge.fury.io/js/webpack)
42 | webpack
43 |
44 | These examples have the following versions for dependancies
45 |
46 | ```json
47 | "devDependencies": {
48 | "autoprefixer": "^6.1.0",
49 | "babel-core": "^6.0.20",
50 | "babel-loader": "^6.0.1",
51 | "babel-preset-es2015": "^6.0.15",
52 | "babel-preset-react": "^6.0.15",
53 | "babel-preset-stage-0": "^6.0.15",
54 | "css-loader": "^0.15.1",
55 | "deku": "^0.5.6",
56 | "express": "^4.13.3",
57 | "postcss-loader": "^0.7.0",
58 | "react": "^0.14.0",
59 | "react-dom": "^0.14.0",
60 | "style-loader": "^0.12.3",
61 | "webpack": "^1.9.6",
62 | "webpack-dev-middleware": "^1.2.0"
63 | }
64 | ```
65 |
66 |
67 |
--------------------------------------------------------------------------------
/basic-deku/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"]
3 | }
--------------------------------------------------------------------------------
/basic-deku/README.md:
--------------------------------------------------------------------------------
1 | Basic React Example
2 | =====================
3 |
4 | This example is to show off basic use of css-loader as documented in the [webpack docs](https://christianalfoni.github.io/react-webpack-cookbook/Loading-CSS.html).
5 |
6 | Note that this does not cover css-modules, it is simple for adding css imports to your deku components. You can use this method and stick to [BEM](https://css-tricks.com/bem-101/) for naming convention in your components, or you can try out the awesome css-modules:
7 |
8 | To change over from just using **css-loader** to use **css modules** update your css loader from
9 |
10 | **from**
11 |
12 | `{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }`
13 |
14 | **to**
15 |
16 | `{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader') }`
17 |
18 | and in your components
19 |
20 | **from**
21 |
22 | ```js
23 | import './style.css';
24 |
25 | className="counter"
26 | ```
27 |
28 | **to**
29 |
30 | ```js
31 | import styles from './AddFriendInput.css';
32 |
33 | className={styles.addFriendInput}
34 | ```
35 |
36 | ## Run Dev
37 |
38 | ```
39 | npm install
40 | npm start
41 | open http://localhost:3000
42 | ```
43 |
44 | ## Run Prod
45 |
46 | ```
47 | npm install
48 | npm run build
49 | npm start:prod
50 | open http://localhost:3000
51 | ```
52 |
53 | ## License
54 |
55 | [MIT](http://isekivacenz.mit-license.org/)
56 |
--------------------------------------------------------------------------------
/basic-deku/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | css loader - basic example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/basic-deku/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack-css-basic-example",
3 | "version": "1.0.0",
4 | "description": "webpack css basic example",
5 | "scripts": {
6 | "dist": "webpack --config webpack.config.js",
7 | "build": "npm run clean && npm run dist",
8 | "clean": "rm -rf public/scripts",
9 | "start": "npm run start:dev",
10 | "start:dev": "node serverDev.js",
11 | "start:prod": "NODE_ENV=production node server.js"
12 | },
13 | "author": "Steven Iseki ",
14 | "license": "MIT",
15 | "homepage": "https://github.com/StevenIseki/webpack-css-loader-examples",
16 | "devDependencies": {
17 | "babel-core": "^6.0.20",
18 | "babel-loader": "^6.0.1",
19 | "babel-preset-es2015": "^6.0.15",
20 | "babel-preset-react": "^6.0.15",
21 | "babel-preset-stage-0": "^6.0.15",
22 | "css-loader": "^0.15.1",
23 | "express": "^4.13.3",
24 | "style-loader": "^0.12.3",
25 | "webpack": "^1.9.6",
26 | "webpack-dev-middleware": "^1.2.0"
27 | },
28 | "dependencies": {
29 | "deku": "^0.5.6",
30 | "virtual-element": "^1.2.0"
31 | }
32 | }
--------------------------------------------------------------------------------
/basic-deku/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express')
2 | var path = require('path')
3 | var port = process.env.PORT || 3000
4 |
5 | express()
6 | .use('/public', express.static(__dirname + '/public'))
7 | .get('*', function (req, res) {
8 | res.sendFile(path.join(__dirname, '/index.html'))
9 | })
10 | .listen(port, function () {
11 | /* console.log(process.env) */
12 | console.log('Listening on ' + port + '.')
13 | console.log('Go to in your browser.')
14 | })
--------------------------------------------------------------------------------
/basic-deku/serverDev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.get('*', function(req, res) {
15 | res.sendFile(path.join(__dirname, 'index.html'));
16 | });
17 |
18 | app.listen(3000, 'localhost', function(err) {
19 | if (err) {
20 | console.log(err);
21 | return;
22 | }
23 |
24 | console.log('Listening at http://localhost:3000');
25 | });
26 |
--------------------------------------------------------------------------------
/basic-deku/src/Counter/Counter.js:
--------------------------------------------------------------------------------
1 | /** @jsx element */
2 |
3 | import './style.css';
4 | import {render,tree} from 'deku'
5 | import element from 'virtual-element'
6 |
7 | let Counter = {
8 |
9 | initialState () {
10 | return { secondsElapsed: 0 }
11 | },
12 |
13 | render (component) {
14 | let { props, state } = component;
15 | let { secondsElapsed } = state;
16 |
17 | return (
18 |
19 |
20 | Counter: { secondsElapsed }
21 |
22 |
23 | );
24 | },
25 |
26 | afterUpdate (component) {
27 | let { props, state } = component;
28 | },
29 |
30 | afterMount (component, el, setState) {
31 | var counter = 0;
32 | component.interval = setInterval(() => {
33 | setState({ secondsElapsed: counter++ })
34 | }, 1000);
35 | },
36 |
37 | beforeUnmount (component) {
38 | clearInterval(component.interval);
39 | }
40 | }
41 |
42 | export {Counter}
43 |
--------------------------------------------------------------------------------
/basic-deku/src/Counter/style.css:
--------------------------------------------------------------------------------
1 | .counter {
2 | background-color: #EEE;
3 | padding: 5px;
4 | }
5 |
6 | .counter--darkred {
7 | color: darkred;
8 | }
9 |
10 | .counter--pink {
11 | color: pink;
12 | }
13 |
--------------------------------------------------------------------------------
/basic-deku/src/app.css:
--------------------------------------------------------------------------------
1 | .app {
2 | background-color: purple;
3 | padding: 20px;
4 | }
--------------------------------------------------------------------------------
/basic-deku/src/index.js:
--------------------------------------------------------------------------------
1 | /** @jsx element */
2 |
3 | import element from 'virtual-element'
4 | import { Counter } from './Counter/Counter';
5 | import './app.css';
6 | import {render,tree} from 'deku'
7 |
8 | let counter = tree(
9 |
10 |
11 |
12 |
13 | );
14 |
15 | render(counter, document.getElementById('root'))
16 |
--------------------------------------------------------------------------------
/basic-deku/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: ['./src/index'],
7 | output: {
8 | filename: 'bundle.js',
9 | path: path.join(__dirname, 'scripts'),
10 | publicPath: '/public/scripts/'
11 | },
12 | module: {
13 | loaders: [
14 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
15 | { test: /\.css$/,loader: 'style!css'}
16 | ]
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/basic-deku/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: [
6 | './src/index'
7 | ],
8 | output: {
9 | filename: 'bundle.js',
10 | path: path.join(__dirname, 'public/scripts'),
11 | publicPath: '/static/'
12 | },
13 | plugins: [
14 | new webpack.optimize.OccurenceOrderPlugin(),
15 | new webpack.DefinePlugin({
16 | 'process.env': {
17 | 'NODE_ENV': JSON.stringify('production')
18 | }
19 | }),
20 | new webpack.optimize.UglifyJsPlugin({
21 | compressor: {
22 | warnings: false
23 | }
24 | })
25 | ],
26 | module: {
27 | loaders: [
28 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
29 | { test: /\.css$/,loader: 'style!css'}
30 | ]
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/basic-react/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"]
3 | }
--------------------------------------------------------------------------------
/basic-react/README.md:
--------------------------------------------------------------------------------
1 | Basic React Example
2 | =====================
3 |
4 | This example is to show off basic use of css-loader as documented in the [webpack docs](https://christianalfoni.github.io/react-webpack-cookbook/Loading-CSS.html).
5 |
6 | Note that this does not cover css-modules, it is simple for adding css imports to your react components. You can use this method and stick to [BEM](https://css-tricks.com/bem-101/) for naming convention in your components, or you can try out the awesome css-modules:
7 |
8 | To change over from just using **css-loader** to use **css modules** update your css loader from
9 |
10 | **from**
11 |
12 | `{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }`
13 |
14 | **to**
15 |
16 | `{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader') }`
17 |
18 | and in your components
19 |
20 | **from**
21 |
22 | ```js
23 | import './style.css';
24 |
25 | className="counter"
26 | ```
27 |
28 | **to**
29 |
30 | ```js
31 | import styles from './AddFriendInput.css';
32 |
33 | className={styles.addFriendInput}
34 | ```
35 |
36 | ## Run Dev
37 |
38 | ```
39 | npm install
40 | npm start
41 | open http://localhost:3000
42 | ```
43 |
44 | ## Run Prod
45 |
46 | ```
47 | npm install
48 | npm run build
49 | npm start:prod
50 | open http://localhost:3000
51 | ```
52 |
53 | ## License
54 |
55 | [MIT](http://isekivacenz.mit-license.org/)
56 |
--------------------------------------------------------------------------------
/basic-react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | css loader - basic example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/basic-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack-css-basic-example",
3 | "version": "1.0.0",
4 | "description": "webpack css basic example",
5 | "scripts": {
6 | "dist": "webpack --config webpack.config.js",
7 | "build": "npm run clean && npm run dist",
8 | "clean": "rm -rf public/scripts",
9 | "start": "npm run start:dev",
10 | "start:dev": "node serverDev.js",
11 | "start:prod": "NODE_ENV=production node server.js"
12 | },
13 | "author": "Steven Iseki ",
14 | "license": "MIT",
15 | "homepage": "https://github.com/StevenIseki/webpack-css-loader-examples",
16 | "devDependencies": {
17 | "babel-core": "^6.0.20",
18 | "babel-loader": "^6.0.1",
19 | "babel-preset-es2015": "^6.0.15",
20 | "babel-preset-react": "^6.0.15",
21 | "babel-preset-stage-0": "^6.0.15",
22 | "css-loader": "^0.15.1",
23 | "express": "^4.13.3",
24 | "style-loader": "^0.12.3",
25 | "webpack": "^1.9.6",
26 | "webpack-dev-middleware": "^1.2.0"
27 | },
28 | "dependencies": {
29 | "react": "^0.14.0",
30 | "react-dom": "^0.14.0"
31 | }
32 | }
--------------------------------------------------------------------------------
/basic-react/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express')
2 | var path = require('path')
3 | var port = process.env.PORT || 3000
4 |
5 | express()
6 | .use('/public', express.static(__dirname + '/public'))
7 | .get('*', function (req, res) {
8 | res.sendFile(path.join(__dirname, '/index.html'))
9 | })
10 | .listen(port, function () {
11 | /* console.log(process.env) */
12 | console.log('Listening on ' + port + '.')
13 | console.log('Go to in your browser.')
14 | })
--------------------------------------------------------------------------------
/basic-react/serverDev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.get('*', function(req, res) {
15 | res.sendFile(path.join(__dirname, 'index.html'));
16 | });
17 |
18 | app.listen(3000, 'localhost', function(err) {
19 | if (err) {
20 | console.log(err);
21 | return;
22 | }
23 |
24 | console.log('Listening at http://localhost:3000');
25 | });
26 |
--------------------------------------------------------------------------------
/basic-react/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Counter } from './Counter/Counter'
3 | import './app.css';
4 |
5 | export class App extends Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
12 | );
13 | }
14 | }
--------------------------------------------------------------------------------
/basic-react/src/Counter/Counter.js:
--------------------------------------------------------------------------------
1 | import './style.css';
2 | import React, { Component } from 'react';
3 | import { render } from 'react-dom';
4 |
5 | export class Counter extends Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = { counter: 0 };
9 | this.interval = setInterval(() => this.tick(), 1000);
10 | }
11 |
12 | tick() {
13 | this.setState({
14 | counter: this.state.counter + this.props.increment
15 | });
16 | }
17 |
18 | componentWillUnmount() {
19 | clearInterval(this.interval);
20 | }
21 |
22 | render() {
23 | return (
24 |
25 |
26 | Counter ({this.props.increment}): {this.state.counter}
27 |
28 |
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/basic-react/src/Counter/style.css:
--------------------------------------------------------------------------------
1 | .counter {
2 | background-color: #EEE;
3 | padding: 5px;
4 | }
5 |
6 | .counter--darkred {
7 | color: darkred;
8 | }
9 |
10 | .counter--pink {
11 | color: pink;
12 | }
13 |
--------------------------------------------------------------------------------
/basic-react/src/app.css:
--------------------------------------------------------------------------------
1 | .app {
2 | background-color: purple;
3 | padding: 20px;
4 | }
--------------------------------------------------------------------------------
/basic-react/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import { App } from './App';
4 |
5 | render(, document.getElementById('root'));
--------------------------------------------------------------------------------
/basic-react/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: ['./src/index'],
7 | output: {
8 | filename: 'bundle.js',
9 | path: path.join(__dirname, 'scripts'),
10 | publicPath: '/public/scripts/'
11 | },
12 | module: {
13 | loaders: [
14 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
15 | { test: /\.css$/,loader: 'style!css'}
16 | ]
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/basic-react/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | entry: [
6 | './src/index'
7 | ],
8 | output: {
9 | filename: 'bundle.js',
10 | path: path.join(__dirname, 'public/scripts'),
11 | publicPath: '/static/'
12 | },
13 | plugins: [
14 | new webpack.optimize.OccurenceOrderPlugin(),
15 | new webpack.DefinePlugin({
16 | 'process.env': {
17 | 'NODE_ENV': JSON.stringify('production')
18 | }
19 | }),
20 | new webpack.optimize.UglifyJsPlugin({
21 | compressor: {
22 | warnings: false
23 | }
24 | })
25 | ],
26 | module: {
27 | loaders: [
28 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
29 | { test: /\.css$/,loader: 'style!css'}
30 | ]
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/post-css/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"]
3 | }
--------------------------------------------------------------------------------
/post-css/README.md:
--------------------------------------------------------------------------------
1 | Basic Post-CSS Example
2 | =====================
3 |
4 | This example is to show off basic use of css-loader as documented in the [webpack docs](https://christianalfoni.github.io/react-webpack-cookbook/Loading-CSS.html) with post-css and autoprefixer.
5 |
6 | Note that this does not cover css-modules, it is simple for adding css imports to your react components. I prefer to use this setup and stick to [BEM](https://css-tricks.com/bem-101/) for naming convention.
7 |
8 | Note the autoprefixer postcss plugin is in use, which will as you would imagine automatically add prefixes for us.
9 |
10 | ```css
11 | a {
12 | display: flex;
13 | }
14 | ```
15 |
16 | compiles to
17 |
18 | ```css
19 | a {
20 | display: -webkit-box;
21 | display: -webkit-flex;
22 | display: -ms-flexbox;
23 | display: flex
24 | }
25 | ```
26 |
27 | ## Run Dev
28 |
29 | ```
30 | npm install
31 | npm start
32 | open http://localhost:3000
33 | ```
34 |
35 | ## Run Prod
36 |
37 | ```
38 | npm install
39 | npm run build
40 | npm start:prod
41 | open http://localhost:3000
42 | ```
43 |
44 | ## License
45 |
46 | [MIT](http://isekivacenz.mit-license.org/)
--------------------------------------------------------------------------------
/post-css/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | css loader - post-css example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/post-css/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack-css-post-css-example",
3 | "version": "1.0.0",
4 | "description": "webpack css post-css example",
5 | "scripts": {
6 | "dist": "webpack --config webpack.config.js",
7 | "build": "npm run clean && npm run dist",
8 | "clean": "rm -rf public/scripts",
9 | "start": "npm run start:dev",
10 | "start:dev": "node serverDev.js",
11 | "start:prod": "NODE_ENV=production node server.js"
12 | },
13 | "author": "Steven Iseki ",
14 | "license": "MIT",
15 | "homepage": "https://github.com/StevenIseki/webpack-css-loader-examples",
16 | "devDependencies": {
17 | "autoprefixer": "^6.1.0",
18 | "babel-core": "^6.0.20",
19 | "babel-loader": "^6.0.1",
20 | "babel-preset-es2015": "^6.0.15",
21 | "babel-preset-react": "^6.0.15",
22 | "babel-preset-stage-0": "^6.0.15",
23 | "css-loader": "^0.15.1",
24 | "express": "^4.13.3",
25 | "postcss-loader": "^0.7.0",
26 | "style-loader": "^0.12.3",
27 | "webpack": "^1.9.6",
28 | "webpack-dev-middleware": "^1.2.0"
29 | },
30 | "dependencies": {
31 | "react": "^0.14.0",
32 | "react-dom": "^0.14.0"
33 | }
34 | }
--------------------------------------------------------------------------------
/post-css/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express')
2 | var path = require('path')
3 | var port = process.env.PORT || 3000
4 |
5 | express()
6 | .use('/public', express.static(__dirname + '/public'))
7 | .get('*', function (req, res) {
8 | res.sendFile(path.join(__dirname, '/index.html'))
9 | })
10 | .listen(port, function () {
11 | /* console.log(process.env) */
12 | console.log('Listening on ' + port + '.')
13 | console.log('Go to in your browser.')
14 | })
--------------------------------------------------------------------------------
/post-css/serverDev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.get('*', function(req, res) {
15 | res.sendFile(path.join(__dirname, 'index.html'));
16 | });
17 |
18 | app.listen(3000, 'localhost', function(err) {
19 | if (err) {
20 | console.log(err);
21 | return;
22 | }
23 |
24 | console.log('Listening at http://localhost:3000');
25 | });
26 |
--------------------------------------------------------------------------------
/post-css/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Grid } from './Grid/Grid'
3 | import './app.css';
4 |
5 | export class App extends Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 | );
12 | }
13 | }
--------------------------------------------------------------------------------
/post-css/src/Grid/Grid.js:
--------------------------------------------------------------------------------
1 | import './style.css';
2 | import React, { Component } from 'react';
3 | import { render } from 'react-dom';
4 |
5 | export class Grid extends Component {
6 | constructor(props) {
7 | super(props);
8 | }
9 |
10 | componentWillUnmount() {
11 | }
12 |
13 | render() {
14 | return (
15 |
16 |
17 | cell 1
18 |
19 |
20 | cell 2
21 |
22 |
23 | cell 3
24 |
25 |
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/post-css/src/Grid/style.css:
--------------------------------------------------------------------------------
1 | .Grid {
2 | display: flex;
3 | background-color: rgba(147,128,108,.1);
4 | padding: 10px;
5 | }
6 |
7 | .Grid__cell {
8 | flex: 1;
9 | }
--------------------------------------------------------------------------------
/post-css/src/app.css:
--------------------------------------------------------------------------------
1 | .app {
2 | background-color: #fff;
3 | padding: 20px;
4 | }
--------------------------------------------------------------------------------
/post-css/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import { App } from './App';
4 |
5 | render(, document.getElementById('root'));
--------------------------------------------------------------------------------
/post-css/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var autoprefixer = require('autoprefixer');
4 |
5 | module.exports = {
6 | devtool: 'source-map',
7 | entry: ['./src/index'],
8 | output: {
9 | filename: 'bundle.js',
10 | path: path.join(__dirname, 'scripts'),
11 | publicPath: '/public/scripts/'
12 | },
13 | module: {
14 | loaders: [
15 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
16 | { test: /\.css$/,loader: 'style-loader!css-loader!postcss-loader'}
17 | ]
18 | },
19 | postcss: function () {
20 | return [autoprefixer];
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/post-css/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var autoprefixer = require('autoprefixer');
4 |
5 | module.exports = {
6 | entry: [
7 | './src/index'
8 | ],
9 | output: {
10 | filename: 'bundle.js',
11 | path: path.join(__dirname, 'public/scripts'),
12 | publicPath: '/static/'
13 | },
14 | plugins: [
15 | new webpack.optimize.OccurenceOrderPlugin(),
16 | new webpack.DefinePlugin({
17 | 'process.env': {
18 | 'NODE_ENV': JSON.stringify('production')
19 | }
20 | }),
21 | new webpack.optimize.UglifyJsPlugin({
22 | compressor: {
23 | warnings: false
24 | }
25 | })
26 | ],
27 | module: {
28 | loaders: [
29 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
30 | { test: /\.css$/,loader: 'style-loader!css-loader!postcss-loader'}
31 | ]
32 | },
33 | postcss: function () {
34 | return [autoprefixer];
35 | }
36 |
37 | };
38 |
--------------------------------------------------------------------------------
/redux-deku/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"]
3 | }
--------------------------------------------------------------------------------
/redux-deku/README.md:
--------------------------------------------------------------------------------
1 | Redux Deku Example
2 | =====================
3 |
4 | * redux, webpack, deku, router5
5 | * styling with css-modules
6 |
7 | extended from thomas roch's awesome [router5 examples](https://github.com/router5/examples)
8 |
9 | ## Run Dev
10 |
11 | ```
12 | npm install
13 | npm start
14 | open http://localhost:3000
15 | ```
16 |
17 | ## Run Prod
18 |
19 | ```
20 | npm install
21 | npm run build
22 | npm start:prod
23 | open http://localhost:3000
24 | ```
25 |
26 | 
27 |
28 | ## License
29 |
30 | [MIT](http://isekivacenz.mit-license.org/)
31 |
--------------------------------------------------------------------------------
/redux-deku/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Redux Deku Example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/redux-deku/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-deku-example",
3 | "version": "1.0.0",
4 | "description": "redux deku example",
5 | "scripts": {
6 | "dist": "webpack --config webpack.config.js",
7 | "build": "npm run clean && npm run dist",
8 | "clean": "rm -rf public/scripts",
9 | "start": "npm run start:dev",
10 | "start:dev": "node serverDev.js",
11 | "start:prod": "NODE_ENV=production node server.js"
12 | },
13 | "author": "Steven Iseki ",
14 | "license": "MIT",
15 | "homepage": "https://github.com/StevenIseki/webpack-css-loader-examples",
16 | "devDependencies": {
17 | "autoprefixer": "^6.1.0",
18 | "babel-core": "^6.0.20",
19 | "babel-loader": "^6.0.1",
20 | "babel-preset-es2015": "^6.0.15",
21 | "babel-preset-react": "^6.0.15",
22 | "babel-preset-stage-0": "^6.0.15",
23 | "css-loader": "^0.15.1",
24 | "express": "^4.13.3",
25 | "extract-text-webpack-plugin": "^0.9.1",
26 | "postcss-loader": "^0.7.0",
27 | "redux-devtools": "^2.1.5",
28 | "style-loader": "^0.13.0",
29 | "webpack": "^1.9.6",
30 | "webpack-dev-middleware": "^1.2.0",
31 | "webpack-hot-middleware": "^2.0.0"
32 | },
33 | "dependencies": {
34 | "deku": "^0.5.6",
35 | "deku-redux": "^1.0.0",
36 | "deku-router5": "^1.0.0",
37 | "lodash.find": "^3.2.1",
38 | "redux": "^3.0.4",
39 | "redux-thunk": "^1.0.0",
40 | "redux-devtools": "^2.1.5",
41 | "redux-router5": "^1.0.0",
42 | "router5": "^1.0.0",
43 | "router5-history": "^1.0.0",
44 | "router5-listeners": "^1.0.0",
45 | "virtual-element": "^1.2.0"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/redux-deku/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express')
2 | var path = require('path')
3 |
4 | var port = process.env.PORT || 3000
5 |
6 | express()
7 | .use('/public', express.static(__dirname + '/public'))
8 | .get('*', function (req, res) {
9 | res.sendFile(path.join(__dirname, '/index.html'))
10 | })
11 | .listen(port, function () {
12 | console.log(process.env)
13 | console.log('Listening on ' + port + '.')
14 | console.log('Go to in your browser.')
15 | })
--------------------------------------------------------------------------------
/redux-deku/serverDev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(3000, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:3000');
27 | });
28 |
--------------------------------------------------------------------------------
/redux-deku/src/actions/draft.js:
--------------------------------------------------------------------------------
1 | export function updateTitle(title) {
2 | return {
3 | type: 'UPDATE_TITLE',
4 | title
5 | };
6 | }
7 |
8 | export function updateMessage(message) {
9 | return {
10 | type: 'UPDATE_MESSAGE',
11 | message
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/redux-deku/src/api.js:
--------------------------------------------------------------------------------
1 | const emails = [
2 | {
3 | "id": "1",
4 | "mailTitle": "Why router5?",
5 | "mailMessage": "I imagine a lot of developers who will first see router5 will ask themselves the question: is it yet another router? is it any good? Why oh why do people keep writing new routers all the time? It is not always easy to see the potential of something straight away, or understand the motivations behind. I therefore decided to try to tell you more about router5, why I decided to develop an entire new routing solution, and what problems it tries to solve."
6 | },
7 | {
8 | "id": "2",
9 | "mailTitle": "Use with React",
10 | "mailMessage": "I have just started playing with it. It does make sense to use a flux-like implementation, to provide a layer between the router and view updates."
11 | },
12 | {
13 | "id": "3",
14 | "mailTitle": "Compose a new message",
15 | "mailMessage": "Click on compose, start to fill title and message fields and then try to navigate away by clicking on app links, or by using the back button."
16 | }
17 | ];
18 |
19 | export function getEmails() {
20 | return emails;
21 | }
22 |
23 | export function getEmail(id) {
24 | let index;
25 |
26 | if (emails) {
27 | for (index in emails) {
28 | if (emails[index].id === id) return emails[index];
29 | }
30 | }
31 | return null;
32 | }
33 |
--------------------------------------------------------------------------------
/redux-deku/src/components/App.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 20px;
3 | line-height: 1.5rem;
4 | font-family: sans-serif, Arial;
5 | padding: 0;
6 | }
7 |
8 | a {
9 | text-decoration: none;
10 | }
11 |
12 | #root {
13 | margin: 40px auto;
14 | max-width: 1050px;
15 | }
16 |
17 | .mailClient {
18 | widht: 100%;
19 | height: 450px;
20 | background-color: #f4f4f4;
21 | position: relative;
22 | border: 1px solid #CCC;
23 | }
24 |
25 | .mailClient > aside {
26 | position: absolute;
27 | top: 0;
28 | right: 0;
29 | height: 45px;
30 | left: 0;
31 | border-bottom: 1px solid #ccc;
32 | }
33 |
34 | .mailClient > main {
35 | position: absolute;
36 | top: 46px;
37 | right: 0;
38 | bottom: 0;
39 | left: 0;
40 | }
41 |
42 | .mailClient > aside > nav > a {
43 | display: inline-block;
44 | padding: .5rem 1rem;
45 | color: #336699;
46 | }
47 |
48 | .mailClient > aside > nav > a.active {
49 | background-color: #336699;
50 | color: white;
51 | }
52 |
53 |
54 |
55 | @media (min-width: 1025px) {
56 | .mailClient > aside {
57 | height: auto;
58 | top: 0;
59 | width: 150px;
60 | bottom: 0;
61 | left: 0;
62 | border-bottom: none;
63 | border-right: 1px solid #ccc;
64 | }
65 |
66 | .mailClient > main {
67 | top: 0;
68 | right: 0;
69 | bottom: 0;
70 | left: 151px;
71 | }
72 |
73 | .mailClient > aside > nav > a {
74 | display: block;
75 | }
76 | }
--------------------------------------------------------------------------------
/redux-deku/src/components/App.js:
--------------------------------------------------------------------------------
1 | import element from 'virtual-element';
2 | import Nav from './Nav';
3 | import Main from './Main';
4 | import styles from './App.css';
5 |
6 | const App = {
7 | render({ props }) {
8 | return element('div', {class: styles.mailClient}, [
9 | element('aside', {}, element(Nav)),
10 | element('main', {}, element(Main))
11 | ]);
12 | }
13 | }
14 |
15 | export default App;
16 |
--------------------------------------------------------------------------------
/redux-deku/src/components/Compose.css:
--------------------------------------------------------------------------------
1 | .compose {
2 | padding: 0 1rem;
3 | }
4 |
5 | .compose > h4 {
6 | padding: .5rem 0;
7 | margin: 0;
8 | }
9 |
10 | .compose > input,
11 | .compose > textarea {
12 | padding: .5rem;
13 | line-height: 1.2rem;
14 | font-size: 0.9rem;
15 | display: block;
16 | width: calc(100% - 2rem);
17 | max-width: 400px;
18 | margin-top: 1rem;
19 | }
20 |
21 | .compose > textarea {
22 | min-height: 200px;
23 | }
24 |
25 | .compose > p {
26 | color: red;
27 | margin: 0;
28 | padding: .5rem 0;
29 | line-height: 1.5em;
30 | font-size: 0.9rem;
31 | }
32 |
--------------------------------------------------------------------------------
/redux-deku/src/components/Compose.js:
--------------------------------------------------------------------------------
1 | import element from 'virtual-element';
2 | import { connect } from 'deku-redux';
3 | import { createSelector } from 'reselect';
4 | import { updateTitle, updateMessage } from '../actions/draft';
5 | import styles from './Compose.css';
6 |
7 | const draftSelector = createSelector(
8 | state => state.draft,
9 | state => state.router,
10 | (draft, router) => ({
11 | title: draft.title,
12 | message: draft.message,
13 | error: hasCannotDeactivateError(router.transitionError)
14 | })
15 | );
16 |
17 | function hasCannotDeactivateError(error) {
18 | return error && error.code === 'CANNOT_DEACTIVATE' && error.segment === 'compose';
19 | }
20 |
21 | const Compose = {
22 | propTypes: {
23 | router: { source: 'router' }
24 | },
25 |
26 | intitalState(props) {
27 | return { title: '', message: '' };
28 | },
29 |
30 | render({ state, props }, setState) {
31 | const { title, message, error, updateTitle, updateMessage, router } = props;
32 |
33 | const updateState = prop => evt => setState(prop, evt.target.value);
34 | router.canDeactivate('compose', !title && !message);
35 |
36 | return element('div', { class: styles.compose }, [
37 | element('h4', {}, 'Compose a new message'),
38 | element('input', { name: 'title', value: title, onChange: updateState('title') }),
39 | element('textarea', { name: 'message', value: message, onChange: updateState('message') }),
40 | error ? element('p', {}, 'Clear inputs before continuing') : null
41 | ]);
42 | }
43 | };
44 |
45 | export default connect(draftSelector, { updateTitle, updateMessage })(Compose);
46 |
--------------------------------------------------------------------------------
/redux-deku/src/components/Inbox.js:
--------------------------------------------------------------------------------
1 | import element from 'virtual-element';
2 | import InboxList from './InboxList';
3 | import Message from './Message';
4 | import { connect } from 'deku-redux';
5 | import { routeNodeSelector } from 'redux-router5';
6 | import { getEmails } from '../api';
7 |
8 | const Inbox = {
9 | displayName: 'Inbox',
10 | render({ props }) {
11 | const { route } = props;
12 |
13 | return element('div', { class: 'inbox' }, [
14 | element(InboxList, { emails: getEmails() }),
15 | route && route.name === 'inbox.message' ? element(Message, { messageId: route.params.id, key: route.params.id }) : null
16 | ]);
17 | }
18 | };
19 |
20 | export default connect(routeNodeSelector('inbox'))(Inbox);
21 |
--------------------------------------------------------------------------------
/redux-deku/src/components/InboxItem.js:
--------------------------------------------------------------------------------
1 | import element from 'virtual-element';
2 |
3 | const InboxItem = {
4 | propTypes: {
5 | router: {source: 'router'},
6 | },
7 |
8 | render({ props }) {
9 | const { mailTitle, mailMessage, router, id } = props;
10 |
11 | return element('li', { onClick: () => router.navigate('inbox.message', { id }) }, [
12 | element('h4', {}, mailTitle),
13 | element('p', {}, mailMessage)
14 | ]);
15 | }
16 | };
17 |
18 | export default InboxItem;
19 |
--------------------------------------------------------------------------------
/redux-deku/src/components/InboxList.css:
--------------------------------------------------------------------------------
1 | .inboxList {
2 | list-style: none;
3 | margin: 0;
4 | padding: 0;
5 | flex: 1;
6 | overflow: hidden;
7 | }
8 |
9 | .inboxList > li {
10 | padding: 0;
11 | cursor: pointer;
12 | border-bottom: 1px solid #ddd;
13 | }
14 |
15 | .inboxList > li:hover {
16 | background-color: #eee;
17 | }
18 |
19 | .inboxList > li > h4 {
20 | padding: .5rem 1rem 0;
21 | margin: 0;
22 | }
23 |
24 | .inboxList > li > p {
25 | margin: 0;
26 | padding: 0 1rem .5rem;
27 | line-height: 1.5em;
28 | font-size: 0.85rem;
29 | }
30 |
31 | .inboxList > li > h4,
32 | .inboxList > li > p {
33 | overflow: hidden;
34 | width: calc(100% - 2rem);
35 | white-space: nowrap;
36 | text-overflow: ellipsis;
37 | }
--------------------------------------------------------------------------------
/redux-deku/src/components/InboxList.js:
--------------------------------------------------------------------------------
1 | import element from 'virtual-element';
2 | import InboxItem from './InboxItem';
3 | import styles from './InboxList.css';
4 |
5 | const InboxList = {
6 | render({ props }) {
7 | return element(
8 | 'ul',
9 | { class: styles.inboxList },
10 | props.emails.map(mail => element(InboxItem, { ...mail, key: mail.id }))
11 | );
12 | }
13 | };
14 |
15 | export default InboxList;
16 |
--------------------------------------------------------------------------------
/redux-deku/src/components/Main.js:
--------------------------------------------------------------------------------
1 | import element from 'virtual-element';
2 | import Inbox from './Inbox';
3 | import Compose from './Compose';
4 | import NotFound from './NotFound';
5 | import { connect } from 'deku-redux';
6 | import { routeNodeSelector } from 'redux-router5';
7 |
8 | const components = {
9 | 'inbox': Inbox,
10 | 'compose': Compose
11 | };
12 |
13 | const Main = {
14 | render({ props }) {
15 | const { route } = props;
16 | const segment = route ? route.name.split('.')[0] : undefined;
17 |
18 | return element(components[segment] || NotFound);
19 | }
20 | };
21 |
22 | export default connect(routeNodeSelector(''))(Main);
23 |
--------------------------------------------------------------------------------
/redux-deku/src/components/Message.css:
--------------------------------------------------------------------------------
1 | .message {
2 | border-left: 2px solid #ddd;
3 | overflow: auto;
4 | flex: 2;
5 | }
6 |
7 | .message > h4 {
8 | padding: 1rem 1rem 0;
9 | margin: 0;
10 | font-size: 0.8rem;
11 | }
12 |
13 | .message > p {
14 | margin: 0;
15 | padding: 1rem;
16 | line-height: 1.5em;
17 | font-size: 0.8rem;
18 | }
--------------------------------------------------------------------------------
/redux-deku/src/components/Message.js:
--------------------------------------------------------------------------------
1 | import element from 'virtual-element';
2 | import { getEmail } from '../api';
3 | import styles from './Message.css';
4 |
5 | const Message = {
6 | render({ props }) {
7 | const { mailTitle, mailMessage } = getEmail(props.messageId);
8 |
9 | return element('section', { class: styles.message } , [
10 | element('h4', {}, mailTitle),
11 | element('p', {}, mailMessage)
12 | ]);
13 | }
14 | };
15 |
16 | export default Message;
17 |
--------------------------------------------------------------------------------
/redux-deku/src/components/Nav.js:
--------------------------------------------------------------------------------
1 | import element from 'virtual-element';
2 | import { Link } from 'deku-router5';
3 | import { connect } from 'deku-redux';
4 | import { actions } from 'redux-router5';
5 |
6 | const Nav = {
7 | propTypes: {
8 | router: { source: 'router' }
9 | },
10 |
11 | render({ props }) {
12 | const { router, navigateTo } = props;
13 |
14 | return element('nav', {}, [
15 | element(Link, { routeName: 'inbox', routeOptions: { reload: true } }, 'Inbox'),
16 | element(Link, { routeName: 'compose' }, 'Compose'),
17 | element(Link, { routeName: 'contacts' }, 'Contacts')
18 | ]);
19 | }
20 | };
21 |
22 | export default connect(
23 | state => state.router.route,
24 | { navigateTo: actions.navigateTo }
25 | )(Nav);
26 |
--------------------------------------------------------------------------------
/redux-deku/src/components/NotFound.js:
--------------------------------------------------------------------------------
1 | import element from 'virtual-element';
2 |
3 | const NotFound = {
4 | render() {
5 | return element('div', { class: 'not-found' }, 'Purposely not found (not a bug)');
6 | }
7 | };
8 |
9 | export default NotFound;
10 |
--------------------------------------------------------------------------------
/redux-deku/src/create-router.js:
--------------------------------------------------------------------------------
1 | import { Router5 } from 'router5'
2 | import historyPlugin from 'router5-history';
3 |
4 | export default function createRouter(routes) {
5 | const router = new Router5()
6 | .setOption('useHash', true)
7 | // .setOption('hashPrefix', '!')
8 | .setOption('defaultRoute', 'inbox')
9 | // Routes
10 | .addNode('inbox', '/inbox')
11 | .addNode('inbox.message', '/message/:id')
12 | .addNode('compose', '/compose')
13 | .addNode('contacts', '/contacts')
14 | // Plugins
15 | .usePlugin(Router5.loggerPlugin())
16 | .usePlugin(historyPlugin());
17 |
18 | return router;
19 | };
20 |
--------------------------------------------------------------------------------
/redux-deku/src/index.js:
--------------------------------------------------------------------------------
1 | import { tree, render } from 'deku';
2 | import element from 'virtual-element';
3 | import { storePlugin } from 'deku-redux';
4 | import { routerPlugin } from 'deku-router5';
5 | import App from './components/App';
6 | import createRouter from './create-router'
7 | import configureStore from './store';
8 |
9 | const router = createRouter();
10 |
11 | router.start((err, state) => {
12 | const store = configureStore(router, { router: { route: state }});
13 |
14 | const app = tree()
15 | .use(storePlugin(store))
16 | .set('router', router)
17 | .mount(element(App));
18 |
19 | render(app, document.getElementById('root'));
20 | });
21 |
--------------------------------------------------------------------------------
/redux-deku/src/reducers/draft.js:
--------------------------------------------------------------------------------
1 | const initialState = {
2 | title: '',
3 | message: ''
4 | };
5 |
6 | export default function draft(state = initialState, action) {
7 | switch (action.type) {
8 | case 'UPDATE_TITLE':
9 | return {
10 | ...state,
11 | title: action.title
12 | };
13 |
14 | case 'UPDATE_MESSAGE':
15 | return {
16 | ...state,
17 | message: action.message
18 | };
19 |
20 | default:
21 | return state;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/redux-deku/src/reducers/emails.js:
--------------------------------------------------------------------------------
1 | const initialState = [
2 | {
3 | "id": "1",
4 | "mailTitle": "Why router5?",
5 | "mailMessage": "I imagine a lot of developers who will first see router5 will ask themselves the question: is it yet another router? is it any good? Why oh why do people keep writing new routers all the time? It is not always easy to see the potential of something straight away, or understand the motivations behind. I therefore decided to try to tell you more about router5, why I decided to develop an entire new routing solution, and what problems it tries to solve."
6 | },
7 | {
8 | "id": "2",
9 | "mailTitle": "Use with React",
10 | "mailMessage": "I have just started playing with it. It does make sense to use a flux-like implementation, to provide a layer between the router and view updates."
11 | },
12 | {
13 | "id": "3",
14 | "mailTitle": "Compose a new message",
15 | "mailMessage": "Click on compose, start to fill title and message fields and then try to navigate away by clicking on app links, or by using the back button."
16 | }
17 | ];
18 |
19 | export default function emails(state = initialState, action) {
20 | switch (action.type) {
21 | default:
22 | return state;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/redux-deku/src/store.js:
--------------------------------------------------------------------------------
1 | import { compose, createStore, applyMiddleware, combineReducers } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import { router5Middleware, router5Reducer } from 'redux-router5';
4 | import emails from './reducers/emails';
5 | import draft from './reducers/draft';
6 |
7 | export default function configureStore(router, initialState = {}) {
8 | const createStoreWithMiddleware = applyMiddleware(router5Middleware(router))(createStore);
9 |
10 | const store = createStoreWithMiddleware(combineReducers({
11 | router: router5Reducer,
12 | emails,
13 | draft
14 | }), initialState);
15 |
16 | window.store = store;
17 | return store;
18 | }
19 |
--------------------------------------------------------------------------------
/redux-deku/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var autoprefixer = require('autoprefixer');
4 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
5 |
6 | module.exports = {
7 | devtool: 'source-map',
8 | entry: ['./src/index'],
9 | output: {
10 | filename: 'bundle.js',
11 | path: path.join(__dirname, 'public'),
12 | publicPath: '/public/'
13 | },
14 | module: {
15 | loaders: [
16 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
17 | { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader') }
18 | ]
19 | },
20 | postcss: function () {
21 | return [autoprefixer];
22 | },
23 | plugins: [
24 | new ExtractTextPlugin('style.css', { allChunks: true })
25 | ]
26 |
27 | };
--------------------------------------------------------------------------------
/redux-deku/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var autoprefixer = require('autoprefixer');
4 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
5 |
6 | module.exports = {
7 | entry: [
8 | './src/index'
9 | ],
10 | output: {
11 | filename: 'bundle.js',
12 | path: path.join(__dirname, 'public'),
13 | publicPath: '/public/'
14 | },
15 | plugins: [
16 | new ExtractTextPlugin('style.css', { allChunks: true }),
17 | new webpack.optimize.OccurenceOrderPlugin(),
18 | new webpack.DefinePlugin({
19 | 'process.env': {
20 | 'NODE_ENV': JSON.stringify('production')
21 | }
22 | }),
23 | new webpack.optimize.UglifyJsPlugin({
24 | compressor: {
25 | warnings: false
26 | }
27 | })
28 | ],
29 | module: {
30 | loaders: [
31 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
32 | { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader') }
33 | ]
34 | },
35 | postcss: function () {
36 | return [autoprefixer];
37 | }
38 |
39 | };
--------------------------------------------------------------------------------
/redux-react/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"]
3 | }
--------------------------------------------------------------------------------
/redux-react/README.md:
--------------------------------------------------------------------------------
1 | Redux React App Example
2 | =====================
3 |
4 | * redux, webpack, react, router5
5 | * styling with css-modules
6 | * Check out this [simple example](https://github.com/StevenIseki/redux-examples/tree/master/redux-friends-app) for BEM style class names imported with css-loader.
7 |
8 | extended from Jérôme Chapron's awesome [friendlist example](https://github.com/jchapron/redux-friendlist-demo)
9 |
10 | ## Run Dev
11 |
12 | ```
13 | npm install
14 | npm start
15 | open http://localhost:3000
16 | ```
17 |
18 | ## Run Prod
19 |
20 | ```
21 | npm install
22 | npm run build
23 | npm start:prod
24 | open http://localhost:3000
25 | ```
26 |
27 | 
28 |
29 | ## License
30 |
31 | [MIT](http://isekivacenz.mit-license.org/)
32 |
--------------------------------------------------------------------------------
/redux-react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Redux Friends App Example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/redux-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-react-app",
3 | "version": "1.0.0",
4 | "description": "redux react app",
5 | "scripts": {
6 | "dist": "webpack --config webpack.config.js",
7 | "build": "npm run clean && npm run dist",
8 | "clean": "rm -rf public/scripts",
9 | "start": "npm run start:dev",
10 | "start:dev": "node serverDev.js",
11 | "start:prod": "NODE_ENV=production node server.js"
12 | },
13 | "author": "Steven Iseki ",
14 | "license": "MIT",
15 | "homepage": "https://github.com/StevenIseki/webpack-css-loader-examples",
16 | "devDependencies": {
17 | "autoprefixer": "^6.1.0",
18 | "babel-core": "^6.0.20",
19 | "babel-loader": "^6.0.1",
20 | "babel-preset-es2015": "^6.0.15",
21 | "babel-preset-react": "^6.0.15",
22 | "babel-preset-stage-0": "^6.0.15",
23 | "css-loader": "^0.15.1",
24 | "express": "^4.13.3",
25 | "extract-text-webpack-plugin": "^0.9.1",
26 | "postcss-loader": "^0.7.0",
27 | "redux-devtools": "^2.1.5",
28 | "style-loader": "^0.13.0",
29 | "webpack": "^1.9.6",
30 | "webpack-dev-middleware": "^1.2.0",
31 | "webpack-hot-middleware": "^2.0.0"
32 | },
33 | "dependencies": {
34 | "react": "^0.14.0",
35 | "react-dom": "^0.14.0",
36 | "react-redux": "^4.0.0",
37 | "react-router5": "^1.0.0",
38 | "redux": "^3.0.4",
39 | "redux-router5": "^1.0.0",
40 | "redux-thunk": "^1.0.0",
41 | "router5": "^1.0.0",
42 | "router5-history": "^1.0.0",
43 | "router5-listeners": "^1.0.0"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/redux-react/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express')
2 | var path = require('path')
3 |
4 | var port = process.env.PORT || 3000
5 |
6 | express()
7 | .use('/public', express.static(__dirname + '/public'))
8 | .get('*', function (req, res) {
9 | res.sendFile(path.join(__dirname, '/index.html'))
10 | })
11 | .listen(port, function () {
12 | /* console.log(process.env) */
13 | console.log('Listening on ' + port + '.')
14 | console.log('Go to in your browser.')
15 | })
--------------------------------------------------------------------------------
/redux-react/serverDev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.get('*', function(req, res) {
15 | res.sendFile(path.join(__dirname, 'index.html'));
16 | });
17 |
18 | app.listen(3000, 'localhost', function(err) {
19 | if (err) {
20 | console.log(err);
21 | return;
22 | }
23 |
24 | console.log('Listening at http://localhost:3000');
25 | });
26 |
--------------------------------------------------------------------------------
/redux-react/src/actions/FriendsActions.js:
--------------------------------------------------------------------------------
1 | import * as types from '../constants/ActionTypes';
2 |
3 | export function addFriend(name) {
4 | return {
5 | type: types.ADD_FRIEND,
6 | name
7 | };
8 | }
9 |
10 | export function deleteFriend(id) {
11 | return {
12 | type: types.DELETE_FRIEND,
13 | id
14 | };
15 | }
16 |
17 | export function starFriend(id) {
18 | return {
19 | type: types.STAR_FRIEND,
20 | id
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/redux-react/src/components/AddFriendInput.css:
--------------------------------------------------------------------------------
1 | .addFriendInput {
2 | border-radius: 0;
3 | border-color: #ABAAAA;
4 | border-left: 0;
5 | border-right: 0;
6 | }
7 |
--------------------------------------------------------------------------------
/redux-react/src/components/AddFriendInput.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import styles from './AddFriendInput.css';
3 |
4 | export default class AddFriendInput extends Component {
5 |
6 | static propTypes = {
7 | addFriend: PropTypes.func.isRequired
8 | }
9 |
10 | render () {
11 | return (
12 |
21 | );
22 | }
23 |
24 | constructor (props, context) {
25 | super(props, context);
26 | this.state = {
27 | name: this.props.name || '',
28 | };
29 | }
30 |
31 | handleChange (e) {
32 | this.setState({ name: e.target.value });
33 | }
34 |
35 | handleSubmit (e) {
36 | const name = e.target.value.trim();
37 | if (e.which === 13) {
38 | this.props.addFriend(name);
39 | this.setState({ name: '' });
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/redux-react/src/components/FriendList.css:
--------------------------------------------------------------------------------
1 | .friendList {
2 | padding-left: 0;
3 | margin-bottom: 0;
4 | }
--------------------------------------------------------------------------------
/redux-react/src/components/FriendList.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import FriendListItem from '../components/FriendListItem';
3 | import { connect } from 'react-redux';
4 | import { bindActionCreators } from 'redux';
5 | import { actions } from 'redux-router5';
6 | import styles from './FriendList.css';
7 |
8 | import * as FriendsActions from '../actions/FriendsActions';
9 |
10 | function FriendList(props) {
11 | return (
12 |
13 | {
14 | props.friends.map(
15 | friend => props.deleteFriend(friend.id) }
18 | starFriend={ () => props.starFriend(friend.id) }
19 | />
20 | )
21 | }
22 |
23 | );
24 | }
25 |
26 | export default connect(
27 | state => ({ friends: state.friendlist.friends }),
28 | dispatch => bindActionCreators(FriendsActions, dispatch)
29 | )(FriendList);
30 |
--------------------------------------------------------------------------------
/redux-react/src/components/FriendListItem.css:
--------------------------------------------------------------------------------
1 | .friendListItem {
2 | list-style: none;
3 | padding: 20px 10px 20px 20px;
4 | background-color: white;
5 | border-bottom: 1px solid #E3E3E3;
6 | display: flex;
7 | }
8 |
9 | .friendInfos {
10 | flex: 1 0 auto;
11 | }
12 |
13 | .friendInfos span {
14 | font-weight: bold;
15 | }
16 |
17 | .friendActions {
18 | flex: 0 0 90px;
19 | }
20 |
21 | .btn-default,
22 | .btn-default:active,
23 | .btn-default:focus,
24 | .btn-default:hover {
25 | margin-right: 5px;
26 | color: #5C75B0;
27 | }
28 |
29 | button:focus {
30 | outline: 0 !important;
31 | }
32 |
--------------------------------------------------------------------------------
/redux-react/src/components/FriendListItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from './FriendListItem.css';
3 |
4 | function FriendListItem(props) {
5 | const { name, starred, starFriend, deleteFriend } = props;
6 |
7 | let starClassName = 'fa fa-star-o'
8 | if(starred){
9 | starClassName = 'fa fa-star'
10 | }
11 |
12 |
13 | return (
14 |
15 |
16 |
17 |
18 | {props.name}
19 |
20 |
21 |
22 |
25 |
28 |
29 |
30 |
31 | );
32 | }
33 |
34 | export default FriendListItem;
35 |
--------------------------------------------------------------------------------
/redux-react/src/components/NotFound.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default function NotFound(props) {
4 | return Purposely Not found (not a bug)
;
5 | }
6 |
--------------------------------------------------------------------------------
/redux-react/src/constants/ActionTypes.js:
--------------------------------------------------------------------------------
1 | export const ADD_FRIEND = 'ADD_FRIEND';
2 | export const STAR_FRIEND = 'STAR_FRIEND';
3 | export const DELETE_FRIEND = 'DELETE_FRIEND';
4 |
--------------------------------------------------------------------------------
/redux-react/src/containers/FriendListContainer.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #F4F3F0;
3 | display: flex;
4 | align-items: center;
5 | justify-content: center;
6 | }
7 |
8 | .friendListApp {
9 | width: 300px;
10 | padding-top: 18px;
11 | background-color: #5C75B0;
12 | border: 1px solid #E3E3E3;
13 | }
14 |
15 | .friendListApp h1 {
16 | color: white;
17 | font-size: 16px;
18 | line-height: 20px;
19 | margin-bottom: 10px;
20 | margin-top: 0;
21 | padding-left: 10px;
22 | font-family: Helvetica;
23 | }
24 |
--------------------------------------------------------------------------------
/redux-react/src/containers/FriendListContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from './FriendListContainer.css';
3 | import AddFriendInput from '../components/AddFriendInput';
4 | import FriendList from '../components/FriendList';
5 | import { connect } from 'react-redux';
6 | import { routeNodeSelector } from 'redux-router5';
7 | import { bindActionCreators } from 'redux';
8 | import * as FriendsActions from '../actions/FriendsActions';
9 |
10 | function FriendListContainer( props) {
11 |
12 | const { route } = props;
13 |
14 | const actions = {
15 | addFriend: props.addFriend,
16 | deleteFriend: props.deleteFriend,
17 | starFriend: props.starFriend
18 | }
19 |
20 | return (
21 |
22 |
The FriendList
23 |
24 |
25 |
26 | );
27 | }
28 |
29 | export default connect(
30 | routeNodeSelector('friends'),
31 | dispatch => bindActionCreators(FriendsActions, dispatch)
32 | )(FriendListContainer);
33 |
--------------------------------------------------------------------------------
/redux-react/src/containers/Main.js:
--------------------------------------------------------------------------------
1 | import React, { createElement } from 'react';
2 | import { connect } from 'react-redux';
3 | import { routeNodeSelector } from 'redux-router5';
4 |
5 | import FriendListContainer from './FriendListContainer';
6 | import NotFound from '../components/NotFound';
7 |
8 | const components = {
9 | 'friends': FriendListContainer
10 | };
11 |
12 | function Main(props) {
13 | const { route } = props;
14 | const segment = route ? route.name.split('.')[0] : undefined;
15 | return createElement(components[segment] || NotFound);
16 | }
17 |
18 | export default connect(routeNodeSelector(''))(Main);
19 |
--------------------------------------------------------------------------------
/redux-react/src/create-router.js:
--------------------------------------------------------------------------------
1 | import { Router5 } from 'router5'
2 | import historyPlugin from 'router5-history';
3 |
4 | export default function createRouter(routes) {
5 | const router = new Router5()
6 | .setOption('useHash', false)
7 | .setOption('defaultRoute', 'friends')
8 | // Routes
9 | .addNode('friends', '/friends')
10 | // Plugins
11 | .usePlugin(Router5.loggerPlugin())
12 | .usePlugin(historyPlugin());
13 |
14 | return router;
15 | };
16 |
--------------------------------------------------------------------------------
/redux-react/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Provider } from 'react-redux';
3 | import { RouterProvider } from 'react-router5';
4 | import ReactDOM from 'react-dom';
5 | import createRouter from './create-router'
6 | import configureStore from './store';
7 |
8 | import Main from './containers/Main';
9 |
10 | function App(props) {
11 | return (
12 |
13 |
14 |
15 | );
16 | }
17 |
18 |
19 | const router = createRouter();
20 |
21 | router.start((err, state) => {
22 | const store = configureStore(router, { router: { route: state }});
23 |
24 | const wrappedApp = (
25 |
26 |
27 |
28 |
29 |
30 | );
31 |
32 | ReactDOM.render(wrappedApp, document.getElementById('root'));
33 | });
34 |
--------------------------------------------------------------------------------
/redux-react/src/reducers/friendlist.js:
--------------------------------------------------------------------------------
1 | import * as types from '../constants/ActionTypes';
2 |
3 | const initialState = {
4 | friendsAdded: 3,
5 | friends: [
6 | {
7 | id: 1,
8 | name: 'Brian Lara',
9 | starred: false
10 | },
11 | {
12 | id: 2,
13 | name: 'Sachin Tendulkar',
14 | starred: false
15 | },
16 | {
17 | id: 3,
18 | name: 'Ricky Ponting',
19 | starred: false
20 | }
21 | ]
22 | }
23 |
24 | export default function friends(state = initialState, action) {
25 | switch (action.type) {
26 |
27 | case types.ADD_FRIEND:
28 | const newFriend = {
29 | id: state.friendsAdded + 1,
30 | name: action.name
31 | }
32 |
33 | return {
34 | ...state,
35 | friendsAdded: state.friendsAdded + 1,
36 | friends: state.friends.concat(newFriend)
37 | }
38 |
39 | case types.DELETE_FRIEND:
40 |
41 | const newFriends = state.friends.filter(function(friend) {
42 | return friend.id !== action.id;
43 | });
44 |
45 | return {
46 | ...state,
47 | friends: newFriends
48 | }
49 |
50 | case types.STAR_FRIEND:
51 |
52 | const starredFriends = state.friends.map(function(friend) {
53 | if(friend.id === action.id){
54 | friend.starred = true;
55 | }
56 | return friend;
57 | });
58 |
59 | return {
60 | ...state,
61 | friends: starredFriends
62 | }
63 |
64 | default:
65 | return state;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/redux-react/src/store.js:
--------------------------------------------------------------------------------
1 | import { compose, createStore, applyMiddleware, combineReducers } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import { router5Middleware, router5Reducer } from 'redux-router5';
4 | import friendlist from './reducers/friendlist';
5 |
6 | export default function configureStore(router, initialState = {}) {
7 | const createStoreWithMiddleware = applyMiddleware(router5Middleware(router))(createStore);
8 |
9 | const store = createStoreWithMiddleware(combineReducers({
10 | router: router5Reducer,
11 | friendlist
12 | }), initialState);
13 |
14 | window.store = store;
15 | return store;
16 | }
17 |
--------------------------------------------------------------------------------
/redux-react/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var autoprefixer = require('autoprefixer');
4 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
5 |
6 | module.exports = {
7 | devtool: 'source-map',
8 | entry: ['./src/index'],
9 | output: {
10 | filename: 'bundle.js',
11 | path: path.join(__dirname, 'public'),
12 | publicPath: '/public/'
13 | },
14 | module: {
15 | loaders: [
16 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
17 | { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader') }
18 | ]
19 | },
20 | postcss: function () {
21 | return [autoprefixer];
22 | },
23 | plugins: [
24 | new ExtractTextPlugin('style.css', { allChunks: true })
25 | ]
26 |
27 | };
--------------------------------------------------------------------------------
/redux-react/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var autoprefixer = require('autoprefixer');
4 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
5 |
6 | module.exports = {
7 | entry: [
8 | './src/index'
9 | ],
10 | output: {
11 | filename: 'bundle.js',
12 | path: path.join(__dirname, 'public'),
13 | publicPath: '/public/'
14 | },
15 | plugins: [
16 | new ExtractTextPlugin('style.css', { allChunks: true }),
17 | new webpack.optimize.OccurenceOrderPlugin(),
18 | new webpack.DefinePlugin({
19 | 'process.env': {
20 | 'NODE_ENV': JSON.stringify('production')
21 | }
22 | }),
23 | new webpack.optimize.UglifyJsPlugin({
24 | compressor: {
25 | warnings: false
26 | }
27 | })
28 | ],
29 | module: {
30 | loaders: [
31 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
32 | { test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss-loader') }
33 | ]
34 | },
35 | postcss: function () {
36 | return [autoprefixer];
37 | }
38 |
39 | };
--------------------------------------------------------------------------------