Skip to content

Commit

Permalink
feature #1 Bundle Set up (matheo)
Browse files Browse the repository at this point in the history
This PR was squashed before being merged into the main branch.

Discussion
----------

Bundle Set up

Commits
-------

a675491 Bundle Set up
  • Loading branch information
weaverryan committed Aug 18, 2023
2 parents 5f3ee58 + a675491 commit 8954514
Show file tree
Hide file tree
Showing 17 changed files with 838 additions and 2 deletions.
27 changes: 27 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

if (!file_exists(__DIR__.'/src') || !file_exists(__DIR__.'/tests')) {
exit(0);
}

$finder = (new \PhpCsFixer\Finder())
->in([__DIR__.'/src', __DIR__.'/tests'])
;

return (new \PhpCsFixer\Config())
->setRules(array(
'@Symfony' => true,
'@Symfony:risky' => true,
'phpdoc_to_comment' => false,
'header_comment' => [
'header' => <<<EOF
This file is part of the SymfonyCasts SassBundle package.
Copyright (c) SymfonyCasts <https://symfonycasts.com/>
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
EOF
]
))
->setRiskyAllowed(true)
->setFinder($finder)
;
85 changes: 83 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,83 @@
# sass-bundle
Delightful Sass Support for Symfony + AssetMapper
# Sass For Symfony!

This bundle make it easy to use Sass with Symfony's AssetMapper Component
(no Node required!).

- Automatically downloads the correct Sass binary
- Adds a `sass:build` command to build and watch your Sass changes

## Installation

Install the bundle

```shell
composer require symfonycasts/sass-bundle
```

## Usage

Start by writing your first Sass file `assets/styles/app.scss`, and let's add some basic style

```scss
// assets/styles/app.scss

$red: #fc030b

body {
background: $red;
}
```

Then point your styles in your template.

```php
{# templates/base.html.twig #}

{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('styles/app.scss') }}">
{% endblock %}
```

That's right! You point directly to the `.scss` file. But don't worry, the final built `.css` file will be returned!

Then run the command:

```shell
php bin/console sass:build --watch
```

And that's it!

## How Does it work

The first time you run one of the Sass commands, the bundle will download the correct Sass binary for you system in to `bin/dart-sass`
directory.

When you run `sass:build`, that binary is uses to compile Sass file into a
`var/sass/app.built.css` file. Finally, when the contents of assets/styles/app.scss is requested, the bundle swaps the contents of that file
with the contents of `var/sass/app.built.css`. Nice!

## Deploying

When you deploy, run `sass:build` command before the `asset-map:compile` command so the built file is available:
```shell
php bin/console sass:build
php bin/console asset-map:compile
```

## Configuration

To see the full config from this bundle, run:
```shell
php bin/console config:dump symfonycasts_sass
```
The main option is `root_sass` option, which default to `assets/styles/app.scss`. This represents the source Sass file.

## Using a different binary

This bundle already installed for you the right binary. However if you already have a binary installed on your machine
you can instruct the bundle to use that binary, set the `binary` option:
```yaml
symfonycasts_sass:
binary: 'node_modules/.bin/sass'
```
37 changes: 37 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "symfonycasts/sass-bundle",
"description": "Delightful Sass Support for Symfony + AssetMapper",
"license": "MIT",
"type": "library",
"keywords": ["asset-mapper", "sass"],
"authors": [
{
"name": "Mathéo Daninos",
"homepage": "https://github.com/WebMamba"
}
],
"require": {
"php": ">=8.1",
"symfony/asset-mapper": "^6.3",
"symfony/console": "^5.4|^6.3",
"symfony/http-client": "^5.4|^6.3",
"symfony/process": "^5.4|^6.3"
},
"require-dev": {
"symfony/filesystem": "^6.3",
"symfony/framework-bundle": "^6.3",
"symfony/phpunit-bridge": "^6.3",
"phpstan/phpstan": "1.11.x-dev"
},
"minimum-stability": "dev",
"autoload": {
"psr-4": {
"Symfonycasts\\SassBundle\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Symfonycasts\\SassBundle\\Tests\\": "tests/"
}
}
}
43 changes: 43 additions & 0 deletions config/services.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfonycasts\SassBundle\Command\SassBuildCommand;
use Symfonycasts\SassBundle\SassBuilder;
use Symfonycasts\SassBundle\AssetMapper\SassCssCompiler;
use Symfonycasts\SassBundle\AssetMapper\SassPublicPathAssetPathResolver;
use function Symfony\Component\DependencyInjection\Loader\Configurator\abstract_arg;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
use function Symfony\Component\DependencyInjection\Loader\Configurator\param;

