Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
252 commits
Select commit Hold shift + click to select a range
07d46fc
Add helper method
emmadesilva Dec 10, 2024
b22a7e8
Draft outline for interactive publish
emmadesilva Dec 10, 2024
0ff87fd
First pass at collecting and formatting publish paths
emmadesilva Dec 10, 2024
73bcf9f
Cleanup code
emmadesilva Dec 10, 2024
ff5757f
Prompt for files
emmadesilva Dec 10, 2024
6025e57
Use Laravel Prompts multiselect
emmadesilva Dec 10, 2024
b6eba4a
Cleanup and move up output
emmadesilva Dec 10, 2024
98e4b16
Filter the files to only include the selected ones
emmadesilva Dec 10, 2024
6607d3d
Publish the files
emmadesilva Dec 10, 2024
cea2f6c
Display how to select/unselect all at once
emmadesilva Dec 10, 2024
e737d1f
Create InteractivePublishCommandHelper.php
emmadesilva Dec 10, 2024
1b93233
Create InteractivePublishCommandHelperTest.php
emmadesilva Dec 10, 2024
d27fae8
Add test crosslink
emmadesilva Dec 10, 2024
e04400a
Support all groups
emmadesilva Dec 10, 2024
f9bb56a
Add todo
emmadesilva Dec 10, 2024
3f406ad
Tweak setup
emmadesilva Dec 10, 2024
04813da
Create helper instance
emmadesilva Dec 10, 2024
15b67aa
Introduce local variable
emmadesilva Dec 10, 2024
5589e36
Mark helper as internal
emmadesilva Dec 10, 2024
6d7810a
Move method to helper
emmadesilva Dec 10, 2024
32105b2
Revert "Move method to helper"
emmadesilva Dec 10, 2024
72b4b11
Move logic to helper
emmadesilva Dec 10, 2024
2faf686
Refactor the extracted class
emmadesilva Dec 10, 2024
26eff2f
Refactor so output is handled in command
emmadesilva Dec 11, 2024
b6010c2
Refactor to initialise all data in constructor
emmadesilva Dec 11, 2024
acaf3f3
Make it clearer where data is initialized
emmadesilva Dec 11, 2024
093c96e
Cleanup code
emmadesilva Dec 11, 2024
2f000ba
Refactor data retrieval to clean up constructor
emmadesilva Dec 11, 2024
cd6dbb0
Rename properties to indicate they are directories
emmadesilva Dec 11, 2024
ecc8725
Use array facade instead of unnecessary collections
emmadesilva Dec 11, 2024
9c9b113
Extract helper method
emmadesilva Dec 11, 2024
50f8362
Formatting
emmadesilva Dec 11, 2024
3330f3a
Extract helper method and add more array types
emmadesilva Dec 11, 2024
7c68954
Change variable used to match signature
emmadesilva Dec 11, 2024
179a6d3
Fix formatting
emmadesilva Dec 11, 2024
c60a240
Split out output handler to void handle method
emmadesilva Dec 11, 2024
8df8040
Add base test setups
emmadesilva Dec 11, 2024
c28a233
Disable interactive publishing on Windows
emmadesilva Dec 22, 2024
27d3cc2
Create ConsoleHelper.php
emmadesilva Dec 22, 2024
4e59a21
Mark helper as internal
emmadesilva Dec 22, 2024
2c221b1
Code coverage ignore helper
emmadesilva Dec 22, 2024
b9a4ed8
Add mocking scaffolding
emmadesilva Dec 22, 2024
443a1a8
Add and use mockable Windows detection helper
emmadesilva Dec 22, 2024
03bcc03
Create mockable helper for multiselect
emmadesilva Dec 22, 2024
4f90765
Create first tests
emmadesilva Dec 22, 2024
366c42d
Mock test state
emmadesilva Dec 22, 2024
f64240b
Refactor to add check earlier so we can fail command
emmadesilva Dec 22, 2024
86ac4a6
Clean up after test
emmadesilva Dec 22, 2024
0200cc6
Cleanup test
emmadesilva Dec 22, 2024
6824637
Also bail if the stream is not interactive
emmadesilva Dec 22, 2024
e72985a
Revert "Also bail if the stream is not interactive"
emmadesilva Dec 22, 2024
802a9ab
Mock the application
emmadesilva Dec 22, 2024
b3faee7
Annotate literal parameters
emmadesilva Dec 22, 2024
7b4003d
Annotate array types
emmadesilva Dec 22, 2024
6fa6b35
Implement the unit test
emmadesilva Dec 22, 2024
31a1400
Cleanup code and use real app
emmadesilva Dec 22, 2024
540c0c4
Add final unit test
emmadesilva Dec 22, 2024
2438471
Refactor to mock by default
emmadesilva Dec 22, 2024
8e45658
Simplify setup
emmadesilva Dec 22, 2024
4394f66
Add testing helper
emmadesilva Dec 23, 2024
9f37161
Use proper single file pluralization
emmadesilva Dec 23, 2024
f71272d
More Laravely formatting
emmadesilva Dec 23, 2024
7895391
Extract helper method
emmadesilva Dec 23, 2024
dbb49aa
Clearer helper method name
emmadesilva Dec 23, 2024
945d5f1
Inline local variable
emmadesilva Dec 23, 2024
988a1dc
Helper method
emmadesilva Dec 23, 2024
a799498
Inline variable
emmadesilva Dec 23, 2024
63c8b61
Implement dynamic formatting
emmadesilva Dec 23, 2024
95a305c
Remove inlined helper method
emmadesilva Dec 23, 2024
84b5459
Go really crazy with the formatting
emmadesilva Dec 23, 2024
1cabc8b
Expect smarter formatting
emmadesilva Dec 23, 2024
e9a1dda
Cleanup formatting
emmadesilva Dec 24, 2024
38d8a21
Create ConsoleHelperTest.php
emmadesilva Dec 24, 2024
65ebd7e
Add regression test
emmadesilva Dec 24, 2024
e9bee64
Cleanup code
emmadesilva Dec 24, 2024
5143c0b
Clean up formatting
emmadesilva Dec 24, 2024
88d7545
Don't trigger interactive if "all" is selected
emmadesilva Dec 25, 2024
88b9713
Cleanup code with expanded func get args
emmadesilva Dec 25, 2024
5791d29
Use Laravel Prompts for initial select
emmadesilva Dec 25, 2024
c48d2de
Remove resolved todo
emmadesilva Dec 25, 2024
21f7f54
Annotate array types
emmadesilva Dec 25, 2024
c5f57d4
Formatting
emmadesilva Dec 25, 2024
b2f9692
Use proper Laravel Prompts fallback
emmadesilva Dec 25, 2024
56895f4
Fix formatting
emmadesilva Dec 25, 2024
9538c71
Add todo
emmadesilva Dec 25, 2024
24b5838
Normalize implementation to not call vendor command
emmadesilva Dec 25, 2024
5183876
Support single file
emmadesilva Dec 25, 2024
138c4db
Formatting
emmadesilva Dec 25, 2024
4ad163b
Update PublishViewsCommand.php
emmadesilva Dec 25, 2024
4b08069
Use proper fallback syntax
emmadesilva Dec 25, 2024
50f078b
Create ViewPublishGroup.php
emmadesilva Dec 25, 2024
e7b6e9c
Create internal helper class for wrapping publishable files
emmadesilva Dec 25, 2024
e49769d
Cleanup formatting
emmadesilva Dec 25, 2024
ffad46b
Fix formatting
emmadesilva Dec 25, 2024
c73740f
Construct directory
emmadesilva Dec 25, 2024
a77da56
Formatting
emmadesilva Dec 25, 2024
54c8891
Declare options using helper
emmadesilva Dec 25, 2024
3d7dff9
Add helper to get the map of all publishable file paths
emmadesilva Dec 25, 2024
e08e68c
Map to keys
emmadesilva Dec 25, 2024
542a2f5
Use object access
emmadesilva Dec 25, 2024
bc4e9a4
Unwrap curly braces
emmadesilva Dec 25, 2024
881960a
Add spacing
emmadesilva Dec 25, 2024
9c95612
More semantic implementation with collect call
emmadesilva Dec 25, 2024
14ce32d
Refactor and cleanup
emmadesilva Dec 25, 2024
4b4f439
Move down prepend
emmadesilva Dec 25, 2024
ddb86dd
Validate selection
emmadesilva Dec 25, 2024
5b6f41f
Merge allowed keys
emmadesilva Dec 25, 2024
f915c1d
Inline variable
emmadesilva Dec 25, 2024
5bce274
Inline method
emmadesilva Dec 25, 2024
a64767d
Return with better error instead of throw
emmadesilva Dec 25, 2024
fdcb53c
Flip values
emmadesilva Dec 25, 2024
7890671
Trim the prefix we don't need here
emmadesilva Dec 25, 2024
5f82f32
Convert the arrow function to a traditional closure
emmadesilva Dec 25, 2024
8d95940
Fix formatting
emmadesilva Dec 25, 2024
87bbb3d
Should not need string cast
emmadesilva Dec 25, 2024
ac4ebcf
Fallback to all earlier
emmadesilva Dec 25, 2024
9ecb4be
Only support groups to publish here
emmadesilva Dec 25, 2024
d9f59e0
Merge all paths to publish
emmadesilva Dec 25, 2024
3b30a4f
Buffer files to publish
emmadesilva Dec 25, 2024
860e262
Remove unused method
emmadesilva Dec 25, 2024
f1ceaef
Normalize format
emmadesilva Dec 25, 2024
0e812af
Algorithm to find the most specific common path
emmadesilva Dec 25, 2024
1ec031a
Refactor for the new publisher system
emmadesilva Dec 25, 2024
4f982fd
Make array writable
emmadesilva Dec 25, 2024
9917956
Remove unused methods
emmadesilva Dec 25, 2024
648e3c4
Use instance data
emmadesilva Dec 25, 2024
c50f60a
Simplify publishing handler
emmadesilva Dec 25, 2024
a7a6652
Formatting
emmadesilva Dec 25, 2024
ee62aaf
Remove todo as we overwrite files
emmadesilva Dec 25, 2024
8697255
Get selected files from instance
emmadesilva Dec 25, 2024
6ff9b2c
Use dynamic base directory
emmadesilva Dec 25, 2024
df24553
Publish files
emmadesilva Dec 25, 2024
b69a93a
Write output
emmadesilva Dec 25, 2024
9672a0b
Fix source not being used
emmadesilva Dec 25, 2024
05d4781
Helper to only publish the selected files
emmadesilva Dec 25, 2024
e6d03fe
Cleanup code
emmadesilva Dec 25, 2024
5c8cec8
Interactively publish selected files
emmadesilva Dec 25, 2024
aebd65a
Cleanup code
emmadesilva Dec 25, 2024
93af32e
Remove unused method
emmadesilva Dec 25, 2024
7b1b7b0
Remove unused import
emmadesilva Dec 25, 2024
6406e7c
Improve formatting
emmadesilva Dec 25, 2024
8f1790c
Simplify 'if'
emmadesilva Dec 25, 2024
5f19c37
Extract helper method
emmadesilva Dec 25, 2024
1624692
Extract helper method
emmadesilva Dec 25, 2024
0966cc7
Cleanup code
emmadesilva Dec 25, 2024
54c125f
Remove extra newline
emmadesilva Dec 25, 2024
991ffe1
Print more helpful output
emmadesilva Dec 25, 2024
6d5606f
Add magic key to select all
emmadesilva Dec 25, 2024
215d75c
No need to write we select all here
emmadesilva Dec 25, 2024
4be8d42
Move up method in source
emmadesilva Dec 25, 2024
a628959
Fix parameter name
emmadesilva Dec 25, 2024
e54e782
No longer need hint
emmadesilva Dec 25, 2024
5df46b5
Use search instead of select
emmadesilva Dec 25, 2024
3f95c4d
Revert "Use search instead of select"
emmadesilva Dec 25, 2024
004b7de
Extract helper method
emmadesilva Dec 25, 2024
baf1571
Fix formatting
emmadesilva Dec 25, 2024
20d879c
Cleanup control flow
emmadesilva Dec 25, 2024
93fba2c
Ignore coverage for Laravel Prompts patch
emmadesilva Dec 25, 2024
592c2eb
Dynamic label
emmadesilva Dec 25, 2024
d51c768
Revert "Dynamic label"
emmadesilva Dec 25, 2024
793d27c
Use more semantic helper
emmadesilva Dec 25, 2024
d54b2b9
Formatting
emmadesilva Dec 25, 2024
da40f5f
Rewrite unit test for new scope
emmadesilva Dec 25, 2024
134b4e9
Remove experimental marker
emmadesilva Dec 25, 2024
2025cf6
Fix formatting
emmadesilva Dec 25, 2024
51358b1
Make helper public
emmadesilva Dec 25, 2024
574fb8b
Use more realistic dataset
emmadesilva Dec 25, 2024
e52b1f9
Test more cases
emmadesilva Dec 25, 2024
0f3fd96
Test the base directory helper
emmadesilva Dec 25, 2024
7c21ab5
Fix formatting
emmadesilva Dec 25, 2024
796bae1
Cleanup and refactor method
emmadesilva Dec 25, 2024
2529630
Reformat to ternary return
emmadesilva Dec 25, 2024
1accd03
Merge declaration
emmadesilva Dec 25, 2024
f9d36f6
Remove multi select mocker we are no longer using
emmadesilva Dec 25, 2024
68b584f
Inline variable
emmadesilva Dec 25, 2024
06aad10
Refactor to move up output
emmadesilva Dec 25, 2024
80dbaec
Inline local variable
emmadesilva Dec 25, 2024
ca97255
Tweak helper methods
emmadesilva Dec 25, 2024
842c3d8
Fix logic
emmadesilva Dec 25, 2024
7023dea
Check the input
emmadesilva Dec 25, 2024
d466607
Check using helper
emmadesilva Dec 25, 2024
1248247
Update the test
emmadesilva Dec 25, 2024
7fee865
Remove annotation we don't need
emmadesilva Dec 26, 2024
586068f
Handle output in main command body
emmadesilva Dec 26, 2024
970b4c3
Return publisher to handle all output in main command body
emmadesilva Dec 26, 2024
5570d91
Print the published group
emmadesilva Dec 26, 2024
31fcc3a
Check group is not all
emmadesilva Dec 26, 2024
c7f1eac
Don't print selected when using argument
emmadesilva Dec 26, 2024
b829a82
Instead of printing count, assume we did what the user wanted
emmadesilva Dec 26, 2024
0ab7e8c
Null all group
emmadesilva Dec 26, 2024
c2002df
Remove unused variable
emmadesilva Dec 26, 2024
3c708b9
Remove unused method
emmadesilva Dec 26, 2024
50c55eb
Replace ternary return with match returns
emmadesilva Dec 26, 2024
bc5fb30
Print the file count
emmadesilva Dec 26, 2024
6bc6030
Add testing helper to disable prompts
emmadesilva Dec 26, 2024
78e73d7
Move up variable declaration
emmadesilva Dec 26, 2024
19d6e36
Use local variable
emmadesilva Dec 26, 2024
460bd1f
Invert local data flow
emmadesilva Dec 26, 2024
413e777
Remove null support as real code is never null
emmadesilva Dec 26, 2024
763662e
Fix wrong comparison
emmadesilva Dec 26, 2024
7bbdc49
Move down formatting
emmadesilva Dec 26, 2024
b6fc64c
Dynamic noun modifiers
emmadesilva Dec 26, 2024
10c0fde
Introduce local variable
emmadesilva Dec 26, 2024
def449a
Fix formatting
emmadesilva Dec 26, 2024
ea2dc68
Test covers helper
emmadesilva Dec 26, 2024
eda8e34
Remove periods
emmadesilva Dec 26, 2024
ef11340
Reimplement the test for base usages
emmadesilva Dec 26, 2024
3a9591c
Test with interactive output
emmadesilva Dec 26, 2024
0a8e97d
Add hacky workaround for https://github.com/laravel/prompts/issues/158
emmadesilva Dec 26, 2024
a527e30
Apply fixes from StyleCI
StyleCIBot Dec 26, 2024
4236205
Update tests for added parameter
emmadesilva Dec 26, 2024
3abd5bc
Remove code coverage ignore
emmadesilva Dec 26, 2024
bbea834
Set mock through container
emmadesilva Dec 26, 2024
24bf9ea
Cleanup formatting
emmadesilva Dec 26, 2024
c0683c3
Test all interactive selection paths
emmadesilva Dec 26, 2024
ba6d1b6
Extract helper method and cleanup code
emmadesilva Dec 26, 2024
e9fbb80
Use more explicit disabler
emmadesilva Dec 26, 2024
826779a
Test with the group question
emmadesilva Dec 26, 2024
e47239a
Change comment text
emmadesilva Dec 26, 2024
101d398
Create ViewPublishGroupTest.php
emmadesilva Dec 26, 2024
d5d36af
Call method on static provider
emmadesilva Dec 26, 2024
d8192c7
Use static instead of self
emmadesilva Dec 26, 2024
dfca559
Create setup for mocking the provider
emmadesilva Dec 26, 2024
ad57a2e
Mock paths to publish based on real output
emmadesilva Dec 26, 2024
2a8540f
Reorder helper classes
emmadesilva Dec 26, 2024
ebe07f8
Assert from helper class
emmadesilva Dec 26, 2024
4a7229a
Simplify test passer
emmadesilva Dec 26, 2024
f346f16
Cleanup
emmadesilva Dec 26, 2024
c272bbf
Import class
emmadesilva Dec 26, 2024
5010001
Draft initial test
emmadesilva Dec 26, 2024
3855c86
Mock file finder
emmadesilva Dec 26, 2024
0afe4ab
Assert all properties
emmadesilva Dec 26, 2024
adf92fc
Test custom names and descriptions
emmadesilva Dec 26, 2024
49eee7e
Use static instead of self
emmadesilva Dec 26, 2024
b701c32
Clean up formatting
emmadesilva Dec 26, 2024
7199591
Fix paths needing to be dynamic for monorepo
emmadesilva Dec 26, 2024
93ff4eb
Test we can get the publishable files map
emmadesilva Dec 26, 2024
9936d17
Test does not need config
emmadesilva Dec 26, 2024
b9b038b
Should not be singleton
emmadesilva Dec 26, 2024
2d74f49
Merge branch '2.x-dev' into interactive-component-publisher-command
emmadesilva Dec 28, 2024
b9df42b
Apparently faked prompts can't run on Windows
emmadesilva Dec 28, 2024
fc972d5
Annotate array types
emmadesilva Dec 28, 2024
2650e64
Add missing closure type
emmadesilva Dec 28, 2024
a07d1ed
Add array cast
emmadesilva Dec 28, 2024
724362b
Fix default for non-interactive mode
emmadesilva Dec 29, 2024
e91f050
Revert "Fix default for non-interactive mode"
emmadesilva Dec 29, 2024
c74b8ba
Fall back naught value to all
emmadesilva Dec 29, 2024
ba2c0b6
Don't show feedback when prompts are not used
emmadesilva Dec 29, 2024
09c668e
Update customization.md
emmadesilva Dec 29, 2024
68aec1e
Update RELEASE_NOTES.md
emmadesilva Dec 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ This serves two purposes:
- Added a Vite HMR support for the realtime compiler in https://github.com/hydephp/develop/pull/2016
- Added Vite facade in https://github.com/hydephp/develop/pull/2016
- Added a custom Blade-based heading renderer for Markdown conversions in https://github.com/hydephp/develop/pull/2047
- The `publish:views` command is now interactive for Unix-like systems in https://github.com/hydephp/develop/pull/2062

