@ideasonpurpose/build-tools-wordpress 2.6.6 → 2.8.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.
@@ -13,7 +13,7 @@ jobs:
13
13
 
14
14
  steps:
15
15
  # https://github.com/marketplace/actions/checkout
16
- - uses: actions/checkout@v4
16
+ - uses: actions/checkout@v6
17
17
 
18
18
  - name: Set TAG environment variable
19
19
  run: |
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,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
+ #### v2.7.0
8
+
9
+ > 6 May 2026
10
+
11
+ - bump deps, migrate some tooling
12
+ - feat: add AI Coding Assistant guidelines documentation
13
+
14
+ #### v2.6.6
15
+
16
+ > 27 April 2026
17
+
18
+ - bump deps
19
+ - feat: add nested list structure and formatting for WordPress block patterns
20
+ - feat: add experimental formatting helpers for HTML/PHP and WordPress block patterns
21
+
7
22
  #### v2.6.5
8
23
 
9
24
  > 5 April 2026
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ideasonpurpose/build-tools-wordpress
2
2
 
3
- #### Version 2.6.6
3
+ #### Version 2.8.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,10 @@ 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
+ ### SVG Optimization
45
+
46
+ SVGO is included along with our preferred [svgo.config.mjs][]. Run this with `npx svgo <file-to-optimize.svg>`
47
+
44
48
  ## Experimental formatting helpers
45
49
 
46
50
  This package includes two experimental formatting scripts:
@@ -94,6 +98,7 @@ A GitHub action will auto-publish version-tagged releases to npm. In order to pu
94
98
 
95
99
  #### Brought to you by IOP
96
100
 
97
- <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>.
101
+ | <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> | This project is actively developed and used in production at <a href="https://www.ideasonpurpose.com">Ideas On Purpose</a>. |
102
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
98
103
 
99
104
  <!-- END IOP CREDIT BLURB -->
