From 7d1225224e4e298f4b3b83e368fc178f3c12185d Mon Sep 17 00:00:00 2001 From: benatespina Date: Thu, 29 Dec 2016 15:04:02 +0100 Subject: [PATCH 1/4] Extracted the js and css logic to different parsers --- .gitignore | 3 +- ...TokenParser.php => EntryTokenParserCss.php | 29 +++---- EntryTokenParserJs.php | 79 +++++++++++++++++++ README.md | 41 ++++++---- WebpackExtension.php | 22 ++++-- composer.json | 6 +- 6 files changed, 143 insertions(+), 37 deletions(-) rename EntryTokenParser.php => EntryTokenParserCss.php (60%) create mode 100644 EntryTokenParserJs.php diff --git a/.gitignore b/.gitignore index 57872d0..ff72e2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/vendor/ +/composer.lock +/vendor diff --git a/EntryTokenParser.php b/EntryTokenParserCss.php similarity index 60% rename from EntryTokenParser.php rename to EntryTokenParserCss.php index d9f4b5c..44dbf2e 100644 --- a/EntryTokenParser.php +++ b/EntryTokenParserCss.php @@ -5,7 +5,7 @@ /** * EntryTokenParser. */ -class EntryTokenParser extends \Twig_TokenParser +class EntryTokenParserCss extends \Twig_TokenParser { /** * @var string @@ -45,24 +45,25 @@ public function parse(\Twig_Token $token) $stream->expect(\Twig_Token::BLOCK_END_TYPE); if (!file_exists($this->manifestFile)) { - throw new \Twig_Error_Loader('Webpack manifest file not exists.', $token->getLine(), $stream->getFilename()); + throw new \Twig_Error_Loader( + 'Webpack manifest file not exists.', + $token->getLine(), + $stream->getFilename() + ); } $manifest = json_decode(file_get_contents($this->manifestFile), true); $assets = []; - if (isset($manifest[$entryName.'.js']) || isset($manifest[$entryName.'.css'])) { - if (isset($manifest[$entryName.'.css'])) { - $entryPath = $this->publicPath.$manifest[$entryName.'.css']; - $assets[] = ''; - } - - if (isset($manifest[$entryName.'.js'])) { - $entryPath = $this->publicPath.$manifest[$entryName.'.js']; - $assets[] = ''; - } + if (isset($manifest[$entryName . '.css'])) { + $entryPath = $this->publicPath . $manifest[$entryName . '.css']; + $assets[] = ''; } else { - throw new \Twig_Error_Loader('Webpack entry '.$entryName.' not exists.', $token->getLine(), $stream->getFilename()); + throw new \Twig_Error_Loader( + 'Webpack entry ' . $entryName . ' not exists.', + $token->getLine(), + $stream->getFilename() + ); } return new \Twig_Node_Text(implode('', $assets), $token->getLine()); @@ -73,6 +74,6 @@ public function parse(\Twig_Token $token) */ public function getTag() { - return 'webpack_entry'; + return 'webpack_entry_css'; } } diff --git a/EntryTokenParserJs.php b/EntryTokenParserJs.php new file mode 100644 index 0000000..3ece4a9 --- /dev/null +++ b/EntryTokenParserJs.php @@ -0,0 +1,79 @@ +manifestFile = $manifestFile; + $this->publicPath = $publicPath; + } + + /** + * Parses a token and returns a node. + * + * @param Twig_Token $token A Twig_Token instance + * + * @return Twig_Node_Text + * + * @throws Twig_Error_Loader + */ + public function parse(\Twig_Token $token) + { + $stream = $this->parser->getStream(); + $entryName = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); + $stream->expect(\Twig_Token::BLOCK_END_TYPE); + + if (!file_exists($this->manifestFile)) { + throw new \Twig_Error_Loader( + 'Webpack manifest file not exists.', + $token->getLine(), + $stream->getSourceContext() + ); + } + + $manifest = json_decode(file_get_contents($this->manifestFile), true); + $assets = []; + + if (isset($manifest[$entryName . '.js'])) { + $entryPath = $this->publicPath . $manifest[$entryName . '.js']; + $assets[] = ''; + } else { + throw new \Twig_Error_Loader( + 'Webpack entry ' . $entryName . ' not exists.', + $token->getLine(), + $stream->getSourceContext() + ); + } + + return new \Twig_Node_Text(implode('', $assets), $token->getLine()); + } + + /** + * {@inheritdoc} + */ + public function getTag() + { + return 'webpack_entry_js'; + } +} diff --git a/README.md b/README.md index 4d6cd1f..9cfe29f 100644 --- a/README.md +++ b/README.md @@ -2,34 +2,45 @@ ### Install -`composer require fullpipe/twig-webpack-extension` +```bash +$ composer require fullpipe/twig-webpack-extension +``` ### Set up webpack Install webpack-manifest-plugin -``` -npm install webpack-manifest-plugin --save +```bash +$ npm install webpack-manifest-plugin --save-dev +$ yarn add webpack-manifest-plugin --dev ``` Configure `webpack.config.js` ```js var ManifestPlugin = require('webpack-manifest-plugin'); -... + +(...) + module.exports = { - ... + + (...) + entry: { vendor: ["jquery", "lodash"], main: './src/main.js' }, output: { - ... + + (...) + publicPath: '/build/', //required! - ... + + (...) }, plugins: [ new ManifestPlugin(), - ... + + (...) ] } ``` @@ -40,17 +51,19 @@ In symfony 2/3 ```yaml parameters: - ... + (...) + webpack.manifest: "%kernel.root_dir%/../web/build/manifest.json" #should be absolute - webpack.publicPath: /build/ #should be same as output.publicPath in webpack config + webpack.public_path: /build/ #should be same as output.publicPath in webpack config services: - ... + (...) + app.twig_extension: class: Fullpipe\Twig\Extension\Webpack\WebpackExtension arguments: - '%webpack.manifest%' - - '%webpack.publicPath%' + - '%webpack.public_path%' tags: - { name: twig.extension } ``` @@ -58,8 +71,8 @@ services: ### Inject entry points to your templates ```twig - {% webpack_entry 'vendor' %} - {% webpack_entry 'main' %} + {% webpack_entry_js 'vendor' %} + {% webpack_entry_js 'main' %} ``` ### Others diff --git a/WebpackExtension.php b/WebpackExtension.php index 969e8d9..9dd019a 100644 --- a/WebpackExtension.php +++ b/WebpackExtension.php @@ -15,18 +15,25 @@ class WebpackExtension extends \Twig_Extension /** * @var string */ - protected $publicPath; + protected $publicPathJs; + + /** + * @var string + */ + protected $publicPathCss; /** * Constructor. * - * @param string $manifestFile absolute path to your manifest.json - * @param string $publicPath your webpack output.publicPath + * @param string $manifestFile absolute path to your manifest.json + * @param string $publicPathJs your webpack output.publicPath + * @param string $publicPathCss your webpack output.publicPath */ - public function __construct($manifestFile, $publicPath = '/build/') + public function __construct($manifestFile, $publicPathJs = '/js/', $publicPathCss = '/css/') { $this->manifestFile = $manifestFile; - $this->publicPath = $publicPath; + $this->publicPathJs = $publicPathJs; + $this->publicPathCss = $publicPathCss; } /** @@ -42,6 +49,9 @@ public function getName() */ public function getTokenParsers() { - return array(new EntryTokenParser($this->manifestFile, $this->publicPath)); + return [ + new EntryTokenParserJs($this->manifestFile, $this->publicPathJs), + new EntryTokenParserCss($this->manifestFile, $this->publicPathCss), + ]; } } diff --git a/composer.json b/composer.json index f5cc0eb..aa8f4ee 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "library", "keywords": ["webpack", "twig", "extension"], "require": { - "twig/twig": "~1.20|~2.0" + "twig/twig": "^1.20 || ^2.0" }, "license": "MIT", "authors": [ @@ -14,6 +14,8 @@ } ], "autoload": { - "psr-4": { "Fullpipe\\Twig\\Extension\\Webpack\\": "" } + "psr-4": { + "Fullpipe\\Twig\\Extension\\Webpack\\": "" + } } } From 32171ac1945172f4a0632f1432b584ef1cb318e2 Mon Sep 17 00:00:00 2001 From: benatespina Date: Thu, 29 Dec 2016 17:29:00 +0100 Subject: [PATCH 2/4] Improved Documentation --- README.md | 125 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 79 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 9cfe29f..0589ead 100644 --- a/README.md +++ b/README.md @@ -1,91 +1,124 @@ -# Twig webpack extension +# Twig Webpack extension +This repo provides a Twig extension that joins Webpack resultant files with Twig template engine in an easy way. +This approach allows the dynamic insertion of the css stylesheets and js scripts with Webpack generated hash. +>Also works well with **extract-text-webpack-plugin** ### Install - ```bash $ composer require fullpipe/twig-webpack-extension ``` -### Set up webpack -Install webpack-manifest-plugin - +### Set up Webpack +You need to install the `webpack-manifest-plugin` ```bash $ npm install webpack-manifest-plugin --save-dev + +# or with Yarn $ yarn add webpack-manifest-plugin --dev ``` Configure `webpack.config.js` - ```js +// webpack.config.js + var ManifestPlugin = require('webpack-manifest-plugin'); (...) module.exports = { - + + (...) + + entry: { + vendor: ["jquery", "lodash"], + main: './src/main.js' + }, + output: { (...) - - entry: { - vendor: ["jquery", "lodash"], - main: './src/main.js' - }, - output: { - - (...) - - publicPath: '/build/', //required! - - (...) - }, - plugins: [ - new ManifestPlugin(), - - (...) - ] + path: './js' + publicPath: '/', // required + + (...) + }, + plugins: [ + new ManifestPlugin(), + new ExtractTextPlugin({ + filename: './../css/[name].css', + publicPath: '/' + }), + + (...) + ] } ``` -### Add twig extension - -In symfony 2/3 - +### Register as a service the Twig extension inside Symfony ```yaml +# app/config/services.yml + parameters: (...) webpack.manifest: "%kernel.root_dir%/../web/build/manifest.json" #should be absolute - webpack.public_path: /build/ #should be same as output.publicPath in webpack config + webpack.public_path_js: /js/ + webpack.public_path_css: /css/ services: (...) - app.twig_extension: + twig_extension.webpack: class: Fullpipe\Twig\Extension\Webpack\WebpackExtension + public: false arguments: - - '%webpack.manifest%' - - '%webpack.public_path%' + - "%webpack.manifest%" + - "%webpack.public_path_js%" + - "%webpack.public_path_css%" tags: - { name: twig.extension } ``` -### Inject entry points to your templates - +### Inject entry points to your Twig ```twig +{# app/Resources/views/base.html.twig #} + +(...) + + +(...) + + {% webpack_entry_css 'main' %} + + + +(...) + {% webpack_entry_js 'vendor' %} {% webpack_entry_js 'main' %} + ``` -### Others -If you use `[hash]` or `[chunkhash]` in webpack.output - +### Hashing output files avoiding the browser cache +If you use `[hash]` or `[chunkhash]`: ```js - output: { - filename: '[name].[chunkhash].js', - chunkFilename: '[name].[chunkhash].js' - }, -``` +// webpack.config.js + +(...) -You should clear twig cache after each webpack compilation. -So for dev environment do not use `[hash]` or `[chunkhash]`. +output: { + (...) -#### Works with extract-text-webpack-plugin + filename: '[name].[chunkhash].js', + chunkFilename: '[name].[chunkhash].js' +}, +plugins: [ + (...) + + new ExtractTextPlugin({ + filename: './../css/[name].[contenthash].css', + publicPath: '/' + }), + + (...) +] +``` +>You should clear twig cache after each webpack compilation. So for dev environment do not use `[hash]` or `[chunkhash]`. From 4cb29c9a14989b05a177ab4e1ec333035972a556 Mon Sep 17 00:00:00 2001 From: benatespina Date: Thu, 29 Dec 2016 17:46:00 +0100 Subject: [PATCH 3/4] Refactored the library and improved folder structure --- EntryTokenParserJs.php | 79 ------------------- LICENSE | 4 +- README.md | 2 + composer.json | 14 ++-- .../TokenParser/EntryTokenParser.php | 45 +++-------- .../TokenParser/EntryTokenParserCss.php | 16 ++++ .../TokenParser/EntryTokenParserJs.php | 16 ++++ .../TwigWebpackExtension/WebpackExtension.php | 32 +------- 8 files changed, 59 insertions(+), 149 deletions(-) delete mode 100644 EntryTokenParserJs.php rename EntryTokenParserCss.php => src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParser.php (60%) create mode 100644 src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParserCss.php create mode 100644 src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParserJs.php rename WebpackExtension.php => src/Fullpipe/TwigWebpackExtension/WebpackExtension.php (58%) diff --git a/EntryTokenParserJs.php b/EntryTokenParserJs.php deleted file mode 100644 index 3ece4a9..0000000 --- a/EntryTokenParserJs.php +++ /dev/null @@ -1,79 +0,0 @@ -manifestFile = $manifestFile; - $this->publicPath = $publicPath; - } - - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_Node_Text - * - * @throws Twig_Error_Loader - */ - public function parse(\Twig_Token $token) - { - $stream = $this->parser->getStream(); - $entryName = $stream->expect(\Twig_Token::STRING_TYPE)->getValue(); - $stream->expect(\Twig_Token::BLOCK_END_TYPE); - - if (!file_exists($this->manifestFile)) { - throw new \Twig_Error_Loader( - 'Webpack manifest file not exists.', - $token->getLine(), - $stream->getSourceContext() - ); - } - - $manifest = json_decode(file_get_contents($this->manifestFile), true); - $assets = []; - - if (isset($manifest[$entryName . '.js'])) { - $entryPath = $this->publicPath . $manifest[$entryName . '.js']; - $assets[] = ''; - } else { - throw new \Twig_Error_Loader( - 'Webpack entry ' . $entryName . ' not exists.', - $token->getLine(), - $stream->getSourceContext() - ); - } - - return new \Twig_Node_Text(implode('', $assets), $token->getLine()); - } - - /** - * {@inheritdoc} - */ - public function getTag() - { - return 'webpack_entry_js'; - } -} diff --git a/LICENSE b/LICENSE index 7e05c3a..f929606 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,4 @@ -The MIT License (MIT) - -Copyright (c) 2016 Eugene +Copyright (c) 2016-2017 Eugene Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0589ead..4e8d694 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # Twig Webpack extension +> Inject your webpack entry points into twig templates with easy. + This repo provides a Twig extension that joins Webpack resultant files with Twig template engine in an easy way. This approach allows the dynamic insertion of the css stylesheets and js scripts with Webpack generated hash. >Also works well with **extract-text-webpack-plugin** diff --git a/composer.json b/composer.json index aa8f4ee..f30ac66 100644 --- a/composer.json +++ b/composer.json @@ -2,20 +2,24 @@ "name": "fullpipe/twig-webpack-extension", "description": "Inject your webpack entry points into twig templates with easy.", "type": "library", - "keywords": ["webpack", "twig", "extension"], - "require": { - "twig/twig": "^1.20 || ^2.0" - }, "license": "MIT", + "keywords": [ + "webpack", + "twig", + "extension" + ], "authors": [ { "name": "Eugene Bravov", "email": "eugene.bravov@gmail.com" } ], + "require": { + "twig/twig": "^1.20 || ^2.0" + }, "autoload": { "psr-4": { - "Fullpipe\\Twig\\Extension\\Webpack\\": "" + "Fullpipe\\TwigWebpackExtension\\": "src/Fullpipe/TwigWebpackExtension/" } } } diff --git a/EntryTokenParserCss.php b/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParser.php similarity index 60% rename from EntryTokenParserCss.php rename to src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParser.php index 44dbf2e..cc175cb 100644 --- a/EntryTokenParserCss.php +++ b/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParser.php @@ -1,43 +1,22 @@ manifestFile = $manifestFile; $this->publicPath = $publicPath; } - /** - * Parses a token and returns a node. - * - * @param Twig_Token $token A Twig_Token instance - * - * @return Twig_Node_Text - * - * @throws Twig_Error_Loader - */ public function parse(\Twig_Token $token) { $stream = $this->parser->getStream(); @@ -55,9 +34,10 @@ public function parse(\Twig_Token $token) $manifest = json_decode(file_get_contents($this->manifestFile), true); $assets = []; - if (isset($manifest[$entryName . '.css'])) { - $entryPath = $this->publicPath . $manifest[$entryName . '.css']; - $assets[] = ''; + if (isset($manifest[$entryName . '.' . $this->type()])) { + $entryPath = $this->publicPath . $manifest[$entryName . '.' . $this->type()]; + + $assets[] = $this->generateHtml($entryPath); } else { throw new \Twig_Error_Loader( 'Webpack entry ' . $entryName . ' not exists.', @@ -69,11 +49,8 @@ public function parse(\Twig_Token $token) return new \Twig_Node_Text(implode('', $assets), $token->getLine()); } - /** - * {@inheritdoc} - */ public function getTag() { - return 'webpack_entry_css'; + return 'webpack_entry_' . $this->type(); } } diff --git a/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParserCss.php b/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParserCss.php new file mode 100644 index 0000000..ac72677 --- /dev/null +++ b/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParserCss.php @@ -0,0 +1,16 @@ +'; + } +} diff --git a/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParserJs.php b/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParserJs.php new file mode 100644 index 0000000..82d4e28 --- /dev/null +++ b/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParserJs.php @@ -0,0 +1,16 @@ +'; + } +} diff --git a/WebpackExtension.php b/src/Fullpipe/TwigWebpackExtension/WebpackExtension.php similarity index 58% rename from WebpackExtension.php rename to src/Fullpipe/TwigWebpackExtension/WebpackExtension.php index 9dd019a..d14a4bc 100644 --- a/WebpackExtension.php +++ b/src/Fullpipe/TwigWebpackExtension/WebpackExtension.php @@ -1,34 +1,16 @@ manifestFile = $manifestFile; @@ -36,17 +18,11 @@ public function __construct($manifestFile, $publicPathJs = '/js/', $publicPathCs $this->publicPathCss = $publicPathCss; } - /** - * {@inheritdoc} - */ public function getName() { return 'fullpipe.extension.webpack'; } - /** - * {@inheritdoc} - */ public function getTokenParsers() { return [ From d5696da623f5d9762d9369795392334f88f9a0ae Mon Sep 17 00:00:00 2001 From: benatespina Date: Thu, 29 Dec 2016 17:58:41 +0100 Subject: [PATCH 4/4] Fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e8d694..18075a2 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ services: (...) twig_extension.webpack: - class: Fullpipe\Twig\Extension\Webpack\WebpackExtension + class: Fullpipe\TwigWebpackExtension\WebpackExtension public: false arguments: - "%webpack.manifest%"