From df07dde450669bc04081a7afb3ee68b8429c37fd Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 07:04:41 -0400 Subject: [PATCH 001/254] Fix travis ci tests? --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 25e06b3e..df8c6adc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,10 @@ php: - 5.6 - 7.0 - 7.1 -# - nightly + - nightly script: - - phpunit tests + - vendor/bin/phpunit tests branches: only: From 2b7ed3384a66ac5a69846c3d2e0eb204fb6cef34 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 07:10:22 -0400 Subject: [PATCH 002/254] Travis: run composer install --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index df8c6adc..e0e9a57f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ php: - nightly script: + - composer install - vendor/bin/phpunit tests branches: From 3814a55a4efe56ac33eb586b97845a8e88093225 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 08:28:16 -0400 Subject: [PATCH 003/254] fixes #505 - remove .phar build --- .gitignore | 2 +- Makefile | 3 --- box.json.dist | 12 ------------ composer.json | 4 +--- 4 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 box.json.dist diff --git a/.gitignore b/.gitignore index dbff826d..be6e5ecf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .sass-cache +scss_cache composer.lock -pscss.phar /*.css /*.scss /_site/ diff --git a/Makefile b/Makefile index b42e4a2b..5115941c 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,3 @@ compat: standard: vendor/bin/phpcs --standard=PSR2 bin src tests *.php - -phar: - php -dphar.readonly=0 vendor/bin/box build -v diff --git a/box.json.dist b/box.json.dist deleted file mode 100644 index 63d13baf..00000000 --- a/box.json.dist +++ /dev/null @@ -1,12 +0,0 @@ -{ - "chmod": "0755", - "files": [ - "LICENSE.md", - "scss.inc.php" - ], - "directories": ["src"], - "main": "bin/pscss", - "output": "pscss.phar", - "compactors": "Herrera\\Box\\Compactor\\Php", - "stub": true -} diff --git a/composer.json b/composer.json index 112a6bde..95118a32 100644 --- a/composer.json +++ b/composer.json @@ -25,8 +25,7 @@ }, "require-dev": { "squizlabs/php_codesniffer": "~2.5", - "phpunit/phpunit": "~3.7", - "kherge/box": "~2.5" + "phpunit/phpunit": "~4.6" }, "bin": ["bin/pscss"], "archive": { @@ -35,7 +34,6 @@ "/.gitattributes", "/.gitignore", "/.travis.yml", - "/box.json.dist", "/phpunit.xml.dist", "/tests" ] From b6f1ea6baac258aacc3e44cd48df9b4b0dd95ef2 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 09:04:34 -0400 Subject: [PATCH 004/254] Move Server class to example/ folder --- {src => example}/Server.php | 4 ++-- scss.inc.php | 1 - tests/ServerTest.php | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) rename {src => example}/Server.php (98%) diff --git a/src/Server.php b/example/Server.php similarity index 98% rename from src/Server.php rename to example/Server.php index c3fa985d..8dc3c7b2 100644 --- a/src/Server.php +++ b/example/Server.php @@ -434,7 +434,7 @@ public function __construct($dir, $cacheDir = null, $scss = null) $this->cacheDir = $cacheDir; if (! is_dir($this->cacheDir)) { - mkdir($this->cacheDir, 0755, true); + throw new ServerException('Cache directory doesn\'t exist: ' . $cacheDir); } if (! isset($scss)) { @@ -446,7 +446,7 @@ public function __construct($dir, $cacheDir = null, $scss = null) $this->showErrorsAsCSS = false; if (! ini_get('date.timezone')) { - date_default_timezone_set('UTC'); + throw new ServerException('Default date.timezone not set'); } } } diff --git a/scss.inc.php b/scss.inc.php index b6892fec..d2ec2639 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -26,5 +26,4 @@ include_once __DIR__ . '/src/Type.php'; include_once __DIR__ . '/src/Util.php'; include_once __DIR__ . '/src/Version.php'; - include_once __DIR__ . '/src/Server.php'; } diff --git a/tests/ServerTest.php b/tests/ServerTest.php index 2cecdd97..7c7ef6a9 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -8,9 +8,10 @@ * * @link http://leafo.github.io/scssphp */ - namespace Leafo\ScssPhp\Tests; +require_once __DIR__ . '/../example/Server.php'; + use Leafo\ScssPhp\Server; /** From 53e86eae35448fbcce8f682ffcb9fa224cd40cc3 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 14 Mar 2017 10:58:24 -0400 Subject: [PATCH 005/254] Fix failing ServerTest --- tests/ServerTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ServerTest.php b/tests/ServerTest.php index 7c7ef6a9..fcaea917 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -23,6 +23,10 @@ class ServerTest extends \PHPUnit_Framework_TestCase { public function testCheckedCachedCompile() { + if (! file_exists(__DIR__ . '/inputs/scss_cache')) { + mkdir(__DIR__ . '/inputs/scss_cache', 0755); + } + $server = new Server(__DIR__ . '/inputs/'); $css = $server->checkedCachedCompile(__DIR__ . '/inputs/import.scss', '/tmp/scss.css'); From 942c61055d53a893f79a7d34261d609b618a1ced Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 22 Aug 2017 07:20:58 -0400 Subject: [PATCH 006/254] Update scss.inc.php --- scss.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scss.inc.php b/scss.inc.php index d2ec2639..83d23a6b 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -3,7 +3,7 @@ throw new \Exception('scssphp requires PHP 5.4 or above'); } -if (! class_exists('scssc', false)) { +if (! class_exists('Leafo\ScssPhp\Version', false)) { include_once __DIR__ . '/src/Base/Range.php'; include_once __DIR__ . '/src/Block.php'; include_once __DIR__ . '/src/Colors.php'; From 494c09f0c08261265ddca4ca51c51670e01680c4 Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Fri, 29 Sep 2017 16:00:39 +0800 Subject: [PATCH 007/254] fixes #532 Substitute deprecated php function each() --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index d4f406fa..442d57d5 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -290,7 +290,7 @@ public function output(Compiler $compiler = null) } reset($units); - list($unit, ) = each($units); + $unit = key($units); return (string) $dimension . $unit; } From 9eaf3a6db4d88ce2c265a89e3fc495fbec9bf7a6 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 13 Oct 2017 11:52:30 -0400 Subject: [PATCH 008/254] Bump to version 0.7.0 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 2d6eabc1..dd7cd384 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.6.7'; + const VERSION = 'v0.7.0'; } From f79f49b9aebf7c1885a27f1152f2d58d1ba4b05a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 13 Oct 2017 12:06:15 -0400 Subject: [PATCH 009/254] Workaround PHP 7.2 code sniff --- src/Parser.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index 619b7ba8..44043e65 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2468,7 +2468,13 @@ private function getSourcePosition($pos) */ private function saveEncoding() { - if (ini_get('mbstring.func_overload') & 2) { + if (version_compare(PHP_VERSION, '7.2.0') >= 0) { + return; + } + + $iniDirective = 'mbstring' . '.func_overload'; // deprecated in PHP 7.2 + + if (ini_get($iniDirective) & 2) { $this->encoding = mb_internal_encoding(); mb_internal_encoding('iso-8859-1'); From 192f9b29d41ba0e2c610c2343da61bdde28f6436 Mon Sep 17 00:00:00 2001 From: Thomas Eiling Date: Fri, 13 Oct 2017 12:48:50 -0400 Subject: [PATCH 010/254] misc - fix some phpdoc comments and code styles --- src/Compiler.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 68003b90..89119933 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1181,7 +1181,7 @@ protected function flattenSelectorSingle($single) /** * Compile selector to string; self(&) should have been replaced by now * - * @param array $selector + * @param string|array $selector * * @return string */ @@ -1203,7 +1203,7 @@ protected function compileSelector($selector) /** * Compile selector part * - * @param arary $piece + * @param array $piece * * @return string */ @@ -1963,7 +1963,7 @@ protected function shouldEval($value) * @param array $value * @param boolean $inExp * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ protected function reduce($value, $inExp = false) { @@ -2238,7 +2238,7 @@ public function normalizeValue($value) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opAddNumberNumber($left, $right) { @@ -2251,7 +2251,7 @@ protected function opAddNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opMulNumberNumber($left, $right) { @@ -2264,7 +2264,7 @@ protected function opMulNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opSubNumberNumber($left, $right) { @@ -2277,7 +2277,7 @@ protected function opSubNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ protected function opDivNumberNumber($left, $right) { @@ -2294,7 +2294,7 @@ protected function opDivNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opModNumberNumber($left, $right) { @@ -2580,7 +2580,7 @@ protected function opLtNumberNumber($left, $right) * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opCmpNumberNumber($left, $right) { @@ -3505,7 +3505,7 @@ protected function fileExists($name) * Call SCSS @function * * @param string $name - * @param array $args + * @param array $argValues * @param array $returnValue * * @return boolean Returns true if returnValue is set; otherwise, false @@ -3777,7 +3777,7 @@ protected function applyArguments($argDef, $argValues) * * @param mixed $value * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ private function coerceValue($value) { @@ -3850,7 +3850,8 @@ protected function coerceMap($item) /** * Coerce something to list * - * @param array $item + * @param array $item + * @param string $delim * * @return array */ @@ -5210,6 +5211,8 @@ protected function libVariableExists($args) * Workaround IE7's content counter bug. * * @param array $args + * + * @return array */ protected function libCounter($args) { From b413ff279f5f0ba5938e1315a8068a06643c9443 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 13 Oct 2017 13:29:38 -0400 Subject: [PATCH 011/254] Change default precision to 10 (introduced in sass 3.5.0) --- bin/pscss | 2 +- src/Node/Number.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/pscss b/bin/pscss index 4ec061b8..70a2be3c 100755 --- a/bin/pscss +++ b/bin/pscss @@ -76,7 +76,7 @@ Options include: -i=path Set import path --iso8859-1 Use iso8859-1 encoding instead of utf-8 (default utf-8) --line-numbers Annotate selectors with comments referring to the source file and line number - -p=precision Set decimal number precision (default 5) + -p=precision Set decimal number precision (default 10) -T Dump formatted parse tree -v, --version Print the version diff --git a/src/Node/Number.php b/src/Node/Number.php index 442d57d5..5faedfdf 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -31,7 +31,7 @@ class Number extends Node implements \ArrayAccess /** * @var integer */ - static public $precision = 5; + static public $precision = 10; /** * @see http://www.w3.org/TR/2012/WD-css3-values-20120308/ From 75c9fb79ae362a84e89736c2812fc022332e0f5a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 13:29:08 -0400 Subject: [PATCH 012/254] Remove setlocale() to mitigate Windows side-effects --- src/Compiler.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 89119933..0d67c525 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -165,9 +165,6 @@ public function __construct() */ public function compile($code, $path = null) { - $locale = setlocale(LC_NUMERIC, 0); - setlocale(LC_NUMERIC, 'C'); - $this->indentLevel = -1; $this->commentsSeen = []; $this->extends = []; @@ -196,8 +193,6 @@ public function compile($code, $path = null) $out = $this->formatter->format($this->scope); - setlocale(LC_NUMERIC, $locale); - return $out; } From 9b3c1ff4eab71cdda612646fc94cfba0de9b6672 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 13:33:24 -0400 Subject: [PATCH 013/254] Use number_format to output locale-independent floats --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index 5faedfdf..df1b79cc 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -292,7 +292,7 @@ public function output(Compiler $compiler = null) reset($units); $unit = key($units); - return (string) $dimension . $unit; + return preg_replace('/[.]0*$/', '', number_format($dimension, static::$precision)) . $unit; } /** From 3cbdd6330958420b949fd19ef24472edfd3ee517 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:31:52 -0400 Subject: [PATCH 014/254] Trim trailing zeros --- src/Node/Number.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index df1b79cc..59eb811a 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -291,8 +291,9 @@ public function output(Compiler $compiler = null) reset($units); $unit = key($units); - - return preg_replace('/[.]0*$/', '', number_format($dimension, static::$precision)) . $unit; + $dimension = number_format($dimension, static::$precision, '.', ''); + + return (static::$precision ? rtrim($dimension, '0') : $dimension) . $unit; } /** From db5273cd3a33cc0fb1b1fbba85dbb00deb61446b Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:35:20 -0400 Subject: [PATCH 015/254] Trim trailing decimal point. --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index 59eb811a..acfdaf29 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -293,7 +293,7 @@ public function output(Compiler $compiler = null) $unit = key($units); $dimension = number_format($dimension, static::$precision, '.', ''); - return (static::$precision ? rtrim($dimension, '0') : $dimension) . $unit; + return (static::$precision ? rtrim($dimension, '.0') : $dimension) . $unit; } /** From c0ec63ff8eaff626ddeee1245b9119c82990c0ff Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:40:55 -0400 Subject: [PATCH 016/254] Fix previous edit from my phone. =P --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index acfdaf29..3dcdd2ca 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -293,7 +293,7 @@ public function output(Compiler $compiler = null) $unit = key($units); $dimension = number_format($dimension, static::$precision, '.', ''); - return (static::$precision ? rtrim($dimension, '.0') : $dimension) . $unit; + return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit; } /** From f735beeb2b8926f4d361d2c51bbefbc5271fbdfc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:46:23 -0400 Subject: [PATCH 017/254] Fix unit test (1/2) --- tests/outputs/operators.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/outputs/operators.css b/tests/outputs/operators.css index fb492c82..50aaa8c0 100644 --- a/tests/outputs/operators.css +++ b/tests/outputs/operators.css @@ -6,7 +6,7 @@ body { color: 5px; top: 1.5em; left: -1cm; - top: 6.29921; } + top: 6.2992125984; } div { color: false; @@ -19,7 +19,7 @@ div { color: 1; } #units { - test: 2.5748in; + test: 2.5748031496in; test: 13mm; test: 4em; test: 11mm; @@ -169,7 +169,7 @@ div { margin: 0 -2em; } div { - *margin-left: 2.07447%; } + *margin-left: 2.0744680851%; } .foo { width: 12.5%; } From 69cb0ca734665c9a9ea41dd3e3d6f7a65dfdd93d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 14:48:01 -0400 Subject: [PATCH 018/254] Fix unit test (2/2) --- tests/outputs_numbered/operators.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/outputs_numbered/operators.css b/tests/outputs_numbered/operators.css index c3ea3d1d..ad4d678e 100644 --- a/tests/outputs_numbered/operators.css +++ b/tests/outputs_numbered/operators.css @@ -7,7 +7,7 @@ body { color: 5px; top: 1.5em; left: -1cm; - top: 6.29921; } + top: 6.2992125984; } /* line 15, inputs/operators.scss */ div { color: false; @@ -20,7 +20,7 @@ div { color: 1; } /* line 29, inputs/operators.scss */ #units { - test: 2.5748in; + test: 2.5748031496in; test: 13mm; test: 4em; test: 11mm; @@ -170,7 +170,7 @@ div { margin: 0 -2em; } /* line 184, inputs/operators.scss */ div { - *margin-left: 2.07447%; } + *margin-left: 2.0744680851%; } /* line 190, inputs/operators.scss */ .foo { width: 12.5%; } From 4f605a51462c85cf6df5bb45460483a138e04025 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 14 Oct 2017 17:35:01 -0400 Subject: [PATCH 019/254] Bump version to 0.7.2 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index dd7cd384..0dd95fe6 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.0'; + const VERSION = 'v0.7.2'; } From c96edd1bedd3ba3e8ebeaf5ddd1f8669fb6f5625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20FRAN=C3=87OIS?= Date: Thu, 9 Nov 2017 11:16:12 +0100 Subject: [PATCH 020/254] add inline sourceMaps support --- src/Block.php | 5 + src/Compiler.php | 49 +++++- src/Formatter.php | 77 ++++++++- src/Formatter/Compressed.php | 4 +- src/Formatter/Crunched.php | 4 +- src/Formatter/Debug.php | 16 +- src/Formatter/Expanded.php | 4 +- src/Formatter/Nested.php | 20 ++- src/Formatter/OutputBlock.php | 15 ++ src/Parser.php | 1 + src/SourceMap/Base64VLQEncoder.php | 174 +++++++++++++++++++ src/SourceMap/SourceMapGenerator.php | 241 +++++++++++++++++++++++++++ 12 files changed, 579 insertions(+), 31 deletions(-) create mode 100644 src/SourceMap/Base64VLQEncoder.php create mode 100644 src/SourceMap/SourceMapGenerator.php diff --git a/src/Block.php b/src/Block.php index a6b5f3f0..87cfd6d8 100644 --- a/src/Block.php +++ b/src/Block.php @@ -28,6 +28,11 @@ class Block */ public $parent; + /** + * @var string; + */ + public $sourceName; + /** * @var integer */ diff --git a/src/Compiler.php b/src/Compiler.php index 0d67c525..a677760d 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -18,6 +18,7 @@ use Leafo\ScssPhp\Exception\CompilerException; use Leafo\ScssPhp\Formatter\OutputBlock; use Leafo\ScssPhp\Node; +use Leafo\ScssPhp\SourceMap\SourceMapGenerator; use Leafo\ScssPhp\Type; use Leafo\ScssPhp\Parser; use Leafo\ScssPhp\Util; @@ -64,6 +65,9 @@ class Compiler const WITH_SUPPORTS = 4; const WITH_ALL = 7; + const SOURCE_MAP_NONE = 0; + const SOURCE_MAP_INLINE = 1; + /** * @var array */ @@ -120,11 +124,16 @@ class Compiler protected $encoding = null; protected $lineNumberStyle = null; + protected $sourceMap = self::SOURCE_MAP_NONE; + protected $sourceMapOptions = []; + + /** @var string|Formatter */ protected $formatter = 'Leafo\ScssPhp\Formatter\Nested'; protected $rootEnv; protected $rootBlock; + /** @var Environment */ protected $env; protected $scope; protected $storeEnv; @@ -191,8 +200,21 @@ public function compile($code, $path = null) $this->compileRoot($tree); $this->popEnv(); - $out = $this->formatter->format($this->scope); + $sourceMapGenerator = null; + if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + } + $out = $this->formatter->format($this->scope, $sourceMapGenerator); + if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMap = $sourceMapGenerator->generateJson(); + + $sourceMapUrl = null; + if($this->sourceMap == self::SOURCE_MAP_INLINE) { + $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + } + $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); + } return $out; } @@ -274,6 +296,9 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; + $out->sourceName = $this->env->block->sourceName; + $out->sourceLine = $this->env->block->sourceLine; + $out->sourceColumn = $this->env->block->sourceColumn; return $out; } @@ -656,6 +681,7 @@ protected function compileMedia(Block $media) if ($needsWrap) { $wrapped = new Block; + $wrapped->sourceName = $media->sourceName; $wrapped->sourceIndex = $media->sourceIndex; $wrapped->sourceLine = $media->sourceLine; $wrapped->sourceColumn = $media->sourceColumn; @@ -729,6 +755,7 @@ protected function compileAtRoot(Block $block) // wrap inline selector if ($block->selector) { $wrapped = new Block; + $wrapped->sourceName = $block->sourceName; $wrapped->sourceIndex = $block->sourceIndex; $wrapped->sourceLine = $block->sourceLine; $wrapped->sourceColumn = $block->sourceColumn; @@ -785,6 +812,7 @@ private function spliceTree($envs, Block $block, $without) } $b = new Block; + $b->sourceName = $e->block->sourceName; $b->sourceIndex = $e->block->sourceIndex; $b->sourceLine = $e->block->sourceLine; $b->sourceColumn = $e->block->sourceColumn; @@ -1076,6 +1104,13 @@ protected function evalSelectors($selectors) return $selectors; } + /** + * @param array $sourceMapOptions + */ + public function setSourceMapOptions($sourceMapOptions) { + $this->sourceMapOptions = $sourceMapOptions; + } + /** * Evaluate selector * @@ -3299,6 +3334,13 @@ public function setLineNumberStyle($lineNumberStyle) $this->lineNumberStyle = $lineNumberStyle; } + /** + * @param int $sourceMap + */ + public function setSourceMap($sourceMap) { + $this->sourceMap = $sourceMap; + } + /** * Register function * @@ -5256,4 +5298,9 @@ protected function libInspect($args) return $args[0]; } + + public static function encodeURIComponent($string){ + $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + return strtr(rawurlencode($string), $revert); + } } diff --git a/src/Formatter.php b/src/Formatter.php index 0d2635f1..487b6612 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp; use Leafo\ScssPhp\Formatter\OutputBlock; +use Leafo\ScssPhp\SourceMap\SourceMapGenerator; /** * Base formatter @@ -56,10 +57,31 @@ abstract class Formatter public $assignSeparator; /** - * @var boolea + * @var boolean */ public $keepSemicolons; + /** + * @var OutputBlock; + */ + protected $currentBlock; + + + /** + * @var int + */ + protected $currentLine; + + /** + * @var int; + */ + protected $currentColumn; + + /** + * @var SourceMapGenerator + */ + protected $sourceMapGenerator; + /** * Initialize formatter * @@ -123,10 +145,10 @@ protected function blockLines(OutputBlock $block) $glue = $this->break . $inner; - echo $inner . implode($glue, $block->lines); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write($this->break); } } @@ -139,9 +161,9 @@ protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); - echo $inner + $this->write($inner . implode($this->tagSeparator, $block->selectors) - . $this->open . $this->break; + . $this->open . $this->break); } /** @@ -167,6 +189,8 @@ protected function block(OutputBlock $block) return; } + $this->currentBlock = $block; + $pre = $this->indentStr(); if (! empty($block->selectors)) { @@ -187,10 +211,10 @@ protected function block(OutputBlock $block) $this->indentLevel--; if (empty($block->children)) { - echo $this->break; + $this->write($this->break); } - echo $pre . $this->close . $this->break; + $this->write($pre . $this->close . $this->break); } } @@ -201,10 +225,19 @@ protected function block(OutputBlock $block) * * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree * + * @param SourceMapGenerator|null $sourceMapGenerator * @return string + * @internal param bool $collectSourceMap */ - public function format(OutputBlock $block) + public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null) { + if($sourceMapGenerator) { + $this->currentLine = 1; + $this->currentColumn = 0; + $this->sourceMapGenerator = $sourceMapGenerator; + } else { + $this->sourceMapGenerator = null; + } ob_start(); $this->block($block); @@ -213,4 +246,32 @@ public function format(OutputBlock $block) return $out; } + + /** + * @param $str + */ + protected function write($str) { + if($this->sourceMapGenerator) { + $this->sourceMapGenerator->addMapping( + $this->currentLine, + $this->currentColumn, + $this->currentBlock->sourceLine, + $this->currentBlock->sourceColumn, + $this->currentBlock->sourceName + ); + + $lines = explode("\n", $str); + $lineCount = count($lines); + $this->currentLine += $lineCount-1; + + $lastLine = array_pop($lines); + if($lineCount == 1) { + $this->currentColumn += mb_strlen($lastLine); + } else { + $this->currentColumn = mb_strlen($lastLine); + } + } + + echo $str; + } } diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 17aca54a..69a67850 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -53,10 +53,10 @@ public function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write( $inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write( $this->break); } } } diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index 0a89bc48..43d63833 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -51,10 +51,10 @@ public function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write($this->break); } } } diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index 321c5368..cb29641a 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -52,13 +52,13 @@ protected function blockLines(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->lines)) { - echo "{$indent}block->lines: []\n"; + $this->write("{$indent}block->lines: []\n"); return; } foreach ($block->lines as $index => $line) { - echo "{$indent}block->lines[{$index}]: $line\n"; + $this->write( "{$indent}block->lines[{$index}]: $line\n"); } } @@ -70,13 +70,13 @@ protected function blockSelectors(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->selectors)) { - echo "{$indent}block->selectors: []\n"; + $this->write( "{$indent}block->selectors: []\n"); return; } foreach ($block->selectors as $index => $selector) { - echo "{$indent}block->selectors[{$index}]: $selector\n"; + $this->write( "{$indent}block->selectors[{$index}]: $selector\n"); } } @@ -88,7 +88,7 @@ protected function blockChildren(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->children)) { - echo "{$indent}block->children: []\n"; + $this->write( "{$indent}block->children: []\n"); return; } @@ -109,8 +109,10 @@ protected function block(OutputBlock $block) { $indent = $this->indentStr(); - echo "{$indent}block->type: {$block->type}\n" . - "{$indent}block->depth: {$block->depth}\n"; + $this->write( "{$indent}block->type: {$block->type}\n" . + "{$indent}block->depth: {$block->depth}\n"); + + $this->currentBlock = $block; $this->blockSelectors($block); $this->blockLines($block); diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index dff17e6b..0449787d 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -59,10 +59,10 @@ protected function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write($inner . implode($glue, $block->lines)); if (empty($block->selectors) || ! empty($block->children)) { - echo $this->break; + $this->write($this->break); } } } diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index c4ff803c..48e19ef7 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -12,7 +12,6 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; -use Leafo\ScssPhp\Formatter\OutputBlock; /** * Nested formatter @@ -66,10 +65,10 @@ protected function blockLines(OutputBlock $block) } } - echo $inner . implode($glue, $block->lines); + $this->write( $inner . implode($glue, $block->lines)); if (! empty($block->children)) { - echo $this->break; + $this->write( $this->break); } } @@ -80,9 +79,9 @@ protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); - echo $inner + $this->write( $inner . implode($this->tagSeparator, $block->selectors) - . $this->open . $this->break; + . $this->open . $this->break); } /** @@ -94,13 +93,13 @@ protected function blockChildren(OutputBlock $block) $this->block($child); if ($i < count($block->children) - 1) { - echo $this->break; + $this->write( $this->break); if (isset($block->children[$i + 1])) { $next = $block->children[$i + 1]; if ($next->depth === max($block->depth, 1) && $child->depth >= $next->depth) { - echo $this->break; + $this->write( $this->break); } } } @@ -120,6 +119,9 @@ protected function block(OutputBlock $block) return; } + $this->currentBlock = $block; + + $this->depth = $block->depth; if (! empty($block->selectors)) { @@ -139,11 +141,11 @@ protected function block(OutputBlock $block) if (! empty($block->selectors)) { $this->indentLevel--; - echo $this->close; + $this->write( $this->close); } if ($block->type === 'root') { - echo $this->break; + $this->write( $this->break); } } diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index 88e86402..7d233a8d 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -47,4 +47,19 @@ class OutputBlock * @var \Leafo\ScssPhp\Formatter\OutputBlock */ public $parent; + + /** + * @var string + */ + public $sourceName; + + /** + * @var int + */ + public $sourceLine; + + /** + * @var int + */ + public $sourceColumn; } diff --git a/src/Parser.php b/src/Parser.php index 44043e65..cc0a51df 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -708,6 +708,7 @@ protected function pushBlock($selectors, $pos = 0) list($line, $column) = $this->getSourcePosition($pos); $b = new Block; + $b->sourceName = $this->sourceName; $b->sourceLine = $line; $b->sourceColumn = $column; $b->sourceIndex = $this->sourceIndex; diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php new file mode 100644 index 00000000..14652fbe --- /dev/null +++ b/src/SourceMap/Base64VLQEncoder.php @@ -0,0 +1,174 @@ + 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, + 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, + 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, + 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, + 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34, + 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41, + 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48, + 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56, + 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, + ); + /** + * Integer to char map + * + * @var array + */ + private $intToCharMap = array( + 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', + 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', + 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', + 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', + 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', + 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p', + 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', + 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', + 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', + 63 => '/', + ); + /** + * Constructor + */ + public function __construct(){ + // I leave it here for future reference + // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) + // { + // $this->charToIntMap[$char] = $i; + // $this->intToCharMap[$i] = $char; + // } + } + /** + * Convert from a two-complement value to a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, + * even on a 64 bit machine. + * @param string $aValue + */ + public function toVLQSigned($aValue){ + return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0); + } + /** + * Convert to a two-complement value from a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * We assume that the value was generated with a 32 bit machine in mind. + * Hence + * 1 becomes -2147483648 + * even on a 64 bit machine. + * @param integer $aValue + */ + public function fromVLQSigned($aValue){ + return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1); + } + /** + * Return the base 64 VLQ encoded value. + * + * @param string $aValue The value to encode + * @return string The encoded value + */ + public function encode($aValue){ + $encoded = ''; + $vlq = $this->toVLQSigned($aValue); + do + { + $digit = $vlq & $this->mask; + $vlq = $this->zeroFill($vlq, $this->shift); + if($vlq > 0){ + $digit |= $this->continuationBit; + } + $encoded .= $this->base64Encode($digit); + } while($vlq > 0); + return $encoded; + } + /** + * Return the value decoded from base 64 VLQ. + * + * @param string $encoded The encoded value to decode + * @return integer The decoded value + */ + public function decode($encoded){ + $vlq = 0; + $i = 0; + do + { + $digit = $this->base64Decode($encoded[$i]); + $vlq |= ($digit & $this->mask) << ($i * $this->shift); + $i++; + } while($digit & $this->continuationBit); + return $this->fromVLQSigned($vlq); + } + /** + * Right shift with zero fill. + * + * @param integer $a number to shift + * @param integer $b number of bits to shift + * @return integer + */ + public function zeroFill($a, $b){ + return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1)); + } + /** + * Encode single 6-bit digit as base64. + * + * @param integer $number + * @return string + * @throws Exception If the number is invalid + */ + public function base64Encode($number){ + if($number < 0 || $number > 63){ + throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); + } + return $this->intToCharMap[$number]; + } + /** + * Decode single 6-bit digit from base64 + * + * @param string $char + * @return number + * @throws Exception If the number is invalid + */ + public function base64Decode($char){ + if(!array_key_exists($char, $this->charToIntMap)){ + throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); + } + return $this->charToIntMap[$char]; + } +} diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php new file mode 100644 index 00000000..3d21f4fe --- /dev/null +++ b/src/SourceMap/SourceMapGenerator.php @@ -0,0 +1,241 @@ + '', + // an optional name of the generated code that this source map is associated with. + 'sourceMapFilename' => null, + // url of the map + 'sourceMapURL' => null, + // absolute path to a file to write the map to + 'sourceMapWriteTo' => null, + // output source contents? + 'outputSourceFiles' => false, + // base path for filename normalization + 'sourceMapRootpath' => '', + // base path for filename normalization + 'sourceMapBasepath' => '' + ); + + /** + * The base64 VLQ encoder + * + * @var Base64VLQEncoder + */ + protected $encoder; + /** + * Array of mappings + * + * @var array + */ + protected $mappings = array(); + + /** + * Array of contents map + * + * @var array + */ + protected $contentsMap = array(); + /** + * File to content map + * + * @var array + */ + protected $sources = array(); + protected $source_keys = array(); + /** + * @var array + */ + private $options; + + public function __construct(array $options = []) { + $this->options = array_merge($this->defaultOptions, $options); + $this->encoder = new Base64VLQEncoder(); + } + + /** + * Adds a mapping + * + * @param integer $generatedLine The line number in generated file + * @param integer $generatedColumn The column number in generated file + * @param integer $originalLine The line number in original file + * @param integer $originalColumn The column number in original file + * @param string $sourceFile The original source file + */ + public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) { + $this->mappings[] = array( + 'generated_line' => $generatedLine, + 'generated_column' => $generatedColumn, + 'original_line' => $originalLine, + 'original_column' => $originalColumn, + 'source_file' => $sourceFile + ); + + $this->sources[$sourceFile] = $sourceFile; + } + + /** + * Generates the JSON source map + * + * @return string + * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# + */ + public function generateJson() { + $sourceMap = array(); + $mappings = $this->generateMappings(); + // File version (always the first entry in the object) and must be a positive integer. + $sourceMap['version'] = self::VERSION; + // An optional name of the generated code that this source map is associated with. + $file = $this->options['sourceMapFilename']; + if($file) { + $sourceMap['file'] = $file; + } + // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field. + $root = $this->options['sourceRoot']; + if($root) { + $sourceMap['sourceRoot'] = $root; + } + // A list of original sources used by the 'mappings' entry. + $sourceMap['sources'] = array(); + foreach($this->sources as $source_uri => $source_filename) { + $sourceMap['sources'][] = $this->normalizeFilename($source_filename); + } + // A list of symbol names used by the 'mappings' entry. + $sourceMap['names'] = array(); + // A string with the encoded mapping data. + $sourceMap['mappings'] = $mappings; + if($this->options['outputSourceFiles']) { + // An optional list of source content, useful when the 'source' can't be hosted. + // The contents are listed in the same order as the sources above. + // 'null' may be used if some original sources should be retrieved by name. + $sourceMap['sourcesContent'] = $this->getSourcesContent(); + } + // less.js compat fixes + if(count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) { + unset($sourceMap['sourceRoot']); + } + return json_encode($sourceMap); + } + + /** + * Returns the sources contents + * + * @return array|null + */ + protected function getSourcesContent() { + if(empty($this->sources)) { + return null; + } + $content = array(); + foreach($this->sources as $sourceFile) { + $content[] = file_get_contents($sourceFile); + } + return $content; + } + + /** + * Generates the mappings string + * + * @return string + */ + public function generateMappings() { + if(!count($this->mappings)) { + return ''; + } + $this->source_keys = array_flip(array_keys($this->sources)); + // group mappings by generated line number. + $groupedMap = $groupedMapEncoded = array(); + foreach($this->mappings as $m) { + $groupedMap[$m['generated_line']][] = $m; + } + ksort($groupedMap); + $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0; + foreach($groupedMap as $lineNumber => $line_map) { + while(++$lastGeneratedLine < $lineNumber) { + $groupedMapEncoded[] = ';'; + } + $lineMapEncoded = array(); + $lastGeneratedColumn = 0; + foreach($line_map as $m) { + $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn); + $lastGeneratedColumn = $m['generated_column']; + // find the index + if($m['source_file']) { + $index = $this->findFileIndex($m['source_file']); + if($index !== false) { + $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex); + $lastOriginalIndex = $index; + // lines are stored 0-based in SourceMap spec version 3 + $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine); + $lastOriginalLine = $m['original_line'] - 1; + $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn); + $lastOriginalColumn = $m['original_column']; + } + } + $lineMapEncoded[] = $mapEncoded; + } + $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';'; + } + return rtrim(implode($groupedMapEncoded), ';'); + } + + /** + * Finds the index for the filename + * + * @param string $filename + * @return integer|false + */ + protected function findFileIndex($filename) { + return $this->source_keys[$filename]; + } + + protected function normalizeFilename($filename) { + $filename = $this->fixWindowsPath($filename); + $rootpath = $this->options['sourceMapRootpath']; + $basePath = $this->options['sourceMapBasepath']; + // "Trim" the 'sourceMapBasepath' from the output filename. + if(strpos($filename, $basePath) === 0) { + $filename = substr($filename, strlen($basePath)); + } + // Remove extra leading path separators. + if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) { + $filename = substr($filename, 1); + } + return $rootpath . $filename; + } + + /** + * fix windows paths + * @param string $path + * @param bool $addEndSlash + * @return string + */ + public function fixWindowsPath($path, $addEndSlash = false) { + $slash = ($addEndSlash) ? '/' : ''; + if(!empty($path)) { + $path = str_replace('\\', '/', $path); + $path = rtrim($path, '/') . $slash; + } + return $path; + } +} From 87c8b53442f0be9c8dd33cbac2308842dd240240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20FRAN=C3=87OIS?= Date: Thu, 9 Nov 2017 13:37:18 +0100 Subject: [PATCH 021/254] inline sourceMaps: fix off-by-one column in source maps --- src/Formatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Formatter.php b/src/Formatter.php index 487b6612..7939113b 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -256,7 +256,7 @@ protected function write($str) { $this->currentLine, $this->currentColumn, $this->currentBlock->sourceLine, - $this->currentBlock->sourceColumn, + $this->currentBlock->sourceColumn - 1, //columns from parser are off by one $this->currentBlock->sourceName ); From ce6c958bc90b729467b010d35fece984e6edec44 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 14 Dec 2017 12:34:12 -0500 Subject: [PATCH 022/254] Remove trailing whitespace --- src/Node/Number.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Node/Number.php b/src/Node/Number.php index 3dcdd2ca..efa83f5b 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -292,7 +292,7 @@ public function output(Compiler $compiler = null) reset($units); $unit = key($units); $dimension = number_format($dimension, static::$precision, '.', ''); - + return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit; } From eb4ab3f9be1d7fe854017c3bf97a3ca8c27730fb Mon Sep 17 00:00:00 2001 From: dleffler Date: Fri, 15 Dec 2017 12:42:20 -0500 Subject: [PATCH 023/254] Adds File based source map (in addition to inline source map) - SOURCE_MAP_FILE, using code from less.php; ONLY if there is output from the scss file. --- scss.inc.php | 4 ++++ src/Compiler.php | 5 ++++- src/SourceMap/SourceMapGenerator.php | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/scss.inc.php b/scss.inc.php index 83d23a6b..77303ba3 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -11,6 +11,7 @@ include_once __DIR__ . '/src/Compiler/Environment.php'; include_once __DIR__ . '/src/Exception/CompilerException.php'; include_once __DIR__ . '/src/Exception/ParserException.php'; + include_once __DIR__ . '/src/Exception/RangeException.php'; //exp include_once __DIR__ . '/src/Exception/ServerException.php'; include_once __DIR__ . '/src/Formatter.php'; include_once __DIR__ . '/src/Formatter/Compact.php'; @@ -23,6 +24,9 @@ include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; +// include_once __DIR__ . '/src/example/Server.php'; + include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php'; + include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php'; include_once __DIR__ . '/src/Type.php'; include_once __DIR__ . '/src/Util.php'; include_once __DIR__ . '/src/Version.php'; diff --git a/src/Compiler.php b/src/Compiler.php index a677760d..2dc50531 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -67,6 +67,7 @@ class Compiler const SOURCE_MAP_NONE = 0; const SOURCE_MAP_INLINE = 1; + const SOURCE_MAP_FILE = 2; /** * @var array @@ -205,12 +206,14 @@ public function compile($code, $path = null) $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); } $out = $this->formatter->format($this->scope, $sourceMapGenerator); - if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + if(!empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { $sourceMap = $sourceMapGenerator->generateJson(); $sourceMapUrl = null; if($this->sourceMap == self::SOURCE_MAP_INLINE) { $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + } elseif ($this->sourceMap == self::SOURCE_MAP_FILE) { + $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); } $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 3d21f4fe..300a5a08 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -8,6 +8,8 @@ namespace Leafo\ScssPhp\SourceMap; +use Leafo\ScssPhp\Exception\CompilerException; + class SourceMapGenerator { /** * What version of source map does the generator generate? @@ -94,6 +96,28 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori $this->sources[$sourceFile] = $sourceFile; } + /** + * Saves the source map to a file + * + * @param string $file The absolute path to a file + * @param string $content The content to write + * @throws Exception If the file could not be saved + */ + public function saveMap($content){ + $file = $this->options['sourceMapWriteTo']; + $dir = dirname($file); + // directory does not exist + if( !is_dir($dir) ){ + // FIXME: create the dir automatically? + throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); + } + // FIXME: proper saving, with dir write check! + if(file_put_contents($file, $content) === false){ + throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file)); + } + return $this->options['sourceMapURL']; + } + /** * Generates the JSON source map * From 201b8978143ac913f00ab5a04a11cf2ba7151091 Mon Sep 17 00:00:00 2001 From: dleffler Date: Fri, 15 Dec 2017 12:42:58 -0500 Subject: [PATCH 024/254] Adds cachedCompile function to Server example (taken/modified from less.php) --- example/Server.php | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/example/Server.php b/example/Server.php index 8dc3c7b2..a6e8b986 100644 --- a/example/Server.php +++ b/example/Server.php @@ -416,6 +416,69 @@ public function checkedCachedCompile($in, $out, $force = false) return $css; } + /** + * Execute scssphp on a .scss file or a scssphp cache structure + * + * The scssphp cache structure contains information about a specific + * scss file having been parsed. It can be used as a hint for future + * calls to determine whether or not a rebuild is required. + * + * The cache structure contains two important keys that may be used + * externally: + * + * compiled: The final compiled CSS + * updated: The time (in seconds) the CSS was last compiled + * + * The cache structure is a plain-ol' PHP associative array and can + * be serialized and unserialized without a hitch. + * + * @param mixed $in Input + * @param bool $force Force rebuild? + * @return array scssphp cache structure + */ + public function cachedCompile($in, $force = false) { + // assume no root + $root = null; + + if (is_string($in)) { + $root = $in; + } elseif (is_array($in) and isset($in['root'])) { + if ($force or ! isset($in['files'])) { + // If we are forcing a recompile or if for some reason the + // structure does not contain any file information we should + // specify the root to trigger a rebuild. + $root = $in['root']; + } elseif (isset($in['files']) and is_array($in['files'])) { + foreach ($in['files'] as $fname => $ftime ) { + if (!file_exists($fname) or filemtime($fname) > $ftime) { + // One of the files we knew about previously has changed + // so we should look at our incoming root again. + $root = $in['root']; + break; + } + } + } + } else { + // TODO: Throw an exception? We got neither a string nor something + // that looks like a compatible lessphp cache structure. + return null; + } + + if ($root !== null) { + // If we have a root value which means we should rebuild. + $out = array(); + $out['root'] = $root; + $out['compiled'] = $this->compileFile($root); + $out['files'] = $this->scss->getParsedFiles(); + $out['updated'] = time(); + return $out; + } else { + // No changes, pass back the structure + // we were given initially. + return $in; + } + } + /** * Constructor * From 89d9dabd18f8f068c62d7d0949da0c608ae5ed94 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 18 Dec 2017 17:32:42 -0500 Subject: [PATCH 025/254] Coding style (phpcs) cleanup --- Makefile | 2 +- example/Server.php | 124 +++++++++--------- scss.inc.php | 3 +- src/Block.php | 2 +- src/Compiler.php | 48 +++++-- src/Formatter.php | 27 ++-- src/Formatter/Compressed.php | 4 +- src/Formatter/Debug.php | 10 +- src/Formatter/Nested.php | 14 +- src/Formatter/OutputBlock.php | 4 +- src/SourceMap/Base64VLQEncoder.php | 136 ++++++++++++-------- src/SourceMap/SourceMapGenerator.php | 186 ++++++++++++++++++--------- src/Util.php | 8 +- tests/ServerTest.php | 1 + 14 files changed, 342 insertions(+), 227 deletions(-) diff --git a/Makefile b/Makefile index 5115941c..fb50ffdb 100644 --- a/Makefile +++ b/Makefile @@ -5,4 +5,4 @@ compat: TEST_SCSS_COMPAT=1 vendor/bin/phpunit --colors tests | tail -2 standard: - vendor/bin/phpcs --standard=PSR2 bin src tests *.php + vendor/bin/phpcs --standard=PSR2 bin src example tests *.php diff --git a/example/Server.php b/example/Server.php index a6e8b986..a8dc0457 100644 --- a/example/Server.php +++ b/example/Server.php @@ -417,67 +417,69 @@ public function checkedCachedCompile($in, $out, $force = false) } /** - * Execute scssphp on a .scss file or a scssphp cache structure - * - * The scssphp cache structure contains information about a specific - * scss file having been parsed. It can be used as a hint for future - * calls to determine whether or not a rebuild is required. - * - * The cache structure contains two important keys that may be used - * externally: - * - * compiled: The final compiled CSS - * updated: The time (in seconds) the CSS was last compiled - * - * The cache structure is a plain-ol' PHP associative array and can - * be serialized and unserialized without a hitch. - * - * @param mixed $in Input - * @param bool $force Force rebuild? - * @return array scssphp cache structure - */ - public function cachedCompile($in, $force = false) { - // assume no root - $root = null; - - if (is_string($in)) { - $root = $in; - } elseif (is_array($in) and isset($in['root'])) { - if ($force or ! isset($in['files'])) { - // If we are forcing a recompile or if for some reason the - // structure does not contain any file information we should - // specify the root to trigger a rebuild. - $root = $in['root']; - } elseif (isset($in['files']) and is_array($in['files'])) { - foreach ($in['files'] as $fname => $ftime ) { - if (!file_exists($fname) or filemtime($fname) > $ftime) { - // One of the files we knew about previously has changed - // so we should look at our incoming root again. - $root = $in['root']; - break; - } - } - } - } else { - // TODO: Throw an exception? We got neither a string nor something - // that looks like a compatible lessphp cache structure. - return null; - } - - if ($root !== null) { - // If we have a root value which means we should rebuild. - $out = array(); - $out['root'] = $root; - $out['compiled'] = $this->compileFile($root); - $out['files'] = $this->scss->getParsedFiles(); - $out['updated'] = time(); - return $out; - } else { - // No changes, pass back the structure - // we were given initially. - return $in; - } - } + * Execute scssphp on a .scss file or a scssphp cache structure + * + * The scssphp cache structure contains information about a specific + * scss file having been parsed. It can be used as a hint for future + * calls to determine whether or not a rebuild is required. + * + * The cache structure contains two important keys that may be used + * externally: + * + * compiled: The final compiled CSS + * updated: The time (in seconds) the CSS was last compiled + * + * The cache structure is a plain-ol' PHP associative array and can + * be serialized and unserialized without a hitch. + * + * @param mixed $in Input + * @param boolean $force Force rebuild? + * + * @return array scssphp cache structure + */ + public function cachedCompile($in, $force = false) + { + // assume no root + $root = null; + + if (is_string($in)) { + $root = $in; + } elseif (is_array($in) and isset($in['root'])) { + if ($force or ! isset($in['files'])) { + // If we are forcing a recompile or if for some reason the + // structure does not contain any file information we should + // specify the root to trigger a rebuild. + $root = $in['root']; + } elseif (isset($in['files']) and is_array($in['files'])) { + foreach ($in['files'] as $fname => $ftime) { + if (! file_exists($fname) or filemtime($fname) > $ftime) { + // One of the files we knew about previously has changed + // so we should look at our incoming root again. + $root = $in['root']; + break; + } + } + } + } else { + // TODO: Throw an exception? We got neither a string nor something + // that looks like a compatible lessphp cache structure. + return null; + } + + if ($root !== null) { + // If we have a root value which means we should rebuild. + $out = array(); + $out['root'] = $root; + $out['compiled'] = $this->compileFile($root); + $out['files'] = $this->scss->getParsedFiles(); + $out['updated'] = time(); + return $out; + } else { + // No changes, pass back the structure + // we were given initially. + return $in; + } + } /** * Constructor diff --git a/scss.inc.php b/scss.inc.php index 77303ba3..13c84bf5 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -11,7 +11,7 @@ include_once __DIR__ . '/src/Compiler/Environment.php'; include_once __DIR__ . '/src/Exception/CompilerException.php'; include_once __DIR__ . '/src/Exception/ParserException.php'; - include_once __DIR__ . '/src/Exception/RangeException.php'; //exp + include_once __DIR__ . '/src/Exception/RangeException.php'; include_once __DIR__ . '/src/Exception/ServerException.php'; include_once __DIR__ . '/src/Formatter.php'; include_once __DIR__ . '/src/Formatter/Compact.php'; @@ -24,7 +24,6 @@ include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; -// include_once __DIR__ . '/src/example/Server.php'; include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php'; include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php'; include_once __DIR__ . '/src/Type.php'; diff --git a/src/Block.php b/src/Block.php index 87cfd6d8..fc21e5a4 100644 --- a/src/Block.php +++ b/src/Block.php @@ -29,7 +29,7 @@ class Block public $parent; /** - * @var string; + * @var string */ public $sourceName; diff --git a/src/Compiler.php b/src/Compiler.php index 2dc50531..64399b2a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -128,13 +128,17 @@ class Compiler protected $sourceMap = self::SOURCE_MAP_NONE; protected $sourceMapOptions = []; - /** @var string|Formatter */ + /** + * @var string|\Leafo\ScssPhp\Formatter + */ protected $formatter = 'Leafo\ScssPhp\Formatter\Nested'; protected $rootEnv; protected $rootBlock; - /** @var Environment */ + /** + * @var \Leafo\ScssPhp\Compiler\Environment + */ protected $env; protected $scope; protected $storeEnv; @@ -202,22 +206,30 @@ public function compile($code, $path = null) $this->popEnv(); $sourceMapGenerator = null; - if($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + + if ($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); } + $out = $this->formatter->format($this->scope, $sourceMapGenerator); - if(!empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { - $sourceMap = $sourceMapGenerator->generateJson(); + if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMap = $sourceMapGenerator->generateJson(); $sourceMapUrl = null; - if($this->sourceMap == self::SOURCE_MAP_INLINE) { - $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); - } elseif ($this->sourceMap == self::SOURCE_MAP_FILE) { - $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); + + switch ($this->sourceMap) { + case self::SOURCE_MAP_INLINE: + $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + break; + + case self::SOURCE_MAP_FILE: + $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); + break; } $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); } + return $out; } @@ -1110,7 +1122,8 @@ protected function evalSelectors($selectors) /** * @param array $sourceMapOptions */ - public function setSourceMapOptions($sourceMapOptions) { + public function setSourceMapOptions($sourceMapOptions) + { $this->sourceMapOptions = $sourceMapOptions; } @@ -1959,7 +1972,7 @@ protected function isTruthy($value) * * @param string $value * - * @return bool + * @return boolean */ protected function isImmediateRelationshipCombinator($value) { @@ -3338,9 +3351,14 @@ public function setLineNumberStyle($lineNumberStyle) } /** - * @param int $sourceMap + * Set source map option + * + * @api + * + * @param integer $sourceMap */ - public function setSourceMap($sourceMap) { + public function setSourceMap($sourceMap) + { $this->sourceMap = $sourceMap; } @@ -5302,8 +5320,10 @@ protected function libInspect($args) return $args[0]; } - public static function encodeURIComponent($string){ + public static function encodeURIComponent($string) + { $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + return strtr(rawurlencode($string), $revert); } } diff --git a/src/Formatter.php b/src/Formatter.php index 7939113b..68d20c88 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -62,18 +62,18 @@ abstract class Formatter public $keepSemicolons; /** - * @var OutputBlock; + * @var \Leafo\ScssPhp\Formatter\OutputBlock */ protected $currentBlock; /** - * @var int + * @var integer */ protected $currentLine; /** - * @var int; + * @var integer */ protected $currentColumn; @@ -231,13 +231,14 @@ protected function block(OutputBlock $block) */ public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null) { - if($sourceMapGenerator) { + $this->sourceMapGenerator = null; + + if ($sourceMapGenerator) { $this->currentLine = 1; $this->currentColumn = 0; $this->sourceMapGenerator = $sourceMapGenerator; - } else { - $this->sourceMapGenerator = null; } + ob_start(); $this->block($block); @@ -248,10 +249,11 @@ public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerato } /** - * @param $str + * @param string $str */ - protected function write($str) { - if($this->sourceMapGenerator) { + protected function write($str) + { + if ($this->sourceMapGenerator) { $this->sourceMapGenerator->addMapping( $this->currentLine, $this->currentColumn, @@ -265,11 +267,8 @@ protected function write($str) { $this->currentLine += $lineCount-1; $lastLine = array_pop($lines); - if($lineCount == 1) { - $this->currentColumn += mb_strlen($lastLine); - } else { - $this->currentColumn = mb_strlen($lastLine); - } + + $this->currentColumn = ($lineCount === 1 ? $this->currentColumn : 0) + strlen($lastLine); } echo $str; diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 69a67850..21cbf28e 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -53,10 +53,10 @@ public function blockLines(OutputBlock $block) } } - $this->write( $inner . implode($glue, $block->lines)); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - $this->write( $this->break); + $this->write($this->break); } } } diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index cb29641a..b31c8c7a 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -58,7 +58,7 @@ protected function blockLines(OutputBlock $block) } foreach ($block->lines as $index => $line) { - $this->write( "{$indent}block->lines[{$index}]: $line\n"); + $this->write("{$indent}block->lines[{$index}]: $line\n"); } } @@ -70,13 +70,13 @@ protected function blockSelectors(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->selectors)) { - $this->write( "{$indent}block->selectors: []\n"); + $this->write("{$indent}block->selectors: []\n"); return; } foreach ($block->selectors as $index => $selector) { - $this->write( "{$indent}block->selectors[{$index}]: $selector\n"); + $this->write("{$indent}block->selectors[{$index}]: $selector\n"); } } @@ -88,7 +88,7 @@ protected function blockChildren(OutputBlock $block) $indent = $this->indentStr(); if (empty($block->children)) { - $this->write( "{$indent}block->children: []\n"); + $this->write("{$indent}block->children: []\n"); return; } @@ -109,7 +109,7 @@ protected function block(OutputBlock $block) { $indent = $this->indentStr(); - $this->write( "{$indent}block->type: {$block->type}\n" . + $this->write("{$indent}block->type: {$block->type}\n" . "{$indent}block->depth: {$block->depth}\n"); $this->currentBlock = $block; diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 48e19ef7..7573a596 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -65,10 +65,10 @@ protected function blockLines(OutputBlock $block) } } - $this->write( $inner . implode($glue, $block->lines)); + $this->write($inner . implode($glue, $block->lines)); if (! empty($block->children)) { - $this->write( $this->break); + $this->write($this->break); } } @@ -79,7 +79,7 @@ protected function blockSelectors(OutputBlock $block) { $inner = $this->indentStr(); - $this->write( $inner + $this->write($inner . implode($this->tagSeparator, $block->selectors) . $this->open . $this->break); } @@ -93,13 +93,13 @@ protected function blockChildren(OutputBlock $block) $this->block($child); if ($i < count($block->children) - 1) { - $this->write( $this->break); + $this->write($this->break); if (isset($block->children[$i + 1])) { $next = $block->children[$i + 1]; if ($next->depth === max($block->depth, 1) && $child->depth >= $next->depth) { - $this->write( $this->break); + $this->write($this->break); } } } @@ -141,11 +141,11 @@ protected function block(OutputBlock $block) if (! empty($block->selectors)) { $this->indentLevel--; - $this->write( $this->close); + $this->write($this->close); } if ($block->type === 'root') { - $this->write( $this->break); + $this->write($this->break); } } diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index 7d233a8d..e4092175 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -54,12 +54,12 @@ class OutputBlock public $sourceName; /** - * @var int + * @var integer */ public $sourceLine; /** - * @var int + * @var integer */ public $sourceColumn; } diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php index 14652fbe..d1d1ed2a 100644 --- a/src/SourceMap/Base64VLQEncoder.php +++ b/src/SourceMap/Base64VLQEncoder.php @@ -1,140 +1,167 @@ + * @author Nicolas FRANÇOIS + */ +class Base64VLQEncoder +{ /** * Shift * * @var integer */ private $shift = 5; + /** * Mask * * @var integer */ private $mask = 0x1F; // == (1 << shift) == 0b00011111 + /** * Continuation bit * * @var integer */ private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000 + /** * Char to integer map * * @var array */ private $charToIntMap = array( - 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, - 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, - 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, - 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, - 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34, - 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41, - 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48, - 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56, - 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, + 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, 'H' => 7, + 'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 'P' => 15, + 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23, + 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, + 'g' => 32, 'h' => 33, 'i' => 34, 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, + 'o' => 40, 'p' => 41, 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, + 'w' => 48, 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, + 4 => 56, 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, ); + /** * Integer to char map * * @var array */ private $intToCharMap = array( - 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', - 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', - 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', - 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', - 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', - 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p', - 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', - 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', - 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', - 63 => '/', + 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H', + 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P', + 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', 21 => 'V', 22 => 'W', 23 => 'X', + 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', + 32 => 'g', 33 => 'h', 34 => 'i', 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', + 40 => 'o', 41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', + 48 => 'w', 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', + 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', 63 => '/', ); + /** * Constructor */ - public function __construct(){ + public function __construct() + { // I leave it here for future reference - // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) + // foreach (str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) // { - // $this->charToIntMap[$char] = $i; - // $this->intToCharMap[$i] = $char; + // $this->charToIntMap[$char] = $i; + // $this->intToCharMap[$i] = $char; // } } + /** * Convert from a two-complement value to a value where the sign bit is - * is placed in the least significant bit. For example, as decimals: - * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) - * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, * even on a 64 bit machine. * @param string $aValue */ - public function toVLQSigned($aValue){ + public function toVLQSigned($aValue) + { return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0); } + /** * Convert to a two-complement value from a value where the sign bit is * is placed in the least significant bit. For example, as decimals: - * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 - * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 * We assume that the value was generated with a 32 bit machine in mind. * Hence - * 1 becomes -2147483648 + * 1 becomes -2147483648 * even on a 64 bit machine. * @param integer $aValue */ - public function fromVLQSigned($aValue){ + public function fromVLQSigned($aValue) + { return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1); } + /** * Return the base 64 VLQ encoded value. * * @param string $aValue The value to encode * @return string The encoded value */ - public function encode($aValue){ + public function encode($aValue) + { $encoded = ''; $vlq = $this->toVLQSigned($aValue); - do - { + + do { $digit = $vlq & $this->mask; $vlq = $this->zeroFill($vlq, $this->shift); - if($vlq > 0){ + + if ($vlq > 0) { $digit |= $this->continuationBit; } + $encoded .= $this->base64Encode($digit); - } while($vlq > 0); + } while ($vlq > 0); + return $encoded; } + /** * Return the value decoded from base 64 VLQ. * * @param string $encoded The encoded value to decode * @return integer The decoded value */ - public function decode($encoded){ + public function decode($encoded) + { $vlq = 0; $i = 0; - do - { + + do { $digit = $this->base64Decode($encoded[$i]); $vlq |= ($digit & $this->mask) << ($i * $this->shift); $i++; - } while($digit & $this->continuationBit); + } while ($digit & $this->continuationBit); + return $this->fromVLQSigned($vlq); } + /** * Right shift with zero fill. * @@ -142,9 +169,11 @@ public function decode($encoded){ * @param integer $b number of bits to shift * @return integer */ - public function zeroFill($a, $b){ + public function zeroFill($a, $b) + { return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1)); } + /** * Encode single 6-bit digit as base64. * @@ -152,12 +181,15 @@ public function zeroFill($a, $b){ * @return string * @throws Exception If the number is invalid */ - public function base64Encode($number){ - if($number < 0 || $number > 63){ + public function base64Encode($number) + { + if ($number < 0 || $number > 63) { throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); } + return $this->intToCharMap[$number]; } + /** * Decode single 6-bit digit from base64 * @@ -165,10 +197,12 @@ public function base64Encode($number){ * @return number * @throws Exception If the number is invalid */ - public function base64Decode($char){ - if(!array_key_exists($char, $this->charToIntMap)){ + public function base64Decode($char) + { + if (! array_key_exists($char, $this->charToIntMap)) { throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); } + return $this->charToIntMap[$char]; } } diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 300a5a08..31905b10 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -1,20 +1,33 @@ + * @author Nicolas FRANÇOIS + */ +class SourceMapGenerator +{ /** * What version of source map does the generator generate? */ const VERSION = 3; + /** * Array of default options * @@ -25,16 +38,22 @@ class SourceMapGenerator { // on a server or removing repeated values in the 'sources' entry. // This value is prepended to the individual entries in the 'source' field. 'sourceRoot' => '', + // an optional name of the generated code that this source map is associated with. 'sourceMapFilename' => null, + // url of the map 'sourceMapURL' => null, + // absolute path to a file to write the map to 'sourceMapWriteTo' => null, + // output source contents? 'outputSourceFiles' => false, + // base path for filename normalization 'sourceMapRootpath' => '', + // base path for filename normalization 'sourceMapBasepath' => '' ); @@ -42,9 +61,10 @@ class SourceMapGenerator { /** * The base64 VLQ encoder * - * @var Base64VLQEncoder + * @var \Leafo\ScssPhp\SourceMap\Base64VLQEncoder */ protected $encoder; + /** * Array of mappings * @@ -58,6 +78,7 @@ class SourceMapGenerator { * @var array */ protected $contentsMap = array(); + /** * File to content map * @@ -65,12 +86,14 @@ class SourceMapGenerator { */ protected $sources = array(); protected $source_keys = array(); + /** * @var array */ private $options; - public function __construct(array $options = []) { + public function __construct(array $options = []) + { $this->options = array_merge($this->defaultOptions, $options); $this->encoder = new Base64VLQEncoder(); } @@ -84,7 +107,8 @@ public function __construct(array $options = []) { * @param integer $originalColumn The column number in original file * @param string $sourceFile The original source file */ - public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) { + public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) + { $this->mappings[] = array( 'generated_line' => $generatedLine, 'generated_column' => $generatedColumn, @@ -97,26 +121,30 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori } /** - * Saves the source map to a file - * - * @param string $file The absolute path to a file - * @param string $content The content to write - * @throws Exception If the file could not be saved - */ - public function saveMap($content){ - $file = $this->options['sourceMapWriteTo']; - $dir = dirname($file); - // directory does not exist - if( !is_dir($dir) ){ - // FIXME: create the dir automatically? - throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); - } - // FIXME: proper saving, with dir write check! - if(file_put_contents($file, $content) === false){ - throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file)); - } - return $this->options['sourceMapURL']; - } + * Saves the source map to a file + * + * @param string $file The absolute path to a file + * @param string $content The content to write + * @throws Exception If the file could not be saved + */ + public function saveMap($content) + { + $file = $this->options['sourceMapWriteTo']; + $dir = dirname($file); + + // directory does not exist + if (! is_dir($dir)) { + // FIXME: create the dir automatically? + throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); + } + + // FIXME: proper saving, with dir write check! + if (file_put_contents($file, $content) === false) { + throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file)); + } + + return $this->options['sourceMapURL']; + } /** * Generates the JSON source map @@ -124,40 +152,44 @@ public function saveMap($content){ * @return string * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# */ - public function generateJson() { + public function generateJson() + { $sourceMap = array(); - $mappings = $this->generateMappings(); + $mappings = $this->generateMappings(); + // File version (always the first entry in the object) and must be a positive integer. $sourceMap['version'] = self::VERSION; + // An optional name of the generated code that this source map is associated with. $file = $this->options['sourceMapFilename']; - if($file) { + + if ($file) { $sourceMap['file'] = $file; } - // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field. + + // An optional source root, useful for relocating source files on a server or removing repeated values in the + // 'sources' entry. This value is prepended to the individual entries in the 'source' field. $root = $this->options['sourceRoot']; - if($root) { - $sourceMap['sourceRoot'] = $root; - } - // A list of original sources used by the 'mappings' entry. - $sourceMap['sources'] = array(); - foreach($this->sources as $source_uri => $source_filename) { - $sourceMap['sources'][] = $this->normalizeFilename($source_filename); + + if ($root) { + $sourceMap['names'] = array(); } - // A list of symbol names used by the 'mappings' entry. - $sourceMap['names'] = array(); + // A string with the encoded mapping data. $sourceMap['mappings'] = $mappings; - if($this->options['outputSourceFiles']) { + + if ($this->options['outputSourceFiles']) { // An optional list of source content, useful when the 'source' can't be hosted. // The contents are listed in the same order as the sources above. // 'null' may be used if some original sources should be retrieved by name. $sourceMap['sourcesContent'] = $this->getSourcesContent(); } + // less.js compat fixes - if(count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) { + if (count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) { unset($sourceMap['sourceRoot']); } + return json_encode($sourceMap); } @@ -166,14 +198,18 @@ public function generateJson() { * * @return array|null */ - protected function getSourcesContent() { - if(empty($this->sources)) { + protected function getSourcesContent() + { + if (empty($this->sources)) { return null; } + $content = array(); - foreach($this->sources as $sourceFile) { + + foreach ($this->sources as $sourceFile) { $content[] = file_get_contents($sourceFile); } + return $content; } @@ -182,31 +218,41 @@ protected function getSourcesContent() { * * @return string */ - public function generateMappings() { - if(!count($this->mappings)) { + public function generateMappings() + { + if (! count($this->mappings)) { return ''; } + $this->source_keys = array_flip(array_keys($this->sources)); + // group mappings by generated line number. $groupedMap = $groupedMapEncoded = array(); - foreach($this->mappings as $m) { + + foreach ($this->mappings as $m) { $groupedMap[$m['generated_line']][] = $m; } + ksort($groupedMap); $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0; - foreach($groupedMap as $lineNumber => $line_map) { - while(++$lastGeneratedLine < $lineNumber) { + + foreach ($groupedMap as $lineNumber => $line_map) { + while (++$lastGeneratedLine < $lineNumber) { $groupedMapEncoded[] = ';'; } + $lineMapEncoded = array(); $lastGeneratedColumn = 0; - foreach($line_map as $m) { + + foreach ($line_map as $m) { $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn); $lastGeneratedColumn = $m['generated_column']; + // find the index - if($m['source_file']) { + if ($m['source_file']) { $index = $this->findFileIndex($m['source_file']); - if($index !== false) { + + if ($index !== false) { $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex); $lastOriginalIndex = $index; // lines are stored 0-based in SourceMap spec version 3 @@ -216,10 +262,13 @@ public function generateMappings() { $lastOriginalColumn = $m['original_column']; } } + $lineMapEncoded[] = $mapEncoded; } + $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';'; } + return rtrim(implode($groupedMapEncoded), ';'); } @@ -227,39 +276,50 @@ public function generateMappings() { * Finds the index for the filename * * @param string $filename + * * @return integer|false */ - protected function findFileIndex($filename) { + protected function findFileIndex($filename) + { return $this->source_keys[$filename]; } - protected function normalizeFilename($filename) { + protected function normalizeFilename($filename) + { $filename = $this->fixWindowsPath($filename); $rootpath = $this->options['sourceMapRootpath']; $basePath = $this->options['sourceMapBasepath']; + // "Trim" the 'sourceMapBasepath' from the output filename. - if(strpos($filename, $basePath) === 0) { + if (strpos($filename, $basePath) === 0) { $filename = substr($filename, strlen($basePath)); } + // Remove extra leading path separators. - if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) { + if (strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) { $filename = substr($filename, 1); } + return $rootpath . $filename; } /** - * fix windows paths - * @param string $path - * @param bool $addEndSlash + * Fix windows paths + * + * @param string $path + * @param boolean $addEndSlash + * * @return string */ - public function fixWindowsPath($path, $addEndSlash = false) { + public function fixWindowsPath($path, $addEndSlash = false) + { $slash = ($addEndSlash) ? '/' : ''; - if(!empty($path)) { + + if (! empty($path)) { $path = str_replace('\\', '/', $path); $path = rtrim($path, '/') . $slash; } + return $path; } } diff --git a/src/Util.php b/src/Util.php index b2a05db8..f5aca7a4 100644 --- a/src/Util.php +++ b/src/Util.php @@ -25,10 +25,10 @@ class Util * Asserts that `value` falls within `range` (inclusive), leaving * room for slight floating-point errors. * - * @param string $name The name of the value. Used in the error message. - * @param Range $range Range of values. - * @param array $value The value to check. - * @param string $unit The unit of the value. Used in error reporting. + * @param string $name The name of the value. Used in the error message. + * @param \Leafo\ScssPhp\Base\Range $range Range of values. + * @param array $value The value to check. + * @param string $unit The unit of the value. Used in error reporting. * * @return mixed `value` adjusted to fall within range, if it was outside by a floating-point margin. * diff --git a/tests/ServerTest.php b/tests/ServerTest.php index fcaea917..290d8fbf 100644 --- a/tests/ServerTest.php +++ b/tests/ServerTest.php @@ -8,6 +8,7 @@ * * @link http://leafo.github.io/scssphp */ + namespace Leafo\ScssPhp\Tests; require_once __DIR__ . '/../example/Server.php'; From 266913f8bf86b0e6a144e75225c82dc12652bedc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Dec 2017 16:24:54 -0500 Subject: [PATCH 026/254] More coding style tweaks --- src/Compiler.php | 57 +++++++++++++--------------- src/Formatter.php | 8 ++-- src/Formatter/Nested.php | 1 + src/Parser.php | 2 +- src/SourceMap/Base64VLQEncoder.php | 19 +++++++--- src/SourceMap/SourceMapGenerator.php | 14 ++++--- src/Util.php | 16 +++++++- 7 files changed, 69 insertions(+), 48 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 64399b2a..f12cf7ee 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -65,9 +65,9 @@ class Compiler const WITH_SUPPORTS = 4; const WITH_ALL = 7; - const SOURCE_MAP_NONE = 0; + const SOURCE_MAP_NONE = 0; const SOURCE_MAP_INLINE = 1; - const SOURCE_MAP_FILE = 2; + const SOURCE_MAP_FILE = 2; /** * @var array @@ -219,7 +219,7 @@ public function compile($code, $path = null) switch ($this->sourceMap) { case self::SOURCE_MAP_INLINE: - $sourceMapUrl = sprintf('data:application/json,%s', self::encodeURIComponent($sourceMap)); + $sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap)); break; case self::SOURCE_MAP_FILE: @@ -305,14 +305,14 @@ protected function pushExtends($target, $origin, $block) protected function makeOutputBlock($type, $selectors = null) { $out = new OutputBlock; - $out->type = $type; - $out->lines = []; - $out->children = []; - $out->parent = $this->scope; - $out->selectors = $selectors; - $out->depth = $this->env->depth; - $out->sourceName = $this->env->block->sourceName; - $out->sourceLine = $this->env->block->sourceLine; + $out->type = $type; + $out->lines = []; + $out->children = []; + $out->parent = $this->scope; + $out->selectors = $selectors; + $out->depth = $this->env->depth; + $out->sourceName = $this->env->block->sourceName; + $out->sourceLine = $this->env->block->sourceLine; $out->sourceColumn = $this->env->block->sourceColumn; return $out; @@ -696,7 +696,7 @@ protected function compileMedia(Block $media) if ($needsWrap) { $wrapped = new Block; - $wrapped->sourceName = $media->sourceName; + $wrapped->sourceName = $media->sourceName; $wrapped->sourceIndex = $media->sourceIndex; $wrapped->sourceLine = $media->sourceLine; $wrapped->sourceColumn = $media->sourceColumn; @@ -1119,14 +1119,6 @@ protected function evalSelectors($selectors) return $selectors; } - /** - * @param array $sourceMapOptions - */ - public function setSourceMapOptions($sourceMapOptions) - { - $this->sourceMapOptions = $sourceMapOptions; - } - /** * Evaluate selector * @@ -3351,7 +3343,7 @@ public function setLineNumberStyle($lineNumberStyle) } /** - * Set source map option + * Enable/disable source maps * * @api * @@ -3362,6 +3354,18 @@ public function setSourceMap($sourceMap) $this->sourceMap = $sourceMap; } + /** + * Set source map options + * + * @api + * + * @param array $sourceMapOptions + */ + public function setSourceMapOptions($sourceMapOptions) + { + $this->sourceMapOptions = $sourceMapOptions; + } + /** * Register function * @@ -5188,7 +5192,7 @@ protected function libToLowerCase($args) $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - $string[2] = [mb_strtolower($stringContent)]; + $string[2] = [function_exists('mb_strtolower') ? mb_strtolower($stringContent) : strtolower($stringContent)]; return $string; } @@ -5199,7 +5203,7 @@ protected function libToUpperCase($args) $string = $this->coerceString($args[0]); $stringContent = $this->compileStringContent($string); - $string[2] = [mb_strtoupper($stringContent)]; + $string[2] = [function_exists('mb_strtoupper') ? mb_strtoupper($stringContent) : strtoupper($stringContent)]; return $string; } @@ -5319,11 +5323,4 @@ protected function libInspect($args) return $args[0]; } - - public static function encodeURIComponent($string) - { - $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); - - return strtr(rawurlencode($string), $revert); - } } diff --git a/src/Formatter.php b/src/Formatter.php index 68d20c88..4d2fdd08 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -66,7 +66,6 @@ abstract class Formatter */ protected $currentBlock; - /** * @var integer */ @@ -78,7 +77,7 @@ abstract class Formatter protected $currentColumn; /** - * @var SourceMapGenerator + * @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator */ protected $sourceMapGenerator; @@ -223,11 +222,10 @@ protected function block(OutputBlock $block) * * @api * - * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree + * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree + * @param \Leafo\ScssPhp\SourceMap\SourceMapGenerator|null $sourceMapGenerator Optional source map generator * - * @param SourceMapGenerator|null $sourceMapGenerator * @return string - * @internal param bool $collectSourceMap */ public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null) { diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 7573a596..626fec13 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp\Formatter; use Leafo\ScssPhp\Formatter; +use Leafo\ScssPhp\Formatter\OutputBlock; /** * Nested formatter diff --git a/src/Parser.php b/src/Parser.php index cc0a51df..ccac0d3f 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -708,7 +708,7 @@ protected function pushBlock($selectors, $pos = 0) list($line, $column) = $this->getSourcePosition($pos); $b = new Block; - $b->sourceName = $this->sourceName; + $b->sourceName = $this->sourceName; $b->sourceLine = $line; $b->sourceColumn = $column; $b->sourceIndex = $this->sourceIndex; diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php index d1d1ed2a..435a7c60 100644 --- a/src/SourceMap/Base64VLQEncoder.php +++ b/src/SourceMap/Base64VLQEncoder.php @@ -94,6 +94,7 @@ public function __construct() * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, * even on a 64 bit machine. + * * @param string $aValue */ public function toVLQSigned($aValue) @@ -110,6 +111,7 @@ public function toVLQSigned($aValue) * Hence * 1 becomes -2147483648 * even on a 64 bit machine. + * * @param integer $aValue */ public function fromVLQSigned($aValue) @@ -121,6 +123,7 @@ public function fromVLQSigned($aValue) * Return the base 64 VLQ encoded value. * * @param string $aValue The value to encode + * * @return string The encoded value */ public function encode($aValue) @@ -146,6 +149,7 @@ public function encode($aValue) * Return the value decoded from base 64 VLQ. * * @param string $encoded The encoded value to decode + * * @return integer The decoded value */ public function decode($encoded) @@ -167,6 +171,7 @@ public function decode($encoded) * * @param integer $a number to shift * @param integer $b number of bits to shift + * * @return integer */ public function zeroFill($a, $b) @@ -178,13 +183,15 @@ public function zeroFill($a, $b) * Encode single 6-bit digit as base64. * * @param integer $number + * * @return string - * @throws Exception If the number is invalid + * + * @throws \Exception If the number is invalid */ public function base64Encode($number) { if ($number < 0 || $number > 63) { - throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); + throw new \Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); } return $this->intToCharMap[$number]; @@ -194,13 +201,15 @@ public function base64Encode($number) * Decode single 6-bit digit from base64 * * @param string $char - * @return number - * @throws Exception If the number is invalid + * + * @return integer + * + * @throws \Exception If the number is invalid */ public function base64Decode($char) { if (! array_key_exists($char, $this->charToIntMap)) { - throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); + throw new \Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); } return $this->charToIntMap[$char]; diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 31905b10..cbfaa7cf 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -101,11 +101,11 @@ public function __construct(array $options = []) /** * Adds a mapping * - * @param integer $generatedLine The line number in generated file + * @param integer $generatedLine The line number in generated file * @param integer $generatedColumn The column number in generated file - * @param integer $originalLine The line number in original file - * @param integer $originalColumn The column number in original file - * @param string $sourceFile The original source file + * @param integer $originalLine The line number in original file + * @param integer $originalColumn The column number in original file + * @param string $sourceFile The original source file */ public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile) { @@ -123,9 +123,10 @@ public function addMapping($generatedLine, $generatedColumn, $originalLine, $ori /** * Saves the source map to a file * - * @param string $file The absolute path to a file + * @param string $file The absolute path to a file * @param string $content The content to write - * @throws Exception If the file could not be saved + * + * @throws \Leafo\ScssPhp\Exception\CompilerException If the file could not be saved */ public function saveMap($content) { @@ -150,6 +151,7 @@ public function saveMap($content) * Generates the JSON source map * * @return string + * * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# */ public function generateJson() diff --git a/src/Util.php b/src/Util.php index f5aca7a4..d49b265c 100644 --- a/src/Util.php +++ b/src/Util.php @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Exception\RangeException; /** - * Utilties + * Utilty functions * * @author Anthon Pang */ @@ -53,4 +53,18 @@ public static function checkRange($name, Range $range, $value, $unit = '') throw new RangeException("$name {$val} must be between {$range->first} and {$range->last}$unit"); } + + /** + * Encode URI component + * + * @param string $string + * + * @return string + */ + public static function encodeURIComponent($string) + { + $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + + return strtr(rawurlencode($string), $revert); + } } From f92d3e46cf98f1ba7a6af0d8ceee1beeaff75827 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Dec 2017 16:31:11 -0500 Subject: [PATCH 027/254] Version bump to v0.7.3; fixed #135 --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 0dd95fe6..e9c0218c 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.2'; + const VERSION = 'v0.7.3'; } From b373570479027fa226afec3762852e54ee560bdd Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Dec 2017 16:45:24 -0500 Subject: [PATCH 028/254] Add --sourcemap flag to bin/pscss --- bin/pscss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bin/pscss b/bin/pscss index 70a2be3c..be1cb51c 100755 --- a/bin/pscss +++ b/bin/pscss @@ -32,6 +32,7 @@ $debugInfo = false; $lineNumbers = false; $ignoreErrors = false; $encoding = false; +$sourceMap = false; /** * Parse argument @@ -77,6 +78,7 @@ Options include: --iso8859-1 Use iso8859-1 encoding instead of utf-8 (default utf-8) --line-numbers Annotate selectors with comments referring to the source file and line number -p=precision Set decimal number precision (default 10) + --sourcemap Create source map file -T Dump formatted parse tree -v, --version Print the version @@ -108,6 +110,11 @@ EOT; continue; } + if ($argv[$i] === '--sourcemap') { + $sourceMap = true; + continue; + } + if ($argv[$i] === '-T') { $dumpTree = true; continue; @@ -193,6 +200,10 @@ if ($style) { $scss->setFormatter('Leafo\\ScssPhp\\Formatter\\' . ucfirst($style)); } +if ($sourceMap) { + $scss->setSourceMap(Compiler::SOURCE_MAP_FILE); +} + if ($encoding) { $scss->setEncoding($encoding); } From 1e862d7f28bbcce159e3c75eee8069e059f95563 Mon Sep 17 00:00:00 2001 From: dleffler Date: Wed, 20 Dec 2017 09:08:18 -0500 Subject: [PATCH 029/254] Fix/restore working source map generation in 0.7.3 --- src/SourceMap/SourceMapGenerator.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index cbfaa7cf..3a55851a 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -174,9 +174,17 @@ public function generateJson() $root = $this->options['sourceRoot']; if ($root) { +// $sourceMap['sourceRoot'] = $root; + // A list of symbol names used by the 'mappings' entry. $sourceMap['names'] = array(); } + // A list of original sources used by the 'mappings' entry. + $sourceMap['sources'] = array(); + foreach($this->sources as $source_uri => $source_filename) { + $sourceMap['sources'][] = $this->normalizeFilename($source_filename); + } + // A string with the encoded mapping data. $sourceMap['mappings'] = $mappings; From e8e4df7c5447165638ac255bd86a08b093bc9fb0 Mon Sep 17 00:00:00 2001 From: dleffler Date: Wed, 20 Dec 2017 16:03:15 -0500 Subject: [PATCH 030/254] Regression fix last push to fix 0.7.3 source maps --- src/SourceMap/SourceMapGenerator.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 3a55851a..c0f96dab 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -172,11 +172,9 @@ public function generateJson() // An optional source root, useful for relocating source files on a server or removing repeated values in the // 'sources' entry. This value is prepended to the individual entries in the 'source' field. $root = $this->options['sourceRoot']; - if ($root) { -// $sourceMap['sourceRoot'] = $root; + $sourceMap['sourceRoot'] = $root; // A list of symbol names used by the 'mappings' entry. - $sourceMap['names'] = array(); } // A list of original sources used by the 'mappings' entry. @@ -184,6 +182,7 @@ public function generateJson() foreach($this->sources as $source_uri => $source_filename) { $sourceMap['sources'][] = $this->normalizeFilename($source_filename); } + $sourceMap['names'] = array(); // A string with the encoded mapping data. $sourceMap['mappings'] = $mappings; From 045f3087bcb7a293ca2f6da02109fa17d20834a5 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 20 Dec 2017 19:17:12 -0500 Subject: [PATCH 031/254] Update SourceMapGenerator.php --- src/SourceMap/SourceMapGenerator.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index c0f96dab..08e06168 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -172,16 +172,19 @@ public function generateJson() // An optional source root, useful for relocating source files on a server or removing repeated values in the // 'sources' entry. This value is prepended to the individual entries in the 'source' field. $root = $this->options['sourceRoot']; + if ($root) { $sourceMap['sourceRoot'] = $root; - // A list of symbol names used by the 'mappings' entry. } // A list of original sources used by the 'mappings' entry. $sourceMap['sources'] = array(); - foreach($this->sources as $source_uri => $source_filename) { + + foreach ($this->sources as $source_uri => $source_filename) { $sourceMap['sources'][] = $this->normalizeFilename($source_filename); } + + // A list of symbol names used by the 'mappings' entry. $sourceMap['names'] = array(); // A string with the encoded mapping data. From d7296256bd1bfacb8bd24e69030fd5e187181835 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 20 Dec 2017 19:18:53 -0500 Subject: [PATCH 032/254] Version bump --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index e9c0218c..6d07d5ab 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.3'; + const VERSION = 'v0.7.4'; } From 11f556571b2d4d531a6215398f39e313e48d1aa2 Mon Sep 17 00:00:00 2001 From: dleffler Date: Mon, 5 Feb 2018 09:23:38 -0500 Subject: [PATCH 033/254] Add option to pass a custom SourceMapGenerator object --- src/Compiler.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index f12cf7ee..35a3f866 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -207,8 +207,13 @@ public function compile($code, $path = null) $sourceMapGenerator = null; - if ($this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { - $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + if ($this->sourceMap) { + if (is_object($this->sourceMap) && is_a($this->sourceMap, 'SourceMapGenerator')) { + $sourceMapGenerator = $this->sourceMap; + $this->sourceMap = self::SOURCE_MAP_FILE; + } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + } } $out = $this->formatter->format($this->scope, $sourceMapGenerator); From a73e32a5e110a8de9919939b3f5371d44c1299b5 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 03:14:51 -0500 Subject: [PATCH 034/254] is_a() -> instanceof --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 35a3f866..03c94d9a 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -208,7 +208,7 @@ public function compile($code, $path = null) $sourceMapGenerator = null; if ($this->sourceMap) { - if (is_object($this->sourceMap) && is_a($this->sourceMap, 'SourceMapGenerator')) { + if (is_object($this->sourceMap) && $this->sourceMap instanceof SourceMapGenerator) { $sourceMapGenerator = $this->sourceMap; $this->sourceMap = self::SOURCE_MAP_FILE; } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) { From 593b92929c3f0e9d82c84ace7d29e8550e386413 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 03:19:06 -0500 Subject: [PATCH 035/254] No side-effect in abs(), ceil(), floor(), round(); fixes #551 --- src/Compiler.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 03c94d9a..8da8be77 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4740,36 +4740,32 @@ protected function libPercentage($args) protected function libRound($args) { $num = $args[0]; - $num[1] = round($num[1]); - return $num; + return new Node\Number(round($num[1]), $num[2]); } protected static $libFloor = ['value']; protected function libFloor($args) { $num = $args[0]; - $num[1] = floor($num[1]); - return $num; + return new Node\Number(floor($num[1]), $num[2]); } protected static $libCeil = ['value']; protected function libCeil($args) { $num = $args[0]; - $num[1] = ceil($num[1]); - return $num; + return new Node\Number(ceil($num[1]), $num[2]); } protected static $libAbs = ['value']; protected function libAbs($args) { $num = $args[0]; - $num[1] = abs($num[1]); - return $num; + return new Node\Number(abs($num[1]), $num[2]); } protected function libMin($args) From d3801706dbf2423a380b7815383c0cbc9203c58a Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 03:50:15 -0500 Subject: [PATCH 036/254] `@for` with units; fixes #548 --- src/Compiler.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 8da8be77..d46d5332 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1772,9 +1772,18 @@ protected function compileChild($child, OutputBlock $out) list(, $for) = $child; $start = $this->reduce($for->start, true); + $end = $this->reduce($for->end, true); + + if ( ! ($start[2] == $end[2] || $end->unitless())) { + $this->throwError('Incompatible units: "%s" and "%s".', $start->unitStr(), $end->unitStr()); + + break; + } + + $unit = $start[2]; $start = $start[1]; - $end = $this->reduce($for->end, true); - $end = $end[1]; + $end = $end[1]; + $d = $start < $end ? 1 : -1; for (;;) { @@ -1784,7 +1793,7 @@ protected function compileChild($child, OutputBlock $out) break; } - $this->set($for->var, new Node\Number($start, '')); + $this->set($for->var, new Node\Number($start, $unit)); $start += $d; $ret = $this->compileChildren($for->children, $out); From a4b566f0571f63a2cc00ee03025f6a498092383f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 6 Feb 2018 04:03:48 -0500 Subject: [PATCH 037/254] Update docblocks --- src/Base/Range.php | 2 +- src/Block.php | 2 +- src/Colors.php | 2 +- src/Compiler.php | 4 ++-- src/Compiler/Environment.php | 2 +- src/Exception/CompilerException.php | 2 +- src/Exception/ParserException.php | 2 +- src/Exception/RangeException.php | 2 +- src/Exception/ServerException.php | 2 +- src/Formatter.php | 2 +- src/Formatter/Compact.php | 2 +- src/Formatter/Compressed.php | 2 +- src/Formatter/Crunched.php | 2 +- src/Formatter/Debug.php | 2 +- src/Formatter/Expanded.php | 2 +- src/Formatter/Nested.php | 2 +- src/Formatter/OutputBlock.php | 2 +- src/Node.php | 2 +- src/Node/Number.php | 2 +- src/Parser.php | 2 +- src/SourceMap/Base64VLQEncoder.php | 2 +- src/SourceMap/SourceMapGenerator.php | 2 +- src/Type.php | 2 +- src/Util.php | 2 +- src/Version.php | 2 +- 25 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Base/Range.php b/src/Base/Range.php index 3f3a067d..8bcc6ecf 100644 --- a/src/Base/Range.php +++ b/src/Base/Range.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2017 Leaf Corcoran + * @copyright 2015-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Block.php b/src/Block.php index fc21e5a4..a6ef8e03 100644 --- a/src/Block.php +++ b/src/Block.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Colors.php b/src/Colors.php index 61a71ab6..2314ea58 100644 --- a/src/Colors.php +++ b/src/Colors.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Compiler.php b/src/Compiler.php index d46d5332..292e960e 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * @@ -1774,7 +1774,7 @@ protected function compileChild($child, OutputBlock $out) $start = $this->reduce($for->start, true); $end = $this->reduce($for->end, true); - if ( ! ($start[2] == $end[2] || $end->unitless())) { + if (! ($start[2] == $end[2] || $end->unitless())) { $this->throwError('Incompatible units: "%s" and "%s".', $start->unitStr(), $end->unitStr()); break; diff --git a/src/Compiler/Environment.php b/src/Compiler/Environment.php index b4b86e32..fe309dd3 100644 --- a/src/Compiler/Environment.php +++ b/src/Compiler/Environment.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/CompilerException.php b/src/Exception/CompilerException.php index 45fc1671..7ca2e2b3 100644 --- a/src/Exception/CompilerException.php +++ b/src/Exception/CompilerException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/ParserException.php b/src/Exception/ParserException.php index c0ee002d..6d64335f 100644 --- a/src/Exception/ParserException.php +++ b/src/Exception/ParserException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/RangeException.php b/src/Exception/RangeException.php index 47192ff5..3ba6bf14 100644 --- a/src/Exception/RangeException.php +++ b/src/Exception/RangeException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Exception/ServerException.php b/src/Exception/ServerException.php index 68d3a290..d0ed0842 100644 --- a/src/Exception/ServerException.php +++ b/src/Exception/ServerException.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter.php b/src/Formatter.php index 4d2fdd08..b4f90aa9 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Compact.php b/src/Formatter/Compact.php index aaf972b7..4efa1a08 100644 --- a/src/Formatter/Compact.php +++ b/src/Formatter/Compact.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Compressed.php b/src/Formatter/Compressed.php index 21cbf28e..1faa7e11 100644 --- a/src/Formatter/Compressed.php +++ b/src/Formatter/Compressed.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Crunched.php b/src/Formatter/Crunched.php index 43d63833..42d77b5f 100644 --- a/src/Formatter/Crunched.php +++ b/src/Formatter/Crunched.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Debug.php b/src/Formatter/Debug.php index b31c8c7a..bfcbf41a 100644 --- a/src/Formatter/Debug.php +++ b/src/Formatter/Debug.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Expanded.php b/src/Formatter/Expanded.php index 0449787d..d8c1e887 100644 --- a/src/Formatter/Expanded.php +++ b/src/Formatter/Expanded.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/Nested.php b/src/Formatter/Nested.php index 626fec13..8f72206f 100644 --- a/src/Formatter/Nested.php +++ b/src/Formatter/Nested.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Formatter/OutputBlock.php b/src/Formatter/OutputBlock.php index e4092175..5eb589c9 100644 --- a/src/Formatter/OutputBlock.php +++ b/src/Formatter/OutputBlock.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Node.php b/src/Node.php index eb543c73..b9f7945a 100644 --- a/src/Node.php +++ b/src/Node.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Node/Number.php b/src/Node/Number.php index efa83f5b..42c16803 100644 --- a/src/Node/Number.php +++ b/src/Node/Number.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Parser.php b/src/Parser.php index ccac0d3f..6fdea3e2 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/SourceMap/Base64VLQEncoder.php b/src/SourceMap/Base64VLQEncoder.php index 435a7c60..1189ce0e 100644 --- a/src/SourceMap/Base64VLQEncoder.php +++ b/src/SourceMap/Base64VLQEncoder.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 08e06168..70b47cdc 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Type.php b/src/Type.php index 2d659e4d..e84c47e4 100644 --- a/src/Type.php +++ b/src/Type.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Util.php b/src/Util.php index d49b265c..7526e020 100644 --- a/src/Util.php +++ b/src/Util.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * diff --git a/src/Version.php b/src/Version.php index 6d07d5ab..2bda80ff 100644 --- a/src/Version.php +++ b/src/Version.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * From 8b538d828bbb75276974605c4a1a435e939da74e Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 8 Feb 2018 21:04:21 -0500 Subject: [PATCH 038/254] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 2bda80ff..3b8dcd00 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.4'; + const VERSION = 'v0.7.5'; } From 9410cbb398d17b096ce4bdd533f1712a651aca3d Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 28 Feb 2018 23:06:40 -0500 Subject: [PATCH 039/254] Fixes #559 - mix() fix --- src/Compiler.php | 2 +- tests/inputs/functions.scss | 9 ++++++++- tests/outputs/functions.css | 3 +++ tests/outputs_numbered/functions.css | 3 +++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 292e960e..698d4d89 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4526,7 +4526,7 @@ protected function libMix($args) ]; if ($firstAlpha != 1.0 || $secondAlpha != 1.0) { - $new[] = $firstAlpha * $weight + $secondAlpha * ($weight - 1); + $new[] = $firstAlpha * $weight + $secondAlpha * (1 - $weight); } return $this->fixColor($new); diff --git a/tests/inputs/functions.scss b/tests/inputs/functions.scss index bf3edf1f..9ebd544f 100644 --- a/tests/inputs/functions.scss +++ b/tests/inputs/functions.scss @@ -165,4 +165,11 @@ $str: 'global'; div { margin: mapping(one two); @include test(); -} \ No newline at end of file +} + +$a: rgba(#000, 0.5); +$b: rgba(#fff, 0.75); + +.test { + color: mix($a, $b); +} diff --git a/tests/outputs/functions.css b/tests/outputs/functions.css index aa0bebbb..e89beb8f 100644 --- a/tests/outputs/functions.css +++ b/tests/outputs/functions.css @@ -49,3 +49,6 @@ p { div { margin: 1px 1px 2px 2px; padding: 1px 1px 2px 2px; } + +.test { + color: rgba(159, 159, 159, 0.625); } diff --git a/tests/outputs_numbered/functions.css b/tests/outputs_numbered/functions.css index 6583ff05..6841fa05 100644 --- a/tests/outputs_numbered/functions.css +++ b/tests/outputs_numbered/functions.css @@ -51,3 +51,6 @@ p { div { margin: 1px 1px 2px 2px; padding: 1px 1px 2px 2px; } +/* line 173, inputs/functions.scss */ +.test { + color: rgba(159, 159, 159, 0.625); } From 847f9f2d6cb64042ed6ca91938f8aa7d20dcadba Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Thu, 19 Apr 2018 11:42:23 -0400 Subject: [PATCH 040/254] fixes #568 example/Server: set default timezone to UTC --- example/Server.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/example/Server.php b/example/Server.php index a8dc0457..6ea97018 100644 --- a/example/Server.php +++ b/example/Server.php @@ -207,7 +207,7 @@ protected function compile($in, $out) $elapsed = round((microtime(true) - $start), 4); $v = Version::VERSION; - $t = date('r'); + $t = gmdate('r'); $css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css; $etag = md5($css); @@ -329,7 +329,7 @@ public function serve($salt = '') try { list($css, $etag) = $this->compile($input, $output); - $lastModified = gmdate('D, d M Y H:i:s', filemtime($output)) . ' GMT'; + $lastModified = gmdate('r', filemtime($output)); header('Last-Modified: ' . $lastModified); header('Content-type: text/css'); @@ -371,7 +371,7 @@ public function serve($salt = '') return; } - $lastModified = gmdate('D, d M Y H:i:s', $mtime) . ' GMT'; + $lastModified = gmdate('r', $mtime); header('Last-Modified: ' . $lastModified); echo file_get_contents($output); @@ -510,8 +510,6 @@ public function __construct($dir, $cacheDir = null, $scss = null) $this->scss = $scss; $this->showErrorsAsCSS = false; - if (! ini_get('date.timezone')) { - throw new ServerException('Default date.timezone not set'); - } + date_default_timezone_set('UTC'); } } From 53bb5275a7e3fad3676f1e01f3014d39a0c6d675 Mon Sep 17 00:00:00 2001 From: AzJezz Date: Sun, 29 Apr 2018 16:32:07 +0100 Subject: [PATCH 041/254] Update Compiler.php --- src/Compiler.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 698d4d89..32e9b774 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -316,10 +316,15 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; - $out->sourceName = $this->env->block->sourceName; - $out->sourceLine = $this->env->block->sourceLine; - $out->sourceColumn = $this->env->block->sourceColumn; - + if($this->env->block instanceof Block) { + $out->sourceName = $this->env->block->sourceName; + $out->sourceLine = $this->env->block->sourceLine; + $out->sourceColumn = $this->env->block->sourceColumn; + } else { + $out->sourceName = null; + $out->sourceLine = null; + $out->sourceColum = null; + } return $out; } From ddebe1a68a53c4b8524c03f8cb2c24174cb028dc Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Fri, 18 May 2018 14:48:28 -0400 Subject: [PATCH 042/254] Fix #573 --- src/Compiler.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 32e9b774..1aad621b 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2699,7 +2699,9 @@ public function compileValue($value) $b = round($b); if (count($value) === 5 && $value[4] !== 1) { // rgba - return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $value[4] . ')'; + $a = new Node\Number($value[4]); + + return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $a . ')'; } $h = sprintf('#%02x%02x%02x', $r, $g, $b); @@ -4336,7 +4338,7 @@ protected function libRgb($args) protected function libRgba($args) { if ($color = $this->coerceColor($args[0])) { - $num = ! isset($args[1]) ? $args[3] : $args[1]; + $num = isset($args[3]) ? $args[3] : $args[1]; $alpha = $this->assertNumber($num); $color[4] = $alpha; From 57292fba51af83b0e812bc1cb31c7816b3f61936 Mon Sep 17 00:00:00 2001 From: Tim Elsass Date: Sun, 20 May 2018 00:52:50 -0400 Subject: [PATCH 043/254] Fix #575 --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 1aad621b..746c6422 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2699,7 +2699,7 @@ public function compileValue($value) $b = round($b); if (count($value) === 5 && $value[4] !== 1) { // rgba - $a = new Node\Number($value[4]); + $a = new Node\Number($value[4],''); return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $a . ')'; } From 04482345fc0dd1b5305bcd5032ac4deb8a9ebbeb Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 20 May 2018 10:11:19 -0400 Subject: [PATCH 044/254] Update Compiler.php --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 746c6422..b727f178 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2699,7 +2699,7 @@ public function compileValue($value) $b = round($b); if (count($value) === 5 && $value[4] !== 1) { // rgba - $a = new Node\Number($value[4],''); + $a = new Node\Number($value[4], ''); return 'rgba(' . $r . ', ' . $g . ', ' . $b . ', ' . $a . ')'; } From 1a819c3b926c251891ede7c6bd12d91e403dc384 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 18:05:54 -0400 Subject: [PATCH 045/254] Fix phpcs warnings --- src/Compiler.php | 4 ++-- src/Parser.php | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index b727f178..bfda455f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -316,14 +316,14 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; - if($this->env->block instanceof Block) { + if ($this->env->block instanceof Block) { $out->sourceName = $this->env->block->sourceName; $out->sourceLine = $this->env->block->sourceLine; $out->sourceColumn = $this->env->block->sourceColumn; } else { $out->sourceName = null; $out->sourceLine = null; - $out->sourceColum = null; + $out->sourceColum = null; } return $out; } diff --git a/src/Parser.php b/src/Parser.php index 6fdea3e2..b8bfdc36 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1302,6 +1302,21 @@ protected function value(&$out) { $s = $this->seek(); + if ($this->literal('url(') && $this->match('data:image\/([a-z0-9+-]+);base64,', $m, false)) { + $len = strspn($this->buffer, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=', $this->count); + + $this->count += $len; + + if ($this->literal(')')) { + $content = substr($this->buffer, $s, $this->count - $s); + $out = [Type::T_KEYWORD, $content]; + + return true; + } + } + + $this->seek($s); + if ($this->literal('not', false) && $this->whitespace() && $this->value($inner)) { $out = [Type::T_UNARY, 'not', $inner, $this->inParens]; From e94a5c21addf8541a710d5ac6507532118ba3295 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 18:12:08 -0400 Subject: [PATCH 046/254] Refs #527 - parsing base64 --- src/Parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index b8bfdc36..de86f094 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1302,7 +1302,7 @@ protected function value(&$out) { $s = $this->seek(); - if ($this->literal('url(') && $this->match('data:image\/([a-z0-9+-]+);base64,', $m, false)) { + if ($this->literal('url(') && $this->match('data:([a-z]+)\/([a-z0-9.+-]+);base64,', $m, false)) { $len = strspn($this->buffer, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=', $this->count); $this->count += $len; From deaf1dede19347fed905931ad80b495b077e4220 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 21:33:11 -0400 Subject: [PATCH 047/254] Fix typo --- src/Compiler.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index bfda455f..72df991e 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -316,6 +316,7 @@ protected function makeOutputBlock($type, $selectors = null) $out->parent = $this->scope; $out->selectors = $selectors; $out->depth = $this->env->depth; + if ($this->env->block instanceof Block) { $out->sourceName = $this->env->block->sourceName; $out->sourceLine = $this->env->block->sourceLine; @@ -323,8 +324,9 @@ protected function makeOutputBlock($type, $selectors = null) } else { $out->sourceName = null; $out->sourceLine = null; - $out->sourceColum = null; + $out->sourceColumn = null; } + return $out; } From 585f6ae84de62ffecf69c23805f25d78d7e4b794 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 23 May 2018 22:18:53 -0400 Subject: [PATCH 048/254] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index 3b8dcd00..c8a56a5b 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.5'; + const VERSION = 'v0.7.6'; } From 0b94ebc2b0e61e797bfc0cb74acab5f3e95a09fd Mon Sep 17 00:00:00 2001 From: Stephan Date: Sun, 10 Jun 2018 01:09:50 +0200 Subject: [PATCH 049/254] Actually merge maps instead of just concatenating --- src/Compiler.php | 12 +++++++++++- tests/inputs/map.scss | 5 +++-- tests/outputs/map.css | 4 ++++ tests/outputs_numbered/map.css | 10 ++++++++-- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 72df991e..8551f001 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4985,7 +4985,17 @@ protected function libMapMerge($args) $map1 = $this->assertMap($args[0]); $map2 = $this->assertMap($args[1]); - return [Type::T_MAP, array_merge($map1[1], $map2[1]), array_merge($map1[2], $map2[2])]; + foreach ($map2[1] as $i2 => $key2) { + foreach ($map1[1] as $i1 => $key1) { + if ($this->compileStringContent($this->coerceString($key1)) === $this->compileStringContent($this->coerceString($key2))) { + $map1[2][$i1] = $map2[2][$i2]; + continue 2; + } + } + $map1[1][] = $map2[1][$i2]; + $map1[2][] = $map2[2][$i2]; + } + return $map1; } protected static $libKeywords = ['args']; diff --git a/tests/inputs/map.scss b/tests/inputs/map.scss index 0fb6be19..7f8adeba 100644 --- a/tests/inputs/map.scss +++ b/tests/inputs/map.scss @@ -25,9 +25,10 @@ div { bar: nth(nth($map, 1), 1); } -$color: ("black" : #000000); +$color: ("black" : #000000, "grey" : #777777); +$color2: ("grey" : #888888, "white" : #ffffff); -@each $color_name, $color_value in $color { +@each $color_name, $color_value in map_merge( $color, $color2 ) { .#{$color_name} { background-color: $color_value !important; } diff --git a/tests/outputs/map.css b/tests/outputs/map.css index 9c38094a..a8898915 100644 --- a/tests/outputs/map.css +++ b/tests/outputs/map.css @@ -12,6 +12,10 @@ div { bar: color; } .black { background-color: #000 !important; } + .grey { + background-color: #888 !important; } + .white { + background-color: #fff !important; } div { a: 1; diff --git a/tests/outputs_numbered/map.css b/tests/outputs_numbered/map.css index 16de52b9..23c144fb 100644 --- a/tests/outputs_numbered/map.css +++ b/tests/outputs_numbered/map.css @@ -11,10 +11,16 @@ div { div { foo: color black; bar: color; } -/* line 31, inputs/map.scss */ +/* line 32, inputs/map.scss */ .black { background-color: #000 !important; } -/* line 52, inputs/map.scss */ +/* line 32, inputs/map.scss */ +.grey { + background-color: #888 !important; } +/* line 32, inputs/map.scss */ +.white { + background-color: #fff !important; } +/* line 53, inputs/map.scss */ div { a: 1; b: 2; From 4963903c0a88db128026f1a78de205a7153d4b48 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Jun 2018 18:06:25 -0400 Subject: [PATCH 050/254] #569 attempt to fix call() with ellipsis --- src/Compiler.php | 38 +++++++++++++++++++++-------- tests/inputs/builtins.scss | 20 +++++++++++++++ tests/outputs/builtins.css | 12 +++++++++ tests/outputs_numbered/builtins.css | 12 +++++++++ 4 files changed, 72 insertions(+), 10 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index 72df991e..7d8db736 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3655,7 +3655,7 @@ protected function callNativeFunction($name, $args, &$returnValue) return false; } - list($sorted, $kwargs) = $this->sortArgs($prototype, $args); + @list($sorted, $kwargs) = $this->sortArgs($prototype, $args); if ($name !== 'if' && $name !== 'call') { foreach ($sorted as &$val) { @@ -3714,7 +3714,7 @@ protected function sortArgs($prototype, $args) $key = $key[1]; if (empty($key)) { - $posArgs[] = $value; + $posArgs[] = empty($arg[2]) ? $value : $arg; } else { $keyArgs[$key] = $value; } @@ -4266,20 +4266,38 @@ protected function libCall($args, $kwargs) { $name = $this->compileStringContent($this->coerceString($this->reduce(array_shift($args), true))); - $args = array_map( - function ($a) { - return [null, $a, false]; - }, - $args - ); + $posArgs = []; + + foreach ($args as $arg) { + if (empty($arg[0])) { + if ($arg[2] === true) { + $tmp = $this->reduce($arg[1]); + + if ($tmp[0] === Type::T_LIST) { + foreach ($tmp[2] as $item) { + $posArgs[] = [null, $item, false]; + } + } else { + $posArgs[] = [null, $tmp, true]; + } + + continue; + } + + $posArgs[] = [null, $this->reduce($arg), false]; + continue; + } + + $posArgs[] = [null, $arg, false]; + } if (count($kwargs)) { foreach ($kwargs as $key => $value) { - $args[] = [[Type::T_VARIABLE, $key], $value, false]; + $posArgs[] = [[Type::T_VARIABLE, $key], $value, false]; } } - return $this->reduce([Type::T_FUNCTION_CALL, $name, $args]); + return $this->reduce([Type::T_FUNCTION_CALL, $name, $posArgs]); } protected static $libIf = ['condition', 'if-true', 'if-false']; diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index a80db5b1..00cd3c63 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -216,3 +216,23 @@ $type: text; div.unquote-test { a: unquote('[type=\'#{$type}\']'); } + +.does-compile-1 { + color: call(lighten, #000, 40%); +} + +.does-compile-2 { + $color: #000; + $percent: 40%; + color: call(lighten, $color, $percent); +} + +.does-compile-3 { + $args: (#000, 40%); + color: call(lighten, nth($args, 1), nth($args, 2)); +} + +.does-compile-4 { + $args: (#000, 40%); + color: call(lighten, $args...); +} diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index 50e2a82e..db43727d 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -159,3 +159,15 @@ div.call-tests { div.unquote-test { a: [type='text']; } + +.does-compile-1 { + color: #666; } + +.does-compile-2 { + color: #666; } + +.does-compile-3 { + color: #666; } + +.does-compile-4 { + color: #666; } diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index 722c6af4..e84d727c 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -160,3 +160,15 @@ div.call-tests { /* line 216, inputs/builtins.scss */ div.unquote-test { a: [type='text']; } +/* line 220, inputs/builtins.scss */ +.does-compile-1 { + color: #666; } +/* line 224, inputs/builtins.scss */ +.does-compile-2 { + color: #666; } +/* line 230, inputs/builtins.scss */ +.does-compile-3 { + color: #666; } +/* line 235, inputs/builtins.scss */ +.does-compile-4 { + color: #666; } From 3d3fd508102242ab5744b91e51ff78e262db9467 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 19 Jun 2018 23:10:38 -0400 Subject: [PATCH 051/254] refs #548 - treat 0 as a special unitless number --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 7d8db736..62d125d8 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -4856,7 +4856,7 @@ protected function getNormalizedNumbers($args) if (null === $unit) { $unit = $number[2]; $originalUnit = $item->unitStr(); - } elseif ($unit !== $number[2]) { + } elseif ($number[1] && $unit !== $number[2]) { $this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item->unitStr()); break; } From 1d656f8c02a3a69404bba6b28ec4e06edddf0f49 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sat, 21 Jul 2018 21:22:08 -0400 Subject: [PATCH 052/254] refs #578 peephole optimization --- src/Compiler.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 414e4401..637f1c1c 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -5004,15 +5004,19 @@ protected function libMapMerge($args) $map2 = $this->assertMap($args[1]); foreach ($map2[1] as $i2 => $key2) { + $key = $this->compileStringContent($this->coerceString($key2)); + foreach ($map1[1] as $i1 => $key1) { - if ($this->compileStringContent($this->coerceString($key1)) === $this->compileStringContent($this->coerceString($key2))) { + if ($key === $this->compileStringContent($this->coerceString($key1))) { $map1[2][$i1] = $map2[2][$i2]; continue 2; } } + $map1[1][] = $map2[1][$i2]; $map1[2][] = $map2[2][$i2]; } + return $map1; } From b72d613f682b714f498412483aff353cde977c00 Mon Sep 17 00:00:00 2001 From: Jiong Ye Date: Fri, 17 Aug 2018 16:24:08 -0400 Subject: [PATCH 053/254] leafo/scssphp#581 generate inline sourcemap in commandline --- bin/pscss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/pscss b/bin/pscss index be1cb51c..ce80e6a9 100755 --- a/bin/pscss +++ b/bin/pscss @@ -201,7 +201,7 @@ if ($style) { } if ($sourceMap) { - $scss->setSourceMap(Compiler::SOURCE_MAP_FILE); + $scss->setSourceMap(Compiler::SOURCE_MAP_INLINE); } if ($encoding) { From a7be38d3dabb6d3c15e28073541163428ad819ab Mon Sep 17 00:00:00 2001 From: Bastian Rihm Date: Thu, 30 Aug 2018 14:43:47 +0200 Subject: [PATCH 054/254] #515 Fix backslash escape --- src/Parser.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Parser.php b/src/Parser.php index de86f094..748d38ae 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -1737,6 +1737,8 @@ protected function string(&$out) $content[] = $m[2] . '"'; } elseif ($this->literal("'", false)) { $content[] = $m[2] . "'"; + } elseif ($this->literal("\\", false)) { + $content[] = $m[2] . "\\"; } else { $content[] = $m[2]; } @@ -1753,7 +1755,9 @@ protected function string(&$out) $delim = '"'; foreach ($content as &$string) { - if ($string === "\\'") { + if ($string === "\\\\") { + $string = "\\"; + } elseif ($string === "\\'") { $string = "'"; } elseif ($string === '\\"') { $string = '"'; From 0b589890bfbdd2e14d272f78d9d49038c051f12c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 24 Jul 2018 10:47:05 -0400 Subject: [PATCH 055/254] Bump version --- src/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Version.php b/src/Version.php index c8a56a5b..ec0fee6f 100644 --- a/src/Version.php +++ b/src/Version.php @@ -18,5 +18,5 @@ */ class Version { - const VERSION = 'v0.7.6'; + const VERSION = 'v0.7.8'; } From 2a88d3e9ba903e1568cda7cd33add91d093a639c Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 26 Sep 2018 16:46:29 -0400 Subject: [PATCH 056/254] Update unit tests (fixes #515) --- tests/inputs/builtins.scss | 1 + tests/outputs/builtins.css | 1 + tests/outputs_numbered/builtins.css | 27 ++++++++++++++------------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/tests/inputs/builtins.scss b/tests/inputs/builtins.scss index 00cd3c63..c75a854e 100644 --- a/tests/inputs/builtins.scss +++ b/tests/inputs/builtins.scss @@ -78,6 +78,7 @@ color: quote("I do?"); color: str_index(abc, b); color: str_insert(string, insert, 2); + color: str_insert(string, "\\", 2); color: str_length(string); color: str_slice(string, 2, 4); color: str-slice(string, 2, 0); diff --git a/tests/outputs/builtins.css b/tests/outputs/builtins.css index db43727d..94f29b4e 100644 --- a/tests/outputs/builtins.css +++ b/tests/outputs/builtins.css @@ -53,6 +53,7 @@ color: "I do?"; color: 2; color: sinserttring; + color: s\\tring; color: 6; color: tri; color: trin; diff --git a/tests/outputs_numbered/builtins.css b/tests/outputs_numbered/builtins.css index e84d727c..559b6a30 100644 --- a/tests/outputs_numbered/builtins.css +++ b/tests/outputs_numbered/builtins.css @@ -54,6 +54,7 @@ color: "I do?"; color: 2; color: sinserttring; + color: s\\tring; color: 6; color: tri; color: trin; @@ -74,7 +75,7 @@ color: st; color: string; color: strin; } -/* line 105, inputs/builtins.scss */ +/* line 106, inputs/builtins.scss */ #number { color: 250%; color: 50%; @@ -86,7 +87,7 @@ width: 200%; bottom: 10px; padding: 3em 1in 96px 72pt; } -/* line 119, inputs/builtins.scss */ +/* line 120, inputs/builtins.scss */ #list { len: 3; len: 1; @@ -108,7 +109,7 @@ cool: great job one two three; zip: 1px solid, 2px dashed; zip: 1px solid red, 2px dashed green; } -/* line 155, inputs/builtins.scss */ +/* line 156, inputs/builtins.scss */ #introspection { t: number; t: string; @@ -128,47 +129,47 @@ c: true; c: false; c: true; } -/* line 179, inputs/builtins.scss */ +/* line 180, inputs/builtins.scss */ #if { color: yes; color: no; color: yes; color: yes; } -/* line 186, inputs/builtins.scss */ +/* line 187, inputs/builtins.scss */ .transparent { r: 0; g: 0; b: 0; a: 0; } -/* line 193, inputs/builtins.scss */ +/* line 194, inputs/builtins.scss */ .alpha { a: 1; a: 1; a: 1; a: 0.5; a: alpha(currentColor); } -/* line 202, inputs/builtins.scss */ +/* line 203, inputs/builtins.scss */ #exists { a: true; b: true; c: false; } -/* line 209, inputs/builtins.scss */ +/* line 210, inputs/builtins.scss */ div.call-tests { a: #0a64ff; b: #0058ef; c: b; } -/* line 216, inputs/builtins.scss */ +/* line 217, inputs/builtins.scss */ div.unquote-test { a: [type='text']; } -/* line 220, inputs/builtins.scss */ +/* line 221, inputs/builtins.scss */ .does-compile-1 { color: #666; } -/* line 224, inputs/builtins.scss */ +/* line 225, inputs/builtins.scss */ .does-compile-2 { color: #666; } -/* line 230, inputs/builtins.scss */ +/* line 231, inputs/builtins.scss */ .does-compile-3 { color: #666; } -/* line 235, inputs/builtins.scss */ +/* line 236, inputs/builtins.scss */ .does-compile-4 { color: #666; } From f893748cd4482051bc503064c6a4396b10280957 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Wed, 26 Sep 2018 17:15:59 -0400 Subject: [PATCH 057/254] Add Base64VLQ encoder test (refs #600) --- tests/Base64VLQTest.php | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tests/Base64VLQTest.php diff --git a/tests/Base64VLQTest.php b/tests/Base64VLQTest.php new file mode 100644 index 00000000..21b373b8 --- /dev/null +++ b/tests/Base64VLQTest.php @@ -0,0 +1,54 @@ + + */ +class Base64VLQTest extends \PHPUnit_Framework_TestCase +{ + /** + * Test encode + * + * param string $expected + * param string $value + * + * @dataProvider getEncode + */ + public function testEncode($expected, $value) + { + $encoder = new Base64VLQEncoder; + + $this->assertEquals($expected, $encoder->encode($value)); + } + + /** + * Data provider for testEncode + * + * @return array + */ + public static function getEncode() + { + return [ + ['A', 0], + ['C', 1], + ['D', -1], + ['2H', 123], + ['qxmvrH', 123456789], + ['+/////D', 2147483647], // 2^31-1 + ]; + } +} From 0fcbdf599f9f8289d033f327532b6a2230b1d77f Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 30 Sep 2018 20:57:13 -0400 Subject: [PATCH 058/254] Fixes #600 - change Base64 VLQ encoder/decoder implementtion --- scss.inc.php | 3 +- src/SourceMap/Base64.php | 184 +++++++++++++++++++++++++++ src/SourceMap/Base64VLQ.php | 137 ++++++++++++++++++++ src/SourceMap/SourceMapGenerator.php | 4 +- tests/Base64VLQTest.php | 4 +- 5 files changed, 327 insertions(+), 5 deletions(-) create mode 100644 src/SourceMap/Base64.php create mode 100644 src/SourceMap/Base64VLQ.php diff --git a/scss.inc.php b/scss.inc.php index 13c84bf5..d75ecb55 100644 --- a/scss.inc.php +++ b/scss.inc.php @@ -24,7 +24,8 @@ include_once __DIR__ . '/src/Node.php'; include_once __DIR__ . '/src/Node/Number.php'; include_once __DIR__ . '/src/Parser.php'; - include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php'; + include_once __DIR__ . '/src/SourceMap/Base64.php'; + include_once __DIR__ . '/src/SourceMap/Base64VLQ.php'; include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php'; include_once __DIR__ . '/src/Type.php'; include_once __DIR__ . '/src/Util.php'; diff --git a/src/SourceMap/Base64.php b/src/SourceMap/Base64.php new file mode 100644 index 00000000..9d17e6f0 --- /dev/null +++ b/src/SourceMap/Base64.php @@ -0,0 +1,184 @@ + + */ +class Base64 +{ + /** + * @var array + */ + private static $encodingMap = array ( + 0 => 'A', + 1 => 'B', + 2 => 'C', + 3 => 'D', + 4 => 'E', + 5 => 'F', + 6 => 'G', + 7 => 'H', + 8 => 'I', + 9 => 'J', + 10 => 'K', + 11 => 'L', + 12 => 'M', + 13 => 'N', + 14 => 'O', + 15 => 'P', + 16 => 'Q', + 17 => 'R', + 18 => 'S', + 19 => 'T', + 20 => 'U', + 21 => 'V', + 22 => 'W', + 23 => 'X', + 24 => 'Y', + 25 => 'Z', + 26 => 'a', + 27 => 'b', + 28 => 'c', + 29 => 'd', + 30 => 'e', + 31 => 'f', + 32 => 'g', + 33 => 'h', + 34 => 'i', + 35 => 'j', + 36 => 'k', + 37 => 'l', + 38 => 'm', + 39 => 'n', + 40 => 'o', + 41 => 'p', + 42 => 'q', + 43 => 'r', + 44 => 's', + 45 => 't', + 46 => 'u', + 47 => 'v', + 48 => 'w', + 49 => 'x', + 50 => 'y', + 51 => 'z', + 52 => '0', + 53 => '1', + 54 => '2', + 55 => '3', + 56 => '4', + 57 => '5', + 58 => '6', + 59 => '7', + 60 => '8', + 61 => '9', + 62 => '+', + 63 => '/', + ); + + /** + * @var array + */ + private static $decodingMap = array( + 'A' => 0, + 'B' => 1, + 'C' => 2, + 'D' => 3, + 'E' => 4, + 'F' => 5, + 'G' => 6, + 'H' => 7, + 'I' => 8, + 'J' => 9, + 'K' => 10, + 'L' => 11, + 'M' => 12, + 'N' => 13, + 'O' => 14, + 'P' => 15, + 'Q' => 16, + 'R' => 17, + 'S' => 18, + 'T' => 19, + 'U' => 20, + 'V' => 21, + 'W' => 22, + 'X' => 23, + 'Y' => 24, + 'Z' => 25, + 'a' => 26, + 'b' => 27, + 'c' => 28, + 'd' => 29, + 'e' => 30, + 'f' => 31, + 'g' => 32, + 'h' => 33, + 'i' => 34, + 'j' => 35, + 'k' => 36, + 'l' => 37, + 'm' => 38, + 'n' => 39, + 'o' => 40, + 'p' => 41, + 'q' => 42, + 'r' => 43, + 's' => 44, + 't' => 45, + 'u' => 46, + 'v' => 47, + 'w' => 48, + 'x' => 49, + 'y' => 50, + 'z' => 51, + 0 => 52, + 1 => 53, + 2 => 54, + 3 => 55, + 4 => 56, + 5 => 57, + 6 => 58, + 7 => 59, + 8 => 60, + 9 => 61, + '+' => 62, + '/' => 63, + ); + + /** + * Convert to base64 + * + * @param integer $value + * + * @return string + */ + public static function encode($value) + { + return self::$encodingMap[$value]; + } + + /** + * Convert from base64 + * + * @param string $value + * + * @return integer + */ + public static function decode($value) + { + return self::$decodingMap[$value]; + } +} diff --git a/src/SourceMap/Base64VLQ.php b/src/SourceMap/Base64VLQ.php new file mode 100644 index 00000000..6de3b5ee --- /dev/null +++ b/src/SourceMap/Base64VLQ.php @@ -0,0 +1,137 @@ + + * @author Anthon Pang + */ +class Base64VLQ +{ + // A Base64 VLQ digit can represent 5 bits, so it is base-32. + const VLQ_BASE_SHIFT = 5; + + // A mask of bits for a VLQ digit (11111), 31 decimal. + const VLQ_BASE_MASK = 31; + + // The continuation bit is the 6th bit. + const VLQ_CONTINUATION_BIT = 32; + + /** + * Returns the VLQ encoded value. + * + * @param integer $value + * + * @return string + */ + public static function encode($value) + { + $encoded = ''; + $vlq = self::toVLQSigned($value); + + do { + $digit = $vlq & self::VLQ_BASE_MASK; + $vlq >>= self::VLQ_BASE_SHIFT; + + if ($vlq > 0) { + $digit |= self::VLQ_CONTINUATION_BIT; + } + + $encoded .= Base64::encode($digit); + } while ($vlq > 0); + + return $encoded; + } + + /** + * Decodes VLQValue. + * + * @param string $str + * @param integer $index + * + * @return integer + */ + public static function decode($str, &$index) + { + $result = 0; + $shift = 0; + + do { + $c = $str[$index++]; + $digit = Base64::decode($c); + $continuation = ($digit & self::VLQ_CONTINUATION_BIT) != 0; + $digit &= self::VLQ_BASE_MASK; + $result = $result + ($digit << $shift); + $shift = $shift + self::VLQ_BASE_SHIFT; + } while ($continuation); + + return self::fromVLQSigned($result); + } + + /** + * Converts from a two-complement value to a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * + * @param integer $value + * + * @return integer + */ + private static function toVLQSigned($value) + { + if ($value < 0) { + return ((-$value) << 1) + 1; + } + + return ($value << 1) + 0; + } + + /** + * Converts to a two-complement value from a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * + * @param integer $value + * + * @return integer + */ + private static function fromVLQSigned($value) + { + $negate = ($value & 1) === 1; + $value = $value >> 1; + + return $negate ? -$value : $value; + } +} diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 70b47cdc..6e969cae 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -61,7 +61,7 @@ class SourceMapGenerator /** * The base64 VLQ encoder * - * @var \Leafo\ScssPhp\SourceMap\Base64VLQEncoder + * @var \Leafo\ScssPhp\SourceMap\Base64VLQ */ protected $encoder; @@ -95,7 +95,7 @@ class SourceMapGenerator public function __construct(array $options = []) { $this->options = array_merge($this->defaultOptions, $options); - $this->encoder = new Base64VLQEncoder(); + $this->encoder = new Base64VLQ(); } /** diff --git a/tests/Base64VLQTest.php b/tests/Base64VLQTest.php index 21b373b8..a3b638de 100644 --- a/tests/Base64VLQTest.php +++ b/tests/Base64VLQTest.php @@ -11,7 +11,7 @@ namespace Leafo\ScssPhp\Tests; -use Leafo\ScssPhp\SourceMap\Base64VLQEncoder; +use Leafo\ScssPhp\SourceMap\Base64VLQ; /** * Base64VLQ encoder test @@ -30,7 +30,7 @@ class Base64VLQTest extends \PHPUnit_Framework_TestCase */ public function testEncode($expected, $value) { - $encoder = new Base64VLQEncoder; + $encoder = new Base64VLQ; $this->assertEquals($expected, $encoder->encode($value)); } From c1ca4095952a431d4847862e048715bc6fa331fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Nguyen?= Date: Thu, 11 Oct 2018 13:27:55 +0200 Subject: [PATCH 059/254] fix doc for addImportPath, should also accept callable as input --- src/Compiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler.php b/src/Compiler.php index 637f1c1c..ed80d8d3 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3308,7 +3308,7 @@ public function getParsedFiles() * * @api * - * @param string $path + * @param string|callable $path */ public function addImportPath($path) { From b931793663ac56d57d5f87f9f6f3f50cf6b73aa0 Mon Sep 17 00:00:00 2001 From: Geoff Willings Date: Tue, 23 Oct 2018 23:12:14 +0100 Subject: [PATCH 060/254] Bug fix error thrown from strpos if needle (basePath) is empty and ignore PhpStorm .idea IDE directory --- .gitignore | 1 + src/SourceMap/SourceMapGenerator.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index be6e5ecf..999984a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea .sass-cache scss_cache composer.lock diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 6e969cae..39bfac1c 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -303,7 +303,7 @@ protected function normalizeFilename($filename) $basePath = $this->options['sourceMapBasepath']; // "Trim" the 'sourceMapBasepath' from the output filename. - if (strpos($filename, $basePath) === 0) { + if (strlen($basePath) && strpos($filename, $basePath) === 0) { $filename = substr($filename, strlen($basePath)); } From a0e5590e65e33ad6c10e4c98e4aa7c8563779ee8 Mon Sep 17 00:00:00 2001 From: Geoff Willings Date: Wed, 24 Oct 2018 00:41:31 +0100 Subject: [PATCH 061/254] Fix bug where rooted SCSS URIs aren't normalised properly with baseUri provided --- src/Compiler.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index ed80d8d3..ee4efc96 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -3481,9 +3481,12 @@ public function findImport($url) if (is_string($dir)) { // check urls for normal import paths foreach ($urls as $full) { - $full = $dir - . (! empty($dir) && substr($dir, -1) !== '/' ? '/' : '') - . $full; + $separator = ( + !empty($dir) && + substr($dir, -1) !== '/' && + substr($full, 0, 1) !== '/' + ) ? '/' : ''; + $full = $dir . $separator . $full; if ($this->fileExists($file = $full . '.scss') || ($hasExtension && $this->fileExists($file = $full)) From 51041822ae0e8b646e7d86de6c6f81100345f939 Mon Sep 17 00:00:00 2001 From: Geoff Willings Date: Wed, 24 Oct 2018 00:42:04 +0100 Subject: [PATCH 062/254] Json preference to not escape slashes (opinionated) --- src/SourceMap/SourceMapGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SourceMap/SourceMapGenerator.php b/src/SourceMap/SourceMapGenerator.php index 39bfac1c..7bb005c4 100644 --- a/src/SourceMap/SourceMapGenerator.php +++ b/src/SourceMap/SourceMapGenerator.php @@ -202,7 +202,7 @@ public function generateJson() unset($sourceMap['sourceRoot']); } - return json_encode($sourceMap); + return json_encode($sourceMap, JSON_UNESCAPED_SLASHES); } /** From 94261339c3b11fdec1d230c57a54084c34a8c824 Mon Sep 17 00:00:00 2001 From: Vince Date: Wed, 13 Mar 2019 14:54:47 -0700 Subject: [PATCH 063/254] Fix for "continue" causing a warning in PHP 7.3 "continue statements targeting switch control flow structures will now generate a warning. In PHP such continue statements are equivalent to break, while they behave as continue 2 in other languages." http://php.net/manual/en/migration73.incompatible.php "In PHP the switch statement is considered a looping structure for the purposes of continue. continue behaves like break (when no arguments are passed). If a switch is inside a loop, continue 2 will continue with the next iteration of the outer loop." http://php.net/manual/en/control-structures.continue.php Signed-off-by: Vince --- tests/ScssTest.php | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/ScssTest.php b/tests/ScssTest.php index 96f0d76e..9c412fe3 100644 --- a/tests/ScssTest.php +++ b/tests/ScssTest.php @@ -75,7 +75,7 @@ public function provideTests() if (preg_match('/^\s*def test_([a-z_]+)/', $line, $matches)) { $state = 1; // enter function $name = $matches[1]; - continue; + continue 2; } break; @@ -83,7 +83,7 @@ public function provideTests() case 1: // inside function if ($line === '' || $line[0] === '#') { - continue; + continue 2; } if (preg_match('/= <<([A-Z_]+)\s*$/', $line, $matches) @@ -95,14 +95,14 @@ public function provideTests() ; } - continue; + continue 2; } if (preg_match('/^\s*assert_equal\(< e' ) { - continue; + continue 2; } if (preg_match('/^\s*end\s*$/', $line)) { @@ -168,7 +168,7 @@ public function provideTests() $scss = array(); $css = array(); $style = null; - continue; + continue 2; } $skipped[] = $line; @@ -179,9 +179,9 @@ public function provideTests() // get css if (preg_match('/^CSS\s*$/', $line)) { $state = 3; // get scss - continue; + continue 2; } - + $css[] = $lines[$i]; break; @@ -190,9 +190,9 @@ public function provideTests() // get scss if (preg_match('/^SCSS\s*$/', $line)) { $state = 1; // end of parameter list - continue; + continue 2; } - + $scss[] = $lines[$i]; break; @@ -201,12 +201,12 @@ public function provideTests() // inside block if (preg_match('/^\s*end\s*$/', $line)) { $state = 1; // end block - continue; + continue 2; } if (preg_match('/^\s*assert_equal < Date: Sun, 17 Mar 2019 23:23:12 +0200 Subject: [PATCH 064/254] Avoid infinitely duplicating parts when extending selector --- src/Compiler.php | 7 ++++++- tests/inputs/extending_compound_selector.scss | 19 +++++++++++++++++++ tests/outputs/extending_compound_selector.css | 4 ++++ .../extending_compound_selector.css | 11 +++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/inputs/extending_compound_selector.scss create mode 100644 tests/outputs/extending_compound_selector.css create mode 100644 tests/outputs_numbered/extending_compound_selector.css diff --git a/src/Compiler.php b/src/Compiler.php index ed80d8d3..c73f5b02 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -463,7 +463,12 @@ protected function matchExtends($selector, &$out, $from = 0, $initial = true) $tempReplacement = $k > 0 ? array_slice($new, $k) : $new; for ($l = count($tempReplacement) - 1; $l >= 0; $l--) { - $slice = $tempReplacement[$l]; + $slice = []; + foreach ($tempReplacement[$l] as $chunk) { + if (!in_array($chunk, $slice)) { + $slice[] = $chunk; + } + } array_unshift($replacement, $slice); if (! $this->isImmediateRelationshipCombinator(end($slice))) { diff --git a/tests/inputs/extending_compound_selector.scss b/tests/inputs/extending_compound_selector.scss new file mode 100644 index 00000000..669591fd --- /dev/null +++ b/tests/inputs/extending_compound_selector.scss @@ -0,0 +1,19 @@ +.foo { + &:after { + color: red; + } + &:hover:after { + color: blue; + } +} + +.bar { + @extend .foo; + &:before { + @extend .bar:after; + } +} + +.baz.bar:before { + @extend .bar:before; +} diff --git a/tests/outputs/extending_compound_selector.css b/tests/outputs/extending_compound_selector.css new file mode 100644 index 00000000..7e8cb599 --- /dev/null +++ b/tests/outputs/extending_compound_selector.css @@ -0,0 +1,4 @@ +.foo:after, .bar:after, .bar:before, .baz.bar:before { + color: red; } + .foo:hover:after, .bar:hover:after, .bar:before:hover, .baz.bar:before:hover { + color: blue; } diff --git a/tests/outputs_numbered/extending_compound_selector.css b/tests/outputs_numbered/extending_compound_selector.css new file mode 100644 index 00000000..50685dcf --- /dev/null +++ b/tests/outputs_numbered/extending_compound_selector.css @@ -0,0 +1,11 @@ +/* line 1, inputs/extending_compound_selector.scss */ +/* line 2, inputs/extending_compound_selector.scss */ + .foo:after, .bar:after, .bar:before, .baz.bar:before { + color: red; } +/* line 5, inputs/extending_compound_selector.scss */ +.foo:hover:after, .bar:hover:after, .bar:before:hover, .baz.bar:before:hover { + color: blue; } +/* line 10, inputs/extending_compound_selector.scss */ +/* line 12, inputs/extending_compound_selector.scss */ + +/* line 17, inputs/extending_compound_selector.scss */ From 405ebd336c86b397841eb3d17af1aa9200ca2b41 Mon Sep 17 00:00:00 2001 From: Vjacheslav Trushkin Date: Sun, 17 Mar 2019 23:56:10 +0200 Subject: [PATCH 065/254] Change private properties to protected in compiler --- src/Compiler.php | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/Compiler.php b/src/Compiler.php index ed80d8d3..f9934aa2 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -145,18 +145,18 @@ class Compiler protected $charsetSeen; protected $sourceNames; - private $indentLevel; - private $commentsSeen; - private $extends; - private $extendsMap; - private $parsedFiles; - private $parser; - private $sourceIndex; - private $sourceLine; - private $sourceColumn; - private $stderr; - private $shouldEvaluate; - private $ignoreErrors; + protected $indentLevel; + protected $commentsSeen; + protected $extends; + protected $extendsMap; + protected $parsedFiles; + protected $parser; + protected $sourceIndex; + protected $sourceLine; + protected $sourceColumn; + protected $stderr; + protected $shouldEvaluate; + protected $ignoreErrors; /** * Constructor @@ -817,7 +817,7 @@ protected function compileAtRoot(Block $block) * * @return array */ - private function spliceTree($envs, Block $block, $without) + protected function spliceTree($envs, Block $block, $without) { $newBlock = null; @@ -894,7 +894,7 @@ private function spliceTree($envs, Block $block, $without) * * @return integer */ - private function compileWith($with) + protected function compileWith($with) { static $mapping = [ 'rule' => self::WITH_RULE, @@ -945,7 +945,7 @@ private function compileWith($with) * * @return \Leafo\ScssPhp\Compiler\Environment */ - private function filterWithout($envs, $without) + protected function filterWithout($envs, $without) { $filtered = []; @@ -968,7 +968,7 @@ private function filterWithout($envs, $without) * * @return boolean */ - private function isWithout($without, Block $block) + protected function isWithout($without, Block $block) { if ((($without & static::WITH_RULE) && isset($block->selectors)) || (($without & static::WITH_MEDIA) && @@ -2215,7 +2215,7 @@ protected function reduce($value, $inExp = false) * * @return array|null */ - private function fncall($name, $argValues) + protected function fncall($name, $argValues) { // SCSS @function if ($this->callScssFunction($name, $argValues, $returnValue)) { @@ -3016,7 +3016,7 @@ protected function multiplyMedia(Environment $env = null, $childQueries = null) * * @return array */ - private function compactEnv(Environment $env) + protected function compactEnv(Environment $env) { for ($envs = []; $env; $env = $env->parent) { $envs[] = $env; @@ -3032,7 +3032,7 @@ private function compactEnv(Environment $env) * * @return \Leafo\ScssPhp\Compiler\Environment */ - private function extractEnv($envs) + protected function extractEnv($envs) { for ($env = null; $e = array_pop($envs);) { $e->parent = $env; @@ -3864,7 +3864,7 @@ protected function applyArguments($argDef, $argValues) * * @return array|\Leafo\ScssPhp\Node\Number */ - private function coerceValue($value) + protected function coerceValue($value) { if (is_array($value) || $value instanceof \ArrayAccess) { return $value; @@ -4203,7 +4203,7 @@ public function toHSL($red, $green, $blue) * * @return float */ - private function hueToRGB($m1, $m2, $h) + protected function hueToRGB($m1, $m2, $h) { if ($h < 0) { $h += 1; From 20d88db8a832429c2a4cac035c32a63b968cb6f4 Mon Sep 17 00:00:00 2001 From: josh Date: Sun, 27 Dec 2015 13:30:27 -0700 Subject: [PATCH 066/254] add matchChar() --- src/Parser.php | 168 ++++++++++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 70 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 748d38ae..5114cdab 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -286,7 +286,7 @@ protected function parseChunk() if ($this->literal('@at-root') && ($this->selectors($selector) || true) && ($this->map($with) || true) && - $this->literal('{') + $this->matchChar('{') ) { $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s); $atRoot->selector = $selector; @@ -297,7 +297,7 @@ protected function parseChunk() $this->seek($s); - if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->literal('{')) { + if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->matchChar('{')) { $media = $this->pushSpecialBlock(Type::T_MEDIA, $s); $media->queryList = $mediaQueryList[2]; @@ -309,7 +309,7 @@ protected function parseChunk() if ($this->literal('@mixin') && $this->keyword($mixinName) && ($this->argumentDef($args) || true) && - $this->literal('{') + $this->matchChar('{') ) { $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s); $mixin->name = $mixinName; @@ -322,11 +322,11 @@ protected function parseChunk() if ($this->literal('@include') && $this->keyword($mixinName) && - ($this->literal('(') && + ($this->matchChar('(') && ($this->argValues($argValues) || true) && - $this->literal(')') || true) && + $this->matchChar(')') || true) && ($this->end() || - $this->literal('{') && $hasBlock = true) + $this->matchChar('{') && $hasBlock = true) ) { $child = [Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null]; @@ -391,7 +391,7 @@ protected function parseChunk() if ($this->literal('@function') && $this->keyword($fnName) && $this->argumentDef($args) && - $this->literal('{') + $this->matchChar('{') ) { $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s); $func->name = $fnName; @@ -431,7 +431,7 @@ protected function parseChunk() $this->genericList($varNames, 'variable', ',', false) && $this->literal('in') && $this->valueList($list) && - $this->literal('{') + $this->matchChar('{') ) { $each = $this->pushSpecialBlock(Type::T_EACH, $s); @@ -448,7 +448,7 @@ protected function parseChunk() if ($this->literal('@while') && $this->expression($cond) && - $this->literal('{') + $this->matchChar('{') ) { $while = $this->pushSpecialBlock(Type::T_WHILE, $s); $while->cond = $cond; @@ -465,7 +465,7 @@ protected function parseChunk() ($this->literal('through') || ($forUntil = true && $this->literal('to'))) && $this->expression($end) && - $this->literal('{') + $this->matchChar('{') ) { $for = $this->pushSpecialBlock(Type::T_FOR, $s); $for->var = $varName[1]; @@ -478,7 +478,7 @@ protected function parseChunk() $this->seek($s); - if ($this->literal('@if') && $this->valueList($cond) && $this->literal('{')) { + if ($this->literal('@if') && $this->valueList($cond) && $this->matchChar('{')) { $if = $this->pushSpecialBlock(Type::T_IF, $s); $if->cond = $cond; $if->cases = []; @@ -535,9 +535,9 @@ protected function parseChunk() list(, $if) = $last; if ($this->literal('@else')) { - if ($this->literal('{')) { + if ($this->matchChar('{')) { $else = $this->pushSpecialBlock(Type::T_ELSE, $s); - } elseif ($this->literal('if') && $this->valueList($cond) && $this->literal('{')) { + } elseif ($this->literal('if') && $this->valueList($cond) && $this->matchChar('{')) { $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s); $else->cond = $cond; } @@ -576,10 +576,10 @@ protected function parseChunk() $this->seek($s); // doesn't match built in directive, do generic one - if ($this->literal('@', false) && + if ($this->matchChar('@', false) && $this->keyword($dirName) && ($this->variable($dirValue) || $this->openString('{', $dirValue) || true) && - $this->literal('{') + $this->matchChar('{') ) { if ($dirName === 'media') { $directive = $this->pushSpecialBlock(Type::T_MEDIA, $s); @@ -617,7 +617,7 @@ protected function parseChunk() // variable assigns if ($this->variable($name) && - $this->literal(':') && + $this->matchChar(':') && $this->valueList($value) && $this->end() ) { @@ -636,7 +636,7 @@ protected function parseChunk() } // opening css block - if ($this->selectors($selectors) && $this->literal('{')) { + if ($this->selectors($selectors) && $this->matchChar('{')) { $this->pushBlock($selectors, $s); return true; @@ -645,7 +645,7 @@ protected function parseChunk() $this->seek($s); // property assign, or nested assign - if ($this->propertyName($name) && $this->literal(':')) { + if ($this->propertyName($name) && $this->matchChar(':')) { $foundSomething = false; if ($this->valueList($value)) { @@ -653,7 +653,7 @@ protected function parseChunk() $foundSomething = true; } - if ($this->literal('{')) { + if ($this->matchChar('{')) { $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s); $propBlock->prefix = $name; $foundSomething = true; @@ -669,7 +669,7 @@ protected function parseChunk() $this->seek($s); // closing a block - if ($this->literal('}')) { + if ($this->matchChar('}')) { $block = $this->popBlock(); if (isset($block->type) && $block->type === Type::T_INCLUDE) { @@ -686,7 +686,7 @@ protected function parseChunk() } // extra stuff - if ($this->literal(';') || + if ($this->matchChar(';') || $this->literal('')) { + if ($this->literal('-->', 3)) { return true; } @@ -687,7 +687,7 @@ protected function parseChunk() // extra stuff if ($this->matchChar(';') || - $this->literal('