Skip to content

Commit b595f6c

Browse files
authored
Hooks (#5)
* #3 Implements hooks * Align array values * Test public trigger method call
1 parent d470741 commit b595f6c

File tree

5 files changed

+239
-34
lines changed

5 files changed

+239
-34
lines changed

src/Pug/Keyword/Minify.php

Lines changed: 138 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ class Minify
3939
*/
4040
protected $css;
4141

42+
/**
43+
* @var array
44+
*/
45+
protected $events;
46+
4247
/**
4348
* @var Pug|Jade
4449
*/
@@ -135,74 +140,117 @@ protected function needUpate($source, $destination)
135140
protected function parseScript($path)
136141
{
137142
list($extension, $path, $source, $destination) = $this->getPathInfo($path, 'js');
138-
if ($this->needUpate($source, $destination)) {
139-
switch ($extension) {
143+
$params = (object) array(
144+
'extension' => $extension,
145+
'type' => 'script',
146+
'path' => $path,
147+
'source' => $source,
148+
'destination' => $destination,
149+
);
150+
if ($this->needUpate($params->source, $params->destination)) {
151+
switch ($params->extension) {
152+
case 'jsxp':
153+
$params->contents = preg_replace_callback('/(?<!\s)(\s*)::`(([^`]+|(?<!`)`(?!`))*?)`(?!`)/', array($this, 'parsePugInJsx'), file_get_contents($params->source));
154+
break;
155+
case 'cofp':
156+
$params->contents = preg_replace_callback('/(?<!\s)(\s*)::"""([\s\S]*?)"""/', array($this, 'parsePugInCoffee'), file_get_contents($params->source));
157+
break;
158+
}
159+
$this->trigger('pre-parse', $params);
160+
switch ($params->extension) {
140161
case 'jsxp':
141-
$contents = preg_replace_callback('/(?<!\s)(\s*)::`(([^`]+|(?<!`)`(?!`))*?)`(?!`)/', array($this, 'parsePugInJsx'), file_get_contents($source));
142-
$this->writeWith(new React($contents), $destination);
162+
$this->writeWith(new React($params->contents), $destination);
143163
break;
144164
case 'jsx':
145-
$this->writeWith(new React($source), $destination);
165+
$this->writeWith(new React($params->source), $params->destination);
146166
break;
147167
case 'cofp':
148-
$contents = preg_replace_callback('/(?<!\s)(\s*)::"""([\s\S]*?)"""/', array($this, 'parsePugInCoffee'), file_get_contents($source));
149-
$this->writeWith(new Coffeescript($contents), $destination);
168+
$this->writeWith(new Coffeescript($params->contents), $params->destination);
150169
break;
151170
case 'coffee':
152-
$this->writeWith(new Coffeescript($source), $destination);
171+
$this->writeWith(new Coffeescript($params->source), $params->destination);
153172
break;
154173
default:
155-
copy($source, $destination);
174+
copy($params->source, $params->destination);
156175
}
176+
$this->trigger('post-parse', $params);
157177
}
158178
if ($this->dev) {
159-
return $path . '?' . time();
179+
return $params->path . '?' . time();
160180
}
161181
$this->js[] = $destination;
162182
}
163183

164184
protected function parseStyle($path)
165185
{
166186
list($extension, $path, $source, $destination) = $this->getPathInfo($path, 'css');
167-
if ($this->needUpate($source, $destination)) {
168-
switch ($extension) {
187+
$params = (object) array(
188+
'extension' => $extension,
189+
'type' => 'style',
190+
'path' => $path,
191+
'source' => $source,
192+
'destination' => $destination,
193+
);
194+
if ($this->needUpate($params->source, $params->destination)) {
195+
$this->trigger('pre-parse', $params);
196+
switch ($params->extension) {
169197
case 'styl':
170-
$this->writeWith(new Stylus($source), $destination);
198+
$this->writeWith(new Stylus($params->source), $params->destination);
171199
break;
172200
case 'less':
173-
$this->writeWith(new Less($source), $destination);
201+
$this->writeWith(new Less($params->source), $params->destination);
174202
break;
175203
default:
176-
copy($source, $destination);
204+
copy($params->source, $params->destination);
177205
}
206+
$this->trigger('post-parse', $params);
178207
}
179208
if ($this->dev) {
180-
return $path . '?' . time();
209+
return $params->path . '?' . time();
181210
}
182-
$this->css[] = $destination;
211+
$this->css[] = $params->destination;
183212
}
184213

185-
protected function uglify($language, $path)
214+
protected function uglify(&$params)
186215
{
216+
$params->concat = true;
217+
$params->minify = true;
218+
$language = $params->language;
219+
$path = $params->path;
187220
$outputFile = $language . '/' . $path . '.min.' . $language;
188221
$uglify = new Uglify($this->$language);
189-
$path = $this->path($this->outputDirectory, $outputFile);
190-
$uglify->write($this->prepareDirectory($path));
191-
192-
return $outputFile;
222+
$uglify->setModeFromPath($outputFile);
223+
$params->outputDirectory = $this->outputDirectory;
224+
$params->outputFile = $outputFile;
225+
$params->content = $uglify->getResult();
226+
$this->trigger('pre-write', $params);
227+
$path = $this->path($params->outputDirectory, $params->outputFile);
228+
$path = $this->prepareDirectory($path);
229+
$params->outputPath = $path;
230+
file_put_contents($path, $params->content);
231+
$this->trigger('post-write', $params);
193232
}
194233

195-
protected function concat($language, $path)
234+
protected function concat(&$params)
196235
{
236+
$params->concat = true;
237+
$params->minify = false;
238+
$language = $params->language;
239+
$path = $params->path;
197240
$outputFile = $language . '/' . $path . '.' . $language;
198241
$output = '';
199242
foreach ($this->$language as $path) {
200243
$output .= file_get_contents($path) . "\n";
201244
}
202-
$path = $this->path($this->outputDirectory, $outputFile);
203-
file_put_contents($this->prepareDirectory($path), $output);
204-
205-
return $outputFile;
245+
$params->outputDirectory = $this->outputDirectory;
246+
$params->outputFile = $outputFile;
247+
$params->content = $output;
248+
$this->trigger('pre-write', $params);
249+
$path = $this->path($params->outputDirectory, $params->outputFile);
250+
$path = $this->prepareDirectory($path);
251+
$params->outputPath = $path;
252+
file_put_contents($path, $params->content);
253+
$this->trigger('post-write', $params);
206254
}
207255

208256
protected function getOption($option, $defaultValue = null)
@@ -226,6 +274,37 @@ protected function initializeRendering()
226274
$this->css = array();
227275
}
228276

277+
public function on($event, $action)
278+
{
279+
$event = strtolower(str_replace('-', '', $event));
280+
281+
if (!isset($this->events[$event])) {
282+
$this->events[$event] = array();
283+
}
284+
285+
$this->events[$event][] = $action;
286+
}
287+
288+
public function trigger($event, &$params = null)
289+
{
290+
$event = strtolower(str_replace('-', '', $event));
291+
292+
if (!is_object($params)) {
293+
$params = (object) (is_array($params) ? $params : array());
294+
}
295+
296+
if (isset($this->events[$event])) {
297+
foreach ($this->events[$event] as $action) {
298+
if (is_callable($action)) {
299+
$newParams = call_user_func($action, $params, $event, $this);
300+
if (is_object($newParams)) {
301+
$params = $newParams;
302+
}
303+
}
304+
}
305+
}
306+
}
307+
229308
public function linkExtractor($href, $rel)
230309
{
231310
if ($href && $rel === 'stylesheet') {
@@ -259,27 +338,52 @@ public function __invoke($arguments, $block, $keyword)
259338
return '';
260339
}
261340

262-
$extractor = new BlockExtractor($block);
341+
$renderParams = (object) array(
342+
'minify' => !in_array(strtolower(str_replace('-', '', $keyword)), array('concat', 'concatto')),
343+
'keyword' => $keyword,
344+
'arguments' => $arguments,
345+
'block' => $block,
346+
);
347+
$this->trigger('pre-render', $renderParams);
348+
349+
$extractor = new BlockExtractor($renderParams->block);
263350
$extractor->registerTagExtractor('link', array($this, 'linkExtractor'), 'href', 'rel');
264351
$extractor->registerTagExtractor('script', array($this, 'scriptExtractor'), 'src');
265352
$extractor->extract();
266353

267354
$html = '';
268355

269356
if (count($this->js) || count($this->css)) {
270-
$compilation = in_array(strtolower(str_replace('-', '', $keyword)), array('concat', 'concatto'))
271-
? 'concat'
272-
: 'uglify';
357+
$compilation = $renderParams->minify ? 'uglify' : 'concat';
358+
$event = $renderParams->minify ? 'minify' : 'concat';
273359

274360
if (count($this->js)) {
275-
$html .= '<script src="' . $this->$compilation('js', $arguments) . '"></script>';
361+
$params = (object) array(
362+
'language' => 'js',
363+
'path' => $renderParams->arguments,
364+
);
365+
$this->trigger('pre-' . $event, $params);
366+
$this->$compilation($params);
367+
$this->trigger('post-' . $event, $params);
368+
369+
$html .= '<script src="' . $params->outputFile . '"></script>';
276370
}
277371

278372
if (count($this->css)) {
279-
$html .= '<link rel="stylesheet" href="' . $this->$compilation('css', $arguments) . '">';
373+
$params = (object) array(
374+
'language' => 'css',
375+
'path' => $renderParams->arguments,
376+
);
377+
$this->trigger('pre-' . $event, $params);
378+
$this->$compilation($params);
379+
$this->trigger('post-' . $event, $params);
380+
381+
$html .= '<link rel="stylesheet" href="' . $params->outputFile . '">';
280382
}
281383
}
384+
$renderParams->html = $html;
385+
$this->trigger('post-render', $renderParams);
282386

283-
return $html;
387+
return $renderParams->html;
284388
}
285389
}

tests/MinifyTest.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,4 +217,88 @@ public function testMultipleAssetDirectories()
217217

218218
$this->cleanTempDir();
219219
}
220+
221+
/**
222+
* @group hooks
223+
*/
224+
public function testHooks()
225+
{
226+
$this->cleanTempDir();
227+
$outputDirectory = $this->getTempDir();
228+
229+
$pug = new Pug(array(
230+
'prettyprint' => true,
231+
'assetDirectory' => __DIR__,
232+
'outputDirectory' => $outputDirectory,
233+
'environment' => 'production',
234+
));
235+
$minify = new Minify($pug);
236+
$minify->on('pre-write', function ($params) {
237+
$params->content = str_replace('42', '43', $params->content);
238+
239+
return $params;
240+
});
241+
$minify->on('post-parse', function ($params) {
242+
$contents = file_get_contents($params->destination);
243+
$contents = str_replace('Hello', 'Bye', $contents);
244+
file_put_contents($params->destination, $contents);
245+
});
246+
$minifications = 0;
247+
$customCalled = false;
248+
$minify->on('pre-minify', function ($params, $event, $ref) use (&$minifications, &$customCalled) {
249+
if ($ref instanceof Minify) {
250+
$minifications++;
251+
}
252+
if (isset($params->custom)) {
253+
$customCalled = true;
254+
}
255+
});
256+
$minify->on('post-minify', function ($params) {
257+
$params->outputPath .= '.copy';
258+
259+
return $params;
260+
});
261+
$minify->on('pre-render', function ($params) {
262+
if ($params->arguments === 'top') {
263+
$params->arguments = 'up';
264+
265+
return $params;
266+
}
267+
});
268+
$minify->on('post-render', function ($params) {
269+
$params->html .= "\n<!-- Bye -->";
270+
271+
return $params;
272+
});
273+
$pug->addKeyword('minify', $minify);
274+
$html = static::simpleHtml($pug->render(__DIR__ . '/test-minify.pug'));
275+
$expected = static::simpleHtml(file_get_contents(__DIR__ . '/hooks.html'));
276+
277+
$this->assertSame($expected, $html);
278+
$this->assertSame(3, $minifications, 'CSS and JS on the top + JS on the bottom should trigger the pre-minify event 3 times.');
279+
280+
$file = $outputDirectory . '/js/up.min.js';
281+
$javascript = static::fileGetAsset($file);
282+
unlink($file);
283+
$this->assertSame(static::fileGetAsset(__DIR__ . '/js/up.min.js.copy'), $javascript);
284+
285+
$file = $outputDirectory . '/js/bottom.min.js';
286+
$javascript = static::fileGetAsset($file);
287+
unlink($file);
288+
$this->assertSame(str_replace('Hello', 'Bye', static::fileGetAsset(__DIR__ . '/js/bottom.min.js')), $javascript);
289+
290+
$file = $outputDirectory . '/css/up.min.css';
291+
$style = static::fileGetAsset($file);
292+
unlink($file);
293+
$this->assertSame(str_replace('Hello', 'Bye', static::fileGetAsset(__DIR__ . '/css/up.min.css')), $style);
294+
295+
$this->cleanTempDir();
296+
297+
$params = array(
298+
'custom' => 'foobar',
299+
);
300+
$this->assertFalse($customCalled, 'Hooks should be callable from outside the rendring.');
301+
$minify->trigger('pre-minify', $params);
302+
$this->assertTrue($customCalled, 'Hooks should be callable from outside the rendring.');
303+
}
220304
}

tests/css/up.min.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/hooks.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Foo</title>
5+
<script src="js/up.min.js"></script>
6+
<link rel="stylesheet" href="css/up.min.css">
7+
<!-- Bye -->
8+
<meta name="foo" content="bar">
9+
</head>
10+
<body>
11+
<h1>Foobar</h1>
12+
<script src="js/bottom.min.js"></script>
13+
<!-- Bye -->
14+
</body>
15+
</html>

tests/js/up.min.js.copy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log("Bye");var cubes,list,math,num,number,opposite,race,square,slice=[].slice;number=43;opposite=true;if(opposite){number=-43}square=function(x){return x*x};list=[1,2,3,4,5];math={root:Math.sqrt,square:square,cube:function(x){return x*square(x)}};race=function(){var runners,winner;winner=arguments[0],runners=2<=arguments.length?slice.call(arguments,1):[];return print(winner,runners)};if(typeof elvis!=="undefined"&&elvis!==null){alert("I knew it!")}cubes=function(){var i,len,results;results=[];for(i=0,len=list.length;i<len;i++){num=list[i];results.push(math.cube(num))}return results}();var dom=React.createElement;ReactDOM.render(dom("h1",null,"Bye,",dom("em",null,"world!")," ",dom("button",{className:"btn btn-primary"},"OK")),document.getElementById("main"));

0 commit comments

Comments
 (0)