@ideasonpurpose/build-tools-wordpress 1.1.6 → 1.1.8

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.1.7
8
+
9
+ > 4 March 2024
10
+
11
+ - extracting the zip script, updated deps
12
+ - WIP example webpack.config.js file
13
+ - update readme
14
+
15
+ #### v1.1.6
16
+
17
+ > 30 January 2024
18
+
19
+ - test stuff and fixes
20
+ - misc fixes and first zip testing
21
+
7
22
  #### v1.1.5
8
23
 
9
24
  > 25 January 2024
package/README.md CHANGED
@@ -1,18 +1,25 @@
1
1
  # @ideasonpurpose/build-tools-wordpress
2
2
 
3
- #### Version 1.1.6
3
+ #### Version 1.1.8
4
4
 
5
5
  ![NPM Version](https://img.shields.io/npm/v/%40ideasonpurpose%2Fbuild-tools-wordpress?logo=npm)
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)
7
7
 
8
-
9
8
  Build scripts and dependencies for IOP's WordPress development environments.
10
9
 
10
+ ## Config
11
+
12
+ Each project should have an **ideasonpurpose.config.js** file in the same directory as **package.json**. This file should export an object with at least three properties set:
13
+
14
+ - **`src`** - The **source** directory containing source files which should be compiled or transposed. The contents of this directory will be omitted from builds.
15
+ - **`dist`** - The **distribution** directory where processed, production-ready files will be output to. All contents of this directory will be included in builds.
16
+ - **`src`** - An array of file entry points relative to the `src` directory. Each entry point will generate a like-named output file. All files and assets imported by a given entry point will be accessible from that entry's corresponding output file.
17
+
11
18
  ## About This Project
12
19
 
