Passed
Push — master ( 3afcce...e044cf )
by Guillaume
08:13
created

TailwindCSS::resourceEntries()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 13
nc 2
nop 2
dl 0
loc 22
ccs 0
cts 14
cp 0
crap 6
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
namespace App\Docsets;
4
5
use Illuminate\Support\Str;
6
use Wa72\HtmlPageDom\HtmlPage;
7
use Illuminate\Support\Collection;
8
use Wa72\HtmlPageDom\HtmlPageCrawler;
9
use Illuminate\Support\Facades\Storage;
10
use Godbout\DashDocsetBuilder\Docsets\BaseDocset;
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/index.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
        'refactoring-ui.nyc3.cdn.digitaloceanspaces.com',
23
        'jsdelivr.net',
24
        'code.jquery.com',
25
        'rsms.me',
26
        'googleapis.com',
27
        // 'images.unsplash.com'
28
    ];
29
30
    public function entries(string $file): Collection
31
    {
32
        $crawler = HtmlPageCrawler::create(Storage::get($file));
33
34
        $entries = collect();
35
36
        $entries = $entries->merge($this->environmentEntries($crawler, $file));
37
        $entries = $entries->merge($this->instructionEntries($crawler, $file));
38
        $entries = $entries->merge($this->sampleEntries($crawler, $file));
39
        $entries = $entries->merge($this->resourceEntries($crawler, $file));
40
        $entries = $entries->merge($this->guideEntries($crawler, $file));
41
        $entries = $entries->merge($this->sectionEntries($crawler, $file));
42
43
        return $entries;
44
    }
45
46
    protected function environmentEntries(HtmlPageCrawler $crawler, string $file)
47
    {
48
        $entries = collect();
49
50
        if (Str::contains($file, "{$this->url()}/community/index.html")) {
51
            $crawler->filter('h2')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
52
                $entries->push([
53
                    'name' => $this->cleanAnchorText($node->text()),
54
                    'type' => 'Environment',
55
                    'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
56
                ]);
57
            });
58
59
            return $entries;
60
        }
61
    }
62
63
    protected function instructionEntries(HtmlPageCrawler $crawler, string $file)
64
    {
65
        $entries = collect();
66
67
        if (Str::contains($file, "{$this->url()}/screencasts/index.html")) {
68
            $crawler->filter('span.relative')->each(function (HtmlPageCrawler $node) use ($entries) {
69
                $entries->push([
70
                    'name' => $this->cleanAnchorText($node->text()),
71
                    'type' => 'Instruction',
72
                    'path' => $this->url() . '/docs/' . $node->parents()->first()->attr('href'),
73
                ]);
74
            });
75
76
            return $entries;
77
        }
78
    }
79
80
    protected function sampleEntries(HtmlPageCrawler $crawler, string $file)
81
    {
82
        $entries = collect();
83
84
        if (Str::contains($file, "{$this->url()}/components/index.html")) {
85
            $crawler->filter('span.relative')->each(function (HtmlPageCrawler $node) use ($entries) {
86
                $entries->push([
87
                    'name' => $this->cleanAnchorText($node->text()),
88
                    'type' => 'Sample',
89
                    'path' => $this->url() . '/components/' . $node->parents()->first()->attr('href'),
90
                ]);
91
            });
92
93
            return $entries;
94
        }
95
    }
96
97
    protected function resourceEntries(HtmlPageCrawler $crawler, string $file)
98
    {
99
        $entries = collect();
100
101
        if (Str::contains($file, "{$this->url()}/resources/index.html")) {
102
            $crawler->filter('h2')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
103
                $entries->push([
104
                    'name' => $this->cleanAnchorText($node->text()),
105
                    'type' => 'Resource',
106
                    'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
107
                ]);
108
            });
109
110
            $crawler->filter('h3')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
111
                $entries->push([
112
                    'name' => $this->cleanAnchorText($node->text()),
113
                    'type' => 'Section',
114
                    'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
115
                ]);
116
            });
117
118
            return $entries;
119
        }
120
    }
121
122
    protected function guideEntries(HtmlPageCrawler $crawler, string $file)