@@ -141,7 +141,7 @@ export function unTokenizeHTML(tokenizedHTML, phpCodeBlocks) {
141
141
  *
142
142
  * @param {string} filepath - The path to the file to format (must be a valid file path).
143
143
  */
144
- async function formatHTMLThenPHP(filepath) {
144
+ export async function formatHTMLThenPHP(filepath) {
145
145
  try {
146
146
  const startTime = process.hrtime.bigint();
147
147
  const rawFile = await readFile(filepath, "utf8");
@@ -175,9 +175,11 @@ async function formatHTMLThenPHP(filepath) {
175
175
  }
176
176
  }
177
177
 
178
- if (process.argv[2]) {
179
- const fullPath = resolve(process.argv[2]);
180
- formatHTMLThenPHP(fullPath);
181
- } else {
182
- console.error("Error: A filepath is required.");
178
+ export async function main(filepath = process.argv[2]) {
179
+ if (!filepath) {
180
+ console.error("Error: A filepath is required.");
181
+ return;
182
+ }
183
+ await formatHTMLThenPHP(resolve(filepath));
183
184
  }
185
+ if (import.meta.url === `file://${process.argv[1]}`) main();
@@ -108,6 +108,16 @@ export function normalizeCommentTagSpacing(content) {
108
108
  /** @param {string} match @param {string} p1 */ (match, p1) =>
109
109
  `>\n${p1}\n`,
110
110
  );
111
+
112
+ if (block === "wp:paragraph") {
113
+ newContent = newContent
114
+ .replace(/\n\s*<p>\s*/g, "\n<p>")
115
+ .replace(/\s*<\/p>/g, "</p>")
116
+ .replace(/<p>[\s\S]*?<\/p>/g, (match) =>
117
+ match.replace(/\s+/g, " ").trim(),
118
+ );
119
+ newContent = newContent.replace(/\n{3,}/g, "\n\n").replace(/\s+$/, "\n");
120
+ }
111
121
  });
112
122
  return newContent;
113
123
  }
@@ -169,38 +179,36 @@ export function formatWithPrettier(content) {
169
179
  /**
170
180
  * @param {String} filepath
171
181
  */
172
- async function formatWPBlockPattern(filepath) {
173
- try {
174
- const startTime = process.hrtime.bigint();
175
- const rawFile = await readFile(filepath, "utf8");
176
-
177
- const formatters = [
178
- formatWithPrettier,
179
- normalizeCommentTagSpacing,
180
- formatAllWpComments,
181
- trimInsideListElements,
182
- trimInsideHeadings,
183
- normalizeNewlines,
184
- ];
185
-
186
- const outputHtml = await formatters.reduce(
187
- async (acc, fn) => fn(await acc),
188
- Promise.resolve(rawFile),
189
- );
182
+ export async function formatWPBlockPattern(filepath) {
183
+ const startTime = process.hrtime.bigint();
184
+ const rawFile = await readFile(filepath, "utf8");
185
+
186
+ const formatters = [
187
+ formatWithPrettier,
188
+ normalizeCommentTagSpacing,
189
+ formatAllWpComments,
190
+ trimInsideListElements,
191
+ trimInsideHeadings,
192
+ normalizeNewlines,
193
+ ];
190
194
 
191
- await writeFile(filepath, outputHtml, "utf8");
192
- const endTime = process.hrtime.bigint();
193
- const duration = Number(endTime - startTime);
195
+ const outputHtml = await formatters.reduce(
196
+ async (acc, fn) => fn(await acc),
197
+ Promise.resolve(rawFile),
198
+ );
194
199
 
195
- console.log(`${basename(filepath)} ${(duration / 1e6).toFixed(2)}ms`);
196
- } catch (error) {
197
- console.error("Error:", error);
198
- }
200
+ await writeFile(filepath, outputHtml, "utf8");
201
+ const endTime = process.hrtime.bigint();
202
+ const duration = Number(endTime - startTime);
203
+
204
+ console.log(`${basename(filepath)} ${(duration / 1e6).toFixed(2)}ms`);
199
205
  }
200
206
 
201
- if (process.argv[2]) {
202
- const fullPath = resolve(process.argv[2]);
203
- formatWPBlockPattern(fullPath);
204
- } else {
205
- console.error("Error: A filepath is required.");
207
+ export async function main(filepath = process.argv[2]) {
208
+ if (!filepath) {
209
+ console.error("Error: A filepath is required.");
210
+ return;
211
+ }
212
+ await formatWPBlockPattern(resolve(filepath));
206
213
  }
214
+ if (import.meta.url === `file://${process.argv[1]}`) main();
@@ -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/
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "ideasonpurpose/iop-theme",
3
+ "description": "Block-based hybrid theme for the Stone Rabbit NYC website.",
4
+ "authors": [
5
+ {
6
+ "name": "Ideas On Purpose",
7
+ "homepage": "https://www.ideasonpurpose.com"
8
+ }
9
+ ],
10
+ "config": {
11
+ "optimize-autoloader": true,
12
+ "sort-packages": true,
13
+ "vendor-dir": "wp-content/themes/iop-theme/vendor",
14
+ "platform": {
15
+ "php": "8.4",
16
+ "ext-intl": "0"
17
+ }
18
+ },
19
+ "autoload": {
20
+ "psr-4": {
21
+ "IdeasOnPurpose\\": [
22
+ "wp-content/themes/iop-theme/lib"
23
+ ]
24
+ }
25
+ },
26
+ "require": {
27
+ "ideasonpurpose/wp-admin-separators": "1.0.8",
28
+ "ideasonpurpose/wp-google-analytics": "^1.1.0",
29
+ "ideasonpurpose/wp-svg-lib": "^3.2.0",
30
+ "ideasonpurpose/wp-theme-i18n": "dev-main",
31
+ "ideasonpurpose/wp-theme-init": "^2.20.5"
32
+ },
33
+ "repositories": [
34
+ {
35
+ "type": "vcs",
36
+ "url": "https://github.com/ideasonpurpose/wp-theme-i18n"
37
+ }
38
+ ],
39
+ "require-dev": {
40
+ "php-stubs/acf-pro-stubs": "^6.5"
41
+ }
42
+ }
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "iop-theme",
3
+ "version": "0.1.10",
4
+ "private": true,
5
+ "description": "Theme for the CLIENT_NAME website",
6
+ "license": "UNLICENSED",
7
+ "author": "Ideas On Purpose (https://www.ideasonpurpose.com/)",
8
+ "contributors": [
9
+ "Joe Maller <joe@ideasonpurpose.com>",
10
+ "Codrin Pavel <codrin@ideasonpurpose.com>"
11
+ ],
12
+ "type": "module",
13
+ "main": "index.js",
14
+ "scripts": {
15
+ "_bootstrap:composer": "npm run composer:install",
16
+ "_bootstrap:npm": "npm ci",
17
+ "prebootstrap": "npm run permissions:repair",
18
+ "bootstrap": "npm run _bootstrap:npm && npm run _bootstrap:composer && npm run theme:activate",
19
+ "postbootstrap": "echo && echo ' 🚀' && echo ' ✨ All set!' && echo '🌏 Run this to get started:' && chalk bold yellow ' npm run start' && echo",
20
+ "prebuild": "npm run clean",
21
+ "build": "NODE_ENV=production webpack",
22
+ "postbuild": "npm run zip",
23
+ "clean": "npx rimraf -g 'wp-content/themes/iop*/dist'",
24
+ "composer": "npm run composer:install",
25
+ "composer:install": "docker compose run --rm composer",
26
+ "composer:require": "docker compose run --rm composer require",
27
+ "composer:update": "docker compose run --rm composer update",
28
+ "db:admin": "docker compose run --rm --service-ports phpmyadmin",
29
+ "db:dump": "docker compose run --rm db-dump",
30
+ "db:pull": "npm run pull:db",
31
+ "db:reload": "docker compose run --rm db-reload",
32
+ "postdb:reload": "npm run theme:activate",
33
+ "dev": "npm run start",
34
+ "log:wordpress": "npm run logs:wordpress",
35
+ "logs:wordpress": "docker compose exec wordpress tail -f /var/log/wordpress/debug.log",
36
+ "mysqldump": "npm run db:dump",
37
+ "permissions:repair": "docker compose run --rm repair-permissions",
38
+ "phpmyadmin": "npm run db:admin",
39
+ "preproject:refresh": "docker compose pull refresh",
40
+ "project:refresh": "docker compose run --rm refresh",
41
+ "postproject:refresh": "docker compose pull wordpress",
42
+ "pull": "npm run pull:db && npm run pull:plugins && npm run pull:uploads",
43
+ "prepull:db": "npm run db:dump",
44
+ "pull:db": "docker compose run --rm pull database",
45
+ "postpull:db": "npm run db:reload",
46
+ "pull:plugins": "docker compose run --rm pull plugins",
47
+ "pull:uploads": "docker compose run --rm pull uploads",
48
+ "pull:uploads-all": "docker compose run --rm pull uploads all",
49
+ "prestart": "npm run clean && docker compose run --rm --service-ports -d wordpress && iop-build-port-reporter",
50
+ "start": "webpack serve",
51
+ "poststart": "npm run stop",
52
+ "stop": "docker compose down --remove-orphans",
53
+ "test": "playwright test --ui",
54
+ "theme:activate": "docker compose run --rm theme-activate",
55
+ "version": "version-everything && git add -u",
56
+ "postversion": "npm run build",
57
+ "webgrind": "docker compose run --rm --service-ports webgrind",
58
+ "wp-cli": "docker compose exec -u wp wordpress",
59
+ "zip": "iop-build-zip-archive"
60
+ },
61
+ "prettier": "@ideasonpurpose/prettier-config",
62
+ "stylelint": {
63
+ "extends": "@ideasonpurpose/stylelint-config"
64
+ },
65
+ "devDependencies": {
66
+ "@ideasonpurpose/build-tools-wordpress": "^2.5.1"
67
+ },
68
+ "version-everything": {
69
+ "files": [
70
+ "README.md",
71
+ "wp-content/themes/iop-theme/style.css"
72
+ ]
73
+ }
74
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * This is primarily intended to work with the SVGO VS Code Plugin, and to
3
+ * save trips out to SVGOMG.
4
+ *
5
+ * This config allows for immediate optimization of SVGs in the editor
6
+ *
7
+ *
8
+ * @links
9
+ * - SVGO: https://github.com/svg/svgo
10
+ * - SVGO preset-default docs: https://svgo.dev/docs/preset-default
11
+ * - VS Code Plugin: https://marketplace.visualstudio.com/items?itemName=1000ch.svgo
12
+ * - SVGOMG: https://jakearchibald.github.io/svgomg/
13
+ */
14
+
15
+ export default {
16
+ js2svg: {
17
+ indent: 4, // number
18
+ pretty: true, // boolean
19
+ },
20
+
21
+ plugins: [
22
+ {
23
+ name: "preset-default",
24
+ params: {
25
+ overrides: {
26
+ cleanupIds: false,
27
+ },
28
+ },
29
+ },
30
+ "removeDimensions",
31
+ ],
32
+ };