Skip to content

Commit 52ba3b3

Browse files
committed
refactor debug:twig-component
1 parent 3956b48 commit 52ba3b3

File tree

4 files changed

+72
-54
lines changed

4 files changed

+72
-54
lines changed

src/TwigComponent/config/debug.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
namespace Symfony\UX\TwigComponent\DependencyInjection\Loader\Configurator;
12+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

14-
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
14+
use Symfony\UX\TwigComponent\Command\TwigComponentDebugCommand;
1515
use Symfony\UX\TwigComponent\DataCollector\TwigComponentDataCollector;
1616
use Symfony\UX\TwigComponent\EventListener\TwigComponentLoggerListener;
1717

18-
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
19-
2018
return static function (ContainerConfigurator $container) {
2119
$container->services()
2220

@@ -35,5 +33,16 @@
3533
'template' => '@TwigComponent/Collector/twig_component.html.twig',
3634
'id' => 'twig_component',
3735
'priority' => 256,
38-
]);
36+
])
37+
38+
->set('ux.twig_component.command.debug', TwigComponentDebugCommand::class)
39+
->args([
40+
param('twig.default_path'),
41+
service('ux.twig_component.component_factory'),
42+
service('twig'),
43+
param('ux.twig_component.class_component_map'),
44+
param('ux.twig_component.anonymous_template_directory'),
45+
])
46+
->tag('console.command')
47+
;
3948
};

src/TwigComponent/src/Command/TwigComponentDebugCommand.php

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,20 @@
2828
use Symfony\UX\TwigComponent\Twig\PropsNode;
2929
use Twig\Environment;
3030

31-
#[AsCommand(name: 'debug:twig-component', description: 'Display components and them usages for an application')]
31+
#[AsCommand(name: 'debug:twig-component', description: 'Display Twig components and their usages')]
3232
class TwigComponentDebugCommand extends Command
3333
{
34-
private readonly string $anonymousDirectory;
34+
/** @var array<string, ComponentMetadata> */
35+
private array $components;
3536

3637
public function __construct(
3738
private string $twigTemplatesPath,
3839
private ComponentFactory $componentFactory,
3940
private Environment $twig,
4041
private readonly array $componentClassMap,
41-
?string $anonymousDirectory = null,
42+
private string $anonymousDirectory = 'components',
4243
) {
4344
parent::__construct();
44-
$this->anonymousDirectory = $anonymousDirectory ?? 'components';
4545
}
4646

4747
protected function configure(): void
@@ -51,28 +51,26 @@ protected function configure(): void
5151
new InputArgument('name', InputArgument::OPTIONAL, 'A component name or part of the component name'),
5252
])
5353
->setHelp(
54-
<<<'EOF'
55-
The <info>%command.name%</info> display all the Twig components in your application.
54+
<<<EOF
55+
The <info>%command.name%</info> display all the Twig components in your application.
5656
57-
To list all components:
57+
To list all components:
5858
59-
<info>php %command.full_name%</info>
59+
<info>php %command.full_name%</info>
6060
61-
To get specific information about a component, specify its name (or a part of it):
61+
To get specific information about a component, specify its name (or a part of it):
6262
63-
<info>php %command.full_name% Alert</info>
64-
EOF
63+
<info>php %command.full_name% Alert</info>
64+
EOF
6565
);
6666
}
6767

