From cdf60def1ab415e6596e5de86055f59e14941344 Mon Sep 17 00:00:00 2001 From: Pavol Caban Date: Wed, 5 Aug 2026 14:29:04 +0200 Subject: [PATCH] fix(core): emit editor.deps.json where PHP reads it, sorted (release 0.15.8) app/setup-core.php reads WEBENTOR_CORE_PUBLIC_PATH . '/editor.deps.json' -- public/build/editor.deps.json -- and that file has never existed. The build only ever emitted it hashed under assets/ (editor.deps-Bd1lgXQE.json), so File::exists() was always false, $dependencies was always [], and the core editor bundle enqueued with nothing but react/react-dom. All ten WordPress handles it means to declare -- wp-block-editor, wp-blocks, wp-components, wp-compose, wp-data, wp-element, wp-hooks, wp-html-entities, wp-i18n, wp-primitives -- were silently dropped. Same failure class as the frontend wp-i18n dependency bug, which is why it went unnoticed: WP loads most of these on the editor screen anyway, so the missing deps only mattered for load order. The second problem is where that list comes from. @roots/vite-plugin accumulates it in a Set and emits JSON.stringify([...dependencies]) from generateBundle. A Set preserves insertion order, insertion order follows module-transform order, and that is nondeterministic under Rolldown. Four consecutive builds produced four different hashes (Bd1lgXQE, Cwtx5wN7, BY0wt7iZ, Dq_yk3UQ), each rewriting the asset filename and manifest.json. Since core's public/build/ is tracked and published, `pnpm test-release` dirtied the tree on every run and `git diff` could not distinguish a real build change from reordering noise. One fix covers both: a stableEditorDeps() plugin re-emits the asset sorted, with an explicit fileName (opting out of hashing) at the path PHP reads, drops the hashed copy, and repoints the manifest entry -- which would otherwise dangle and keep churning, since its hash derives from the unsorted source. Sorting is safe: WordPress resolves script load order from its own dependency graph, so the order of a deps array carries no meaning. Verified: - Three consecutive builds produce byte-identical manifest.json and editor.deps.json, with no hashed variant left behind. - `pnpm test-release` (which rebuilds core in Phase 1) no longer dirties the tree -- previously it always did. - In the test-site, WEBENTOR_CORE_PUBLIC_PATH . '/editor.deps.json' now resolves and reads all 10 handles; before the fix file_exists() returned false. - scripts/check-blocks-console.mjs against the test-site: 26 blocks inserted, none invalid, frontStatus 200, and editor + frontend console output identical to the pre-change baseline. ("No API Key set for this site" appears intermittently in both states -- two consecutive post-fix runs disagreed on it -- so it is flaky test-site config, unrelated.) Editor-only and transparent to consumers: no API change, no constraint change, no codemod. Pull with `pnpm up @webikon/webentor-core` within the existing ^0.15. Claude-Session: https://claude.ai/code/session_015gnVTZywLgQYkM6Epr2uW2 --- docs/src/compatibility-matrix.md | 1 + packages/webentor-core/CHANGELOG.md | 6 ++ packages/webentor-core/composer.json | 2 +- packages/webentor-core/package.json | 2 +- ...or.deps-Bd1lgXQE.json => editor.deps.json} | 4 +- .../webentor-core/public/build/manifest.json | 2 +- packages/webentor-core/vite.config.js | 63 +++++++++++++++++++ 7 files changed, 75 insertions(+), 5 deletions(-) rename packages/webentor-core/public/build/{assets/editor.deps-Bd1lgXQE.json => editor.deps.json} (98%) diff --git a/docs/src/compatibility-matrix.md b/docs/src/compatibility-matrix.md index 160ead2..e8d4545 100644 --- a/docs/src/compatibility-matrix.md +++ b/docs/src/compatibility-matrix.md @@ -4,6 +4,7 @@ | starterVersion | themeVersion | coreVersion | configsVersion | setupCliVersion | php | node | | --- | --- | --- | --- | --- | --- | --- | +| 2.1.3 | 2.1.3 | 0.15.8 | 1.1.0 | 1.2.0 | >=8.3 | >=20 | | 2.1.3 | 2.1.3 | 0.15.7 | 1.1.0 | 1.2.0 | >=8.3 | >=20 | | 2.1.2 | 2.1.2 | 0.15.7 | 1.1.0 | 1.1.0 | >=8.3 | >=20 | | 2.1.2 | 2.1.2 | 0.15.6 | 1.1.0 | 1.1.0 | >=8.3 | >=20 | diff --git a/packages/webentor-core/CHANGELOG.md b/packages/webentor-core/CHANGELOG.md index 1198ae1..e4d9643 100644 --- a/packages/webentor-core/CHANGELOG.md +++ b/packages/webentor-core/CHANGELOG.md @@ -1,5 +1,11 @@ # Webentor Core Changelog +## 0.15.8 + +- **Fix: the editor script's WordPress dependencies were never applied.** `setup-core.php` reads `public/build/editor.deps.json`, but the build only ever emitted that file hashed under `assets/`, so the path never existed and the core editor bundle enqueued with just `react`/`react-dom` — none of its ten `wp-*` dependencies. It is now emitted, sorted, at the path PHP reads. +- Fixes a related build-output churn: `@roots/vite-plugin` builds that dependency list in a `Set`, whose insertion order follows module-transform order and is nondeterministic under Rolldown. Every rebuild changed the asset hash, filename and `manifest.json`, so `git diff` could not distinguish a real build change from noise. Output is now byte-stable across rebuilds. +- Editor-only and transparent to consumers: `pnpm up @webikon/webentor-core` within the existing `^0.15` range. Verified against the test-site — 26 blocks insert and validate, editor and frontend console output identical to 0.15.7. + ## 0.15.7 - **Pattern Overrides support for Webentor blocks (WordPress 7.0).** WP 7.0 opened Pattern Overrides to custom blocks via a single server-side opt-in, so `e-button` (`button`), `e-accordion` and `e-tab-container` (`title`), `e-image` (`imgId`, `link`), `e-svg` (`imgId`), `e-icon-picker` (`icon`) and `e-gallery` (`images`) now expose their content attributes to Block Bindings — a synced pattern can be reused across pages with per-instance content while the design keeps propagating from the original. The list is filterable via the new `webentor/block_bindings_supported_attributes` filter, and because Pattern Overrides and Block Bindings share it, these attributes also become connectable to post meta. Container blocks are overridden through their inner blocks rather than the container itself; see the new Pattern Overrides guide in the docs. One caveat: WordPress keys bindings by top-level attribute name only, so `e-button`'s single `button` object is overridden as a whole — once an instance overrides a button it keeps its own `variant`/`size`/`icon` and stops inheriting later design changes from the pattern. diff --git a/packages/webentor-core/composer.json b/packages/webentor-core/composer.json index f921405..621b270 100644 --- a/packages/webentor-core/composer.json +++ b/packages/webentor-core/composer.json @@ -1,6 +1,6 @@ { "name": "webikon/webentor-core", - "version": "0.15.7", + "version": "0.15.8", "license": "MIT", "description": "Webentor Core package", "homepage": "https://webikon.sk", diff --git a/packages/webentor-core/package.json b/packages/webentor-core/package.json index eff8073..9365445 100644 --- a/packages/webentor-core/package.json +++ b/packages/webentor-core/package.json @@ -1,7 +1,7 @@ { "name": "@webikon/webentor-core", "homepage": "https://webikon.sk", - "version": "0.15.7", + "version": "0.15.8", "description": "Core functionality and useful utilities for Webentor Stack", "license": "MIT", "author": "Webikon s.r.o.", diff --git a/packages/webentor-core/public/build/assets/editor.deps-Bd1lgXQE.json b/packages/webentor-core/public/build/editor.deps.json similarity index 98% rename from packages/webentor-core/public/build/assets/editor.deps-Bd1lgXQE.json rename to packages/webentor-core/public/build/editor.deps.json index b7120f3..750453a 100644 --- a/packages/webentor-core/public/build/assets/editor.deps-Bd1lgXQE.json +++ b/packages/webentor-core/public/build/editor.deps.json @@ -1,12 +1,12 @@ [ "wp-block-editor", "wp-blocks", - "wp-i18n", "wp-components", "wp-compose", "wp-data", "wp-element", "wp-hooks", "wp-html-entities", + "wp-i18n", "wp-primitives" -] \ No newline at end of file +] diff --git a/packages/webentor-core/public/build/manifest.json b/packages/webentor-core/public/build/manifest.json index a0f4a1a..a43f53f 100644 --- a/packages/webentor-core/public/build/manifest.json +++ b/packages/webentor-core/public/build/manifest.json @@ -4,7 +4,7 @@ "name": "_utils" }, "editor.deps.json": { - "file": "assets/editor.deps-Bd1lgXQE.json", + "file": "editor.deps.json", "src": "editor.deps.json" }, "resources/blocks/e-accordion/script.ts": { diff --git a/packages/webentor-core/vite.config.js b/packages/webentor-core/vite.config.js index 8080859..adf8407 100644 --- a/packages/webentor-core/vite.config.js +++ b/packages/webentor-core/vite.config.js @@ -19,6 +19,66 @@ blocksScripts.forEach((js) => { blockScriptsEntries[normalizedPath.replace('.ts', '')] = normalizedPath; }); +/** + * `@roots/vite-plugin` emits editor.deps.json from a Set, so its order follows + * module-transform order — nondeterministic under Rolldown. That churned the + * asset hash, filename and manifest on every rebuild, making `git diff` useless + * for telling a real build change from noise. + * + * It also emits the file hashed under assets/, while app/setup-core.php reads a + * fixed `public/build/editor.deps.json` — a path that never existed, so the + * editor bundle silently enqueued with no wp-* dependencies at all. + * + * Re-emitting it sorted at that fixed path fixes both: stable bytes, and a file + * PHP can actually find. Order is irrelevant to WordPress, which resolves script + * load order from its own dependency graph. + */ +function stableEditorDeps() { + return { + name: 'webentor-stable-editor-deps', + generateBundle: { + order: 'post', + handler(_options, bundle) { + const key = Object.keys(bundle).find( + (k) => + bundle[k].originalFileName === 'editor.deps.json' || + bundle[k].name === 'editor.deps.json', + ); + if (!key) { + this.warn( + 'editor.deps.json was not emitted — @roots/vite-plugin may have changed.', + ); + return; + } + + const deps = JSON.parse(bundle[key].source); + delete bundle[key]; + + this.emitFile({ + type: 'asset', + // fileName (not name) opts out of hashing — PHP reads this exact path. + fileName: 'editor.deps.json', + source: `${JSON.stringify([...deps].sort(), null, 2)}\n`, + }); + + // The manifest still points at the hashed asset just dropped. Left alone + // it would both dangle and keep churning, since its hash comes from the + // unsorted source. + const manifest = bundle['manifest.json']; + if (!manifest) { + this.warn('manifest.json not in bundle — editor.deps entry left stale.'); + return; + } + const entries = JSON.parse(manifest.source); + if (entries['editor.deps.json']) { + entries['editor.deps.json'].file = 'editor.deps.json'; + manifest.source = JSON.stringify(entries, null, 2); + } + }, + }, + }; +} + export default defineConfig(({ command }) => ({ publicDir: 'public-assets', plugins: [ @@ -46,6 +106,9 @@ export default defineConfig(({ command }) => ({ // roots wordpressPlugin + interop shims for the build, kucrut wp_scripts() for the dev server. // See @webikon/webentor-configs/vite. ...wordpressExternals(command), + + // Must come after wordpressExternals — it rewrites what roots' plugin emits. + stableEditorDeps(), ], optimizeDeps: { // Fix imports from webpack built libraries