Passed
Push — master ( 8cc5f6...d8f97d )
by Guillaume
40:07 queued 32:47
created

TailwindCSS::guideEntries()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 9
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 17
ccs 11
cts 11
cp 1
crap 2
rs 9.9666
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\HtmlPage;
10
use Wa72\HtmlPageDom\HtmlPageCrawler;
11
12
class TailwindCSS extends BaseDocset
13
{
14
    public const CODE = 'tailwindcss';
15
    public const NAME = 'Tailwind CSS';
16
    public const URL = 'tailwindcss.com';
17
    public const INDEX = 'docs/installation.html';
18
    public const PLAYGROUND = 'https://codesandbox.io/s/github/lbogdan/tailwindcss-playground';
19
    public const ICON_16 = 'favicon-16x16.png';
20
    public const ICON_32 = 'favicon-32x32.png';
21
    public const EXTERNAL_DOMAINS = [
22
    ];
23
24
25
    public function grab(): bool
26
    {
27
        $toIgnore = implode('|', [
28
            'blog.tailwindcss.com',
29
        ]);
30
31
        system(
32
            "echo; wget tailwindcss.com/docs \
33
                --mirror \
34
                --trust-server-names \
35
                --reject-regex='{$toIgnore}' \
36
                --page-requisites \
37
                --adjust-extension \
38
                --convert-links \
39
                --span-hosts \
40
                --domains={$this->externalDomains()} \
41
                --directory-prefix=storage/{$this->downloadedDirectory()} \
42
                -e robots=off \
43
                --quiet \
44
                --show-progress",
45
            $result
46
        );
47
48
        return $result === 0;
49
    }
50
51 16
    public function entries(string $file): Collection
52
    {
53 16
        $crawler = HtmlPageCrawler::create(Storage::get($file));
54
55 16
        $entries = collect();
56
57 16
        $entries = $entries->union($this->environmentEntries($crawler, $file));
58 16
        $entries = $entries->union($this->instructionEntries($crawler, $file));
59 16
        $entries = $entries->union($this->sampleEntries($crawler, $file));
60 16
        $entries = $entries->union($this->resourceEntries($crawler, $file));
61 16
        $entries = $entries->union($this->guideEntries($crawler, $file));
62 16
        $entries = $entries->union($this->sectionEntries($crawler, $file));
63
64 16
        return $entries;
65
    }
66
67 16
    protected function environmentEntries(HtmlPageCrawler $crawler, string $file)
68
    {
69 16
        $entries = collect();
70
71 16
        if (Str::contains($file, "{$this->url()}/community.html")) {
72 8
            $crawler->filter('h2')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
73 8
                $entries->push([
74 8
                    'name' => $this->cleanAnchorText($node->text()),
75 8
                    'type' => 'Environment',
76 8
                    'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
77
                ]);
78 8
            });
79
80 8
            return $entries;
81
        }
82 16
    }
83
84 16
    protected function instructionEntries(HtmlPageCrawler $crawler, string $file)
85
    {
86 16
        $entries = collect();
87
88 16
        if (Str::contains($file, "{$this->url()}/course.html")) {
89 8
            $crawler->filter('span.relative')->each(function (HtmlPageCrawler $node) use ($entries) {
90 8
                $entries->push([
91 8
                    'name' => $this->cleanAnchorText($node->text()),
92 8
                    'type' => 'Instruction',
93 8
                    'path' => $this->url() . '/' . $node->parents()->first()->attr('href'),
94
                ]);
95 8
            });
96
97 8
            return $entries;
98
        }
99 16
    }
100
101 16
    protected function sampleEntries(HtmlPageCrawler $crawler, string $file)
102
    {
103 16
        $entries = collect();
104
105 16
        if (Str::contains($file, "{$this->url()}/components.html")) {
106 8
            $crawler->filter('span.relative')->each(function (HtmlPageCrawler $node) use ($entries) {
107 8
                $entries->push([
108 8
                    'name' => $this->cleanAnchorText($node->text()),
109 8
                    'type' => 'Sample',
110 8
                    'path' => $this->url() . '/' . $node->parents()->first()->attr('href'),
111
                ]);
112 8
            });
113
114 8
            return $entries;
115
        }
116 16
    }
117
118 16
    protected function resourceEntries(HtmlPageCrawler $crawler, string $file)
119
    {
120 16
        $entries = collect();
121
122 16
        if (Str::contains($file, "{$this->url()}/resources.html")) {
123 16
            $crawler->filter('h2')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
124 16
                $entries->push([
125 16
                    'name' => $this->cleanAnchorText($node->text()),
126 16
                    'type' => 'Resource',
127 16
                    'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
128
                ]);
129 16
            });
130
131 16
            $crawler->filter('h3')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
132 16
                $entries->push([
133 16
                    'name' => $this->cleanAnchorText($node->text()),
134 16
                    'type' => 'Section',
135 16
                    'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
136
                ]);
137 16
            });
138
139 16
            return $entries;
140
        }
141 16
    }
142
143 16
    protected function guideEntries(HtmlPageCrawler $crawler, string $file)
144
    {
145 16
        $pageTitle = (new HtmlPage(Storage::get($file)))->getTitle();
146
147 16
        $entries = collect();
148
149 16
        if ($pageTitle === 'Tailwind CSS - A Utility-First CSS Framework for Rapidly Building Custom Designs') {
150 8
            $crawler->filter('#navWrapper li a')->each(function (HtmlPageCrawler $node) use ($entries) {
151 8
                $entries->push([
152 8
                    'name' => trim($node->text()),
153 8
                    'type' => 'Guide',
154 8
                    'path' => $this->url() . '/' . $node->attr('href'),
155
                ]);
156 8
            });
157
        }
158
159 16
        return $entries;
160
    }
161
162 16
    protected function sectionEntries(HtmlPageCrawler $crawler, string $file)
163
    {
164 16
        $entries = collect();
165
166 16
        $crawler->filter('h2')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
167 16
            $entries->push([
168 16
                'name' => $this->cleanAnchorText($node->text()),
169 16
                'type' => 'Section',
170 16
                'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
171
            ]);
172 16
        });
173
174 16
        return $entries;
175
    }
176
177 16
    public function format(string $file): string
178
    {
179 16
        $crawler = HtmlPageCrawler::create(Storage::get($file));
180
181 16
        $this->removeNavbarAndHeader($crawler);
182 16
        $this->removeLeftSidebar($crawler);
183 16
        $this->removeRightSidebar($crawler);
184 16
        $this->removeTailwindUIAlert($crawler);
185 16
        $this->removeUnwantedJavaScript($crawler);
186 16
        $this->ignoreDarkModeForSomeColors($crawler);
187 16
        $this->updateCSS($crawler);
188
189 16
        $this->insertOnlineRedirection($crawler, $file);
190 16
        $this->insertDashTableOfContents($crawler);
191
192 16
        return $crawler->saveHTML();
193
    }
194
195 16
    protected function removeNavbarAndHeader(HtmlPageCrawler $crawler)
196
    {
197 16
        $crawler->filter('#header')->remove();
198 16
    }
199
200 16
    protected function removeLeftSidebar(HtmlPageCrawler $crawler)
201
    {
202 16
        $crawler->filter('#sidebar')->remove();
203 16
    }
204
205 16
    protected function removeRightSidebar(HtmlPageCrawler $crawler)
206
    {
207 16
        $crawler->filter('#app div.flex > div.hidden')->remove();
208 16
    }
209
210 16
    protected function removeTailwindUIAlert(HtmlPageCrawler $crawler)
211
    {
212 16
        $crawler->filter('div.transition.transform.fixed.z-100')->remove();
213 16
    }
214
215 16
    protected function removeUnwantedJavaScript(HtmlPageCrawler $crawler)
216
    {
217 16
        $crawler->filter('script')->remove();
218 16
    }
219
220 16
    protected function ignoreDarkModeForSomeColors(HtmlPageCrawler $crawler)
221
    {
222 16
        $this->ignoreDarkModeForDefaultColorPaletteSection($crawler);
223 16
        $this->ignoreDarkModeForBackgroundColorTable($crawler);
224 16
        $this->ignoreDarkModeForTextColorAndPlaceholderColorTables($crawler);
225 16
        $this->ignoreDarkModeForBorderColorTable($crawler);
226 16
        $this->ignoreDarkModeForDivideColorTable($crawler);
227 16
    }
228
229 16
    protected function ignoreDarkModeForDefaultColorPaletteSection(HtmlPageCrawler $crawler)
230
    {
231 16
        $crawler->filter('h2 ~ div div.w-12')->addClass('dash-ignore-dark-mode');
232 16
    }
233
234 16
    protected function ignoreDarkModeForBackgroundColorTable(HtmlPageCrawler $crawler)
235
    {
236 16
        $crawler->filter('h2 + div td.w-24')->addClass('dash-ignore-dark-mode');
237 16
    }
238
239 16
    protected function ignoreDarkModeForTextColorAndPlaceholderColorTables(HtmlPageCrawler $crawler)
240
    {
241 16
        $crawler->filter('h2 + div td.w-16.font-medium')->addClass('dash-ignore-dark-mode');
242 16
    }
243
244 16
    protected function ignoreDarkModeForBorderColorTable(HtmlPageCrawler $crawler)
245
    {
246 16
        $crawler->filter('h2 + div td > div.absolute.m-2.border')->addClass('dash-ignore-dark-mode');
247 16
    }
248
249 16
    protected function ignoreDarkModeForDivideColorTable(HtmlPageCrawler $crawler)
250
    {
251 16
        $crawler->filter('h2 + div td > div.absolute.m-2')->addClass('dash-ignore-dark-mode');
252 16
    }
253
254 16
    protected function updateCSS(HtmlPageCrawler $crawler)
255
    {
256 16
        $this->updateTopPadding($crawler);
257 16
        $this->updateHeader($crawler);
258 16
        $this->updateContainerWidth($crawler);
259 16
        $this->updateBottomPadding($crawler);
260 16
    }
261
262 16
    protected function updateTopPadding(HtmlPageCrawler $crawler)
263
    {
264 16
        $crawler->filter('#app > div')
265 16
            ->removeClass('pt-24')
266 16
            ->addClass('pt-8')
267 16
            ->removeClass('pb-16')
268 16
            ->removeClass('lg:pt-28')
269 16
            ->addClass('px-4')
270
        ;
271 16
    }
272
273 16
    protected function updateHeader(HtmlPageCrawler $crawler)
274
    {
275 16
        $crawler->filter('#app > div > div.markdown')
276 16
            ->removeClass('lg:ml-0')
277 16
            ->removeClass('lg:mr-auto')
278 16
            ->removeClass('xl:mx-0')
279 16
            ->removeClass('xl:w-3/4')
280 16
            ->removeClass('max-w-3xl')
281 16
            ->removeClass('xl:px-12')
282
        ;
283 16
    }
284
285 16
    protected function updateContainerWidth(HtmlPageCrawler $crawler)
286
    {
287 16
        $crawler->filter('#__next > div:nth-child(2)')
288 16
            ->removeClass('max-w-screen-xl');
289
290 16
        $crawler->filter('#content-wrapper')
291 16
            ->removeClass('lg:static')
292 16
            ->removeClass('lg:max-h-full')
293 16
            ->removeClass('lg:overflow-visible')
294 16
            ->removeClass('lg:w-3/4')
295 16
            ->removeClass('xl:w-4/5')
296
        ;
297
298 16
        $crawler->filter('#app > div > div.flex > div.markdown')
299 16
            ->removeClass('xl:p-12')
300 16
            ->removeClass('max-w-3xl')
301 16
            ->removeClass('lg:ml-0')
302 16
            ->removeClass('lg:mr-auto')
303 16
            ->removeClass('xl:w-3/4')
304 16
            ->removeClass('xl:px-12')
305 16
            ->removeClass('xl:mx-0')
306
        ;
307 16
    }
308
309 16
    protected function updateBottomPadding(HtmlPageCrawler $crawler)
310
    {
311 16
        $crawler->filter('body')
312 16
            ->addClass('pb-16');
313 16
    }
314
315 16
    protected function insertOnlineRedirection(HtmlPageCrawler $crawler, string $file)
316
    {
317 16
        $onlineUrl = Str::substr(Str::after($file, $this->innerDirectory()), 1, -5);
318
319 16
        $crawler->filter('html')->prepend("<!-- Online page at $onlineUrl -->");
320 16
    }
321
322 16
    protected function insertDashTableOfContents(HtmlPageCrawler $crawler)
323
    {
324 16
        $crawler->filter('body')
325 16
            ->before('<a name="//apple_ref/cpp/Section/Top" class="dashAnchor"></a>');
326
327 16
        $crawler->filter('h2, h3')->each(function (HtmlPageCrawler $node) {
328 16
            $node->prepend(
329 16
                '<a id="' . Str::slug($node->text()) . '" name="//apple_ref/cpp/Section/' . rawurlencode($this->cleanAnchorText($node->text())) . '" class="dashAnchor"></a>'
330
            );
331 16
        });
332 16
    }
333
334 24
    protected function cleanAnchorText($anchorText)
335
    {
336 24
        return trim(preg_replace('/\s+/', ' ', $anchorText));
337
    }
338
}
339