From b8c983e182a03592f59a541f9ad6aea039535120 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 24 Apr 2020 10:47:51 -0400 Subject: [PATCH 1/3] Adding a way to return ALL CSS and JS files - i.e. disable returned file tracking --- src/Asset/EntrypointLookup.php | 22 +++++++++- src/Twig/EntryFilesTwigExtension.php | 24 +++++++++++ tests/Asset/EntrypointLookupTest.php | 27 +++++++++++++ tests/EventListener/ExceptionListenerTest.php | 4 +- tests/IntegrationTest.php | 40 ++++++++++++++++++- tests/fixtures/template_disable_tracking.twig | 6 +++ 6 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/template_disable_tracking.twig diff --git a/src/Asset/EntrypointLookup.php b/src/Asset/EntrypointLookup.php index d1ff9f24..48267b38 100644 --- a/src/Asset/EntrypointLookup.php +++ b/src/Asset/EntrypointLookup.php @@ -33,6 +33,8 @@ class EntrypointLookup implements EntrypointLookupInterface, IntegrityDataProvid private $strictMode; + private $trackReturnedFiles = true; + public function __construct(string $entrypointJsonPath, CacheItemPoolInterface $cache = null, string $cacheKey = null, bool $strictMode = true) { $this->entrypointJsonPath = $entrypointJsonPath; @@ -70,6 +72,18 @@ public function reset() $this->returnedFiles = []; } + /** + * Can be used to disable file tracking. + * + * When file tracking is disabled, *all* CSS and JS files will be + * returned from getJavaScriptFiles() and getCssFiles() including + * those that were previously returned. + */ + public function enableReturnedFileTracking(bool $shouldTrackReturnedFiles) + { + $this->trackReturnedFiles = $shouldTrackReturnedFiles; + } + private function getEntryFiles(string $entryName, string $key): array { $this->validateEntryName($entryName); @@ -81,8 +95,14 @@ private function getEntryFiles(string $entryName, string $key): array return []; } - // make sure to not return the same file multiple times $entryFiles = $entryData[$key]; + + // if tracking is disabled, return everything & do not mutate returnedFiles list + if (!$this->trackReturnedFiles) { + return $entryFiles; + } + + // make sure to not return the same file multiple times $newFiles = array_values(array_diff($entryFiles, $this->returnedFiles)); $this->returnedFiles = array_merge($this->returnedFiles, $newFiles); diff --git a/src/Twig/EntryFilesTwigExtension.php b/src/Twig/EntryFilesTwigExtension.php index 30254cb3..503be4d5 100644 --- a/src/Twig/EntryFilesTwigExtension.php +++ b/src/Twig/EntryFilesTwigExtension.php @@ -10,6 +10,7 @@ namespace Symfony\WebpackEncoreBundle\Twig; use Psr\Container\ContainerInterface; +use Symfony\WebpackEncoreBundle\Asset\EntrypointLookup; use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupInterface; use Symfony\WebpackEncoreBundle\Asset\TagRenderer; use Twig\Extension\AbstractExtension; @@ -31,6 +32,8 @@ public function getFunctions() new TwigFunction('encore_entry_css_files', [$this, 'getWebpackCssFiles']), new TwigFunction('encore_entry_script_tags', [$this, 'renderWebpackScriptTags'], ['is_safe' => ['html']]), new TwigFunction('encore_entry_link_tags', [$this, 'renderWebpackLinkTags'], ['is_safe' => ['html']]), + new TwigFunction('encore_disable_file_tracking', [$this, 'disableReturnedFileTracking']), + new TwigFunction('encore_enable_file_tracking', [$this, 'enableReturnedFileTracking']), ]; } @@ -58,6 +61,27 @@ public function renderWebpackLinkTags(string $entryName, string $packageName = n ->renderWebpackLinkTags($entryName, $packageName, $entrypointName); } + public function disableReturnedFileTracking(string $entrypointName = '_default') + { + $this->changeReturnedFileTracking(false, $entrypointName); + } + + public function enableReturnedFileTracking(string $entrypointName = '_default') + { + $this->changeReturnedFileTracking(true, $entrypointName); + } + + private function changeReturnedFileTracking(bool $isEnabled, string $entrypointName) + { + $lookup = $this->getEntrypointLookup($entrypointName); + + if (!$lookup instanceof EntrypointLookup) { + throw new \LogicException('In order to use encore_disable_returned_file_tracking/encore_enable_returned_file_tracking, the EntrypointLookupInterface must be an instance of EntrypointLookup.'); + } + + $lookup->enableReturnedFileTracking($isEnabled); + } + private function getEntrypointLookup(string $entrypointName): EntrypointLookupInterface { return $this->container->get('webpack_encore.entrypoint_lookup_collection') diff --git a/tests/Asset/EntrypointLookupTest.php b/tests/Asset/EntrypointLookupTest.php index 6f21fdc3..b8d80679 100644 --- a/tests/Asset/EntrypointLookupTest.php +++ b/tests/Asset/EntrypointLookupTest.php @@ -87,6 +87,33 @@ public function testGetJavaScriptFilesReturnsUniqueFilesOnly() ); } + public function testGetJavaScriptFilesWithFileTrackingDisabled() + { + // with file tracking on, grab a few files + $this->entrypointLookup->getJavaScriptFiles('my_entry'); + + $this->entrypointLookup->enableReturnedFileTracking(false); + $this->assertEquals( + ['file1.js', 'file3.js'], + $this->entrypointLookup->getJavaScriptFiles('other_entry'), + 'file1.js is returned even though it was also returned above', + ); + + $this->assertEquals( + // file1.js is returned even though it was also returned above + ['file1.js', 'file3.js'], + $this->entrypointLookup->getJavaScriptFiles('other_entry'), + 'repeat calls always return all the files' + ); + + $this->entrypointLookup->enableReturnedFileTracking(true); + $this->assertEquals( + ['file3.js'], + $this->entrypointLookup->getJavaScriptFiles('other_entry'), + 'file1.js is not returned once tracking is re-enabled' + ); + } + public function testGetCssFiles() { $this->assertEquals( diff --git a/tests/EventListener/ExceptionListenerTest.php b/tests/EventListener/ExceptionListenerTest.php index 7e0df5a0..616ef9ed 100644 --- a/tests/EventListener/ExceptionListenerTest.php +++ b/tests/EventListener/ExceptionListenerTest.php @@ -10,6 +10,7 @@ namespace Symfony\WebpackEncoreBundle\Tests\EventListener; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use PHPUnit\Framework\TestCase; @@ -39,7 +40,8 @@ public function testItResetsAllEntrypointLookups() $request = new Request(); $exception = new \Exception(); - $event = new GetResponseForExceptionEvent( + $exceptionClass = class_exists(ExceptionEvent::class) ? ExceptionEvent::class : GetResponseForExceptionEvent::class; + $event = new $exceptionClass( $this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index 4a28959f..cf4879a0 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -69,6 +69,22 @@ public function testTwigIntegration() ); } + public function testTwigIntegrationWithTrackingDisabled() + { + $kernel = new WebpackEncoreIntegrationTestKernel(true); + $kernel->boot(); + $container = $kernel->getContainer(); + + $html = $container->get('twig')->render('@integration_test/template_disable_tracking.twig'); + + $this->assertStringContainsStringCount( + '/build/file1.js', + $html, + // 1 time for my_entry, again for other_entry + 2 + ); + } + public function testEntriesAreNotRepeatedWhenAlreadyOutputIntegration() { $kernel = new WebpackEncoreIntegrationTestKernel(true); @@ -176,6 +192,23 @@ public function testAutowireDefaultBuildArgument() // Testing that it doesn't throw an exception is enough $this->assertTrue(true); } + + private function assertStringContainsStringCount(string $needle, $haystack, int $expectedCount) + { + $actualCount = substr_count($haystack, $needle); + + $this->assertSame( + $expectedCount, + $actualCount, + sprintf( + 'Expected to find string "%s" in haystack "%s" "%d" times but only found it "%d" times', + $needle, + $haystack, + $expectedCount, + $actualCount + ) + ); + } } abstract class AbstractWebpackEncoreIntegrationTestKernel extends Kernel @@ -208,6 +241,9 @@ protected function configureContainer(ContainerBuilder $container, LoaderInterfa { $container->loadFromExtension('framework', [ 'secret' => 'foo', + 'router' => [ + 'utf8' => true, + ], 'assets' => [ 'enabled' => $this->enableAssets, ], @@ -264,9 +300,9 @@ public function renderFoo() } } -if (method_exists(AbstractWebpackEncoreIntegrationTestKernel::class, 'configureRouting')) { +if (Kernel::VERSION_ID >= 50100) { class WebpackEncoreIntegrationTestKernel extends AbstractWebpackEncoreIntegrationTestKernel { - protected function configureRouting(RoutingConfigurator $routes): void + protected function configureRoutes(RoutingConfigurator $routes): void { $routes->add('/foo', 'kernel:'.(parent::VERSION_ID >= 40100 ? ':' : '').'renderFoo'); } diff --git a/tests/fixtures/template_disable_tracking.twig b/tests/fixtures/template_disable_tracking.twig new file mode 100644 index 00000000..116e29ed --- /dev/null +++ b/tests/fixtures/template_disable_tracking.twig @@ -0,0 +1,6 @@ +{% do encore_disable_file_tracking() %} +{{ encore_entry_script_tags('my_entry') }} +{{ encore_entry_link_tags('my_entry') }} +{{ encore_entry_script_tags('other_entry') }} +{{ encore_entry_link_tags('other_entry') }} +{% do encore_enable_file_tracking() %} From 3287980e98219fe382f471213ec196ee22033307 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 24 Apr 2020 15:05:18 -0400 Subject: [PATCH 2/3] Adding new Twig functions to fetch the *source* of files --- README.md | 64 +++++++++++-- composer.json | 4 +- src/Asset/BuildFileLocator.php | 96 +++++++++++++++++++ src/Asset/EntrypointLookup.php | 5 + .../WebpackEncoreExtension.php | 6 ++ src/Resources/config/services.xml | 5 + src/Twig/EntryFilesTwigExtension.php | 58 +++++++++++ tests/Asset/BuildFileLocatorTest.php | 74 ++++++++++++++ tests/Asset/EntrypointLookupTest.php | 2 +- tests/IntegrationTest.php | 29 ++++++ tests/fixtures/build/file1.js | 1 + tests/fixtures/build/file2.js | 1 + tests/fixtures/build/styles.css | 3 + tests/fixtures/build/styles2.css | 3 + tests/fixtures/inline_css_template.twig | 12 +++ 15 files changed, 352 insertions(+), 11 deletions(-) create mode 100644 src/Asset/BuildFileLocator.php create mode 100644 tests/Asset/BuildFileLocatorTest.php create mode 100644 tests/fixtures/build/file1.js create mode 100644 tests/fixtures/build/file2.js create mode 100644 tests/fixtures/build/styles.css create mode 100644 tests/fixtures/build/styles2.css create mode 100644 tests/fixtures/inline_css_template.twig diff --git a/README.md b/README.md index 503be84c..ee80cf79 100644 --- a/README.md +++ b/README.md @@ -105,21 +105,67 @@ If you want more control, you can use the `encore_entry_js_files()` and `encore_entry_css_files()` methods to get the list of files needed, then loop and create the `script` and `link` tags manually. -## Rendering Multiple Times in a Request (e.g. to Generate a PDF) +## Rendering Multiple Templates (e.g. Emails or PDFs) When you render your script or link tags, the bundle is smart enough not to repeat the same JavaScript or CSS file within the same request. This prevents you from having duplicate `` or ` +``` + +If you can't use these `encore_entry_*_source` functions, you can instead +manually disable and enable "file tracking": + +```twig +{# some template that renders a PDF or an email #} + +{% do encore_disable_file_tracking() %} + {{ encore_entry_link_tags('entry1') }} + {{ encore_entry_script_tags('entry1') }} +{% do encore_enable_file_tracking() %} +``` + +With this, *all* JS and CSS files for `entry1` will be rendered and +this won't affect any other Twig templates rendered in the request. + +## Resetting the Entrypoint + +If using `encore_disable_file_tracking()` won't work for you for some +reason, you can also "reset" EncoreBundle's internal cache so that the +bundle re-renders CSS or JS files that it previously rendered. For +example, in a controller: ```php // src/Controller/SomeController.php diff --git a/composer.json b/composer.json index f7f5d3e4..f5eb6865 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,9 @@ "symfony/framework-bundle": "^3.4 || ^4.0 || ^5.0", "symfony/phpunit-bridge": "^4.3.5 || ^5.0", "symfony/twig-bundle": "^3.4 || ^4.0 || ^5.0", - "symfony/web-link": "^3.4 || ^4.0 || ^5.0" + "symfony/web-link": "^3.4 || ^4.0 || ^5.0", + "twig/extra-bundle": "3.x-dev", + "twig/cssinliner-extra": "3.x-dev" }, "extra": { "thanks": { diff --git a/src/Asset/BuildFileLocator.php b/src/Asset/BuildFileLocator.php new file mode 100644 index 00000000..0c6bfb44 --- /dev/null +++ b/src/Asset/BuildFileLocator.php @@ -0,0 +1,96 @@ +buildPaths = $buildPaths; + } + + public function findFile(string $path, string $buildName = '_default'): string + { + if (!isset($this->buildPaths[$buildName])) { + throw new \InvalidArgumentException(sprintf('Invalid build name "%s"', $buildName)); + } + + // sanity / security check + if (!$this->strEndsWith($path, '.css') && !$this->strEndsWith($path, '.js')) { + throw new \InvalidArgumentException('Can only read files ending in .css and .js'); + } + + $buildPath = $this->buildPaths[$buildName]; + + $targetPath = $this->combinePaths($buildPath, $path); + + if ($this->ensureFileExists && !file_exists($targetPath)) { + throw new \LogicException(sprintf('Cannot determine how to locate the "%s" file by combining with the output_path "%s". Looked in "%s".', $path, $buildPath, $targetPath)); + } + + return $targetPath; + } + + /** + * This method tries to combine the build path and asset path to get a final path. + * + * It's really an "attempt" and will work in all normal cases, but not + * in all cases. For example with this config: + * + * output_path: %kernel.project_dir%/public/build + * + * If you pass an asset whose path is "build/file1.js", this would + * remove the duplicated "build" on both and return a final path of: + * + * %kernel.project_dir%/public/build/file1.js + */ + private function combinePaths(string $buildPath, string $path): string + { + $pathParts = explode('/', ltrim($path, '/')); + $buildPathInfo = new \SplFileInfo($buildPath); + + while (true) { + // break if there are no directories left + if (count($pathParts) == 1) { + break; + } + + // break if the beginning of the path and the "directory name" of the build path + // don't intersect + if ($pathParts[0] !== $buildPathInfo->getFilename()) { + break; + } + + // pop the first "directory" off of the path + unset($pathParts[0]); + $pathParts = array_values($pathParts); + } + + return $buildPathInfo->getPathname().'/'.implode('/', $pathParts); + } + + private function strEndsWith(string $haystack, string $needle): bool + { + return '' === $needle || $needle === \substr($haystack, -\strlen($needle)); + } + + /** + * @internal + */ + public function disableFileExistsCheck(): void + { + $this->ensureFileExists = false; + } +} diff --git a/src/Asset/EntrypointLookup.php b/src/Asset/EntrypointLookup.php index 48267b38..0600bbbf 100644 --- a/src/Asset/EntrypointLookup.php +++ b/src/Asset/EntrypointLookup.php @@ -84,6 +84,11 @@ public function enableReturnedFileTracking(bool $shouldTrackReturnedFiles) $this->trackReturnedFiles = $shouldTrackReturnedFiles; } + public function isReturnedFileTrackingEnabled(): bool + { + return $this->trackReturnedFiles; + } + private function getEntryFiles(string $entryName, string $key): array { $this->validateEntryName($entryName); diff --git a/src/DependencyInjection/WebpackEncoreExtension.php b/src/DependencyInjection/WebpackEncoreExtension.php index 521a2e97..a34bb7c7 100644 --- a/src/DependencyInjection/WebpackEncoreExtension.php +++ b/src/DependencyInjection/WebpackEncoreExtension.php @@ -35,10 +35,12 @@ public function load(array $configs, ContainerBuilder $container) $factories = []; $cacheKeys = []; + $buildPaths = []; if (false !== $config['output_path']) { $factories['_default'] = $this->entrypointFactory($container, '_default', $config['output_path'], $config['cache'], $config['strict_mode']); $cacheKeys['_default'] = $config['output_path'].'/'.self::ENTRYPOINTS_FILE_NAME; + $buildPaths['_default'] = $config['output_path']; $container->getDefinition('webpack_encore.entrypoint_lookup_collection') ->setArgument(1, '_default'); @@ -47,6 +49,7 @@ public function load(array $configs, ContainerBuilder $container) foreach ($config['builds'] as $name => $path) { $factories[$name] = $this->entrypointFactory($container, $name, $path, $config['cache'], $config['strict_mode']); $cacheKeys[rawurlencode($name)] = $path.'/'.self::ENTRYPOINTS_FILE_NAME; + $buildPaths[$name] = $path; } $container->getDefinition('webpack_encore.exception_listener') @@ -61,6 +64,9 @@ public function load(array $configs, ContainerBuilder $container) $container->setAlias(EntrypointLookupInterface::class, new Alias($this->getEntrypointServiceId('_default'))); } + $container->getDefinition('webpack_encore.build_file_locator') + ->replaceArgument(0, $buildPaths); + $defaultAttributes = []; if (false !== $config['crossorigin']) { diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 2464c368..e87e079e 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -28,6 +28,7 @@ + @@ -60,5 +61,9 @@ + + + + diff --git a/src/Twig/EntryFilesTwigExtension.php b/src/Twig/EntryFilesTwigExtension.php index 503be4d5..edcc9179 100644 --- a/src/Twig/EntryFilesTwigExtension.php +++ b/src/Twig/EntryFilesTwigExtension.php @@ -10,6 +10,7 @@ namespace Symfony\WebpackEncoreBundle\Twig; use Psr\Container\ContainerInterface; +use Symfony\WebpackEncoreBundle\Asset\BuildFileLocator; use Symfony\WebpackEncoreBundle\Asset\EntrypointLookup; use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupInterface; use Symfony\WebpackEncoreBundle\Asset\TagRenderer; @@ -30,6 +31,8 @@ public function getFunctions() return [ new TwigFunction('encore_entry_js_files', [$this, 'getWebpackJsFiles']), new TwigFunction('encore_entry_css_files', [$this, 'getWebpackCssFiles']), + new TwigFunction('encore_entry_js_source', [$this, 'getWebpackJsSource']), + new TwigFunction('encore_entry_css_source', [$this, 'getWebpackCssSource']), new TwigFunction('encore_entry_script_tags', [$this, 'renderWebpackScriptTags'], ['is_safe' => ['html']]), new TwigFunction('encore_entry_link_tags', [$this, 'renderWebpackLinkTags'], ['is_safe' => ['html']]), new TwigFunction('encore_disable_file_tracking', [$this, 'disableReturnedFileTracking']), @@ -49,6 +52,34 @@ public function getWebpackCssFiles(string $entryName, string $entrypointName = ' ->getCssFiles($entryName); } + public function getWebpackJsSource(string $entryName, string $entrypointName = '_default'): string + { + $originalTrackingValue = $this->isReturnedFileTrackingEnabled($entrypointName); + $this->changeReturnedFileTracking(false, $entrypointName); + + $files = $this->getEntrypointLookup($entrypointName) + ->getJavaScriptFiles($entryName); + + $source = $this->concatenateFileSources($files); + $this->changeReturnedFileTracking($originalTrackingValue, $entrypointName); + + return $source; + } + + public function getWebpackCssSource(string $entryName, string $entrypointName = '_default'): string + { + $originalTrackingValue = $this->isReturnedFileTrackingEnabled($entrypointName); + $this->changeReturnedFileTracking(false, $entrypointName); + + $files = $this->getEntrypointLookup($entrypointName) + ->getCssFiles($entryName); + + $source = $this->concatenateFileSources($files); + $this->changeReturnedFileTracking($originalTrackingValue, $entrypointName); + + return $source; + } + public function renderWebpackScriptTags(string $entryName, string $packageName = null, string $entrypointName = '_default'): string { return $this->getTagRenderer() @@ -82,6 +113,28 @@ private function changeReturnedFileTracking(bool $isEnabled, string $entrypointN $lookup->enableReturnedFileTracking($isEnabled); } + private function isReturnedFileTrackingEnabled(string $entrypointName): bool + { + $lookup = $this->getEntrypointLookup($entrypointName); + + if (!$lookup instanceof EntrypointLookup) { + throw new \LogicException('In order to use encore_entry_js_source/encore_entry_css_source, the EntrypointLookupInterface must be an instance of EntrypointLookup.'); + } + + return $lookup->isReturnedFileTrackingEnabled(); + } + + private function concatenateFileSources(array $files): string + { + $locator = $this->getBuildFileLocator(); + $source = ''; + foreach ($files as $file) { + $source .= file_get_contents($locator->findFile($file)); + } + + return $source; + } + private function getEntrypointLookup(string $entrypointName): EntrypointLookupInterface { return $this->container->get('webpack_encore.entrypoint_lookup_collection') @@ -92,4 +145,9 @@ private function getTagRenderer(): TagRenderer { return $this->container->get('webpack_encore.tag_renderer'); } + + private function getBuildFileLocator(): BuildFileLocator + { + return $this->container->get('webpack_encore.build_file_locator'); + } } diff --git a/tests/Asset/BuildFileLocatorTest.php b/tests/Asset/BuildFileLocatorTest.php new file mode 100644 index 00000000..2f074e99 --- /dev/null +++ b/tests/Asset/BuildFileLocatorTest.php @@ -0,0 +1,74 @@ + + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\WebpackEncoreBundle\Tests\Asset; + +use PHPUnit\Framework\TestCase; +use Symfony\WebpackEncoreBundle\Asset\BuildFileLocator; + +class BuildFileLocatorTest extends TestCase +{ + /** + * @dataProvider getFindFileTests + */ + public function testFindFile(string $buildPath, string $assetPath, string $expectedPath) + { + $fileLocator = new BuildFileLocator(['_default' => $buildPath]); + $fileLocator->disableFileExistsCheck(); + + $this->assertSame($expectedPath, $fileLocator->findFile($assetPath)); + } + + public function getFindFileTests() + { + yield 'no_overlap' => [ + '/app/public', + 'foo.js', + '/app/public/foo.js' + ]; + + yield 'simple_overlap' => [ + '/app/public/build', + 'build/foo.js', + '/app/public/build/foo.js' + ]; + + yield 'simple_overlap_with_slash' => [ + '/app/public/build', + '/build/foo.js', + '/app/public/build/foo.js' + ]; + + yield 'simple_overlap_with_build_path_slash' => [ + '/app/public/build/', + 'build/foo.js', + '/app/public/build/foo.js' + ]; + + yield 'partial_overlap' => [ + '/app/public/build', + 'build/subdirectory/foo.js', + '/app/public/build/subdirectory/foo.js' + ]; + + yield 'overlap_in_wrong_spot' => [ + '/app/public/build', + 'subdirectory/build/foo.js', + '/app/public/build/subdirectory/build/foo.js' + ]; + + yield 'windows_paths' => [ + 'C:\\\\app\\public\\build', + 'build/foo.js', + 'C:\\\\app\\public\\build/build/foo.js' + ]; + } + + // test only CSS & JS allowed +} diff --git a/tests/Asset/EntrypointLookupTest.php b/tests/Asset/EntrypointLookupTest.php index b8d80679..2e728a1d 100644 --- a/tests/Asset/EntrypointLookupTest.php +++ b/tests/Asset/EntrypointLookupTest.php @@ -96,7 +96,7 @@ public function testGetJavaScriptFilesWithFileTrackingDisabled() $this->assertEquals( ['file1.js', 'file3.js'], $this->entrypointLookup->getJavaScriptFiles('other_entry'), - 'file1.js is returned even though it was also returned above', + 'file1.js is returned even though it was also returned above' ); $this->assertEquals( diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index cf4879a0..9d910afe 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -29,6 +29,7 @@ use Symfony\WebpackEncoreBundle\Asset\TagRenderer; use Symfony\WebpackEncoreBundle\CacheWarmer\EntrypointCacheWarmer; use Symfony\WebpackEncoreBundle\WebpackEncoreBundle; +use Twig\Extra\TwigExtraBundle\TwigExtraBundle; class IntegrationTest extends TestCase { @@ -85,6 +86,33 @@ public function testTwigIntegrationWithTrackingDisabled() ); } + public function testTwigIntegrationWithSourceFiles() + { + $kernel = new WebpackEncoreIntegrationTestKernel(true); + $kernel->boot(); + $container = $kernel->getContainer(); + + $html = $container->get('twig')->render('@integration_test/inline_css_template.twig'); + + $this->assertStringContainsString( + '