### Changed

Expand Down
2 changes: 2 additions & 0 deletions docs/digging-deeper/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@ php hyde publish:views

The files will then be available in the `resources/views/vendor/hyde` directory.

>info **Tip:** If you use Linux/macOS or Windows with WSL you will be able to interactively select individual files to publish.

## Frontend Styles

Hyde is designed to not only serve as a framework but a whole starter kit and comes with a Tailwind starter template
Expand Down
165 changes: 113 additions & 52 deletions packages/framework/src/Console/Commands/PublishViewsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@

namespace Hyde\Console\Commands;

use Closure;
use Hyde\Console\Concerns\Command;
use Illuminate\Support\Facades\Artisan;

use Hyde\Console\Helpers\ConsoleHelper;
use Hyde\Console\Helpers\InteractivePublishCommandHelper;
use Hyde\Console\Helpers\ViewPublishGroup;
use Illuminate\Support\Str;
use Laravel\Prompts\Key;
use Laravel\Prompts\MultiSelectPrompt;
use Laravel\Prompts\SelectPrompt;

use function Laravel\Prompts\select;
use function str_replace;
use function sprintf;
use function strstr;
Expand All @@ -20,78 +28,131 @@ class PublishViewsCommand extends Command
protected $signature = 'publish:views {category? : The category to publish}';

