├── .babelrc ├── .eslintrc ├── .gitignore ├── README.md ├── __mocks__ └── noopMock.js ├── package.json ├── server.js ├── src ├── Demo │ ├── Demo.js │ ├── Demo.less │ ├── __test__ │ │ └── Demo.test.js │ └── react-logo.png ├── index.js └── public │ └── index.ejs ├── webpack ├── webpack.config.development.js ├── webpack.config.production.js └── webpack.loaders.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env", "react"], 3 | "plugins": ["babel-plugin-transform-class-properties", "transform-object-assign"] 4 | } 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "react-app" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Introduction 2 | 3 | This is a sample repository showing how to setup a React working environment 4 | from scratch. Currently implemented: 5 | 6 | - babel with env and React presets 7 | - webpack 4.1 8 | - less, css, file-loaders with extract-text-webpack-plugin 9 | - hot module replacement 10 | - jest 11 | - enzyme 12 | - eslint 13 | - eslint react rules 14 | 15 | #License 16 | 17 | MIT 18 | -------------------------------------------------------------------------------- /__mocks__/noopMock.js: -------------------------------------------------------------------------------- 1 | module.exports = "noop-stub"; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-on", 3 | "version": "2.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "jest", 9 | "lint": "eslint src", 10 | "build": "webpack --config webpack/webpack.config.production.js --progress --hide-modules" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "babel": "^6.23.0", 17 | "babel-core": "^6.26.0", 18 | "babel-eslint": "^8.2.2", 19 | "babel-jest": "^22.4.1", 20 | "babel-loader": "^7.1.3", 21 | "babel-plugin-transform-class-properties": "^6.24.1", 22 | "babel-plugin-transform-object-assign": "^6.22.0", 23 | "babel-preset-react": "^6.24.1", 24 | "connect-history-api-fallback": "^1.5.0", 25 | "css-loader": "^0.28.10", 26 | "enzyme": "^3.3.0", 27 | "eslint": "^4.18.1", 28 | "eslint-config-standard-jsx": "^5.0.0", 29 | "eslint-config-standard-react": "^6.0.0", 30 | "eslint-plugin-react": "^7.7.0", 31 | "express": "^4.16.2", 32 | "extract-text-webpack-plugin": "^4.0.0-alpha.0", 33 | "file-loader": "1.1.10", 34 | "html-webpack-plugin": "webpack-contrib/html-webpack-plugin", 35 | "identity-obj-proxy": "^3.0.0", 36 | "jest": "^22.4.2", 37 | "less": "^3.0.1", 38 | "less-loader": "^4.0.6", 39 | "react": "^16.2.0", 40 | "react-addons-test-utils": "^15.6.2", 41 | "react-dom": "^16.2.0", 42 | "rimraf": "^2.6.2", 43 | "style-loader": "^0.20.2", 44 | "url-loader": "^0.6.2", 45 | "webpack": "^4.0.1", 46 | "webpack-cli": "^2.0.9", 47 | "webpack-dev-middleware": "^2.0.6", 48 | "webpack-hot-middleware": "^2.21.0" 49 | }, 50 | "jest": { 51 | "moduleFileExtensions": [ 52 | "js", 53 | "jsx" 54 | ], 55 | "moduleDirectories": [ 56 | "node_modules", 57 | "src" 58 | ], 59 | "moduleNameMapper": { 60 | "\\.(css|less)$": "identity-obj-proxy", 61 | "\\.(jpeg|jpg|png|gif|svg|woff|woff2|ttf)$": "/__mocks__/noopMock.js" 62 | } 63 | }, 64 | "eslintConfig": { 65 | "parser": "babel-eslint", 66 | "extends": [ 67 | "standard-react" 68 | ], 69 | "env": { 70 | "node": true, 71 | "browser": true 72 | }, 73 | "rules": { 74 | "jsx-quotes": [ 75 | "warn", 76 | "prefer-double" 77 | ] 78 | } 79 | }, 80 | "dependencies": { 81 | "babel-preset-env": "^1.6.1" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var express = require('express'); 3 | 4 | var app = express(); 5 | 6 | var webpack = require('webpack'); 7 | var config = require('./webpack/webpack.config.development.js'); 8 | var compiler = webpack(config); 9 | 10 | app.use(require('connect-history-api-fallback')({verbose: false})); 11 | app.use(require('webpack-dev-middleware')(compiler, { 12 | noInfo: true, 13 | publicPath: config.output.publicPath 14 | })); 15 | 16 | app.use(require("webpack-hot-middleware")(compiler)); 17 | 18 | app.get('*', function(req, res) { 19 | res.sendFile(path.join(__dirname, 'src', 'public', 'index.ejs')); 20 | }); 21 | 22 | var server = app.listen(8000, 'localhost', function(err) { 23 | if(err) { 24 | console.log(err); 25 | return; 26 | } 27 | console.log('Listening at http://localhost:%d', server.address().port); 28 | }); 29 | -------------------------------------------------------------------------------- /src/Demo/Demo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Style from "./Demo.less"; 4 | import logo from "./react-logo.png"; 5 | 6 | export default class Demo extends React.Component { 7 | 8 | render() { 9 | return ( 10 |
11 | React 12 | Welcome to React-on! 13 |
14 | ) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Demo/Demo.less: -------------------------------------------------------------------------------- 1 | .Demo { 2 | font-family: Tahoma, Arial, sans-serif; 3 | font-size: 16px; 4 | line-height: 1.4; 5 | color: #333; 6 | text-align: center; 7 | 8 | img { 9 | width: 140px; 10 | display: block; 11 | margin: 10px auto; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Demo/__test__/Demo.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { shallow } from "enzyme"; 3 | import Demo from "../Demo"; 4 | 5 | describe("", () => { 6 | it("should render", () => { 7 | shallow(); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/Demo/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BTMPL/react-on/fdf3dba322758edf39d5a2ffa94655f61f75a81e/src/Demo/react-logo.png -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import Demo from "Demo/Demo"; 4 | 5 | ReactDOM.render(, document.getElementById("react-app")); 6 | 7 | if(module.hot) { 8 | module.hot.accept(); 9 | } 10 | -------------------------------------------------------------------------------- /src/public/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= htmlWebpackPlugin.options.title %> 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /webpack/webpack.config.development.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var webpack = require("webpack"); 3 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | var ExtractTextPlugin = require("extract-text-webpack-plugin"); 5 | 6 | 7 | module.exports = { 8 | mode: "development", 9 | devtool: "source-map", 10 | entry: ["webpack-hot-middleware/client", "./src/index.js"], 11 | output: { 12 | path: path.join(__dirname, "..", "dist"), 13 | filename: "bundle.js", 14 | publicPath: "/" 15 | }, 16 | module: { 17 | rules: require('./webpack.loaders.js') 18 | }, 19 | plugins: [ 20 | new ExtractTextPlugin("style.css"), 21 | new webpack.DefinePlugin({ 22 | "environment": '"developement"', 23 | NODE_ENV: JSON.stringify("developement") 24 | }), 25 | new webpack.HotModuleReplacementPlugin(), 26 | new HtmlWebpackPlugin({template: path.join("src", "public", "index.ejs")}), 27 | ], 28 | resolve: { 29 | modules: ["node_modules", "src"] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /webpack/webpack.config.production.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var webpack = require("webpack"); 3 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | var ExtractTextPlugin = require("extract-text-webpack-plugin"); 5 | var rimraf = require('rimraf'); 6 | 7 | 8 | module.exports = { 9 | mode: "production", 10 | entry: ["./src/index.js"], 11 | output: { 12 | path: path.join(__dirname, "..", "dist"), 13 | filename: "bundle.js", 14 | publicPath: "/" 15 | }, 16 | module: { 17 | rules: require('./webpack.loaders.js') 18 | }, 19 | optimization: { 20 | minimize: true 21 | }, 22 | plugins: [ 23 | function() { 24 | console.log("Clearing /dist directory"); 25 | rimraf.sync(path.join(__dirname, "..", "dist"), require('fs'), (er) => { 26 | if(er) console.log("Clearing of /dist directory failed", er); 27 | }); 28 | }, 29 | new ExtractTextPlugin("style.css"), 30 | new webpack.DefinePlugin({ 31 | "environment": '"production"', 32 | NODE_ENV: JSON.stringify("production") 33 | }), 34 | new HtmlWebpackPlugin({template: path.join("src", "public", "index.ejs")}), 35 | ], 36 | resolve: { 37 | modules: ["node_modules", "src"] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /webpack/webpack.loaders.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var ExtractTextPlugin = require("extract-text-webpack-plugin"); 3 | 4 | 5 | module.exports = [ 6 | { 7 | test: /\.js$/, 8 | exclude: /node_modules/, 9 | use: [{ 10 | loader: "babel-loader" 11 | }] 12 | }, 13 | { 14 | test: /\.css$/, 15 | use: ((env) => { 16 | if(env == 'production') { 17 | return ExtractTextPlugin.extract({ 18 | use: [{ 19 | loader:'css-loader', 20 | options: { 21 | modules: true, 22 | localIdentName: '[local]--[hash:base64:5]', 23 | } 24 | }] 25 | }); 26 | } 27 | else { 28 | return [{ 29 | loader: 'style-loader' 30 | }, { 31 | loader:'css-loader', 32 | options: { 33 | modules: true, 34 | localIdentName: '[local]--[hash:base64:5]', 35 | } 36 | } 37 | ]; 38 | } 39 | })(process.env.NODE_ENV) 40 | }, 41 | { 42 | test: /\.less$/, 43 | use: ((env) => { 44 | if(env == 'production') { 45 | return ExtractTextPlugin.extract({ 46 | use: [{ 47 | loader:'css-loader', 48 | options: { 49 | modules: true, 50 | localIdentName: '[local]--[hash:base64:5]', 51 | } 52 | }, { 53 | loader:'less-loader' 54 | }] 55 | }); 56 | } 57 | else { 58 | return [{ 59 | loader: 'style-loader' 60 | }, { 61 | loader:'css-loader', 62 | options: { 63 | modules: true, 64 | localIdentName: '[local]--[hash:base64:5]', 65 | } 66 | }, { 67 | loader:'less-loader' 68 | } 69 | ]; 70 | } 71 | })(process.env.NODE_ENV) 72 | }, 73 | { 74 | test: /\.(ttf|woff|woff2|jpeg|jpg|png|gif|svg)$/, 75 | use: [ 76 | { 77 | loader: "file-loader", 78 | options: { 79 | outputPath: path.join("assets", "/"), 80 | publicPath: "assets/", 81 | name: '[name]--[hash:base64:5].[ext]' 82 | } 83 | } 84 | ] 85 | } 86 | ]; 87 | --------------------------------------------------------------------------------