From a0e5590e65e33ad6c10e4c98e4aa7c8563779ee8 Mon Sep 17 00:00:00 2001 From: Geoff Willings Date: Wed, 24 Oct 2018 00:41:31 +0100 Subject: [PATCH 001/194] 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 002/194] 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 003/194] 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 004/194] 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 005/194] 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 006/194] 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('