/** @var string */
protected $description = 'Publish the hyde components for customization. Note that existing files will be overwritten';

/** @var array<string, array<string, string>> */
protected array $options = [
'layouts' => [
'name' => 'Blade Layouts',
'description' => 'Shared layout views, such as the app layout, navigation menu, and Markdown page templates',
'group' => 'hyde-layouts',
],
'components' => [
'name' => 'Blade Components',
'description' => 'More or less self contained components, extracted for customizability and DRY code',
'group' => 'hyde-components',
],
'page-404' => [
'name' => '404 Page',
'description' => 'A beautiful 404 error page by the Laravel Collective',
'group' => 'hyde-page-404',
],
];
protected $description = 'Publish the Hyde components for customization. Note that existing files will be overwritten';

/** @var array<string, \Hyde\Console\Helpers\ViewPublishGroup> */
protected array $options;

public function handle(): int
{
$selected = (string) ($this->argument('category') ?? $this->promptForCategory());
$this->options = static::mapToKeys([
ViewPublishGroup::fromGroup('hyde-layouts', 'Blade Layouts', 'Shared layout views, such as the app layout, navigation menu, and Markdown page templates'),
ViewPublishGroup::fromGroup('hyde-components', 'Blade Components', 'More or less self contained components, extracted for customizability and DRY code'),
]);

if ($selected === 'all' || $selected === '') {
foreach ($this->options as $key => $_ignored) {
$this->publishOption($key);
}
} else {
$this->publishOption($selected);
$selected = ($this->argument('category') ?? $this->promptForCategory()) ?: 'all';

if ($selected !== 'all' && (bool) $this->argument('category') === false && ConsoleHelper::canUseLaravelPrompts($this->input)) {
$this->infoComment(sprintf('Selected category [%s]', $selected));
}

return Command::SUCCESS;
}
if (! in_array($selected, $allowed = array_merge(['all'], array_keys($this->options)), true)) {
$this->error("Invalid selection: '$selected'");
$this->infoComment('Allowed values are: ['.implode(', ', $allowed).']');

protected function publishOption(string $selected): void
{
Artisan::call('vendor:publish', [
'--tag' => $this->options[$selected]['group'] ?? $selected,
'--force' => true,
], $this->output);
return Command::FAILURE;
}

$files = $selected === 'all'
? collect($this->options)->flatMap(fn (ViewPublishGroup $option): array => $option->publishableFilesMap())->all()
: $this->options[$selected]->publishableFilesMap();

$publisher = $this->publishSelectedFiles($files, $selected === 'all');

$this->infoComment($publisher->formatOutput($selected));

return Command::SUCCESS;
}

protected function promptForCategory(): string
{
/** @var string $choice */
$choice = $this->choice(
'Which category do you want to publish?',
$this->formatPublishableChoices(),
0
SelectPrompt::fallbackUsing(function (SelectPrompt $prompt): string {
return $this->choice($prompt->label, $prompt->options, $prompt->default);
});

return $this->parseChoiceIntoKey(
select('Which category do you want to publish?', $this->formatPublishableChoices(), 0) ?: 'all'
);
}

$selection = $this->parseChoiceIntoKey($choice);
protected function formatPublishableChoices(): array
{
return collect($this->options)
->map(fn (ViewPublishGroup $option, string $key): string => sprintf('<comment>%s</comment>: %s', $key, $option->description))
->prepend('Publish all categories listed below')
->values()
->all();
}

$this->infoComment(sprintf("Selected category [%s]\n", $selection ?: 'all'));
protected function parseChoiceIntoKey(string $choice): string
{
return strstr(str_replace(['<comment>', '</comment>'], '', $choice), ':', true) ?: '';
}

return $selection;
/**
* @param array<string, \Hyde\Console\Helpers\ViewPublishGroup> $groups
* @return array<string, \Hyde\Console\Helpers\ViewPublishGroup>
*/
protected static function mapToKeys(array $groups): array
{
return collect($groups)->mapWithKeys(function (ViewPublishGroup $group): array {
return [Str::after($group->group, 'hyde-') => $group];
})->all();
}

protected function formatPublishableChoices(): array
/** @param array<string, string> $files */
protected function publishSelectedFiles(array $files, bool $isPublishingAll): InteractivePublishCommandHelper
{
$keys = ['Publish all categories listed below'];
foreach ($this->options as $key => $option) {
$keys[] = "<comment>$key</comment>: {$option['description']}";
$publisher = new InteractivePublishCommandHelper($files);

if (! $isPublishingAll && ConsoleHelper::canUseLaravelPrompts($this->input)) {
$publisher->only($this->promptUserForWhichFilesToPublish($publisher->getFileChoices()));
}

return $keys;
$publisher->publishFiles();

return $publisher;
}

protected function parseChoiceIntoKey(string $choice): string
/**
* @param array<string, string> $files
* @return array<string>
*/
protected function promptUserForWhichFilesToPublish(array $files): array
{
return strstr(str_replace(['<comment>', '</comment>'], '', $choice), ':', true) ?: '';
$choices = array_merge(['all' => '<comment>All files</comment>'], $files);

$prompt = new MultiSelectPrompt('Select the files you want to publish', $choices, [], 10, 'required', hint: 'Navigate with arrow keys, space to select, enter to confirm.');

$prompt->on('key', static::supportTogglingAll($prompt));

return (array) $prompt->prompt();
}

protected static function supportTogglingAll(MultiSelectPrompt $prompt): Closure
{
return function (string $key) use ($prompt): void {
static $isToggled = false;

if ($prompt->isHighlighted('all')) {
if ($key === Key::SPACE) {
$prompt->emit('key', Key::CTRL_A);

if ($isToggled) {
// We need to emit CTRL+A twice to deselect all for some reason
$prompt->emit('key', Key::CTRL_A);
$isToggled = false;
} else {
$isToggled = true;
}
} elseif ($key === Key::ENTER) {
if (! $isToggled) {
$prompt->emit('key', Key::CTRL_A);
}

$prompt->state = 'submit';
}
}
};
}
}
43 changes: 43 additions & 0 deletions packages/framework/src/Console/Helpers/ConsoleHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Hyde\Console\Helpers;

use Laravel\Prompts\Prompt;
use Symfony\Component\Console\Input\InputInterface;

/**
* @internal This class contains internal helpers for interacting with the console, and for easier testing.
*
* @codeCoverageIgnore This class provides internal testing helpers and does not need to be tested.
*/
class ConsoleHelper
{
/** Allows for mocking the Windows OS check. Remember to clear the mock after the test. */
protected static ?bool $enableLaravelPrompts = null;

public static function clearMocks(): void
{
static::$enableLaravelPrompts = null;
}

public static function disableLaravelPrompts(): void
{
static::$enableLaravelPrompts = false;
}

public static function mockWindowsOs(bool $isWindowsOs): void
{
static::$enableLaravelPrompts = ! $isWindowsOs;
}

public static function canUseLaravelPrompts(InputInterface $input): bool
{
if (static::$enableLaravelPrompts !== null) {
return static::$enableLaravelPrompts;
}

return $input->isInteractive() && windows_os() === false && Prompt::shouldFallback() === false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

namespace Hyde\Console\Helpers;

use Hyde\Facades\Filesystem;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

/**
* @internal This class offloads logic from the PublishViewsCommand class and should not be used elsewhere.
*/
class InteractivePublishCommandHelper
{
/** @var array<string, string> Map of source files to target files */
protected array $publishableFilesMap;

protected readonly int $originalFileCount;

/** @param array<string, string> $publishableFilesMap */
public function __construct(array $publishableFilesMap)
{
$this->publishableFilesMap = $publishableFilesMap;
$this->originalFileCount = count($publishableFilesMap);
}

/** @return array<string, string> */
public function getFileChoices(): array
{
return Arr::mapWithKeys($this->publishableFilesMap, /** @return array<string, string> */ function (string $target, string $source): array {
return [$source => $this->pathRelativeToDirectory($source, $this->getBaseDirectory())];
});
}

/**
* Only publish the selected files.
*
* @param array<string> $selectedFiles Array of selected file paths, matching the keys of the publishableFilesMap.
*/
public function only(array $selectedFiles): void
{
$this->publishableFilesMap = Arr::only($this->publishableFilesMap, $selectedFiles);
}

/** Find the most specific common parent directory path for the files, trimming as much as possible whilst keeping specificity and uniqueness. */
public function getBaseDirectory(): string
{
$partsMap = collect($this->publishableFilesMap)->map(function (string $file): array {
return explode('/', $file);
});

$commonParts = $partsMap->reduce(function (array $carry, array $parts): array {
return array_intersect($carry, $parts);
}, $partsMap->first());

return implode('/', $commonParts);
}

public function publishFiles(): void
{
foreach ($this->publishableFilesMap as $source => $target) {
Filesystem::ensureDirectoryExists(dirname($target));
Filesystem::copy($source, $target);
}
}

public function formatOutput(string $group): string
{
$fileCount = count($this->publishableFilesMap);
$publishedOneFile = $fileCount === 1;
$publishedAllGroups = $group === 'all';
$publishedAllFiles = $fileCount === $this->originalFileCount;
$selectedFilesModifier = $publishedAllFiles ? 'all' : 'selected';

return match (true) {
$publishedAllGroups => sprintf('Published all %d files to [%s]', $fileCount, $this->getBaseDirectory()),
$publishedOneFile => sprintf('Published selected file to [%s]', reset($this->publishableFilesMap)),
default => sprintf('Published %s [%s] files to [%s]', $selectedFilesModifier, Str::singular($group), $this->getBaseDirectory())
};
}

protected function pathRelativeToDirectory(string $source, string $directory): string
{
return Str::after($source, basename($directory).'/');
}
}
Loading
Loading