Passed
Push — master ( 126e7b...74de7d )
by Guillaume
20:09 queued 14:22
created

TailwindCSS   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 217
Duplicated Lines 0 %

Test Coverage

Coverage 92.37%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 91
c 1
b 0
f 0
dl 0
loc 217
ccs 109
cts 118
cp 0.9237
rs 10
wmc 24

20 Methods

Rating   Name   Duplication   Size   Complexity  
A grab() 0 24 1
A guideEntries() 0 17 3
A resourceEntries() 0 22 2
A sectionEntries() 0 13 1
A entries() 0 11 1
A ignoreDarkModeForSomeColors() 0 4 1
A insertOnlineRedirection() 0 5 1
A removeLeftSidebar() 0 3 1
A ignoreDarkModeForVariousColorTables() 0 3 1
A removeTopbar() 0 3 1
A removeAnnouncementBar() 0 3 1
A removeBottomMenuButton() 0 3 1
A removeRightSidebar() 0 3 1
A updateContainerWidth() 0 3 1
A ignoreDarkModeForDefaultColorPaletteSection() 0 3 1
A insertDashTableOfContents() 0 9 2
A updateBottomPadding() 0 5 1
A removeUnwantedJavaScript() 0 3 1
A cleanAnchorText() 0 3 1
A format() 0 21 1
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 16
    public function entries(string $file): Collection
51
    {
52 16
        $crawler = HtmlPageCrawler::create(Storage::get($file));
53
54 16
        $entries = collect();
55
56 16
        $entries = $entries->union($this->resourceEntries($crawler, $file));
57 16
        $entries = $entries->union($this->guideEntries($crawler, $file));
58 16
        $entries = $entries->union($this->sectionEntries($crawler, $file));
59
60 16
        return $entries;
61
    }
62
63 16
    protected function resourceEntries(HtmlPageCrawler $crawler, string $file)
64
    {
65 16
        $entries = collect();
66
67 16
        if (Str::contains($file, "{$this->url()}/resources.html")) {
68 16
            $crawler->filter('h2')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
69 16
                $entries->push([
70 16
                    'name' => $this->cleanAnchorText($node->text()),
71 16
                    'type' => 'Resource',
72 16
                    'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
73
                ]);
74 16
            });
75
76 16
            $crawler->filter('h3')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
77 16
                $entries->push([
78 16
                    'name' => $this->cleanAnchorText($node->text()),
79 16
                    'type' => 'Section',
80 16
                    'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
81
                ]);
82 16
            });
83
84 16
            return $entries;
85
        }
86 16
    }
87
88 16
    protected function guideEntries(HtmlPageCrawler $crawler, string $file)
89
    {
90 16
        $entries = collect();
91
92 16
        if (Str::contains($file, "{$this->url()}/docs.html")) {
93 8
            $crawler->filter('nav#nav li.mt-8 a')->each(function (HtmlPageCrawler $node) use ($entries) {
94 8
                if (! Str::contains($node->text(), 'Release Notes')) {
95 8
                    $entries->push([
96 8
                        'name' => trim($node->text()),
97 8
                        'type' => 'Guide',
98 8
                        'path' => $this->url() . '/' . $node->attr('href'),
99
                    ]);
100
                }
101 8
            });
102
        }
103
104 16
        return $entries;
105
    }
106
107 16
    protected function sectionEntries(HtmlPageCrawler $crawler, string $file)
108
    {
109 16
        $entries = collect();
110
111 16
        $crawler->filter('h2')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
112 16
            $entries->push([
113 16
                'name' => $this->cleanAnchorText($node->text()),
114 16
                'type' => 'Section',
115 16
                'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
116
            ]);
117 16
        });
118
119 16
        return $entries;
120
    }
121
122 16
    public function format(string $file): string
123
    {
124 16
        $crawler = HtmlPageCrawler::create(Storage::get($file));
125
126 16
        $this->removeAnnouncementBar($crawler);
127 16
        $this->removeTopbar($crawler);
128 16
        $this->removeLeftSidebar($crawler);
129 16
        $this->removeRightSidebar($crawler);
130 16
        $this->removeBottomMenuButton($crawler);
131
132 16
        $this->updateContainerWidth($crawler);
133 16
        $this->updateBottomPadding($crawler);
134
135 16
        $this->ignoreDarkModeForSomeColors($crawler);
136
137 16
        $this->removeUnwantedJavaScript($crawler);
138
139 16
        $this->insertOnlineRedirection($crawler, $file);
140 16
        $this->insertDashTableOfContents($crawler, $file);
141
142 16
        return $crawler->saveHTML();
143
    }
