@ideasonpurpose/build-tools-wordpress 1.1.13 → 1.2.0

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,20 @@ 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.1.14
8
+
9
+ > 6 May 2024
10
+
11
+ - minor bumps
12
+
13
+ #### v1.1.13
14
+
15
+ > 10 April 2024
16
+
17
+ - bump most all the deps
18
+ - add read-package-up, esTarget: es2020
19
+ - readme tweak
20
+
7
21
  #### v1.1.12
8
22
 
9
23
  > 4 March 2024
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ideasonpurpose/build-tools-wordpress
2
2
 
3
- #### Version 1.1.13
3
+ #### Version 1.2.0
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,615 @@
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
+ // orphanModules: false,
63
+ // dependentModules: true,
64
+ modules: false,
65
+ groupAssetsByChunk: true,
66
+ entrypoints: true,
67
+ // // errorDetails: "auto",
68
+ children: false,
69
+ // errorDetails: true,
70
+ errors: true,
71
+ errorStack: true,
72
+ // excludeAssets: [/hot-update/, /_sock-/],
73
+ // groupAssetsByChunk: true,
74
+ // logging: "warn",
75
+ // optimizationBailout: true,
76
+ // loggingTrace: false,
77
+ performance: true,
78
+ reasons: true,
79
+ relatedAssets: true,
80
+ timings: true,
81
+ version: true,
82
+ warnings: true,
83
+ loggingDebug: ["sass-loader"],
84
+ };
85
+
86
+ export default async (env) => {
87
+ // const siteDir = new URL(import.meta.url).pathname;
88
+ const siteDir = process.cwd();
89
+ const explorer = cosmiconfig("ideasonpurpose", { searchStrategy: "project" });
90
+ const configFile = await explorer.search(siteDir);
91
+
92
+ const config = await buildConfig(configFile);
93
+
94
+ const proxy = isProduction ? {} : await devserverProxy(config);
95
+
96
+ setTimeout(() =>
97
+ console.log(
98
+ chalk.magenta.bold("sass implementation"),
99
+ chalk.yellow.bold(config.sass),
100
+ // config.sass === "sass" ? nodeSass : dartSass,
101
+ ),
102
+ );
103
+
104
+ /**
105
+ * `usePolling` is a placeholder, try and detect native Windows Docker mounts
106
+ * since they don't support file-watching (no inotify events), if there's
107
+ * something clean, use that instead. For now, this will force-enable polling.
108
+ *
109
+ * TODO: Why so much dancing around defaults when this could just inherit from default.config?
110
+ */
111
+ const usePolling = Boolean(config.usePolling); // likely undefined, coerced to false
112
+ const pollInterval = Math.max(
113
+ parseInt(config.pollInterval, 10) || parseInt(config.usePolling, 10) || 400,
114
+ 400,
115
+ );
116
+
117
+ const devtool = config.devtool || false;
118
+
119
+ const sassLibs = ["sass-embedded", "sass", "node-sass"];
120
+ const sassImplementation = sassLibs.includes(config.sass)
121
+ ? { implementation: config.sass }
122
+ : {};
123
+
124
+ console.log({
125
+ metaurl: new URL("webpack/stats/index.html", import.meta.url),
126
+ config,
127
+ // devServerProxy: await devserverProxy(config),
128
+ });
129
+
130
+ return {
131
+ // stats: "errors-warnings",
132
+ stats,
133
+ module: {
134
+ rules: [
135
+ // {
136
+ // test: /\.(js|jsx|mjs)$/,
137
+ // include: [
138
+ // path.resolve(config.src),
139
+ // path.resolve("../tools/node_modules"),
140
+ // path.resolve("../site/node_modules"),
141
+ // ],
142
+ // exclude: function (module) {
143
+ // const moduleRegex = new RegExp(
144
+ // `node_modules/(${config.transpileDependencies.join("|")})`
145
+ // );
146
+ // return /node_modules/.test(module) && !moduleRegex.test(module);
147
+ // },
148
+
149
+ // /**
150
+ // * EXPERIMENTAL!!
151
+ // * If JS compilation breaks, try reverting this first.
152
+ // */
153
+ // loader: "esbuild-loader",
154
+ // options: {
155
+ // loader: "jsx",
156
+ // target: "es2015",
157
+ // },
158
+
159
+ /**
160
+ * Updated 2022-09, simpler
161
+ */
162
+ {
163
+ test: /\.[jt]sx?$/,
164
+ // test: /\.js$/,
165
+ loader: "esbuild-loader",
166
+ options: {
167
+ loader: "jsx",
168
+ target: config.esTarget,
169
+ },
170
+ },
171
+ // {
172
+ // test: /\.tsx?$/,
173
+ // loader: "esbuild-loader",
174
+ // options: {
175
+ // loader: "tsx",
176
+ // target: config.esTarget,
177
+ // },
178
+ // },
179
+
180
+ // use: {
181
+ // loader: "babel-loader",
182
+ // options: {
183
+ // cacheDirectory: !isProduction,
184
+ // sourceType: "unambiguous",
185
+ // plugins: [
186
+ // "@babel/plugin-syntax-dynamic-import",
187
+ // ...(isProduction
188
+ // ? []
189
+ // : ["@babel/plugin-transform-react-jsx-source"]),
190
+ // ],
191
+ // presets: [
192
+ // [
193
+ // "@babel/preset-env",
194
+ // {
195
+ // forceAllTransforms: true,
196
+ // useBuiltIns: "usage",
197
+ // configPath: config.src,
198
+ // corejs: 3,
199
+ // modules: false,
200
+ // debug: false,
201
+ // },
202
+ // ],
203
+ // "@babel/preset-react",
204
+ // ],
205
+ // },
206
+ // },
207
+ // },
208
+ {
209
+ test: /\.(scss|css)$/,
210
+ use: [
211
+ { loader: MiniCssExtractPlugin.loader },
212
+ {
213
+ loader: "css-loader",
214
+ options: {
215
+ import: false, // imports already handled by Sass or PostCSS
216
+ // sourceMap: !isProduction,
217
+ },
218
+ },
219
+ {
220
+ loader: "postcss-loader",
221
+ options: {
222
+ postcssOptions: {
223
+ plugins: isProduction
224
+ ? [
225
+ autoprefixer,
226
+ cssnano({ preset: ["default", { colormin: false }] }),
227
+ ]
228
+ : [autoprefixer],
229
+ },
230
+ },
231
+ },
232
+ // {
233
+ // loader: "resolve-url-loader",
234
+ // options: {
235
+ // // sourceMap: true,
236
+ // // debug: true,
237
+ // },
238
+ // },
239
+ {
240
+ loader: "sass-loader",
241
+ options: {
242
+ ...sassImplementation,
243
+ // implementation: config.sass === "sass" ? nodeSass : dartSass,
244
+ // implementation: nodeSass,
245
+ // implementation: await import(config.sass),
246
+ sourceMap: !isProduction,
247
+ warnRuleAsWarning: true,
248
+ webpackImporter: false,
249
+ sassOptions: {
250
+ // api: "modern",
251
+
252
+ includePaths: [
253
+ path.resolve(config.src, "sass"),
254
+ path.resolve(config.src),
255
+ // path.resolve("../site/node_modules"),
256
+ path.resolve("node_modules"),
257
+ ],
258
+ style: "expanded",
259
+ verbose: true,
260
+ },
261
+ },
262
+ },
263
+ ],
264
+ },
265
+ /**
266
+ * This image loader is specifically for images which are required or
267
+ * imported into a webpack processed entry file. Optimization is
268
+ * handled by image-minimizer-webpack-plugin. These assets will be
269
+ * renamed with a chunkhash fragment.
270
+ *
271
+ * All images under `config.src` will be optimized and copied by
272
+ * copy-webpack-plugin but will keep their original filenames and
273
+ * relative paths. Images included in SCSS files will be processed
274
+ * twice, once with a hashed name and again with its original name.
275
+ */
276
+ {
277
+ test: /\.(jpe?g|png|gif|tif|webp|avif)$/i,
278
+ type: "asset",
279
+ },
280
+
281
+ /**
282
+ * SVGs can be imported as asset urls or React components
283
+ *
284
+ * To import an SVG file as a src url, append ?url to the filename:
285
+ * import svg from './assets/file.svg?url'
286
+ *
287
+ * To import an SVG file as a React component
288
+
289
+ * @link https://react-svgr.com/docs/webpack/#use-svgr-and-asset-svg-in-the-same-project
290
+ */
291
+ {
292
+ test: /\.svg$/i,
293
+ type: "asset",
294
+ resourceQuery: /url/, // *.svg?url
295
+ },
296
+ {
297
+ test: /\.svg$/i,
298
+ issuer: /\.[jt]sx?$/,
299
+ resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url
300
+ use: ["@svgr/webpack"],
301
+ },
302
+ {
303
+ test: /fonts\/.*\.(ttf|eot|woff2?)$/i,
304
+ type: "asset",
305
+ },
306
+ ],
307
+ },
308
+
309
+ context: path.resolve(config.src),
310
+
311
+ resolve: {
312
+ modules: [
313
+ path.resolve("../tools/node_modules"),
314
+ path.resolve("../site/node_modules"),
315
+ path.resolve("./node_modules"),
316
+ ],
317
+ },
318
+
319
+ resolveLoader: {
320
+ modules: [
321
+ path.resolve("../tools/node_modules"),
322
+ path.resolve("./node_modules"), // for local development when running outside of Docker
323
+ ],
324
+ },
325
+
326
+ entry: config.entry,
327
+
328
+ output: {
329
+ path: new URL(config.dist, import.meta.url).pathname,
330
+ /**
331
+ * Primary output filenames SHOULD NOT include hashes in development because
332
+ * some files are written to disk from devServer middleware. Because those
333
+ * files are written outside standard webpack output, they aren't cleaned
334
+ * up by standard webpack cleaning functions.
335
+ */
336
+ filename: isProduction ? "[name]-[contenthash:8].js" : "[name].js",
337
+ chunkFilename: "[id]-[chunkhash:8].js",
338
+ publicPath: config.publicPath,
339
+ /**
340
+ * Assets are not cleaned when writeToDisk is true in devServer
341
+ * Works correctly with builds.
342
+ * @link https://github.com/webpack/webpack-dev-middleware/issues/861
343
+ */
344
+ clean: true,
345
+ },
346
+
347
+ devServer: {
348
+ host: "0.0.0.0",
349
+ allowedHosts: "all",
350
+ port: "auto",
351
+ // hot: true, // TODO: What does 'only' do? https://webpack.js.org/configuration/dev-server/#devserverhot
352
+ hot: "only", // TODO: What does 'only' do? https://webpack.js.org/configuration/dev-server/#devserverhot
353
+ client: {
354
+ logging: "info",
355
+ overlay: {
356
+ errors: true,
357
+ warnings: true,
358
+ runtimeErrors: (error) => {
359
+ // specific errors can be inspected and handled individually
360
+ // https://webpack.js.org/configuration/dev-server/#overlay
361
+ // console.log("webpack devServer runtimeError:", error);
362
+ return false;
363
+ },
364
+ },
365
+ reconnect: 30,
366
+ // webSocketURL: {
367
+ // port: parseInt(process.env.PORT), // external port, so websockets hit the right endpoint
368
+ // },
369
+ },
370
+ // webSocketServer: "ws",
371
+ // static: {
372
+ // // TODO: Should contentBase be `false` when there's a proxy?
373
+ // directory: path.join("/usr/src/site/", config.contentBase),
374
+ // /*
375
+ // * TODO: Poll options were enabled as a workaround for Docker-win volume inotify
376
+ // * issues. Looking to make this conditional...
377
+ // * Maybe defined `isWindows` or `hasiNotify` for assigning a value
378
+ // * Placeholder defined at the top of the file.
379
+ // * For now, `usePolling` is a boolean (set to true)
380
+ // * ref: https://github.com/docker/for-win/issues/56
381
+ // * https://www.npmjs.com/package/is-windows
382
+ // * TODO: Safe to remove?
383
+ // * TODO: Test on vanilla Windows (should now work in WSL)
384
+ // */
385
+
386
+ // watch: {
387
+ // poll: usePolling && pollInterval,
388
+ // ignored: ["node_modules", "vendor"],
389
+ // },
390
+ // },
391
+
392
+ devMiddleware: {
393
+ index: false, // enable root proxying
394
+
395
+ writeToDisk: (filePath) => {
396
+ /**
397
+ * Note: If this is an async function, it will write everything to disk
398
+ *
399
+ * Never write hot-update files to disk.
400
+ */
401
+ // vendors-node_modules_mini-css-extract-plugin_dist_hmr_hotModuleReplacement_js-node_modules_we-780fe4.js.map
402
+ if (/.+(hot-update)\.(js|json|js\.map)$/.test(filePath)) {
403
+ return false;
404
+ }
405
+ // SHORT_CIRCUIT FOR TESTING
406
+ return true;
407
+
408
+ if (/.+\.(svg|json|php|jpg|png)$/.test(filePath)) {
409
+ const fileStat = statSync(filePath, { throwIfNoEntry: false });
410
+
411
+ /**
412
+ * Always write SVG, PHP & JSON files
413
+ */
414
+ if (/.+\.(svg|json|php)$/.test(filePath)) {
415
+ return true;
416
+ } else {
417
+ /**
418
+ * Write any images under 100k and anything not yet on disk
419
+ */
420
+ if (!fileStat || fileStat.size < 100 * 1024) {
421
+ return true;
422
+ }
423
+ /**
424
+ * TODO: This might all be unnecessary. Webpack seems to be doing a good job with its native caching
425
+ */
426
+ // const randOffset = Math.random() * 300000; // 0-5 minutes
427
+ // const expired = new Date() - fileStat.mtime > randOffset;
428
+ // const relPath = filePath.replace(config.dist, "dist");
429
+ // if (expired) {
430
+ // console.log("DEBUG writeToDisk:", { replacing: relPath });
431
+ // return true;
432
+ // }
433
+ // console.log("DEBUG writeToDisk:", { cached: relPath });
434
+ }
435
+ }
436
+ return false;
437
+ },
438
+ // stats,
439
+ // stats: 'verbose',
440
+ },
441
+
442
+ // NOTE: trying to make injection conditional so wp-admin stops reloading
443
+ // injectClient: compilerConfig => {
444
+ // console.log(compilerConfig);
445
+ // return true;
446
+ // },
447
+
448
+ onListening: (devServer) => {
449
+ const port = devServer.server.address().port;
450
+ devServer.compiler.options.devServer.port =
451
+ devServer.server.address().port;
452
+ devServer.compiler._devServer = devServer;
453
+
454
+ console.log("Listening on port:", port);
455
+ },
456
+
457
+ setupMiddlewares: (middlewares, devServer) => {
458
+ // devServer.compiler.options.devServer.port = devServer.options.port;
459
+ // devServer.compiler._devServer = devServer;
460
+ // if (!devServer) {
461
+ // throw new Error("webpack-dev-server is not defined");
462
+ // }
463
+
464
+ /**
465
+ * The `/inform` route is an annoying bit of code. Here's why:
466
+ * Ubiquity Wi-fi hardware frequently spams the shit out of their
467
+ * networks, specifically requesting the `/inform` route from
468
+ * every device. We still have some Ubiquity hardware on our
469
+ * networks, so dev servers were constantly responding to
470
+ * `/inform` requests with 404s, filling logs and cluttering
471
+ * terminals. So that's why this is here. I hate it.
472
+ */
473
+ devServer.app.all("/inform", () => false);
474
+
475
+ /**
476
+ * The "/webpack/reload" endpoint will trigger a full devServer refresh
477
+ * Originally from our Browsersync implementation:
478
+ *
479
+ * https://github.com/ideasonpurpose/wp-theme-init/blob/ad8039c9757ffc3a0a0ed0adcc616a013fdc8604/src/ThemeInit.php#L202
480
+ */
481
+ devServer.app.get("/webpack/reload", (req, res) => {
482
+ console.log(
483
+ chalk.yellow("Reload triggered by request to /webpack/reload"),
484
+ );
485
+
486
+ devServer.sendMessage(
487
+ devServer.webSocketServer.clients,
488
+ "content-changed",
489
+ );
490
+ res.json({ status: "Reloading!" });
491
+ });
492
+
493
+ return middlewares;
494
+ },
495
+
496
+ watchFiles: {
497
+ paths: [
498
+ path.resolve(config.src, "../**/*.{php,html,svg,json}"), // WordPress
499
+ ],
500
+ options: {
501
+ ignored: [
502
+ "**/.git/**",
503
+ "**/vendor/**",
504
+ "**/node_modules/**",
505
+ "**/dist/**",
506
+ ],
507
+ ignoreInitial: true,
508
+ ignorePermissionErrors: true,
509
+ /**
510
+ * TODO: Can polling be removed everywhere?
511
+ * @link https://github.com/docker/for-win/issues/56#issuecomment-576749639
512
+ */
513
+ usePolling,
514
+ interval: pollInterval,
515
+ },
516
+ },
517
+
518
+ // ...(await devserverProxy(config)),
519
+ // TODO: Move
520
+ // ...(isProduction ? {} : await devserverProxy(config)),
521
+ ...proxy,
522
+ },
523
+
524
+ mode: isProduction ? "production" : "development",
525
+
526
+ performance: {
527
+ hints: isProduction ? "warning" : false,
528
+ },
529
+
530
+ devtool,
531
+
532
+ plugins: [
533
+ new MiniCssExtractPlugin({
534
+ filename: isProduction ? "[name]-[contenthash:8].css" : "[name].css",
535
+ }),
536
+
537
+ new CopyPlugin({
538
+ patterns: [
539
+ {
540
+ from: "**/*",
541
+ globOptions: {
542
+ dot: true, // TODO: Dangerous? Why is this ever necessary?!
543
+ ignore: [
544
+ "**/{.gitignore,.DS_Store,*:Zone.Identifier}",
545
+ config.src + "/{block,blocks,fonts,js,sass}/**",
546
+ ],
547
+ },
548
+ noErrorOnMissing: true,
549
+ },
550
+ {
551
+ from: config.src + "/{block,blocks}/**/block.json",
552
+ noErrorOnMissing: true,
553
+ }, // re-add block.json files for loading from PHP
554
+ ],
555
+ options: { concurrency: 50 },
556
+ }),
557
+
558
+ /**
559
+ * @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dependency-extraction-webpack-plugin/
560
+ */
561
+ new DependencyExtractionWebpackPlugin(),
562
+
563
+ new DependencyManifestPlugin({
564
+ writeManifestFile: true,
565
+ manifestFile: config.manifestFile,
566
+ }),
567
+
568
+ new WatchRunReporterPlugin(),
569
+
570
+ new AfterDoneReporterPlugin({
571
+ echo: env && env.WEBPACK_SERVE,
572
+ // message:
573
+ // "Dev site " + chalk.blue.bold(`http://localhost:${process.env.PORT}`),
574
+ }),
575
+
576
+ new BundleAnalyzerPlugin({
577
+ analyzerMode: isProduction ? "static" : "disabled",
578
+ openAnalyzer: false,
579
+ reportFilename: new URL("webpack/stats/index.html", import.meta.url)
580
+ .pathname,
581
+ }),
582
+ ],
583
+ optimization: {
584
+ splitChunks: {
585
+ chunks: "all",
586
+ },
587
+ minimizer: [
588
+ new EsbuildPlugin({
589
+ target: config.esTarget,
590
+ css: true,
591
+ }),
592
+ new ImageMinimizerPlugin({
593
+ severityError: "error",
594
+
595
+ minimizer: {
596
+ implementation: ImageMinimizerPlugin.sharpMinify,
597
+
598
+ options: {
599
+ /**
600
+ * Sharp options
601
+ */
602
+ encodeOptions: {
603
+ jpeg: {
604
+ quality: 70,
605
+ mozjpeg: true,
606
+ },
607
+ png: {},
608
+ },
609
+ },
610
+ },
611
+ }),
612
+ ],
613
+ },
614
+ };
615
+ };
@@ -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,8 @@ 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.log("\n", "✨", msg);
49
50
  }
50
51
  });
51
52
  }
@@ -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.13",
3
+ "version": "1.2.0",
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.6.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.30001608",
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": "^9.0.0",
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.8.1",
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.74.1",
80
- "sass-embedded": "^1.74.1",
81
- "sass-loader": "^14.1.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.3.1",
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",
95
- "webpack-bundle-analyzer": "^4.9.1",
91
+ "version-everything": "^0.11.4",
92
+ "webpack": "^5.94.0",
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.4.0",
107
- "vitest": "^1.4.0"
108
106
  }
109
107
  }