Skip to content

Commit 28b2636

Browse files
committed
feature #17 Allow to define multiple entrypoints and load assets from each of them. (shulard)
This PR was squashed before being merged into the master branch (closes #17). Discussion ---------- Allow to define multiple entrypoints and load assets from each of them. This PR is added to fix the issue #13. With that new code, you can configure your project like this : ```yaml webpack_encore: # the "default" build output_path: '%kernel.public_dir%/build' builds: frontend: '%kernel.public_dir%/frontend/build' ``` Then in your Twig template : ```twig {{ encore_entry_script_tags('entry1', 'frontend') }} ``` Commits ------- 0f3134b Add test regarding EntrypointLookupCollection. a238fe7 Handle the new Entrypoint collection to render paths. 2c38514 Create a new exception when the Entrypoint is not defined. 59bbd29 Update extension to load entrypoint build path. 0f69516 Add an EntrypointLookupCollection. ebe61d9 Allow to define a list of entrypoints to be used in the project.
2 parents 2d2ce93 + 0f3134b commit 28b2636

14 files changed

+340
-24
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony WebpackEncoreBundle package.
5+
* (c) Fabien Potencier <[email protected]>
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace Symfony\WebpackEncoreBundle\Asset;
11+
12+
use Symfony\WebpackEncoreBundle\Exception\UndefinedBuildException;
13+
use Psr\Container\ContainerInterface;
14+
15+
/**
16+
* Aggregate the different entry points configured in the container.
17+
*
18+
* Retrieve the EntrypointLookup instance from the given key.
19+
*
20+
* @final
21+
*/
22+
class EntrypointLookupCollection
23+
{
24+
private $buildEntrypoints;
25+
26+
public function __construct(ContainerInterface $buildEntrypoints)
27+
{
28+
$this->buildEntrypoints = $buildEntrypoints;
29+
}
30+
31+
public function getEntrypointLookup(string $buildName): EntrypointLookupInterface
32+
{
33+
if (!$this->buildEntrypoints->has($buildName)) {
34+
throw new UndefinedBuildException(sprintf('Given entry point "%s" is not configured', $buildName));
35+
}
36+
37+
return $this->buildEntrypoints->get($buildName);
38+
}
39+
}

src/Asset/TagRenderer.php

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,39 @@
1010
namespace Symfony\WebpackEncoreBundle\Asset;
1111

1212
use Symfony\Component\Asset\Packages;
13+
use Symfony\Component\DependencyInjection\ServiceLocator;
1314

1415
final class TagRenderer
1516
{
16-
private $entrypointLookup;
17+
private $entrypointLookupCollection;
1718

1819
private $packages;
1920

20-
public function __construct(EntrypointLookupInterface $entrypointLookup, Packages $packages)
21-
{
22-
$this->entrypointLookup = $entrypointLookup;
21+
public function __construct(
22+
$entrypointLookupCollection,
23+
Packages $packages
24+
) {
25+
if ($entrypointLookupCollection instanceof EntrypointLookupInterface) {
26+
@trigger_error(sprintf('The "$entrypointLookupCollection" argument in method "%s()" must be an instance of EntrypointLookupCollection.', __METHOD__), E_USER_DEPRECATED);
27+
28+
$this->entrypointLookupCollection = new EntrypointLookupCollection(
29+
new ServiceLocator(['_default' => function() use ($entrypointLookupCollection) {
30+
return $entrypointLookupCollection;
31+
}])
32+
);
33+
} elseif ($entrypointLookupCollection instanceof EntrypointLookupCollection) {
34+
$this->entrypointLookupCollection = $entrypointLookupCollection;
35+
} else {
36+
throw new \TypeError('The "$entrypointLookupCollection" argument must be an instance of EntrypointLookupCollection.');
37+
}
38+
2339
$this->packages = $packages;
2440
}
2541

26-
public function renderWebpackScriptTags(string $entryName, string $packageName = null): string
42+
public function renderWebpackScriptTags(string $entryName, string $packageName = null, string $entrypointName = '_default'): string
2743
{
2844
$scriptTags = [];
29-
foreach ($this->entrypointLookup->getJavaScriptFiles($entryName) as $filename) {
45+
foreach ($this->getEntrypointLookup($entrypointName)->getJavaScriptFiles($entryName) as $filename) {
3046
$scriptTags[] = sprintf(
3147
'<script src="%s"></script>',
3248
htmlentities($this->getAssetPath($filename, $packageName))
@@ -36,10 +52,10 @@ public function renderWebpackScriptTags(string $entryName, string $packageName =
3652
return implode('', $scriptTags);
3753
}
3854

39-
public function renderWebpackLinkTags(string $entryName, string $packageName = null): string
55+
public function renderWebpackLinkTags(string $entryName, string $packageName = null, string $entrypointName = '_default'): string
4056
{
4157
$scriptTags = [];
42-
foreach ($this->entrypointLookup->getCssFiles($entryName) as $filename) {
58+
foreach ($this->getEntrypointLookup($entrypointName)->getCssFiles($entryName) as $filename) {
4359
$scriptTags[] = sprintf(
4460
'<link rel="stylesheet" href="%s">',
4561
htmlentities($this->getAssetPath($filename, $packageName))
@@ -60,4 +76,9 @@ private function getAssetPath(string $assetPath, string $packageName = null): st
6076
$packageName
6177
);
6278
}
79+
80+
private function getEntrypointLookup(string $buildName): EntrypointLookupInterface
81+
{
82+
return $this->entrypointLookupCollection->getEntrypointLookup($buildName);
83+
}
6384
}

src/DependencyInjection/Configuration.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
1313
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
1414
use Symfony\Component\Config\Definition\ConfigurationInterface;
15+
use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;
1516

1617
final class Configuration implements ConfigurationInterface
1718
{
@@ -27,6 +28,19 @@ public function getConfigTreeBuilder()
2728
->isRequired()
2829
->info('The path where Encore is building the assets - i.e. Encore.setOutputPath()')
2930
->end()
31+
->arrayNode('builds')
32+
->useAttributeAsKey('name')
33+
->scalarPrototype()
34+
->validate()
35+
->always(function ($values) {
36+
if (isset($values['_default'])) {
37+
throw new InvalidDefinitionException("Key '_default' can't be used as build name.");
38+
}
39+
40+
return $values;
41+
})
42+
->end()
43+
->end()
3044
->end()
3145
;
3246

src/DependencyInjection/WebpackEncoreExtension.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
use Symfony\Component\DependencyInjection\ContainerBuilder;
1414
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
1515
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
16+
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
17+
use Symfony\Component\DependencyInjection\Definition;
18+
use Symfony\Component\DependencyInjection\Reference;
19+
use Symfony\WebpackEncoreBundle\Asset\EntrypointLookup;
1620

1721
final class WebpackEncoreExtension extends Extension
1822
{
@@ -24,7 +28,23 @@ public function load(array $configs, ContainerBuilder $container)
2428
$configuration = $this->getConfiguration($configs, $container);
2529
$config = $this->processConfiguration($configuration, $configs);
2630

31+
$factories = [
32+
'_default' => new Reference($this->entrypointFactory($container, '_default', $config['output_path']))
33+
];
34+
foreach ($config['builds'] as $name => $path) {
35+
$factories[$name] = new Reference($this->entrypointFactory($container, $name, $path));
36+
};
37+
2738
$container->getDefinition('webpack_encore.entrypoint_lookup')
28-
->replaceArgument(0, $config['output_path'].'/entrypoints.json');
39+
->replaceArgument(0, $factories['_default']);
40+
$container->getDefinition('webpack_encore.entrypoint_lookup_collection')
41+
->replaceArgument(0, ServiceLocatorTagPass::register($container, $factories));
42+
}
43+
44+
private function entrypointFactory(ContainerBuilder $container, string $name, string $path): string
45+
{
46+
$id = sprintf('webpack_encore.entrypoint_lookup[%s]', $name);
47+
$container->setDefinition($id, new Definition(EntrypointLookup::class, [$path.'/entrypoints.json']));
48+
return $id;
2949
}
3050
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony WebpackEncoreBundle package.
5+
* (c) Fabien Potencier <[email protected]>
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace Symfony\WebpackEncoreBundle\Exception;
11+
12+
class UndefinedBuildException extends \InvalidArgumentException
13+
{
14+
}

src/Resources/config/services.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
<service id="webpack_encore.entrypoint_lookup" class="Symfony\WebpackEncoreBundle\Asset\EntrypointLookup">
1111
<argument /> <!-- entrypoints.json path -->
1212
</service>
13+
<service id="webpack_encore.entrypoint_lookup_collection" class="Symfony\WebpackEncoreBundle\Asset\EntrypointLookupCollection">
14+
<argument /> <!-- build list of entrypoints path -->
15+
</service>
1316

1417
<service id="webpack_encore.tag_renderer" class="Symfony\WebpackEncoreBundle\Asset\TagRenderer">
15-
<argument type="service" id="webpack_encore.entrypoint_lookup" />
18+
<argument type="service" id="webpack_encore.entrypoint_lookup_collection" />
1619
<argument type="service" id="assets.packages" />
1720
</service>
1821

@@ -23,6 +26,7 @@
2326
<tag name="container.service_locator" />
2427
<argument type="collection">
2528
<argument key="webpack_encore.entrypoint_lookup" type="service" id="webpack_encore.entrypoint_lookup" />
29+
<argument key="webpack_encore.entrypoint_lookup_collection" type="service" id="webpack_encore.entrypoint_lookup_collection" />
2630
<argument key="webpack_encore.tag_renderer" type="service" id="webpack_encore.tag_renderer" />
2731
</argument>
2832
</service>

src/Twig/EntryFilesTwigExtension.php

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,33 +34,34 @@ public function getFunctions()
3434
];
3535
}
3636

37-
public function getWebpackJsFiles(string $entryName): array
37+
public function getWebpackJsFiles(string $entryName, string $entrypointName = '_default'): array
3838
{
39-
return $this->getEntrypointLookup()
39+
return $this->getEntrypointLookup($entrypointName)
4040
->getJavaScriptFiles($entryName);
4141
}
4242

43-
public function getWebpackCssFiles(string $entryName): array
43+
public function getWebpackCssFiles(string $entryName, string $entrypointName = '_default'): array
4444
{
45-
return $this->getEntrypointLookup()
45+
return $this->getEntrypointLookup($entrypointName)
4646
->getCssFiles($entryName);
4747
}
4848

49-
public function renderWebpackScriptTags(string $entryName, string $packageName = null): string
49+
public function renderWebpackScriptTags(string $entryName, string $packageName = null, string $entrypointName = '_default'): string
5050
{
5151
return $this->getTagRenderer()
52-
->renderWebpackScriptTags($entryName, $packageName);
52+
->renderWebpackScriptTags($entryName, $packageName, $entrypointName);
5353
}
5454

55-
public function renderWebpackLinkTags(string $entryName, string $packageName = null): string
55+
public function renderWebpackLinkTags(string $entryName, string $packageName = null, string $entrypointName = '_default'): string
5656
{
5757
return $this->getTagRenderer()
58-
->renderWebpackLinkTags($entryName, $packageName);
58+
->renderWebpackLinkTags($entryName, $packageName, $entrypointName);
5959
}
6060

61-
private function getEntrypointLookup(): EntrypointLookupInterface
61+
private function getEntrypointLookup(string $entrypointName): EntrypointLookupInterface
6262
{
63-
return $this->container->get('webpack_encore.entrypoint_lookup');
63+
return $this->container->get('webpack_encore.entrypoint_lookup_collection')
64+
->getEntrypointLookup($entrypointName);
6465
}
6566

6667
private function getTagRenderer(): TagRenderer
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Symfony\WebpackEncoreBundle\Tests\Asset;
4+
5+
use Symfony\Component\DependencyInjection\ServiceLocator;
6+
use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupCollection;
7+
use PHPUnit\Framework\TestCase;
8+
9+
class EntrypointLookupCollectionTest extends TestCase
10+
{
11+
/**
12+
* @expectedException Symfony\WebpackEncoreBundle\Exception\UndefinedBuildException
13+
* @expectedExceptionMessage Given entry point "something" is not configured
14+
*/
15+
public function testExceptionOnMissingEntry()
16+
{
17+
$collection = new EntrypointLookupCollection(new ServiceLocator([]));
18+
$collection->getEntrypointLookup('something');
19+
}
20+
}

tests/Asset/EntrypointLookupTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ public function testGetJavaScriptFiles()
4848
['file1.js', 'file2.js'],
4949
$this->entrypointLookup->getJavaScriptFiles('my_entry')
5050
);
51+
52+
$this->assertEquals(
53+
[],
54+
$this->entrypointLookup->getJavaScriptFiles('my_entry')
55+
);
56+
57+
$this->entrypointLookup->reset();
58+
59+
$this->assertEquals(
60+
['file1.js', 'file2.js'],
61+
$this->entrypointLookup->getJavaScriptFiles('my_entry')
62+
);
5163
}
5264

5365
public function testGetJavaScriptFilesReturnsUniqueFilesOnly()
@@ -79,6 +91,32 @@ public function testEmptyReturnOnValidEntryNoJsOrCssFile()
7991
);
8092
}
8193

94+
/**
95+
* @expectedException \InvalidArgumentException
96+
* @expectedExceptionMessageContains There was a problem JSON decoding the
97+
*/
98+
public function testExceptionOnInvalidJson()
99+
{
100+
$filename = tempnam(sys_get_temp_dir(), 'WebpackEncoreBundle');
101+
file_put_contents($filename, "abcd");
102+
103+
$this->entrypointLookup = new EntrypointLookup($filename);
104+
$this->entrypointLookup->getJavaScriptFiles('an_entry');
105+
}
106+
107+
/**
108+
* @expectedException \InvalidArgumentException
109+
* @expectedExceptionMessageContains Could not find an "entrypoints" key in the
110+
*/
111+
public function testExceptionOnMissingEntrypointsKeyInJson()
112+
{
113+
$filename = tempnam(sys_get_temp_dir(), 'WebpackEncoreBundle');
114+
file_put_contents($filename, "{}");
115+
116+
$this->entrypointLookup = new EntrypointLookup($filename);
117+
$this->entrypointLookup->getJavaScriptFiles('an_entry');
118+
}
119+
82120
/**
83121
* @expectedException \InvalidArgumentException
84122
* @expectedExceptionMessage Could not find the entrypoints file

0 commit comments

Comments
 (0)