Skip to content
This repository has been archived by the owner on Nov 20, 2020. It is now read-only.

Commit

Permalink
Merge pull request #10 from yannickl88/master
Browse files Browse the repository at this point in the history
Made inline webpack tag
  • Loading branch information
linaori committed Sep 28, 2015
2 parents 5d472eb + d4671ac commit e8e3c4c
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 26 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ a much more elegant fashion. The syntax of this tag works like this:
{{ asset }}
{% endwebpack %}
```
Or if you want to have inline javascript code without including a file:
```twig
{% webpack <type: inline> %}
<javascript code>
{% endwebpack %}
```

If you want to include javascript files, simply do this:
```twig
Expand All @@ -130,6 +136,14 @@ If you want to include javascript files, simply do this:
<script src="{{ asset }}"></script>
{% endwebpack %}
```
Or the inline variant:
```twig
{% webpack inline %}
<script type="text/javascript">
console.log("Hello world!");
</script>
{% endwebpack %}
```

The same method can be applied for CSS files.
```twig
Expand Down
2 changes: 2 additions & 0 deletions src/Bundle/Resources/config/webpack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ services:
- '%kernel.root_dir%'
- '' # asset_path
- [] # bundles

hostnet_webpack.bridge.asset_dumper:
class: Hostnet\Component\Webpack\Asset\Dumper
arguments:
Expand All @@ -47,6 +48,7 @@ services:
arguments:
- '@hostnet_webpack.bridge.asset_tracker'
- '@twig'
- '%kernel.cache_dir%'

hostnet_webpack.bridge.asset_compiler:
class: Hostnet\Component\Webpack\Asset\Compiler
Expand Down
35 changes: 35 additions & 0 deletions src/Bundle/Twig/Node/WebpackInlineNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
namespace Hostnet\Bundle\WebpackBundle\Twig\Node;

/**
* @author Yannick de Lange <[email protected]>
*/
class WebpackInlineNode extends \Twig_Node
{
/**
* @param array $attributes An array of attributes (should not be nodes)
* @param int $lineno The line number
* @param string $tag The tag name associated with the Node
*/
public function __construct(array $attributes = array(), $lineno = 0, $tag = null)
{
parent::__construct([], $attributes, $lineno, $tag);
}

/** {@inheritdoc} */
public function compile(\Twig_Compiler $compiler)
{
if (false !== ($file = $this->getAttribute('js_file'))) {
$compiler
->write('echo ')
->string('<script type="text/javascript" src="'. $file .'"></script>')
->raw(";\n");
}
if (false !== ($file = $this->getAttribute('css_file'))) {
$compiler
->write('echo ')
->string('<link rel="stylesheet" href="'. $file .'">')
->raw(";\n");
}
}
}
53 changes: 50 additions & 3 deletions src/Bundle/Twig/Token/WebpackTokenParser.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
namespace Hostnet\Bundle\WebpackBundle\Twig\Token;

use Hostnet\Bundle\WebpackBundle\Twig\Node\WebpackInlineNode;
use Hostnet\Bundle\WebpackBundle\Twig\Node\WebpackNode;
use Hostnet\Bundle\WebpackBundle\Twig\TwigExtension;

Expand All @@ -24,8 +25,14 @@ class WebpackTokenParser implements \Twig_TokenParserInterface
*/
private $extension;

/**
* @var int
*/
private $inline_blocks = [];

