Passed
Push — fix-removed-entries ( 3b3f78...6e2db6 )
by Guillaume
16:57
created

TailwindCSS::format()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 12
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 20
rs 9.8666
1
<?php
2
3
namespace App\Docsets;
4
5
use Godbout\DashDocsetBuilder\Docsets\BaseDocset;
6
use Illuminate\Support\Collection;
7
use Illuminate\Support\Facades\Storage;
8
use Illuminate\Support\Str;
9
use Wa72\HtmlPageDom\HtmlPageCrawler;
10
11
class TailwindCSS extends BaseDocset
12
{
13
    public const CODE = 'tailwindcss';
14
    public const NAME = 'Tailwind CSS';
15
    public const URL = 'tailwindcss.com';
16
    public const INDEX = 'docs/installation.html';
17
    public const PLAYGROUND = 'https://play.tailwindcss.com/';
18
    public const ICON_16 = 'favicon-16x16.png';
19
    public const ICON_32 = 'favicon-32x32.png';
20
    public const EXTERNAL_DOMAINS = [
21
    ];
22
23
24
    public function grab(): bool
25
    {
26
        $toIgnore = implode('|', [
27
            'blog.tailwindcss.com',
28
        ]);
29
30
        system(
31
            "echo; wget tailwindcss.com/docs \
32
                --mirror \
33
                --trust-server-names \
34
                --reject-regex='{$toIgnore}' \
35
                --page-requisites \
36
                --adjust-extension \
37
                --convert-links \
38
                --span-hosts \
39
                --domains={$this->externalDomains()} \
40
                --directory-prefix=storage/{$this->downloadedDirectory()} \
41
                -e robots=off \
42
                --quiet \
43
                --show-progress",
44
            $result
45
        );
46
47
        return $result === 0;
48
    }
49
50
    public function entries(string $file): Collection
51
    {
52
        $crawler = HtmlPageCrawler::create(Storage::get($file));
53
54
        $entries = collect();
55
56
        $entries = $entries->union($this->resourceEntries($crawler, $file));
57
        $entries = $entries->union($this->guideEntries($crawler, $file));
58
        $entries = $entries->union($this->sectionEntries($crawler, $file));
59
60
        return $entries;
61
    }
62
63
    protected function resourceEntries(HtmlPageCrawler $crawler, string $file)
64
    {
65
        $entries = collect();
66
67
        if (Str::contains($file, "{$this->url()}/resources.html")) {
68
            $crawler->filter('h2')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
69
                $entries->push([
70
                    'name' => $this->cleanAnchorText($node->text()),
71
                    'type' => 'Resource',
72
                    'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
73
                ]);
74
            });
75
76
            $crawler->filter('h3')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
77
                $entries->push([
78
                    'name' => $this->cleanAnchorText($node->text()),
79
                    'type' => 'Section',
80
                    'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
81
                ]);
82
            });
83
84
            return $entries;
85
        }
86
    }
87
88
    protected function guideEntries(HtmlPageCrawler $crawler, string $file)
89
    {
90
        $entries = collect();
91
92
        if (Str::contains($file, "{$this->url()}/docs.html")) {
93
            $crawler->filter('nav#nav li.mt-8 a')->each(function (HtmlPageCrawler $node) use ($entries) {
94
                if (! Str::contains($node->text(), 'Release Notes')) {
95
                    $entries->push([
96
                        'name' => trim($node->text()),
97
                        'type' => 'Guide',
98
                        'path' => $this->url() . '/' . $node->attr('href'),
99
                    ]);
100
                }
101
            });
102
        }
103
104
        return $entries;
105
    }
106
107
    protected function sectionEntries(HtmlPageCrawler $crawler, string $file)
108
    {
109
        $entries = collect();
110
111
        $crawler->filter('h2')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
112
            $entries->push([
113
                'name' => $this->cleanAnchorText($node->text()),
114
                'type' => 'Section',
115
                'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
116
            ]);
117
        });
118
119
        return $entries;
120
    }
121
122
    public function format(string $file): string
