99use Aion \Engine \Engine ;
1010use Aion \Engine \FeatureRegistry ;
1111use Aion \Engine \PathResolver ;
12+ use Aion \Engine \PromptTypeEnum ;
1213use Aion \Features \Authentication \ApiTokensFeature ;
1314use Aion \Features \Authentication \OAuthFeature ;
1415use Aion \Features \ExternalTools \ECSLoggingFeature ;
16+ use Aion \Features \OptionDefinition ;
1517use Aion \Features \System \AgenticAiSystemFeature ;
1618use Aion \Features \System \DatabaseSystemFeature ;
1719use Aion \Features \System \LogSystemFeature ;
1820use Aion \Features \System \PhpStanSystemFeature ;
19- use Aion \Stacks \ApiWithDefaultFrontEndSupportStack ;
20- use Aion \Stacks \BareApiStack ;
2121use Aion \Stacks \StackStrategyContract ;
2222use Symfony \Component \Console \Command \Command ;
2323use Symfony \Component \Console \Input \InputInterface ;
@@ -40,15 +40,29 @@ class AionSparkCommand extends Command
4040
4141 private bool $ isInteractive = false ;
4242
43+ private OutputInterface $ output ;
44+
4345 protected function configure (): void
4446 {
4547 $ this ->setName ($ this ->name )
4648 ->addOption ('dry-run ' , description: 'Execute a dry run of the bake process. ' )
47- ->addOption (ConfigKeyEnum::Frontend->value , mode: InputOption::VALUE_NEGATABLE , description: 'Include frontend support. ' );
49+ ->addOption (
50+ ConfigKeyEnum::Frontend->value ,
51+ mode: InputOption::VALUE_NEGATABLE ,
52+ description: $ this ->getWhetherWantToUseFrontendOptionDefinition ()->label ,
53+ );
4854
4955 $ this ->bootstrapFeatureRegistry ();
56+ }
5057
51- $ this ->addDynamicOptionsFromFeatures ();
58+ private static function getWhetherWantToUseFrontendOptionDefinition (): OptionDefinition
59+ {
60+ return new OptionDefinition (
61+ label: 'Do you need a Frontend? ' ,
62+ type: PromptTypeEnum::Confirm,
63+ default: false ,
64+ hint: 'Determines the technology stack for your application. ' ,
65+ );
5266 }
5367
5468 private function bootstrapFeatureRegistry (): void
@@ -59,11 +73,6 @@ private function bootstrapFeatureRegistry(): void
5973
6074 self ::$ featureRegistry = new FeatureRegistry ;
6175
62- self ::$ featureRegistry ->registerStacks ([
63- BareApiStack::class,
64- ApiWithDefaultFrontEndSupportStack::class,
65- ]);
66-
6776 self ::$ featureRegistry ->registerFeatures ([
6877 ApiTokensFeature::class,
6978 OAuthFeature::class,
@@ -73,6 +82,8 @@ private function bootstrapFeatureRegistry(): void
7382 PhpStanSystemFeature::class,
7483 AgenticAiSystemFeature::class,
7584 ]);
85+
86+ $ this ->addDynamicOptionsFromFeatures ();
7687 }
7788
7889 private function addDynamicOptionsFromFeatures (): void
@@ -82,7 +93,7 @@ private function addDynamicOptionsFromFeatures(): void
8293 continue ;
8394 }
8495
85- $ type = $ definition ->type === ' confirm '
96+ $ type = $ definition ->type === PromptTypeEnum::Confirm
8697 ? InputOption::VALUE_NEGATABLE
8798 : InputOption::VALUE_REQUIRED ;
8899
@@ -93,27 +104,27 @@ private function addDynamicOptionsFromFeatures(): void
93104 /** @throws Throwable */
94105 protected function execute (InputInterface $ input , OutputInterface $ output ): int
95106 {
107+ $ this ->output = $ output ;
96108 $ this ->bootstrapFeatureRegistry ();
97109 $ this ->ui = new SparkUI ($ output );
98110 $ this ->configurationPrompter = new ConfigurationPrompter ($ input );
99111 $ this ->isInteractive = $ input ->isInteractive ();
100112
101113 $ this ->ui ->displayHeader ();
102114
103- [$ stackStrategy , $ aionConfig ] = $ this ->promptForConfiguration ($ output );
115+ [$ stackStrategy , $ aionConfig ] = $ this ->promptForConfiguration ();
104116
105- $ this ->runEngine (
117+ $ this ->runCompositionEngine (
106118 $ stackStrategy ,
107119 $ aionConfig ,
108- $ output ,
109120 dryRun: (bool ) $ input ->getOption ('dry-run ' ),
110121 );
111122
112123 if (! $ input ->getOption ('dry-run ' )) {
113- $ this ->installDependencies ($ output , $ aionConfig );
124+ $ this ->installDependencies ($ aionConfig );
114125 }
115126
116- info ( ' >_ ✨ Setup is complete. Enjoy your new Aion-backed project! ' .($ input ->getOption ('dry-run ' ) ? ' (Dry Run Complete) ' : '' ));
127+ $ this -> ui -> displaySuccess ( ' Setup is complete. Enjoy your new Aion-backed project! ' .($ input ->getOption ('dry-run ' ) ? ' (Dry Run Complete) ' : '' ));
117128
118129 if (PHP_OS_FAMILY === 'Windows ' && ! $ input ->getOption ('dry-run ' )) {
119130 info ('>_ Note: On Windows, you should manually delete the .aion folder to finish the cleanup. ' );
@@ -123,37 +134,40 @@ protected function execute(InputInterface $input, OutputInterface $output): int
123134 }
124135
125136 /** @return array{0: StackStrategyContract, 1: AionConfig} */
126- private function promptForConfiguration (OutputInterface $ output ): array
137+ private function promptForConfiguration (): array
127138 {
128139 while (true ) {
129- $ stack = $ this ->resolveStack ($ output );
130- $ aionConfig = $ this ->resolveAionConfig ($ output );
140+ $ stackDefinition = $ this ->getWhetherWantToUseFrontendOptionDefinition ();
131141
132- if ($ this ->confirmSetup ($ stack , $ aionConfig )) {
142+ $ stack = $ this ->resolveStack ($ stackDefinition );
143+
144+ $ aionConfig = $ this ->resolveAionConfig ();
145+
146+ if ($ this ->confirmSetup ($ stack , $ aionConfig , $ stackDefinition )) {
133147 return [$ stack , $ aionConfig ];
134148 }
135149
136- $ output ->writeln ("\n<fg=yellow>Restarting configuration...</> \n" );
150+ $ this -> output ->writeln ("\n<fg=yellow>Restarting configuration...</> \n" );
137151 }
138152 }
139153
140- private function resolveStack (OutputInterface $ output ): StackStrategyContract
154+ private function resolveStack (OptionDefinition $ definition ): StackStrategyContract
141155 {
142- $ output ->writeln ("\n<comment>>_ First, let's pick your technology stack.</comment> " );
156+ $ this -> output ->writeln ("\n<comment>>_ First, let's pick your technology stack.</comment> " );
143157
144- return $ this ->configurationPrompter ->promptForStack ();
158+ return $ this ->configurationPrompter ->promptForStack ($ definition );
145159 }
146160
147- private function resolveAionConfig (OutputInterface $ output ): AionConfig
161+ private function resolveAionConfig (): AionConfig
148162 {
149- $ output ->writeln ("\n<comment>>_ Awesome. Let's configure your application.</comment> \n" );
163+ $ this -> output ->writeln ("\n<comment>>_ Awesome. Let's configure your application.</comment> \n" );
150164
151165 return $ this ->configurationPrompter ->promptForConfiguration (
152166 optionDefinitions: self ::$ featureRegistry ->getOptionDefinitions (),
153167 );
154168 }
155169
156- private function confirmSetup (StackStrategyContract $ stack , AionConfig $ config ): bool
170+ private function confirmSetup (StackStrategyContract $ stack , AionConfig $ config, OptionDefinition $ stackDefinition ): bool
157171 {
158172 if ($ this ->isInteractive === false ) {
159173 return true ;
@@ -162,46 +176,35 @@ private function confirmSetup(StackStrategyContract $stack, AionConfig $config):
162176 $ this ->ui ->displaySummary (
163177 stack: $ stack ,
164178 config: $ config ,
165- optionDefinitions: self ::$ featureRegistry ->getOptionDefinitions (),
179+ optionDefinitions: [
180+ ConfigKeyEnum::Frontend->value => $ stackDefinition ,
181+ ...self ::$ featureRegistry ->getOptionDefinitions (),
182+ ],
166183 );
167184
168185 return confirm (label: 'Is this configuration correct? ' );
169186 }
170187
171188 /** @throws Throwable */
172- private function runEngine (
189+ private function runCompositionEngine (
173190 StackStrategyContract $ stackStrategy ,
174191 AionConfig $ aionConfig ,
175- OutputInterface $ output ,
176192 bool $ dryRun = false ,
177193 ): void {
178194 $ engine = new Engine (
179- registry : self ::$ featureRegistry ,
195+ featureRegistry : self ::$ featureRegistry ,
180196 stack: $ stackStrategy ,
181197 configuration: $ aionConfig ,
182198 pathResolver: new PathResolver (getcwd ()),
183199 dryRun: $ dryRun ,
184200 );
185201
186- $ titleSection = $ output ->section ();
187- $ titleSection ->overwrite ('Putting things together... ' );
188-
189- $ progressSection = $ output ->section ();
190-
191- $ engine ->bake (function (string $ description ) use ($ progressSection ) {
192- $ progressSection ->overwrite ("> $ description " );
193-
194- usleep (rand (50_000 , 150_000 ));
195- });
196-
197- $ progressSection ->overwrite ('100% ' );
198- $ titleSection ->overwrite ('Putting things together... Done! ' );
199- usleep (350_000 );
200-
201- $ output ->writeln ('' );
202+ $ this ->ui ->displayProgress (
203+ progressSteps: $ engine ->yieldBakingOperations (),
204+ );
202205 }
203206
204- private function installDependencies (OutputInterface $ output , AionConfig $ config ): void
207+ private function installDependencies (AionConfig $ config ): void
205208 {
206209 if ($ this ->isInteractive && ! confirm ('Would you like to install the composer dependencies now? ' )) {
207210 return ;
@@ -218,6 +221,6 @@ private function installDependencies(OutputInterface $output, AionConfig $config
218221 passthru ('php artisan boost:install ' );
219222 }
220223
221- $ output ->writeln ('' );
224+ $ this -> output ->writeln ('' );
222225 }
223226}
0 commit comments