UNPKG

11.7 kBJavaScriptView Raw
1//
2
3
4
5const webpack = require('webpack')
6const path = require('path')
7const AssetsPlugin = require('assets-webpack-plugin')
8const ExtractTextPlugin = require('extract-text-webpack-plugin')
9const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
10const autoprefixer = require('autoprefixer')
11const { ArrayUtils, LogicUtils } = require('constellate-dev-utils')
12const generateBabelConfig = require('./generate-babel-config')
13
14
15
16
17
18module.exports = function generateConfig(pkg , options = {}) {
19 const { devServerPort } = options
20
21 const env = process.env.NODE_ENV
22
23 const webpackConfig = {
24 // Keep quiet in dev mode.
25 stats: LogicUtils.onlyIf(env === 'development', 'none'),
26
27 target: 'web',
28
29 entry: {
30 // We name it "index" to make it easy to resolve the entry files within
31 // the bundled output.
32 index: [
33 // The application source entry.
34 pkg.paths.packageEntryFile,
35 ],
36 },
37
38 output: {
39 // The dir in which our bundle should be output.
40 path: pkg.paths.packageBuildOutput,
41
42 // The filename format for the entry chunk.
43 // eslint-disable-next-line no-nested-ternary
44 filename:
45 env === 'production'
46 ? // For a production build of a web target we want a cache busting
47 // name format.
48 '[name]-[chunkhash].js'
49 : // Else we use a predictable name format.
50 '[name].js',
51
52 // The name format for any additional chunks produced for the bundle.
53 chunkFilename:
54 env === 'development' ? '[name]-[hash].js' : '[name]-[chunkhash].js',
55
56 publicPath:
57 env === 'development' && !!devServerPort
58 ? // As we run a seperate webpack-dev-server in development we need an
59 // absolute http path for the public path.
60 `http://0.0.0.0:${devServerPort}/constellate/${pkg.name}/`
61 : `/constellate/${pkg.name}/`,
62
63 // Add /* filename */ comments to generated require()s in the output.
64 pathinfo: env === 'development',
65
66 libraryTarget: 'var',
67 },
68
69 resolve: {
70 extensions: ['.js', '.json', '.jsx'],
71 modules: ['node_modules', pkg.paths.monoRepoRootNodeModules],
72 },
73
74 // Source map settings.
75 devtool:
76 env === 'development'
77 ? // Produces an external source map (lives next to bundle output files).
78 'source-map'
79 : // Produces no source map.
80 'hidden-source-map',
81 // Only maps line numbers
82 // 'cheap-eval-source-map',
83
84 // https://webpack.js.org/configuration/performance/
85 performance: {
86 hints: env === 'development' ? false : 'warning',
87 },
88
89 plugins: ArrayUtils.removeNil([
90 new webpack.EnvironmentPlugin({
91 // It is really important to use NODE_ENV=production in order to use
92 // optimised versions of some node_modules, such as React.
93 NODE_ENV: env,
94 // This may be handy for someone...
95 CONSTELLATE_IS_WEBPACK: JSON.stringify(true),
96 }),
97
98 // Generates a JSON file containing a map of all the output files for
99 // our webpack bundle.
100 new AssetsPlugin({
101 filename: 'webpack-manifest.json',
102 path: pkg.paths.packageBuildOutput,
103 }),
104
105 // Moment.js is an extremely popular library that bundles large locale files
106 // by default due to how Webpack interprets its code. This is a practical
107 // solution that requires the user to opt into importing specific locales.
108 // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
109 // You can remove this if you don't use Moment.js:
110 new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
111
112 // This makes debugging much easier as webpack will add filenames to
113 // modules
114 LogicUtils.onlyIf(
115 env === 'development',
116 () => new webpack.NamedModulesPlugin(),
117 ),
118
119 // For our production web targets we need to make sure we pass the required
120 // configuration to ensure that the output is minimized/optimized.
121 LogicUtils.onlyIf(
122 env === 'production',
123 () =>
124 new webpack.optimize.UglifyJsPlugin({
125 // sourceMap: config('includeSourceMapsForOptimisedClientBundle'),
126 compress: {
127 screw_ie8: true,
128 warnings: false,
129 },
130 mangle: {
131 screw_ie8: true,
132 },
133 output: {
134 comments: false,
135 screw_ie8: true,
136 },
137 }),
138 ),
139
140 // For our production web targets we need to make sure we pass the required
141 // configuration to ensure that the output is minimized/optimized.
142 LogicUtils.onlyIf(
143 env === 'production',
144 () =>
145 new webpack.LoaderOptionsPlugin({
146 minimize: true,
147 debug: process.env.DEBUG === 'true',
148 }),
149 ),
150
151 // Watcher doesn't work well if you mistype casing in a path so we use
152 // a plugin that prints an error when you attempt to do this.
153 // See https://github.com/facebookincubator/create-react-app/issues/240
154 LogicUtils.onlyIf(
155 env === 'development',
156 () => new CaseSensitivePathsPlugin(),
157 ),
158
159 // TODO: Look at how this would work with yarn workspaces
160 /*
161 // If you require a missing module and then `npm install` it, you still have
162 // to restart the development server for Webpack to discover it. This plugin
163 // makes the discovery automatic so you don't have to restart.
164 // See https://github.com/facebookincubator/create-react-app/issues/186
165 LogicUtils.onlyIf(
166 env === 'development',
167 () => new WatchMissingNodeModulesPlugin(package.paths.packageNodeModules),
168 ),
169 */
170
171 // We don't want webpack errors to occur during development as it will
172 // kill our dev servers.
173 LogicUtils.onlyIf(
174 env === 'development',
175 () => new webpack.NoEmitOnErrorsPlugin(),
176 ),
177
178 // We need this plugin to enable hot reloading of our client.
179 LogicUtils.onlyIf(
180 env === 'development',
181 () => new webpack.HotModuleReplacementPlugin(),
182 ),
183
184 // For a production build of a web target we need to extract the CSS into
185 // CSS files.
186 LogicUtils.onlyIf(
187 env === 'production',
188 () =>
189 new ExtractTextPlugin({
190 filename: '[name].[contenthash:8].css',
191 allChunks: true,
192 }),
193 ),
194
195 // This will inject the module.hot.accept code in the entry file for our
196 // development build.
197 LogicUtils.onlyIf(
198 env === 'development',
199 () =>
200 // eslint-disable-next-line global-require
201 new (require('./plugins/InjectHMRCodeForEntryModule.js'))({
202 entryFile: pkg.paths.packageEntryFile,
203 }),
204 ),
205 ]),
206
207 module: {
208 rules: ArrayUtils.removeNil([
209 {
210 test: /\.js$/,
211 use: [
212 {
213 loader: require.resolve('cache-loader'),
214 options: {
215 cacheDirectory: pkg.paths.packageWebpackCache,
216 },
217 },
218 {
219 loader: require.resolve('babel-loader'),
220 options: generateBabelConfig(pkg),
221 },
222 ],
223 include: [pkg.paths.packageRoot],
224 exclude: [pkg.paths.packageNodeModules, pkg.paths.packageBuildOutput],
225 },
226
227 {
228 test: /\.(jpg|jpeg|png|gif|ico|eot|svg|ttf|woff|woff2|otf|mp4|mp3|ogg|swf|webp)$/,
229 loader: require.resolve('url-loader'),
230 options: {
231 // We only emit files when building a web bundle, node bundles only
232 // need the file paths.
233 emitFile: true,
234 // Any files under this size will be "inlined" as a base64 encoding.
235 limit: 10000,
236 },
237 },
238
239 // For development web targets we will use the style loader which will
240 // allow hot reloading of the CSS.
241 LogicUtils.onlyIf(env === 'development', () => ({
242 test: /\.css$/,
243 loader: [
244 require.resolve('style-loader'),
245 {
246 loader: require.resolve('css-loader'),
247 options: {
248 importLoaders: 1,
249 // Include sourcemaps for dev experience++.
250 sourceMap: true,
251 },
252 },
253 {
254 loader: require.resolve('postcss-loader'),
255 options: {
256 ident: 'postcss',
257 plugins: () => [
258 autoprefixer({
259 browsers: [
260 '>1%',
261 'last 4 versions',
262 'Firefox ESR',
263 'not ie < 9',
264 ],
265 }),
266 ],
267 },
268 },
269 ],
270 include: [pkg.paths.packageRoot, pkg.paths.monoRepoRootNodeModules],
271 exclude: [pkg.paths.packageBuildOutput],
272 })),
273
274 // For a production web target we use the ExtractTextPlugin which
275 // will extract our CSS into CSS files.
276 // Note: The ExtractTextPlugin needs to be registered within the
277 // plugins section too.
278 LogicUtils.onlyIf(env === 'production', () => ({
279 test: /\.css$/,
280 loader: ExtractTextPlugin.extract({
281 fallback: require.resolve('style-loader'),
282 use: [
283 {
284 loader: require.resolve('css-loader'),
285 options: {
286 importLoaders: 1,
287 },
288 },
289 {
290 loader: require.resolve('postcss-loader'),
291 options: {
292 ident: 'postcss',
293 plugins: () => [
294 autoprefixer({
295 browsers: [
296 '>1%',
297 'last 4 versions',
298 'Firefox ESR',
299 'not ie < 9',
300 ],
301 }),
302 ],
303 },
304 },
305 ],
306 }),
307 include: [pkg.paths.packageRoot, pkg.paths.monoRepoRootNodeModules],
308 exclude: [pkg.paths.packageBuildOutput],
309 })),
310 ]),
311 },
312 }
313
314 if (env === 'development' && !!devServerPort) {
315 webpackConfig.entry.index = [
316 `${path.dirname(
317 require.resolve('webpack-dev-server/package.json'),
318 )}/client?http://0.0.0.0:${devServerPort}`,
319 `${path.dirname(require.resolve('webpack/package.json'))}/hot/dev-server`,
320 ...webpackConfig.entry.index,
321 ]
322
323 // For web target packages we rely on webpack-dev-server, but will provide
324 // the configuration here to make our configuration more centralised.
325 webpackConfig.devServer = {
326 host: '0.0.0.0',
327 port: devServerPort,
328 disableHostCheck: true,
329 contentBase: false,
330 headers: {
331 'Access-Control-Allow-Origin': '*',
332 },
333 compress: true,
334 publicPath: webpackConfig.output.publicPath,
335 noInfo: true,
336 quiet: true,
337 historyApiFallback: true,
338 hot: true,
339 // Will show compile errors in browser.
340 overlay: true,
341 watchOptions: {
342 // Watching too many files can result in high CPU/memory usage.
343 // We will manually control reloads based on dependency changes.
344 ignored: /node_modules/,
345 // TODO: Allow watching of any dependencies that are Constellate packages.
346 },
347 }
348 }
349
350 return webpackConfig
351}