Skip to content

Commit 71776d8

Browse files
authored
Merge pull request #10 from pug-php/feature/dedicated-parsers
Implement relative path support
2 parents 978ed84 + 9400199 commit 71776d8

15 files changed

Lines changed: 196 additions & 14 deletions

File tree

‎.github/workflows/tests.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
strategy:
1414
fail-fast: false
1515
matrix:
16-
php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
16+
php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
1717
setup: ['lowest', 'stable']
1818

1919
name: PHP ${{ matrix.php }} - ${{ matrix.setup }}

‎composer.json‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@
2020
},
2121
"require-dev": {
2222
"composer/composer": "^1.10.23 || ^2.1.9",
23-
"phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^8.5.15"
23+
"phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^8.5.31"
2424
},
2525
"autoload": {
2626
"psr-0": {
2727
"Pug\\": "src/"
2828
}
29+
},
30+
"config": {
31+
"allow-plugins": {
32+
"nodejs-php-fallback/nodejs-php-fallback": true
33+
}
2934
}
3035
}

‎src/Pug/Keyword/Minify.php‎

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,43 @@ protected function writeWith($parser, $path)
9898
$parser->write($path);
9999
}
100100

101-
protected function getPathInfo($path, $newExtension)
101+
/**
102+
* @param string $path
103+
* @param string $newExtension
104+
* @param string|null $relativePath
105+
*
106+
* @return string[]
107+
*/
108+
protected function getPathInfo($path, $newExtension, $relativePath = null)
102109
{
103-
$source = new Path($this->assetDirectory, $path);
110+
$source = $this->getSourcePath($path, $relativePath);
104111
$extension = pathinfo($path, PATHINFO_EXTENSION);
105112
$path = substr($path, 0, -strlen($extension)) . $newExtension;
106113
$destination = $this->prepareDirectory(new Path($this->outputDirectory, $path));
107114

108115
return array($extension, $path, (string) $source, $destination);
109116
}
110117

118+
/**
119+
* @param string $path
120+
* @param string|null $relativePath
121+
*
122+
* @return Path
123+
*/
124+
protected function getSourcePath($path, $relativePath = null)
125+
{
126+
if ($relativePath) {
127+
$relativeSource = new Path($path);
128+
$relativeSource = $relativeSource->relativeTo($relativePath);
129+
130+
if (file_exists((string) $relativeSource)) {
131+
return $relativeSource;
132+
}
133+
}
134+
135+
return new Path($this->assetDirectory, $path);
136+
}
137+
111138
protected function needUpdate($source, $destination)
112139
{
113140
return !$this->dev || !file_exists($destination) || filemtime($source) >= filemtime($destination);
@@ -134,9 +161,9 @@ protected function prepareSource($params)
134161
}
135162
}
136163