6868
protected function execute(InputInterface $input, OutputInterface $output): int
6969
{
7070
$io = new SymfonyStyle($input, $output);
71-
$name = $input->getArgument('name');
7271

73-
if (\is_string($name)) {
74-
$component = $this->findComponentName($io, $name, $input->isInteractive());
75-
if (null === $component) {
72+
if ($name = $input->getArgument('name')) {
73+
if (!$component = $this->findComponentName($io, $name, $input->isInteractive())) {
7674
$io->error(sprintf('Unknown component "%s".', $name));
7775

7876
return Command::FAILURE;
@@ -99,25 +97,31 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti
9997
private function findComponentName(SymfonyStyle $io, string $name, bool $interactive): ?string
10098
{
10199
$components = [];
102-
foreach ($this->componentClassMap as $componentName) {
100+
101+
foreach ($this->findComponents() as $componentName => $metadata) {
103102
if ($name === $componentName) {
104103
return $name;
105104
}
105+
106106
if (str_contains($componentName, $name)) {
107107
$components[$componentName] = $componentName;
108108
}
109109
}
110+
110111
foreach ($this->findAnonymousComponents() as $componentName) {
111112
if (isset($components[$componentName])) {
112113
continue;
113114
}
115+
114116
if ($name === $componentName) {
115117
return $name;
116118
}
119+
117120
if (str_contains($componentName, $name)) {
118121
$components[$componentName] = $componentName;
119122
}
120123
}
124+
121125
if ($interactive && \count($components)) {
122126
return $io->choice('Select one of the following component to display its information', array_values($components), 0);
123127
}
@@ -130,15 +134,21 @@ private function findComponentName(SymfonyStyle $io, string $name, bool $interac
130134
*/
131135
private function findComponents(): array
132136
{
133-
$components = [];
134-
foreach ($this->componentClassMap as $class => $name) {
135-
$components[$name] ??= $this->componentFactory->metadataFor($name);
137+
if (isset($this->components)) {
138+
return $this->components;
136139
}
140+
141+
$this->components = [];
142+
143+
foreach ($this->componentClassMap as $name) {
144+
$this->components[$name] ??= $this->componentFactory->metadataFor($name);
145+
}
146+
137147
foreach ($this->findAnonymousComponents() as $name => $template) {
138-
$components[$name] ??= $this->componentFactory->metadataFor($name);
148+
$this->components[$name] ??= $this->componentFactory->metadataFor($name);
139149
}
140150

141-
return $components;
151+
return $this->components;
142152
}
143153

144154
/**
@@ -152,11 +162,14 @@ private function findAnonymousComponents(): array
152162
$anonymousPath = $this->twigTemplatesPath.'/'.$this->anonymousDirectory;
153163
$finderTemplates = new Finder();
154164
$finderTemplates->files()->in($anonymousPath)->notPath('/_');
165+
155166
foreach ($finderTemplates as $template) {
156167
$component = str_replace('/', ':', $template->getRelativePathname());
168+
157169
if (str_ends_with($component, '.html.twig')) {
158170
$component = substr($component, 0, -10);
159171
}
172+
160173
$components[$component] = $component;
161174
}
162175

@@ -204,16 +217,21 @@ private function displayComponentDetails(SymfonyStyle $io, string $name): void
204217

205218
return sprintf('%s(%s)', $m->getName(), implode(', ', $params));
206219
};
220+
207221
$hooks = [];
222+
208223
if ($method = AsTwigComponent::mountMethod($metadata->getClass())) {
209224
$hooks[] = ['Mount', $logMethod($method)];
210225
}
226+
211227
foreach (AsTwigComponent::preMountMethods($metadata->getClass()) as $method) {
212228
$hooks[] = ['PreMount', $logMethod($method)];
213229
}
230+
214231
foreach (AsTwigComponent::postMountMethods($metadata->getClass()) as $method) {
215232
$hooks[] = ['PostMount', $logMethod($method)];
216233
}
234+
217235
if ($hooks) {
218236
$table->addRows([
219237
new TableSeparator(),
@@ -233,12 +251,17 @@ private function displayComponentsTable(SymfonyStyle $io, array $components): vo
233251
$table->setStyle('default');
234252
$table->setHeaderTitle('Components');
235253
$table->setHeaders(['Name', 'Class', 'Template', 'Type']);
254+
236255
foreach ($components as $metadata) {
237256
$table->addRow([
238257
$metadata->getName(),
239258
$metadata->get('class') ? $metadata->getClass() : '',
240259
$metadata->getTemplate(),
241-
$metadata->get('live') ? '<info>Live</info>' : ($metadata->get('class') ? '' : '<comment>Anon</comment>'),
260+
match (true) {
261+
null === $metadata->get('class') => '<comment>Anon</comment>',
262+
$metadata->get('live') => '<info>Live</info>',
263+
default => '',
264+
},
242265
]);
243266
}
244267
$table->render();
@@ -251,16 +274,13 @@ private function getComponentProperties(ComponentMetadata $metadata): array
251274
{
252275
$properties = [];
253276
$reflectionClass = new \ReflectionClass($metadata->getClass());
277+
254278
foreach ($reflectionClass->getProperties() as $property) {
255279
$propertyName = $property->getName();
256280

257281
if ($metadata->isPublicPropsExposed() && $property->isPublic()) {
258282
$type = $property->getType();
259-
if ($type instanceof \ReflectionNamedType) {
260-
$typeName = $type->getName();
261-
} else {
262-
$typeName = (string) $type;
263-
}
283+
$typeName = $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
264284
$value = $property->getDefaultValue();
265285
$propertyDisplay = $typeName.' $'.$propertyName.(null !== $value ? ' = '.json_encode($value) : '');
266286
$properties[$property->name] = $propertyDisplay;
@@ -286,33 +306,32 @@ private function getAnonymousComponentProperties(ComponentMetadata $metadata): a
286306
$source = $this->twig->load($metadata->getTemplate())->getSourceContext();
287307
$tokenStream = $this->twig->tokenize($source);
288308
$moduleNode = $this->twig->parse($tokenStream);
289-
290309
$propsNode = null;
310+
291311
foreach ($moduleNode->getNode('body') as $bodyNode) {
292312
foreach ($bodyNode as $node) {
293313
if (PropsNode::class === $node::class) {
294314
$propsNode = $node;
315+
295316
break 2;
296317
}
297318
}
298319
}
320+
299321
if (!$propsNode instanceof PropsNode) {
300322
return [];
301323
}
302324

303325
$propertyNames = $propsNode->getAttribute('names');
304326
$properties = array_combine($propertyNames, $propertyNames);
327+
305328
foreach ($propertyNames as $propName) {
306329
if ($propsNode->hasNode($propName)
307330
&& ($valueNode = $propsNode->getNode($propName))
308331
&& $valueNode->hasAttribute('value')
309332
) {
310333
$value = $valueNode->getAttribute('value');
311-
if (\is_bool($value)) {
312-
$value = $value ? 'true' : 'false';
313-
} else {
314-
$value = json_encode($value);
315-
}
334+
$value = \is_bool($value) ? ($value ? 'true' : 'false') : json_encode($value);
316335
$properties[$propName] = $propName.' = '.$value;
317336
}
318337
}

src/TwigComponent/src/DependencyInjection/Compiler/TwigComponentPass.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ public function process(ContainerBuilder $container): void
8080
$factoryDefinition->setArgument(4, $componentConfig);
8181
$factoryDefinition->setArgument(5, $componentClassMap);
8282

83-
$debugCommandDefinition = $container->findDefinition('ux.twig_component.command.debug');
84-
$debugCommandDefinition->setArgument(3, $componentClassMap);
83+
if ($container->hasDefinition('ux.twig_component.command.debug')) {
84+
$container->setParameter('ux.twig_component.class_component_map', $componentClassMap);
85+
}
8586
}
8687

8788
private function findMatchingDefaults(string $className, array $componentDefaults): ?array

src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@
2222
use Symfony\Component\DependencyInjection\Exception\LogicException;
2323
use Symfony\Component\DependencyInjection\Extension\Extension;
2424
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
25-
use Symfony\Component\DependencyInjection\Parameter;
2625
use Symfony\Component\DependencyInjection\Reference;
2726
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
28-
use Symfony\UX\TwigComponent\Command\TwigComponentDebugCommand;
2927
use Symfony\UX\TwigComponent\ComponentFactory;
3028
use Symfony\UX\TwigComponent\ComponentRenderer;
3129
use Symfony\UX\TwigComponent\ComponentRendererInterface;
@@ -111,22 +109,13 @@ class_exists(AbstractArgument::class) ? new AbstractArgument(sprintf('Added in %
111109
->setDecoratedService(new Reference('twig.configurator.environment'))
112110
->setArguments([new Reference('ux.twig_component.twig.environment_configurator.inner')]);
113111

114-
$container->register('ux.twig_component.command.debug', TwigComponentDebugCommand::class)
115-
->setArguments([
116-
new Parameter('twig.default_path'),
117-
new Reference('ux.twig_component.component_factory'),
118-
new Reference('twig'),
119-
class_exists(AbstractArgument::class) ? new AbstractArgument(sprintf('Added in %s.', TwigComponentPass::class)) : [],
120-
$config['anonymous_template_directory'],
121-
])
122-
->addTag('console.command')
123-
;
124-
125112
$container->setAlias('console.command.stimulus_component_debug', 'ux.twig_component.command.debug')
126113
->setDeprecated('symfony/ux-twig-component', '2.13', '%alias_id%');
127114

128115
if ($container->getParameter('kernel.debug')) {
129116
$loader->load('debug.php');
117+
118+
$container->setParameter('ux.twig_component.anonymous_template_directory', $config['anonymous_template_directory']);
130119
}
131120
}
132121

@@ -155,7 +144,7 @@ public function getConfigTreeBuilder(): TreeBuilder
155144
->always(function ($v) {
156145
foreach ($v as $namespace => $defaults) {
157146
if (!str_ends_with($namespace, '\\')) {
158-
throw new InvalidConfigurationException(sprintf('The twig_component.defaults namespace "%s" is invalid: it must end in a "\"', $namespace));
147+
throw new InvalidConfigurationException(sprintf('The twig_component.defaults namespace "%s" is invalid: it must end in a "\".', $namespace));
159148
}
160149
}
161150

0 commit comments

Comments
 (0)