@ideasonpurpose/build-tools-wordpress 2.6.5 → 2.7.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/AGENTS.md ADDED
@@ -0,0 +1,68 @@
1
+ ## AI Coding Assistant Guidelines
2
+
3
+ **Mantra:** Minimal. Surgical. Collaborative. Verifiable.
4
+
5
+ ### 0. Communicate like a senior engineer
6
+
7
+ - Start with the answer, no preamble.
8
+ - Match response length to task complexity.
9
+ - Begin with a brief plan: what you understand, assumptions, approach.
10
+ - Call out trade-offs explicitly (simplicity vs future-proofing, readability vs performance).
11
+ - When ambiguous, present 2-3 concrete options with pros/cons instead of guessing.
12
+ - Ask for confirmation on anything unclear.
13
+
14
+ ## 1. Think Before Coding
15
+
16
+ Optimize for simplicity, maintainability, readability.
17
+
18
+ **Don't assume. Don't hide confusion. Offer alternatives.**
19
+
20
+ Before implementing:
21
+
22
+ - State assumptions explicitly. If uncertain, ask.
23
+ - If multiple interpretations exist, offer choices — don't pick silently.
24
+ - If a simpler approach exists, say so and push back.
25
+ - If unclear, stop and name what's confusing.
26
+
27
+ ## 2. Simplicity First
28
+
29
+ **Minimum code that solves the problem. Nothing speculative.**
30
+
31
+ - Delete more than you add.
32
+ - Prefer boring, obvious code over clever code.
33
+ - No features beyond what was asked.
34
+ - No abstractions for single-use code.
35
+ - No "flexibility" or "configurability" that wasn't requested.
36
+ - Only add error handling for realistically possible cases given current scope and style.
37
+ - Never introduce new dependencies unless explicitly asked.
38
+ - If you write 200 lines and it could be 50, rewrite it.
39
+
40
+ Ask yourself: "Would a senior engineer call this overcomplicated?" If yes, simplify.
41
+
42
+ ## 3. Surgical Changes
43
+
44
+ **Touch only what you must. Clean up only your own mess.**
45
+
46
+ When editing existing code:
47
+
48
+ - Preserve the spirit and conventions of the codebase.
49
+ - Don't "improve" adjacent code, comments, or formatting.
50
+ - Don't remove comments or commented-out code.
51
+ - Don't refactor things that aren't broken.
52
+ - Match existing style, even if you'd do it differently.
53
+ - If you notice unrelated dead code, mention it - don't delete it.
54
+
55
+ When your changes create orphans:
56
+
57
+ - Remove imports/variables/functions that _your_ changes made unused.
58
+ - Do not remove pre-existing dead code unless asked.
59
+
60
+ Every changed line should trace directly to the user's request.
61
+
62
+ ## 4. Documentation and Comments
63
+
64
+ - Prefer self-documenting code and descriptive names over comments.
65
+ - Add comments only where the _why_ is not obvious from the code.
66
+ - Update or delete comments that your changes invalidate.
67
+ - Follow the existing codebase's conventions for types, docstrings, and documentation style.
68
+
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
+ #### v2.6.6
8
+
9
+ > 27 April 2026
10
+
11
+ - bump deps
12
+ - feat: add nested list structure and formatting for WordPress block patterns
13
+ - feat: add experimental formatting helpers for HTML/PHP and WordPress block patterns
14
+
15
+ #### v2.6.5
16
+
17
+ > 5 April 2026
18
+
19
+ - fix: update setup-node action version to v6 for consistency
20
+
7
21
  #### v2.6.4
8
22
 
9
23
  > 5 April 2026
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ideasonpurpose/build-tools-wordpress
2
2
 
3
- #### Version 2.6.5
3
+ #### Version 2.7.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)
@@ -41,6 +41,20 @@ Webpack handles SVG files differently based on import context:
41
41
 
42
42
  Data URIs are generated as `data:image/svg+xml,<url-encoded-content>` using more efficient URL-encoding, not base64.
