@imwz/wp-pattern-sentinel 1.1.1 → 1.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imwz/wp-pattern-sentinel",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Browser-based WordPress block pattern validator using Playwright",
5
5
  "type": "module",
6
6
  "engines": {
package/src/editor.js CHANGED
@@ -113,6 +113,21 @@ export async function insertPatternIntoEditor(page, blockContent, verbose = fals
113
113
  { timeout: 15000 }
114
114
  );
115
115
 
116
+ // Nested `core/pattern` placeholders are swapped for the referenced
117
+ // pattern's blocks asynchronously, once the pattern registry has loaded.
118
+ // Wait for that so the save and the content comparison see the expanded
119
+ // tree. Unregistered slugs never expand, so a timeout here is non-fatal.
120
+ if (/<!-- wp:pattern\s/.test(blockContent)) {
121
+ await page
122
+ .waitForFunction(() => {
123
+ const hasPattern = blocks => blocks.some(
124
+ b => b.name === 'core/pattern' || hasPattern(b.innerBlocks ?? [])
125
+ );
126
+ return !hasPattern(window.wp.data.select('core/block-editor').getBlocks());
127
+ }, { timeout: 10000 })
128
+ .catch(() => {});
129
+ }
130
+
116
131
  if (verbose) log(' → Pattern inserted', 'gray');
117
132
  return true;
118
133
  } catch (error) {
package/src/validation.js CHANGED
@@ -93,6 +93,33 @@ const collapseInterTagWhitespace = str => str.replace(/>\s+</g, '><');
93
93
  const normalizeForComparison = str =>
94
94
  collapseInterTagWhitespace(normalizeBlockAttrJson(normalizeCssProps(stripNavRef(str))));
95
95
 
96
+ /**
97
+ * Matches a nested pattern reference: `<!-- wp:pattern {"slug":"theme/x"} /-->`.
98
+ * The editor replaces these with the referenced pattern's blocks, so the
99
+ * source must be expanded the same way before it can be diffed.
100
+ */
101
+ const PATTERN_REF = /<!-- wp:pattern (\{[\s\S]*?\}) \/-->/g;
102
+
103
+ /** Map of registered pattern name → raw content, read from the REST API. */
104
+ const fetchRegisteredPatterns = page =>
105
+ page.evaluate(async () => {
106
+ const patterns = await window.wp.apiFetch({ path: '/wp/v2/block-patterns/patterns' });
107
+ return Object.fromEntries(patterns.map(p => [p.name, p.content]));
108
+ });
109
+
110
+ /**
111
+ * Recursively substitute each `wp:pattern` reference with the referenced
112
+ * pattern's content. Unregistered slugs and self-references are left as-is,
113
+ * mirroring the editor, which leaves those `core/pattern` blocks unexpanded.
114
+ */
115
+ const expandPatternRefs = (str, registry, seen = []) =>
116
+ str.replace(PATTERN_REF, (match, json) => {
117
+ let slug;
118
+ try { slug = JSON.parse(json).slug; } catch { return match; }
119
+ if (typeof registry[slug] !== 'string' || seen.includes(slug)) return match;
120
+ return expandPatternRefs(registry[slug], registry, [...seen, slug]);
121
+ });
122
+
96
123
  /**
97
124
  * Compare the editor's serialized output against the original source.
98
125
  * Whitespace-normalizes both sides before diffing to avoid false positives
@@ -108,6 +135,17 @@ export async function compareContent(page, originalContent, verbose = false) {
108
135
  );
109
136
  result.savedContent = savedContent;
110
137
 
138
+ if (originalContent.includes('<!-- wp:pattern ')) {
139
+ try {
140
+ originalContent = expandPatternRefs(originalContent, await fetchRegisteredPatterns(page));
141
+ } catch (error) {
142
+ result.warnings.push({
143
+ type: 'pattern_ref_unresolved',
144
+ message: `Could not resolve nested wp:pattern references: ${error.message}`,
145
+ });
146
+ }
147
+ }
148
+
111
149
  const normalize = str => str.replace(/\s+/g, ' ').trim();
112
150
  if (normalize(normalizeForComparison(savedContent)) === normalize(normalizeForComparison(originalContent))) {
113
151
  if (verbose) log(' → Content matches', 'gray');