13
20
  These tools were migrated from our [Docker-based WordPress build tools](https://github.com/ideasonpurpose/docker-build) to speed up development and began the process of moving our build tools away from webpack. Gathering dependencies also simplifies the package.json files in host projects, making those slightly more manageable.
14
21
 
15
- The included webpack.config.js file works best when paired with a PHP environment like our [Docker WordPress environments](https://github.com/ideasonpurpose/docker-wordpress-dev). It's capable of proxying to other servers, but that's sort of crazy.
22
+ The **example/webpack.config.js** file works best when paired with a PHP environment like our [Docker WordPress environments](https://github.com/ideasonpurpose/docker-wordpress-dev). It's capable of proxying to other servers, but that's sort of crazy.
16
23
 
17
24
  ### Additional Notes
18
25
 
@@ -20,12 +27,14 @@ This project expects an entirely ES Module based environment and specifies all d
20
27
 
21
28
  #### Publishing to [npm](https://www.npmjs.com/package/@ideasonpurpose/build-tools-wordpress)
22
29
 
23
- A GitHub action will auto-publish version-tagged releases to npm. In order to publish, the repository must have an `NPM_TOKEN` secret set with the token from npm. [Log into npmjs.org](https://www.npmjs.com/login) with a publish-authorized account, then find the token page linked from the Profile page sidebar. Generate a new token and update the repository secret.
30
+ A GitHub action will auto-publish version-tagged releases to npm. In order to publish, the repository must have an `NPM_TOKEN` secret set with the token from npm. [Log into npmjs.org](https://www.npmjs.com/login) with a publish-authorized account, then find the token page linked from the Profile page sidebar. Generate a new token and update the repository secret.
24
31
 
25
- ##  
32
+ <!-- START IOP CREDIT BLURB -->
26
33
 
34
+ ## &nbsp;
27
35
 
28
36
  #### Brought to you by IOP
29
37
 
30
38
  <a href="https://www.ideasonpurpose.com"><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/iop-logo-white-on-black-88px.png" height="44" align="top" alt="IOP Logo"></a><img src="https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/spacer.png" align="middle" width="4" height="54"> This project is actively developed and used in production at <a href="https://www.ideasonpurpose.com">Ideas On Purpose</a>.
31
39
 
40
+ <!-- END IOP CREDIT BLURB -->
package/bin/zip.js CHANGED
@@ -1,5 +1,11 @@
1
- import fs from "fs-extra";
2
- import { posix as path } from "path";
1
+ import { readJson, ensureFile } from "fs-extra/esm";
2
+ import { stat } from "node:fs/promises";
3
+
4
+ import { basename, dirname, join } from "node:path/posix";
5
+ import { createReadStream, createWriteStream, statSync } from "node:fs";
6
+
7
+ import { clearLine, cursorTo } from "node:readline";
8
+
3
9
  import url from "url";
4
10
  import { fileURLToPath, pathToFileURL } from "node:url";
5
11
 
@@ -13,75 +19,48 @@ import cliTruncate from "cli-truncate";
13
19
  import stringLength from "string-length";
14
20
  import replaceStream from "replacestream";
15
21
 
16
- import buildConfig from "./lib/buildConfig.js";
17
- import { prettierHrtime } from "./lib/prettier-hrtime.js";
18
-
22
+ // import buildConfig from "../lib/buildConfig.js";
23
+ import { buildConfig } from "../index.js";
24
+ // import {dirname} from "node:path";
25
+ import { prettierHrtime } from "../lib/prettier-hrtime.js";
19
26
 
27
+ // console.log(process.cwd());
28
+ // console.log(import.meta.url);
29
+ // console.log(new URL(import.meta.url));
20
30
 
21
- export async function zip() {
22
- // const siteDir = new URL("../site", import.meta.url).pathname;
23
- const siteDir = new URL(import.meta.url).pathname;
24
- // const explorerSync = cosmiconfigSync("ideasonpurpose");
31
+ async function getConfig() {
32
+ const siteDir = process.cwd();
25
33
  const explorer = cosmiconfig("ideasonpurpose");
26
- // const configFile = explorerSync.search(siteDir);
27
34
  const configFile = await explorer.search(siteDir);
28
- }
29
-
30
- zip()
31
-
32
- /**
33
- * This script expects to the site to live in /usr/src/site/ to
34
- * match the webpack config. It can also be called with a single
35
- * path argument which will be evaluated relative to the script.
36
- * This can be used for testing, or to bundle any directory while
37
- * excluding src, node_modules, etc.
38
- */
39
- let siteDirBase = process.argv[2] || "/usr/src/site/";
40
- siteDirBase += siteDirBase.endsWith("/") ? "" : "/";
41
- // const siteDir = new URL(siteDirBase, import.meta.url);
42
-
43
- // const explorerSync = cosmiconfigSync("ideasonpurpose");
44
- // const configFile = explorerSync.search(siteDir.pathname) || {
45
- // config: {},
46
- // filepath: siteDir.pathname,
47
- // };
48
- // const configFileUrl = url.pathToFileURL(configFile.filepath);
49
- // const config = buildConfig(configFile);
50
-
51
- // const explorerSync = cosmiconfigSync("ideasonpurpose");
52
- // const configFile = explorerSync.search(siteDir);
53
-
54
- const configFile = {
55
- filepath: fileURLToPath(
56
- new URL("../site/ideasonpurpose.config.js", import.meta.url)
57
- ),
58
- };
59
- const configFileUrl = pathToFileURL(configFile.filepath);
35
+ const config = await buildConfig(configFile);
60
36
 
61
- // import buildConfig from "./lib/buildConfig.js";
62
- import siteConfig from "../site/ideasonpurpose.config.js";
63
-
64
- console.log({ configFile, configFileUrl });
37
+ // config.configFileUrl2 = configFile.filepath;
38
+ console.log({ config, configFile });
65
39
 
40
+ return { config };
41
+ }
42
+ const { config } = await getConfig();
66
43
 
67
- return ;
68
- // const config = buildConfig(configFile ?? siteConfig);
69
- const config = buildConfig({ config: siteConfig });
44
+ config.configFileUrl = new URL("package.json", config.configFileUrl);
45
+ const packageJson = readJson(new URL("package.json", config.configFileUrl));
46
+ packageJson.version = (await packageJson).version ??= "";
70
47
 
71
- const packageJson = fs.readJsonSync(new URL("package.json", configFileUrl));
72
- packageJson.version ??= "";
48
+ console.log({ pgk: await packageJson });
49
+ // console.log({ packageJson, env: process.env });
73
50
 
74
- const archiveName = packageJson.name || "archive";
51
+ const archiveName = (await packageJson).name || "archive";
75
52
 
76
53
  const versionDirName = packageJson.version
77
54
  ? `${archiveName}-${packageJson.version}`.replace(/[ .]/g, "_")
78
55
  : archiveName;
79
56
 
80
57
  const zipFileName = `${versionDirName}.zip`;
81
- const zipFile = new URL(`_builds/${zipFileName}`, configFileUrl).pathname;
58
+ const zipFile = new URL(`_builds/${zipFileName}`, config.configFileUrl);
59
+ // .pathname;
82
60
 
83
- fs.ensureFileSync(zipFile);
84
- const output = fs.createWriteStream(zipFile);
61
+ console.log({ zipFile, zipFileName });
62
+ await ensureFile(zipFile.pathname);
63
+ const output = createWriteStream(zipFile);
85
64
 
86
65
  output.on("finish", finishReporter);
87
66
 
@@ -95,7 +74,8 @@ const start = process.hrtime();
95
74
  * Set projectDir to the parent directory of config.src, all bundled
96
75
  * files will be found relative to this.
97
76
  */
98
- const projectDir = new URL(`${config.src}/../`, configFileUrl);
77
+ const projectDir = new URL(`${config.src}/../`, config.configFileUrl);
78
+ // process.exit();
99
79
 
100
80
  /**
101
81
  * Counters for total uncompressed size and number of files
@@ -105,7 +85,25 @@ let inBytes = 0;
105
85
  let fileCount = 0;
106
86
 
107
87
  const globOpts = { cwd: projectDir.pathname, nodir: false };
108
- globby(["**/*", "!src", "!_builds", "!**/*.sql", "!**/node_modules"], globOpts)
88
+ globby(
89
+ [
90
+ "**/*",
91
+ "!_builds",
92
+ "!**/*.sql",
93
+ "!**/node_modules",
94
+ "!CHANGELOG.md",
95
+ "!composer.lock",
96
+ "!coverage*",
97
+ "!docker-compose.yml",
98
+ "!docker-compose.yml",
99
+ "!package-lock.json",
100
+ "!phpunit.xml",
101
+ "!src",
102
+ "!test",
103
+ "!tests",
104
+ ],
105
+ globOpts,
106
+ )
109
107
  .then((fileList) => {
110
108
  /**
111
109
  * Throw an error and bail out if there are no files to zip
@@ -119,8 +117,8 @@ globby(["**/*", "!src", "!_builds", "!**/*.sql", "!**/node_modules"], globOpts)
119
117
  fileList.map((f) => {
120
118
  const file = {
121
119
  path: f,
122
- stat: fs.statSync(new URL(f, projectDir)),
123
- contents: fs.createReadStream(path.join(globOpts.cwd, f)),
120
+ stat: statSync(new URL(f, projectDir)),
121
+ contents: createReadStream(join(globOpts.cwd, f)),
124
122
  };
125
123
 
126
124
  /**
@@ -132,7 +130,7 @@ globby(["**/*", "!src", "!_builds", "!**/*.sql", "!**/node_modules"], globOpts)
132
130
  const devPath = new RegExp(`wp-content/themes/${archiveName}/`, "gi");
133
131
 
134
132
  file.contents = file.contents.pipe(
135
- replaceStream(devPath, `wp-content/themes/${versionDirName}/`)
133
+ replaceStream(devPath, `wp-content/themes/${versionDirName}/`),
136
134
  );
137
135
  }
138
136
 
@@ -148,15 +146,15 @@ globby(["**/*", "!src", "!_builds", "!**/*.sql", "!**/node_modules"], globOpts)
148
146
  */
149
147
  file.contents.pause();
150
148
  return file;
151
- })
149
+ }),
152
150
  )
153
151
  .then((fileList) =>
154
152
  fileList.map((f) =>
155
153
  archive.append(f.contents, {
156
154
  name: f.path,
157
155
  prefix: versionDirName,
158
- })
159
- )
156
+ }),
157
+ ),
160
158
  )
