Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony assetic sass filter via node-sass?

I'm having some difficulties getting an assetic sass filter to work with node-sass instead of the ruby alternative. I have the following configuration in my config.yml file:

assetic:
    debug:          "%kernel.debug%"

    use_controller: false
    bundles:        [ ]

    write-to:       "%kernel.root_dir%/../web/assets"
    read_from:      "%kernel.root_dir%/../web/assets"

    node:        "%%PROGRAMFILES%%\nodejs\\node.exe"
    node_paths: ["%%USERPROFILE%%\\AppData\\Roaming\\npm\\node_modules"]
    sass:        "%%USERPROFILE%%\\AppData\\Roaming\\npm\\node-sass"
    ruby: null

    filters:
        cssrewrite: ~
        scss:
            output-style: compressed
            apply_to: "\.(scss|sass|css)%"

Although this triggers the right node-sass command, I'm not sure the configuration is correct. If I remove ruby: null it tries to run C:\Program Files...\path\to\ruby.exe %%USERPROFILE%%\\AppData\\Roaming\\npm\\node-sass which is totally wrong. But having ruby: null doesn't solve the problem as well, because it sets the wrong arguments (i.e --load-path instead of --include-path) and that messes things as well.

Does anybody know how to set the sass filter with node instead of ruby?

like image 477
tftd Avatar asked May 15 '15 17:05

tftd


1 Answers

I'd like to share my solution to this issue for anybody out there who might be experiencing it as well.

It appears that the BaseSassFilter is suited to work only with the ruby version. So I decided to create my own filter. Here is my class:

<?php

namespace App\YourBundle\Assetic\Filter;

use Assetic\Asset\AssetInterface;
use Assetic\Exception\FilterException;
use Assetic\Filter\Sass\BaseSassFilter;
use Assetic\Filter\Sass\SassFilter;

/**
 * This class is based on Assetic\Filter\Sass\SassFilter and is slightly modified to work with node-sass instead of Ruby.
 */

class NodeSassFilter extends BaseSassFilter
{

    const STYLE_NESTED     = 'nested';
    const STYLE_EXPANDED   = 'expanded';
    const STYLE_COMPACT    = 'compact';
    const STYLE_COMPRESSED = 'compressed';

    private $sassPath;
    private $scss;
    private $style;
    private $quiet;
    private $cacheLocation;

    public function __construct($sassPath = '/usr/bin/node-sass')
    {
        $this->sassPath = $sassPath;
        $this->cacheLocation = realpath(sys_get_temp_dir());
    }

    public function setScss($scss)
    {
        $this->scss = $scss;
    }

    public function setStyle($style)
    {
        $this->style = $style;
    }

    public function setQuiet($quiet)
    {
        $this->quiet = $quiet;
    }

    public function filterLoad(AssetInterface $asset)
    {
        $sassProcessArgs = array($this->sassPath);

        $pb = $this->createProcessBuilder($sassProcessArgs);

        if ($dir = $asset->getSourceDirectory()) {
            $pb->add('--include-path')->add($dir);
        }

        if ($this->style) {
            $pb->add('--output-style')->add($this->style);
        }

        if ($this->quiet) {
            $pb->add('--quiet');
        }

        // input
        $pb->add($input = tempnam(sys_get_temp_dir(), 'assetic_sass'));
        file_put_contents($input, $asset->getContent());

        $proc = $pb->getProcess();
        $code = $proc->run();
        unlink($input);

        if (0 !== $code) {
            throw FilterException::fromProcess($proc)->setInput($asset->getContent());
        }

        $asset->setContent($proc->getOutput());
    }

    public function filterDump(AssetInterface $asset)
    {
    }
}

Then, in your config.yml you add the following:

assetic:
    filters:
        nodesass:
            bin: "%sass.bin%"
            resource: '%kernel.root_dir%/config/filters/nodesass.xml'
            style: compressed
            apply_to: "\.scss%"

In app/config/filters/nodesass.xml you add the following xml:

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        <parameter key="assetic.filter.nodesass.class">App\YourBundle\Assetic\Filter\NodeSassFilter</parameter>
        <parameter key="assetic.filter.nodesass.bin">%assetic.sass.bin%</parameter>
        <parameter key="assetic.filter.nodesass.timeout">240</parameter>
        <parameter key="assetic.filter.nodesass.style">null</parameter>
        <parameter key="assetic.filter.nodesass.load_paths" type="collection" />
    </parameters>

    <services>
        <service id="assetic.filter.nodesass" class="%assetic.filter.nodesass.class%">
            <tag name="assetic.filter" alias="nodesass" />
            <argument>%assetic.filter.nodesass.bin%</argument>
            <call method="setTimeout"><argument>%assetic.filter.nodesass.timeout%</argument></call>
            <call method="setStyle"><argument>%assetic.filter.nodesass.style%</argument></call>
            <call method="setLoadPaths"><argument>%assetic.filter.nodesass.load_paths%</argument></call>
        </service>
    </services>

</container>

This should get things working for now.

like image 54
tftd Avatar answered Oct 12 '22 23:10

tftd