-
Notifications
You must be signed in to change notification settings - Fork 0
/
webpack.config.js
378 lines (375 loc) · 12.8 KB
/
webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
var webpack = require('webpack')
const path = require('path')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const Dotenv = require('dotenv-webpack')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const occurrenceOrderPlugin = new webpack.optimize.OccurrenceOrderPlugin()
const hotModuleReplacementPlugin = new webpack.HotModuleReplacementPlugin()
const cleanWebpackPlugin = new CleanWebpackPlugin(['dist'])
/**
* The always write to disk option is available because of the hard disk plugin.
* This setup enables hard disk writting for Webpack dev server.
* @type {HtmlWebpackPlugin}
*/
const htmlWebpackPlugin = new HtmlWebpackPlugin({
alwaysWriteToDisk: true,
template: '../public/index.html'
})
const htmlWebpackHarddiskPlugin = new HtmlWebpackHarddiskPlugin({
/**
* Output path is used by middleware like webpack dev server as a location from
* which files will be served.
* @type {String}
*/
outputPath: path.resolve(__dirname, 'dist')
})
const extractTextPlugin = new ExtractTextPlugin({
/**
* Location and filename where the extracted css will be saved.
* @type {String}
*/
filename: 'css/index.css'
})
const copyWebpackPlugin = new CopyWebpackPlugin([{ from: '../favicon.ico' }], {
copyUnmodified: true
})
module.exports = env => {
/**
* In package json under scripts we set the --env.NODE_ENV= variable.
* If we are in development set the debug variable to true.
* @type {Boolean}
*/
let debug = env ? env.NODE_ENV !== 'production' : true
/*
* Because of env, now we have a function and we need to return the
* configuration.
*/
return {
/**
* This is the base directory, an absolute path, for resolving entry points
* and loaders from configuration.
* @type {String}
*/
context: path.resolve(__dirname, 'src'), // maybe just __dirname
/**
* If env is debug do inline-sourcemapping which helps console logging
* https://webpack.js.org/configuration/devtool/#devtool
* @type {[type]}
*/
devtool: debug ? 'inline-sourcemap' : false,
/**
* Webpack dev server, by adding /webpack-dev-server to the url we can see
* where the files are being served from, example:
* localhost:8888/webpack-dev-server
* @type {Object}
*/
devServer: {
/**
* With contentBase we can tell the server where to serve content from.
* This is only necessary if we want to serve static files.
* @type {String}
*/
contentBase: path.resolve(__dirname, 'dist'),
/**
* Watch for changes on all the files under contentBase.
* @type {Boolean}
*/
watchContentBase: true,
/**
* In order to enable gzip compression for everything served we need to
* set the compress option.
* @type {Boolean}
*/
compress: true,
/**
* We can set deploy port number for the application in the
* webpack-dev-server.
* @type {Number}
*/
port: 8895,
/**
* We can control what bundle information is displayed. To show only errors
* more info on stats options https://webpack.js.org/configuration/stats
* @type {String}
*/
stats: 'errors-only',
/**
* If we want dev-server to open the app at the first time in our browser
* and just refresh afterwards while we change our code we can use the
* open option.
* @type {Boolean}
*/
open: true,
/**
* Inline option adds “Live reloading” for the entire page.
* @type {Boolean}
*/
inline: true,
/**
* Hot option enables “Hot Module Reloading” HMR that tries to reload just
* the component that’s changed. By setting both inline and hot options,
* HMR will be done first and if that is not enough the entire page will
* reload because of the inline option.
* @type {Boolean}
*/
hot: true,
/**
* In order to be able to use react-router-dom with BrowserRouter this
* option has to be set to true, reference:
* https://tylermcginnis.com/react-router-cannot-get-url-refresh/
* @type {Boolean}
*/
historyApiFallback: true
},
/**
* Entry source, where magic needs to happen Q.Q
* @type {Array}
*/
entry: ['babel-polyfill', './index.js'], // maybe ./src/index.js
/**
* Output option has the path to the folder where we will create the new
* bundle with name as filename.
* @type {Object}
*/
output: {
/**
* Path tells Webpack where it should store the result.
* We can make use of output path using nodeJs path module or just do
* string concatenation (using path is preffered).
* path.resolve https://nodejs.org/dist/latest-v6.x/docs/api/path.html#path_path_resolve_paths
* path.join https://nodejs.org/dist/latest-v6.x/docs/api/path.html#path_path_join_paths
* @type {String}
*/
path: path.resolve(__dirname, 'dist'),
/**
* publicPath is used by Webpack plugins to update the URLs inside CSS,
* HTML files when generating production builds.
* We need to set the public path option to the folder that stores the bundle
* https://webpack.js.org/configuration/dev-server/#devserver-publicpath-
* @type {String}
*/
publicPath: '/',
/**
* Naming with multiple bundles for multiple entry points is possible:
* using the entry name "[name].bundle.js"
* using the hash based on each chunk's content "[chunkhash].bundle.js"
* more on https://webpack.js.org/configuration/output/#output-filename
* @type {String}
*/
filename: 'js/bundle.js',
chunkFilename: 'js/[id].[hash].bundle.js'
},
/**
* This option determines how the different types of modules in the project
* will be treated https://webpack.js.org/configuration/module/
* @type {Object}
*/
module: {
/**
* In order to boost build performances we can ignore some large libraries
* if they pass the regex test.
* @param {String} content File name
* @return {Boolean} True if the content doesn't need to be parsed
*/
noParse: function (content) {
return /jquery/.test(content)
},
/**
* Every element of the rules option array is an object containing
* individual loaders and their respective configurations.
* @type {Array}
*/
rules: [
// Babel loader
{
/**
* Required option, the loader needs to know which file extension it’s
* going to work with.
* @type {RegExp}
*/
test: /\.js$/,
/**
* Optional, the loader needs a directory to locate where it’s working
* files are stored.
* @type {RegExp}
*/
include: /src/,
/**
* Optional, in order to save a lot of memory and execution time we
* don't need to parse modules from a specific directory.
* @type {RegExp}
*/
exclude: /node_modules/,
/**
* Required option, the rule must have a loader property as a string.
* Here we set the loaders we want to use.
* Loaders can be chained by passing multiple loaders, they will be
* applied from right to left (last to first configured).
* They can have options property as a string or object.
* This value is passed to the loader, which should interpret it as
* loader options https://webpack.js.org/configuration/module/#module-rules
* @type {Object}
*/
use: {
loader: 'babel-loader',
options: {
presets: ['react', 'env'],
plugins: [
'transform-class-properties',
'transform-object-rest-spread',
'syntax-dynamic-import',
'transform-regenerator'
]
}
}
},
// HTML loader
{
test: /\.html$/,
use: [
{
loader: 'html-loader',
options: {
/**
* This option will minimize the .html files, like UglifyJs does
* to .js files https://webpack.js.org/loaders/html-loader/#examples
* @type {Boolean}
*/
minimize: !debug // if in production mode minimize html file
}
}
]
},
// SCSS, CSS loaders
{
test: /\.(css|scss|sass)$/,
use: extractTextPlugin.extract({
// postcss loader is used in order for autoprefixer to auto add
// browser specific prefixes
use: [
{
// the css loader is set up to use CSS modules spec.
// More on https://github.com/webpack-contrib/css-loader#modules
loader: 'css-loader',
options: {
modules: true,
importLoaders: 2, // postcss and sass
localIdentName: '[name]___[local]___[hash:base64:5]'
}
},
'postcss-loader',
'sass-loader'
],
fallback: 'style-loader'
})
},
// File loader for pictures
{
test: /\.(jpe?g|png|gif|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192, // return DataURL if image size <= 8KB
name: 'assets/[name].[ext]',
fallback: 'file-loader' // use file loader for size > 8KB
}
}
]
},
// File loader for fonts
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
}
]
},
/**
* Since Webpack 4 optimizations are removed from the plugin option, now
* we need to setup the optimization option.
* @type {Object}
*/
optimization: debug
? {}
: {
/**
* For production we can uglify the .js files.
* @type {Boolean}
*/
minimize: true
},
/**
* The plugins option is used to customize the Webpack build process in
* different ways. We can run different plugins depending on the environment.
* @type {Array}
*/
plugins: debug
? [
/**
* Enable hot option under devServer.
* @type {Object}
*/
hotModuleReplacementPlugin,
/**
* Enable .html files script bundling and minimization (check html-loader).
* @type {Object}
*/
htmlWebpackPlugin,
/**
* Enables hard disk file writting for html script injection. This way
* Webpack's dev server will see the injected script.
* @type {[type]}
*/
htmlWebpackHarddiskPlugin,
/**
* Extracts css from the bundle.
* @type {[type]}
*/
extractTextPlugin,
/**
* Get environment variables by using process.env, options:
* systemvars (false) - If true, will add all system variables as well
* silent (false) - If true, all warnings will be surpressed
* safe (false) - If false ignore safe-mode, if true load
* './.env.example', if a string load that file as the sample.
* @type {Object}
*/
new Dotenv({
path: '.env.dev',
systemvars: true
}),
/**
* Copy static files such as favicon.ico.
* @type {Object}
*/
copyWebpackPlugin
]
: [
/**
* We can clear the content from our dist folder before every build:prod
* by utilising the clean-webpack-plugin https://github.com/johnagan/clean-webpack-plugin
* @type {Object}
*/
cleanWebpackPlugin,
htmlWebpackPlugin,
htmlWebpackHarddiskPlugin,
extractTextPlugin,
/**
* Official docs https://github.com/webpack/docs/wiki/optimization:
* Webpack gives our modules and chunks ids to identify them. Webpack can
* vary the distribution of the ids to get the smallest id length for
* often used ids with a simple option.
* @type {Object}
*/
occurrenceOrderPlugin,
new Dotenv({
path: '.env.prod',
systemvars: true
}),
copyWebpackPlugin
]
}
}