Passed
Push — 1.8.3 ( 0f2f6c )
by Guillaume
17:45
created

TailwindCSS::insertOnlineRedirection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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