From c8842113a774c9a602ad4b7c77b9aaa11234da7b Mon Sep 17 00:00:00 2001 From: Bas Brands Date: Fri, 16 Mar 2018 10:36:40 +0100 Subject: [PATCH] MDL-61529 core: update lib scssphp to version 0.7.5 --- lib/scssphp/Base/Range.php | 2 +- lib/scssphp/Block.php | 7 +- lib/scssphp/Colors.php | 2 +- lib/scssphp/Compiler.php | 151 ++++-- lib/scssphp/Compiler/Environment.php | 2 +- lib/scssphp/Exception/CompilerException.php | 2 +- lib/scssphp/Exception/ParserException.php | 2 +- lib/scssphp/Exception/RangeException.php | 2 +- lib/scssphp/Exception/ServerException.php | 2 +- lib/scssphp/Formatter.php | 78 +++- lib/scssphp/Formatter/Compact.php | 2 +- lib/scssphp/Formatter/Compressed.php | 6 +- lib/scssphp/Formatter/Crunched.php | 6 +- lib/scssphp/Formatter/Debug.php | 18 +- lib/scssphp/Formatter/Expanded.php | 6 +- lib/scssphp/Formatter/Nested.php | 21 +- lib/scssphp/Formatter/OutputBlock.php | 17 +- lib/scssphp/Node.php | 2 +- lib/scssphp/Node/Number.php | 7 +- lib/scssphp/Parser.php | 11 +- lib/scssphp/Server.php | 463 ------------------- lib/scssphp/SourceMap/Base64VLQEncoder.php | 217 +++++++++ lib/scssphp/SourceMap/SourceMapGenerator.php | 337 ++++++++++++++ lib/scssphp/Type.php | 2 +- lib/scssphp/Util.php | 26 +- lib/scssphp/Version.php | 4 +- lib/scssphp/moodle_readme.txt | 1 - lib/thirdpartylibs.xml | 2 +- 28 files changed, 834 insertions(+), 564 deletions(-) delete mode 100644 lib/scssphp/Server.php create mode 100644 lib/scssphp/SourceMap/Base64VLQEncoder.php create mode 100644 lib/scssphp/SourceMap/SourceMapGenerator.php diff --git a/lib/scssphp/Base/Range.php b/lib/scssphp/Base/Range.php index 3f3a067da23..8bcc6ecf35f 100644 --- a/lib/scssphp/Base/Range.php +++ b/lib/scssphp/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/lib/scssphp/Block.php b/lib/scssphp/Block.php index a6b5f3f0ebc..a6ef8e034f3 100644 --- a/lib/scssphp/Block.php +++ b/lib/scssphp/Block.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * @@ -28,6 +28,11 @@ class Block */ public $parent; + /** + * @var string + */ + public $sourceName; + /** * @var integer */ diff --git a/lib/scssphp/Colors.php b/lib/scssphp/Colors.php index 61a71ab67ae..2314ea58d19 100644 --- a/lib/scssphp/Colors.php +++ b/lib/scssphp/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/lib/scssphp/Compiler.php b/lib/scssphp/Compiler.php index 68003b90f43..292e960e48a 100644 --- a/lib/scssphp/Compiler.php +++ b/lib/scssphp/Compiler.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * @@ -18,6 +18,7 @@ use Leafo\ScssPhp\Compiler\Environment; 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,10 @@ class Compiler const WITH_SUPPORTS = 4; const WITH_ALL = 7; + const SOURCE_MAP_NONE = 0; + const SOURCE_MAP_INLINE = 1; + const SOURCE_MAP_FILE = 2; + /** * @var array */ @@ -120,11 +125,20 @@ class Compiler protected $encoding = null; protected $lineNumberStyle = null; + protected $sourceMap = self::SOURCE_MAP_NONE; + protected $sourceMapOptions = []; + + /** + * @var string|\Leafo\ScssPhp\Formatter + */ protected $formatter = 'Leafo\ScssPhp\Formatter\Nested'; protected $rootEnv; protected $rootBlock; + /** + * @var \Leafo\ScssPhp\Compiler\Environment + */ protected $env; protected $scope; protected $storeEnv; @@ -165,9 +179,6 @@ class Compiler */ public function compile($code, $path = null) { - $locale = setlocale(LC_NUMERIC, 0); - setlocale(LC_NUMERIC, 'C'); - $this->indentLevel = -1; $this->commentsSeen = []; $this->extends = []; @@ -194,9 +205,35 @@ class Compiler $this->compileRoot($tree); $this->popEnv(); - $out = $this->formatter->format($this->scope); + $sourceMapGenerator = null; - setlocale(LC_NUMERIC, $locale); + if ($this->sourceMap) { + 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) { + $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(); + $sourceMapUrl = null; + + switch ($this->sourceMap) { + case self::SOURCE_MAP_INLINE: + $sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap)); + break; + + case self::SOURCE_MAP_FILE: + $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); + break; + } + + $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); + } return $out; } @@ -273,12 +310,15 @@ class Compiler 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->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; } @@ -661,6 +701,7 @@ class Compiler if ($needsWrap) { $wrapped = new Block; + $wrapped->sourceName = $media->sourceName; $wrapped->sourceIndex = $media->sourceIndex; $wrapped->sourceLine = $media->sourceLine; $wrapped->sourceColumn = $media->sourceColumn; @@ -734,6 +775,7 @@ class Compiler // 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; @@ -790,6 +832,7 @@ class Compiler } $b = new Block; + $b->sourceName = $e->block->sourceName; $b->sourceIndex = $e->block->sourceIndex; $b->sourceLine = $e->block->sourceLine; $b->sourceColumn = $e->block->sourceColumn; @@ -1181,7 +1224,7 @@ class Compiler /** * Compile selector to string; self(&) should have been replaced by now * - * @param array $selector + * @param string|array $selector * * @return string */ @@ -1203,7 +1246,7 @@ class Compiler /** * Compile selector part * - * @param arary $piece + * @param array $piece * * @return string */ @@ -1729,9 +1772,18 @@ class Compiler 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 (;;) { @@ -1741,7 +1793,7 @@ class Compiler 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); @@ -1926,7 +1978,7 @@ class Compiler * * @param string $value * - * @return bool + * @return boolean */ protected function isImmediateRelationshipCombinator($value) { @@ -1963,7 +2015,7 @@ class Compiler * @param array $value * @param boolean $inExp * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ protected function reduce($value, $inExp = false) { @@ -2238,7 +2290,7 @@ class Compiler * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opAddNumberNumber($left, $right) { @@ -2251,7 +2303,7 @@ class Compiler * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opMulNumberNumber($left, $right) { @@ -2264,7 +2316,7 @@ class Compiler * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opSubNumberNumber($left, $right) { @@ -2277,7 +2329,7 @@ class Compiler * @param array $left * @param array $right * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ protected function opDivNumberNumber($left, $right) { @@ -2294,7 +2346,7 @@ class Compiler * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opModNumberNumber($left, $right) { @@ -2580,7 +2632,7 @@ class Compiler * @param array $left * @param array $right * - * @return array + * @return \Leafo\ScssPhp\Node\Number */ protected function opCmpNumberNumber($left, $right) { @@ -3304,6 +3356,30 @@ class Compiler $this->lineNumberStyle = $lineNumberStyle; } + /** + * Enable/disable source maps + * + * @api + * + * @param integer $sourceMap + */ + public function setSourceMap($sourceMap) + { + $this->sourceMap = $sourceMap; + } + + /** + * Set source map options + * + * @api + * + * @param array $sourceMapOptions + */ + public function setSourceMapOptions($sourceMapOptions) + { + $this->sourceMapOptions = $sourceMapOptions; + } + /** * Register function * @@ -3505,7 +3581,7 @@ class Compiler * 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 +3853,7 @@ class Compiler * * @param mixed $value * - * @return array + * @return array|\Leafo\ScssPhp\Node\Number */ private function coerceValue($value) { @@ -3850,7 +3926,8 @@ class Compiler /** * Coerce something to list * - * @param array $item + * @param array $item + * @param string $delim * * @return array */ @@ -4672,36 +4749,32 @@ class Compiler 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) @@ -5129,7 +5202,7 @@ class Compiler $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; } @@ -5140,7 +5213,7 @@ class Compiler $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; } @@ -5210,6 +5283,8 @@ class Compiler * Workaround IE7's content counter bug. * * @param array $args + * + * @return array */ protected function libCounter($args) { diff --git a/lib/scssphp/Compiler/Environment.php b/lib/scssphp/Compiler/Environment.php index b4b86e32db6..fe309dd30f8 100644 --- a/lib/scssphp/Compiler/Environment.php +++ b/lib/scssphp/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/lib/scssphp/Exception/CompilerException.php b/lib/scssphp/Exception/CompilerException.php index 45fc16713c9..7ca2e2b3bb9 100644 --- a/lib/scssphp/Exception/CompilerException.php +++ b/lib/scssphp/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/lib/scssphp/Exception/ParserException.php b/lib/scssphp/Exception/ParserException.php index c0ee002d62e..6d64335fe7c 100644 --- a/lib/scssphp/Exception/ParserException.php +++ b/lib/scssphp/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/lib/scssphp/Exception/RangeException.php b/lib/scssphp/Exception/RangeException.php index 47192ff5f2c..3ba6bf143b1 100644 --- a/lib/scssphp/Exception/RangeException.php +++ b/lib/scssphp/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/lib/scssphp/Exception/ServerException.php b/lib/scssphp/Exception/ServerException.php index 68d3a290060..d0ed0842739 100644 --- a/lib/scssphp/Exception/ServerException.php +++ b/lib/scssphp/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/lib/scssphp/Formatter.php b/lib/scssphp/Formatter.php index 0d2635f1e55..b4f90aa9d87 100644 --- a/lib/scssphp/Formatter.php +++ b/lib/scssphp/Formatter.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * @@ -12,6 +12,7 @@ namespace Leafo\ScssPhp; use Leafo\ScssPhp\Formatter\OutputBlock; +use Leafo\ScssPhp\SourceMap\SourceMapGenerator; /** * Base formatter @@ -56,10 +57,30 @@ abstract class Formatter public $assignSeparator; /** - * @var boolea + * @var boolean */ public $keepSemicolons; + /** + * @var \Leafo\ScssPhp\Formatter\OutputBlock + */ + protected $currentBlock; + + /** + * @var integer + */ + protected $currentLine; + + /** + * @var integer + */ + protected $currentColumn; + + /** + * @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator + */ + protected $sourceMapGenerator; + /** * Initialize formatter * @@ -123,10 +144,10 @@ abstract class Formatter $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 +160,9 @@ abstract class Formatter { $inner = $this->indentStr(); - echo $inner + $this->write($inner . implode($this->tagSeparator, $block->selectors) - . $this->open . $this->break; + . $this->open . $this->break); } /** @@ -167,6 +188,8 @@ abstract class Formatter return; } + $this->currentBlock = $block; + $pre = $this->indentStr(); if (! empty($block->selectors)) { @@ -187,10 +210,10 @@ abstract class Formatter $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); } } @@ -199,12 +222,21 @@ abstract class Formatter * * @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 * * @return string */ - public function format(OutputBlock $block) + public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null) { + $this->sourceMapGenerator = null; + + if ($sourceMapGenerator) { + $this->currentLine = 1; + $this->currentColumn = 0; + $this->sourceMapGenerator = $sourceMapGenerator; + } + ob_start(); $this->block($block); @@ -213,4 +245,30 @@ abstract class Formatter return $out; } + + /** + * @param string $str + */ + protected function write($str) + { + if ($this->sourceMapGenerator) { + $this->sourceMapGenerator->addMapping( + $this->currentLine, + $this->currentColumn, + $this->currentBlock->sourceLine, + $this->currentBlock->sourceColumn - 1, //columns from parser are off by one + $this->currentBlock->sourceName + ); + + $lines = explode("\n", $str); + $lineCount = count($lines); + $this->currentLine += $lineCount-1; + + $lastLine = array_pop($lines); + + $this->currentColumn = ($lineCount === 1 ? $this->currentColumn : 0) + strlen($lastLine); + } + + echo $str; + } } diff --git a/lib/scssphp/Formatter/Compact.php b/lib/scssphp/Formatter/Compact.php index aaf972b7409..4efa1a089a3 100644 --- a/lib/scssphp/Formatter/Compact.php +++ b/lib/scssphp/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/lib/scssphp/Formatter/Compressed.php b/lib/scssphp/Formatter/Compressed.php index 17aca54a5e6..1faa7e11e5d 100644 --- a/lib/scssphp/Formatter/Compressed.php +++ b/lib/scssphp/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 * @@ -53,10 +53,10 @@ class Compressed extends Formatter } } - 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/lib/scssphp/Formatter/Crunched.php b/lib/scssphp/Formatter/Crunched.php index 0a89bc48c1a..42d77b5f784 100644 --- a/lib/scssphp/Formatter/Crunched.php +++ b/lib/scssphp/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 * @@ -51,10 +51,10 @@ class Crunched extends Formatter } } - 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/lib/scssphp/Formatter/Debug.php b/lib/scssphp/Formatter/Debug.php index 321c5368040..bfcbf41ad7f 100644 --- a/lib/scssphp/Formatter/Debug.php +++ b/lib/scssphp/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 * @@ -52,13 +52,13 @@ class Debug extends Formatter $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 @@ class Debug extends Formatter $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 @@ class Debug extends Formatter $indent = $this->indentStr(); if (empty($block->children)) { - echo "{$indent}block->children: []\n"; + $this->write("{$indent}block->children: []\n"); return; } @@ -109,8 +109,10 @@ class Debug extends Formatter { $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/lib/scssphp/Formatter/Expanded.php b/lib/scssphp/Formatter/Expanded.php index dff17e6b0ed..d8c1e8879e8 100644 --- a/lib/scssphp/Formatter/Expanded.php +++ b/lib/scssphp/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 * @@ -59,10 +59,10 @@ class Expanded extends Formatter } } - 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/lib/scssphp/Formatter/Nested.php b/lib/scssphp/Formatter/Nested.php index c4ff803c71d..8f72206fd16 100644 --- a/lib/scssphp/Formatter/Nested.php +++ b/lib/scssphp/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 * @@ -66,10 +66,10 @@ class Nested extends Formatter } } - 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 +80,9 @@ class Nested extends Formatter { $inner = $this->indentStr(); - echo $inner + $this->write($inner . implode($this->tagSeparator, $block->selectors) - . $this->open . $this->break; + . $this->open . $this->break); } /** @@ -94,13 +94,13 @@ class Nested extends Formatter $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 +120,9 @@ class Nested extends Formatter return; } + $this->currentBlock = $block; + + $this->depth = $block->depth; if (! empty($block->selectors)) { @@ -139,11 +142,11 @@ class Nested extends Formatter 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/lib/scssphp/Formatter/OutputBlock.php b/lib/scssphp/Formatter/OutputBlock.php index 88e864023a7..5eb589c9459 100644 --- a/lib/scssphp/Formatter/OutputBlock.php +++ b/lib/scssphp/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 * @@ -47,4 +47,19 @@ class OutputBlock * @var \Leafo\ScssPhp\Formatter\OutputBlock */ public $parent; + + /** + * @var string + */ + public $sourceName; + + /** + * @var integer + */ + public $sourceLine; + + /** + * @var integer + */ + public $sourceColumn; } diff --git a/lib/scssphp/Node.php b/lib/scssphp/Node.php index eb543c73263..b9f7945a353 100644 --- a/lib/scssphp/Node.php +++ b/lib/scssphp/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/lib/scssphp/Node/Number.php b/lib/scssphp/Node/Number.php index 442d57d581b..42c168037fa 100644 --- a/lib/scssphp/Node/Number.php +++ b/lib/scssphp/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 * @@ -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/ @@ -291,8 +291,9 @@ class Number extends Node implements \ArrayAccess reset($units); $unit = key($units); + $dimension = number_format($dimension, static::$precision, '.', ''); - return (string) $dimension . $unit; + return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit; } /** diff --git a/lib/scssphp/Parser.php b/lib/scssphp/Parser.php index 619b7ba83d5..6fdea3e262f 100644 --- a/lib/scssphp/Parser.php +++ b/lib/scssphp/Parser.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * @@ -708,6 +708,7 @@ class Parser list($line, $column) = $this->getSourcePosition($pos); $b = new Block; + $b->sourceName = $this->sourceName; $b->sourceLine = $line; $b->sourceColumn = $column; $b->sourceIndex = $this->sourceIndex; @@ -2468,7 +2469,13 @@ class Parser */ 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'); diff --git a/lib/scssphp/Server.php b/lib/scssphp/Server.php deleted file mode 100644 index e4caddb45de..00000000000 --- a/lib/scssphp/Server.php +++ /dev/null @@ -1,463 +0,0 @@ - - */ -class Server -{ - /** - * @var boolean - */ - private $showErrorsAsCSS; - - /** - * @var string - */ - private $dir; - - /** - * @var string - */ - private $cacheDir; - - /** - * @var \Leafo\ScssPhp\Compiler - */ - private $scss; - - /** - * Join path components - * - * @param string $left Path component, left of the directory separator - * @param string $right Path component, right of the directory separator - * - * @return string - */ - protected function join($left, $right) - { - return rtrim($left, '/\\') . DIRECTORY_SEPARATOR . ltrim($right, '/\\'); - } - - /** - * Get name of requested .scss file - * - * @return string|null - */ - protected function inputName() - { - switch (true) { - case isset($_GET['p']): - return $_GET['p']; - case isset($_SERVER['PATH_INFO']): - return $_SERVER['PATH_INFO']; - case isset($_SERVER['DOCUMENT_URI']): - return substr($_SERVER['DOCUMENT_URI'], strlen($_SERVER['SCRIPT_NAME'])); - } - } - - /** - * Get path to requested .scss file - * - * @return string - */ - protected function findInput() - { - if (($input = $this->inputName()) - && strpos($input, '..') === false - && substr($input, -5) === '.scss' - ) { - $name = $this->join($this->dir, $input); - - if (is_file($name) && is_readable($name)) { - return $name; - } - } - - return false; - } - - /** - * Get path to cached .css file - * - * @return string - */ - protected function cacheName($fname) - { - return $this->join($this->cacheDir, md5($fname) . '.css'); - } - - /** - * Get path to meta data - * - * @return string - */ - protected function metadataName($out) - { - return $out . '.meta'; - } - - /** - * Determine whether .scss file needs to be re-compiled. - * - * @param string $out Output path - * @param string $etag ETag - * - * @return boolean True if compile required. - */ - protected function needsCompile($out, &$etag) - { - if (! is_file($out)) { - return true; - } - - $mtime = filemtime($out); - - $metadataName = $this->metadataName($out); - - if (is_readable($metadataName)) { - $metadata = unserialize(file_get_contents($metadataName)); - - foreach ($metadata['imports'] as $import => $originalMtime) { - $currentMtime = filemtime($import); - - if ($currentMtime !== $originalMtime || $currentMtime > $mtime) { - return true; - } - } - - $metaVars = crc32(serialize($this->scss->getVariables())); - - if ($metaVars !== $metadata['vars']) { - return true; - } - - $etag = $metadata['etag']; - - return false; - } - - return true; - } - - /** - * Get If-Modified-Since header from client request - * - * @return string|null - */ - protected function getIfModifiedSinceHeader() - { - $modifiedSince = null; - - if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { - $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE']; - - if (false !== ($semicolonPos = strpos($modifiedSince, ';'))) { - $modifiedSince = substr($modifiedSince, 0, $semicolonPos); - } - } - - return $modifiedSince; - } - - /** - * Get If-None-Match header from client request - * - * @return string|null - */ - protected function getIfNoneMatchHeader() - { - $noneMatch = null; - - if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) { - $noneMatch = $_SERVER['HTTP_IF_NONE_MATCH']; - } - - return $noneMatch; - } - - /** - * Compile .scss file - * - * @param string $in Input path (.scss) - * @param string $out Output path (.css) - * - * @return array - */ - protected function compile($in, $out) - { - $start = microtime(true); - $css = $this->scss->compile(file_get_contents($in), $in); - $elapsed = round((microtime(true) - $start), 4); - - $v = Version::VERSION; - $t = date('r'); - $css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css; - $etag = md5($css); - - file_put_contents($out, $css); - file_put_contents( - $this->metadataName($out), - serialize([ - 'etag' => $etag, - 'imports' => $this->scss->getParsedFiles(), - 'vars' => crc32(serialize($this->scss->getVariables())), - ]) - ); - - return [$css, $etag]; - } - - /** - * Format error as a pseudo-element in CSS - * - * @param \Exception $error - * - * @return string - */ - protected function createErrorCSS(\Exception $error) - { - $message = str_replace( - ["'", "\n"], - ["\\'", "\\A"], - $error->getfile() . ":\n\n" . $error->getMessage() - ); - - return "body { display: none !important; } - html:after { - background: white; - color: black; - content: '$message'; - display: block !important; - font-family: mono; - padding: 1em; - white-space: pre; - }"; - } - - /** - * Render errors as a pseudo-element within valid CSS, displaying the errors on any - * page that includes this CSS. - * - * @param boolean $show - */ - public function showErrorsAsCSS($show = true) - { - $this->showErrorsAsCSS = $show; - } - - /** - * Compile .scss file - * - * @param string $in Input file (.scss) - * @param string $out Output file (.css) optional - * - * @return string|bool - * - * @throws \Leafo\ScssPhp\Exception\ServerException - */ - public function compileFile($in, $out = null) - { - if (! is_readable($in)) { - throw new ServerException('load error: failed to find ' . $in); - } - - $pi = pathinfo($in); - - $this->scss->addImportPath($pi['dirname'] . '/'); - - $compiled = $this->scss->compile(file_get_contents($in), $in); - - if ($out !== null) { - return file_put_contents($out, $compiled); - } - - return $compiled; - } - - /** - * Check if file need compiling - * - * @param string $in Input file (.scss) - * @param string $out Output file (.css) - * - * @return bool - */ - public function checkedCompile($in, $out) - { - if (! is_file($out) || filemtime($in) > filemtime($out)) { - $this->compileFile($in, $out); - - return true; - } - - return false; - } - - /** - * Compile requested scss and serve css. Outputs HTTP response. - * - * @param string $salt Prefix a string to the filename for creating the cache name hash - */ - public function serve($salt = '') - { - $protocol = isset($_SERVER['SERVER_PROTOCOL']) - ? $_SERVER['SERVER_PROTOCOL'] - : 'HTTP/1.0'; - - if ($input = $this->findInput()) { - $output = $this->cacheName($salt . $input); - $etag = $noneMatch = trim($this->getIfNoneMatchHeader(), '"'); - - if ($this->needsCompile($output, $etag)) { - try { - list($css, $etag) = $this->compile($input, $output); - - $lastModified = gmdate('D, d M Y H:i:s', filemtime($output)) . ' GMT'; - - header('Last-Modified: ' . $lastModified); - header('Content-type: text/css'); - header('ETag: "' . $etag . '"'); - - echo $css; - } catch (\Exception $e) { - if ($this->showErrorsAsCSS) { - header('Content-type: text/css'); - - echo $this->createErrorCSS($e); - } else { - header($protocol . ' 500 Internal Server Error'); - header('Content-type: text/plain'); - - echo 'Parse error: ' . $e->getMessage() . "\n"; - } - } - - return; - } - - header('X-SCSS-Cache: true'); - header('Content-type: text/css'); - header('ETag: "' . $etag . '"'); - - if ($etag === $noneMatch) { - header($protocol . ' 304 Not Modified'); - - return; - } - - $modifiedSince = $this->getIfModifiedSinceHeader(); - $mtime = filemtime($output); - - if (strtotime($modifiedSince) === $mtime) { - header($protocol . ' 304 Not Modified'); - - return; - } - - $lastModified = gmdate('D, d M Y H:i:s', $mtime) . ' GMT'; - header('Last-Modified: ' . $lastModified); - - echo file_get_contents($output); - - return; - } - - header($protocol . ' 404 Not Found'); - header('Content-type: text/plain'); - - $v = Version::VERSION; - echo "/* INPUT NOT FOUND scss $v */\n"; - } - - /** - * Based on explicit input/output files does a full change check on cache before compiling. - * - * @param string $in - * @param string $out - * @param boolean $force - * - * @return string Compiled CSS results - * - * @throws \Leafo\ScssPhp\Exception\ServerException - */ - public function checkedCachedCompile($in, $out, $force = false) - { - if (! is_file($in) || ! is_readable($in)) { - throw new ServerException('Invalid or unreadable input file specified.'); - } - - if (is_dir($out) || ! is_writable(file_exists($out) ? $out : dirname($out))) { - throw new ServerException('Invalid or unwritable output file specified.'); - } - - if ($force || $this->needsCompile($out, $etag)) { - list($css, $etag) = $this->compile($in, $out); - } else { - $css = file_get_contents($out); - } - - return $css; - } - - /** - * Constructor - * - * @param string $dir Root directory to .scss files - * @param string $cacheDir Cache directory - * @param \Leafo\ScssPhp\Compiler|null $scss SCSS compiler instance - */ - public function __construct($dir, $cacheDir = null, $scss = null) - { - $this->dir = $dir; - - if (! isset($cacheDir)) { - $cacheDir = $this->join($dir, 'scss_cache'); - } - - $this->cacheDir = $cacheDir; - - if (! is_dir($this->cacheDir)) { - mkdir($this->cacheDir, 0755, true); - } - - if (! isset($scss)) { - $scss = new Compiler(); - $scss->setImportPaths($this->dir); - } - - $this->scss = $scss; - $this->showErrorsAsCSS = false; - - if (! ini_get('date.timezone')) { - date_default_timezone_set('UTC'); - } - } - - /** - * Helper method to serve compiled scss - * - * @param string $path Root path - */ - public static function serveFrom($path) - { - $server = new static($path); - $server->serve(); - } -} diff --git a/lib/scssphp/SourceMap/Base64VLQEncoder.php b/lib/scssphp/SourceMap/Base64VLQEncoder.php new file mode 100644 index 00000000000..1189ce0e5a5 --- /dev/null +++ b/lib/scssphp/SourceMap/Base64VLQEncoder.php @@ -0,0 +1,217 @@ + + * @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, + ); + + /** + * 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 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)); + } + + return $this->charToIntMap[$char]; + } +} diff --git a/lib/scssphp/SourceMap/SourceMapGenerator.php b/lib/scssphp/SourceMap/SourceMapGenerator.php new file mode 100644 index 00000000000..70b47cdc7a4 --- /dev/null +++ b/lib/scssphp/SourceMap/SourceMapGenerator.php @@ -0,0 +1,337 @@ + + * @author Nicolas FRANÇOIS + */ +class SourceMapGenerator +{ + /** + * What version of source map does the generator generate? + */ + const VERSION = 3; + + /** + * Array of default options + * + * @var array + */ + protected $defaultOptions = array( + // 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. + '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' => '' + ); + + /** + * The base64 VLQ encoder + * + * @var \Leafo\ScssPhp\SourceMap\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; + } + + /** + * Saves the source map to a file + * + * @param string $file The absolute path to a file + * @param string $content The content to write + * + * @throws \Leafo\ScssPhp\Exception\CompilerException 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 + * + * @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 boolean $addEndSlash + * + * @return string + */ + public function fixWindowsPath($path, $addEndSlash = false) + { + $slash = ($addEndSlash) ? '/' : ''; + + if (! empty($path)) { + $path = str_replace('\\', '/', $path); + $path = rtrim($path, '/') . $slash; + } + + return $path; + } +} diff --git a/lib/scssphp/Type.php b/lib/scssphp/Type.php index 2d659e4d592..e84c47e4032 100644 --- a/lib/scssphp/Type.php +++ b/lib/scssphp/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/lib/scssphp/Util.php b/lib/scssphp/Util.php index b2a05db8130..7526e020566 100644 --- a/lib/scssphp/Util.php +++ b/lib/scssphp/Util.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * @@ -15,7 +15,7 @@ use Leafo\ScssPhp\Base\Range; use Leafo\ScssPhp\Exception\RangeException; /** - * Utilties + * Utilty functions * * @author Anthon Pang */ @@ -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. * @@ -53,4 +53,18 @@ class Util 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); + } } diff --git a/lib/scssphp/Version.php b/lib/scssphp/Version.php index 2d6eabc14ac..3b8dcd00795 100644 --- a/lib/scssphp/Version.php +++ b/lib/scssphp/Version.php @@ -2,7 +2,7 @@ /** * SCSSPHP * - * @copyright 2012-2017 Leaf Corcoran + * @copyright 2012-2018 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * @@ -18,5 +18,5 @@ namespace Leafo\ScssPhp; */ class Version { - const VERSION = 'v0.6.7'; + const VERSION = 'v0.7.5'; } diff --git a/lib/scssphp/moodle_readme.txt b/lib/scssphp/moodle_readme.txt index 53ec986a624..44fd815da84 100644 --- a/lib/scssphp/moodle_readme.txt +++ b/lib/scssphp/moodle_readme.txt @@ -7,6 +7,5 @@ Import procedure: - Copy all the files from the folder 'src' this directory. - Copy the license file from the project root. -- Apply path for https://github.com/leafo/scssphp/issues/532 if it is not merged yet Licensed under MIT, Copyright (c) 2015 Leaf Corcoran. diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml index b00346519d3..0b670a1dc45 100644 --- a/lib/thirdpartylibs.xml +++ b/lib/thirdpartylibs.xml @@ -250,7 +250,7 @@ scssphp scssphp MIT - 0.6.7 + 0.7.5 spout