Skip to content
Snippets Groups Projects
Commit a7304718 authored by Jean Valverde's avatar Jean Valverde :construction_site: Committed by Pierre Dureau
Browse files

Issue #3464630 by mogtofu33, pdureau: [2.0.0-beta1] Component validator: Add...

Issue #3464630 by mogtofu33, pdureau: [2.0.0-beta1] Component validator: Add blocks and attributes to variables checks
parent 85b7a732
Branches
Tags
1 merge request!176Issue #3464630 by mogtofu33, pdureau: [2.0.0-beta1] Component validator: Add blocks and attributes to variables checks
Pipeline #249207 passed
Showing
with 52 additions and 14 deletions
<div style="background-color:#fcf4f2; color:#a51b00; padding:10px;">
<div {{ attributes }}style="background-color:#fcf4f2; color:#a51b00; padding:10px;">
<p>
<b>{{ type }}</b>: {{ message }}<br>
{{ file }} line {{ line }}
......
......@@ -76,7 +76,7 @@ final class UiPatternsDevelCommands extends DrushCommands {
])]
#[CLI\DefaultTableFields(fields: ['component', 'severity', 'message', 'Type', 'line', 'source'])]
#[CLI\FilterDefaultField(field: 'component')]
public function uiPatternsDevelValidate(string $project, string $id = NULL, array $options = ['install' => FALSE]): RowsOfFields {
public function uiPatternsDevelValidate(string $project, ?string $id = NULL, array $options = ['install' => FALSE]): RowsOfFields {
if (FALSE !== \strpos($project, ',')) {
$projects = \explode(',', $project);
}
......
......@@ -23,6 +23,10 @@ use Twig\Node\Node;
self::RULE_NAME_FORBID => [
'componentMetadata' => 'This is an internal SDC variable specific to Drupal.',
],
// Attributes is always injected.
self::RULE_NAME_IGNORE => [
'attributes',
],
],
label: new TranslatableMarkup('Name rules'),
description: new TranslatableMarkup('Rules around Twig name.'),
......@@ -78,8 +82,29 @@ final class TwigValidatorRuleName extends TwigValidatorRulePluginBase {
* Is or is not.
*/
private function isKnownVariable(Node $node, array $definition, array $variableSet): bool {
if (!$node->hasAttribute('name')) {
return TRUE;
}
$name = $node->getAttribute('name');
return !$node->hasAttribute('name') || isset($definition[$name]) || isset($variableSet[$name]) || isset(TwigVariableCollectorVisitor::ALLOW_NOT_SET_VARIABLE[$name]);
if (isset($definition[$name])) {
return TRUE;
}
if (isset($variableSet[$name])) {
return TRUE;
}
if (isset(TwigVariableCollectorVisitor::ALLOW_NOT_SET_VARIABLE[$name])) {
return TRUE;
}
if (in_array($name, $this->getNameIgnore(), TRUE)) {
return TRUE;
}
return FALSE;
}
/**
......
......@@ -149,12 +149,15 @@ final class TwigValidator extends ValidatorBase {
*/
private function collectVariables(TwigVariableCollectorVisitor $variableCollector, string $id, array $definitionVariables): array {
$diff = \array_diff_key($variableCollector->getVariableSetList() + $definitionVariables, $variableCollector->getVariablePrintList());
$whitelist = $variableCollector::ALLOW_NOT_SET_VARIABLE;
$unusedVarList = \array_filter(\array_keys($diff), static fn($varName) => !isset($whitelist[$varName]));
if (!empty($unusedVarList)) {
$message = new TranslatableMarkup('Unused variables: @list', ['@list' => \implode(', ', $unusedVarList)]);
$this->addMessage(ValidatorMessage::createForTwigString($id, $message));
}
return $variableCollector->getVariableSetList();
}
......@@ -168,7 +171,8 @@ final class TwigValidator extends ValidatorBase {
* Flat variables with their types.
*/
private function flattenDefinitionVariablesWithType(string $id): array {
$result = [];
// Attributes is injected and should be always used.
$result['attributes'] = 'object';
if (!$this->componentPluginManager->hasDefinition($id)) {
return [];
......
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Drupal\ui_patterns_devel\TwigValidator;
use Twig\Environment;
use Twig\Node\BlockReferenceNode;
use Twig\Node\Expression\AssignNameExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\MacroNode;
......@@ -22,7 +23,6 @@ final class TwigVariableCollectorVisitor implements NodeVisitorInterface {
* @var array
*/
public const ALLOW_NOT_SET_VARIABLE = [
'attributes' => 'object',
'variant' => 'string',
'loop' => '',
'_self' => '',
......@@ -82,11 +82,15 @@ final class TwigVariableCollectorVisitor implements NodeVisitorInterface {
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
public function enterNode(Node $node, Environment $env): Node {
if ($node instanceof NameExpression) {
if (!$node->hasAttribute('name')) {
return $node;
// Collect block names as valid variable, fix #3464630.
if ($node instanceof BlockReferenceNode) {
if ($node->hasAttribute('name')) {
$name = $node->getAttribute('name');
$this->variablePrintList[$name] = '';
}
}
if ($node instanceof NameExpression) {
// Is parent a macro? Than add macro parameters to set list.
$macro = TwigNodeFinder::filterParents($node, static fn (Node $node): bool => $node instanceof MacroNode);
if (!empty($macro) && isset($macro[0])) {
......
......@@ -197,9 +197,10 @@ final class ValidatorMessage {
* The message message.
*/
public function messageWithTip(): FormattableMarkup {
// phpcs:disable Drupal.Semantics.FunctionT.NotLiteralString
return new FormattableMarkup("@message<br>@tip", ['@message' => $this->message, '@tip' => $this->tip]);
// phpcs:enable Drupal.Semantics.FunctionT.NotLiteralString
if ($this->tip) {
return new FormattableMarkup(sprintf('%s<br>%s', $this->message, $this->tip), []);
}
return $this->message;
}
/**
......
......@@ -23,7 +23,7 @@ final class TwigVariableCollectorTest extends TwigValidatorTestBase {
$this->assertEquals(1, \count($errors), 'Error count do not match');
$this->assertEquals('Unused variables: zoo_unused, item_unused, my_attributes, macro_unused_1, test_slot_string, test_prop_bool', (string) $errors[0]->message());
$this->assertEquals('Unused variables: zoo_unused, item_unused, my_attributes, macro_unused_1, test_slot_string, test_slot_block, test_prop_bool', (string) $errors[0]->message());
}
}
......@@ -90,7 +90,7 @@ class ValidatorTest extends ComponentKernelTestBase {
$errors = $this->validator->getMessages();
$expected = [
'Unused variables: test_slot_string, nothing, object, array, array_object, test_prop_string_error, test_enum_string, test_prop_links, test_enum_items',
'Unused variables: attributes, test_slot_string, nothing, object, array, array_object, test_prop_string_error, test_enum_string, test_prop_links, test_enum_items',
'Filter `trans` or `t` unsafe translation, do not translate variables!',
"Don't use `default` filter on boolean.",
'A single variant do not need to be declared.',
......
......@@ -13,6 +13,9 @@ slots:
test_slot_string:
title: Test string
type: string
test_slot_block:
title: Block
description: 'An array of something.'
props:
type: object
properties:
......
......@@ -12,3 +12,4 @@
{% endfor %}
{{ test_prop_string_collector }}
{% set my_attributes = create_attribute() %}
<span {{ attributes }}></span>
\ No newline at end of file
<b>variant:</b> {{ variant }}
<b {{ attributes }}>variant:</b> {{ variant }}
test_slot_string: {{ test_slot_string }}
test_prop_string_foo: {{ test_prop_string_foo }}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment