Skip to content

Commit c3857c9

Browse files
committed
Initial implementation.
1 parent 9ac15e4 commit c3857c9

6 files changed

Lines changed: 294 additions & 5 deletions

File tree

.travis.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
language: php
2+
sudo: false
3+
4+
php:
5+
- 7.1
6+
- 7.2
7+
- 7.3
8+
- 7.4
9+
10+
env:
11+
- TEST_SUITE=PHP_CodeSniffer
12+
- TEST_SUITE=PHPUnit
13+
14+
# Only run the coding standards check once.
15+
matrix:
16+
exclude:
17+
- php: 7.1
18+
env: TEST_SUITE=PHP_CodeSniffer
19+
- php: 7.2
20+
env: TEST_SUITE=PHP_CodeSniffer
21+
- php: 7.3
22+
env: TEST_SUITE=PHP_CodeSniffer
23+
24+
before_script:
25+
- composer install
26+
27+
script:
28+
- test ${TEST_SUITE} == "PHP_CodeSniffer" || ./vendor/bin/phpunit tests/
29+
- test ${TEST_SUITE} == "PHPUnit" || ./vendor/bin/phpcs

composer.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
"keywords": ["task-runner"],
55
"type": "library",
66
"require": {
7-
"php": ">=7.1",
7+
"php": "~7.1",
88
"consolidation/robo": "^1.4.11",
9-
"openeuropa/task-runner": "dev-master",
9+
"openeuropa/task-runner": "^1.0.0-beta6",
1010
"scssphp/scssphp": "~1.1"
1111
},
1212
"require-dev": {
13-
"phpunit/phpunit": "~6 || ~7 || ~8 || ~9",
14-
"squizlabs/php_codesniffer": "^3.0@dev"
13+
"phpunit/phpunit": "~9",
14+
"slevomat/coding-standard": "~6",
15+
"squizlabs/php_codesniffer": "~3"
1516
},
1617
"license": "EUPL-1.2",
1718
"authors": [
@@ -27,7 +28,7 @@
2728
},
2829
"autoload-dev": {
2930
"psr-4": {
30-
"TaskRunner\\Scss\\Tests\\": "./tests/"
31+
"TaskRunner\\Scss\\Tests\\": "./tests/src/"
3132
}
3233
},
3334
"config": {

phpcs.xml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ruleset name="TaskRunner/Scss">
3+
<description>PHP CodeSniffer configuration for the php-task-runner/scss library.</description>
4+
<config name="installed_paths" value="../../slevomat/coding-standard"/>
5+
<rule ref="PSR12">
6+
<exclude name="Generic.Files.LineLength.TooLong" />
7+
<exclude name="PSR2.Classes.ClassDeclaration.CloseBraceAfterBody" />
8+
</rule>
9+
<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
10+
<properties>
11+
<property name="newlinesCountBetweenOpenTagAndDeclare" value="2" />
12+
<property name="newlinesCountAfterDeclare" value="2" />
13+
<property name="spacesCountAroundEqualsSign" value="0" />
14+
</properties>
15+
</rule>
16+
<rule ref="SlevomatCodingStandard.Arrays.DisallowImplicitArrayCreation" />
17+
<rule ref="SlevomatCodingStandard.ControlStructures.DisallowContinueWithoutIntegerOperandInSwitch" />
18+
<rule ref="SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator" />
19+
<rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators" />
20+
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses" />
21+
<rule ref="SlevomatCodingStandard.Namespaces.UseFromSameNamespace" />
22+
<rule ref="SlevomatCodingStandard.PHP.UselessParentheses" />
23+
<rule ref="SlevomatCodingStandard.Variables.UnusedVariable" />
24+
<rule ref="SlevomatCodingStandard.Arrays.TrailingArrayComma" />
25+
<rule ref="SlevomatCodingStandard.Namespaces.AlphabeticallySortedUses" />
26+
<rule ref="SlevomatCodingStandard.Namespaces.NamespaceDeclaration" />
27+
<rule ref="SlevomatCodingStandard.Namespaces.NamespaceSpacing" />
28+
<rule ref="SlevomatCodingStandard.Classes.ClassConstantVisibility" />
29+
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHintSpacing" />
30+
<rule ref="SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue" />
31+
<rule ref="SlevomatCodingStandard.Namespaces.UseDoesNotStartWithBackslash" />
32+
<file>src</file>
33+
<file>tests</file>
34+
<arg name="report" value="full"/>
35+
<arg value="p"/>
36+
</ruleset>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TaskRunner\Scss\TaskRunner\Commands;
6+
7+
use Consolidation\AnnotatedCommand\CommandData;
8+
use OpenEuropa\TaskRunner\Commands\AbstractCommands;
9+
use Robo\Collection\CollectionBuilder;
10+
use Robo\Task\Assets\loadTasks;
11+
use Symfony\Component\Console\Input\InputOption;
12+
13+
/**
14+
* Command wrapper for the "taskScss" task that is included in Robo.
15+
*
16+
* @see \Robo\Task\Assets\loadTasks
17+
*/
18+
class ScssCommands extends AbstractCommands
19+
{
20+
use loadTasks;
21+
22+
/**
23+
* List of formatters that is offered by the ScssPhp compiler.
24+
*
25+
* @see \ScssPhp\ScssPhp\Formatter
26+
*/
27+
protected const SCSS_FORMATTERS = ['compact', 'compressed', 'crunched', 'expanded', 'nested'];
28+
29+
/**
30+
* Compiles SCSS.
31+
*
32+
* @command assets:compile-scss
33+
*
34+
* @param string $input_file The path to the SCSS file to process
35+
* @param string $output_file The path where to store the compiled CSS file
36+
* @option style Set the output format (compact, compressed, crunched, expanded, or nested)
37+
* @option import-dir Set an import path
38+
*
39+
* @param array $options
40+
*
41+
* @return \Robo\Collection\CollectionBuilder
42+
*/
43+
public function compileScss(string $input_file, string $output_file, array $options = [
44+
'style' => InputOption::VALUE_REQUIRED,
45+
'import-dir' => [],
46+
]): CollectionBuilder
47+
{
48+
$scss = $this->taskScss([$input_file => $output_file]);
49+
50+
if ($options['style']) {
51+
$scss->setFormatter('ScssPhp\\ScssPhp\\Formatter\\' . ucfirst($options['style']));
52+
}
53+
54+
foreach ($options['import-dir'] as $import_dir) {
55+
$scss->addImportPath($import_dir);
56+
}
57+
58+
return $this->collectionBuilder()->addTask($scss);
59+
}
60+
61+
/**
62+
* @hook pre-validate assets:compile-scss
63+
*/
64+
public function preValidateCompileScss(CommandData $commandData): void
65+
{
66+
$input = $commandData->input();
67+
$style = $input->getOption('style');
68+
if ($style) {
69+
// Ensure case insensitive matching for the style option.
70+
$input->setOption('style', strtolower($style));
71+
}
72+
}
73+
74+
/**
75+
* @hook validate assets:compile-scss
76+
*/
77+
public function validateCompileScss(CommandData $commandData): void
78+
{
79+
$input = $commandData->input();
80+
$input_file = $input->getArgument('input_file');
81+
if (!is_file($input_file) || !is_readable($input_file)) {
82+
throw new \Exception(sprintf('Input file "%s" does not exist or is not readable', $input_file));
83+
}
84+
85+
$style = $input->getOption('style');
86+
if ($style && !in_array($style, self::SCSS_FORMATTERS)) {
87+
throw new \Exception(sprintf('Unknown style "%s"', $style));
88+
}
89+
90+
$import_dirs = $input->getOption('import-dir');
91+
foreach ($import_dirs as $import_dir) {
92+
if (!is_dir($import_dir) || !is_readable($import_dir)) {
93+
throw new \Exception(sprintf('Import dir "%s" does not exist or is not readable', $import_dir));
94+
}
95+
}
96+
}
97+
98+
}

tests/fixtures/example.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Example SCSS file to test the `assets:compile-scss` command.
2+
$dark: #111;
3+
nav {
4+
ul {
5+
margin: 0;
6+
li {
7+
color: $dark;
8+
}
9+
}
10+
}

tests/src/ScssCommandsTest.php

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TaskRunner\Scss\Tests;
6+
7+
use Composer\Autoload\ClassLoader;
8+
use OpenEuropa\TaskRunner\TaskRunner;
9+
use PHPUnit\Framework\TestCase;
10+
use Symfony\Component\Console\Input\StringInput;
11+
use Symfony\Component\Console\Output\BufferedOutput;
12+
13+
/**
14+
* Tests the command wrappers for Robo "assets" tasks.
15+
*
16+
* @coversDefaultClass \TaskRunner\Scss\TaskRunner\Commands\ScssCommands
17+
*/
18+
final class ScssCommandsTest extends TestCase
19+
{
20+
21+
/**
22+
* @param string $style
23+
* The CSS style to be generated.
24+
* @param string $expected
25+
* The expected compiled CSS.
26+
*
27+
* @covers ::compileScss
28+
* @dataProvider compileScssDataProvider
29+
*/
30+
public function testCompileScss(string $style, string $expected): void
31+
{
32+
$command = sprintf(
33+
'assets:compile-scss --working-dir=%s --style=%s %s %s',
34+
sys_get_temp_dir(),
35+
$style,
36+
__DIR__ . '/../fixtures/example.scss',
37+
'output.css'
38+
);
39+
$input = new StringInput($command);
40+
$output = new BufferedOutput();
41+
$runner = new TaskRunner($input, $output, $this->getClassLoader());
42+
$runner->run();
43+
44+
$actual = file_get_contents(sys_get_temp_dir() . '/output.css');
45+
$this->assertEquals($expected, $actual);
46+
}
47+
48+
/**
49+
* Data provider for ::testCompileScss().
50+
*
51+
* @return array[]
52+
* An array of test cases, each test case an array with two elements:
53+
* - A string containing the CSS style to be generated.
54+
* - A string containing the expected compiled CSS.
55+
*/
56+
public function compileScssDataProvider(): array
57+
{
58+
return [
59+
[
60+
'compact',
61+
<<<CSS
62+
nav ul { margin:0; }
63+
64+
nav ul li { color:#111; }
65+
66+
67+
CSS,
68+
],
69+
[
70+
'compressed',
71+
<<<CSS
72+
nav ul{margin:0}nav ul li{color:#111}
73+
CSS,
74+
],
75+
[
76+
'crunched',
77+
<<<CSS
78+
nav ul{margin:0}nav ul li{color:#111}
79+
CSS,
80+
],
81+
[
82+
'expanded',
83+
<<<CSS
84+
nav ul {
85+
margin: 0;
86+
}
87+
nav ul li {
88+
color: #111;
89+
}
90+
91+
CSS,
92+
],
93+
[
94+
'nested',
95+
<<<CSS
96+
nav ul {
97+
margin: 0; }
98+
nav ul li {
99+
color: #111; }
100+
101+
CSS,
102+
],
103+
];
104+
}
105+
106+
/**
107+
* Returns the Composer classloader.
108+
*
109+
* @return \Composer\Autoload\ClassLoader
110+
*/
111+
protected function getClassLoader(): ClassLoader
112+
{
113+
return require __DIR__ . '/../../vendor/autoload.php';
114+
}
115+
}

0 commit comments

Comments
 (0)