PublishViewsCommand   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 129
Duplicated Lines 0 %

Importance

Changes 64
Bugs 0 Features 0
Metric Value
eloc 55
dl 0
loc 129
rs 10
c 64
b 0
f 0
wmc 23

8 Methods

Rating   Name   Duplication   Size   Complexity  
A formatPublishableChoices() 0 7 1
A parseChoiceIntoKey() 0 3 2
A supportTogglingAll() 0 22 6
A promptForGroup() 0 8 2
B handle() 0 29 7
A mapToKeys() 0 5 1
A promptUserForWhichFilesToPublish() 0 9 1
A publishSelectedFiles() 0 11 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Console\Commands;
6
7
use Closure;
8
use Hyde\Console\Concerns\Command;
9
use Hyde\Console\Helpers\ConsoleHelper;
10
use Hyde\Console\Helpers\InteractivePublishCommandHelper;
11
use Hyde\Console\Helpers\ViewPublishGroup;
12
use Illuminate\Support\Str;
13
use Laravel\Prompts\Key;
14
use Laravel\Prompts\MultiSelectPrompt;
15
use Laravel\Prompts\SelectPrompt;
16
17
use function Laravel\Prompts\select;
18
use function str_replace;
19
use function sprintf;
20
use function strstr;
21
22
/**
23
 * Publish the Hyde Blade views.
24
 */
25
class PublishViewsCommand extends Command
26
{
27
    /** @var string */
28
    protected $signature = 'publish:views {group? : The group to publish}';
29
30
    /** @var string */
31
    protected $description = 'Publish the Hyde components for customization. Note that existing files will be overwritten';
32
33
    /** @var array<string, \Hyde\Console\Helpers\ViewPublishGroup> */
34
    protected array $options;
35
36
    public function handle(): int
37
    {
38
        $this->options = static::mapToKeys([
39
            ViewPublishGroup::fromGroup('hyde-layouts', 'Blade Layouts', 'Shared layout views, such as the app layout, navigation menu, and Markdown page templates'),
40
            ViewPublishGroup::fromGroup('hyde-components', 'Blade Components', 'More or less self contained components, extracted for customizability and DRY code'),
41
        ]);
42
43
        $selected = ($this->argument('group') ?? $this->promptForGroup()) ?: 'all';
44
45
        if ($selected !== 'all' && (bool) $this->argument('group') === false && ConsoleHelper::canUseLaravelPrompts($this->input)) {
46
            $this->infoComment(sprintf('Selected group [%s]', $selected));
47
        }
48
49
        if (! in_array($selected, $allowed = array_merge(['all'], array_keys($this->options)), true)) {
50
            $this->error("Invalid selection: '$selected'");
51
            $this->infoComment('Allowed values are: ['.implode(', ', $allowed).']');
52
53
            return Command::FAILURE;
54
        }
55
56
        $files = $selected === 'all'
57
            ? collect($this->options)->flatMap(fn (ViewPublishGroup $option): array => $option->publishableFilesMap())->all()
0 ignored issues
show
Bug introduced by
$this->options of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

57
            ? collect(/** @scrutinizer ignore-type */ $this->options)->flatMap(fn (ViewPublishGroup $option): array => $option->publishableFilesMap())->all()
Loading history...
58
            : $this->options[$selected]->publishableFilesMap();
59
60
        $publisher = $this->publishSelectedFiles($files, $selected === 'all');
61
62
        $this->infoComment($publisher->formatOutput($selected));
63
64
        return Command::SUCCESS;
65
    }
66
67
    protected function promptForGroup(): string
68
    {
69
        SelectPrompt::fallbackUsing(function (SelectPrompt $prompt): string {
70
            return $this->choice($prompt->label, $prompt->options, $prompt->default);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->choice($pr...ions, $prompt->default) could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
71
        });
72
73
        return $this->parseChoiceIntoKey(
74
            select('Which group do you want to publish?', $this->formatPublishableChoices(), 0) ?: 'all'
75
        );
76
    }
77
78
    protected function formatPublishableChoices(): array
79
    {
80
        return collect($this->options)
0 ignored issues
show
Bug introduced by
$this->options of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

80
        return collect(/** @scrutinizer ignore-type */ $this->options)
Loading history...
81
            ->map(fn (ViewPublishGroup $option, string $key): string => sprintf('<comment>%s</comment>: %s', $key, $option->description))
82
            ->prepend('Publish all groups listed below')
83
            ->values()
84
            ->all();
85
    }
86
87
    protected function parseChoiceIntoKey(string $choice): string
88
    {
89
        return strstr(str_replace(['<comment>', '</comment>'], '', $choice), ':', true) ?: '';
90
    }
91
92
    /**
93
     * @param  array<string, \Hyde\Console\Helpers\ViewPublishGroup>  $groups
94
     * @return array<string, \Hyde\Console\Helpers\ViewPublishGroup>
95
     */
96
    protected static function mapToKeys(array $groups): array
97
    {
98
        return collect($groups)->mapWithKeys(function (ViewPublishGroup $group): array {
0 ignored issues
show
Bug introduced by
$groups of type array<string,Hyde\Consol...lpers\ViewPublishGroup> is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

98
        return collect(/** @scrutinizer ignore-type */ $groups)->mapWithKeys(function (ViewPublishGroup $group): array {
Loading history...
99
            return [Str::after($group->group, 'hyde-') => $group];
100
        })->all();
101
    }
102
103
    /** @param  array<string, string>  $files */
104
    protected function publishSelectedFiles(array $files, bool $isPublishingAll): InteractivePublishCommandHelper
105
    {
106
        $publisher = new InteractivePublishCommandHelper($files);
107
108
        if (! $isPublishingAll && ConsoleHelper::canUseLaravelPrompts($this->input)) {
109
            $publisher->only($this->promptUserForWhichFilesToPublish($publisher->getFileChoices()));
110
        }
111
112
        $publisher->publishFiles();
113
114
        return $publisher;
115
    }
116
117
    /**
118
     * @param  array<string, string>  $files
119
     * @return array<string>
120
     */
121
    protected function promptUserForWhichFilesToPublish(array $files): array
122
    {
123
        $choices = array_merge(['all' => '<comment>All files</comment>'], $files);
124
125
        $prompt = new MultiSelectPrompt('Select the files you want to publish', $choices, [], 10, 'required', hint: 'Navigate with arrow keys, space to select, enter to confirm.');
126
127
        $prompt->on('key', static::supportTogglingAll($prompt));
128
129
        return (array) $prompt->prompt();
130
    }
131
132
    protected static function supportTogglingAll(MultiSelectPrompt $prompt): Closure
133
    {
134
        return function (string $key) use ($prompt): void {
135
            static $isToggled = false;
136
137
            if ($prompt->isHighlighted('all')) {
138
                if ($key === Key::SPACE) {
139
                    $prompt->emit('key', Key::CTRL_A);
140
141
                    if ($isToggled) {
142
                        // We need to emit CTRL+A twice to deselect all for some reason
143
                        $prompt->emit('key', Key::CTRL_A);
144
                        $isToggled = false;
145
                    } else {
146
                        $isToggled = true;
147
                    }
148
                } elseif ($key === Key::ENTER) {
149
                    if (! $isToggled) {
150
                        $prompt->emit('key', Key::CTRL_A);
151
                    }
152
153
                    $prompt->state = 'submit';
154
                }
155
            }
156
        };
157
    }
158
}
159