161
159
  .then(() => archive.finalize())
162
160
  .catch(console.error);
@@ -178,9 +176,12 @@ function foundReporter(file) {
178
176
  outString += chalk.blue(cliTruncate(file.path, cols, { position: "middle" }));
179
177
 
180
178
  if (fileCount % 25 == 0) {
181
- process.stdout.clearLine();
182
- process.stdout.cursorTo(0);
179
+ // process.stdout.clearLine();
180
+ clearLine(stdout);
181
+ // process.stdout.cursorTo(0);
182
+ cursorTo(stdout, 0);
183
183
  process.stdout.write(outString);
184
+
184
185
  }
185
186
  }
186
187
 
@@ -199,33 +200,33 @@ function finishReporter() {
199
200
  chalk.yellow("Found"),
200
201
  chalk.magenta(fileCount),
201
202
  chalk.yellow("files"),
202
- chalk.gray(`(Uncompressed: ${filesize(inBytes)})`)
203
+ chalk.gray(`(Uncompressed: ${filesize(inBytes)})`),
203
204
  );
204
205
  console.log(
205
206
  "👀 ",
206
207
  chalk.yellow("Webpack Bundle Analyzer report:"),
207
- chalk.magenta("webpack/stats/index.html")
208
+ chalk.magenta("webpack/stats/index.html"),
208
209
  );
209
210
  console.log(
210
211
  "📦 ",
211
212
  chalk.yellow("Created"),
212
213
  chalk.magenta(filesize(outBytes)),
213
214
  chalk.yellow("Zip archive"),
214
- chalk.gray(`(Saved ${filesize(savedBytes)}, ${savedPercent}%)`)
215
+ chalk.gray(`(Saved ${filesize(savedBytes)}, ${savedPercent}%)`),
215
216
  );
216
217
  console.log(
217
218
  "🎁 ",
218
219
  chalk.yellow("Theme archive"),
219
220
  chalk.magenta(
220
- `${path.basename(path.dirname(zipFile))}/${chalk.bold(zipFileName)}`
221
+ `${basename(dirname(zipFile.pathname))}/${chalk.bold(zipFileName)}`,
221
222
  ),
222
223
  chalk.yellow("created in"),
223
- chalk.magenta(duration)
224
+ chalk.magenta(duration),
224
225
  );
