Skip to content

Commit

Permalink
Sass module: Update SCSS-PHP compiler to 1.12.1
Browse files Browse the repository at this point in the history
- Resolves #85
- Add tests for edge cases
- Known issue with handling of slashes in CSS values
  - scssphp/scssphp#146
  - See #82 - Workaround is to use `unquote('..')`
  • Loading branch information
eliot-akira committed Mar 8, 2024
1 parent 5450f10 commit cf8e740
Show file tree
Hide file tree
Showing 207 changed files with 7,056 additions and 3,835 deletions.
5 changes: 5 additions & 0 deletions modules/sass/readme.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Sass

Compile [Sass](https://sass-lang.com/) to CSS.

- Based on [SCSS-PHP](https://github.com/scssphp/scssphp/) 1.12.1
- To upgrade:
- Copy `src` folder and rename as `scssphp`
- Run the script `replace-namespace.sh` to use unique namescape in all files, in case other plugins are using a different version of SCSS-PHP
67 changes: 36 additions & 31 deletions modules/sass/replace-namespace.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,52 @@ set -eou pipefail
# of the same module.
#

OS="`uname`"
main() {

CURRENT_FOLDER="scssphp"
local OS
local CURRENT_FOLDER="scssphp"
OS="$(uname)"

namespace-files() {
namespace-files() {

echo "Folder: $CURRENT_FOLDER"
local RESTORE_FOLDER="$CURRENT_FOLDER"
echo "Folder: $CURRENT_FOLDER"
local RESTORE_FOLDER="$CURRENT_FOLDER"

for file in *.php; do
for file in *.php; do

echo " File: $file";
echo " File: $file";

if [ "$OS" == "Darwin" ]; then
# macOS-specific options for sed
# @see https://stackoverflow.com/questions/5694228/sed-in-place-flag-that-works-both-on-mac-bsd-and-linux#22084103
sed -i '' -e 's/namespace\ ScssPhp\\/namespace\ Tangible\\/g' "$file"
sed -i '' -e 's/use\ ScssPhp\\/use\ Tangible\\/g' "$file"
else
# Linux (or WSL2 - Windows Subsystem for Linux)
sed -i -e 's/namespace\ ScssPhp\\/namespace\ Tangible\\/g' "$file"
sed -i -e 's/use\ ScssPhp\\/use\ Tangible\\/g' "$file"
fi
done
if [ "$OS" == "Darwin" ]; then
# macOS-specific options for sed
# @see https://stackoverflow.com/questions/5694228/sed-in-place-flag-that-works-both-on-mac-bsd-and-linux#22084103
sed -i '' -e 's/namespace\ ScssPhp\\/namespace\ Tangible\\/g' "$file"
sed -i '' -e 's/use\ ScssPhp\\/use\ Tangible\\/g' "$file"
else
# Linux or WSL2 - Windows Subsystem for Linux
sed -i -e 's/namespace\ ScssPhp\\/namespace\ Tangible\\/g' "$file"
sed -i -e 's/use\ ScssPhp\\/use\ Tangible\\/g' "$file"
fi
done

for folder in *; do
if [ -d $folder ]; then
for folder in *; do
if [ -d "$folder" ]; then

CURRENT_FOLDER="$RESTORE_FOLDER/$folder"
CURRENT_FOLDER="$RESTORE_FOLDER/$folder"

cd $folder
namespace-files
cd ..
fi
done
cd "$folder"
namespace-files
cd ..
fi
done

CURRENT_FOLDER="$RESTORE_FOLDER"
}
CURRENT_FOLDER="$RESTORE_FOLDER"
}

cd scssphp

cd scssphp
namespace-files

namespace-files
cd ..
}

cd ..
main
4 changes: 1 addition & 3 deletions modules/sass/scssphp/Ast/AstNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
*
* @internal
*/
interface AstNode
interface AstNode extends \Stringable
{
public function getSpan(): FileSpan;

public function __toString(): string;
}
47 changes: 16 additions & 31 deletions modules/sass/scssphp/Ast/Css/CssMediaQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,29 @@

use Tangible\ScssPhp\Exception\SassFormatException;
use Tangible\ScssPhp\Logger\LoggerInterface;
use Tangible\ScssPhp\Parser\InterpolationMap;
use Tangible\ScssPhp\Parser\MediaQueryParser;

/**
* A plain CSS media query, as used in `@media` and `@import`.
*
* @internal
*/
final class CssMediaQuery
final class CssMediaQuery implements MediaQueryMergeResult
{
public const MERGE_RESULT_EMPTY = 'empty';
public const MERGE_RESULT_UNREPRESENTABLE = 'unrepresentable';

/**
* The modifier, probably either "not" or "only".
*
* This may be `null` if no modifier is in use.
*
* @var string|null
* @readonly
*/
private $modifier;
private readonly ?string $modifier;

/**
* The media type, for example "screen" or "print".
*
* This may be `null`. If so, {@see $conditions} will not be empty.
*
* @var string|null
* @readonly
*/
private $type;
private readonly ?string $type;

/**
* Whether {@see $conditions} is a conjunction or a disjunction.
Expand All @@ -54,11 +46,8 @@ final class CssMediaQuery
* condition in {@see $conditions} is met.
*
* If this is `false`, {@see $modifier} and {@see $type} will both be `null`.
*
* @var bool
* @readonly
*/
private $conjunction;
private readonly bool $conjunction;

/**
* Media conditions, including parentheses.
Expand All @@ -68,9 +57,8 @@ final class CssMediaQuery
* [`<media-in-parens>`]: https://drafts.csswg.org/mediaqueries-4/#typedef-media-in-parens
*
* @var list<string>
* @readonly
*/
private $conditions;
private readonly array $conditions;

/**
* Parses a media query from $contents.
Expand All @@ -81,9 +69,9 @@ final class CssMediaQuery
*
* @throws SassFormatException if parsing fails
*/
public static function parseList(string $contents, ?LoggerInterface $logger = null, ?string $url = null): array
public static function parseList(string $contents, ?LoggerInterface $logger = null, ?string $url = null, ?InterpolationMap $interpolationMap = null): array
{
return (new MediaQueryParser($contents, $logger, $url))->parse();
return (new MediaQueryParser($contents, $logger, $url, $interpolationMap))->parse();
}

/**
Expand Down Expand Up @@ -161,14 +149,11 @@ public function matchesAllTypes(): bool
/**
* Merges this with $other to return a query that matches the intersection
* of both inputs.
*
* @return CssMediaQuery|string
* @phpstan-return CssMediaQuery|CssMediaQuery::*
*/
public function merge(CssMediaQuery $other)
public function merge(CssMediaQuery $other): MediaQueryMergeResult
{
if (!$this->conjunction || !$other->conjunction) {
return self::MERGE_RESULT_UNREPRESENTABLE;
return MediaQuerySingletonMergeResult::unrepresentable;
}

$ourModifier = $this->modifier !== null ? strtolower($this->modifier) : null;
Expand All @@ -193,14 +178,14 @@ public function merge(CssMediaQuery $other)
// (grid)`, because it means `not (screen and (color))` and so it allows
// a screen with no color but with a grid.
if (empty(array_diff($negativeConditions, $positiveConditions))) {
return self::MERGE_RESULT_EMPTY;
return MediaQuerySingletonMergeResult::empty;
}

return self::MERGE_RESULT_UNREPRESENTABLE;
return MediaQuerySingletonMergeResult::unrepresentable;
}

if ($this->matchesAllTypes() || $other->matchesAllTypes()) {
return self::MERGE_RESULT_UNREPRESENTABLE;
return MediaQuerySingletonMergeResult::unrepresentable;
}

if ($ourModifier === 'not') {
Expand All @@ -215,7 +200,7 @@ public function merge(CssMediaQuery $other)
} elseif ($ourModifier === 'not') {
// CSS has no way of representing "neither screen nor print".
if ($ourType !== $theirType) {
return self::MERGE_RESULT_UNREPRESENTABLE;
return MediaQuerySingletonMergeResult::unrepresentable;
}

$moreConditions = \count($this->conditions) > \count($other->conditions) ? $this->conditions : $other->conditions;
Expand All @@ -229,7 +214,7 @@ public function merge(CssMediaQuery $other)
$conditions = $moreConditions;
} else {
// Otherwise, there's no way to represent the intersection.
return self::MERGE_RESULT_UNREPRESENTABLE;
return MediaQuerySingletonMergeResult::unrepresentable;
}
} elseif ($this->matchesAllTypes()) {
$modifier = $theirModifier;
Expand All @@ -242,7 +227,7 @@ public function merge(CssMediaQuery $other)
$type = $ourType;
$conditions = array_merge($this->conditions, $other->conditions);
} elseif ($ourType !== $theirType) {
return self::MERGE_RESULT_EMPTY;
return MediaQuerySingletonMergeResult::empty;
} else {
$modifier = $ourModifier ?? $theirModifier;
$type = $ourType;
Expand Down
2 changes: 1 addition & 1 deletion modules/sass/scssphp/Ast/Css/CssNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function isGroupEnd(): bool;
*
* @return T
*/
public function accept($visitor);
public function accept(CssVisitor $visitor);

/**
* Whether this is invisible and won't be emitted to the compiled stylesheet.
Expand Down
4 changes: 1 addition & 3 deletions modules/sass/scssphp/Ast/Css/CssStyleRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ interface CssStyleRule extends CssParentNode
{
/**
* The selector for this rule.
*
* @return CssValue<SelectorList>
*/
public function getSelector(): CssValue;
public function getSelector(): SelectorList;

/**
* The selector for this rule, before any extensions were applied.
Expand Down
30 changes: 19 additions & 11 deletions modules/sass/scssphp/Ast/Css/CssValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,34 @@
namespace Tangible\ScssPhp\Ast\Css;

use Tangible\ScssPhp\Ast\AstNode;
use Tangible\ScssPhp\Ast\Selector\Combinator;
use Tangible\ScssPhp\SourceSpan\FileSpan;
use Tangible\ScssPhp\Util\Equatable;
use Tangible\ScssPhp\Util\EquatableUtil;

/**
* A value in a plain CSS tree.
*
* This is used to associate a span with a value that doesn't otherwise track
* its span.
* its span. It has value equality semantics.
*
* @template T
* @template-covariant T of string|\Stringable|array<string|\Stringable>|Combinator|null
*
* @internal
*/
class CssValue implements AstNode
final class CssValue implements AstNode, Equatable
{
/**
* @phpstan-var T
*/
protected $value;
private readonly mixed $value;

/**
* @var FileSpan
* @readonly
*/
private $span;
private readonly FileSpan $span;

/**
* @param T $value
*/
public function __construct($value, FileSpan $span)
public function __construct(mixed $value, FileSpan $span)
{
$this->value = $value;
$this->span = $span;
Expand All @@ -50,7 +49,7 @@ public function __construct($value, FileSpan $span)
/**
* @return T
*/
public function getValue()
public function getValue(): mixed
{
return $this->value;
}
Expand All @@ -60,8 +59,17 @@ public function getSpan(): FileSpan
return $this->span;
}

public function equals(object $other): bool
{
return $other instanceof CssValue && EquatableUtil::equals($this->value, $other->value);
}

public function __toString(): string
{
if ($this->value instanceof Combinator) {
return $this->value->getText();
}

if (\is_array($this->value)) {
return implode($this->value);
}
Expand Down
18 changes: 6 additions & 12 deletions modules/sass/scssphp/Ast/Css/IsInvisibleVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,41 +23,35 @@ final class IsInvisibleVisitor extends EveryCssVisitor
{
/**
* Whether to consider selectors with bogus combinators invisible.
*
* @var bool
* @readonly
*/
private $includeBogus;
private readonly bool $includeBogus;

/**
* Whether to consider comments invisible.
*
* @var bool
* @readonly
*/
private $includeComments;
private readonly bool $includeComments;

public function __construct(bool $includeBogus, bool $includeComments)
{
$this->includeBogus = $includeBogus;
$this->includeComments = $includeComments;
}

public function visitCssAtRule($node): bool
public function visitCssAtRule(CssAtRule $node): bool
{
// An unknown at-rule is never invisible. Because we don't know the semantics
// of unknown rules, we can't guarantee that (for example) `@foo {}` isn't
// meaningful.
return false;
}

public function visitCssComment($node): bool
public function visitCssComment(CssComment $node): bool
{
return $this->includeComments && !$node->isPreserved();
}

public function visitCssStyleRule($node): bool
public function visitCssStyleRule(CssStyleRule $node): bool
{
return ($this->includeBogus ? $node->getSelector()->getValue()->isInvisible() : $node->getSelector()->getValue()->isInvisibleOtherThanBogusCombinators()) || parent::visitCssStyleRule($node);
return ($this->includeBogus ? $node->getSelector()->isInvisible() : $node->getSelector()->isInvisibleOtherThanBogusCombinators()) || parent::visitCssStyleRule($node);
}
}
Loading

2 comments on commit cf8e740

@nicolas-jaussaud
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @eliot-akira!

While testing/updating blocks I noticed that the minimal php version for SCSS-PHP is now 8.1

Are we also switching to php 8.1 and above as the minimal version for template-system and the associated projects, or should we still try to correct anything we encounter that is not php 7.4 compatible?

@eliot-akira
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for noticing this, I've started an issue about it:

Please sign in to comment.