commit d5c252ee54ea860830fe71fc9ffc20eca7d6d853 Author: Ali Al Dallal Date: Mon Jun 15 09:21:19 2015 -0400 Initial commit diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..86c445f5 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015", "react"] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..2b6d1dc3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{js,jsx,html,sass}] +charset = utf-8 +indent_style = tab +indent_size = 2 +trim_trailing_whitespace = true diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 00000000..b56f2e1e --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,81 @@ +--- + extends: + - plugin:react/recommended + + env: + browser: true + node: true + es6: true + + parserOptions: + ecmaVersion: 6 + sourceType: "module" + ecmaFeatures: + jsx: true + + globals: + __DEV__: true + __SERVER__: true + + plugins: + - react + + rules: + react/jsx-uses-vars: 1 + react/prop-types: [1, { ignore: [children] }] + + semi: 0 + key-spacing: 1 + curly: 0 + consistent-return: 0 + space-infix-ops: 1 + camelcase: 0 + no-spaced-func: 1 + no-alert: 1 + eol-last: 1 + comma-spacing: 1 + eqeqeq: 1 + + # possible errors + comma-dangle: 0 + no-cond-assign: 2 + no-console: 0 + no-constant-condition: 2 + no-control-regex: 2 + no-debugger: 2 + no-dupe-args: 2 + no-dupe-keys: 2 + no-duplicate-case: 2 + no-empty-character-class: 2 + no-empty: 2 + no-ex-assign: 2 + no-extra-boolean-cast: 2 + no-extra-parens: 0 + no-extra-semi: 2 + no-func-assign: 2 + no-inner-declarations: 2 + no-invalid-regexp: 2 + no-irregular-whitespace: 2 + no-negated-in-lhs: 2 + no-obj-calls: 2 + no-regex-spaces: 2 + no-sparse-arrays: 2 + no-unexpected-multiline: 2 + no-unreachable: 2 + use-isnan: 2 + valid-jsdoc: 2 + valid-typeof: 2 + + no-redeclare: 2 + + init-declarations: 2 + no-catch-shadow: 2 + no-delete-var: 2 + no-label-var: 2 + no-shadow-restricted-names: 2 + no-shadow: 2 + no-undef-init: 2 + no-undef: 2 + no-undefined: 2 + no-unused-vars: 2 + no-use-before-define: 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..99084787 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules + +# Ignore build files +public diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..2b6f469f --- /dev/null +++ b/.jshintrc @@ -0,0 +1,3 @@ +{ + "esversion": 6 +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..20de79f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Ali Al Dallal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 00000000..02d9ff93 --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# react-webpack-babel +Simple React Webpack Babel Starter Kit + +Tired of complicated starters with 200MB of dependencies which are hard to understand and modify? + +Try this is a simple [React](https://facebook.github.io/react/), [Webpack](http://webpack.github.io/) and [Babel](https://babeljs.io/) application with nothing else in it. + +### What's in it? + +* Simple src/index.jsx and src/index.css (local module css). +* Webpack configuration for development (with hot reloading) and production (with minification). +* CSS module loading, so you can include your css by ```import styles from './path/to.css';```. +* Both js(x) and css hot loaded during development. + +### To run + +* You'll need to have [git](https://git-scm.com/) and [node](https://nodejs.org/en/) installed in your system. +* Fork and clone the project: + +``` +> $ git clone THIS_REPO_URL +``` + +* Then install the dependencies: + +``` +> $ npm install +``` + +* Run development server: + +``` +> $ npm start +``` + +Open the web browser to `http://localhost:8888/` + +### To build production package + +``` +> $ npm run build +``` + +### Nginx Config + +Here is the suggested Nginx config: +``` +server { + # ... root and other options + + gzip on; + gzip_http_version 1.1; + gzip_types text/plain text/css text/xml application/javascript image/svg+xml; + + location ~ \.html?$ { + expires 1d; + } + + location ~ \.(svg|ttf|js|css|svgz|eot|otf|woff|jpg|jpeg|gif|png|ico)$ { + access_log off; + log_not_found off; + expires max; + } +} +``` + +### Eslint +There is a .eslint.yaml config for eslint ready with React plugin. +To use it, you need to install additional dependencies though: + +``` +> npm install --save-dev eslint eslint-plugin-react +``` + +To do the actual linting, run: + +``` +> npm run lint +``` + +### Notes on importing css styles +* styles having /src/ in their absolute path are considered part of the application and exported as local css modules. +* styles having /node_modules|global/ in their absolute path are considered global styles used by many components and are included in the css bundle directly. + +### Contribute +Please contribute to the project if you think this can be done better in anyway even for this README :) diff --git a/package.json b/package.json new file mode 100644 index 00000000..f018d523 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "react-webpack-babel", + "version": "0.0.3", + "description": "React Webpack Babel Starter Kit", + "main": "''", + "scripts": { + "build": "webpack --config webpack.production.config.js --progress --profile --colors", + "start": "webpack-dev-server --progress --profile --colors", + "lint": "eslint --ext js --ext jsx src || exit 0" + }, + "repository": { + "type": "git", + "url": "https://github.com/alicoding/react-webpack-babel" + }, + "author": "Ali Al Dallal", + "license": "MIT", + "homepage": "https://github.com/alicoding/react-webpack-babel#readme", + "dependencies": { + "bootstrap": "^4.0.0-alpha.3", + "node-sass": "^3.9.2", + "react": "15.3.0", + "react-dom": "15.3.0", + "sass-loader": "^4.0.1" + }, + "devDependencies": { + "babel-core": "6.14.0", + "babel-loader": "6.2.4", + "babel-plugin-transform-class-properties": "^6.11.5", + "babel-plugin-transform-decorators-legacy": "^1.3.4", + "babel-plugin-transform-runtime": "^6.15.0", + "babel-preset-es2015": "6.14.0", + "babel-preset-react": "6.11.1", + "babel-runtime": "^6.11.6", + "css-loader": "0.25.0", + "extract-text-webpack-plugin": "^1.0.1", + "file-loader": "0.9.0", + "html-webpack-plugin": "^2.22.0", + "react-hot-loader": "1.3.0", + "style-loader": "0.13.1", + "url-loader": "0.5.7", + "webpack": "1.13.2", + "webpack-cleanup-plugin": "^0.3.0", + "webpack-dev-server": "1.15.1" + } +} diff --git a/src/app.jsx b/src/app.jsx new file mode 100644 index 00000000..09c33d13 --- /dev/null +++ b/src/app.jsx @@ -0,0 +1,16 @@ +import 'bootstrap/dist/css/bootstrap.min.css'; +import styles from './index.scss'; +import React from 'react'; + +export default class App extends React.Component { + render() { + return ( +
+

