Skip to content

Commit

Permalink
Handle Sass cli options (style, --no-source_map, --embed-sources...) (#…
Browse files Browse the repository at this point in the history
…39)

* Handle Sass cli options

* Rework sass options

* Rework sass options

* Fix defaults

* Fix deprecated version

* Rework configuration

* Add tests for Sass options

* Fix test

* Fix test

* Allow external options

* Add Documentation

* Update doc/index.rst

Co-authored-by: Victor Bocharsky <[email protected]>

* Update doc/index.rst

Co-authored-by: Victor Bocharsky <[email protected]>

---------

Co-authored-by: Victor Bocharsky <[email protected]>
  • Loading branch information
smnandre and bocharsky-bw authored Dec 21, 2023
1 parent df89162 commit 5b831bf
Show file tree
Hide file tree
Showing 11 changed files with 559 additions and 30 deletions.
2 changes: 1 addition & 1 deletion config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
abstract_arg('path to css directory'),
param('kernel.project_dir'),
abstract_arg('path to binary'),
abstract_arg('embed sourcemap'),
abstract_arg('sass options'),
])

->set('sass.command.build', SassBuildCommand::class)
Expand Down
49 changes: 49 additions & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,57 @@ To see the full config from this bundle, run:
$ php bin/console config:dump symfonycasts_sass
Source Sass file
~~~~~~~~~~~~~~~~

The main option is ``root_sass`` option, which defaults to ``assets/styles/app.scss``. This represents the source Sass file.

.. code-block:: yaml
# config/packages/symfonycasts_sass.yaml
symfonycasts_sass:
root_sass: 'assets/styles/app.scss'
Sass CLI Options
~~~~~~~~~~~~~~~~

You can configure most of the `Dart Sass CLI options <https://sass-lang.com/documentation/cli/dart-sass>`_:

.. code-block:: yaml
# config/packages/symfonycasts_sass.yaml
symfonycasts_sass:
sass_options:
# The output style for the compiled CSS files: expanded or compressed. Defaults to expanded.
# style: expanded
# Emit a @charset or BOM for CSS with non-ASCII characters. Defaults to true in Dart Sass.
# charset: true
# Wether to generate source maps. Defaults to true when "kernel.debug" is true.
# source_map: true
# Embed source file contents in source maps. Defaults to false.
# embed_sources:
# Embed source map contents in CSS. Defaults to false.
# embed_source_map:
# Don't print warnings. Defaults to false.
# quiet:
# Don't print deprecated warnings for dependencies. Defaults to false.
# quiet_deps:
# Don't compile more files once an error is encountered. Defaults to false.
# stop_on_error:
# Print full Dart stack traces for exceptions. Defaults to false.
# trace:
Using a different binary
--------------------------

Expand Down
54 changes: 49 additions & 5 deletions src/DependencyInjection/SymfonycastsSassExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,16 @@ public function load(array $configs, ContainerBuilder $container): void
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);

// BC Layer with SassBundle < 0.4
if (isset($config['embed_sourcemap'])) {
$config['sass_options']['embed_source_map'] = $config['embed_sourcemap'];
}

$container->findDefinition('sass.builder')
->replaceArgument(0, $config['root_sass'])
->replaceArgument(1, '%kernel.project_dir%/var/sass')
->replaceArgument(3, $config['binary'])
->replaceArgument(4, $config['embed_sourcemap'])
->replaceArgument(4, $config['sass_options'])
;

$container->findDefinition('sass.css_asset_compiler')
Expand Down Expand Up @@ -80,11 +85,50 @@ public function getConfigTreeBuilder(): TreeBuilder
->scalarNode('binary')
->info('The Sass binary to use')
->defaultNull()
->end()
->arrayNode('sass_options')
->addDefaultsIfNotSet()
->children()
->enumNode('style')
->info('The style of the generated CSS: compressed or expanded.')
->values(['compressed', 'expanded'])
->defaultValue('expanded')
->end()
->booleanNode('charset')
->info('Whether to include the charset declaration in the generated Sass.')
->end()
->booleanNode('error_css')
->info('Emit a CSS file when an error occurs.')
->end()
->booleanNode('source_map')
->info('Whether to generate source maps.')
->defaultValue(true)
->end()
->booleanNode('embed_sources')
->info('Embed source file contents in source maps.')
->end()
->booleanNode('embed_source_map')
->info('Embed source map contents in CSS.')
->defaultValue('%kernel.debug%')
->end()
->booleanNode('quiet')
->info('Don\'t print warnings.')
->end()
->booleanNode('quiet_deps')
->info(' Don\'t print compiler warnings from dependencies.')
->end()
->booleanNode('stop_on_error')
->info('Don\'t compile more files once an error is encountered.')
->end()
->booleanNode('trace')
->info('Print full Dart stack traces for exceptions.')
->end()
->end()
->scalarNode('embed_sourcemap')
->info('Whether to embed the sourcemap in the compiled CSS. By default, enabled only when debug mode is on.')
->defaultValue('%kernel.debug%')
->end()
->end()
->booleanNode('embed_sourcemap')
->setDeprecated('symfonycast/sass-bundle', '0.4', 'Option "%node%" at "%path%" is deprecated. Use "sass_options.embed_source_map" instead".')
->defaultNull()
->end()
->end()
;

Expand Down
146 changes: 126 additions & 20 deletions src/SassBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,66 @@ class SassBuilder
private ?SymfonyStyle $output = null;