/**
* @param TwigExtension $extension
* @param string $cache_dird
*/
public function __construct(TwigExtension $extension)
{
Expand All @@ -48,33 +55,73 @@ public function getTag()
public function parse(\Twig_Token $token)
{
$stream = $this->parser->getStream();
$files = [];
$lineno = $stream->getCurrent()->getLine();

// Export type: "js" or "css"
$export_type = $stream->expect(\Twig_Token::NAME_TYPE)->getValue();
if (! in_array($export_type, ['js', 'css'])) {
if (! in_array($export_type, ['js', 'css', 'inline'])) {
// This exception will include the template filename by itself.
throw new \Twig_Error_Syntax(sprintf(
'Expected export type "js" or "css", got "%s" at line %d.',
'Expected export type "inline", "js" or "css", got "%s" at line %d.',
$export_type,
$lineno
));
}

if ($export_type === "inline") {
return $this->parseInline($stream, $lineno);
}
return $this->parseType($stream, $lineno, $export_type);
}

private function parseType(\Twig_TokenStream $stream, $lineno, $export_type)
{
$files = [];
while (! $stream->isEOF() && ! $stream->getCurrent()->test(\Twig_Token::BLOCK_END_TYPE)) {
$asset = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();

if (false === ($file = $this->extension->webpackAsset($asset)[$export_type])) {
continue;
}
$files[] = $file;
}

$stream->expect(\Twig_Token::BLOCK_END_TYPE);

$body = $this->parser->subparse(function ($token) {
return $token->test(['end' . $this->getTag()]);
}, true);

$stream->expect(\Twig_Token::BLOCK_END_TYPE);

return new WebpackNode([$body], ['files' => $files], $lineno, $this->getTag());
}

private function parseInline(\Twig_TokenStream $stream, $lineno)
{
$stream->expect(\Twig_Token::BLOCK_END_TYPE);

$this->parser->subparse(function ($token) {
return $token->test(['end' . $this->getTag()]);
}, true);

$stream->expect(\Twig_Token::BLOCK_END_TYPE);

$file = $this->parser->getEnvironment()->getLoader()->getCacheKey($stream->getFilename());

if (!isset($this->inline_blocks[$file])) {
$this->inline_blocks[$file] = 0;
}

$file_name = md5($file . $this->inline_blocks[$file]). ".js";
$assets = $this->extension->webpackAsset('cache.' . $file_name);

$this->inline_blocks[$file]++;

return new WebpackInlineNode(
['js_file' => $assets['js'], 'css_file' => $assets['css']],
$lineno,
$this->getTag()
);
}
}
50 changes: 39 additions & 11 deletions src/Component/Asset/TwigParser.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
<?php
namespace Hostnet\Component\Webpack\Asset;

use Hostnet\Bundle\WebpackBundle\Twig\Token\JavascriptTokenParser;
use Hostnet\Bundle\WebpackBundle\Twig\Token\StylesheetTokenParser;
use Hostnet\Bundle\WebpackBundle\Twig\Token\WebpackTokenParser;
use Hostnet\Bundle\WebpackBundle\Twig\TwigExtension;

Expand All @@ -15,14 +13,16 @@ class TwigParser
{
private $tracker;
private $twig;
private $cache_dir;

/**
* @param Tracker $tracker
*/
public function __construct(Tracker $tracker, \Twig_Environment $twig)
public function __construct(Tracker $tracker, \Twig_Environment $twig, $cache_dir)
{
$this->tracker = $tracker;
$this->twig = $twig;
$this->tracker = $tracker;
$this->twig = $twig;
$this->cache_dir = $cache_dir;
}

/**
Expand All @@ -33,8 +33,9 @@ public function __construct(Tracker $tracker, \Twig_Environment $twig)
*/
public function findSplitPoints($template_file)
{
$stream = $this->twig->tokenize(file_get_contents($template_file));
$points = [];
$inline_blocks = 0;
$stream = $this->twig->tokenize(file_get_contents($template_file));
$points = [];

while (! $stream->isEOF() && $token = $stream->next()) {
// {{ webpack_asset(...) }}
Expand All @@ -47,10 +48,27 @@ public function findSplitPoints($template_file)
// {% webpack_javascripts %} and {% webpack_stylesheets %}
if ($token->test(\Twig_Token::BLOCK_START_TYPE) && $stream->getCurrent()->test(WebpackTokenParser::TAG_NAME)) {
$stream->next();
$stream->next();
while (! $stream->isEOF() && ! $stream->getCurrent()->test(\Twig_Token::BLOCK_END_TYPE)) {
$asset = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
$points[$asset] = $this->resolveAssetPath($asset, $template_file, $token);

if ($stream->getCurrent()->getValue() === 'inline') {
$stream->next();
$stream->next();

$file_name = md5($template_file . $inline_blocks). ".js";

file_put_contents(
$this->cache_dir . '/' . $file_name,
$this->stripScript($stream->getCurrent()->getValue())
);

$asset = $file_name;
$points['cache/'.$asset] = $this->resolveAssetPath($this->cache_dir . '/' . $asset, $template_file, $token);
$inline_blocks++;
} else {
$stream->next();
while (! $stream->isEOF() && ! $stream->getCurrent()->test(\Twig_Token::BLOCK_END_TYPE)) {
$asset = $stream->expect(\Twig_Token::STRING_TYPE)->getValue();
$points[$asset] = $this->resolveAssetPath($asset, $template_file, $token);
}
}
}
}
Expand Down Expand Up @@ -105,4 +123,14 @@ private function expect($filename, \Twig_Token $token, $type, $value = null)
));
}
}