It Works!

+

This React project just works including module local styles.

+

Global bootstrap css import works too as you can see on the following button.

+

Enjoy!

+
+ ) + } +} \ No newline at end of file diff --git a/src/index.jsx b/src/index.jsx new file mode 100644 index 00000000..5aa7e017 --- /dev/null +++ b/src/index.jsx @@ -0,0 +1,5 @@ +import React from 'react'; +import { render } from 'react-dom'; +import App from './app.jsx'; + +render(, document.querySelector("#app")); diff --git a/src/index.scss b/src/index.scss new file mode 100644 index 00000000..e5eb38a6 --- /dev/null +++ b/src/index.scss @@ -0,0 +1,4 @@ +.blueBg { + background-color: red; + color: white; +} diff --git a/src/template.html b/src/template.html new file mode 100644 index 00000000..d13e7666 --- /dev/null +++ b/src/template.html @@ -0,0 +1,13 @@ + + + + + <%= htmlWebpackPlugin.options.title %> + + + +
+
Loading...
+
+ + diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..94c389f1 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,74 @@ +"use strict"; +var webpack = require('webpack'); +var path = require('path'); +var loaders = require('./webpack.loaders'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); + +const HOST = process.env.HOST || "127.0.0.1"; +const PORT = process.env.PORT || "8888"; + +// global css +loaders.push({ + test: /[\/\\](node_modules|global)[\/\\].*\.css$/, + loaders: [ + 'style?sourceMap', + 'css' + ] +}); +// local scss modules +loaders.push({ + test: /[\/\\]src[\/\\].*\.scss/, + loaders: [ + 'style?sourceMap', + 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]', + 'sass' + ] +}); + +// local css modules +loaders.push({ + test: /[\/\\]src[\/\\].*\.css/, + loaders: [ + 'style?sourceMap', + 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]' + ] +}); + +module.exports = { + entry: [ + `webpack-dev-server/client?http://${HOST}:${PORT}`, + `webpack/hot/only-dev-server`, + `./src/index.jsx` // Your appʼs entry point + ], + devtool: process.env.WEBPACK_DEVTOOL || 'cheap-module-source-map', + output: { + path: path.join(__dirname, 'public'), + filename: 'bundle.js' + }, + resolve: { + extensions: ['', '.js', '.jsx'] + }, + module: { + loaders + }, + devServer: { + contentBase: "./public", + // do not print bundle build stats + noInfo: true, + // enable HMR + hot: true, + // embed the webpack-dev-server runtime into the bundle + inline: true, + // serve index.html in place of 404 responses to allow HTML5 history + historyApiFallback: true, + port: PORT, + host: HOST + }, + plugins: [ + new webpack.NoErrorsPlugin(), + new webpack.HotModuleReplacementPlugin(), + new HtmlWebpackPlugin({ + template: './src/template.html' + }), + ] +}; diff --git a/webpack.loaders.js b/webpack.loaders.js new file mode 100644 index 00000000..c855e335 --- /dev/null +++ b/webpack.loaders.js @@ -0,0 +1,51 @@ +module.exports = [ + { + test: /\.jsx?$/, + exclude: /(node_modules|bower_components|public)/, + loaders: ['react-hot'] + }, + { + test: /\.jsx?$/, + exclude: /(node_modules|bower_components|public)/, + loader: 'babel', + query: { + presets: ['es2015', 'react'], + plugins: ['transform-runtime', 'transform-decorators-legacy', 'transform-class-properties'], + } + }, + { + test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, + exclude: /(node_modules|bower_components)/, + loader: "file" + }, + { + test: /\.(woff|woff2)$/, + exclude: /(node_modules|bower_components)/, + loader: "url?prefix=font/&limit=5000" + }, + { + test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, + exclude: /(node_modules|bower_components)/, + loader: "url?limit=10000&mimetype=application/octet-stream" + }, + { + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + exclude: /(node_modules|bower_components)/, + loader: "url?limit=10000&mimetype=image/svg+xml" + }, + { + test: /\.gif/, + exclude: /(node_modules|bower_components)/, + loader: "url-loader?limit=10000&mimetype=image/gif" + }, + { + test: /\.jpg/, + exclude: /(node_modules|bower_components)/, + loader: "url-loader?limit=10000&mimetype=image/jpg" + }, + { + test: /\.png/, + exclude: /(node_modules|bower_components)/, + loader: "url-loader?limit=10000&mimetype=image/png" + } +]; diff --git a/webpack.production.config.js b/webpack.production.config.js new file mode 100644 index 00000000..44e606e5 --- /dev/null +++ b/webpack.production.config.js @@ -0,0 +1,65 @@ + +var webpack = require('webpack'); +var path = require('path'); +var loaders = require('./webpack.loaders'); +var ExtractTextPlugin = require('extract-text-webpack-plugin'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var WebpackCleanupPlugin = require('webpack-cleanup-plugin'); + +// local css modules +loaders.push({ + test: /[\/\\]src[\/\\].*\.css/, + loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]') +}); + +// local scss modules +loaders.push({ + test: /[\/\\]src[\/\\].*\.scss/, + loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]', 'sass') +}); +// global css files +loaders.push({ + test: /[\/\\](node_modules|global)[\/\\].*\.css$/, + loader: ExtractTextPlugin.extract('style', 'css') +}); + +module.exports = { + entry: [ + './src/index.jsx' + ], + output: { + path: path.join(__dirname, 'public'), + filename: '[chunkhash].js' + }, + resolve: { + extensions: ['', '.js', '.jsx'] + }, + module: { + loaders + }, + plugins: [ + new WebpackCleanupPlugin(), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: '"production"' + } + }), + new webpack.optimize.UglifyJsPlugin({ + compress: { + warnings: false, + screw_ie8: true, + drop_console: true, + drop_debugger: true + } + }), + new webpack.optimize.OccurenceOrderPlugin(), + new ExtractTextPlugin('[contenthash].css', { + allChunks: true + }), + new HtmlWebpackPlugin({ + template: './src/template.html', + title: 'Webpack App' + }), + new webpack.optimize.DedupePlugin() + ] +};