How to make Sculpin skip certain sources

Posted on by Matthias Noback

Whenever I run the Sculpin generate command to generate a new version of the static website that is this blog, I notice there are a lot of useless files that get copied from the project's source/ directory to the project's output/ directory. All the files in the output/ directory will eventually get copied into a Docker image based on nginx (see also my blog series on Containerizing a static website with Docker). And since I'm on a hotel wifi now, I realized that now was the time to shave off any unnecessary weight from this Docker image.

My biggest mistake was not googling for the quickest way to skip certain sources from getting copied to output/. Instead, I set out to hook into Sculpin's event system. I thought it would be a good idea to create an event subscriber and make it subscribe to the Sculpin::EVENT_BEFORE_RUN event. Event subscribers for this event will receive a so-called SourceSetEvent, allowing them to mark certain sources as "should be skipped".

Sculpin is built on many Symfony components and it turned out to be quite easy to set up a traditional event subscriber, which I called SkipSources:

final class SkipSources implements EventSubscriberInterface
{
    /**
     * @var string[]
     */
    private $patterns = [];

    public function __construct(array $patterns)
    {
        $this->patterns = $patterns;
    }

    public function skipSourcesMatchingPattern(SourceSetEvent $event): void
    {
        // see below
    }

    public static function getSubscribedEvents(): array
    {
        return [
            Sculpin::EVENT_BEFORE_RUN => ['skipSourcesMatchingPattern']
        ];
    }
}

You can create your own Symfony-style bundles for a Sculpin project, but in this case defining a simple service in sculpin_kernel.yml seemed to me like a fine option too:

# in app/config/sculpin_kernel.yml

services:
    skip_sources:
        class: SculpinTools\SkipSources
        arguments:
            # more about this below
            - ["components/*", "_css/*", "_js/*"]
        tags:
            - { name: kernel.event_subscriber }

Due to the presence of the kernel.event_subscriber tag Symfony will make sure to register this service for the events returned by its getSubscribedEvents() method.

Looking for a way to use glob-like patterns to filter out certain sources, I stumbled on the fnmatch() function. After that, the code for the skipSourcesMatchingPattern() method ended up being quite simple:

foreach ($event->allSources() as $source) {
    foreach ($this->patterns as $pattern) {
        if (fnmatch($pattern, $source->relativePathname())) {
            $source->setShouldBeSkipped();
        }
    }
}

It matches a source with each of the patterns based on the source's relative pathname, as nothing outside of the source/ directory is relevant anyway. The patterns themselves are passed in as the event subscriber's first constructor argument. It's simply a list of glob-like string patterns.

My solution turned out to be quite an effective way to mark certain files as "should be skipped", which was my goal.

La grande finale

Just like in my previous blog post, I finally ran into another possible solution, that's actually built in to Sculpin - a simple ignore configuration key allowing you to ignore certain sources using glob-like patterns. It does use a rather elaborate pattern matching utility based on code from Ant. Not sure if this library and fnmatch() have "feature parity" though.

Turns out, all my extra work wasn't required after all. A simple Google search would have sufficed!

So I removed all of this code and configuration from my project. But I still wanted to share my journey with you. And who knows, it could just be useful to have an example lying around of how to register an event subscriber and hook into Sculpin's build lifecycle...

PHP Sculpin
Comments
This website uses MailComments: you can send your comments to this post by email. Read more about MailComments, including suggestions for writing your comments (in HTML or Markdown).