123
    {
124
        $pageTitle = (new HtmlPage(Storage::get($file)))->getTitle();
125
126
        $entries = collect();
127
128
        if ($pageTitle === 'Tailwind CSS - A Utility-First CSS Framework for Rapidly Building Custom Designs') {
129
            $crawler->filter('#navWrapper li a')->each(function (HtmlPageCrawler $node) use ($entries) {
130
                $entries->push([
131
                    'name' => trim($node->text()),
132
                    'type' => 'Guide',
133
                    'path' => $this->url() . '/' . $node->attr('href'),
134
                ]);
135
            });
136
        }
137
138
        return $entries;
139
    }
140
141
    protected function sectionEntries(HtmlPageCrawler $crawler, string $file)
142
    {
143
        $entries = collect();
144
145
        $crawler->filter('h2')->each(function (HtmlPageCrawler $node) use ($entries, $file) {
146
            $entries->push([
147
                'name' => $this->cleanAnchorText($node->text()),
148
                'type' => 'Section',
149
                'path' => Str::after($file . '#' . Str::slug($node->text()), $this->innerDirectory()),
150
            ]);
151
        });
152
153
        return $entries;
154
    }
155
156
    public function format(string $html): string
157
    {
158
        $crawler = HtmlPageCrawler::create($html);
159
160
        $this->removeNavbarAndHeader($crawler);
161
        $this->removeLeftSidebar($crawler);
162
        $this->removeRightSidebar($crawler);
163
        $this->removeTailwindUIAlert($crawler);
164
        $this->removeUnwantedCSS($crawler);
165
        $this->removeUnwantedJavaScript($crawler);
166
        $this->ignoreDarkModeForSomeColors($crawler);
167
        $this->updateCSS($crawler);
168
        $this->insertDashTableOfContents($crawler);
169
170
        return $crawler->saveHTML();
171
    }
172
173
    protected function removeNavbarAndHeader(HtmlPageCrawler $crawler)
174
    {
175
        $crawler->filter('body > div:first-child')->remove();
176
    }
177
178
    protected function removeLeftSidebar(HtmlPageCrawler $crawler)
179
    {
180
        $crawler->filter('#sidebar')->remove();
181
    }
182
183
    protected function removeRightSidebar(HtmlPageCrawler $crawler)
184
    {
185
        $crawler->filter('#app div.flex > div.hidden')->remove();
186
    }
187
188
    protected function removeTailwindUIAlert(HtmlPageCrawler $crawler)
189
    {
190
        $crawler->filter('body > div.transition.transform.fixed.z-100')->remove();
191
    }
192
193
    protected function removeUnwantedCSS(HtmlPageCrawler $crawler)
194
    {
195
        $crawler->filter('link[href*="docsearch.min.css"]')->remove();
196
    }
197
198
    protected function removeUnwantedJavaScript(HtmlPageCrawler $crawler)
199
    {
200
        $crawler->filter('script[src*=analytics]')->remove();
201
        $crawler->filter('script[src*=docsearch]')->remove();
202
        $crawler->filter('script[src*=gtag]')->remove();
203
        $crawler->filterXPath("//script[text()[contains(.,'docsearch')]]")->remove();
204
        $crawler->filterXPath("//script[text()[contains(.,'gtag')]]")->remove();
205
        $crawler->filter('script[src*=jquery]')
206
            ->removeAttribute('integrity')
207
            ->removeAttribute('crossorigin');
208
    }
209
210
    protected function ignoreDarkModeForSomeColors(HtmlPageCrawler $crawler)
211
    {
212
        $this->ignoreDarkModeForDefaultColorPaletteSection($crawler);
213
        $this->ignoreDarkModeForBackgroundColorTable($crawler);
214
        $this->ignoreDarkModeForTextColorAndPlaceholderColorTables($crawler);
215
        $this->ignoreDarkModeForBorderColorTable($crawler);
216
        $this->ignoreDarkModeForDivideColorTable($crawler);
217
    }
218
219
    protected function ignoreDarkModeForDefaultColorPaletteSection(HtmlPageCrawler $crawler)
220
    {
221
        $crawler->filter('h2 ~ div div.w-12')->addClass('dash-ignore-dark-mode');
222
    }
