mirror of
https://github.com/mrclay/minify.git
synced 2025-08-23 06:02:51 +02:00
Merge pull request #549 from mrclay/scssphp
add scss support via leafo/scssphp
This commit is contained in:
@@ -34,6 +34,7 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"leafo/lessphp": "~0.4.0",
|
"leafo/lessphp": "~0.4.0",
|
||||||
|
"leafo/scssphp": "^0.3.0",
|
||||||
"meenie/javascript-packer": "~1.1",
|
"meenie/javascript-packer": "~1.1",
|
||||||
"phpunit/phpunit": "4.8.*",
|
"phpunit/phpunit": "4.8.*",
|
||||||
"tedivm/jshrink": "~1.1.0"
|
"tedivm/jshrink": "~1.1.0"
|
||||||
|
175
lib/Minify/ScssCssSource.php
Normal file
175
lib/Minify/ScssCssSource.php
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
<?php
|
||||||
|
use Leafo\ScssPhp\Compiler;
|
||||||
|
use Leafo\ScssPhp\Server;
|
||||||
|
use Leafo\ScssPhp\Version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for using SCSS files
|
||||||
|
*
|
||||||
|
* @link https://github.com/leafo/scssphp/
|
||||||
|
*/
|
||||||
|
class Minify_ScssCssSource extends Minify_Source {
|
||||||
|
/**
|
||||||
|
* @var Minify_CacheInterface
|
||||||
|
*/
|
||||||
|
private $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsed SCSS cache object
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $parsed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function __construct(array $spec, Minify_CacheInterface $cache)
|
||||||
|
{
|
||||||
|
parent::__construct($spec);
|
||||||
|
|
||||||
|
$this->cache = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get last modified of all parsed files
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getLastModified()
|
||||||
|
{
|
||||||
|
$cache = $this->getCache();
|
||||||
|
|
||||||
|
return $cache['updated'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get content
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
$cache = $this->getCache();
|
||||||
|
|
||||||
|
return $cache['content'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a unique cache id for for this source.
|
||||||
|
*
|
||||||
|
* @param string $prefix
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getCacheId($prefix = 'minify')
|
||||||
|
{
|
||||||
|
$md5 = md5($this->filepath);
|
||||||
|
|
||||||
|
return "{$prefix}_scss_{$md5}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get SCSS cache object
|
||||||
|
*
|
||||||
|
* Runs the compilation if needed
|
||||||
|
*
|
||||||
|
* Implements Leafo\ScssPhp\Server logic because we need to get parsed files without parsing actual content
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function getCache()
|
||||||
|
{
|
||||||
|
// cache for single run
|
||||||
|
// so that getLastModified and getContent in single request do not add additional cache roundtrips (i.e memcache)
|
||||||
|
if (isset($this->parsed)) {
|
||||||
|
return $this->parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check from cache first
|
||||||
|
$cache = null;
|
||||||
|
$cacheId = $this->getCacheId();
|
||||||
|
if ($this->cache->isValid($cacheId, 0)) {
|
||||||
|
if ($cache = $this->cache->fetch($cacheId)) {
|
||||||
|
$cache = unserialize($cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$input = $cache ? $cache : $this->filepath;
|
||||||
|
|
||||||
|
if ($this->cacheIsStale($cache)) {
|
||||||
|
$cache = $this->compile($this->filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($input) || $cache['updated'] > $input['updated']) {
|
||||||
|
$this->cache->store($cacheId, serialize($cache));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->parsed = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether .scss file needs to be re-compiled.
|
||||||
|
*
|
||||||
|
* @param array $cache Cache object
|
||||||
|
*
|
||||||
|
* @return boolean True if compile required.
|
||||||
|
*/
|
||||||
|
private function cacheIsStale($cache)
|
||||||
|
{
|
||||||
|
if (!$cache) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$updated = $cache['updated'];
|
||||||
|
foreach ($cache['files'] as $import => $mtime) {
|
||||||
|
$filemtime = filemtime($import);
|
||||||
|
|
||||||
|
if ($filemtime !== $mtime || $filemtime > $updated) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile .scss file
|
||||||
|
*
|
||||||
|
* @param string $filename Input path (.scss)
|
||||||
|
*
|
||||||
|
* @see Server::compile()
|
||||||
|
* @return array meta data result of the compile
|
||||||
|
*/
|
||||||
|
private function compile($filename)
|
||||||
|
{
|
||||||
|
$start = microtime(true);
|
||||||
|
$scss = new Compiler();
|
||||||
|
|
||||||
|
// set import path directory the input filename resides
|
||||||
|
// otherwise @import statements will not find the files
|
||||||
|
// and will treat the @import line as css import
|
||||||
|
$scss->setImportPaths(dirname($filename));
|
||||||
|
|
||||||
|
$css = $scss->compile(file_get_contents($filename), $filename);
|
||||||
|
$elapsed = round((microtime(true) - $start), 4);
|
||||||
|
|
||||||
|
$v = Version::VERSION;
|
||||||
|
$ts = date('r', $start);
|
||||||
|
$css = "/* compiled by scssphp $v on $ts (${elapsed}s) */\n\n" . $css;
|
||||||
|
|
||||||
|
$imports = $scss->getParsedFiles();
|
||||||
|
|
||||||
|
$updated = 0;
|
||||||
|
foreach ($imports as $mtime) {
|
||||||
|
$updated = max($updated, $mtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'elapsed' => $elapsed, // statistic, can be dropped
|
||||||
|
'updated' => $updated,
|
||||||
|
'content' => $css,
|
||||||
|
'files' => $imports,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -67,9 +67,13 @@ class Minify_Source_Factory {
|
|||||||
throw new InvalidArgumentException("fileChecker option is not callable");
|
throw new InvalidArgumentException("fileChecker option is not callable");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->setHandler('~\.less$~i', function ($spec) use ($cache) {
|
$this->setHandler('~\.less$~i', function ($spec) use ($cache) {
|
||||||
return new Minify_LessCssSource($spec, $cache);
|
return new Minify_LessCssSource($spec, $cache);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$this->setHandler('~\.scss~i', function ($spec) use ($cache) {
|
||||||
|
return new Minify_ScssCssSource($spec, $cache);
|
||||||
|
});
|
||||||
|
|
||||||
$this->setHandler('~\.(js|css)$~i', function ($spec) {
|
$this->setHandler('~\.(js|css)$~i', function ($spec) {
|
||||||
return new Minify_Source($spec);
|
return new Minify_Source($spec);
|
||||||
|
40
tests/ScssSourceTest.php
Normal file
40
tests/ScssSourceTest.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class ScssSourceTest extends TestCase
|
||||||
|
{
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$this->realDocRoot = $_SERVER['DOCUMENT_ROOT'];
|
||||||
|
$_SERVER['DOCUMENT_ROOT'] = self::$document_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://github.com/mrclay/minify/issues/500
|
||||||
|
*/
|
||||||
|
public function testTimestamp()
|
||||||
|
{
|
||||||
|
$baseDir = self::$test_files;
|
||||||
|
|
||||||
|
$mainLess = "$baseDir/main.scss";
|
||||||
|
$includedLess = "$baseDir/_included.scss";
|
||||||
|
|
||||||
|
// touch timestamp with 1s difference
|
||||||
|
touch($mainLess);
|
||||||
|
sleep(1);
|
||||||
|
touch($includedLess);
|
||||||
|
|
||||||
|
$mtime1 = filemtime($mainLess);
|
||||||
|
var_dump($mtime1);
|
||||||
|
$mtime2 = filemtime($includedLess);
|
||||||
|
var_dump($mtime2);
|
||||||
|
|
||||||
|
$max = max($mtime1, $mtime2);
|
||||||
|
|
||||||
|
$options = array(
|
||||||
|
'groupsConfigFile' => "$baseDir/htmlHelper_groupsConfig.php",
|
||||||
|
);
|
||||||
|
$res = Minify_HTML_Helper::getUri('scss', $options);
|
||||||
|
|
||||||
|
$this->assertEquals("/min/g=scss&{$max}", $res);
|
||||||
|
}
|
||||||
|
}
|
8
tests/_test_files/_included.scss
Normal file
8
tests/_test_files/_included.scss
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* lesstest2.scss */
|
||||||
|
|
||||||
|
|
||||||
|
a.included {
|
||||||
|
color: $primary-color;
|
||||||
|
font-size: 13px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
@@ -9,4 +9,8 @@ return array(
|
|||||||
'less' => array(
|
'less' => array(
|
||||||
'//_test_files/main.less',
|
'//_test_files/main.less',
|
||||||
),
|
),
|
||||||
|
|
||||||
|
'scss' => array(
|
||||||
|
'//_test_files/main.scss',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
29
tests/_test_files/main.scss
Normal file
29
tests/_test_files/main.scss
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*! preserving comment */
|
||||||
|
|
||||||
|
// Variable
|
||||||
|
$primary-color: hotpink;
|
||||||
|
|
||||||
|
// Mixin
|
||||||
|
@mixin border-radius($radius) {
|
||||||
|
-webkit-border-radius: $radius;
|
||||||
|
-moz-border-radius: $radius;
|
||||||
|
border-radius: $radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-element {
|
||||||
|
color: $primary-color;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-other-element {
|
||||||
|
@include border-radius(6px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* import include -> */
|
||||||
|
@import "_included";
|
||||||
|
/* <- import included */
|
||||||
|
|
||||||
|
/*
|
||||||
|
a normal comment.
|
||||||
|
*/
|
Reference in New Issue
Block a user