2 namespace Robo\Task\Assets;
5 use Robo\Task\BaseTask;
8 * Minifies asset file (CSS or JS).
12 * $this->taskMinify( 'web/assets/theme.css' )
16 * Please install additional dependencies to use:
19 * "patchwork/jsqueeze": "~1.0",
20 * "natxet/CssMin": "~3.0"
23 class Minify extends BaseTask
28 protected $types = ['css', 'js'];
48 protected $squeezeOptions = [
50 'keepImportantComments' => true,
51 'specialVarRx' => false,
55 * Constructor. Accepts asset file path or string source.
57 * @param string $input
59 public function __construct($input)
61 if (file_exists($input)) {
62 $this->fromFile($input);
66 $this->fromText($input);
70 * Sets destination. Tries to guess type from it.
76 public function to($dst)
80 if (!empty($this->dst) && empty($this->type)) {
81 $this->type($this->getExtension($this->dst));
88 * Sets type with validation.
90 * @param string $type css|js
94 public function type($type)
96 $type = strtolower($type);
98 if (in_array($type, $this->types)) {
106 * Sets text from string source.
108 * @param string $text
112 protected function fromText($text)
114 $this->text = (string)$text;
121 * Sets text from asset file path. Tries to guess type and set default destination.
123 * @param string $path
127 protected function fromFile($path)
129 $this->text = file_get_contents($path);
132 $this->type($this->getExtension($path));
134 if (empty($this->dst) && !empty($this->type)) {
135 $ext_length = strlen($this->type) + 1;
136 $this->dst = substr($path, 0, -$ext_length) . '.min.' . $this->type;
143 * Gets file extension from path.
145 * @param string $path
149 protected function getExtension($path)
151 return pathinfo($path, PATHINFO_EXTENSION);
155 * Minifies and returns text.
157 * @return string|bool
159 protected function getMinifiedText()
161 switch ($this->type) {
163 if (!class_exists('\CssMin')) {
164 return Result::errorMissingPackage($this, 'CssMin', 'natxet/CssMin');
167 return \CssMin::minify($this->text);
171 if (!class_exists('\JSqueeze') && !class_exists('\Patchwork\JSqueeze')) {
172 return Result::errorMissingPackage($this, 'Patchwork\JSqueeze', 'patchwork/jsqueeze');
175 if (class_exists('\JSqueeze')) {
176 $jsqueeze = new \JSqueeze();
178 $jsqueeze = new \Patchwork\JSqueeze();
181 return $jsqueeze->squeeze(
183 $this->squeezeOptions['singleLine'],
184 $this->squeezeOptions['keepImportantComments'],
185 $this->squeezeOptions['specialVarRx']
194 * Single line option for the JS minimisation.
196 * @param bool $singleLine
200 public function singleLine($singleLine)
202 $this->squeezeOptions['singleLine'] = (bool)$singleLine;
207 * keepImportantComments option for the JS minimisation.
209 * @param bool $keepImportantComments
213 public function keepImportantComments($keepImportantComments)
215 $this->squeezeOptions['keepImportantComments'] = (bool)$keepImportantComments;
220 * specialVarRx option for the JS minimisation.
222 * @param bool $specialVarRx
226 public function specialVarRx($specialVarRx)
228 $this->squeezeOptions['specialVarRx'] = (bool)$specialVarRx;
235 public function __toString()
237 return (string) $this->getMinifiedText();
243 public function run()
245 if (empty($this->type)) {
246 return Result::error($this, 'Unknown asset type.');
249 if (empty($this->dst)) {
250 return Result::error($this, 'Unknown file destination.');
253 if (file_exists($this->dst) && !is_writable($this->dst)) {
254 return Result::error($this, 'Destination already exists and cannot be overwritten.');
257 $size_before = strlen($this->text);
258 $minified = $this->getMinifiedText();
260 if ($minified instanceof Result) {
262 } elseif (false === $minified) {
263 return Result::error($this, 'Minification failed.');
266 $size_after = strlen($minified);
268 // Minification did not reduce file size, so use original file.
269 if ($size_after > $size_before) {
270 $minified = $this->text;
271 $size_after = $size_before;
274 $dst = $this->dst . '.part';
275 $write_result = file_put_contents($dst, $minified);
277 if (false === $write_result) {
279 return Result::error($this, 'File write failed.');
281 // Cannot be cross-volume; should always succeed.
282 @rename($dst, $this->dst);
283 if ($size_before === 0) {
284 $minified_percent = 0;
286 $minified_percent = number_format(100 - ($size_after / $size_before * 100), 1);
288 $this->printTaskSuccess('Wrote {filepath}', ['filepath' => $this->dst]);
290 'bytes' => $this->formatBytes($size_after),
291 'reduction' => $this->formatBytes(($size_before - $size_after)),
292 'percentage' => $minified_percent,
294 $this->printTaskSuccess('Wrote {bytes} (reduced by {reduction} / {percentage})', $context);
295 return Result::success($this, 'Asset minified.');