Foreword: I generally do these things as Gists, but I figured this one is such a common question it would be worthwhile posting here.
Let's talk about Webpack for a sec. It's great and I think there comes a point in many projects where you step slightly beyond what Browserify and its plugin ecosystem are able to do (ancient non-npm dependencies, etc). Often times the best solution is still Browserify, just with some tweaking - maybe you need to fork that module and refactor it a bit to properly export things.
What if there was a way to make Webpack behave as simply as Browserify, right out of the box? That's where I'm at. It's annoying to switch back and forth depending on the project, when I'm generally using these two tools to accomplish the same task.
Here's what I now use when starting a new Webpack-based app project (not for libraries, for just use Browserify):
Install Webpack & Friends
npm i -D webpack webpack-dev-server babel babel-loader less-loader css-loader style-loader autoprefixer-loader
Add NPM-Scripts
"scripts": {
"dev": "WEBPACK=webpack-dev-server npm run build -s",
"build": "${webpack:-webpack} src/index.js --output-path build --output-file bundle.js -d --module-bind 'js=babel' --module-bind 'less=style!css!autoprefer!less'"
}
... then run them via npm run dev
and npm run build
.
Full Webpack Setup
This is a basic Webpack project template for a web app written in ES6 & LESS.
This assumes you have a directory structure as follows:
package.json
webpack.config.js
src/
index.js
static/
index.html
Installation
1. Clone the repo and start your own:
git clone git@gist.github.com:3c83db422f03ef66ea36.git
rm -rf .git
git init
2. Install dependencies:
npm install
That's it.
Development Workflow
Start the live-reload dev server:
PORT=8080 npm run dev
Open up http://localhost:8080/webpack-dev-server/ to see your app. The app gets reloaded as files change.
Deployment Workflow
To deploy your static app, upload the contents of build/
to a web server.
Or, push this repo to heroku. http-server
is used to serve the files in build/
.
Or, and like the best option, deploy this to Firebase. Use this firebase.json.
package.json
{ | |
"name": "webpack-starter-template", | |
"version": "0.1.0", | |
"description": "Webpack starter template", | |
"scripts": { | |
"dev": "WEBPACK_ENV=dev webpack-dev-server", | |
"start": "http-server --port ${PORT:-8080} build", | |
"prestart": "npm run build", | |
"build": "mkdir -p build && WEBPACK_ENV=production webpack && ncp src/static/ build/" | |
}, | |
"devDependencies": { | |
"autoprefixer-loader": "^3.1.0", | |
"babel-loader": "^5.3.2", | |
"css-loader": "^0.18.0", | |
"exports-loader": "^0.6.2", | |
"extract-text-webpack-plugin": "^0.8.2", | |
"http-server": "^0.8.5", | |
"less-loader": "^2.2.0", | |
"ncp": "^2.0.0", | |
"source-map-loader": "^0.1.5", | |
"style-loader": "^0.12.3", | |
"webpack": "^1.12.1", | |
"webpack-dev-server": "^1.10.1" | |
} | |
} |
webpack.config.js
var webpack = require('webpack'); | |
ExtractTextPlugin = require("extract-text-webpack-plugin"); | |
module.exports = { | |
// your root file | |
entry: './src/index.js', | |
// output JS bundle to: build/bundle.js | |
output: { | |
path: './build', | |
filename: 'bundle.js' | |
}, | |
resolve: { | |
// you can load named modules from any dirs you want. | |
// attempts to find them in the specified order. | |
modulesDirectories: [ | |
'./src/lib', | |
'node_modules' | |
] | |
}, | |
module: { | |
// you can tell webpack to avoid parsing for dependencies in any files matching an Array of regex patterns | |
noParse: [ | |
/(node_modules|~)\/(crappy\-bundled\-lib|jquery)\//gi | |
], | |
preLoaders: [ | |
// before hitting the actual loaders, load any sourcemaps specified by npm modules | |
{ loader: 'source-map' } | |
], | |
loaders: [ | |
// transpile ES6/7 to ES5 via babel | |
{ | |
test: /\.jsx?$/, | |
exclude: /node_modules/, | |
loader: 'babel' | |
}, | |
// bundle LESS and CSS into a single CSS file, auto-generating -vendor-prefixes | |
{ | |
test: /\.(less|css)$/, | |
exclude: /\b(some\-css\-framework|whatever)\b/i, | |
loader: ExtractTextPlugin.extract("style?sourceMap", "css?sourceMap!autoprefixer?browsers=last 2 version!less") | |
} | |
] | |
}, | |
plugins: ([ | |
// Avoid publishing files when compilation failed: | |
new webpack.NoErrorsPlugin(), | |
// Aggressively remove duplicate modules: | |
new webpack.optimize.DedupePlugin(), | |
// Write out CSS bundle to its own file: | |
new ExtractTextPlugin('style.css', { allChunks: true }) | |
]).concat(process.env.WEBPACK_ENV==='dev' ? [] : [ | |
new webpack.optimize.OccurenceOrderPlugin(), | |
// minify the JS bundle | |
new webpack.optimize.UglifyJsPlugin({ | |
output: { comments: false }, | |
exclude: [ /\.min\.js$/gi ] // skip pre-minified libs | |
}) | |
]), | |
// Pretty terminal output | |
stats: { colors: true }, | |
// Generate external sourcemaps for the JS & CSS bundles | |
devtool: 'source-map', | |
// `webpack-dev-server` spawns a live-reloading HTTP server for your project. | |
devServer: { | |
port: process.env.PORT || 8080, | |
contentBase: './src', | |
historyApiFallback: true | |
} | |
}; |