137-
protected function parseScript($path)
164+
protected function parseScript($path, $relativePath = null)
138165
{
139-
list($extension, $path, $source, $destination) = $this->getPathInfo($path, 'js');
166+
list($extension, $path, $source, $destination) = $this->getPathInfo($path, 'js', $relativePath);
140167
$params = (object) array(
141168
'extension' => $extension,
142169
'type' => 'script',
@@ -162,9 +189,9 @@ protected function parseScript($path)
162189
return null;
163190
}
164191

165-
protected function parseStyle($path)
192+
protected function parseStyle($path, $relativePath = null)
166193
{
167-
list($extension, $path, $source, $destination) = $this->getPathInfo($path, 'css');
194+
list($extension, $path, $source, $destination) = $this->getPathInfo($path, 'css', $relativePath);
168195
$params = (object) array(
169196
'extension' => $extension,
170197
'type' => 'style',
@@ -314,10 +341,10 @@ private function triggerEventAction($action, $event, &$params = null)
314341
}
315342
}
316343

317-
public function linkExtractor($href, $rel)
344+
public function linkExtractor($href, $rel, $relativePath = null)
318345
{
319346
if ($href && $rel === 'stylesheet') {
320-
$path = $this->parseStyle($href);
347+
$path = $this->parseStyle($href, $relativePath);
321348
if ($this->dev && $path) {
322349
return array(
323350
'href' => $path,
@@ -326,10 +353,10 @@ public function linkExtractor($href, $rel)
326353
}
327354
}
328355

329-
public function scriptExtractor($src)
356+
public function scriptExtractor($src, $relativePath = null)
330357
{
331358
if ($src) {
332-
$path = $this->parseScript($src);
359+
$path = $this->parseScript($src, $relativePath);
333360
if ($this->dev && $path) {
334361
return array(
335362
'src' => $path,

‎src/Pug/Keyword/Minify/BlockExtractor.php‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
use Jade\Nodes\Node;
77
use Jade\Nodes\Tag;
88
use Phug\Formatter\Element\MarkupElement;
9+
use Phug\Formatter\ElementInterface;
10+
use Phug\Parser\NodeInterface;
11+
use Phug\Util\SourceLocationInterface;
912

1013
class BlockExtractor
1114
{
@@ -85,26 +88,54 @@ protected function setPug2NodeValue($node, $key, $value)
8588
protected function processNode($node)
8689
{
8790
$name = method_exists($node, 'getName') ? $node->getName() : $node->name;
91+
8892
if (!isset($this->extractors[$name])) {
8993
return false;
9094
}
9195

9296
list($extractor, $attributes) = $this->extractors[$name];
9397
$arguments = array();
98+
9499
foreach ($attributes as $attribute) {
95100
$arguments[] = $this->getNodeValue($node, $attribute);
96101
}
102+
103+
$path = $this->getNodePath($node);
104+
105+
if ($path) {
106+
$arguments[] = $path;
107+
}
108+
97109
$newAttributes = call_user_func_array($extractor, $arguments);
110+
98111
if (!$newAttributes) {
99112
return true;
100113
}
114+
101115
foreach ($newAttributes as $name => $value) {
102116
$this->setNodeValue($node, $name, $value);
103117
}
104118

105119
return false;
106120
}
107121

122+
protected function getNodePath($node)
123+
{
124+
if ($node instanceof ElementInterface) {
125+
$node = $node->getOriginNode();
126+
}
127+
128+
if (!($node instanceof NodeInterface)) {
129+
return null;
130+
}
131+
132+
$location = $node->getSourceLocation();
133+
134+
return $location instanceof SourceLocationInterface
135+
? $location->getPath()
136+
: null;
137+
}
138+
108139
protected function scrollBlock($block)
109140
{
110141
if (isset($block->nodes) && is_array($block->nodes)) {

‎src/Pug/Keyword/Minify/Path.php‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@ public function __construct()
1111
$this->parts = array_filter(func_get_args());
1212
}
1313

14+
/**
15+
* @param string $relativePath
16+
*
17+
* @return static
18+
*/
19+
public function relativeTo($relativePath)
20+
{
21+
$path = new static(dirname($relativePath));
22+
$path->parts = array_merge($path->parts, $this->parts);
23+
24+
return $path;
25+
}
26+
1427
public function __toString()
1528
{
1629
$parts = $this->parts;

‎tests/Minify/BlockExtractorTest.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,14 @@ public function testSetNodeValue()
6666

6767
self::assertSame("'after'", $fakeNode->getBarValue());
6868
}
69+
70+
public function testPathForWrongNode()
71+
{
72+
require_once __DIR__ . '/Fixture/FakeNode.php';
73+
require_once __DIR__ . '/Fixture/BlockExtractorTester.php';
74+
75+
$extractor = new BlockExtractorTester((object) array());
76+
77+
self::assertNull($extractor->getPathForWrongNode());
78+
}
6979
}

‎tests/Minify/Fixture/BlockExtractorTester.php‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,9 @@ public function changeValue($node)
2424
{
2525
$this->setNodeValue($node, 'bar', 'after');
2626
}
27+
28+
public function getPathForWrongNode()
29+
{
30+
return $this->getNodePath(new \stdClass());
31+
}
2732
}

‎tests/MinifyTest.php‎

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,11 @@ public function testProductionWithMinify()
187187
$this->cleanTempDir();
188188
}
189189

190-
public function testProductionWithConcat()
190+
/**
191+
* @testWith ["test-concat"]
192+
* ["relative/test-root"]
193+
*/
194+
public function testProductionWithConcat($path)
191195
{
192196
$this->cleanTempDir();
193197
$outputDirectory = $this->getTempDir();
@@ -201,7 +205,7 @@ public function testProductionWithConcat()
201205
));
202206
$minify = new Minify($pug);
203207
$pug->addKeyword('concat', $minify);
204-
$html = static::simpleHtml($this->renderFile($pug, __DIR__ . '/test-concat.pug'));
208+
$html = static::simpleHtml($this->renderFile($pug, __DIR__ . '/' . $path . '.pug'));
205209
$expected = static::simpleHtml(file_get_contents(__DIR__ . '/prod-concat.html'));
206210

207211
self::assertSimilar($expected, $html);
@@ -261,6 +265,45 @@ public function testMultipleAssetDirectories()
261265
$this->cleanTempDir();
262266
}
263267

268+
public function testRelativePath()
269+
{
270+
if (!class_exists('Phug\\Util\\SourceLocationInterface')) {
271+
self::markTestSkipped('SourceLocationInterface needed to calculate relative path');
272+
}
273+
274+
$this->cleanTempDir();
275+
$outputDirectory = $this->getTempDir();
276+
277+
$pug = new Pug(array(
278+
'environment' => 'production',
279+
'prettyprint' => true,
280+
'assetDirectory' => array(dirname(__DIR__), __DIR__, __DIR__ . '/js'),
281+
'outputDirectory' => $outputDirectory,
282+
'execution_max_time' => 300000,
283+
));
284+
$minify = new Minify($pug);
285+
$pug->addKeyword('minify', $minify);
286+
$html = static::simpleHtml($this->renderFile($pug, __DIR__ . '/relative/test-inside.pug'));
287+
$expected = static::simpleHtml(file_get_contents(__DIR__ . '/relative/test-inside.html'));
288+
289+
self::assertSimilar($expected, $html);
290+
291+
$file = $outputDirectory . '/js/top.min.js';
292+
$javascript = static::fileGetAsset($file);
293+
unlink($file);
294+
self::assertSimilar(
295+
"console.log(\"sub/test\");console.log(\"sub/test2\");",
296+
str_replace(array("\n", "\r"), '', $javascript)
297+
);
298+
299+
$file = $outputDirectory . '/css/top.min.css';
300+
$style = static::fileGetAsset($file);
301+
unlink($file);
302+
self::assertSimilar('body{#foo { color:red}} body{.foo { color:lime}', trim($style));
303+
304+
$this->cleanTempDir();
305+
}
306+
264307
/**
265308
* @group hooks
266309
*/

‎tests/relative/sub/test.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('sub/test');

‎tests/relative/sub/test.scss‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
body {
2+
#foo {
3+
color: red;
4+
}
5+
}

0 commit comments

Comments
 (0)