diff --git a/node-packages/wp-tooling/CHANGELOG.md b/node-packages/wp-tooling/CHANGELOG.md index 8a77b0d..00bcb4b 100644 --- a/node-packages/wp-tooling/CHANGELOG.md +++ b/node-packages/wp-tooling/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added +- `lint/i18n` scaffold — a standalone `phpcs.i18n.xml.dist` running only `WordPress.WP.I18n` against the project's text domain, plus a `lint:i18n` composer script. Standalone because the `text_domain` property is project-specific and the engine never edits an existing `phpcs.xml.dist`. The `text_domain` input is `required` (discovered from `.wp-tooling.json` `textDomain` when present) rather than defaulted, because the sniff silently loses its `MissingArgDomain` / `TextDomainMismatch` checks when the property is wrong or unset. The rendered ruleset deliberately omits `default` from the allowed domains — listing it downgrades a missing domain argument from the `MissingArgDomain` error to the `MissingArgDomainDefault` warning — with a commented opt-in for projects that intentionally reuse core strings. Companion to the i18n lens skill in `rtcamp/wp-devtools`. - Remote scaffolds — a scaffold's `scaffold.json` + templates can live in another repo. `scaffolds/sources.json` lists the source repos (pinned `{ repository, ref, path }`); each repo publishes a `scaffolds/index.json` enumerating the scaffolds it offers, which the registry fetches to discover them (one PR in the owning repo adds/changes a scaffold; wp-tooling only changes to onboard a new repo). Manifests + templates are fetched on `add`, cached under `${XDG_CACHE_HOME:-$HOME/.cache}/wp-tooling/remote/` and validated with ETag conditional requests (`304 Not Modified` serves the cache; movable tags refresh when they move). New error code `EFETCHFAIL` (network/HTTP) distinct from `EBADSCAFFOLD` (bad index/manifest). `list` is online-preferred with a cache fallback and reports unreachable sources as warnings; `validate --remote` fetches + schema-validates each index + manifest; `wp-tooling cache clear` empties the cache. Dormant by default — no `sources.json` ships. - Engine-side input discovery (`discover_from`) — an input declaration can say where to source its value from the project, so the engine fills it instead of the caller guessing. Resolves from `composer.json` / `package.json` (dotted paths; `autoload.psr-4` yields the root namespace) and `.wp-tooling.json`, with precedence `supplied → discovered → default`. Fail-safe: a missing or malformed project file falls through to the input's `default`, so a project without those files behaves exactly as before the resolver existed. Adds an optional `transform` step for derived inputs (`json-escape` doubles backslashes for the PSR-4 composer key). The inputs the engine actually rendered with are surfaced on `execute()` as `engine.inputs`. Bundled `setup/psr4` + `wp/*` scaffolds annotated with `discover_from`. - Feature toggle layer — a scaffold may declare an optional `feature` block (`config_key`, `owned_files`, `confirm_remove`, `gitignore`) marking it as a toggleable project feature. New TTY-free `enable` / `disable` / `status` verbs create or remove the owned files idempotently, manage `.gitignore` lines (Mustache-rendered against resolved inputs), prompt before deleting consumer-editable files (`confirm_remove`, overridable with `--force`), and persist on/off state in `.wp-tooling.json`. New `wp-tooling features` command — lists feature status by default, with `--enable` / `--disable` to toggle (plus `--json`, `--force`, `--no-install`, `--dry-run`); `setup/tailwind` ships as the first such feature. Additive — the `feature` block never affects the `add` / `execute` path. diff --git a/node-packages/wp-tooling/scaffolds/lint/i18n/scaffold.json b/node-packages/wp-tooling/scaffolds/lint/i18n/scaffold.json new file mode 100644 index 0000000..9b47450 --- /dev/null +++ b/node-packages/wp-tooling/scaffolds/lint/i18n/scaffold.json @@ -0,0 +1,30 @@ +{ + "slug": "i18n", + "category": "lint", + "name": "PHPCS - i18n", + "description": "Standalone PHP_CodeSniffer ruleset running only WordPress.WP.I18n, configured with the project's text domain, plus a `lint:i18n` composer script. Separate from the main phpcs.xml.dist because the text domain is project-specific and the engine never edits existing files.", + "source": "template", + "files": [ + { + "src": "templates/phpcs.i18n.xml.dist.mustache", + "dest": "phpcs.i18n.xml.dist" + } + ], + "inputs": [ + { + "key": "text_domain", + "description": "The project's text domain, exactly as it appears in the plugin header's `Text Domain:` (or the theme's style.css). The sniff cannot report a wrong domain without it, so it is required rather than guessed.", + "discover_from": "config:textDomain", + "required": true + } + ], + "composer_dev_dependencies": { + "squizlabs/php_codesniffer": "^3.7", + "wp-coding-standards/wpcs": "^3.0" + }, + "scripts": { + "composer": { + "lint:i18n": "phpcs --standard=phpcs.i18n.xml.dist" + } + } +} diff --git a/node-packages/wp-tooling/scaffolds/lint/i18n/templates/phpcs.i18n.xml.dist.mustache b/node-packages/wp-tooling/scaffolds/lint/i18n/templates/phpcs.i18n.xml.dist.mustache new file mode 100644 index 0000000..f9441c1 --- /dev/null +++ b/node-packages/wp-tooling/scaffolds/lint/i18n/templates/phpcs.i18n.xml.dist.mustache @@ -0,0 +1,61 @@ + + + Translation-readiness checks: the WordPress.WP.I18n sniff, bound to this project's text domain. + + + + + + + + + + + + + + + + ./ + */vendor/* + */node_modules/* + */build/* + */tests/* + + */.claude/* + + +