From f96cee9b5dc109a9f069edd12257ccc5af2ed760 Mon Sep 17 00:00:00 2001 From: Bernat Arlandis Date: Sun, 10 May 2020 11:42:24 +0200 Subject: [PATCH 1/5] Add generic pipe filters --- src/Filter/PipeInputFilter.php | 76 ++++++++++++ src/Filter/PipeOutputFilter.php | 47 ++++++++ src/Output/FreshTrait.php | 6 +- tests/TestCase/Filter/PipeInputFilterTest.php | 112 ++++++++++++++++++ .../TestCase/Filter/PipeOutputFilterTest.php | 36 ++++++ 5 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 src/Filter/PipeInputFilter.php create mode 100644 src/Filter/PipeOutputFilter.php create mode 100644 tests/TestCase/Filter/PipeInputFilterTest.php create mode 100644 tests/TestCase/Filter/PipeOutputFilterTest.php diff --git a/src/Filter/PipeInputFilter.php b/src/Filter/PipeInputFilter.php new file mode 100644 index 00000000..bf862ba0 --- /dev/null +++ b/src/Filter/PipeInputFilter.php @@ -0,0 +1,76 @@ + '.css', + 'command' => '/bin/cat', + 'dependencies' => 'none', // none, css, other + 'optional_dependency_prefix' => false, + 'path' => '/bin', + ); + + /** + * It will use prefixed files if they exist. + * + * @var string + */ + protected $optionalDependencyPrefix = null; + + public function getDependencies(\MiniAsset\AssetTarget $file) + { + if ($this->_settings['dependencies'] !== 'none' && $this->_settings['dependencies'] !== 'css') { + return false; + } + + if ($this->_settings['dependencies'] === 'css') { + $this->optionalDependencyPrefix = $this->_settings['optional_dependency_prefix']; + + return $this->getCssDependencies($file); + } + + return []; + } + + /** + * Runs command against any files that match the configured extension. + * + * @param string $filename The name of the input file. + * @param string $input The content of the file. + * @return string + */ + public function input($filename, $input) + { + if (substr($filename, strlen($this->_settings['ext']) * -1) !== $this->_settings['ext']) { + return $input; + } + $filename = preg_replace('/ /', '\\ ', $filename); + $bin = $this->_settings['command'] . ' ' . $filename; + $return = $this->_runCmd($bin, '', array('PATH' => $this->_settings['path'])); + + return $return; + } +} diff --git a/src/Filter/PipeOutputFilter.php b/src/Filter/PipeOutputFilter.php new file mode 100644 index 00000000..ea3ef34d --- /dev/null +++ b/src/Filter/PipeOutputFilter.php @@ -0,0 +1,47 @@ + '/bin/cat', + 'path' => '/bin', + ); + + /** + * Run command against the output and compress it. + * + * @param string $filename Name of the file being generated. + * @param string $input The raw contents for $filename. + * @return string Processed contents. + */ + public function output($filename, $input) + { + $cmd = $this->_settings['command']; + + return $this->_runCmd($cmd, $input, array('PATH' => $this->_settings['path'])); + } +} diff --git a/src/Output/FreshTrait.php b/src/Output/FreshTrait.php index c78d0a33..9b1d9471 100644 --- a/src/Output/FreshTrait.php +++ b/src/Output/FreshTrait.php @@ -90,7 +90,11 @@ public function isFresh(AssetTarget $target) $filters = $this->filterRegistry->collection($target); foreach ($filters->filters() as $filter) { - foreach ($filter->getDependencies($target) as $child) { + $dependencies = $filter->getDependencies($target); + if ($dependencies === false) { + return false; + } + foreach ($dependencies as $child) { $time = $child->modifiedTime(); if ($time >= $buildTime) { return false; diff --git a/tests/TestCase/Filter/PipeInputFilterTest.php b/tests/TestCase/Filter/PipeInputFilterTest.php new file mode 100644 index 00000000..b83ce2ed --- /dev/null +++ b/tests/TestCase/Filter/PipeInputFilterTest.php @@ -0,0 +1,112 @@ +_cssDir = APP . 'css' . DS; + $this->filter = new PipeInputFilter(); + $this->filter->settings([ + 'paths' => [$this->_cssDir], + 'ext' => '.scss', + ]); + } + + public function testParsing() + { + $this->filter->settings(array('command' => '/bin/cat')); + + $content = file_get_contents($this->_cssDir . 'test.scss'); + $result = $this->filter->input($this->_cssDir . 'test.scss', $content); + $expected = file_get_contents($this->_cssDir . 'test.scss'); + $this->assertEquals($expected, $result); + } + + public function testGetDependenciesNone() + { + $this->filter->settings(array( + 'dependencies' => 'none', + 'optional_dependency_prefix' => false, + )); + + $files = [ + new Local($this->_cssDir . 'test.scss') + ]; + $target = new AssetTarget('test.css', $files); + $result = $this->filter->getDependencies($target); + + $this->assertEmpty($result); + } + + public function testGetDependencies() + { + $this->filter->settings(array( + 'dependencies' => 'css', + 'optional_dependency_prefix' => '_', + )); + + $files = [ + new Local($this->_cssDir . 'test.scss') + ]; + $target = new AssetTarget('test.css', $files); + $result = $this->filter->getDependencies($target); + + $this->assertCount(3, $result); + $this->assertEquals('colors.scss', $result[0]->name()); + $this->assertEquals('_utilities.scss', $result[1]->name()); + $this->assertEquals('_reset.scss', $result[2]->name()); + } + + public function testGetDependenciesAlwaysRun() + { + $this->filter->settings(array( + 'dependencies' => 'other', + 'optional_dependency_prefix' => false, + )); + + $files = [ + new Local($this->_cssDir . 'test.scss') + ]; + $target = new AssetTarget('test.css', $files); + $result = $this->filter->getDependencies($target); + + $this->assertFalse($result); + } + + public function testGetDependenciesMissingDependency() + { + $this->filter->settings(array( + 'dependencies' => 'css', + 'optional_dependency_prefix' => '_', + )); + + $files = [ + new Local($this->_cssDir . 'broken.scss') + ]; + $target = new AssetTarget('test.css', $files); + $result = $this->filter->getDependencies($target); + + $this->assertCount(1, $result); + $this->assertEquals('colors.scss', $result[0]->name()); + } +} diff --git a/tests/TestCase/Filter/PipeOutputFilterTest.php b/tests/TestCase/Filter/PipeOutputFilterTest.php new file mode 100644 index 00000000..216187c8 --- /dev/null +++ b/tests/TestCase/Filter/PipeOutputFilterTest.php @@ -0,0 +1,36 @@ +_cssDir = APP . 'css' . DS; + $this->filter = new PipeOutputFilter(); + } + + public function testOutput() + { + $content = file_get_contents($this->_cssDir . 'unminified.css'); + $result = $this->filter->output($this->_cssDir . 'unminified.css', $content); + $expected = file_get_contents($this->_cssDir . 'unminified.css'); + $this->assertEquals($expected, $result); + } +} From 22fee3026582ac9de91a9673deec9e59f2bc6c73 Mon Sep 17 00:00:00 2001 From: Bernat Arlandis Date: Tue, 26 May 2020 18:49:07 +0200 Subject: [PATCH 2/5] Declare class in use list --- src/Filter/PipeInputFilter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Filter/PipeInputFilter.php b/src/Filter/PipeInputFilter.php index bf862ba0..a1f97259 100644 --- a/src/Filter/PipeInputFilter.php +++ b/src/Filter/PipeInputFilter.php @@ -13,6 +13,7 @@ */ namespace MiniAsset\Filter; +use MiniAsset\AssetTarget; use MiniAsset\Filter\AssetFilter; use MiniAsset\Filter\CssDependencyTrait; @@ -40,7 +41,7 @@ class PipeInputFilter extends AssetFilter */ protected $optionalDependencyPrefix = null; - public function getDependencies(\MiniAsset\AssetTarget $file) + public function getDependencies(AssetTarget $file) { if ($this->_settings['dependencies'] !== 'none' && $this->_settings['dependencies'] !== 'css') { return false; From bfbaf0762c818094f83cbebb5f42e71ef6245030 Mon Sep 17 00:00:00 2001 From: Bernat Arlandis Date: Tue, 26 May 2020 18:50:48 +0200 Subject: [PATCH 3/5] Use escapeshellarg --- src/Filter/PipeInputFilter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Filter/PipeInputFilter.php b/src/Filter/PipeInputFilter.php index a1f97259..9cc9202a 100644 --- a/src/Filter/PipeInputFilter.php +++ b/src/Filter/PipeInputFilter.php @@ -68,7 +68,7 @@ public function input($filename, $input) if (substr($filename, strlen($this->_settings['ext']) * -1) !== $this->_settings['ext']) { return $input; } - $filename = preg_replace('/ /', '\\ ', $filename); + $filename = escapeshellarg($filename); $bin = $this->_settings['command'] . ' ' . $filename; $return = $this->_runCmd($bin, '', array('PATH' => $this->_settings['path'])); From d7af3fc672b520f3ed1bad32d0ef37060d58e0ae Mon Sep 17 00:00:00 2001 From: Bernat Arlandis Date: Tue, 26 May 2020 19:00:40 +0200 Subject: [PATCH 4/5] Add filter hook for optional dependencies --- src/Filter/AssetFilter.php | 10 +++++++ src/Filter/FilterInterface.php | 7 +++++ src/Filter/PipeInputFilter.php | 13 +++++----- src/Output/FreshTrait.php | 5 ++-- tests/TestCase/Filter/PipeInputFilterTest.php | 26 ++++--------------- 5 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/Filter/AssetFilter.php b/src/Filter/AssetFilter.php index 3bea0b77..bc73b689 100644 --- a/src/Filter/AssetFilter.php +++ b/src/Filter/AssetFilter.php @@ -82,6 +82,16 @@ public function getDependencies(AssetTarget $file) return []; } + /** + * Overloaded in filters that can't handle dependencies + * + * @return boolean True when the filter supports dependencies + */ + public function hasDependencies() + { + return true; + } + /** * Run the compressor command and get the output * diff --git a/src/Filter/FilterInterface.php b/src/Filter/FilterInterface.php index 8c002b77..09922c01 100644 --- a/src/Filter/FilterInterface.php +++ b/src/Filter/FilterInterface.php @@ -59,4 +59,11 @@ public function settings(array $settings = null); * @return array An array of MiniAsset\File\Local objects. */ public function getDependencies(AssetTarget $target); + + /** + * Returns a boolean whether the filter supports dependencies. + * + * @return boolean True when the filter supports dependencies + */ + public function hasDependencies(); } diff --git a/src/Filter/PipeInputFilter.php b/src/Filter/PipeInputFilter.php index 9cc9202a..79be0376 100644 --- a/src/Filter/PipeInputFilter.php +++ b/src/Filter/PipeInputFilter.php @@ -29,7 +29,7 @@ class PipeInputFilter extends AssetFilter protected $_settings = array( 'ext' => '.css', 'command' => '/bin/cat', - 'dependencies' => 'none', // none, css, other + 'dependencies' => true, 'optional_dependency_prefix' => false, 'path' => '/bin', ); @@ -41,13 +41,14 @@ class PipeInputFilter extends AssetFilter */ protected $optionalDependencyPrefix = null; - public function getDependencies(AssetTarget $file) + public function hasDependencies() { - if ($this->_settings['dependencies'] !== 'none' && $this->_settings['dependencies'] !== 'css') { - return false; - } + return $this->_settings['dependencies']; + } - if ($this->_settings['dependencies'] === 'css') { + public function getDependencies(AssetTarget $file) + { + if ($this->_settings['dependencies']) { $this->optionalDependencyPrefix = $this->_settings['optional_dependency_prefix']; return $this->getCssDependencies($file); diff --git a/src/Output/FreshTrait.php b/src/Output/FreshTrait.php index 9b1d9471..b994d6bd 100644 --- a/src/Output/FreshTrait.php +++ b/src/Output/FreshTrait.php @@ -90,11 +90,10 @@ public function isFresh(AssetTarget $target) $filters = $this->filterRegistry->collection($target); foreach ($filters->filters() as $filter) { - $dependencies = $filter->getDependencies($target); - if ($dependencies === false) { + if (!$filter->hasDependencies()) { return false; } - foreach ($dependencies as $child) { + foreach ($filter->getDependencies($target) as $child) { $time = $child->modifiedTime(); if ($time >= $buildTime) { return false; diff --git a/tests/TestCase/Filter/PipeInputFilterTest.php b/tests/TestCase/Filter/PipeInputFilterTest.php index b83ce2ed..9b85ee3c 100644 --- a/tests/TestCase/Filter/PipeInputFilterTest.php +++ b/tests/TestCase/Filter/PipeInputFilterTest.php @@ -42,10 +42,10 @@ public function testParsing() $this->assertEquals($expected, $result); } - public function testGetDependenciesNone() + public function testGetDependenciesFalse() { $this->filter->settings(array( - 'dependencies' => 'none', + 'dependencies' => false, 'optional_dependency_prefix' => false, )); @@ -58,10 +58,10 @@ public function testGetDependenciesNone() $this->assertEmpty($result); } - public function testGetDependencies() + public function testGetDependenciesTrue() { $this->filter->settings(array( - 'dependencies' => 'css', + 'dependencies' => true, 'optional_dependency_prefix' => '_', )); @@ -77,26 +77,10 @@ public function testGetDependencies() $this->assertEquals('_reset.scss', $result[2]->name()); } - public function testGetDependenciesAlwaysRun() - { - $this->filter->settings(array( - 'dependencies' => 'other', - 'optional_dependency_prefix' => false, - )); - - $files = [ - new Local($this->_cssDir . 'test.scss') - ]; - $target = new AssetTarget('test.css', $files); - $result = $this->filter->getDependencies($target); - - $this->assertFalse($result); - } - public function testGetDependenciesMissingDependency() { $this->filter->settings(array( - 'dependencies' => 'css', + 'dependencies' => true, 'optional_dependency_prefix' => '_', )); From eb8b4365a1db3b07cb9235a7f3e8beb88adf28e1 Mon Sep 17 00:00:00 2001 From: Bernat Arlandis Date: Fri, 29 May 2020 14:56:21 +0200 Subject: [PATCH 5/5] Change default settings --- src/Filter/PipeInputFilter.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Filter/PipeInputFilter.php b/src/Filter/PipeInputFilter.php index 79be0376..e33332f2 100644 --- a/src/Filter/PipeInputFilter.php +++ b/src/Filter/PipeInputFilter.php @@ -26,11 +26,20 @@ class PipeInputFilter extends AssetFilter getDependencies as getCssDependencies; } + /** + * Settings for PipeInputFilter + * + * - `ext` File extension used by the filter + * - `dependencies` Set to true to calculate SCSS/LESS dependencies + * - `optional_dependency_prefix` Filename prefix for dependencies or false + * - `command` Command to run the file through + * - `path` Sets PATH environment variable + */ protected $_settings = array( 'ext' => '.css', - 'command' => '/bin/cat', - 'dependencies' => true, + 'dependencies' => false, 'optional_dependency_prefix' => false, + 'command' => '/bin/cat', 'path' => '/bin', );