/**
* @param array<string> $sassPaths
* @var array<string, bool|string>
*/
private array $sassOptions;

/**
* Run "sass --help" to see all options.
*
* @see https://sass-lang.com/documentation/cli/dart-sass/
*/
private const SASS_OPTIONS = [
// Input and Output
'--style' => 'expanded', // Output style. [expanded (default), compressed]
'--[no-]charset' => null, // Emit a @charset or BOM for CSS with non-ASCII characters.
'--[no-]error-css' => null, // Emit a CSS file when an error occurs.
// Source Maps
'--[no-]source-map' => true, // Whether to generate source maps. (defaults to on)
'--[no-]embed-sources' => null, // Embed source file contents in source maps.
'--[no-]embed-source-map' => null, // Embed source map contents in CSS.
// Warnings
'--[no-]quiet' => null, // Don't print warnings.
'--[no-]quiet-deps' => null, // Don't print deprecation warnings for dependencies.
// Other
'--[no-]stop-on-error' => null, // Don't compile more files once an error is encountered.
'--[no-]trace' => null, // Print full Dart stack traces for exceptions.
];

/**
* @param array<string> $sassPaths
* @param array<string, bool|string> $sassOptions
*/
public function __construct(
private readonly array $sassPaths,
private readonly string $cssPath,
private readonly string $projectRootDir,
private readonly ?string $binaryPath,
private readonly bool $embedSourcemap,
bool|array $sassOptions = [],
) {
if (\is_bool($sassOptions)) {
// Until 0.4, the $sassOptions argument was a boolean named $embedSourceMap
trigger_deprecation('symfonycasts/sass-bundle', '0.4', 'Passing a boolean to embed the source map is deprecated. Set \'sass_options.embed_source_map\' instead.');
$sassOptions = ['embed_source_map' => $sassOptions];
// ...and source maps were always generated.
$sassOptions['source_map'] = true;
}

$this->setOptions($sassOptions);
}

public function runBuild(bool $watch): Process
{
$binary = $this->createBinary();

$args = $this->getScssCssTargets();

if ($watch) {
$args[] = '--watch';
}

if ($this->embedSourcemap) {
$args[] = '--embed-source-map';
}
$args = [
...$this->getScssCssTargets(),
...$this->getBuildOptions(['--watch' => $watch]),
];

$process = $binary->createProcess($args);

if ($watch) {
$process->setTimeout(null);

$inputStream = new InputStream();
$process->setInput($inputStream);
}
Expand All @@ -65,6 +95,11 @@ public function runBuild(bool $watch): Process
return $process;
}

private function createBinary(): SassBinary
{
return new SassBinary($this->projectRootDir.'/var', $this->binaryPath, $this->output);
}

/**
* @return array<string>
*/
Expand All @@ -82,11 +117,6 @@ public function getScssCssTargets(): array
return $targets;
}

public function setOutput(SymfonyStyle $output): void
{
$this->output = $output;
}

/**
* @internal
*/
Expand All @@ -97,8 +127,84 @@ public static function guessCssNameFromSassFile(string $sassFile, string $output
return $outputDirectory.'/'.$fileName.'.output.css';
}

private function createBinary(): SassBinary
/**
* @param array<string, bool|string> $options
*
* @return list<string>
*/
public function getBuildOptions(array $options = []): array
{
return new SassBinary($this->projectRootDir.'/var', $this->binaryPath, $this->output);
$buildOptions = [];
$options = [...self::SASS_OPTIONS, ...$this->sassOptions, ...$options];
foreach ($options as $option => $value) {
// Set only the defined options.
if (null === $value) {
continue;
}
// --no-embed-source-map
// --quiet
if (str_starts_with($option, '--[no-]')) {
$buildOptions[] = str_replace('[no-]', $value ? '' : 'no-', $option);
continue;
}
// --style=compressed
if (\is_string($value)) {
$buildOptions[] = $option.'='.$value;
continue;
}
// --update
// --watch
if ($value) {
$buildOptions[] = $option;
}
}

return $buildOptions;
}

public function setOutput(SymfonyStyle $output): void
{
$this->output = $output;
}

/**
* Save the Sass options for the build.
*
* Options are converted from PHP option names to CLI option names.
*
* @param array<string, bool|string> $options
*
* @see getOptionMap()
*/
private function setOptions(array $options = []): void
{
$sassOptions = [];
$optionMap = $this->getOptionMap();
foreach ($options as $option => $value) {
if (!isset($optionMap[$option])) {
throw new \InvalidArgumentException(sprintf('Invalid option "%s". Available options are: "%s".', $option, implode('", "', array_keys($optionMap))));
}
$sassOptions[$optionMap[$option]] = $value;
}
$this->sassOptions = $sassOptions;
}

/**
* Get a map of the Sass options as <php-option> => <cli-option>.
* Example: ['embed_source_map' => '--embed-source-map'].
*
* @return array<string, string>
*/
private function getOptionMap(): array
{
$phpOptions = [];
foreach (array_keys(self::SASS_OPTIONS) as $cliOption) {
$phpOption = str_replace(['--[no-]', '--'], '', $cliOption);
$phpOption = str_replace('-', '_', $phpOption);

$phpOptions[$phpOption] = $cliOption;
}

return $phpOptions;
}
}
Loading

0 comments on commit 5b831bf

Please sign in to comment.