@ideasonpurpose/build-tools-wordpress 1.1.14 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ #### v1.2.0
8
+
9
+ > 19 September 2024
10
+
11
+ - deprecation free build! closes #5
12
+ - bump deps, remove some for deprecations. Ref #5
13
+ - bump deps, update examples and testing
14
+ - export webpackConfig
15
+
16
+ #### v1.1.14
17
+
18
+ > 6 May 2024
19
+
20
+ - minor bumps
21
+
7
22
  #### v1.1.13
8
23
 
9
24
  > 10 April 2024
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ideasonpurpose/build-tools-wordpress
2
2
 
3
- #### Version 1.1.14
3
+ #### Version 1.2.1
4
4
 
5
5
  [![NPM Version](https://img.shields.io/npm/v/%40ideasonpurpose%2Fbuild-tools-wordpress?logo=npm)](https://www.npmjs.com/package/@ideasonpurpose/build-tools-wordpress)
6
6
  [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ideasonpurpose/build-tools-wordpress/npm-publish.yml?logo=github&logoColor=white)](https://github.com/ideasonpurpose/build-tools-wordpress#readme)
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Future node versions should import package.json directly:
3
+ * import packageJson from "./package.json" with { type: "json" };
4
+ *
5
+ * In the current node.js LTS v20, importing JSON throws the following warnings:
6
+ * (node:14509) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
7
+ * (Use `node --trace-warnings ...` to show where the warning was created)
8
+ *
9
+ * NOTE: This file does not work if the build runs from Docker. Inside a Docker volume, the
10
+ * theme name pulls instead from the docker image's package.json file, which will probably
11
+ * create a theme named 'iop-build-tools'.
12
+ */
13
+ import { readFileSync } from "fs";
14
+ const packageJson = JSON.parse(readFileSync("./package.json"));
15
+ // import packageJson from "./package.json" with { type: "json" };
16
+
17
+ const { name: themeName } = packageJson;
18
+
19
+ export default {
20
+ src: `./wp-content/themes/${themeName}/src`,
21
+ dist: `./wp-content/themes/${themeName}/dist`,
22
+ entry: ["./js/main.js", "./js/admin.js", "./js/editor.js"],
23
+ publicPath: `/wp-content/themes/${themeName}/dist/`,
24
+
25
+ sass: "sass-embedded",
26
+ esTarget: "es2020",
27
+
28
+ devtool: "source-map",
29
+ };
@@ -0,0 +1,618 @@
1
+ import { posix as path } from "path";
2
+
3
+ import { statSync } from "fs";
4
+ import { cosmiconfig, cosmiconfigSync } from "cosmiconfig";
5
+ import chalk from "chalk";
6
+
7
+ import MiniCssExtractPlugin from "mini-css-extract-plugin";
8
+ import { EsbuildPlugin } from "esbuild-loader";
9
+
10
+ import CopyPlugin from "copy-webpack-plugin";
11
+ import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
12
+ import ImageMinimizerPlugin from "image-minimizer-webpack-plugin";
13
+
14
+ import {
15
+ AfterDoneReporterPlugin,
16
+ buildConfig,
17
+ DependencyManifestPlugin,
18
+ devserverProxy,
19
+ findLocalPort,
20
+ WatchRunReporterPlugin,
21
+ } from "../index.js";
22
+
23
+ import autoprefixer from "autoprefixer";
24
+ import cssnano from "cssnano";
25
+
26
+ /**
27
+ * Sass flavors for conditional sass-loader implementations
28
+ */
29
+ // import * as nodeSass from "sass";
30
+ // import * as dartSass from "sass-embedded";
31
+
32
+ // Experimenting with this
33
+ import DependencyExtractionWebpackPlugin from "@wordpress/dependency-extraction-webpack-plugin";
34
+
35
+ /**
36
+ * Force `mode: production` when running the analyzer
37
+ * TODO: webpack5 changed env in here, might need to change WEBPACK_BUNDLE_ANALYZER
38
+ */
39
+ if (process.env.WEBPACK_BUNDLE_ANALYZER) process.env.NODE_ENV = "production";
40
+
41
+ const isProduction = process.env.NODE_ENV === "production";
42
+
43
+ const stats = {
44
+ preset: "normal",
45
+ cachedAssets: false,
46
+ assets: true,
47
+ // assetsSpace: 12,
48
+ // context: new URL( import.meta.url).pathname,
49
+ // all: true,
50
+ // assets: true,
51
+ // builtAt: true,
52
+ // cachedModules: true,
53
+ // children: false, // Adds a bunch of blank lines to stats output
54
+ // chunkGroups: false,
55
+ // chunkModules: false,
56
+ // chunkOrigins: true,
57
+ // chunkRelations: true,
58
+ // chunks: false,
59
+ // colors: true,
60
+ // depth: false,
61
+ // env: true,
62
+ hash: true,
63
+ // orphanModules: false,
64
+ // dependentModules: true,
65
+ modules: false,
66
+ groupAssetsByChunk: true,
67
+ entrypoints: true,
68
+ // // errorDetails: "auto",
69
+ children: false,
70
+ // errorDetails: true,
71
+ errors: true,
72
+ errorStack: true,
73
+ // excludeAssets: [/hot-update/, /_sock-/],
74
+ // groupAssetsByChunk: true,
75
+ // logging: "warn",
76
+ // optimizationBailout: true,
77
+ // loggingTrace: false,
78
+ performance: true,
79
+ reasons: false,
80
+ relatedAssets: false,
81
+ timings: true,
82
+ version: true,
83
+ warnings: true,
84
+ loggingDebug: ["sass-loader"],
85
+ };
86
+
87
+ export default async (env) => {
88
+ // const siteDir = new URL(import.meta.url).pathname;
89
+ const siteDir = process.cwd();
90
+ const explorer = cosmiconfig("ideasonpurpose", { searchStrategy: "project" });
91
+ const configFile = await explorer.search(siteDir);
92
+
93
+ const config = await buildConfig(configFile);
94
+
95
+ const proxy = isProduction ? {} : await devserverProxy(config);
96
+
97
+ setTimeout(() =>
98
+ console.log(
99
+ chalk.magenta.bold("sass implementation"),
100
+ chalk.yellow.bold(config.sass),
101
+ // config.sass === "sass" ? nodeSass : dartSass,
102
+ ),
103
+ );
104
+
105
+ /**
106
+ * `usePolling` is a placeholder, try and detect native Windows Docker mounts
107
+ * since they don't support file-watching (no inotify events), if there's
108
+ * something clean, use that instead. For now, this will force-enable polling.
109
+ *
110
+ * TODO: usePolling is likely no longer needed since webpack is running from node and
111
+ * not Docker
112
+ *
113
+ * TODO: Why so much dancing around defaults when this could just inherit from default.config?
114
+ */
115
+ const usePolling = Boolean(config.usePolling); // likely undefined, coerced to false
116
+ const pollInterval = Math.max(
117
+ parseInt(config.pollInterval, 10) || parseInt(config.usePolling, 10) || 400,
118
+ 400,
119
+ );
120
+
121
+ const devtool = config.devtool || false;
122
+
123
+ const sassLibs = ["sass-embedded", "sass", "node-sass"];
124
+ const sassImplementation = sassLibs.includes(config.sass)
125
+ ? { implementation: config.sass }
126
+ : {};
127
+
128
+ // console.log({
129
+ // metaurl: new URL("webpack/stats/index.html", import.meta.url),
130
+ // config,
131
+ // // devServerProxy: await devserverProxy(config),
132
+ // });
133
+
134
+ return {
135
+ // stats: "errors-warnings",
136
+ stats,
137
+ module: {
138
+ rules: [
139
+ // {
140
+ // test: /\.(js|jsx|mjs)$/,
141
+ // include: [
142
+ // path.resolve(config.src),
143
+ // path.resolve("../tools/node_modules"),
144
+ // path.resolve("../site/node_modules"),
145
+ // ],
146
+ // exclude: function (module) {
147
+ // const moduleRegex = new RegExp(
148
+ // `node_modules/(${config.transpileDependencies.join("|")})`
149
+ // );
150
+ // return /node_modules/.test(module) && !moduleRegex.test(module);
151
+ // },
152
+
153
+ // /**
154
+ // * EXPERIMENTAL!!
155
+ // * If JS compilation breaks, try reverting this first.
156
+ // */
157
+ // loader: "esbuild-loader",
158
+ // options: {
159
+ // loader: "jsx",
160
+ // target: "es2015",
161
+ // },
162
+
163
+ /**
164
+ * Updated 2022-09, simpler
165
+ */
166
+ {
167
+ test: /\.[jt]sx?$/,
168
+ // test: /\.js$/,
169
+ loader: "esbuild-loader",
170
+ options: {
171
+ loader: "jsx",
172
+ target: config.esTarget,
173
+ },
174
+ },
175
+ // {
176
+ // test: /\.tsx?$/,
177
+ // loader: "esbuild-loader",
178
+ // options: {
179
+ // loader: "tsx",
180
+ // target: config.esTarget,
181
+ // },
182
+ // },
183
+
184
+ // use: {
185
+ // loader: "babel-loader",
186
+ // options: {
187
+ // cacheDirectory: !isProduction,
188
+ // sourceType: "unambiguous",
189
+ // plugins: [
190
+ // "@babel/plugin-syntax-dynamic-import",
191
+ // ...(isProduction
192
+ // ? []
193
+ // : ["@babel/plugin-transform-react-jsx-source"]),
194
+ // ],
195
+ // presets: [
196
+ // [
197
+ // "@babel/preset-env",
198
+ // {
199
+ // forceAllTransforms: true,
200
+ // useBuiltIns: "usage",
201
+ // configPath: config.src,
202
+ // corejs: 3,
203
+ // modules: false,
204
+ // debug: false,
205
+ // },
206
+ // ],
207
+ // "@babel/preset-react",
208
+ // ],
209
+ // },
210
+ // },
211
+ // },
212
+ {
213
+ test: /\.(scss|css)$/,
214
+ use: [
215
+ { loader: MiniCssExtractPlugin.loader },
216
+ {
217
+ loader: "css-loader",
218
+ options: {
219
+ import: false, // imports already handled by Sass or PostCSS
220
+ // sourceMap: !isProduction,
221
+ },
222
+ },
223
+ {
224
+ loader: "postcss-loader",
225
+ options: {
226
+ postcssOptions: {
227
+ plugins: isProduction
228
+ ? [
229
+ autoprefixer,
230
+ cssnano({ preset: ["default", { colormin: false }] }),
231
+ ]
232
+ : [autoprefixer],
233
+ },
234
+ },
235
+ },
236
+ // {
237
+ // loader: "resolve-url-loader",
238
+ // options: {
239
+ // // sourceMap: true,
240
+ // // debug: true,
241
+ // },
242
+ // },
243
+ {
244
+ loader: "sass-loader",
245
+ options: {
246
+ ...sassImplementation,
247
+ // implementation: config.sass === "sass" ? nodeSass : dartSass,
248
+ // implementation: nodeSass,
249
+ // implementation: await import(config.sass),
250
+ sourceMap: !isProduction,
251
+ warnRuleAsWarning: true,
252
+ webpackImporter: false,
253
+ sassOptions: {
254
+ // api: "modern",
255
+
256
+ includePaths: [
257
+ path.resolve(config.src, "sass"),
258
+ path.resolve(config.src),
259
+ // path.resolve("../site/node_modules"),
260
+ path.resolve("node_modules"),
261
+ ],
262
+ style: "expanded",
263
+ verbose: true,
264
+ },
265
+ },
266
+ },
267
+ ],
268
+ },
269
+ /**
270
+ * This image loader is specifically for images which are required or
271
+ * imported into a webpack processed entry file. Optimization is
272
+ * handled by image-minimizer-webpack-plugin. These assets will be
273
+ * renamed with a chunkhash fragment.
274
+ *
275
+ * All images under `config.src` will be optimized and copied by
276
+ * copy-webpack-plugin but will keep their original filenames and
277
+ * relative paths. Images included in SCSS files will be processed
278
+ * twice, once with a hashed name and again with its original name.
279
+ */
280
+ {
281
+ test: /\.(jpe?g|png|gif|tif|webp|avif)$/i,
282
+ type: "asset",
283
+ },
284
+
285
+ /**
286
+ * SVGs can be imported as asset urls or React components
287
+ *
288
+ * To import an SVG file as a src url, append ?url to the filename:
289
+ * import svg from './assets/file.svg?url'
290
+ *
291
+ * To import an SVG file as a React component
292
+
293
+ * @link https://react-svgr.com/docs/webpack/#use-svgr-and-asset-svg-in-the-same-project
294
+ */
295
+ {
296
+ test: /\.svg$/i,
297
+ type: "asset",
298
+ resourceQuery: /url/, // *.svg?url
299
+ },
300
+ {
301
+ test: /\.svg$/i,
302
+ issuer: /\.[jt]sx?$/,
303
+ resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url
304
+ use: ["@svgr/webpack"],
305
+ },
306
+ {
307
+ test: /fonts\/.*\.(ttf|eot|woff2?)$/i,
308
+ type: "asset",
309
+ },
310
+ ],
311
+ },
312
+
313
+ context: path.resolve(config.src),
314
+
315
+ resolve: {
316
+ modules: [
317
+ path.resolve("../tools/node_modules"),
318
+ path.resolve("../site/node_modules"),
319
+ path.resolve("./node_modules"),
320
+ ],
321
+ },
322
+
323
+ resolveLoader: {
324
+ modules: [
325
+ path.resolve("../tools/node_modules"),
326
+ path.resolve("./node_modules"), // for local development when running outside of Docker
327
+ ],
328
+ },
329
+
330
+ entry: config.entry,
331
+
332
+ output: {
333
+ path: new URL(config.dist, import.meta.url).pathname,
334
+ /**
335
+ * Primary output filenames SHOULD NOT include hashes in development because
336
+ * some files are written to disk from devServer middleware. Because those
337
+ * files are written outside standard webpack output, they aren't cleaned
338
+ * up by standard webpack cleaning functions.
339
+ */
340
+ filename: isProduction ? "[name]-[contenthash:8].js" : "[name].js",
341
+ chunkFilename: "[id]-[chunkhash:8].js",
342
+ publicPath: config.publicPath,
343
+ /**
344
+ * Assets are not cleaned when writeToDisk is true in devServer
345
+ * Works correctly with builds.
346
+ * @link https://github.com/webpack/webpack-dev-middleware/issues/861
347
+ */
348
+ clean: true,
349
+ },
350
+
351
+ devServer: {
352
+ host: "0.0.0.0",
353
+ allowedHosts: "all",
354
+ port: "auto",
355
+ // hot: true, // TODO: What does 'only' do? https://webpack.js.org/configuration/dev-server/#devserverhot
356
+ hot: "only", // TODO: What does 'only' do? https://webpack.js.org/configuration/dev-server/#devserverhot
357
+ client: {
358
+ logging: "info",
359
+ overlay: {
360
+ errors: true,
361
+ warnings: true,
362
+ runtimeErrors: (error) => {
363
+ // specific errors can be inspected and handled individually
364
+ // https://webpack.js.org/configuration/dev-server/#overlay
365
+ // console.log("webpack devServer runtimeError:", error);
366
+ return false;
367
+ },
368
+ },
369
+ reconnect: 30,
370
+ // webSocketURL: {
371
+ // port: parseInt(process.env.PORT), // external port, so websockets hit the right endpoint
372
+ // },
373
+ },
374
+ // webSocketServer: "ws",
375
+ // static: {
376
+ // // TODO: Should contentBase be `false` when there's a proxy?
377
+ // directory: path.join("/usr/src/site/", config.contentBase),
378
+ // /*
379
+ // * TODO: Poll options were enabled as a workaround for Docker-win volume inotify
380
+ // * issues. Looking to make this conditional...
381
+ // * Maybe defined `isWindows` or `hasiNotify` for assigning a value
382
+ // * Placeholder defined at the top of the file.
383
+ // * For now, `usePolling` is a boolean (set to true)
384
+ // * ref: https://github.com/docker/for-win/issues/56
385
+ // * https://www.npmjs.com/package/is-windows
386
+ // * TODO: Safe to remove?
387
+ // * TODO: Test on vanilla Windows (should now work in WSL)
388
+ // */
389
+
390
+ // watch: {
391
+ // poll: usePolling && pollInterval,
392
+ // ignored: ["node_modules", "vendor"],
393
+ // },
394
+ // },
395
+
396
+ devMiddleware: {
397
+ index: false, // enable root proxying
398
+
399
+ writeToDisk: (filePath) => {
400
+ /**
401
+ * Note: If this is an async function, it will write everything to disk
402
+ *
403
+ * Never write hot-update files to disk.
404
+ */
405
+ // vendors-node_modules_mini-css-extract-plugin_dist_hmr_hotModuleReplacement_js-node_modules_we-780fe4.js.map
406
+ if (/.+(hot-update)\.(js|json|js\.map)$/.test(filePath)) {
407
+ return false;
408
+ }
409
+ // SHORT_CIRCUIT FOR TESTING
410
+ return true;
411
+
412
+ if (/.+\.(svg|json|php|jpg|png)$/.test(filePath)) {
413
+ const fileStat = statSync(filePath, { throwIfNoEntry: false });
414
+
415
+ /**
416
+ * Always write SVG, PHP & JSON files
417
+ */
418
+ if (/.+\.(svg|json|php)$/.test(filePath)) {
419
+ return true;
420
+ } else {
421
+ /**
422
+ * Write any images under 100k and anything not yet on disk
423
+ */
424
+ if (!fileStat || fileStat.size < 100 * 1024) {
425
+ return true;
426
+ }
427
+ /**
428
+ * TODO: This might all be unnecessary. Webpack seems to be doing a good job with its native caching
429
+ */
430
+ // const randOffset = Math.random() * 300000; // 0-5 minutes
431
+ // const expired = new Date() - fileStat.mtime > randOffset;
432
+ // const relPath = filePath.replace(config.dist, "dist");
433
+ // if (expired) {
434
+ // console.log("DEBUG writeToDisk:", { replacing: relPath });
435
+ // return true;
436
+ // }
437
+ // console.log("DEBUG writeToDisk:", { cached: relPath });
438
+ }
439
+ }
440
+ return false;
441
+ },
442
+ // stats,
443
+ // stats: 'verbose',
444
+ },
445
+
446
+ // NOTE: trying to make injection conditional so wp-admin stops reloading
447
+ // injectClient: compilerConfig => {
448
+ // console.log(compilerConfig);
449
+ // return true;
450
+ // },
451
+
452
+ onListening: (devServer) => {
453
+ const port = devServer.server.address().port;
454
+ devServer.compiler.options.devServer.port =
455
+ devServer.server.address().port;
456
+ devServer.compiler._devServer = devServer;
457
+
458
+ console.log("Listening on port:", port);
459
+ },
460
+
461
+ setupMiddlewares: (middlewares, devServer) => {
462
+ // devServer.compiler.options.devServer.port = devServer.options.port;
463
+ // devServer.compiler._devServer = devServer;
464
+ // if (!devServer) {
465
+ // throw new Error("webpack-dev-server is not defined");
466
+ // }
467
+
468
+ // devServer.internalIP('v4').then(ip => console.log('!!!!!!', ip));
469
+
470
+ /**
471
+ * The `/inform` route is an annoying bit of code. Here's why:
472
+ * Ubiquity Wi-fi hardware frequently spams the shit out of their
473
+ * networks, specifically requesting the `/inform` route from
474
+ * every device. We still have some Ubiquity hardware on our
475
+ * networks, so dev servers were constantly responding to
476
+ * `/inform` requests with 404s, filling logs and cluttering
477
+ * terminals. So that's why this is here. I hate it.
478
+ */
479
+ devServer.app.all("/inform", () => false);
480
+
481
+ /**
482
+ * The "/webpack/reload" endpoint will trigger a full devServer refresh
483
+ * Originally from our Browsersync implementation:
484
+ *
485
+ * https://github.com/ideasonpurpose/wp-theme-init/blob/ad8039c9757ffc3a0a0ed0adcc616a013fdc8604/src/ThemeInit.php#L202
486
+ */
487
+ devServer.app.get("/webpack/reload", (req, res) => {
488
+ console.log(
489
+ chalk.yellow("Reload triggered by request to /webpack/reload"),
490
+ );
491
+
492
+ devServer.sendMessage(
493
+ devServer.webSocketServer.clients,
494
+ "content-changed",
495
+ );
496
+ res.json({ status: "Reloading!" });
497
+ });
498
+
499
+ return middlewares;
500
+ },
501
+
502
+ watchFiles: {
503
+ paths: [
504
+ path.resolve(config.src, "../**/*.{php,html,svg,json}"), // WordPress
505
+ ],
506
+ options: {
507
+ ignored: [
508
+ "**/.git/**",
509
+ "**/vendor/**",
510
+ "**/node_modules/**",
511
+ "**/dist/**",
512
+ ],
513
+ ignoreInitial: true,
514
+ ignorePermissionErrors: true,
515
+ /**
516
+ * TODO: Can polling be removed everywhere?
517
+ * @link https://github.com/docker/for-win/issues/56#issuecomment-576749639
518
+ */
519
+ usePolling,
520
+ interval: pollInterval,
521
+ },
522
+ },
523
+
524
+ ...proxy,
525
+ },
526
+
527
+ mode: isProduction ? "production" : "development",
528
+
529
+ performance: {
530
+ hints: isProduction ? "warning" : false,
531
+ },
532
+
533
+ devtool,
534
+
535
+ plugins: [
536
+ new MiniCssExtractPlugin({
537
+ filename: isProduction ? "[name]-[contenthash:8].css" : "[name].css",
538
+ }),
539
+
540
+ new CopyPlugin({
541
+ patterns: [
542
+ {
543
+ from: "**/*",
544
+ globOptions: {
545
+ dot: true, // TODO: Dangerous? Why is this ever necessary?!
546
+ ignore: [
547
+ "**/{.gitignore,.DS_Store,*:Zone.Identifier}",
548
+ config.src + "/{block,blocks,fonts,js,sass}/**",
549
+ ],
550
+ },
551
+ noErrorOnMissing: true,
552
+ },
553
+ {
554
+ from: config.src + "/{block,blocks}/**/block.json",
555
+ noErrorOnMissing: true,
556
+ }, // re-add block.json files for loading from PHP
557
+ ],
558
+ options: { concurrency: 50 },
559
+ }),
560
+
561
+ /**
562
+ * @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dependency-extraction-webpack-plugin/
563
+ */
564
+ new DependencyExtractionWebpackPlugin(),
565
+
566
+ new DependencyManifestPlugin({
567
+ writeManifestFile: true,
568
+ manifestFile: config.manifestFile,
569
+ }),
570
+
571
+ new WatchRunReporterPlugin(),
572
+
573
+ new AfterDoneReporterPlugin({
574
+ echo: env && env.WEBPACK_SERVE,
575
+ // message:
576
+ // "Dev site " + chalk.blue.bold(`http://localhost:${process.env.PORT}`),
577
+ }),
578
+
579
+ new BundleAnalyzerPlugin({
580
+ analyzerMode: isProduction ? "static" : "disabled",
581
+ openAnalyzer: false,
582
+ reportFilename: new URL("webpack/stats/index.html", import.meta.url)
583
+ .pathname,
584
+ }),
585
+ ],
586
+ optimization: {
587
+ splitChunks: {
588
+ chunks: "all",
589
+ },
590
+ minimizer: [
591
+ new EsbuildPlugin({
592
+ target: config.esTarget,
593
+ css: true,
594
+ }),
595
+ new ImageMinimizerPlugin({
596
+ severityError: "error",
597
+
598
+ minimizer: {
599
+ implementation: ImageMinimizerPlugin.sharpMinify,
600
+
601
+ options: {
602
+ /**
603
+ * Sharp options
604
+ */
605
+ encodeOptions: {
606
+ jpeg: {
607
+ quality: 70,
608
+ mozjpeg: true,
609
+ },
610
+ png: {},
611
+ },
612
+ },
613
+ },
614
+ }),
615
+ ],
616
+ },
617
+ };
618
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Future node versions should import package.json directly:
3
+ * import packageJson from "./package.json" with { type: "json" };
4
+ *
5
+ * In the current node.js LTS v20, importing JSON throws the following warnings:
6
+ * (node:14509) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
7
+ * (Use `node --trace-warnings ...` to show where the warning was created)
8
+ *
9
+ * NOTE: This file does not work if the build runs from Docker. Inside a Docker volume, the
10
+ * theme name pulls instead from the docker image's package.json file, which will probably
11
+ * create a theme named 'iop-build-tools'.
12
+ */
13
+ import { readFileSync } from "fs";
14
+ const packageJson = JSON.parse(readFileSync("./package.json"));
15
+ // import packageJson from "./package.json" with { type: "json" };
16
+
17
+ const { name: themeName } = packageJson;
18
+
19
+ export default {
20
+ src: `./wp-content/themes/${themeName}/src`,
21
+ dist: `./wp-content/themes/${themeName}/dist`,
22
+ entry: ["./js/main.js", "./js/admin.js", "./js/editor.js"],
23
+ publicPath: `/wp-content/themes/${themeName}/dist/`,
24
+
25
+ sass: "sass-embedded",
26
+ esTarget: "es2020",
27
+
28
+ devtool: "source-map",
29
+ };
@@ -26,8 +26,8 @@ import cssnano from "cssnano";
26
26
  /**
27
27
  * Sass flavors for conditional sass-loader implementations
28
28
  */
29
- import * as nodeSass from "sass";
30
- import * as dartSass from "sass-embedded";
29
+ // import * as nodeSass from "sass";
30
+ // import * as dartSass from "sass-embedded";
31
31
 
32
32
  // Experimenting with this
33
33
  import DependencyExtractionWebpackPlugin from "@wordpress/dependency-extraction-webpack-plugin";
@@ -84,23 +84,19 @@ const stats = {
84
84
  };
85
85
 
86
86
  export default async (env) => {
87
- // const siteDir = new URL("../site", import.meta.url).pathname;
88
87
  const siteDir = new URL(import.meta.url).pathname;
89
- // const explorerSync = cosmiconfigSync("ideasonpurpose");
90
- const explorer = cosmiconfig("ideasonpurpose");
91
- // const configFile = explorerSync.search(siteDir);
88
+ const explorer = cosmiconfig("ideasonpurpose", { searchStrategy: "project" });
92
89
  const configFile = await explorer.search(siteDir);
93
90
 
94
- // import buildConfig from "./lib/buildConfig.js";
95
- // console.log({ siteDir, configFile, nm: path.resolve("node_modules") });
96
-
97
91
  const config = await buildConfig(configFile);
98
92
 
93
+ const proxy = isProduction ? {} : await devserverProxy(config);
94
+
99
95
  setTimeout(() =>
100
96
  console.log(
101
97
  chalk.magenta.bold("sass implementation"),
102
98
  chalk.yellow.bold(config.sass),
103
- config.sass === "sass" ? nodeSass : dartSass,
99
+ // config.sass === "sass" ? nodeSass : dartSass,
104
100
  ),
105
101
  );
106
102
 
@@ -111,7 +107,7 @@ export default async (env) => {
111
107
  *
112
108
  * TODO: Why so much dancing around defaults when this could just inherit from default.config?
113
109
  */
114
- const usePolling = Boolean(config.usePolling);
110
+ const usePolling = Boolean(config.usePolling); // likely undefined, coerced to false
115
111
  const pollInterval = Math.max(
116
112
  parseInt(config.pollInterval, 10) || parseInt(config.usePolling, 10) || 400,
117
113
  400,
@@ -119,7 +115,19 @@ export default async (env) => {
119
115
 
120
116
  const devtool = config.devtool || false;
121
117
 
122
- console.log({ config, devServerProxy: await devserverProxy(config) });
118
+ const sassLibs = ["sass-embedded", "sass", "node-sass"];
119
+ const sassImplementation = sassLibs.includes(config.sass)
120
+ ? { implementation: config.sass }
121
+ : {};
122
+
123
+ // TODO: Move this into buildConfig()
124
+ config.esTarget = config.esTarget || "es2020"; // was "es2015"
125
+
126
+ console.log({
127
+ metaurl: new URL("webpack/stats/index.html", import.meta.url),
128
+ config,
129
+ // devServerProxy: await devserverProxy(config),
130
+ });
123
131
 
124
132
  return {
125
133
  // stats: "errors-warnings",
@@ -154,21 +162,22 @@ export default async (env) => {
154
162
  * Updated 2022-09, simpler
155
163
  */
156
164
  {
157
- test: /\.m?jsx?$/,
165
+ test: /\.[jt]sx?$/,
166
+ // test: /\.js$/,
158
167
  loader: "esbuild-loader",
159
168
  options: {
160
169
  loader: "jsx",
161
- target: "es2015",
162
- },
163
- },
164
- {
165
- test: /\.tsx?$/,
166
- loader: "esbuild-loader",
167
- options: {
168
- loader: "tsx",
169
- target: "es2015",
170
+ target: config.esTarget,
170
171
  },
171
172
  },
173
+ // {
174
+ // test: /\.tsx?$/,
175
+ // loader: "esbuild-loader",
176
+ // options: {
177
+ // loader: "tsx",
178
+ // target: config.esTarget,
179
+ // },
180
+ // },
172
181
 
173
182
  // use: {
174
183
  // loader: "babel-loader",
@@ -232,7 +241,8 @@ export default async (env) => {
232
241
  {
233
242
  loader: "sass-loader",
234
243
  options: {
235
- implementation: config.sass === "sass" ? nodeSass : dartSass,
244
+ ...sassImplementation,
245
+ // implementation: config.sass === "sass" ? nodeSass : dartSass,
236
246
  // implementation: nodeSass,
237
247
  // implementation: await import(config.sass),
238
248
  sourceMap: !isProduction,
@@ -243,6 +253,7 @@ export default async (env) => {
243
253
 
244
254
  includePaths: [
245
255
  path.resolve(config.src, "sass"),
256
+ path.resolve(config.src),
246
257
  // path.resolve("../site/node_modules"),
247
258
  path.resolve("node_modules"),
248
259
  ],
@@ -338,17 +349,21 @@ export default async (env) => {
338
349
  devServer: {
339
350
  host: "0.0.0.0",
340
351
  allowedHosts: "all",
341
- // setupExitSignals: true,// default
342
-
343
- compress: config.devServerCompress || false, // TODO: True by default in devServer v4, exposed via config.devServerCompress to test speed impact
344
352
  port: "auto",
345
353
  // hot: true, // TODO: What does 'only' do? https://webpack.js.org/configuration/dev-server/#devserverhot
346
- hot: 'only', // TODO: What does 'only' do? https://webpack.js.org/configuration/dev-server/#devserverhot
354
+ hot: "only", // TODO: What does 'only' do? https://webpack.js.org/configuration/dev-server/#devserverhot
347
355
  client: {
348
- // logging: "error", // TODO: New, is this ok?
349
- logging: "info", // TODO: New, is this ok?
350
- overlay: true, // { warnings: true, errors: true },
351
- progress: true, // TODO: New, is this ok?
356
+ logging: "info",
357
+ overlay: {
358
+ errors: true,
359
+ warnings: true,
360
+ runtimeErrors: (error) => {
361
+ // specific errors can be inspected and handled individually
362
+ // https://webpack.js.org/configuration/dev-server/#overlay
363
+ // console.log("webpack devServer runtimeError:", error);
364
+ return false;
365
+ },
366
+ },
352
367
  reconnect: 30,
353
368
  // webSocketURL: {
354
369
  // port: parseInt(process.env.PORT), // external port, so websockets hit the right endpoint
@@ -482,11 +497,15 @@ export default async (env) => {
482
497
 
483
498
  watchFiles: {
484
499
  paths: [
485
- path.resolve(config.src, "../**/*.{php,json}"), // WordPress
486
- // path.resolve(config.src, `../${config.contentBase}/*.html`), // Jekyll
500
+ path.resolve(config.src, "../**/*.{php,html,svg,json}"), // WordPress
487
501
  ],
488
502
  options: {
489
- ignored: ["**/.git/**", "**/vendor/**", "**/node_modules/**", "**/dist/**"],
503
+ ignored: [
504
+ "**/.git/**",
505
+ "**/vendor/**",
506
+ "**/node_modules/**",
507
+ "**/dist/**",
508
+ ],
490
509
  ignoreInitial: true,
491
510
  ignorePermissionErrors: true,
492
511
  /**
@@ -498,7 +517,10 @@ export default async (env) => {
498
517
  },
499
518
  },
500
519
 
501
- ...(await devserverProxy(config)),
520
+ // ...(await devserverProxy(config)),
521
+ // TODO: Move
522
+ // ...(isProduction ? {} : await devserverProxy(config)),
523
+ ...proxy,
502
524
  },
503
525
 
504
526
  mode: isProduction ? "production" : "development",
@@ -522,10 +544,15 @@ export default async (env) => {
522
544
  dot: true, // TODO: Dangerous? Why is this ever necessary?!
523
545
  ignore: [
524
546
  "**/{.gitignore,.DS_Store,*:Zone.Identifier}",
525
- config.src + "/{blocks,fonts,js,sass}/**",
547
+ config.src + "/{block,blocks,fonts,js,sass}/**",
526
548
  ],
527
549
  },
550
+ noErrorOnMissing: true,
528
551
  },
552
+ {
553
+ from: config.src + "/{block,blocks}/**/block.json",
554
+ noErrorOnMissing: true,
555
+ }, // re-add block.json files for loading from PHP
529
556
  ],
530
557
  options: { concurrency: 50 },
531
558
  }),
@@ -551,7 +578,8 @@ export default async (env) => {
551
578
  new BundleAnalyzerPlugin({
552
579
  analyzerMode: isProduction ? "static" : "disabled",
553
580
  openAnalyzer: false,
554
- reportFilename: path.resolve(siteDir, "webpack/stats/index.html"),
581
+ reportFilename: new URL("webpack/stats/index.html", import.meta.url)
582
+ .pathname,
555
583
  }),
556
584
  ],
557
585
  optimization: {
@@ -560,12 +588,8 @@ export default async (env) => {
560
588
  },
561
589
  minimizer: [
562
590
  new EsbuildPlugin({
563
- /**
564
- * Additional targets, can we bump this up to es2020 yet?
565
- * TODO: Expose this to the config
566
- * @link https://esbuild.github.io/content-types/#javascript
567
- */
568
- target: "es2015",
591
+ target: config.esTarget,
592
+ css: true,
569
593
  }),
570
594
  new ImageMinimizerPlugin({
571
595
  severityError: "error",
package/index.js CHANGED
@@ -8,6 +8,8 @@ export { findLocalPort } from "./lib/find-local-docker-port.js";
8
8
  // export * from "./lib/find-local-docker-port.js";
9
9
  // import findLocalPort from "./lib/find-local-docker-port.js";
10
10
 
11
+ import webpackConfig from "./config/webpack.config.js";
12
+ export { webpackConfig };
11
13
  // export default {
12
14
  // // AfterDoneReporterPlugin,
13
15
  // // buildConfig,
@@ -44,7 +44,8 @@ export class AfterDoneReporterPlugin {
44
44
  const aCount = chalk.yellow.bold(assetsInfo.size);
45
45
 
46
46
  const messages = [
47
- `Compiled ${mCount} input modules into ${aCount} files in ${time}`,
47
+ // `Compiled ${mCount} input modules into ${aCount} files in ${time}`,
48
+ // "Dev site " + chalk.blue(`http://${hostname}:${chalk.bold(port)}`),
48
49
  "Dev site " + chalk.blue(`http://${hostname}:${chalk.bold(port)}`),
49
50
  this.config.message,
50
51
  ]
@@ -52,7 +53,8 @@ export class AfterDoneReporterPlugin {
52
53
  .join("\n" + this.config.prefix + " ");
53
54
 
54
55
  setTimeout(() =>
55
- console.log("\n" + this.config.prefix + " " + messages),
56
+ // console.log("\n" + this.config.prefix + " " + messages),
57
+ console.log( "⚙️ " + messages),
56
58
  );
57
59
 
58
60
  setTimeout(() => {
@@ -45,7 +45,11 @@ export class WatchRunReporterPlugin {
45
45
  msg = `Modified ${chalk.bold.yellow(modFiles[0] || basePath)} `;
46
46
  }
47
47
  }
48
- console.log(this.config.prefix, msg + this.config.message);
48
+ // console.log(this.config.prefix, msg + this.config.message);
49
+ console.clear();
50
+ if (msg) {
51
+ console.log("\n💫 ", msg);
52
+ }
49
53
  }
50
54
  });
51
55
  }
@@ -39,20 +39,20 @@ export async function buildConfig(configFile = { config: {} }) {
39
39
  * TODO: This warning can be removed once all process.env.NAME assignments are removed
40
40
  * removed from project configuration files.
41
41
  */
42
- if (configFile.filepath) {
43
- const rawConfigFile = fs.readFileSync(configFile.filepath);
44
- const packageJson = fs.readJsonSync(
45
- path.resolve(configFile.filepath, "../package.json"),
46
- );
47
- // console.log({ p: process.env.NAME });
48
- if (
49
- packageJson.name !== process.env.NAME &&
50
- rawConfigFile.includes("process.env.NAME") &&
51
- configFile.config.src.includes(process.env.NAME)
52
- ) {
53
- nameEnvVarWarning();
54
- }
55
- }
42
+ // if (configFile.filepath) {
43
+ // const rawConfigFile = fs.readFileSync(configFile.filepath);
44
+ // const packageJson = fs.readJsonSync(
45
+ // path.resolve(configFile.filepath, "../package.json"),
46
+ // );
47
+ // // console.log({ p: process.env.NAME });
48
+ // if (
49
+ // packageJson.name !== process.env.NAME &&
50
+ // rawConfigFile.includes("process.env.NAME") &&
51
+ // configFile.config.src.includes(process.env.NAME)
52
+ // ) {
53
+ // nameEnvVarWarning();
54
+ // }
55
+ // }
56
56
 
57
57
  /**
58
58
  * Merge configFile onto defaults
@@ -57,8 +57,9 @@ export async function devserverProxy(config) {
57
57
  return {};
58
58
  }
59
59
 
60
- const proxy = {
61
- "**": {
60
+ const proxy = [
61
+ {
62
+ context: ["**"],
62
63
  target: target.origin,
63
64
  secure: false,
64
65
  autoRewrite: true,
@@ -135,7 +136,7 @@ export async function devserverProxy(config) {
135
136
  });
136
137
  },
137
138
  },
138
- };
139
+ ];
139
140
 
140
141
  return { proxy };
141
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ideasonpurpose/build-tools-wordpress",
3
- "version": "1.1.14",
3
+ "version": "1.2.1",
4
4
  "description": "Build scripts and dependencies for IOP's WordPress development environments.",
5
5
  "homepage": "https://github.com/ideasonpurpose/build-tools-wordpress#readme",
6
6
  "bugs": {
@@ -20,6 +20,7 @@
20
20
  "main": "index.js",
21
21
  "bin": {
22
22
  "iop-build-port-reporter": "./bin/port-reporter.js",
23
+ "iop-project-init": "./bin/project-init.js",
23
24
  "iop-build-zip-archive": "./bin/zip.js"
24
25
  },
25
26
  "directories": {
@@ -31,19 +32,20 @@
31
32
  "prettier": "@ideasonpurpose/prettier-config",
32
33
  "dependencies": {
33
34
  "@ideasonpurpose/prettier-config": "^0.0.6",
35
+ "@inquirer/prompts": "^6.0.1",
34
36
  "@prettier/plugin-php": "^0.22.2",
35
- "@rollup/plugin-commonjs": "^25.0.7",
37
+ "@rollup/plugin-commonjs": "^26.0.1",
36
38
  "@rollup/plugin-json": "^6.1.0",
37
39
  "@rollup/plugin-node-resolve": "^15.2.3",
38
40
  "@svgr/webpack": "^8.1.0",
39
- "@wordpress/dependency-extraction-webpack-plugin": "^5.8.0",
41
+ "@wordpress/dependency-extraction-webpack-plugin": "^6.7.0",
40
42
  "ansi-html": "^0.0.9",
41
43
  "archiver": "^7.0.1",
42
- "auto-changelog": "^2.4.0",
43
- "autoprefixer": "^10.4.19",
44
- "babel-loader": "^9.1.3",
45
- "body-parser": "^1.20.2",
46
- "caniuse-lite": "^1.0.30001616",
44
+ "auto-changelog": "^2.5.0",
45
+ "autoprefixer": "^10.4.20",
46
+ "babel-loader": "^9.2.1",
47
+ "body-parser": "^1.20.3",
48
+ "caniuse-lite": "^1.0.30001660",
47
49
  "chalk": "^5.3.0",
48
50
  "chalk-cli": "^5.0.1",
49
51
  "classnames": "^2.5.1",
@@ -51,59 +53,55 @@
51
53
  "copy-webpack-plugin": "^12.0.2",
52
54
  "cosmiconfig": "^9.0.0",
53
55
  "cross-env": "^7.0.3",
54
- "css-loader": "^6.10.0",
55
- "cssnano": "^6.1.2",
56
- "del": "^7.1.0",
56
+ "css-loader": "^7.1.2",
57
+ "cssnano": "^7.0.6",
57
58
  "dotenv": "^16.4.5",
58
- "esbuild-loader": "^4.1.0",
59
- "eslint": "^8",
60
- "eslint-plugin-react": "^7.34.1",
61
- "filesize": "^10.1.1",
59
+ "esbuild-loader": "^4.2.2",
60
+ "eslint": "^9.10.0",
61
+ "filesize": "^10.1.6",
62
62
  "fs-extra": "^11.1.1",
63
- "globby": "^14.0.1",
63
+ "globby": "^14.0.2",
64
64
  "http-proxy": "^1.18.1",
65
- "humanize-duration": "^3.32.0",
66
- "image-minimizer-webpack-plugin": "^4.0.0",
67
- "is-text-path": "^2.0.0",
65
+ "humanize-duration": "^3.32.1",
66
+ "image-minimizer-webpack-plugin": "^4.1.0",
67
+ "is-text-path": "^3.0.0",
68
68
  "lodash": "^4.17.21",
69
- "mini-css-extract-plugin": "^2.9.0",
70
- "node-sass": "^9.0.0",
71
- "postcss": "^8.4.38",
69
+ "mini-css-extract-plugin": "^2.9.1",
70
+ "ora": "^8.1.0",
71
+ "postcss": "^8.4.47",
72
72
  "postcss-loader": "^8.1.1",
73
73
  "postcss-scss": "^4.0.9",
74
- "prettier": "^3.2.5",
75
- "prettier-eslint": "^16.3.0",
74
+ "prettier": "^3.3.3",
76
75
  "pretty-hrtime": "^1.0.3",
77
76
  "read-package-up": "^11.0.0",
78
77
  "replacestream": "^4.0.3",
79
- "sass": "^1.76.0",
80
- "sass-embedded": "^1.76.0",
81
- "sass-loader": "^14.2.1",
82
- "semver": "^7.6.0",
83
- "sharp": "^0.33.3",
84
- "sort-package-json": "^2.10.0",
85
- "source-map-explorer": "^2.5.3",
78
+ "sass": "^1.78.0",
79
+ "sass-embedded": "^1.78.0",
80
+ "sass-loader": "^16.0.1",
81
+ "semver": "^7.6.3",
82
+ "sharp": "^0.33.5",
83
+ "sort-package-json": "^2.10.1",
86
84
  "string-length": "^6.0.0",
87
85
  "style-loader": "^4.0.0",
88
- "stylelint": "^16.5.0",
86
+ "stylelint": "^16.9.0",
89
87
  "stylelint-order": "^6.0.4",
90
- "stylelint-prettier": "^5.0.0",
91
- "svgo": "^3.0.2",
88
+ "stylelint-prettier": "^5.0.2",
89
+ "svgo": "^3.3.2",
92
90
  "svgo-loader": "^4.0.0",
93
- "version-everything": "^0.11.2",
94
- "webpack": "^5.91.0",
91
+ "version-everything": "^0.11.4",
92
+ "webpack": "^5.94.0",
95
93
  "webpack-bundle-analyzer": "^4.10.2",
96
94
  "webpack-cli": "^5.1.4",
97
- "webpack-dev-middleware": "^7.2.1",
98
- "webpack-dev-server": "^4.15.1"
95
+ "webpack-dev-middleware": "^7.4.2",
96
+ "webpack-dev-server": "^5.1.0"
97
+ },
98
+ "devDependencies": {
99
+ "@vitest/coverage-v8": "^2.1.1",
100
+ "vitest": "^2.1.1"
99
101
  },
100
102
  "version-everything": {
101
103
  "files": [
102
104
  "README.md"
103
105
  ]
104
- },
105
- "devDependencies": {
106
- "@vitest/coverage-v8": "^1.6.0",
107
- "vitest": "^1.6.0"
108
106
  }
109
107
  }