return static function (ContainerConfigurator $container) {
$container->services()
->set('sass.builder', SassBuilder::class)
->args([
abstract_arg('path to sass files'),
abstract_arg('path to css directory'),
param('kernel.project_dir'),
abstract_arg('path to binary'),
])

->set('sass.command.build', SassBuildCommand::class)
->args([
service('sass.builder')
])
->tag('console.command')

->set('sass.css_asset_compiler', SassCssCompiler::class)
->tag('asset_mapper.compiler', [
'priority' => 10
])
->args([
abstract_arg('path to scss files'),
abstract_arg('path to css output directory'),
service('sass.builder'),
])
->set('sass.public_asset_path_resolver', SassPublicPathAssetPathResolver::class)
->decorate('asset_mapper.public_assets_path_resolver')
->args([
service('.inner')
])
;
};
9 changes: 9 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
parameters:
level: 4
paths:
- src
ignoreErrors:
-
message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\:\\:scalarNode\\(\\)\\.$#"
count: 1
path: src/DependencyInjection/TailwindExtension.php
24 changes: 24 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.3/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" cacheDirectory=".phpunit.cache">
<php>
<ini name="display_errors" value="1"/>
<ini name="error_reporting" value="1"/>
<server name="KERNEL_CLASS" value="Symfonycasts\SassBundle\Tests\fixtures\SassTestKernel"/>
<server name="APP_ENV" value="test" force="true"/>
<server name="SHELL_VERBOSITY" value="-1"/>
<server name="SYMFONY_PHPUNIT_REMOVE" value="3"/>
<server name="SYMFONY_PHPUNIT_VERSION" value="9.5"/>
</php>
<testsuites>
<testsuite name="Bundle Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage/>
<source>
<include>
<directory suffix=".php">src</directory>
</include>
</source>
</phpunit>
49 changes: 49 additions & 0 deletions src/AssetMapper/SassCssCompiler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

/*
* This file is part of the SymfonyCasts SassBundle package.
* Copyright (c) SymfonyCasts <https://symfonycasts.com/>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfonycasts\SassBundle\AssetMapper;

use Symfony\Component\AssetMapper\AssetMapperInterface;
use Symfony\Component\AssetMapper\Compiler\AssetCompilerInterface;
use Symfony\Component\AssetMapper\MappedAsset;
use Symfonycasts\SassBundle\SassBuilder;

class SassCssCompiler implements AssetCompilerInterface
{
public function __construct(
private array $scssPaths,
private string $cssPathDirectory,
private readonly SassBuilder $sassBuilder
) {
}

public function supports(MappedAsset $asset): bool
{
foreach ($this->scssPaths as $path) {
if (realpath($asset->sourcePath) === $path) {
return true;
}
}

return false;
}

public function compile(string $content, MappedAsset $asset, AssetMapperInterface $assetMapper): string
{
$cssFile = $this->sassBuilder->guessCssNameFromSassFile($asset->sourcePath, $this->cssPathDirectory);

$asset->addFileDependency($cssFile);

if (($content = file_get_contents($cssFile)) === false) {
throw new \RuntimeException('The file '.$cssFile.' doesn\'t exist, run php bin/console sass:build');
}

return $content;
}
}
41 changes: 41 additions & 0 deletions src/AssetMapper/SassPublicPathAssetPathResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

/*
* This file is part of the SymfonyCasts SassBundle package.
* Copyright (c) SymfonyCasts <https://symfonycasts.com/>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfonycasts\SassBundle\AssetMapper;

use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolverInterface;

class SassPublicPathAssetPathResolver implements PublicAssetsPathResolverInterface
{
public function __construct(private readonly PublicAssetsPathResolverInterface $decorator)
{
}

public function resolvePublicPath(string $logicalPath): string
{
$path = $this->decorator->resolvePublicPath($logicalPath);

if (str_contains($path, '.scss')) {
return str_replace('.scss', '.css', $path);
}

return $path;
}

public function getPublicFilesystemPath(): string
{
$path = $this->decorator->getPublicFilesystemPath();

if (str_contains($path, '.scss')) {
return str_replace('.scss', '.css', $path);
}

return $path;
}
}
58 changes: 58 additions & 0 deletions src/Command/SassBuildCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

/*
* This file is part of the SymfonyCasts SassBundle package.
* Copyright (c) SymfonyCasts <https://symfonycasts.com/>
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfonycasts\SassBundle\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfonycasts\SassBundle\SassBuilder;

#[AsCommand(
name: 'sass:build',
description: 'Builds the Sass assets'
)]
class SassBuildCommand extends Command
{
public function __construct(
private SassBuilder $sassBuilder
) {
parent::__construct();
}

protected function configure(): void
{
$this->addOption('watch', 'w', null, 'Watch for changes and rebuild automatically');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$this->sassBuilder->setOutput($io);

$process = $this->sassBuilder->runBuild(
$input->getOption('watch')
);

$process->wait(function ($type, $buffer) use ($io) {
$io->write($buffer);
});

if (!$process->isSuccessful()) {
$io->error('Sass build failed');

return self::FAILURE;
}

return self::SUCCESS;
}
}
Loading

0 comments on commit 8954514

Please sign in to comment.