43
43
 
44
+ ## Experimental formatting helpers
45
+
46
+ This package includes two experimental formatting scripts:
47
+
48
+ ### `iop-html-php-prettier`
49
+
50
+ This uses [Prettier](https://prettier.io) to double-format mixed HTML and PHP files (like WordPress templates). PHP blocks are tokenized so Prettier can safely format the file as HTML, then PHP blocks are restored and the file is formatted again as PHP using [@prettier/plugin-php](https://github.com/prettier/plugin-php).
51
+ **Usage:** `npx iop-html-php-prettier path/to/file.php`
52
+
53
+ ### `iop-format-wp-block-pattern`
54
+
55
+ A specialized formatter for WordPress PHP block patterns. This makes block markup easier to read, expands JSON settings, and adjusts whitespace to work around Block Editor quirks.
56
+ **Usage:** `npx iop-format-wp-block-pattern path/to/pattern.php`
57
+
44
58
  ## Local Development
45
59
 
46
60
  Because this project makes use of bin scripts, conventional `npm link` workflows won't work correctly. To work on this code in a development project, change the project's package.json to install from a local file path, probably something like this:
@@ -141,6 +141,17 @@ export function trimInsideListElements(content) {
141
141
  .replace(/\s*<\/li>/g, "</li>");
142
142
  }
143
143
 
144
+ /**
145
+ * Special whitespace handling inside <h1..h6> elements
146
+ * @param {string} content
147
+ * @returns {string}
148
+ */
149
+ export function trimInsideHeadings(content) {
150
+ return content
151
+ .replace(/(<h[1-6][^>]*>)\s*/g, "$1")
152
+ .replace(/\s*(<\/h[1-6]>)/g, "$1");
153
+ }
154
+
144
155
  /**
145
156
  * @param {string} content
146
157
  * @returns {Promise<string>}
@@ -168,6 +179,7 @@ async function formatWPBlockPattern(filepath) {
168
179
  normalizeCommentTagSpacing,
169
180
  formatAllWpComments,
170
181
  trimInsideListElements,
182
+ trimInsideHeadings,
171
183
  normalizeNewlines,
172
184
  ];
173
185
 
@@ -0,0 +1,77 @@
1
+ # Configuration for the Ideas On Purpose WordPress Docker environment,
2
+ # more info here: https://github.com/ideasonpurpose/docker-wordpress-dev
3
+
4
+
5
+ SSH_KEY_PATH=
6
+ # SSH_KEY_PATH should be set to the local path to your private key. For example,
7
+ # if you have public key named `id_ed25519_deploy.pub` associated with your managed
8
+ # WordPress account, SSH_KEY_PATH should point to the pair's matching private
9
+ # key like this: SSH_KEY_PATH="~/.ssh/id_ed25519_deploy"
10
+
11
+
12
+ SSH_LOGIN=
13
+ # SSH_LOGIN should be set to the SSH Login string from WP Engine's or Kinsta's
14
+ # admin backend. They should look something like this:
15
+ # - wpengine: "iop001@iop001.ssh.wpengine.net"
16
+ # - kinsta: "ssh iop001@11.22.33.44 -p 54321"
17
+ # In the above examples the elements map like this:
18
+ # `${SSH_USER}@${SSH_HOST}`
19
+ # Each item can also be entered individually, and individual entries will take
20
+ # precedence over components extracted from SSH_LOGIN.
21
+ # Values should be quoted for compatibility with Docker Compose v2.x
22
+
23
+
24
+ SSH_USER=
25
+ # The user account which connects to the server. For WP Engine, this matches the
26
+ # environment name.
27
+
28
+
29
+ SSH_HOST=
30
+ # The server address to connect to.
31
+
32
+
33
+ SSH_PORT=
34
+ # The server port listening for SSH connections
35
+
36
+
37
+ SSH_WP_CONTENT_DIR=
38
+ # default: sites/${SSH_USER}/wp-content
39
+ # SSH_WP_CONTENT_DIR is the path to the wordpress wp-content directory. This will
40
+ # most likely match the `WP_CONTENT_DIR` WordPress constant and does not include
41
+ # a trailing slash.
42
+ # Path can be relative to the SSH user home folder or an absolute path.
43
+ # Examples:
44
+ # - wpengine: sites/${SSH_USER}/wp-content
45
+ # - kinsta: public/wp-content
46
+
47
+
48
+ IOP_DEV_PLUGIN_1=
49
+ IOP_DEV_PLUGIN_2=
50
+ IOP_DEV_PLUGIN_3=
51
+ IOP_DEV_PLUGIN_4=
52
+ IOP_DEV_PLUGIN_5=
53
+ IOP_DEV_PLUGIN_6=
54
+ IOP_DEV_PLUGIN_7=
55
+ IOP_DEV_PLUGIN_8=
56
+ # Additional development plugins can be mounted into the WordPress environment as
57
+ # individual volumes. The local relative path to the working plugin directory should
58
+ # be pointed to the absolute path inside the container. In the following example,
59
+ # an example-plugin project is being developed in a sibling directory to the theme:
60
+ #
61
+ # IOP_DEV_PLUGIN_1="../example-plugin:/var/www/html/wp-content/plugins/example-plugin"
62
+
63
+
64
+ WORDPRESS_DEBUG=1
65
+ # This is used to set the WP_DEBUG constant to toggle WordPress debugging.
66
+ # .env vars are imported as strings, so true/false will not work correctly.
67
+ # PHP does correctly coerce the integers 0 and 1 to their corresponding boolean values.
68
+ # PHP interprets the number 0 as a string to boolean false
69
+ # Leave this value blank or set it to 0 to disable WP_DEBUG
70
+ # Setting this to 1, true or any other non-empty string enables WP_DEBUG
71
+
72
+
73
+ WP_ENVIRONMENT_TYPE=
74
+ # Blank or one of 'production', 'staging' or 'development'. For IOP's development
75
+ # environments, this value defaults to 'development'. Otherwise WordPress defaults
76
+ # to 'production'. Documentation:
77
+ # https://developer.wordpress.org/reference/functions/wp_get_environment_type/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ideasonpurpose/build-tools-wordpress",
3
- "version": "2.6.5",
3
+ "version": "2.7.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": {
@@ -41,40 +41,38 @@
41
41
  "@rollup/plugin-json": "^6.1.0",
42
42
  "@rollup/plugin-node-resolve": "^16.0.3",
43
43
  "@svgr/webpack": "^8.1.0",
44
- "@wordpress/dependency-extraction-webpack-plugin": "^6.43.0",
44
+ "@wordpress/dependency-extraction-webpack-plugin": "^6.45.0",
45
45
  "ansi-html": "^0.0.9",
46
46
  "archiver": "^7.0.1",
47
- "auto-changelog": "^2.5.0",
48
- "autoprefixer": "^10.4.27",
47
+ "autoprefixer": "^10.5.0",
49
48
  "babel-loader": "^10.1.1",
50
- "caniuse-lite": "^1.0.30001785",
49
+ "caniuse-lite": "^1.0.30001792",
51
50
  "chalk": "^5.6.2",
52
51
  "chalk-cli": "^6.0.0",
53
52
  "classnames": "^2.5.1",
54
53
  "cli-truncate": "^6.0.0",
55
54
  "copy-webpack-plugin": "^14.0.0",
56
55
  "cosmiconfig": "^9.0.1",
57
- "cross-env": "^10.1.0",
58
56
  "css-loader": "^7.1.4",
59
- "cssnano": "^7.1.4",
60
- "dotenv": "^17.4.0",
57
+ "cssnano": "^8.0.0",
58
+ "dotenv": "^17.4.2",
61
59
  "esbuild-loader": "^4.4.3",
62
- "eslint": "^10.2.0",
63
- "filesize": "^11.0.15",
64
- "fs-extra": "^11.3.4",
60
+ "eslint": "^10.3.0",
61
+ "filesize": "^11.0.17",
62
+ "fs-extra": "^11.3.5",
65
63
  "globby": "^16.2.0",
66
- "html-webpack-plugin": "^5.6.6",
64
+ "html-webpack-plugin": "^5.6.7",
67
65
  "http-proxy": "^1.18.1",
68
66
  "humanize-duration": "^3.33.2",
69
67
  "image-minimizer-webpack-plugin": "^5.0.0",
70
68
  "is-text-path": "^3.0.0",
71
69
  "lodash": "^4.18.1",
72
70
  "mini-css-extract-plugin": "^2.10.2",
73
- "ora": "^9.3.0",
74
- "postcss": "^8.5.8",
71
+ "ora": "^9.4.0",
72
+ "postcss": "^8.5.14",
75
73
  "postcss-loader": "^8.2.1",
76
74
  "postcss-scss": "^4.0.9",
77
- "prettier": "^3.8.1",
75
+ "prettier": "^3.8.3",
78
76
  "pretty-hrtime": "^1.0.3",
79
77
  "read-package-up": "^12.0.0",
80
78
  "replacestream": "^4.0.3",
@@ -89,9 +87,9 @@
89
87
  "svgo": "^4.0.1",
90
88
  "svgo-loader": "^5.0.0",
91
89
  "version-everything": "^0.12.2",
92
- "webpack": "^5.105.4",
90
+ "webpack": "^5.106.2",
93
91
  "webpack-bundle-analyzer": "^5.3.0",
94
- "webpack-dev-middleware": "^8.0.2",
92
+ "webpack-dev-middleware": "^8.0.3",
95
93
  "webpack-dev-server": "^5.2.3",
96
94
  "webpack-manifest-plugin": "^6.0.1"
97
95
  },
@@ -99,8 +97,8 @@
99
97
  "webpack-cli": "^6.0.1"
100
98
  },
101
99
  "devDependencies": {
102
- "@vitest/coverage-v8": "^4.1.2",
103
- "vitest": "^4.1.2"
100
+ "@vitest/coverage-v8": "^4.1.5",
101
+ "vitest": "^4.1.5"
104
102
  },
105
103
  "version-everything": {
106
104
  "files": [
@@ -0,0 +1,25 @@
1
+ <!-- wp:list -->
2
+ <ul class="wp-block-list"><!-- wp:list-item -->
3
+ <li> UL Item 1 </li>
4
+ <!-- /wp:list-item -->
5
+
6
+ <!-- wp:list-item -->
7
+ <li>UL Item 2<!-- wp:list -->
8
+ <ul class="wp-block-list"><!-- wp:list-item -->
9
+ <li> UL Second level item 3<!-- wp:list -->
10
+ <ul class="wp-block-list">
11
+
12
+ <!-- wp:list-item -->
13
+ <li>
14
+ UL Third level item 4
15
+ </li>
16
+ <!-- /wp:list-item --></ul>
17
+ <!-- /wp:list --></li>
18
+ <!-- /wp:list-item --></ul>
19
+ <!-- /wp:list --></li>
20
+ <!-- /wp:list-item -->
21
+
22
+ <!-- wp:list-item -->
23
+ <li>UL Item 5</li>
24
+ <!-- /wp:list-item --></ul>
25
+ <!-- /wp:list -->
@@ -0,0 +1,33 @@
1
+ <!-- wp:list -->
2
+ <ul class="wp-block-list">
3
+
4
+ <!-- wp:list-item -->
5
+ <li>UL Item 1</li>
6
+ <!-- /wp:list-item -->
7
+
8
+ <!-- wp:list-item -->
9
+ <li>UL Item 2<!-- wp:list -->
10
+ <ul class="wp-block-list">
11
+
12
+ <!-- wp:list-item -->
13
+ <li>UL Second level item 3<!-- wp:list -->
14
+ <ul class="wp-block-list">
15
+
16
+ <!-- wp:list-item -->
17
+ <li>UL Third level item 4</li>
18
+ <!-- /wp:list-item -->
19
+
20
+ </ul>
21
+ <!-- /wp:list --></li>
22
+ <!-- /wp:list-item -->
23
+
24
+ </ul>
25
+ <!-- /wp:list --></li>
26
+ <!-- /wp:list-item -->
27
+
28
+ <!-- wp:list-item -->
29
+ <li>UL Item 5</li>
30
+ <!-- /wp:list-item -->
31
+
32
+ </ul>
33
+ <!-- /wp:list -->
@@ -37,16 +37,20 @@ describe("Format JSON in WP Block comments", () => {
37
37
  });
38
38
 
39
39
  test("Return short JSON for two-key wp:* comments", async () => {
40
- const input = '<!-- wp:group { "align": "full", "className": "group-name"} -->';
41
- const expected = '<!-- wp:group {"align":"full","className":"group-name"} -->';
40
+ const input =
41
+ '<!-- wp:group { "align": "full", "className": "group-name"} -->';
42
+ const expected =
43
+ '<!-- wp:group {"align":"full","className":"group-name"} -->';
42
44
 
43
45
  const actual = formatWpCommentJson(input);
44
46
  expect(actual).toBe(expected);
45
47
  });
46
48
 
47
49
  test("Prettyprint three-key wp:* comments", async () => {
48
- const input = '<!-- wp:group { "align": "full","isLink":true, "className": "group-name"} -->';
49
- const expected = '<!-- wp:group {\n "align": "full",\n "isLink": true,\n "className": "group-name"\n} -->';
50
+ const input =
51
+ '<!-- wp:group { "align": "full","isLink":true, "className": "group-name"} -->';
52
+ const expected =
53
+ '<!-- wp:group {\n "align": "full",\n "isLink": true,\n "className": "group-name"\n} -->';
50
54
 
51
55
  const actual = formatWpCommentJson(input);
52
56
  expect(actual).toBe(expected);
@@ -85,7 +89,6 @@ describe("Format WP Block Patterns", () => {
85
89
  });
86
90
  });
87
91
 
88
-
89
92
  describe("Normalize whitespace", () => {
90
93
  test("Fix Comment tag spacing", async () => {
91
94
  const input = (
@@ -100,15 +103,18 @@ describe("Normalize whitespace", () => {
100
103
 
101
104
  test("List handling", async () => {
102
105
  const input = (
103
- await readFile(
104
- "./test/fixtures/format-wp-block-pattern/nested-list.php",
105
- )
106
+ await readFile("./test/fixtures/format-wp-block-pattern/nested-list.php")
107
+ ).toString();
108
+
109
+ const expected = (
110
+ await readFile("./test/fixtures/format-wp-block-pattern/nested-list__formatted.php")
106
111
  ).toString();
107
112
 
108
113
  const actual = trimInsideListElements(input);
109
- expect(actual).toMatch(/<\/div>\n<!-- \/wp:group -->/);
114
+ expect(actual).toMatch(/<li>UL/);
115
+
116
+ expect(actual).toBe(expected);
110
117
  });
111
118
  });
112
119
 
113
-
114
- // {"align":"full","className":"group-name"}
120
+ // {"align":"full","className":"group-name"}
@@ -15,6 +15,20 @@ services:
15
15
  MYSQL_PASSWORD: wordpress
16
16
  # command: >
17
17
  # --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
18
+ # Command update from https://x.com/i/grok?conversation=2009819808595165421
19
+ command: >
20
+ --disable-log-bin
21
+ --innodb_flush_log_at_trx_commit=2
22
+ --sync_binlog=0
23
+ --innodb_doublewrite=0
24
+ --slow-query-log=0
25
+ --general-log=0
26
+ --performance_schema=OFF
27
+ logging: # Prevent container logs from bloating on disk
28
+ driver: "json-file"
29
+ options:
30
+ max-size: "10m"
31
+ max-file: "3"
18
32
 
19
33
  # Ideas On Purpose's local WordPress development environment.
20
34
  # Update image version with `docker run ideasonpurpose/wordpress init`
@@ -23,7 +37,7 @@ services:
23
37
  depends_on:
24
38
  - db
25
39
  # image: &wp_img ideasonpurpose/wordpress:dev
26
- image: &wp_img ideasonpurpose/wordpress:6.7.2
40
+ image: &wp_img ideasonpurpose/wordpress:6.9.4
27
41
  restart: always
28
42
  volumes:
29
43
  - wp_data:/var/www/html
@@ -49,8 +63,6 @@ services:
49
63
  - 9003
50
64
 
51
65
  # Link external plugin projects by defining host:container path pairs in .env
52
- # An example .env entry looks like this:
53
- # IOP_DEV_PLUGIN_1="../iop-data-model/:/var/www/html/wp-content/plugins/iop-data-model"
54
66
  environment:
55
67
  IOP_DEV_PLUGIN_1:
56
68
  IOP_DEV_PLUGIN_2:
@@ -60,15 +72,6 @@ services:
60
72
  IOP_DEV_PLUGIN_6:
61
73
  IOP_DEV_PLUGIN_7:
62
74
  IOP_DEV_PLUGIN_8:
63
- WP_ENVIRONMENT_TYPE:
64
- WP_MULTISITE:
65
-
66
- # Apache will throw errors for any ulimit value below 8192
67
- # NOTE THAT THIS MIGHT BE MORE THAN THE SYSTEM OFFERS
68
- # TODO: Still true?
69
- # TODO: Testing removal. 2025-03-18
70
- # ulimits:
71
- # nofile: 8192
72
75
 
73
76
  # Required for iptables port-mapping to work inside the Docker image
74
77
  # This is used to fix a PHP/WordPress issue where internal requests
@@ -76,28 +79,10 @@ services:
76
79
  cap_add:
77
80
  - NET_ADMIN
78
81
 
79
- # # TODO: Deprecated and likely ready to remove 2025-03-18
80
- # # Ideas On Purpose's development toolchain
81
- # # Image from: https://hub.docker.com/r/ideasonpurpose/docker-build
82
- # tools:
83
- # # image: ideasonpurpose/docker-build:dev
84
- # image: ideasonpurpose/docker-build:0.17.2
85
- # user: "${UID:-1000}:${GID:-1000}"
86
- # depends_on:
87
- # - wordpress
88
- # volumes:
89
- # - .:/usr/src/site/
90
- # ports:
91
- # - "${npm_config_port:-8080}:8080"
92
- # environment:
93
- # PORT: ${npm_config_port:-8080}
94
- # entrypoint: npm run
95
- # command: start
96
-
97
82
  # Utility service for running composer commands
98
83
  # Image from: https://hub.docker.com/_/composer
99
84
  composer:
100
- image: composer:2.8
85
+ image: composer:2.9
101
86
  profiles: ["utility"]
102
87
  user: "${UID:-1000}:${GID:-1000}"
103
88
  environment:
@@ -281,17 +266,6 @@ services:
281
266
  secrets:
282
267
  - SSH_KEY
283
268
 
284
- # Run the init script from ideasonpurpose/wordpress:latest (intentionally "latest")
285
- # to refresh tooling and bring the project inline with our boilerplate
286
- # Project info: https://github.com/ideasonpurpose/docker-wordpress-dev
287
- refresh:
288
- # image: ideasonpurpose/wordpress:dev
289
- image: ideasonpurpose/wordpress:latest
290
- profiles: ["utility"]
291
- volumes:
292
- - .:/usr/src/site
293
- command: init
294
-
295
269
  secrets:
296
270
  SSH_KEY:
297
271
  file: ${SSH_KEY_PATH:-~/.ssh/your_ssh_private_key}