Completed
Push — develop ( 38da12...667cd2 )
by Vladimir
02:29
created

SassEngine::initializeCompiler()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.054

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 0
dl 0
loc 24
ccs 9
cts 11
cp 0.8182
crap 3.054
rs 9.536
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @copyright 2018 Vladimir Jimenez
5
 * @license   https://github.com/stakx-io/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\AssetEngine\Sass;
9
10
use __;
11
use allejo\stakx\AssetEngine\AssetEngineInterface;
12
use allejo\stakx\Configuration;
13
use allejo\stakx\Document\BasePageView;
14
use allejo\stakx\Document\StaticPageView;
15
use allejo\stakx\Filesystem\FilesystemLoader as fs;
16
use allejo\stakx\Filesystem\WritableFolder;
17
use allejo\stakx\Manager\PageManager;
18
use allejo\stakx\Service;
19
use ScssPhp\ScssPhp\Compiler;
20
use ScssPhp\ScssPhp\Formatter\Compact;
21
use ScssPhp\ScssPhp\Formatter\Crunched;
22
use ScssPhp\ScssPhp\Formatter\Expanded;
23
use ScssPhp\ScssPhp\Formatter\Nested;
24
25
class SassEngine implements AssetEngineInterface
26
{
27
    /** @var bool */
28
    private $fileSourceMap = false;
29
30
    /** @var WritableFolder|null */
31
    private $cacheDirectory;
32
33
    /** @var Configuration */
34
    private $configuration;
35
36
    /** @var PageManager */
37
    private $pageManager;
38
39
    /** @var Compiler|null */
40
    private $compiler;
41
42
    /** @var array<string, mixed> */
43
    private $options = [];
44
45 7
    public function __construct(Configuration $configuration)
46
    {
47 7
        $this->configuration = $configuration;
48 7
    }
49
50 1
    public function getName()
51
    {
52 1
        return 'Sass';
53
    }
54
55 7
    public function getConfigurationNamespace()
56
    {
57 7
        return 'scss';
58
    }
59
60 7
    public function getDefaultConfiguration()
61
    {
62
        return [
63 7
            'style' => 'compressed',
64
            'sourcemap' => false,
65
        ];
66
    }
67
68 7
    public function getFolder()
69
    {
70 7
        return '_sass';
71
    }
72
73 7
    public function getExtensions()
74
    {
75 7
        return ['scss'];
76
    }
77
78
    /**
79
     * @param string $content
80
     * @param $options = [
81
     *     'pageview' => new StaticPageView()
82
     * ]
83
     *
84
     * @return string
85
     */
86 7
    public function parse($content, array $options = [])
87
    {
88 7
        $this->initializeCompiler();
89
90
        $sourceMapOptions = [
91 7
            'sourceMapBasepath' => Service::getWorkingDirectory(),
92
        ];
93
94 7
        $this->handleThemeImports($content);
95
96
        // We don't need to write the source map to a file
97 7
        if (!$this->fileSourceMap)
98
        {
99 6
            $this->compiler->setSourceMapOptions($sourceMapOptions);
100
101 6
            return $this->compiler->compile($content);
102
        }
103
104
        /** @var StaticPageView $pageView */
105 1
        $pageView = $options['pageview'];
106
107
        // Always put our source map next to the respective CSS file
108 1
        $sourceMapTargetPath = $this->getSourceMapTargetFile($pageView);
109 1
        $sourceMapFilename = fs::getFilename($sourceMapTargetPath);
110
111 1
        $sourceMapOptions = array_merge($sourceMapOptions, [
112 1
            'sourceMapFilename' => $sourceMapFilename,
113 1
            'sourceMapURL' => $pageView->getPermalink() . '.map',
114 1
            'sourceMapWriteTo' => $sourceMapTargetPath,
115
        ]);
116
117 1
        $sourceMapGenerator = new SourceMapGenerator($sourceMapOptions);
118 1
        $this->compiler->setSourceMap($sourceMapGenerator);
0 ignored issues
show
Documentation introduced by
$sourceMapGenerator is of type object<allejo\stakx\Asse...ass\SourceMapGenerator>, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
119
120 1
        $sass = $this->compiler->compile($content);
121
122 1
        $sourceMapPageView = BasePageView::createVirtual([
123 1
            'permalink' => $pageView->getPermalink() . '.map',
124 1
        ], $sourceMapGenerator->getSourceMapContents());
125
126 1
        $this->pageManager->trackNewPageView($sourceMapPageView);
127
128 1
        return $sass;
129
    }
130
131 7
    public function setOptions(array $options)
132
    {
133 7
        $this->options = $options;
134 7
    }
135
136 1
    public function setPageManager(PageManager $pageManager)
137
    {
138 1
        $this->pageManager = $pageManager;
139 1
    }
140
141
    public function loadCache(WritableFolder $cacheDir)
142
    {
143
        $this->cacheDirectory = $cacheDir;
144
    }
145
146 1
    public function saveCache(WritableFolder $cacheDir)
147
    {
148 1
    }
149
150 7
    private function configureImportPath()
151
    {
152 7
        $this->compiler->setImportPaths(Service::getWorkingDirectory() . '/_sass/');
153 7
    }
154
155 7
    private function configureOutputStyle()
156
    {
157 7
        $style = __::get($this->options, 'style', 'compressed');
158
159 7
        $this->compiler->setFormatter(self::stringToFormatter($style));
160 7
    }
161
162 7
    private function configureSourceMap()
163
    {
164 7
        $sourceMap = __::get($this->options, 'sourcemap');
165
166 7
        if ($sourceMap === 'inline')
167
        {
168 1
            $this->compiler->setSourceMap(Compiler::SOURCE_MAP_INLINE);
169
        }
170 6
        elseif ($sourceMap === true)
171
        {
172 1
            $this->compiler->setSourceMap(Compiler::SOURCE_MAP_FILE);
173 1
            $this->fileSourceMap = true;
174
        }
175
        else
176
        {
177 5
            $this->compiler->setSourceMap(Compiler::SOURCE_MAP_NONE);
178
        }
179 7
    }
180
181 1
    private function getSourceMapTargetFile(StaticPageView $pageView)
182
    {
183 1
        return fs::absolutePath(
184 1
            $this->configuration->getTargetFolder(),
0 ignored issues
show
Documentation introduced by
$this->configuration->getTargetFolder() is of type string, but the function expects a object<string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
185 1
            $pageView->getTargetFile() . '.map'
0 ignored issues
show
Unused Code introduced by
The call to FilesystemLoader::absolutePath() has too many arguments starting with $pageView->getTargetFile() . '.map'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
186
        );
187
    }
188
189 7
    private function initializeCompiler()
190
    {
191 7
        if ($this->compiler)
192
        {
193
            return;
194
        }
195
196 7
        $cacheOptions = [];
197
198
        // If we have a cache directory set, use it.
199 7
        if ($this->cacheDirectory)
200
        {
201
            $cacheOptions = [
202
                'cacheDir' => (string) $this->cacheDirectory->getFilesystemPath(),
203
                'forceRefresh' => false,
204
            ];
205
        }
206
207 7
        $this->compiler = new Compiler($cacheOptions);
208
209 7
        $this->configureImportPath();
210 7
        $this->configureOutputStyle();
211 7
        $this->configureSourceMap();
212 7
    }
213
214 7
    private function handleThemeImports(&$content)
215
    {
216 7
        if (($themeName = $this->configuration->getTheme()))
217
        {
218
            $themePath = "../_themes/${themeName}/_sass";
219
            $content = preg_replace("/(@import ['\"])(@theme)(.+)/", "$1${themePath}$3", $content);
220
        }
221 7
    }
222
223
    public static function stringToFormatter($format)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
224
    {
225 7
        if ($format == 'nested')
226
        {
227 1
            return Nested::class;
228
        }
229 6
        elseif ($format == 'expanded')
230
        {
231 1
            return Expanded::class;
232
        }
233 5
        elseif ($format == 'compact')
234
        {
235 1
            return Compact::class;
236
        }
237
238 4
        return Crunched::class;
239
    }
240
}
241