Hello

', + $html + ); + $this->assertStringContainsString( + '

World!

', + $html + ); + + $this->assertStringContainsString( + "alert('Hello file1 JavaScript!')", + $html + ); + $this->assertStringContainsString( + "alert('Hello file2 JavaScript!')", + $html + ); + } + public function testEntriesAreNotRepeatedWhenAlreadyOutputIntegration() { $kernel = new WebpackEncoreIntegrationTestKernel(true); @@ -234,6 +262,7 @@ public function registerBundles() new FrameworkBundle(), new TwigBundle(), new WebpackEncoreBundle(), + new TwigExtraBundle(), ]; } diff --git a/tests/fixtures/build/file1.js b/tests/fixtures/build/file1.js new file mode 100644 index 00000000..b97f2091 --- /dev/null +++ b/tests/fixtures/build/file1.js @@ -0,0 +1 @@ +alert('Hello file1 JavaScript!'); diff --git a/tests/fixtures/build/file2.js b/tests/fixtures/build/file2.js new file mode 100644 index 00000000..acc43bda --- /dev/null +++ b/tests/fixtures/build/file2.js @@ -0,0 +1 @@ +alert('Hello file2 JavaScript!'); diff --git a/tests/fixtures/build/styles.css b/tests/fixtures/build/styles.css new file mode 100644 index 00000000..b0b2f237 --- /dev/null +++ b/tests/fixtures/build/styles.css @@ -0,0 +1,3 @@ +h1 { + font-size: 20px; +} diff --git a/tests/fixtures/build/styles2.css b/tests/fixtures/build/styles2.css new file mode 100644 index 00000000..b77735a8 --- /dev/null +++ b/tests/fixtures/build/styles2.css @@ -0,0 +1,3 @@ +h2 { + color: purple; +} diff --git a/tests/fixtures/inline_css_template.twig b/tests/fixtures/inline_css_template.twig new file mode 100644 index 00000000..5acf50a9 --- /dev/null +++ b/tests/fixtures/inline_css_template.twig @@ -0,0 +1,12 @@ +{# should not affect encore_entry_css_source() below #} +{{ encore_entry_link_tags('my_entry') }} +{{ encore_entry_script_tags('my_entry') }} + +{% apply inline_css(encore_entry_css_source('my_entry')) %} +

