forked from wikimedia/mediawiki-extensions-MobileFrontend
-
Notifications
You must be signed in to change notification settings - Fork 0
/
webpack.config.js
229 lines (211 loc) · 9.88 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
'use strict';
const
CleanPlugin = require( 'clean-webpack-plugin' ),
glob = require( 'glob' ),
path = require( 'path' ),
// The output directory for all build artifacts. Only absolute paths are accepted by
// output.path.
distDir = path.resolve( __dirname, 'resources/dist' ),
// The extension used for source map files.
srcMapExt = '.map.json',
ENTRIES = {
tests: 'tests.mobilefrontend',
startup: 'mobile.startup',
categories: 'mobile.categories.overlays',
editor: 'mobile.editor.overlay',
editorVe: 'mobile.editor.ve',
languages: 'mobile.languages.structured',
mediaViewer: 'mobile.mediaViewer',
mobileInit: 'mobile.init',
talk: 'mobile.talk.overlays',
mobileOptions: 'mobile.special.mobileoptions.scripts',
nearby: 'mobile.special.nearby.scripts',
userLogin: 'mobile.special.userlogin.scripts',
watchlist: 'mobile.special.watchlist.scripts'
};
module.exports = ( env, argv ) => ( {
// Apply the rule of silence: https://wikipedia.org/wiki/Unix_philosophy.
stats: {
all: false,
// Output a timestamp when a build completes. Useful when watching files.
builtAt: true,
errors: true,
warnings: true
},
// Fail on the first build error instead of tolerating it for prod builds. This seems to
// correspond to optimization.noEmitOnErrors.
bail: argv.mode === 'production',
// Specify that all paths are relative the Webpack configuration directory not the current
// working directory.
context: __dirname,
// A map of ResourceLoader module / entry chunk names to JavaScript files to pack. E.g.,
// "mobile.startup" maps to src/mobile.startup/mobile.startup.js. The JavaScript entry could be
// named simply "index.js" but the redundancy of "[name].js" improves presentation and search-
// ability in some tools. Entry names are tightly coupled to output.filename and extension.json.
entry: {
// Note that the tests.mobilefrontend module only exists inside Special:JavaScriptTest test
// runs. It provides scaffolding (template mocks) and does not appear inside the
// ResourceLoader startup module, so does not use the `mobile.` prefix that other modules
// do. This is consistent with other test related artifacts. E.g.,
// test.mediawiki.qunit.testrunner and test.sinonjs.
// The glob module is used to ensure that all tests inside the folder (minus stubs) are
// caught and run to ensure we don't forget to register new tests.
[ENTRIES.tests]: glob.sync( './tests/node-qunit/**/*.test.js', {
ignore: './tests/node-qunit/importable.test.js'
} ),
// mobile.startup.runtime: reserved entry for the Webpack bootloader
// optimization.runtimeChunk. Without a distinct runtime chunk, it's instead bundled into
// each entry which is inefficient. This chunk should only change when Webpack or this
// configuration changes.
[ENTRIES.startup]: './src/mobile.startup/mobile.startup.js',
// Make some chunks which will be lazy loaded by resource loader.
// If we utilize webpack lazy loading instead of resource loader lazy
// loading, we won't be required to explicitly create this new chunk and
// this can be removed.
[ENTRIES.categories]: './src/mobile.categories.overlays/mobile.categories.overlays.js',
[ENTRIES.editor]: './src/mobile.editor.overlay/mobile.editor.overlay.js',
[ENTRIES.editorVe]: './src/mobile.editor.ve/mobile.editor.ve.js',
[ENTRIES.languages]: './src/mobile.languages.structured/mobile.languages.structured.js',
[ENTRIES.mediaViewer]: './src/mobile.mediaViewer/mobile.mediaViewer.js',
[ENTRIES.talk]: './src/mobile.talk.overlays/mobile.talk.overlays.js',
// all mobile skins,
[ENTRIES.mobileInit]: './src/mobile.init/mobile.init.js',
// T212823 Make a chunk for each mobile special page
[ENTRIES.mobileOptions]: './src/mobile.special.mobileoptions.scripts.js',
[ENTRIES.nearby]: './src/mobile.special.nearby.scripts/mobile.special.nearby.scripts.js',
[ENTRIES.userLogin]: './src/mobile.special.userlogin.scripts.js',
[ENTRIES.watchlist]: './src/mobile.special.watchlist.scripts/mobile.special.watchlist.scripts.js'
},
// tests.mobilefrontend has additional dependencies but they're provided externally. This code
// can be removed if tests.mobilefrontend is removed.
externals: [ 'jquery', 'jsdom', 'oojs', 'qunit', 'fs', 'path' ],
resolve: {
alias: {
// This avoids leaking unnecessary code into the webpack test build
'./mockMediaWiki': path.resolve( __dirname, 'tests/node-qunit/utils/blank.js' )
}
},
module: {
rules: [ {
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
// Beware of https://github.com/babel/babel-loader/issues/690. Changes to browsers require
// manual invalidation.
cacheDirectory: true
}
}
} ]
},
optimization: {
// Don't produce production output when a build error occurs.
noEmitOnErrors: argv.mode === 'production',
// Use filenames instead of unstable numerical identifiers for file references. This
// increases the gzipped bundle size some but makes the build products easier to debug and
// appear deterministic. I.e., code changes will only alter the bundle they're packed in
// instead of shifting the identifiers in other bundles.
// https://webpack.js.org/guides/caching/#deterministic-hashes (namedModules replaces NamedModulesPlugin.)
namedModules: true,
// Generate a single Webpack bootstrap chunk for ResourceLoader modules to share. This will
// be packaged inside the mobile.startup module which should be a dependency for
// all modules. The inefficient alternative is for each module to bundle its own runtime.
// The terms bootloader and runtime are used interchangeably.
runtimeChunk: { name: 'mobile.startup.runtime' },
splitChunks: {
cacheGroups: {
// Turn off webpack's default 'default' cache group.
// https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks
default: false,
// Turn off webpack's default 'vendors' cache group. If this is desired
// later on, we can explicitly turn this on for clarity.
// https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks
vendors: false,
// TT210210: This was undesirably added when trying to get lazy loaded
// modules to work (e.g. ENTRIES.languages). It will excise modules
// shared between the chunks listed in the whitelist entry array into a
// new 'mobile.common' chunk. Ideally, the common chunk would be merged
// into the mobile.startup chunk and would not exist. However, there was
// difficulty in making webpack cleanly do this. When we overcome
// webpack lazy loading hurdles (or figure out a way to make webpack use
// mobile.startup as the common chunk), we won't be required to do this
// for lazy loaded chunks although it might still be valuable for
// special page chunks.
common: {
name: 'mobile.common',
// Minimum num of chunks module must share before excising into common
// chunk
minChunks: 2,
// Do no reuse existing chunks when splitting (i.e. we do not want
// webpack excising startup modules into an async chunk)
// https://github.com/webpack/webpack.js.org/issues/2122#issuecomment-388609306
reuseExistingChunk: false,
// ignore webpack's default minSize option (and other splitChunks
// defaults) and always create chunks based on criteria specified for
// this cacheGroup
enforce: true,
// Only consider splitting chunks off of these whitelisted entry names
// eslint-disable-next-line no-restricted-syntax
chunks: ( chunk ) => [
ENTRIES.startup,
ENTRIES.categories,
ENTRIES.editor,
ENTRIES.editorVe,
ENTRIES.languages,
ENTRIES.mediaViewer,
ENTRIES.talk,
ENTRIES.mobileInit,
ENTRIES.mobileDiff,
ENTRIES.mobileOptions,
ENTRIES.nearby,
ENTRIES.userLogin,
ENTRIES.watchlist
].includes( chunk.name )
}
}
}
},
output: {
// Specify the destination of all build products.
path: distDir,
// Store outputs per module in files named after the modules. For the JavaScript entry
// itself, append .js to each ResourceLoader module entry name. This value is tightly
// coupled to sourceMapFilename.
filename: '[name].js',
// Rename source map extensions. Per T173491 files with a .map extension cannot be served
// from prod.
sourceMapFilename: `[file]${srcMapExt}`,
// Expose the module.exports of each module entry chunk through the global
// mfModules[name].
// This is useful for debugging. E.g., mfModules['mobile.startup'] is set by the
// module.exports of mobile.startup.js.
library: [ 'mfModules', '[name]' ],
libraryTarget: 'this'
},
// Accurate source maps at the expense of build time. The source map is intentionally exposed
// to users via sourceMapFilename for prod debugging. This goes against convention as source
// code is publicly distributed.
devtool: 'source-map',
plugins: [
// Delete the output directory on each build.
new CleanPlugin( distDir, { verbose: false } )
],
performance: {
// Size violations for prod builds fail; development builds are unchecked.
hints: argv.mode === 'production' ? 'error' : false,
// Minified uncompressed size limits for chunks / assets and entrypoints. Keep these numbers
// up-to-date and rounded to the nearest 10th of a kilobyte so that code sizing costs are
// well understood. Related to bundlesize minified, gzipped compressed file size tests.
// Note: entrypoint size implicitly includes the mobile.startup.runtime and mobile.common
// chunks.
maxAssetSize: 60.5 * 1024,
maxEntrypointSize: 89.9 * 1024,
// The default filter excludes map files but we rename ours. Also, any modules prefixed with
// "tests." are excluded from performance checks as they are not shipped to end users.
// eslint-disable-next-line no-restricted-properties
assetFilter: ( filename ) => !filename.startsWith( 'tests.' ) &&
// eslint-disable-next-line no-restricted-properties
!filename.endsWith( srcMapExt )
}
} );