144
145 16
    protected function removeAnnouncementBar(HtmlPageCrawler $crawler)
146
    {
147 16
        $crawler->filter('#__next > div.from-indigo-600.to-light-blue-500')->remove();
148 16
    }
149
150 16
    protected function removeTopbar(HtmlPageCrawler $crawler)
151
    {
152 16
        $crawler->filter('div.sticky.top-0')->remove();
153 16
    }
154
155 16
    protected function removeLeftSidebar(HtmlPageCrawler $crawler)
156
    {
157 16
        $crawler->filter('#sidebar')->remove();
158 16
    }
159
160 16
    protected function removeRightSidebar(HtmlPageCrawler $crawler)
161
    {
162 16
        $crawler->filter('#content-wrapper div.hidden.flex-none.w-64')->remove();
163 16
    }
164
165 16
    protected function removeBottomMenuButton(HtmlPageCrawler $crawler)
166
    {
167 16
        $crawler->filter('#__next > button[type=button]')->remove();
168 16
    }
169
170 16
    protected function updateContainerWidth(HtmlPageCrawler $crawler)
171
    {
172 16
        $crawler->filter('#content-wrapper')->addClass('px-4');
173 16
    }
174
175 16
    protected function updateBottomPadding(HtmlPageCrawler $crawler)
176
    {
177 16
        $crawler->filter('#content-wrapper > div > div')
178 16
            ->removeClass('pb-24')
179 16
            ->addClass('pb-10')
180
        ;
181 16
    }
182
183 16
    protected function ignoreDarkModeForSomeColors(HtmlPageCrawler $crawler)
184
    {
185 16
        $this->ignoreDarkModeForDefaultColorPaletteSection($crawler);
186 16
        $this->ignoreDarkModeForVariousColorTables($crawler);
187 16
    }
188
189 16
    protected function ignoreDarkModeForDefaultColorPaletteSection(HtmlPageCrawler $crawler)
190
    {
191 16
        $crawler->filter('div.h-10.w-full.rounded.ring-1.ring-inset')->addClass('dash-ignore-dark-mode');
192 16
    }
193
194 16
    protected function ignoreDarkModeForVariousColorTables(HtmlPageCrawler $crawler)
195
    {
196 16
        $crawler->filter('h2 + div td:last-child')->addClass('dash-ignore-dark-mode');
197 16
    }
198
199 16
    protected function removeUnwantedJavaScript(HtmlPageCrawler $crawler)
200
    {
201 16
        $crawler->filter('script')->remove();
202 16
    }
203
204 16
    protected function insertOnlineRedirection(HtmlPageCrawler $crawler, string $file)
205
    {
206 16
        $onlineUrl = Str::substr(Str::after($file, $this->innerDirectory()), 1, -5);
207
208 16
        $crawler->filter('html')->prepend("<!-- Online page at https://$onlineUrl -->");
209 16
    }
210
211 16
    protected function insertDashTableOfContents(HtmlPageCrawler $crawler, string $file)
212
    {
213 16
        if (! Str::contains($file, "{$this->url()}/docs.html")) {
214 16
            $crawler->filter('body')
215 16
                ->before('<a name="//apple_ref/cpp/Section/Top" class="dashAnchor"></a>');
216
217 16
            $crawler->filter('h2, h3')->each(function (HtmlPageCrawler $node) {
218 16
                $node->prepend(
219 16
                    '<a id="' . Str::slug($node->text()) . '" name="//apple_ref/cpp/Section/' . rawurlencode($this->cleanAnchorText($node->text())) . '" class="dashAnchor"></a>'
220
                );
221 16
            });
222
        }
223 16
    }
224
225 24
    protected function cleanAnchorText($anchorText)
226
    {
227 24
        return trim(preg_replace('/\s+/', ' ', $anchorText));
228
    }
229
}
230