1616
1717/**
1818 * @when before_wp_load
19+ *
20+ * @phpstan-type Command_Array array{name:string, description:string, longdesc:string, subcommands?:array<Command_Array>, synopsis?:string, hook?:string}
1921 */
2022class Command {
2123
@@ -382,7 +384,17 @@ public function api_dump() {
382384 echo json_encode ( $ apis );
383385 }
384386
385- private static function gen_cmd_pages ( $ cmd , $ parent = [], $ verbose = false ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.parentFound
387+ /**
388+ * Generate and save markdown documentation for the provided command and its sub-commands.
389+ *
390+ * @param Command_Array $cmd Command details as array.
391+ * @param string[] $parent The breadcrumb of parent commands.
392+ * @param bool $verbose Whether to output the command page as it is generated.
393+ * @param ?string $output_dir The directory to output the generated command pages, null for default `../commands`.
394+ * @return void
395+ */
396+ private static function gen_cmd_pages ( $ cmd , $ parent = [], $ verbose = false , ?string $ output_dir = null ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.parentFound
397+
386398 $ parent [] = $ cmd ['name ' ];
387399
388400 static $ params ;
@@ -516,7 +528,7 @@ private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) {
516528 $ binding ['docs ' ] = $ docs ;
517529 }
518530
519- $ path = dirname ( __DIR__ ) . '/commands/ ' . $ binding ['path ' ];
531+ $ path = ( $ output_dir ?? dirname ( __DIR__ ) . '/commands ' ) . ' / ' . $ binding ['path ' ];
520532 if ( ! is_dir ( dirname ( $ path ) ) ) {
521533 mkdir ( dirname ( $ path ) );
522534 }
@@ -530,7 +542,7 @@ private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) {
530542 }
531543
532544 foreach ( $ cmd ['subcommands ' ] as $ subcmd ) {
533- self ::gen_cmd_pages ( $ subcmd , $ parent , $ verbose );
545+ self ::gen_cmd_pages ( $ subcmd , $ parent , $ verbose, $ output_dir );
534546 }
535547 }
536548
@@ -674,6 +686,84 @@ private static function empty_dir( $dir ) {
674686 }
675687 WP_CLI ::log ( sprintf ( "Removed existing contents of '%s' " , $ dir ) );
676688 }
689+
690+ /**
691+ * Generate the documentation page for a custom command using the same templates as the official WP-CLI commands.
692+ *
693+ * ## OPTIONS
694+ *
695+ * <command>
696+ * : The custom WP CLI command to generate the documentation for.
697+ *
698+ * [--output_dir=<output_dir>]
699+ * : Directory in which to save the documentation.
700+ * ---
701+ * default: ./docs/wp-cli
702+ * ---
703+ *
704+ * [--verbose]
705+ * : If set will list command pages as they are generated.
706+ *
707+ * @subcommand gen-custom
708+ *
709+ * @when after_wp_load
710+ *
711+ * @param string[] $args
712+ * @param array<string,string> $assoc_args
713+ * @return void
714+ */
715+ public function gen_custom_cmd_pages ( $ args , $ assoc_args ) {
716+
717+ $ command_name = $ args [0 ];
718+
719+ $ output_dir = Utils \is_path_absolute ( $ assoc_args ['output_dir ' ] )
720+ ? $ assoc_args ['output_dir ' ]
721+ : getcwd () . '/ ' . $ assoc_args ['output_dir ' ];
722+
723+ if ( ! is_dir ( $ output_dir ) ) {
724+ mkdir ( $ output_dir , 0777 , true );
725+ }
726+
727+ $ verbose = Utils \get_flag_value ( $ assoc_args , 'verbose ' , false );
728+
729+ /**
730+ * Get all registered WP-CLI commands.
731+ *
732+ * @see \CLI_Command::cmd_dump()
733+ *
734+ * @var array{name:string,description:string,longdesc:string,subcommands:array<Command_Array>} $all_commands
735+ */
736+ $ all_commands = WP_CLI ::runcommand (
737+ 'cli cmd-dump ' ,
738+ [
739+ 'launch ' => false ,
740+ 'return ' => 'stdout ' ,
741+ 'parse ' => 'json ' ,
742+ ]
743+ );
744+
745+ /**
746+ * Filter the list of WP-CLI commands to find one matching the name passed as an argument to this command.
747+ *
748+ * @var array<Command_Array> $my_commands
749+ */
750+ $ my_commands = array_values (
751+ array_filter (
752+ $ all_commands ['subcommands ' ],
753+ function ( array $ command ) use ( $ command_name ): bool {
754+ return $ command ['name ' ] === $ command_name ;
755+ }
756+ )
757+ );
758+
759+ if ( empty ( $ my_commands ) ) {
760+ WP_CLI ::error ( "Failed to find command ' {$ command_name }'. " );
761+ }
762+
763+ self ::gen_cmd_pages ( $ my_commands [0 ], [], $ verbose , $ output_dir );
764+
765+ WP_CLI ::success ( "Generated command pages for ' {$ command_name }'. " );
766+ }
677767}
678768
679769WP_CLI ::add_command ( 'handbook ' , '\WP_CLI\Handbook\Command ' );
0 commit comments