Skip to content

Commit

Permalink
Merge pull request #752 from mikeyclarke/libsass-filter
Browse files Browse the repository at this point in the history
Add filter for PHP Libsass implementation
  • Loading branch information
kriswallsmith committed Feb 7, 2016
2 parents ea1160d + 3ea470b commit 4bb27cf
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ The core provides the following filters in the `Assetic\Filter` namespace:
* `ReactJsxFilter`: compiles React JSX into JavaScript
* `Sass\SassFilter`: parses SASS into CSS
* `Sass\ScssFilter`: parses SCSS into CSS
* `SassphpFilter`: parses Sass into CSS using the sassphp bindings for Libsass
* `ScssphpFilter`: parses SCSS using scssphp
* `SeparatorFilter`: inserts a separator between assets to prevent merge failures
* `SprocketsFilter`: Sprockets Javascript dependency management
Expand Down
132 changes: 132 additions & 0 deletions src/Assetic/Filter/SassphpFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2015 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Assetic\Filter;

use Assetic\Factory\AssetFactory;
use Assetic\Asset\AssetInterface;
use Assetic\Filter\DependencyExtractorInterface;
use Assetic\Util\CssUtils;

/**
* Compiles Sass to CSS.
*
* @author Mikey Clarke <[email protected]>
*/
class SassphpFilter implements DependencyExtractorInterface
{
private $includePaths = array();
private $outputStyle;

public function filterLoad(AssetInterface $asset)
{
$sass = new \Sass();

$includePaths = array_merge(
array($asset->getSourceDirectory()),
$this->includePaths
);
$sass->setIncludePath(implode(':', $includePaths));

if ($this->outputStyle) {
$sass->setStyle($this->outputStyle);
}

$css = $sass->compile($asset->getContent());

$asset->setContent($css);
}

public function filterDump(AssetInterface $asset)
{
}

public function setOutputStyle($outputStyle)
{
$this->outputStyle = $outputStyle;
}

public function setIncludePaths(array $paths)
{
$this->includePaths = $paths;
}

public function addIncludePath($path)
{
$this->includePaths[] = $path;
}

public function getChildren(AssetFactory $factory, $content, $loadPath = null)
{
$children = array();

$includePaths = $this->includePaths;
if (null !== $loadPath && !in_array($loadPath, $includePaths)) {
array_unshift($includePaths, $loadPath);
}

if (empty($includePaths)) {
return $children;
}

foreach (CssUtils::extractImports($content) as $reference) {
if ('.css' === substr($reference, -4)) {
continue;
}

// the reference may or may not have an extension or be a partial
if (pathinfo($reference, PATHINFO_EXTENSION)) {
$needles = array(
$reference,
$this->partialize($reference),
);
} else {
$needles = array(
$reference . '.scss',
$this->partialize($reference) . '.scss',
);
}

foreach ($includePaths as $includePath) {
foreach ($needles as $needle) {
if (file_exists($file = $includePath . '/' . $needle)) {
$child = $factory->createAsset($file, array(), array('root' => $includePath));
$children[] = $child;
$child->load();
$children = array_merge(
$children,
$this->getChildren($factory, $child->getContent(), $includePath)
);
}
}
}
}

return $children;
}

private function partialize($reference)
{
$parts = pathinfo($reference);

if ('.' === $parts['dirname']) {
$partial = '_' . $parts['filename'];
} else {
$partial = $parts['dirname'] . DIRECTORY_SEPARATOR . '_' . $parts['filename'];
}

if (isset($parts['extension'])) {
$partial .= '.' . $parts['extension'];
}

return $partial;
}
}
99 changes: 99 additions & 0 deletions tests/Assetic/Test/Filter/SassphpFilterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

/*
* This file is part of the Assetic package, an OpenSky project.
*
* (c) 2010-2015 OpenSky Project Inc
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Assetic\Test\Filter;

use Assetic\Factory\AssetFactory;
use Assetic\Asset\FileAsset;
use Assetic\Asset\StringAsset;
use Assetic\Filter\SassphpFilter;

class SassphpFilterTest extends \PHPUnit_Framework_TestCase
{
private $filter;

protected function setUp()
{
if (!extension_loaded('sass')) {
$this->markTestSkipped('Sass extension is not installed');
}

$this->filter = new SassphpFilter();
$this->filter->setOutputStyle(\Sass::STYLE_COMPRESSED);
}

public function testCompilation()
{
$asset = new FileAsset(__DIR__.'/fixtures/sassphp/example.scss');
$asset->load();

$expected = <<<EOF
body{color:red}
EOF;

$this->filter->filterLoad($asset);
$this->assertEquals($expected, $asset->getContent());
}

public function testOutputStyle()
{
$asset = new FileAsset(__DIR__.'/fixtures/sassphp/example.scss');
$asset->load();

$expected = <<<EOF
body {
color: red;
}
EOF;

$this->filter->setOutputStyle(\Sass::STYLE_EXPANDED);
$this->filter->filterLoad($asset);

$this->assertEquals($expected, $asset->getContent());
}

public function testImports()
{
$asset = new StringAsset('@import "cheese";');
$asset->load();

$expected = <<<EOF
body{color:blue}
EOF;

$this->filter->addIncludePath(__DIR__ . '/fixtures/sassphp/includes');
$this->filter->filterLoad($asset);

$this->assertEquals($expected, $asset->getContent());
}

public function testExtractChildren()
{
$factory = new AssetFactory('');

$children = $this->filter->getChildren($factory, '@import "import";', __DIR__.'/fixtures/sassphp');

$this->assertCount(2, $children);
}

public function testExtractChildrenWithEmptyPath()
{
$factory = new AssetFactory(__DIR__.'/fixtures/sassphp');

$this->filter->addIncludePath(__DIR__.'/fixtures/sassphp');
$children = $this->filter->getChildren($factory, '@import "import";');

$this->assertCount(2, $children);
}
}
3 changes: 3 additions & 0 deletions tests/Assetic/Test/Filter/fixtures/sassphp/_child.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
background-color: green;
}
5 changes: 5 additions & 0 deletions tests/Assetic/Test/Filter/fixtures/sassphp/example.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
$foo: red;

body {
color: $foo;
}
5 changes: 5 additions & 0 deletions tests/Assetic/Test/Filter/fixtures/sassphp/import.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import 'child';

body {
background-color: blue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
color: blue;
}

0 comments on commit 4bb27cf

Please sign in to comment.