Hello

+

World!

+{% endapply %} + + From 5b1a7871370ba793c88d2e02bab764b7fc242f56 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 8 May 2020 10:43:32 -0400 Subject: [PATCH 3/3] Simplifying readme to avoid duplication in official docs --- README.md | 113 ++++++++++++------------------------------------------ 1 file changed, 24 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index ee80cf79..80e591c9 100644 --- a/README.md +++ b/README.md @@ -101,92 +101,27 @@ and `build/entry1.js`, then `encore_entry_script_tags()` is equivalent to: ``` -If you want more control, you can use the `encore_entry_js_files()` and -`encore_entry_css_files()` methods to get the list of files needed, then -loop and create the `script` and `link` tags manually. - -## Rendering Multiple Templates (e.g. Emails or PDFs) - -When you render your script or link tags, the bundle is smart enough -not to repeat the same JavaScript or CSS file within the same request. -This prevents you from having duplicate `` or ` -``` - -If you can't use these `encore_entry_*_source` functions, you can instead -manually disable and enable "file tracking": - -```twig -{# some template that renders a PDF or an email #} - -{% do encore_disable_file_tracking() %} - {{ encore_entry_link_tags('entry1') }} - {{ encore_entry_script_tags('entry1') }} -{% do encore_enable_file_tracking() %} -``` - -With this, *all* JS and CSS files for `entry1` will be rendered and -this won't affect any other Twig templates rendered in the request. - -## Resetting the Entrypoint - -If using `encore_disable_file_tracking()` won't work for you for some -reason, you can also "reset" EncoreBundle's internal cache so that the -bundle re-renders CSS or JS files that it previously rendered. For -example, in a controller: - -```php -// src/Controller/SomeController.php - -use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupInterface; - -class SomeController -{ - public function index(EntrypointLookupInterface $entrypointLookup) - { - $entrypointLookup->reset(); - // render a template - - $entrypointLookup->reset(); - // render another template - - // ... - } -} -``` - -If you have multiple builds, you can also autowire -`Symfony\WebpackEncoreBundle\Asset\EntrypointLookupCollectionInterface` -and use it to get the `EntrypointLookupInterface` object for any build. +## Twig Function Reference + +* `encore_entry_script_tags('entryName')` - renders all `