225
226
  console.log("⏳");
226
227
  console.log(
227
228
  "🚀 ",
228
- chalk.bold(`Remember to push to ${chalk.cyan("GitHub!")}`)
229
+ chalk.bold(`Remember to push to ${chalk.cyan("GitHub!")}`),
229
230
  );
230
231
  console.log("✨");
231
232
  }
@@ -0,0 +1,593 @@
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 "@ideasonpurpose/build-tools-wordpress";
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("../site", import.meta.url).pathname;
88
+ const siteDir = new URL(import.meta.url).pathname;
89
+ // const explorerSync = cosmiconfigSync("ideasonpurpose");
90
+ const explorer = cosmiconfig("ideasonpurpose");
91
+ // const configFile = explorerSync.search(siteDir);
92
+ const configFile = await explorer.search(siteDir);
93
+
94
+ // import buildConfig from "./lib/buildConfig.js";
95
+ // console.log({ siteDir, configFile, nm: path.resolve("node_modules") });
96
+
97
+ const config = await buildConfig(configFile);
98
+
99
+ setTimeout(() =>
100
+ console.log(
101
+ chalk.magenta.bold("sass implementation"),
102
+ chalk.yellow.bold(config.sass),
103
+ config.sass === "sass" ? nodeSass : dartSass,
104
+ ),
105
+ );
106
+
107
+ /**
108
+ * `usePolling` is a placeholder, try and detect native Windows Docker mounts
109
+ * since they don't support file-watching (no inotify events), if there's
110
+ * something clean, use that instead. For now, this will force-enable polling.
111
+ *
112
+ * TODO: Why so much dancing around defaults when this could just inherit from default.config?
113
+ */
114
+ const usePolling = Boolean(config.usePolling);
115
+ const pollInterval = Math.max(
116
+ parseInt(config.pollInterval, 10) || parseInt(config.usePolling, 10) || 400,
117
+ 400,
118
+ );
119
+
120
+ const devtool = config.devtool || false;
121
+
122
+ console.log({ config, devServerProxy: await devserverProxy(config) });
123
+
124
+ return {
125
+ // stats: "errors-warnings",
126
+ stats,
127
+ module: {
128
+ rules: [
129
+ // {
130
+ // test: /\.(js|jsx|mjs)$/,
131
+ // include: [
132
+ // path.resolve(config.src),
133
+ // path.resolve("../tools/node_modules"),
134
+ // path.resolve("../site/node_modules"),
135
+ // ],
136
+ // exclude: function (module) {
137
+ // const moduleRegex = new RegExp(
138
+ // `node_modules/(${config.transpileDependencies.join("|")})`
139
+ // );
140
+ // return /node_modules/.test(module) && !moduleRegex.test(module);
141
+ // },
142
+
143
+ // /**
144
+ // * EXPERIMENTAL!!
145
+ // * If JS compilation breaks, try reverting this first.
146
+ // */
147
+ // loader: "esbuild-loader",
148
+ // options: {
149
+ // loader: "jsx",
150
+ // target: "es2015",
151
+ // },
152
+
153
+ /**
154
+ * Updated 2022-09, simpler
155
+ */
156
+ {
157
+ test: /\.m?jsx?$/,
158
+ loader: "esbuild-loader",
159
+ options: {
160
+ loader: "jsx",
161
+ target: "es2015",
162
+ },
163
+ },
164
+ {
165
+ test: /\.tsx?$/,
166
+ loader: "esbuild-loader",
167
+ options: {
168
+ loader: "tsx",
169
+ target: "es2015",
170
+ },
171
+ },
172
+
173
+ // use: {
174
+ // loader: "babel-loader",
175
+ // options: {
176
+ // cacheDirectory: !isProduction,
177
+ // sourceType: "unambiguous",
178
+ // plugins: [
179
+ // "@babel/plugin-syntax-dynamic-import",
180
+ // ...(isProduction
181
+ // ? []
182
+ // : ["@babel/plugin-transform-react-jsx-source"]),
183
+ // ],
184
+ // presets: [
185
+ // [
186
+ // "@babel/preset-env",
187
+ // {
188
+ // forceAllTransforms: true,
189
+ // useBuiltIns: "usage",
190
+ // configPath: config.src,
191
+ // corejs: 3,
192
+ // modules: false,
193
+ // debug: false,
194
+ // },
195
+ // ],
196
+ // "@babel/preset-react",
197
+ // ],
198
+ // },
199
+ // },
200
+ // },
201
+ {
202
+ test: /\.(scss|css)$/,
203
+ use: [
204
+ { loader: MiniCssExtractPlugin.loader },
205
+ {
206
+ loader: "css-loader",
207
+ options: {
208
+ import: false, // imports already handled by Sass or PostCSS
209
+ // sourceMap: !isProduction,
210
+ },
211
+ },
212
+ {
213
+ loader: "postcss-loader",
214
+ options: {
215
+ postcssOptions: {
216
+ plugins: isProduction
217
+ ? [
218
+ autoprefixer,
219
+ cssnano({ preset: ["default", { colormin: false }] }),
220
+ ]
221
+ : [autoprefixer],
222
+ },
223
+ },
224
+ },
225
+ // {
226
+ // loader: "resolve-url-loader",
227
+ // options: {
228
+ // // sourceMap: true,
229
+ // // debug: true,
230
+ // },
231
+ // },
232
+ {
233
+ loader: "sass-loader",
234
+ options: {
235
+ implementation: config.sass === "sass" ? nodeSass : dartSass,
236
+ // implementation: nodeSass,
237
+ // implementation: await import(config.sass),
238
+ sourceMap: !isProduction,
239
+ warnRuleAsWarning: true,
240
+ webpackImporter: false,
241
+ sassOptions: {
242
+ // api: "modern",
243
+
244
+ includePaths: [
245
+ path.resolve(config.src, "sass"),
246
+ // path.resolve("../site/node_modules"),
247
+ path.resolve("node_modules"),
248
+ ],
249
+ style: "expanded",
250
+ verbose: true,
251
+ },
252
+ },
253
+ },
254
+ ],
255
+ },
256
+ /**
257
+ * This image loader is specifically for images which are required or
258
+ * imported into a webpack processed entry file. Optimization is
259
+ * handled by image-minimizer-webpack-plugin. These assets will be
260
+ * renamed with a chunkhash fragment.
261
+ *
262
+ * All images under `config.src` will be optimized and copied by
263
+ * copy-webpack-plugin but will keep their original filenames and
264
+ * relative paths. Images included in SCSS files will be processed
265
+ * twice, once with a hashed name and again with its original name.
266
+ */
267
+ {
268
+ test: /\.(jpe?g|png|gif|tif|webp|avif)$/i,
269
+ type: "asset",
270
+ },
271
+
272
+ /**
273
+ * SVGs can be imported as asset urls or React components
274
+ *
275
+ * To import an SVG file as a src url, append ?url to the filename:
276
+ * import svg from './assets/file.svg?url'
277
+ *
278
+ * To import an SVG file as a React component
279
+
280
+ * @link https://react-svgr.com/docs/webpack/#use-svgr-and-asset-svg-in-the-same-project
281
+ */
282
+ {
283
+ test: /\.svg$/i,
284
+ type: "asset",
285
+ resourceQuery: /url/, // *.svg?url
286
+ },
287
+ {
288
+ test: /\.svg$/i,
289
+ issuer: /\.[jt]sx?$/,
290
+ resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url
291
+ use: ["@svgr/webpack"],
292
+ },
293
+ {
294
+ test: /fonts\/.*\.(ttf|eot|woff2?)$/i,
295
+ type: "asset",
296
+ },
297
+ ],
298
+ },
299
+
300
+ context: path.resolve(config.src),
301
+
302
+ resolve: {
303
+ modules: [
304
+ path.resolve("../tools/node_modules"),
305
+ path.resolve("../site/node_modules"),
306
+ path.resolve("./node_modules"),
307
+ ],
308
+ },
309
+
310
+ resolveLoader: {
311
+ modules: [
312
+ path.resolve("../tools/node_modules"),
313
+ path.resolve("./node_modules"), // for local development when running outside of Docker
314
+ ],
315
+ },
316
+
317
+ entry: config.entry,
318
+
319
+ output: {
320
+ path: new URL(config.dist, import.meta.url).pathname,
321
+ /**
322
+ * Primary output filenames SHOULD NOT include hashes in development because
323
+ * some files are written to disk from devServer middleware. Because those
324
+ * files are written outside standard webpack output, they aren't cleaned
325
+ * up by standard webpack cleaning functions.
326
+ */
327
+ filename: isProduction ? "[name]-[contenthash:8].js" : "[name].js",
328
+ chunkFilename: "[id]-[chunkhash:8].js",
329
+ publicPath: config.publicPath,
330
+ /**
331
+ * Assets are not cleaned when writeToDisk is true in devServer
332
+ * Works correctly with builds.
333
+ * @link https://github.com/webpack/webpack-dev-middleware/issues/861
334
+ */
335
+ clean: true,
336
+ },
337
+
338
+ devServer: {
339
+ host: "0.0.0.0",
340
+ 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
+ port: "auto",
345
+ // 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
347
+ 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?
352
+ reconnect: 30,
353
+ // webSocketURL: {
354
+ // port: parseInt(process.env.PORT), // external port, so websockets hit the right endpoint
355
+ // },
356
+ },
357
+ // webSocketServer: "ws",
358
+ // static: {
359
+ // // TODO: Should contentBase be `false` when there's a proxy?
360
+ // directory: path.join("/usr/src/site/", config.contentBase),
361
+ // /*
362
+ // * TODO: Poll options were enabled as a workaround for Docker-win volume inotify
363
+ // * issues. Looking to make this conditional...
364
+ // * Maybe defined `isWindows` or `hasiNotify` for assigning a value
365
+ // * Placeholder defined at the top of the file.
366
+ // * For now, `usePolling` is a boolean (set to true)
367
+ // * ref: https://github.com/docker/for-win/issues/56
368
+ // * https://www.npmjs.com/package/is-windows
369
+ // * TODO: Safe to remove?
370
+ // * TODO: Test on vanilla Windows (should now work in WSL)
371
+ // */
372
+
373
+ // watch: {
374
+ // poll: usePolling && pollInterval,
375
+ // ignored: ["node_modules", "vendor"],
376
+ // },
377
+ // },
378
+
379
+ devMiddleware: {
380
+ index: false, // enable root proxying
381
+
382
+ writeToDisk: (filePath) => {
383
+ /**
384
+ * Note: If this is an async function, it will write everything to disk
385
+ *
386
+ * Never write hot-update files to disk.
387
+ */
388
+ // vendors-node_modules_mini-css-extract-plugin_dist_hmr_hotModuleReplacement_js-node_modules_we-780fe4.js.map
389
+ if (/.+(hot-update)\.(js|json|js\.map)$/.test(filePath)) {
390
+ return false;
391
+ }
392
+ // SHORT_CIRCUIT FOR TESTING
393
+ return true;
394
+
395
+ if (/.+\.(svg|json|php|jpg|png)$/.test(filePath)) {
396
+ const fileStat = statSync(filePath, { throwIfNoEntry: false });
397
+
398
+ /**
399
+ * Always write SVG, PHP & JSON files
400
+ */
401
+ if (/.+\.(svg|json|php)$/.test(filePath)) {
402
+ return true;
403
+ } else {
404
+ /**
405
+ * Write any images under 100k and anything not yet on disk
406
+ */
407
+ if (!fileStat || fileStat.size < 100 * 1024) {
408
+ return true;
409
+ }
410
+ /**
411
+ * TODO: This might all be unnecessary. Webpack seems to be doing a good job with its native caching
412
+ */
413
+ // const randOffset = Math.random() * 300000; // 0-5 minutes
414
+ // const expired = new Date() - fileStat.mtime > randOffset;
415
+ // const relPath = filePath.replace(config.dist, "dist");
416
+ // if (expired) {
417
+ // console.log("DEBUG writeToDisk:", { replacing: relPath });
418
+ // return true;
419
+ // }
420
+ // console.log("DEBUG writeToDisk:", { cached: relPath });
421
+ }
422
+ }
423
+ return false;
424
+ },
425
+ // stats,
426
+ // stats: 'verbose',
427
+ },
428
+
429
+ // NOTE: trying to make injection conditional so wp-admin stops reloading
430
+ // injectClient: compilerConfig => {
431
+ // console.log(compilerConfig);
432
+ // return true;
433
+ // },
434
+
435
+ onListening: (devServer) => {
436
+ const port = devServer.server.address().port;
437
+ devServer.compiler.options.devServer.port =
438
+ devServer.server.address().port;
439
+ devServer.compiler._devServer = devServer;
440
+
441
+ console.log("Listening on port:", port);
442
+ },
443
+
444
+ setupMiddlewares: (middlewares, devServer) => {
445
+ // devServer.compiler.options.devServer.port = devServer.options.port;
446
+ // devServer.compiler._devServer = devServer;
447
+ // if (!devServer) {
448
+ // throw new Error("webpack-dev-server is not defined");
449
+ // }
450
+
451
+ /**
452
+ * The `/inform` route is an annoying bit of code. Here's why:
453
+ * Ubiquity Wi-fi hardware frequently spams the shit out of their
454
+ * networks, specifically requesting the `/inform` route from
455
+ * every device. We still have some Ubiquity hardware on our
456
+ * networks, so dev servers were constantly responding to
457
+ * `/inform` requests with 404s, filling logs and cluttering
458
+ * terminals. So that's why this is here. I hate it.
459
+ */
460
+ devServer.app.all("/inform", () => false);
461
+
462
+ /**
463
+ * The "/webpack/reload" endpoint will trigger a full devServer refresh
464
+ * Originally from our Browsersync implementation:
465
+ *
466
+ * https://github.com/ideasonpurpose/wp-theme-init/blob/ad8039c9757ffc3a0a0ed0adcc616a013fdc8604/src/ThemeInit.php#L202
467
+ */
468
+ devServer.app.get("/webpack/reload", (req, res) => {
469
+ console.log(
470
+ chalk.yellow("Reload triggered by request to /webpack/reload"),
471
+ );
472
+
473
+ devServer.sendMessage(
474
+ devServer.webSocketServer.clients,
475
+ "content-changed",
476
+ );
477
+ res.json({ status: "Reloading!" });
478
+ });
479
+
480
+ return middlewares;
481
+ },
482
+
483
+ watchFiles: {
484
+ paths: [
485
+ path.resolve(config.src, "../**/*.{php,json}"), // WordPress
486
+ // path.resolve(config.src, `../${config.contentBase}/*.html`), // Jekyll
487
+ ],
488
+ options: {
489
+ ignored: ["**/.git/**", "**/vendor/**", "**/node_modules/**", "**/dist/**"],
490
+ ignoreInitial: true,
491
+ ignorePermissionErrors: true,
492
+ /**
493
+ * TODO: Can polling be removed everywhere?
494
+ * @link https://github.com/docker/for-win/issues/56#issuecomment-576749639
495
+ */
496
+ usePolling,
497
+ interval: pollInterval,
498
+ },
499
+ },
500
+
501
+ ...(await devserverProxy(config)),
502
+ },
503
+
504
+ mode: isProduction ? "production" : "development",
505
+
506
+ performance: {
507
+ hints: isProduction ? "warning" : false,
508
+ },
509
+
510
+ devtool,
511
+
512
+ plugins: [
513
+ new MiniCssExtractPlugin({
514
+ filename: isProduction ? "[name]-[contenthash:8].css" : "[name].css",
515
+ }),
516
+
517
+ new CopyPlugin({
518
+ patterns: [
519
+ {
520
+ from: "**/*",
521
+ globOptions: {
522
+ dot: true, // TODO: Dangerous? Why is this ever necessary?!
523
+ ignore: [
524
+ "**/{.gitignore,.DS_Store,*:Zone.Identifier}",
525
+ config.src + "/{blocks,fonts,js,sass}/**",
526
+ ],
527
+ },
528
+ },
529
+ ],
530
+ options: { concurrency: 50 },
531
+ }),
532
+
533
+ /**
534
+ * @link https://developer.wordpress.org/block-editor/reference-guides/packages/packages-dependency-extraction-webpack-plugin/
535
+ */
536
+ new DependencyExtractionWebpackPlugin(),
537
+
538
+ new DependencyManifestPlugin({
539
+ writeManifestFile: true,
540
+ manifestFile: config.manifestFile,
541
+ }),
542
+
543
+ new WatchRunReporterPlugin(),
544
+
545
+ new AfterDoneReporterPlugin({
546
+ echo: env && env.WEBPACK_SERVE,
547
+ // message:
548
+ // "Dev site " + chalk.blue.bold(`http://localhost:${process.env.PORT}`),
549
+ }),
550
+
551
+ new BundleAnalyzerPlugin({
552
+ analyzerMode: isProduction ? "static" : "disabled",
553
+ openAnalyzer: false,
554
+ reportFilename: path.resolve(siteDir, "webpack/stats/index.html"),
555
+ }),
556
+ ],
557
+ optimization: {
558
+ splitChunks: {
559
+ chunks: "all",
560
+ },
561
+ minimizer: [
562
+ 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",
569
+ }),
570
+ new ImageMinimizerPlugin({
571
+ severityError: "error",
572
+
573
+ minimizer: {
574
+ implementation: ImageMinimizerPlugin.sharpMinify,
575
+
576
+ options: {
577
+ /**
578
+ * Sharp options
579
+ */
580
+ encodeOptions: {
581
+ jpeg: {
582
+ quality: 70,
583
+ mozjpeg: true,
584
+ },
585
+ png: {},
586
+ },
587
+ },
588
+ },
589
+ }),
590
+ ],
591
+ },
592
+ };
593
+ };
@@ -1,6 +1,7 @@
1
1
  import fs from "fs-extra";
