1
1
1
1
xxxxxxxxxx
1
/* Copy dependencies into asset/vendor. */
2
const CopyWebpackPlugin = require('copy-webpack-plugin');
3
4
/* Output slightly cleaner easier to read errors. */
5
const FriendlyErrorsWebpackPlugin = require('@soda/friendly-errors-webpack-plugin');
6
7
/* Globbing images and sass. */
8
const glob = require('glob');
9
10
/* Removes the unwanted entry point js files that get created with the css files. */
11
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
12
13
/* A plugin that can use multiple tools to optimize/generate images. */
14
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
15
16
/* Extracts the css and saves it as a seperate file. */
17
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
18
19
/* Resolves the path of the output files. */
20
const path = require('path');
21
22
/* Purge the compiled css of unused css not found in the php files. */
23
const { PurgeCSSPlugin } = require('purgecss-webpack-plugin');
24
25
/* Cleanup the images.js and images.js.map file not cleaned by webpack-remove-empty-scripts. */
26
const RemovePlugin = require('remove-files-webpack-plugin');
27
28
/* Creates a manifest file of all assets, mapping the latest build hash filename. */
29
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
30
31
32
33
34
35
36
/* ************************************************************************************************/
37
/* Begin configuring the paths and entry points */
38
39
const srcPath= './assets/src';
40
const outputPath = './assets/build';
41
const entryPoints = {
42
43
// JS Files
44
'js/frontend-header': glob.sync( srcPath + '/js/frontend-header/*.js' ),
45
'js/frontend': glob.sync( srcPath + '/js/frontend/*.js' ),
46
'js/backend': glob.sync( srcPath + '/js/backend/*.js' ),
47
48
// Styles
49
'styles/my-style': srcPath + '/styles/my-style.scss',
50
'styles/my-wp-admin-style': srcPath + '/styles/my-wp-admin-style.scss',
51
52
// Images
53
'images': glob.sync( srcPath + '/images/**/*.*' ),
54
};
55
56
/* css_modules is a glob of individual SCSS files that can be enqueued individually. */
57
let css_modules = glob.sync( srcPath + '/styles/custom/modules/*');
58
59
for ( let i = 0; i < css_modules.length; i++ ) {
60
entryPoints['styles/modules/' + path.parse(css_modules[i]).name] = css_modules[i];
61
}
62
63
/* frontend_modules is a glob of individual js files that can be enqueued individually in the frontend footer when needed */
64
let frontend_modules = glob.sync( srcPath + '/js/frontend/modules/*.js' );
65
66
for ( let i = 0; i < frontend_modules.length; i++ ) {
67
entryPoints['js/frontend-modules/' + path.parse(frontend_modules[i]).name] = frontend_modules[i];
68
}
69
70
/* frontend_header_modules is a glob of individual js files that can be enqueued individually in the frontend header when needed */
71
let frontend_header_modules = glob.sync( srcPath + '/js/frontend-header/modules/*.js' );
72
73
for ( let i = 0; i < frontend_header_modules.length; i++ ) {
74
entryPoints['js/frontend-header-modules/' + path.parse(frontend_header_modules[i]).name] = frontend_header_modules[i];
75
}
76
77
/* backend_modules is a glob of individual js files that can be enqueued individually in the backend footer when needed */
78
let backend_modules = glob.sync( srcPath + '/js/backend/modules/*.js' );
79
80
for ( let i = 0; i < backend_modules.length; i++ ) {
81
entryPoints['js/backend-modules/' + path.parse(backend_modules[i]).name] = backend_modules[i];
82
}
83
84
/* End configuring the paths and entry points */
85
/* ************************************************************************************************/
86
87
88
89
90
91
92
93
module.exports = {
94
devtool: 'source-map',
95
mode: 'production',
96
entry: entryPoints,
97
resolve: {
98
preferRelative: true,
99
},
100
cache: {
101
type: 'filesystem',
102
},
103
output: {
104
path: path.resolve(__dirname, outputPath),
105
filename: '[name].[contenthash:8].js', // Contenthash only changes, if the src file changes.
106
publicPath: '/wp-content/themes/my-custom-theme/assets/build/',
107
clean: true,
108
},
109
optimization: {
110
minimize: true,
111
usedExports: true,
112
},
113
plugins: [
114
new FriendlyErrorsWebpackPlugin(),
115
new RemoveEmptyScriptsPlugin(),
116
new RemovePlugin({
117
after: {
118
root: outputPath,
119
test: [{
120
folder: './',
121
/**
122
* Remove the unnecessary assets/build/images.js and assets/build/images.js.map
123
* files not removed by webpack-remove-empty-scripts.
124
*/
125
method: ( absoluteItemPath ) => {
126
return new RegExp(/.*\.js(\.map)?$/, 'm').test(absoluteItemPath);
127
},
128
}],
129
trash: true
130
}
131
}),
132
new MiniCssExtractPlugin({
133
filename: '[name].[contenthash:8].css', // Contenthash should only change, if the src file changes.
134
}),
135
new PurgeCSSPlugin({
136
paths: () => { // Content to be check for usage of CSS.
137
138
/**
139
* Note: this includes the build dir since only the built JS will include all of the
140
* needed classes. The src JS only includes imports and our custom JS.
141
*/
142
const files = glob.sync(path.join(__dirname, '/**/*.{html,js,jsx,php,svg}'), {
143
nodir: true,
144
ignore: [
145
'**/node_modules/**',
146
'**/assets/src/styles/**',
147
'**/webpack.config.js',
148
],
149
});
150
151
return files;
152
},
153
safelist: [
154
'align-bottom',
155
'border-top-0',
156
'btn-block',
157
'col-lg-1',
158
'custom-control',
159
'custom-switch',
160
'custom-control-input',
161
'custom-control-label',
162
'form-control',
163
'form-group',
164
'form-row',
165
'table-borderless',
166
'flex-column',
167
'flex-md-row',
168
'me-auto',
169
'mt-auto',
170
'mw-100',
171
'badge',
172
'bg-primary',
173
'bg-info',
174
'bg-warning',
175
'bg-danger',
176
'custom-control',
177
'custom-file',
178
'custom-file-input',
179
'custom-file-label',
180
'custom-radio',
181
'form-check',
182
'form-check-input',
183
'form-check-label',
184
'form-select',
185
'input-group',
186
'input-group-prepend',
187
'input-group-text',
188
'list-group-horizontal-sm',
189
'rounded-pill',
190
191
// START Bootstrap Alerts used globally
192
'alert',
193
'alert-primary',
194
'alert-secondary',
195
'alert-accent',
196
'alert-success',
197
'alert-danger',
198
'alert-warning',
199
'alert-info',
200
'alert-light',
201
'alert-dark',
202
// END Bootstrap Alerts
203
],
204
}),
205
new CopyWebpackPlugin({
206
patterns: [
207
// irrelevant... copies/moves some PHP files developed internally.
208
]
209
}),
210
new WebpackManifestPlugin({
211
sort: (fileA, fileB) => { // Order by keys alphabetically.
212
if (fileA.name < fileB.name) {
213
return -1;
214
}
215
216
if (fileA.name > fileB.name) {
217
return 1;
218
}
219
220
return 0;
221
},
222
filter: (file) => {
223
224
// Do not include vendor/ or block/_vendor/ directories.
225
var putInManifest = !new RegExp(/(\/|\\)(blocks\/_)?vendor(\/|\\)/).test(file.path);
226
227
return putInManifest;
228
},
229
}),
230
],
231
module: {
232
rules: [
233
{
234
test: /\.(gif|png|jpe?g|svg|ico)$/i,
235
type: 'asset/resource',
236
generator: {
237
filename: 'images/[name].[contenthash:8][ext]',
238
},
239
use: [
240
{
241
loader: 'image-webpack-loader',
242
options: {
243
disable: false,
244
mozjpeg: {
245
progressive: true,
246
},
247
/**
248
* @todo Disabled because optimizing png's caused different hashes for
249
* png's on different OS's. Skipping this for now, look into it later
250
* after webpack is in place.
251
*/
252
optipng: {
253
enabled: true,
254
},
255
/**
256
* @todo Disabled because optimizing png's caused different hashes for
257
* png's on different OS's. Skipping this for now, look into it later
258
* after webpack is in place.
259
*/
260
pngquant: {
261
enabled: true,
262
},
263
gifsicle: {
264
interlaced: false,
265
},
266
},
267
},
268
],
269
},
270
{
271
test: /\.(js|jsx)$/,
272
use: [
273
{
274
loader: 'babel-loader',
275
options: {
276
presets: [
277
[ '@babel/preset-env', { targets: 'defaults' } ]
278
]
279
},
280
},
281
],
282
},
283
{
284
test: /\.s[ac]ss$/i,
285
use: [
286
MiniCssExtractPlugin.loader,
287
{
288
loader: 'css-loader',
289
options: {
290
sourceMap: true,
291
url: {
292
filter: (url) => {
293
/**
294
* resourcePath is the path to css file. This filter prevents
295
* prevent css-loader from trying to resolve a URL and throwing
296
* a build error. Return false to not resolve the URL.
297
*/
298
299
// Don't handle URLs that are outside of the project.
300
if (/^https?:\/\//.test(url)) {
301
return false;
302
}
303
304
return true;
305
},
306
},
307
}
308
},
309
{
310
loader: 'postcss-loader',
311
options: {
312
sourceMap: true,
313
postcssOptions: {
314
plugins: [
315
require('autoprefixer'),
316
],
317
},
318
}
319
},
320
{
321
loader: 'sass-loader',
322
options: {
323
sourceMap: true,
324
sassOptions: {
325
outputStyle: 'expanded', // Not sure if this is needed.
326
},
327
},
328
},
329
'import-glob-loader',
330
],
331
},
332
],
333
},
334
};
335
Console errors: 0