Passed
Push — master ( ad6b2a...b41ca0 )
by Caen
07:45 queued 14s
created

ConsoleOutput::printStartMessage()   B

Complexity

Conditions 7
Paths 1

Size

Total Lines 30
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 20
nc 1
nop 4
dl 0
loc 30
rs 8.6666
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\RealtimeCompiler;
6
7
use Closure;
8
use Hyde\Facades\Vite;
9
use Hyde\Hyde;
10
use Illuminate\Support\Str;
11
use Illuminate\Support\Arr;
12
use Illuminate\Support\Carbon;
13
use Symfony\Component\Console\Output\ConsoleOutput as SymfonyOutput;
14
15
use function Termwind\render;
16
17
class ConsoleOutput
18
{
19
    protected bool $verbose;
20
    protected SymfonyOutput $output;
21
22
    public function __construct(bool $verbose = false, ?SymfonyOutput $output = null)
23
    {
24
        $this->verbose = $verbose;
25
        $this->output = $output ?? new SymfonyOutput();
26
    }
27
28
    public function printStartMessage(string $host, int $port, array $environment = [], ?bool $willUseVite = false): void
29
    {
30
        $url = sprintf('%s://%s:%d', $port === 443 ? 'https' : 'http', $host, $port);
31
32
        $lines = Arr::whereNotNull([
33
            '',
34
            sprintf('<span class="text-blue-500">%s</span> <span class="text-gray">%s</span>', 'HydePHP Realtime Compiler', 'v'.Hyde::getInstance()->version()),
0 ignored issues
show
Bug introduced by
The method getInstance() does not exist on Hyde\Hyde. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

34
            sprintf('<span class="text-blue-500">%s</span> <span class="text-gray">%s</span>', 'HydePHP Realtime Compiler', 'v'.Hyde::/** @scrutinizer ignore-call */ getInstance()->version()),
Loading history...
35
            '',
36
            sprintf('<span class="text-white">Listening on:</span> <a href="%s" class="text-yellow-500">%s</a>', $url, $url),
37
            (config('hyde.server.dashboard.enabled') || Arr::has($environment, 'HYDE_SERVER_DASHBOARD')) && Arr::get($environment, 'HYDE_SERVER_DASHBOARD') === 'enabled' ?
38
                sprintf('<span class="text-white">Live dashboard:</span> <a href="%s/dashboard" class="text-yellow-500">%s/dashboard</a>', $url, $url) : null,
39
            (Vite::running() || $willUseVite) ?
40
                sprintf('<span class="text-white">Vite HMR server:</span> <a href="http://%s:5173" class="text-yellow-500">http://%s:5173</a>', $host, $host) : null,
41
            '',
42
        ]);
43
44
        $lineLength = max(array_map('strlen', array_map('strip_tags', $lines)));
45
46
        $lines = array_map(function (string $line) use ($lineLength): string {
47
            return sprintf('&nbsp;│&nbsp;<span class="text-white">%s</span>%s│',
48
                $line, str_repeat('&nbsp;', ($lineLength - strlen(strip_tags($line))) + 1)
49
            );
50
        }, $lines);
51
52
        $topLine = sprintf('&nbsp;╭%s╮', str_repeat('─', $lineLength + 2));
53
        $bottomLine = sprintf('&nbsp;╰%s╯', str_repeat('─', $lineLength + 2));
54
55
        $body = implode('<br>', array_merge([''], [$topLine], $lines, [$bottomLine], ['']));
56
57
        render("<div class=\"text-green-500\">$body</div>");
58
    }
59
60
    public function getFormatter(): Closure
61
    {
62
        return function (string $type, string $line): void {
63
            $this->handleOutput($line);
64
        };
65
    }
66
67
    /** @experimental */
68
    public function printMessage(string $message, string $context): void
69
    {
70
        $this->output->writeln(sprintf('%s ::context=[%s]', $message, $context));
71
    }
72
73
    protected function handleOutput(string $buffer): void
74
    {
75
        str($buffer)->trim()->explode("\n")->each(function (string $line): void {
76
            $this->renderLine($this->formatLineForOutput($line));
77
        });
78
    }
79
80
    protected function renderLine(?string $line): void
81
    {
82
        if ($line !== null) {
83
            render($line);
84
        }
85
    }
86
87
    protected function formatLineForOutput(string $line): ?string
88
    {
89
        if (str_contains($line, 'Development Server (http:')) {
90
            return $this->formatServerStartedLine($line);
91
        }
92
        if (str_contains($line, ']: ')) {
93
            return $this->formatRequestLine($line);
94
        }
95
        if (str_ends_with(trim($line), 'Accepted') || str_ends_with(trim($line), 'Closing')) {
96
            return $this->verbose ? $this->formatRequestStatusLine($line) : null;
97
        }
98
        if (str_contains($line, '::context=')) {
99
            return $this->formatContextLine($line);
100
        }
101
102
        return $this->formatLine($line, Carbon::now());
103
    }
104
105
    protected function formatServerStartedLine(string $line): string
106
    {
107
        return $this->formatLine(sprintf('PHP %s Development Server started. <span class="text-yellow-500">Use Ctrl+C to stop.</span>', PHP_VERSION), $this->parseDate($line), 'green-500');
108
    }
109
110
    protected function formatRequestLine(string $line): string
111
    {
112
        $dateString = Str::betweenFirst($line, '[', ']');
113
        $message = substr($line, strlen($dateString) + 3);
114
115
        $statusCode = Str::between($message, ' [', ']:');
116
        if ($statusCode >= 400) {
117
            $message = Str::replaceLast($statusCode, sprintf('<span class="text-red-500">%s</span>', $statusCode), $message);
118
            $iconColor = 'yellow-500';
119
        }
120
121
        return $this->formatLine($message, $this->parseDate($line), $iconColor ?? 'blue-500');
122
    }
123
124
    protected function formatRequestStatusLine(string $line): string
125
    {
126
        $address = trim(Str::between($line, ']', ' '));
127
        $status = str_contains($line, 'Accepted') ? 'Accepted' : 'Closing';
128
129
        return $this->formatLine(sprintf('%s %s', $address, $status), $this->parseDate($line));
130
    }
131
132
    protected function formatContextLine(string $line): string
133
    {
134
        $message = trim(Str::before($line, '::context='), '[]');
135
        $context = Str::between($line, '::context=[', ']');
136
        $success = str_contains($message, 'Created') || str_contains($message, 'Updated');
137
138
        return $this->formatLine($message, Carbon::now(), $success ? 'green-500' : 'blue-500', $context);
139
    }
140
141
    protected function formatLine(string $message, Carbon $date, string $iconColor = 'blue-500', string $context = ''): string
142
    {
143
        if ($context) {
144
            $context = "$context ";
145
        }
146
147
        return sprintf(<<<'HTML'
148
            <div class="flex w-full justify-between">
149
                <span>
150
                    <span class="text-%s">i</span>
151
                    %s
152
                </span>
153
                <span class="text-gray">%s%s</span>
154
            </div>
155
            HTML,
156
            $iconColor, $message, $context, $date->format('Y-m-d H:i:s')
157
        );
158
    }
159
160
    protected function parseDate(string $line): Carbon
161
    {
162
        return Carbon::parse(Str::betweenFirst($line, '[', ']'));
163
    }
164
}
165