2828use Symfony \UX \TwigComponent \Twig \PropsNode ;
2929use 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 ' )]
3232class 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 }
0 commit comments