2
2
  import { posix as path } from "path";
3
3
  import chalk from "chalk";
4
+ import url from "url";
4
5
 
5
6
  import { findLocalPort } from "../index.js";
6
7
 
@@ -87,7 +88,7 @@ export async function buildConfig(configFile = { config: {} }) {
87
88
  configFile.filepath = configFile.filepath || "../site/placeholder.file";
88
89
  config.src = path.resolve(configFile.filepath, "..", config.src);
89
90
  config.dist = path.resolve(configFile.filepath, "..", config.dist);
90
-
91
+ config.configFileUrl = url.pathToFileURL(configFile.filepath);
91
92
  /**
92
93
  * Check for `config.src` and create it if missing
93
94
  * TODO: unresolved, create the file? Fail? Throw error?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ideasonpurpose/build-tools-wordpress",
3
- "version": "1.1.6",
3
+ "version": "1.1.8",
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": {
@@ -31,19 +31,19 @@
31
31
  "prettier": "@ideasonpurpose/prettier-config",
32
32
  "dependencies": {
33
33
  "@ideasonpurpose/prettier-config": "^0.0.4",
34
- "@prettier/plugin-php": "^0.22.1",
34
+ "@prettier/plugin-php": "^0.22.2",
35
35
  "@rollup/plugin-commonjs": "^25.0.7",
36
36
  "@rollup/plugin-json": "^6.1.0",
37
37
  "@rollup/plugin-node-resolve": "^15.2.3",
38
38
  "@svgr/webpack": "^8.1.0",
39
- "@wordpress/dependency-extraction-webpack-plugin": "^5.0.0",
39
+ "@wordpress/dependency-extraction-webpack-plugin": "^5.3.0",
40
40
  "ansi-html": "^0.0.9",
41
- "archiver": "^6.0.1",
41
+ "archiver": "^7.0.0",
42
42
  "auto-changelog": "^2.4.0",
43
- "autoprefixer": "^10.4.17",
43
+ "autoprefixer": "^10.4.18",
44
44
  "babel-loader": "^9.1.3",
45
45
  "body-parser": "^1.20.2",
46
- "caniuse-lite": "^1.0.30001579",
46
+ "caniuse-lite": "^1.0.30001593",
47
47
  "chalk": "^5.3.0",
48
48
  "chalk-cli": "^5.0.1",
49
49
  "classnames": "^2.5.1",
@@ -51,46 +51,46 @@
51
51
  "copy-webpack-plugin": "^12.0.2",
52
52
  "cosmiconfig": "^9.0.0",
53
53
  "cross-env": "^7.0.3",
54
- "css-loader": "^6.9.1",
55
- "cssnano": "^6.0.1",
54
+ "css-loader": "^6.10.0",
55
+ "cssnano": "^6.0.5",
56
56
  "del": "^7.1.0",
57
- "dotenv": "^16.4.0",
57
+ "dotenv": "^16.4.5",
58
58
  "esbuild-loader": "^4.0.3",
59
- "eslint": "^8.56.0",
59
+ "eslint": "^8.57.0",
60
60
  "eslint-plugin-react": "^7.33.2",
61
61
  "filesize": "^10.0.12",
62
62
  "fs-extra": "^11.1.1",
63
- "globby": "^14.0.0",
63
+ "globby": "^14.0.1",
64
64
  "http-proxy": "^1.18.1",
65
65
  "humanize-duration": "^3.31.0",
66
66
  "image-minimizer-webpack-plugin": "^4.0.0",
67
67
  "is-text-path": "^2.0.0",
68
68
  "lodash": "^4.17.21",
69
- "mini-css-extract-plugin": "^2.7.6",
69
+ "mini-css-extract-plugin": "^2.8.1",
70
70
  "node-sass": "^9.0.0",
71
- "postcss": "^8.4.29",
72
- "postcss-loader": "^8.0.0",
71
+ "postcss": "^8.4.35",
72
+ "postcss-loader": "^8.1.1",
73
73
  "postcss-scss": "^4.0.9",
74
- "prettier": "^3.2.4",
74
+ "prettier": "^3.2.5",
75
75
  "prettier-eslint": "^16.3.0",
76
76
  "pretty-hrtime": "^1.0.3",
77
77
  "replacestream": "^4.0.3",
78
- "sass": "^1.70.0",
79
- "sass-embedded": "^1.70.0",
80
- "sass-loader": "^14.0.0",
81
- "semver": "^7.5.4",
78
+ "sass": "^1.71.1",
79
+ "sass-embedded": "^1.71.1",
80
+ "sass-loader": "^14.1.1",
81
+ "semver": "^7.6.0",
82
82
  "sharp": "^0.33.2",
83
- "sort-package-json": "^2.6.0",
83
+ "sort-package-json": "^2.8.0",
84
84
  "source-map-explorer": "^2.5.3",
85
85
  "string-length": "^6.0.0",
86
86
  "style-loader": "^3.3.3",
87
- "stylelint": "^16.2.0",
87
+ "stylelint": "^16.2.1",
88
88
  "stylelint-order": "^6.0.4",
89
89
  "stylelint-prettier": "^5.0.0",
90
90
  "svgo": "^3.0.2",
91
91
  "svgo-loader": "^4.0.0",
92
92
  "version-everything": "^0.11.0",
93
- "webpack": "^5.90.0",
93
+ "webpack": "^5.90.3",
94
94
  "webpack-bundle-analyzer": "^4.9.1",
95
95
  "webpack-cli": "^5.1.4",
96
96
  "webpack-dev-middleware": "^7.0.0",
@@ -102,7 +102,7 @@
102
102
  ]
103
103
  },
104
104
  "devDependencies": {
105
- "@vitest/coverage-v8": "^1.2.2",
106
- "vitest": "^1.2.1"
105
+ "@vitest/coverage-v8": "^1.3.1",
106
+ "vitest": "^1.3.1"
107
107
  }
108
108
  }