223
224
    protected function ignoreDarkModeForBackgroundColorTable(HtmlPageCrawler $crawler)
225
    {
226
        $crawler->filter('h2 + div td.w-24.p-2.font-mono.text-xs')->addClass('dash-ignore-dark-mode');
227
    }
228
229
    protected function ignoreDarkModeForTextColorAndPlaceholderColorTables(HtmlPageCrawler $crawler)
230
    {
231
        $crawler->filter('h2 + div td.relative.w-16.font-medium.border-t.text-base')->addClass('dash-ignore-dark-mode');
232
    }
233
234
    protected function ignoreDarkModeForBorderColorTable(HtmlPageCrawler $crawler)
235
    {
236
        $crawler->filter('h2 + div td > div.absolute.m-2.border')->addClass('dash-ignore-dark-mode');
237
    }
238
239
    protected function ignoreDarkModeForDivideColorTable(HtmlPageCrawler $crawler)
240
    {
241
        $crawler->filter('h2 + div td > div.absolute.m-2.divide-y')->addClass('dash-ignore-dark-mode');
242
    }
243
244
    protected function updateCSS(HtmlPageCrawler $crawler)
245
    {
246
        $this->updateTopPadding($crawler);
247
        $this->updateHeader($crawler);
248
        $this->updateContainerWidth($crawler);
249
        $this->updateBottomPadding($crawler);
250
    }
251
252
    protected function updateTopPadding(HtmlPageCrawler $crawler)
253
    {
254
        $crawler->filter('#app > div')
255
            ->removeClass('pt-12')
256
            ->removeClass('pt-24')
257
            ->removeClass('pb-16')
258
            ->removeClass('lg:pt-28')
259
            ->removeClass('lg:pt-12')
260
        ;
261
    }
262
263
    protected function updateHeader(HtmlPageCrawler $crawler)
264
    {
265
        $crawler->filter('#app > div > div.markdown')
266
            ->removeClass('lg:ml-0')
267
            ->removeClass('lg:mr-auto')
268
            ->removeClass('xl:mx-0')
269
            ->removeClass('xl:w-3/4')
270
            ->removeClass('max-w-3xl')
271
            ->removeClass('xl:px-12')
272
        ;
273
    }
274
275
    protected function updateContainerWidth(HtmlPageCrawler $crawler)
276
    {
277
        $crawler->filter('body > div:first-child')
278
            ->removeClass('max-w-screen-xl');
279
280
        $crawler->filter('#content-wrapper')
281
            ->removeClass('lg:static')
282
            ->removeClass('lg:max-h-full')
283
            ->removeClass('lg:overflow-visible')
284
            ->removeClass('lg:w-3/4')
285
            ->removeClass('xl:w-4/5');
286
287
        $crawler->filter('#app > div > div.flex > div.markdown')
288
            ->removeClass('xl:p-12')
289
            ->removeClass('max-w-3xl')
290
            ->removeClass('lg:ml-0')
291
            ->removeClass('lg:mr-auto')
292
            ->removeClass('xl:w-3/4')
293
            ->removeClass('xl:px-12')
294
            ->removeClass('xl:mx-0');
295
    }
296
297
    protected function updateBottomPadding(HtmlPageCrawler $crawler)
298
    {
299
        $crawler->filter('body')
300
            ->addClass('pb-8');
301
    }
302
303
    protected function insertDashTableOfContents(HtmlPageCrawler $crawler)
304
    {
305
        $crawler->filter('h1')
306
            ->before('<a name="//apple_ref/cpp/Section/Top" class="dashAnchor"></a>');
307
308
        $crawler->filter('h2, h3')->each(function (HtmlPageCrawler $node) {
309
            $node->prepend(
310
                '<a id="' . Str::slug($node->text()) . '" name="//apple_ref/cpp/Section/' . rawurlencode($this->cleanAnchorText($node->text())) . '" class="dashAnchor"></a>'
311
            );
312
        });
313
    }
314
315
    protected function cleanAnchorText($anchorText)
316
    {
317
        return trim(preg_replace('/\s+/', ' ', $anchorText));
318
    }
319
}
320