Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion php-transformer/src/ArtifactCompiler/ArtifactCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public function compile(array $artifact): TransformerResult
);
$sourceReports['compiled_site'] = $this->compiledSiteReport($normalized, $entryPath, $documents['documents'], $assets, $blockTypes, $serializedBlocks);
$sourceReports['materialization_plan'] = ( new MaterializationPlanBuilder() )->fromCompiledSite($sourceReports['compiled_site']);
$sourceReports['runtime_dependency_parity'] = ( new RuntimeDependencyParityReport() )->fromArtifact($normalized['files'], $html, $serializedBlocks);
$sourceReports['runtime_dependency_parity'] = ( new RuntimeDependencyParityReport() )->fromArtifact($normalized['files'], $html, $serializedBlocks, $entryPath);
$provenance = array(
array(
'source_format' => 'artifact',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ final class RuntimeDependencyParityReport
* @param array<int, array<string, mixed>> $files
* @return array<string, mixed>
*/
public function fromArtifact(array $files, string $sourceHtml, string $generatedHtml): array
public function fromArtifact(array $files, string $sourceHtml, string $generatedHtml, string $sourcePath = ''): array
{
$sourceTargets = $this->sourceTargets($sourceHtml);
$sourceTargets = $this->sourceTargets($sourceHtml, $sourcePath);
$generatedTargets = $this->htmlTargets($generatedHtml);
$dependencies = array();
$findings = array();
Expand All @@ -39,9 +39,12 @@ public function fromArtifact(array $files, string $sourceHtml, string $generated
$exists = $this->targetExists($dependency, $generatedTargets);
$canvasApi = true === $dependency['canvas_api'] && 'canvas' === ($target['tag'] ?? '');
$dependencyRow = array_filter(array(
'source_path' => $target['source_path'] ?? $sourcePath,
'script_path' => $scriptPath,
'script_kind' => $scriptKind,
'selector' => $selector,
'target_id' => $target['id'] ?? '',
'target_class' => $target['class'] ?? '',
'target_kind' => $target['tag'] ?? '',
'dependency_kind' => $dependency['kind'],
'events' => $dependency['events'],
Expand All @@ -56,16 +59,24 @@ public function fromArtifact(array $files, string $sourceHtml, string $generated
}

$severity = 'telemetry' === $scriptKind ? 'info' : 'warning';
$repairBucket = $canvasApi ? 'runtime_canvas_target_preservation' : 'runtime_dom_target_preservation';
$findings[] = array_filter(array(
'code' => 'runtime_dependency_target_missing',
'severity' => $severity,
'source_path' => $target['source_path'] ?? $sourcePath,
'script_path' => $scriptPath,
'script_kind' => $scriptKind,
'selector' => $selector,
'target_id' => $target['id'] ?? '',
'target_class' => $target['class'] ?? '',
'target_kind' => $target['tag'] ?? '',
'dependency_kind' => $dependency['kind'],
'events' => $dependency['events'],
'canvas_api' => $canvasApi,
'repair_bucket' => $repairBucket,
'suggested_primitive' => $canvasApi ? 'runtime_canvas' : 'runtime_dom_target',
'actionability' => $canvasApi ? 'preserve_canvas_markup_with_matching_script_runtime_or_rebuild_canvas_behavior' : 'preserve_or_recreate_the_referenced_dom_target_for_script_runtime',
'materialization_hint' => $canvasApi ? 'preserve_canvas_id_class_and_markup_for_runtime_mapping' : 'preserve_id_class_or_wrapper_markup_required_by_first_party_script',
'message' => sprintf('Script %s references %s, but the generated block markup does not expose that DOM target.', $scriptPath, $selector),
), static fn (mixed $value): bool => null !== $value && '' !== $value && array() !== $value);
}
Expand All @@ -90,9 +101,9 @@ private function isScriptFile(array $file): bool
}

/**
* @return array<string, array{tag: string}>
* @return array<string, array{tag: string, source_path: string, id?: string, class?: string}>
*/
private function sourceTargets(string $html): array
private function sourceTargets(string $html, string $sourcePath): array
{
$targets = array();
$document = new DOMDocument();
Expand All @@ -111,11 +122,11 @@ private function sourceTargets(string $html): array
$tag = strtolower($element->tagName);
$id = trim($element->hasAttribute('id') ? $element->getAttribute('id') : '');
if ( '' !== $id ) {
$targets['#' . $id] = array('tag' => $tag);
$targets['#' . $id] = array('tag' => $tag, 'source_path' => $sourcePath, 'id' => $id);
}
foreach ( preg_split('/\s+/', trim($element->hasAttribute('class') ? $element->getAttribute('class') : '')) ?: array() as $class ) {
if ( '' !== $class ) {
$targets['.' . $class] = array('tag' => $tag);
$targets['.' . $class] = array('tag' => $tag, 'source_path' => $sourcePath, 'class' => $class);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion php-transformer/src/HtmlToBlocks/HtmlTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,7 @@ private function convertElement(DOMElement $element, array &$fallbacks, bool $ca

if ( 'button' === $tagName ) {
if ( $this->isNonContentRuntimeControl($element) ) {
return null;
return $this->createBlock('core/html', array( 'content' => $this->outerHtml($element) ), array(), $element);
}

return $this->buttonsPattern->matchButton(
Expand Down
13 changes: 13 additions & 0 deletions php-transformer/tests/contract/run.php
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,12 @@ function serialize_blocks(array $blocks): string
$assert(in_array('carousel', $interactionKinds, true), 'HTML source report detects carousel-ish interactions');
$assert('#panel' === ($interactions['source_reports']['interaction_candidates'][0]['target'] ?? ''), 'control interaction candidate exposes aria-controls target');

$emptyRuntimeControl = ( new HtmlTransformer() )->transform(
'<main><button class="nav-toggle" aria-label="Open navigation" aria-expanded="false"><span></span><span></span><span></span></button></main>'
)->toArray();
$assert(str_contains((string) ($emptyRuntimeControl['serialized_blocks'] ?? ''), 'nav-toggle'), 'empty runtime control button class is preserved for scripts');
$assert(str_contains((string) ($emptyRuntimeControl['serialized_blocks'] ?? ''), 'aria-expanded="false"'), 'empty runtime control button ARIA state is preserved');

$assetMetadataOptions = array(
'context' => array(
'asset_metadata' => array(
Expand Down Expand Up @@ -612,10 +618,17 @@ function serialize_blocks(array $blocks): string
$assert('blocks-engine/php-transformer/runtime-dependency-parity/v1' === ($runtimeDependencyReport['schema'] ?? ''), 'runtime dependency parity report exposes schema');
$assert($runtimeDependencyReport === $runtimeDependencyConversionReport, 'conversion report projects runtime dependency parity');
$assert('runtime_dependency_target_missing' === ($canvasFinding['code'] ?? ''), 'runtime dependency parity reports missing canvas DOM target');
$assert('index.html' === ($canvasFinding['source_path'] ?? ''), 'runtime dependency parity reports source path for missing canvas DOM target');
$assert('canvas' === ($canvasFinding['target_id'] ?? ''), 'runtime dependency parity reports missing canvas target id');
$assert('canvas' === ($canvasFinding['target_kind'] ?? ''), 'runtime dependency parity identifies canvas source target kind');
$assert(true === ($canvasFinding['canvas_api'] ?? null), 'runtime dependency parity flags canvas 2d API usage');
$assert('warning' === ($canvasFinding['severity'] ?? ''), 'first-party missing runtime dependency target is warning severity');
$assert('runtime_canvas_target_preservation' === ($canvasFinding['repair_bucket'] ?? ''), 'runtime dependency parity reports repair bucket for missing canvas DOM target');
$assert('runtime_canvas' === ($canvasFinding['suggested_primitive'] ?? ''), 'runtime dependency parity reports suggested primitive for missing canvas DOM target');
$assert(isset($canvasFinding['actionability']) && '' !== $canvasFinding['actionability'], 'runtime dependency parity reports actionability for missing canvas DOM target');
$assert(isset($canvasFinding['materialization_hint']) && '' !== $canvasFinding['materialization_hint'], 'runtime dependency parity reports materialization hint for missing canvas DOM target');
$assert(null !== $statusDependency, 'runtime dependency parity records preserved status container dependency');
$assert('index.html' === ($statusDependency['source_path'] ?? ''), 'runtime dependency parity records source path for preserved DOM dependency');
$assert(true === ($statusDependency['generated_present'] ?? null), 'runtime dependency parity passes preserved div id target');
$assert(! empty($statusDependency['events'] ?? array()), 'runtime dependency parity records simple addEventListener usage');
$assert('info' === ($rumFinding['severity'] ?? ''), 'telemetry-like runtime dependency misses are info severity');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schema": "blocks-engine/php-transformer/parity-fixture/v1",
"name": "html-address-and-toggle-controls",
"description": "Preserves semantic address content and omits empty runtime-only toggle controls from static block output.",
"description": "Preserves semantic address content and keeps empty runtime-only toggle controls available for scripts.",
"source_reference": {
"repo": "php-transformer",
"path": "tests/fixtures/parity/html-address-and-toggle-controls.json",
Expand All @@ -16,8 +16,10 @@
"content": "<header><button class=\"hamburger\" aria-label=\"Open navigation menu\" aria-expanded=\"false\" aria-controls=\"site-menu\"><span></span><span></span><span></span></button><nav><ul id=\"site-menu\"><li><a href=\"/about\">About</a></li></ul></nav></header><footer><address class=\"footer-address\">48 Elm Street<br><a href=\"mailto:hello@example.com\">hello@example.com</a></address></footer>"
},
"expected_blocks": [
{ "path": "blocks.0", "name": "core/navigation" },
{ "path": "blocks.0.innerBlocks.0", "name": "core/navigation-link", "attrs": { "label": "About", "url": "/about" } },
{ "path": "blocks.0", "name": "core/group" },
{ "path": "blocks.0.innerBlocks.0", "name": "core/html" },
{ "path": "blocks.0.innerBlocks.1", "name": "core/navigation" },
{ "path": "blocks.0.innerBlocks.1.innerBlocks.0", "name": "core/navigation-link", "attrs": { "label": "About", "url": "/about" } },
{ "path": "blocks.1", "name": "core/paragraph", "attrs": { "className": "footer-address", "content": "48 Elm Street<br><a href=\"mailto:hello@example.com\">hello@example.com</a>" } }
],
"expected_fallbacks": [],
Expand All @@ -28,7 +30,8 @@
{ "path": "source_reports.interaction_candidates.0.target", "assert": "equals", "value": "#site-menu" },
{ "path": "serialized_blocks", "assert": "contains", "value": "footer-address" },
{ "path": "serialized_blocks", "assert": "contains", "value": "hello@example.com" },
{ "path": "serialized_blocks", "assert": "not_contains", "value": "Open navigation menu" },
{ "path": "serialized_blocks", "assert": "contains", "value": "Open navigation menu" },
{ "path": "serialized_blocks", "assert": "contains", "value": "aria-controls=\"site-menu\"" },
{ "path": "serialized_blocks", "assert": "not_contains", "value": "wp:button" },
{ "path": "coverage.0.fallback_count", "assert": "equals", "value": 0 }
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,25 @@
{ "path": "blocks.0", "name": "core/group", "attrs": { "className": "site-header" } },
{ "path": "blocks.0.innerBlocks.0", "name": "core/group", "attrs": { "className": "nav-inner" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.0", "name": "core/paragraph", "attrs": { "className": "nav-logo" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1", "name": "core/navigation", "attrs": { "className": "nav-links" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1.innerBlocks.0", "name": "core/navigation-link", "attrs": { "label": "Curriculum", "url": "/curriculum", "kind": "custom" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1.innerBlocks.1", "name": "core/navigation-link", "attrs": { "label": "Pricing", "url": "/pricing", "kind": "custom" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1.innerBlocks.2", "name": "core/navigation-link", "attrs": { "label": "Enroll Now", "url": "/enroll", "kind": "custom" } }
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1", "name": "core/html" },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.2", "name": "core/navigation", "attrs": { "className": "nav-links" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.2.innerBlocks.0", "name": "core/navigation-link", "attrs": { "label": "Curriculum", "url": "/curriculum", "kind": "custom" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.2.innerBlocks.1", "name": "core/navigation-link", "attrs": { "label": "Pricing", "url": "/pricing", "kind": "custom" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.2.innerBlocks.2", "name": "core/navigation-link", "attrs": { "label": "Enroll Now", "url": "/enroll", "kind": "custom" } }
],
"expected_fallbacks": [],
"expect": [
{ "path": "status", "assert": "equals", "value": "success" },
{ "path": "blocks", "assert": "count", "count": 1 },
{ "path": "blocks.0.innerBlocks.0.innerBlocks", "assert": "count", "count": 2 },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1.innerBlocks", "assert": "count", "count": 3 },
{ "path": "blocks.0.innerBlocks.0.innerBlocks", "assert": "count", "count": 3 },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.2.innerBlocks", "assert": "count", "count": 3 },
{ "path": "fallbacks", "assert": "count", "count": 0 },
{ "path": "serialized_blocks", "assert": "contains", "value": "<!-- wp:navigation" },
{ "path": "serialized_blocks", "assert": "contains", "value": "<!-- wp:navigation-link" },
{ "path": "serialized_blocks", "assert": "contains", "value": "nav-toggle" },
{ "path": "serialized_blocks", "assert": "not_contains", "value": "<!-- wp:list {\"className\":\"nav-links\"}" },
{ "path": "source_reports.html.source_provenance.4.block_name", "assert": "equals", "value": "core/navigation-link" },
{ "path": "source_reports.html.source_provenance.4.selector", "assert": "equals", "value": "header:nth-of-type(1) > nav:nth-of-type(1) > ul:nth-of-type(1) > li:nth-of-type(1) > a:nth-of-type(1)" },
{ "path": "source_reports.html.source_provenance.5.block_name", "assert": "equals", "value": "core/navigation-link" },
{ "path": "source_reports.html.source_provenance.5.selector", "assert": "equals", "value": "header:nth-of-type(1) > nav:nth-of-type(1) > ul:nth-of-type(1) > li:nth-of-type(1) > a:nth-of-type(1)" },
{ "path": "coverage.0.fallback_count", "assert": "equals", "value": 0 }
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@
{ "path": "blocks.0", "name": "core/group", "attrs": { "className": "site-nav" } },
{ "path": "blocks.0.innerBlocks.0", "name": "core/group", "attrs": { "className": "nav-inner" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.0", "name": "core/paragraph", "attrs": { "className": "nav-logo" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1", "name": "core/navigation", "attrs": { "className": "nav-menu" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1.innerBlocks.0", "name": "core/navigation-link", "attrs": { "label": "Home", "url": "index.html", "kind": "custom", "anchorClassName": "nav-link" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1.innerBlocks.4", "name": "core/navigation-link", "attrs": { "label": "Join Us", "url": "membership.html", "kind": "custom", "anchorClassName": "nav-cta" } }
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1", "name": "core/html" },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.2", "name": "core/navigation", "attrs": { "className": "nav-menu" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.2.innerBlocks.0", "name": "core/navigation-link", "attrs": { "label": "Home", "url": "index.html", "kind": "custom", "anchorClassName": "nav-link" } },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.2.innerBlocks.4", "name": "core/navigation-link", "attrs": { "label": "Join Us", "url": "membership.html", "kind": "custom", "anchorClassName": "nav-cta" } }
],
"expected_fallbacks": [],
"expect": [
{ "path": "status", "assert": "equals", "value": "success" },
{ "path": "blocks", "assert": "count", "count": 1 },
{ "path": "blocks.0.innerBlocks.0.innerBlocks", "assert": "count", "count": 2 },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.1.innerBlocks", "assert": "count", "count": 5 },
{ "path": "blocks.0.innerBlocks.0.innerBlocks", "assert": "count", "count": 3 },
{ "path": "blocks.0.innerBlocks.0.innerBlocks.2.innerBlocks", "assert": "count", "count": 5 },
{ "path": "serialized_blocks", "assert": "not_contains", "value": "<!-- wp:navigation-submenu" },
{ "path": "serialized_blocks", "assert": "contains", "value": "hamburger" },
{ "path": "fallbacks", "assert": "count", "count": 0 }
]
}
Loading