private function stripScript($str)
{
$matches = [];
if (preg_match('/^\s*<script(\s.+?)?>(.*)<\/script>\s*$/s', $str, $matches)) {
return $matches[2];
}

return $str;
}
}
35 changes: 23 additions & 12 deletions test/Component/Asset/TwigParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,43 @@ class TwigParserTest extends \PHPUnit_Framework_TestCase
*/
private $path;

/**
* @var string
*/
private $cache_dir;

/** {@inheritdoc} */
protected function setUp()
{
$this->tracker = $this->getMockBuilder(Tracker::class)->disableOriginalConstructor()->getMock();
$this->twig = new \Twig_Environment();
$this->path = realpath(__DIR__ . '/../../Fixture');
$this->tracker = $this->getMockBuilder(Tracker::class)->disableOriginalConstructor()->getMock();
$this->twig = new \Twig_Environment();
$this->path = realpath(__DIR__ . '/../../Fixture');
$this->cache_dir = '/tmp';
}

public function testParseValid()
{
// Call count expectations:
// 1: webpack_asset.js
// 2: webpack_asset.css
// 3: {% webpack_javascripts %}
// 4: {% webpack_javascripts %}
// 5: {% webpack_stylesheets %}
$this->tracker->expects($this->exactly(5))->method('resolveResourcePath')->willReturn('foobar');
// 3: {% webpack js %}
// 4: {% webpack js %}
// 5: {% webpack css %}
// 6: {% webpack inline %}
// 7: {% webpack inline %}
$this->tracker->expects($this->exactly(7))->method('resolveResourcePath')->willReturn('foobar');

$parser = new TwigParser($this->tracker, $this->twig);
$points = ($parser->findSplitPoints($this->path . '/Resources/template.html.twig'));
$parser = new TwigParser($this->tracker, $this->twig, $this->cache_dir);
$file = $this->path . '/Resources/template.html.twig';
$points = ($parser->findSplitPoints($file));

$this->assertCount(4, $points);
$this->assertCount(6, $points);
$this->assertArrayHasKey('@BarBundle/app.js', $points);
$this->assertArrayHasKey('@BarBundle/app2.js', $points);
$this->assertArrayHasKey('@BarBundle/app3.js', $points);
$this->assertArrayHasKey('@BarBundle/app4.js', $points);
$this->assertArrayHasKey('cache/' . md5($file . 0) . '.js', $points);
$this->assertArrayHasKey('cache/' . md5($file . 1) . '.js', $points);

$this->assertContains('foobar', $points);
}
Expand All @@ -60,7 +71,7 @@ public function testParseError()
{
$this->tracker->expects($this->never())->method('resolveResourcePath');

$parser = new TwigParser($this->tracker, $this->twig);
$parser = new TwigParser($this->tracker, $this->twig, $this->cache_dir);
$parser->findSplitPoints($this->path . '/Resources/template_parse_error.html.twig');
}

Expand All @@ -72,7 +83,7 @@ public function testResolveError()
{
$this->tracker->expects($this->once())->method('resolveResourcePath')->willReturn(false);

$parser = new TwigParser($this->tracker, $this->twig);
$parser = new TwigParser($this->tracker, $this->twig, $this->cache_dir);
$parser->findSplitPoints($this->path . '/Resources/template.html.twig');
}
}
10 changes: 10 additions & 0 deletions test/Fixture/Resources/template.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,13 @@
{% webpack css '@BarBundle/app4.js' %}
<link rel="stylesheet" href="{{ asset }}">
{% endwebpack %}

{# and webpack inline #}
{% webpack inline %}
<script type="text/javascript">
console.log("HENK");
</script>
{% endwebpack %}
{% webpack inline %}
console.log("HENK");
{% endwebpack %}

0 comments on commit e8e3c4c

Please sign in to comment.