• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

68publishers / webpack-encore-bundle / 12719007642

10 Jan 2025 11:45PM UTC coverage: 91.379% (-4.7%) from 96.114%
12719007642

push

github

tg666
Stack update

- changed minimal PHP version to `8.3`
- dropped support for Latte v2
- dropped support for symfony components v5 and v6

318 of 348 relevant lines covered (91.38%)

0.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.57
/src/Bridge/Nette/DI/WebpackEncoreBundleExtension.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace SixtyEightPublishers\WebpackEncoreBundle\Bridge\Nette\DI;
6

7
use Latte\Engine;
8
use RuntimeException;
9
use Nette\Schema\Expect;
10
use Nette\Schema\Schema;
11
use Nette\DI\ContainerBuilder;
12
use Nette\DI\CompilerExtension;
13
use Nette\Application\Application;
14
use Nette\DI\Definitions\Reference;
15
use Nette\DI\Definitions\Statement;
16
use Symfony\Component\Asset\Packages;
17
use Nette\Http\IResponse as HttpResponse;
18
use Nette\DI\Definitions\FactoryDefinition;
19
use Nette\DI\Definitions\ServiceDefinition;
20
use Symfony\Component\Console\Command\Command;
21
use Nette\Bridges\ApplicationDI\ApplicationExtension;
22
use SixtyEightPublishers\WebpackEncoreBundle\Asset\EntryPointLookup;
23
use SixtyEightPublishers\WebpackEncoreBundle\Bridge\Latte\WebpackEncoreLatte2Extension;
24
use SixtyEightPublishers\WebpackEncoreBundle\Bridge\Latte\WebpackEncoreLatte3Extension;
25
use SixtyEightPublishers\WebpackEncoreBundle\Bridge\Nette\Application\ApplicationErrorHandler;
26
use SixtyEightPublishers\WebpackEncoreBundle\Bridge\Nette\Application\ApplicationResponseHandler;
27
use function assert;
28
use function array_keys;
29
use function class_exists;
30
use function rawurlencode;
31
use function version_compare;
32
use function array_key_exists;
33

