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/EntryTokenParser.php deleted file mode 100644 index d9f4b5c..0000000 --- a/EntryTokenParser.php +++ /dev/null @@ -1,78 +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->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[] = ''; - } - } else { - throw new \Twig_Error_Loader('Webpack entry '.$entryName.' not exists.', $token->getLine(), $stream->getFilename()); - } - - return new \Twig_Node_Text(implode('', $assets), $token->getLine()); - } - - /** - * {@inheritdoc} - */ - public function getTag() - { - return 'webpack_entry'; - } -} 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 4d6cd1f..18075a2 100644 --- a/README.md +++ b/README.md @@ -1,78 +1,126 @@ -# Twig webpack extension +# Twig Webpack extension +> Inject your webpack entry points into twig templates with easy. -### Install +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** -`composer require fullpipe/twig-webpack-extension` +### 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 -``` -npm install webpack-manifest-plugin --save +# 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: { - ... - publicPath: '/build/', //required! - ... - }, - plugins: [ - new ManifestPlugin(), - ... - ] + + (...) + + entry: { + vendor: ["jquery", "lodash"], + main: './src/main.js' + }, + output: { + (...) + 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.publicPath: /build/ #should be same as output.publicPath in webpack config + webpack.public_path_js: /js/ + webpack.public_path_css: /css/ services: - ... - app.twig_extension: - class: Fullpipe\Twig\Extension\Webpack\WebpackExtension + (...) + + twig_extension.webpack: + class: Fullpipe\TwigWebpackExtension\WebpackExtension + public: false arguments: - - '%webpack.manifest%' - - '%webpack.publicPath%' + - "%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 - {% webpack_entry 'vendor' %} - {% webpack_entry 'main' %} -``` +{# app/Resources/views/base.html.twig #} -### Others -If you use `[hash]` or `[chunkhash]` in webpack.output +(...) -```js - output: { - filename: '[name].[chunkhash].js', - chunkFilename: '[name].[chunkhash].js' - }, + +(...) + + {% webpack_entry_css 'main' %} + + + +(...) + + {% webpack_entry_js 'vendor' %} + {% webpack_entry_js 'main' %} + ``` -You should clear twig cache after each webpack compilation. -So for dev environment do not use `[hash]` or `[chunkhash]`. +### Hashing output files avoiding the browser cache +If you use `[hash]` or `[chunkhash]`: +```js +// webpack.config.js + +(...) + +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]`. diff --git a/WebpackExtension.php b/WebpackExtension.php deleted file mode 100644 index 969e8d9..0000000 --- a/WebpackExtension.php +++ /dev/null @@ -1,47 +0,0 @@ -manifestFile = $manifestFile; - $this->publicPath = $publicPath; - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return 'fullpipe.extension.webpack'; - } - - /** - * {@inheritdoc} - */ - public function getTokenParsers() - { - return array(new EntryTokenParser($this->manifestFile, $this->publicPath)); - } -} diff --git a/composer.json b/composer.json index f5cc0eb..f30ac66 100644 --- a/composer.json +++ b/composer.json @@ -2,18 +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\\": "" } + "psr-4": { + "Fullpipe\\TwigWebpackExtension\\": "src/Fullpipe/TwigWebpackExtension/" + } } } diff --git a/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParser.php b/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParser.php new file mode 100644 index 0000000..cc175cb --- /dev/null +++ b/src/Fullpipe/TwigWebpackExtension/TokenParser/EntryTokenParser.php @@ -0,0 +1,56 @@ +manifestFile = $manifestFile; + $this->publicPath = $publicPath; + } + + 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->getFilename() + ); + } + + $manifest = json_decode(file_get_contents($this->manifestFile), true); + $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.', + $token->getLine(), + $stream->getFilename() + ); + } + + return new \Twig_Node_Text(implode('', $assets), $token->getLine()); + } + + public function getTag() + { + 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/src/Fullpipe/TwigWebpackExtension/WebpackExtension.php b/src/Fullpipe/TwigWebpackExtension/WebpackExtension.php new file mode 100644 index 0000000..d14a4bc --- /dev/null +++ b/src/Fullpipe/TwigWebpackExtension/WebpackExtension.php @@ -0,0 +1,33 @@ +manifestFile = $manifestFile; + $this->publicPathJs = $publicPathJs; + $this->publicPathCss = $publicPathCss; + } + + public function getName() + { + return 'fullpipe.extension.webpack'; + } + + public function getTokenParsers() + { + return [ + new EntryTokenParserJs($this->manifestFile, $this->publicPathJs), + new EntryTokenParserCss($this->manifestFile, $this->publicPathCss), + ]; + } +}