Skip to content

Commit

Permalink
Reimplements HtmlParser using Symfony DomCrawler (#21)
Browse files Browse the repository at this point in the history
Thanks a lot to @JohnathonKoster 🚀
  • Loading branch information
JohnathonKoster authored Dec 21, 2021
1 parent 49a31b8 commit 799b395
Show file tree
Hide file tree
Showing 9 changed files with 438 additions and 20 deletions.
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,50 @@ Let's assume you want to style links inside lists differently than a general lin

The ordering does not matter. Classify will take care of that for you.

## Working With CSS Frameworks Like Tailwind CSS

Some CSS frameworks utilize JIT compiling, or have some other means of purging CSS classes from production builds to reduce file sizes. Classify provides a Laravel Artisan command to generate a JavaScript configuration file containing all Classify class names that can be used when configuring your CSS build process.

Running the following command from the root of the project:

```
php artisan classify:export
```

Would create a new (or update an existing) `classify.config.js` JavaScript file containing your Classify class name configuration:

```js
module.exports.classes = [
"mt-8",
"first:mt-0",
"text-xs",
"uppercase",
];
```

This configuration file can be used in conjunction with your CSS framework's build tools. For example, we can add our Classify class names to the Tailwind CSS 3 safe list ([https://tailwindcss.com/docs/content-configuration#safelisting-classes](https://tailwindcss.com/docs/content-configuration#safelisting-classes)):

Within `tailwind.config.js`:

```js
const classify = require('./classify.config');

module.exports = {
content: [
'./pages/**/*.{html,js}'
'./components/**/*.{html,js}',
],
safelist: [
...classify.classes,
'bg-red-500',
'text-3xl',
'lg:text-4xl',
]
// ...
}
```

> **Important**: Remember to run the `php artisan classify:export` command after making these changes to your Tailwind CSS configuration file before you build your front-end assets!
# More about us
- [www.statamic-agency.com](https://statamic-agency.com)
Expand Down
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
"require": {
"php": "^7.4 || ^8.0",
"illuminate/support": "^8.0",
"statamic/cms": ">=3.0.38 || 3.1.* || 3.2.*"
"statamic/cms": ">=3.0.38 || 3.1.* || 3.2.*",
"symfony/dom-crawler": "^5.4",
"symfony/css-selector": "^5.4"
},
"require-dev": {
"orchestra/testbench": "^6.0",
Expand Down
41 changes: 41 additions & 0 deletions src/Commands/Export.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace VV\Classify\Commands;

use Illuminate\Console\Command;

class Export extends Command
{
protected $signature = 'classify:export';
protected $description = 'Generates a JSON configuration file containing all Classify class names.';

/**
* Generates a JavaScript configuration file containing all Classify class names.
*/
public static function getConfigurationContents(): string
{
$allConfig = config('classify');
$classNames = [];

foreach ($allConfig as $config) {
foreach ($config as $classList) {
$classes = collect(explode(' ', $classList))->reject(function ($class) {
return strlen(trim($class)) == 0;
})->values()->all();

foreach ($classes as $class) {
if (! in_array($class, $classNames)) {
$classNames[] = $class;
}
}
}
}

return 'module.exports.classes = '.json_encode($classNames, JSON_PRETTY_PRINT).';';
}

public function handle()
{
file_put_contents(base_path('classify.config.js'), self::getConfigurationContents());
}
}
44 changes: 28 additions & 16 deletions src/HtmlParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,42 @@

namespace VV\Classify;

use Symfony\Component\DomCrawler\Crawler;

class HtmlParser implements ClassifyParser
{
public function parse(Tag $tag, string $value): string
{
return preg_replace(
$this->defineRegexPattern($tag),
$this->defineReplacement($tag),
$value
);
}
$selector = $tag->tag;

private function defineRegexPattern(Tag $tag): string
{
$pattern = '';
if (count($tag->before) > 0) {
$selector = implode(' > ', $tag->before).' > '.$selector;

foreach ($tag->before as $name) {
$pattern .= "<{$name}[^>]*>[^<]*";
$firstPart = strtolower($tag->before[0]);

// Guard against producing selectors in the form: body > body > span.
if ($firstPart != 'body') {
$selector = 'body > '.$selector;
}
}

return "/({$pattern})(<{$tag->tag})(?! class)/iU";
}
$crawler = new Crawler($value);
$nodes = $crawler->filter($selector);

private function defineReplacement(Tag $tag): string
{
return "$1<{$tag->tag} class=\"{$tag->classes}\"";
if (count($nodes) == 0) {
return $value;
}

foreach ($nodes as $node) {
$node->setAttribute('class', $tag->classes);
}

// Generate the HTML with our class adjustments made.
$result = $crawler->html();

// Removes the <body> and </body> tags that get added since it's a fragment.
$result = substr($result, 6);

return substr($result, 0, -7);
}
}
3 changes: 2 additions & 1 deletion src/Modifiers/Classify.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ public function index($value, $params, $context)
* Convert style segment information into the Tag class.
* They will get Sorted by count to parse nested tags first.
*/

$segments = collect($this->getStyleSegments($styleSet))
->map(fn ($classes, $tags) => new Tag($tags, $classes))
->sortByDesc('count');
->sortBy('count');

$segments->each(function ($segment) use (&$value) {
$value = app(ClassifyParser::class)->parse($segment, $value);
Expand Down
4 changes: 4 additions & 0 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ class ServiceProvider extends AddonServiceProvider
\VV\Classify\Modifiers\Classify::class,
];

protected $commands = [
\VV\Classify\Commands\Export::class,
];

public function boot()
{
parent::boot();
Expand Down
10 changes: 9 additions & 1 deletion src/Tag.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,15 @@ public function __construct(string $tags, string $classes)
*/
private function convertTagsToArray(string $tags): array
{
return explode(' ', $tags);
return collect(explode(' ', $tags))->reject(function ($tag) {
// Removes explicitly entered > symbols. These will be added
// back later when constructing the final CSS-style selector.
if ($tag == '>') {
return true;
}

return strlen(trim($tag)) == 0;
})->values()->all();
}

/*
Expand Down
Loading

0 comments on commit 799b395

Please sign in to comment.