34
final class WebpackEncoreBundleExtension extends CompilerExtension
35
{
36
        private const DEFAULT_BUILD_NAME = '_default';
37
        private const ENTRYPOINTS_FILE_NAME = 'entrypoints.json';
38

39
        /** @var array<string> */
40
        private array $buildNames;
41

42
        /**
43
         * {@inheritdoc}
44
         */
45
        public function getConfigSchema(): Schema
46
        {
47
                return Expect::structure([
1✔
48
                        'output_path' => Expect::string()->nullable(),
1✔
49
                        'crossorigin' => Expect::anyOf(FALSE, 'anonymous', 'use-credentials')->default(FALSE),
1✔
50
                        'preload' => Expect::bool(FALSE),
1✔
51
                        'cache' => Expect::bool(FALSE)
1✔
52
                                ->assert(static fn (bool $value) => !$value || class_exists(Command::class), 'You can\'t create cached entrypoints without symfony/console.'),
1✔
53
                        'strict_mode' => Expect::bool(TRUE),
1✔
54
                        'builds' => Expect::arrayOf('string', 'string')
1✔
55
                                ->assert(static fn (array $builds): bool => !array_key_exists(self::DEFAULT_BUILD_NAME, $builds), 'Key \'_default\' can\'t be used as build name.'),
1✔
56
                        'script_attributes' => Expect::arrayOf(
1✔
57
                                Expect::anyOf(Expect::string(), TRUE),
1✔
58
                                'string'
1✔
59
                        ),
60
                        'link_attributes' => Expect::arrayOf(
1✔
61
                                Expect::anyOf(Expect::string(), TRUE),
1✔
62
                                'string'
1✔
63
                        ),
64
                ])->castTo(WebpackEncoreConfig::class)
1✔
65
                        ->assert(static fn (object $config): bool => !(!isset($config->output_path) && empty($config->builds)), 'No build is defined.');
1✔
66
        }
67

68
        /**
69
         * {@inheritdoc}
70
         */
71
        public function loadConfiguration(): void
72
        {
73
                $builder = $this->getContainerBuilder();
1✔
74
                $config = $this->getConfig();
1✔
75
                assert($config instanceof WebpackEncoreConfig);
76

77
                $cacheFilename = $builder->parameters['tempDir'] . '/cache/webpack_encore.cache.php';
1✔
78
                $defaultAttributes = FALSE !== $config->crossorigin ? ['crossorigin' => $config->crossorigin] : [];
1✔
79

80
                $this->loadDefinitionsFromConfig($this->loadFromFile(__DIR__ . '/services.neon')['services']);
1✔
81

82
                $this->getServiceDefinition('cache.default')
1✔
83
                        ->setArgument('file', $cacheFilename);
1✔
84

85
                $this->getServiceDefinition('tag_renderer')
1✔
86
                        ->setArgument('defaultAttributes', $defaultAttributes)
1✔
87
                        ->setArgument('defaultScriptAttributes', $config->script_attributes)
1✔
88
                        ->setArgument('defaultLinkAttributes', $config->link_attributes);
1✔
89

90
                $entryPointLookupCollection = $this->getServiceDefinition('entrypoint_lookup_collection.default');
1✔
91
                $entryPointLookups = [];
1✔
92
                $cacheKeys = [];
1✔
93

94
                if (NULL !== $config->output_path) {
1✔
95
                        $path = $config->output_path . '/' . self::ENTRYPOINTS_FILE_NAME;
1✔
96
                        $entryPointLookups['_default'] = $this->createEntrypoint('_default', $path, $config->cache, $config->strict_mode);
1✔
97
                        $cacheKeys['_default'] = $path;
1✔
98

99
                        $entryPointLookupCollection->setArgument('defaultBuildName', '_default');
1✔
100
                }
101

102
                foreach ($config->builds as $name => $path) {
1✔
103
                        $path .= '/' . self::ENTRYPOINTS_FILE_NAME;
1✔
104
                        $entryPointLookups[$name] = $this->createEntrypoint($name, $path, $config->cache, $config->strict_mode);
1✔
105
                        $cacheKeys[rawurlencode($name)] = $path;
1✔
106
                }
107

108
                $this->buildNames = array_keys($entryPointLookups);
1✔
109

110
                $entryPointLookupCollection->setArgument('entryPointLookups', $entryPointLookups);
1✔
111

112
                if (class_exists(Command::class)) {
1✔
113
                        $this->loadDefinitionsFromConfig($this->loadFromFile(__DIR__ . '/console.neon')['services']);
1✔
114

115
                        $this->getServiceDefinition('console.command.warmup_cache')
1✔
116
                                ->setArgument('cacheKeys', $cacheKeys)
1✔
117
                                ->setArgument('cacheFile', $cacheFilename);
1✔
118
                }
119
        }
1✔
120

121
        /**
122
         * {@inheritdoc}
123
         */
124
        public function beforeCompile(): void
125
        {
126
                if (NULL === $this->getContainerBuilder()->getByType(Packages::class, FALSE)) {
1✔
127
                        throw new RuntimeException('Symfony Asset component is not integrated with your application. Please use 68publishers/asset or another integration solution.');
×
128
                }
129

130
                $config = $this->getConfig();
1✔
131
                assert($config instanceof WebpackEncoreConfig);
132

133
                if ($this->compiler->getExtensions(ApplicationExtension::class)) {
1✔
134
                        $this->beforeCompileApplicationHandlers($this->buildNames, $config->preload);
1✔
135
                }
136

137
                $this->beforeCompileLatte();
1✔
138
        }
1✔
139

140
        /**
141
         * @param array<string> $buildNames
142
         */
143
        private function beforeCompileApplicationHandlers(array $buildNames, bool $preload): void
144
        {
145
                $applicationDefinition = $this->getContainerBuilder()->getDefinitionByType(Application::class);
1✔
146
                assert($applicationDefinition instanceof ServiceDefinition);
147

148
                $applicationDefinition->addSetup('?::register(?, ?, ?)', [
1✔
149
                        ContainerBuilder::literal(ApplicationErrorHandler::class),
1✔
150
                        new Reference('self'),
1✔
151
                        new Reference($this->prefix('entrypoint_lookup_collection')),
1✔
152
                        $buildNames,
1✔
153
                ]);
154

155
                if ($preload) {
1✔
156
                        $applicationDefinition->addSetup('?::register(?, ?, ?, ?, ?)', [
×
157
                                ContainerBuilder::literal(ApplicationResponseHandler::class),
×
158
                                new Reference('self'),
×
159
                                new Reference(HttpResponse::class),
×
160
                                new Reference($this->prefix('tag_renderer')),
×
161
                                new Reference($this->prefix('entrypoint_lookup_collection')),
×
162
                                $buildNames,
×
163
                        ]);
164
                }
165
        }
1✔
166

167
        private function beforeCompileLatte(): void
168
        {
169
                $builder = $this->getContainerBuilder();
1✔
170
                $latteFactory = $builder->getDefinition($builder->getByType(Engine::class) ?? 'nette.latteFactory');
1✔
171
                assert($latteFactory instanceof FactoryDefinition);
172
                $resultDefinition = $latteFactory->getResultDefinition();
1✔
173

174
                if (version_compare(Engine::VERSION, '3', '<')) {
1✔
175
                        $resultDefinition->addSetup('?::extend(?, ?, ?)', [
×
176
                                ContainerBuilder::literal(WebpackEncoreLatte2Extension::class),
×
177
                                new Reference('self'),
×
178
                                new Reference($this->prefix('entrypoint_lookup_collection')),
×
179
                                new Reference($this->prefix('tag_renderer')),
×
180
                        ]);
181

182
                        return;
×
183
                }
184

185
                $resultDefinition->addSetup('addExtension', [
1✔
186
                        new Statement(WebpackEncoreLatte3Extension::class, [
1✔
187
                                new Reference($this->prefix('entrypoint_lookup_collection')),
1✔
188
                                new Reference($this->prefix('tag_renderer')),
1✔
189
                        ]),
190
                ]);
191
        }
1✔
192

193
        private function getServiceDefinition(string $name): ServiceDefinition
194
        {
195
                $definition = $this->getContainerBuilder()->getDefinition($this->prefix($name));
1✔
196
                assert($definition instanceof ServiceDefinition);
197

198
                return $definition;
1✔
199
        }
200

201
        private function createEntrypoint(string $name, string $path, bool $cacheEnabled, bool $strictMode): Reference
202
        {
203
                $serviceName = $this->prefix('entrypoint_lookup.' . $name);
1✔
204

205
                $this->getContainerBuilder()
1✔
206
                        ->addDefinition($serviceName)
1✔
207
                        ->setAutowired(FALSE)
1✔
208
                        ->setFactory(new Statement(EntryPointLookup::class, [
1✔
209
                                'entrypointJsonPath' => $path,
1✔
210
                                'cacheItemPool' => $cacheEnabled ? new Reference($this->prefix('cache')) : NULL,
1✔
211
                                'cacheKey' => $name,
1✔
212
                                'strictMode' => $strictMode,
1✔
213
                        ]));
214

215
                return new Reference($serviceName);
1✔
216
        }
217
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc