WebpackEncoreBundleExtension   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 161
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 79
dl 0
loc 161
rs 10
c 0
b 0
f 0
wmc 15

5 Methods

Rating   Name   Duplication   Size   Complexity  
A createEntryPointLookupStatement() 0 6 1
A beforeCompile() 0 21 2
A getValidConfig() 0 22 3
A registerCache() 0 22 4
A loadConfiguration() 0 33 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SixtyEightPublishers\WebpackEncoreBundle\DI;
6
7
use Latte;
8
use Nette;
9
use SixtyEightPublishers;
10
11
final class WebpackEncoreBundleExtension extends Nette\DI\CompilerExtension
12
{
13
	private const ENTRYPOINTS_FILE_NAME = 'entrypoints.json';
14
15
	private const ENTRYPOINT_DEFAULT_NAME = '_default';
16
17
	private const CROSSORIGIN_ALLOWED_VALUES = [ NULL, 'anonymous', 'use-credentials' ];
18
19
	/** @var array  */
20
	private $defaults = [
21
		'output_path' => NULL, # The path where Encore is building the assets - i.e. Encore.setOutputPath()
22
		'builds' => [],
23
		'crossorigin' => NULL, # crossorigin value when Encore.enableIntegrityHashes() is used, can be NULL (default), anonymous or use-credentials
24
		'cache' => [
25
			'enabled' => FALSE,
26
			'storage' => '@' . Nette\Caching\IStorage::class,
27
		],
28
		'latte' => [
29
			'js_assets_macro_name' => 'encore_js',
30
			'css_assets_macro_name' => 'encore_css',
31
		],
32
	];
33
34
	/**
35
	 * {@inheritdoc}
36
	 */
37
	public function loadConfiguration(): void
38
	{
39
		$config = $this->getValidConfig();
40
		$builder = $this->getContainerBuilder();
41
		$cache = $this->registerCache($config['cache']['enabled'], $config['cache']['storage']);
42
		$lookups = [];
43
44
		if (NULL !== $config['output_path']) {
45
			$lookups[] = $this->createEntryPointLookupStatement(self::ENTRYPOINT_DEFAULT_NAME, $config['output_path'], $cache);
46
		}
47
48
		foreach ($config['builds'] as $name => $path) {
49
			$lookups[] = $this->createEntryPointLookupStatement($name, $path, $cache);
50
		}
51
52
		$builder->addDefinition($this->prefix('entryPointLookupProvider'))
53
			->setType(SixtyEightPublishers\WebpackEncoreBundle\EntryPoint\IEntryPointLookupProvider::class)
54
			->setFactory(SixtyEightPublishers\WebpackEncoreBundle\EntryPoint\EntryPointLookupProvider::class, [
55
				'lookups' => $lookups,
56
				'defaultName' => NULL !== $config['output_path'] ? self::ENTRYPOINT_DEFAULT_NAME : NULL,
57
			]);
58
59
		$defaultAttributes = [];
60
61
		if (NULL !== $config['crossorigin']) {
62
			$defaultAttributes['crossorigin'] = $config['crossorigin'];
63
		}
64
65
		$builder->addDefinition($this->prefix('tagRenderer'))
66
			->setType(SixtyEightPublishers\WebpackEncoreBundle\Latte\TagRenderer::class)
67
			->setAutowired(FALSE)
68
			->setArguments([
69
				'defaultAttributes' => $defaultAttributes,
70
			]);
71
	}
72
73
	/**
74
	 * {@inheritdoc}
75
	 */
76
	public function beforeCompile(): void
77
	{
78
		$config = $this->getValidConfig();
79
		$builder = $this->getContainerBuilder();
80
81
		if (NULL === $builder->getByType('Symfony\Component\Asset\Packages', FALSE)) {
82
			throw new \RuntimeException('Missing service of type Symfony\Component\Asset\Packages that is required by this package. You can configure and register it manually or you can use package 68publishers/asset (recommended way).');
83
		}
84
85
		$latteFactory = $builder->getDefinition($builder->getByType(Latte\Engine::class) ?? 'nette.latteFactory');
86
87
		$latteFactory->addSetup('addProvider', [
88
			'name' => 'webpackEncoreTagRenderer',
89
			'value' => $this->prefix('@tagRenderer'),
90
		]);
91
92
		$latteFactory->addSetup('?->onCompile[] = function ($engine) { ?::install(?, ?, $engine->getCompiler()); }', [
93
			'@self',
94
			new Nette\PhpGenerator\PhpLiteral(SixtyEightPublishers\WebpackEncoreBundle\Latte\WebpackEncoreMacros::class),
95
			$config['latte']['js_assets_macro_name'],
96
			$config['latte']['css_assets_macro_name'],
97
		]);
98
	}
99
100
	/**
101
	 * @param string                           $name
102
	 * @param string                           $path
103
	 * @param \Nette\DI\ServiceDefinition|NULL $cache
104
	 *
105
	 * @return \Nette\DI\Statement
106
	 */
107
	private function createEntryPointLookupStatement(string $name, string $path, ?Nette\DI\ServiceDefinition $cache): Nette\DI\Statement
108
	{
109
		return new Nette\DI\Statement(SixtyEightPublishers\WebpackEncoreBundle\EntryPoint\EntryPointLookup::class, [
110
			'buildName' => $name,
111
			'entryPointJsonPath' => $path . '/' . self::ENTRYPOINTS_FILE_NAME,
112
			'cache' => $cache,
113
		]);
114
	}
115
116
	/**
117
	 * @param bool  $enabled
118
	 * @param mixed $storage
119
	 *
120
	 * @return \Nette\DI\ServiceDefinition|NULL
121
	 */
122
	private function registerCache(bool $enabled, $storage): ?Nette\DI\ServiceDefinition
123
	{
124
		if (FALSE === $enabled) {
125
			return NULL;
126
		}
127
128
		$builder = $this->getContainerBuilder();
129
130
		if (!is_string($storage) || !Nette\Utils\Strings::startsWith($storage, '@')) {
131
			$storage = $builder->addDefinition($this->prefix('cache.storage'))
132
				->setType(Nette\Caching\IStorage::class)
133
				->setFactory($storage)
134
				->setInject(FALSE);
135
		}
136
137
		return $builder->addDefinition($this->prefix('cache.cache'))
138
			->setType(Nette\Caching\Cache::class)
139
			->setArguments([
140
				'storage' => $storage,
141
				'namespace' => str_replace('\\', '.', SixtyEightPublishers\WebpackEncoreBundle\EntryPoint\IEntryPointLookup::class),
142
			])
143
			->setInject(FALSE);
144
	}
145
146
	/**
147
	 * @return array
148
	 * @throws \Nette\Utils\AssertionException
149
	 */
150
	private function getValidConfig(): array
151
	{
152
		/** @noinspection PhpInternalEntityUsedInspection */
153
		$config = $this->validateConfig(Nette\DI\Helpers::expand($this->defaults, $this->getContainerBuilder()->parameters));
154
155
		Nette\Utils\Validators::assertField($config, 'output_path', 'null|string');
156
		Nette\Utils\Validators::assertField($config['cache'], 'enabled', 'bool');
157
		Nette\Utils\Validators::assertField($config['cache'], 'storage', 'string|' . Nette\DI\Statement::class);
158
		Nette\Utils\Validators::assertField($config, 'builds', 'string[]');
159
		Nette\Utils\Validators::assertField($config['latte'], 'js_assets_macro_name', 'string');
160
		Nette\Utils\Validators::assertField($config['latte'], 'css_assets_macro_name', 'string');
161
		Nette\Utils\Validators::assertField($config, 'crossorigin', 'null|string');
162
163
		if (isset($config['builds'][self::ENTRYPOINT_DEFAULT_NAME])) {
164
			throw new Nette\Utils\AssertionException(sprintf('Key "%s" can\'t be used as build name.', self::ENTRYPOINT_DEFAULT_NAME));
165
		}
166
167
		if (!in_array($config['crossorigin'], self::CROSSORIGIN_ALLOWED_VALUES, TRUE)) {
168
			throw new Nette\Utils\AssertionException(sprintf('Value "%s" for setting "crossorigin" is not allowed', $config['crossorigin']));
169
		}
170
171
		return $config;
172
	}
173
}
174