123
    {
124
        $crawler = HtmlPageCrawler::create(Storage::get($file));
125
126
        $this->removeTopbar($crawler);
127
        $this->removeLeftSidebar($crawler);
128
        $this->removeRightSidebar($crawler);
129
        $this->removeBottomMenuButton($crawler);
130
131
        $this->updateContainerWidth($crawler);
132
        $this->updateBottomPadding($crawler);
133
134
        $this->ignoreDarkModeForSomeColors($crawler);
135
136
        $this->removeUnwantedJavaScript($crawler);
137
138
        $this->insertOnlineRedirection($crawler, $file);
139
        $this->insertDashTableOfContents($crawler, $file);
140
141
        return $crawler->saveHTML();
142
    }
143
144
    protected function removeTopbar(HtmlPageCrawler $crawler)
145
    {
146
        $crawler->filter('div.sticky.top-0')->remove();
147
    }
148
149
    protected function removeLeftSidebar(HtmlPageCrawler $crawler)
150
    {
151
        $crawler->filter('#sidebar')->remove();
152
    }
153
154
    protected function removeRightSidebar(HtmlPageCrawler $crawler)
155
    {
156
        $crawler->filter('#content-wrapper div.hidden.flex-none.w-64')->remove();
157
    }
158
159
    protected function removeBottomMenuButton(HtmlPageCrawler $crawler)
160
    {
161
        $crawler->filter('#__next > button[type=button]')->remove();
162
    }
163
164
    protected function updateContainerWidth(HtmlPageCrawler $crawler)
165
    {
166
        $crawler->filter('#content-wrapper')->addClass('px-4');
167
    }
168
169
    protected function updateBottomPadding(HtmlPageCrawler $crawler)
170
    {
171
        $crawler->filter('#content-wrapper > div')
172
            ->removeClass('pb-24')
173
            ->addClass('pb-10')
174
        ;
175
    }
176
177
    protected function ignoreDarkModeForSomeColors(HtmlPageCrawler $crawler)
178
    {
179
        $this->ignoreDarkModeForDefaultColorPaletteSection($crawler);
180
        $this->ignoreDarkModeForVariousColorTables($crawler);
181
    }
182
183
    protected function ignoreDarkModeForDefaultColorPaletteSection(HtmlPageCrawler $crawler)
184
    {
185
        $crawler->filter('div.h-10.w-full.rounded.ring-1.ring-inset')->addClass('dash-ignore-dark-mode');
186
    }
187
188
    protected function ignoreDarkModeForVariousColorTables(HtmlPageCrawler $crawler)
189
    {
190
        $crawler->filter('h2 + div td:last-child')->addClass('dash-ignore-dark-mode');
191
    }
192
193
    protected function removeUnwantedJavaScript(HtmlPageCrawler $crawler)
194
    {
195
        $crawler->filter('script')->remove();
196
    }
197
198
    protected function insertOnlineRedirection(HtmlPageCrawler $crawler, string $file)
199
    {
200
        $onlineUrl = Str::substr(Str::after($file, $this->innerDirectory()), 1, -5);
201
202
        $crawler->filter('html')->prepend("<!-- Online page at https://$onlineUrl -->");
203
    }
204
205
    protected function insertDashTableOfContents(HtmlPageCrawler $crawler, string $file)
206
    {
207
        if (! Str::contains($file, "{$this->url()}/docs.html")) {
208
            $crawler->filter('body')
209
                ->before('<a name="//apple_ref/cpp/Section/Top" class="dashAnchor"></a>');
210
211
            $crawler->filter('h2, h3')->each(function (HtmlPageCrawler $node) {
212
                $node->prepend(
213
                    '<a id="' . Str::slug($node->text()) . '" name="//apple_ref/cpp/Section/' . rawurlencode($this->cleanAnchorText($node->text())) . '" class="dashAnchor"></a>'
214
                );
215
            });
216
        }
217
    }
218
219
    protected function cleanAnchorText($anchorText)
220
    {
221
        return trim(preg_replace('/\s+/', ' ', $anchorText));
222
    }
223
}
224