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

68publishers / webpack-encore-bundle / 13510931892

25 Feb 2025 12:37AM UTC coverage: 93.452% (+2.1%) from 91.379%
13510931892

push

github

tg666
PHP-Cs-Fixer should be run on PHP 8.3 because the package does not support PHP 8.4

314 of 336 relevant lines covered (93.45%)

0.93 hits per line

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

91.11
/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 Nette\Application\Application;
9
use Nette\Bridges\ApplicationDI\ApplicationExtension;
10
use Nette\DI\CompilerExtension;
11
use Nette\DI\ContainerBuilder;
12
use Nette\DI\Definitions\FactoryDefinition;
13
use Nette\DI\Definitions\Reference;
14
use Nette\DI\Definitions\ServiceDefinition;
15
use Nette\DI\Definitions\Statement;
16
use Nette\Http\IResponse as HttpResponse;
17
use Nette\Schema\Expect;
18
use Nette\Schema\Schema;
19
use RuntimeException;
20
use SixtyEightPublishers\WebpackEncoreBundle\Asset\EntryPointLookup;
21
use SixtyEightPublishers\WebpackEncoreBundle\Bridge\Latte\WebpackEncoreLatteExtension;
22
use SixtyEightPublishers\WebpackEncoreBundle\Bridge\Nette\Application\ApplicationErrorHandler;
23
use SixtyEightPublishers\WebpackEncoreBundle\Bridge\Nette\Application\ApplicationResponseHandler;
24
use Symfony\Component\Asset\Packages;
25
use Symfony\Component\Console\Command\Command;
26
use function array_key_exists;
27
use function array_keys;
28
use function assert;
29
use function class_exists;
30
use function rawurlencode;
31

32
final class WebpackEncoreBundleExtension extends CompilerExtension
33
{
34
    private const string DefaultBuildName = '_default';
35
    private const string EntrypointsFilename = 'entrypoints.json';
36

37
    /** @var list<string> */
38
    private array $buildNames;
39

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

63
    public function loadConfiguration(): void
64
    {
65
        $builder = $this->getContainerBuilder();
1✔
66
        $config = $this->getConfig();
1✔
67
        assert($config instanceof WebpackEncoreConfig);
68

69
        $cacheFilename = $builder->parameters['tempDir'] . '/cache/webpack_encore.cache.php';
1✔
70
        $defaultAttributes = false !== $config->crossorigin ? ['crossorigin' => $config->crossorigin] : [];
1✔
71

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

74
        $this->getServiceDefinition('cache.default')
1✔
75
            ->setArgument('file', $cacheFilename);
1✔
76

77
        $this->getServiceDefinition('tag_renderer')
1✔
78
            ->setArgument('defaultAttributes', $defaultAttributes)
1✔
79
            ->setArgument('defaultScriptAttributes', $config->script_attributes)
1✔
80
            ->setArgument('defaultLinkAttributes', $config->link_attributes);
1✔
81

82
        $entryPointLookupCollection = $this->getServiceDefinition('entrypoint_lookup_collection.default');
1✔
83
        $entryPointLookups = [];
1✔
84
        $cacheKeys = [];
1✔
85

86
        if (null !== $config->output_path) {
1✔
87
            $path = $config->output_path . '/' . self::EntrypointsFilename;
1✔
88
            $entryPointLookups['_default'] = $this->createEntrypoint('_default', $path, $config->cache, $config->strict_mode);
1✔
89
            $cacheKeys['_default'] = $path;
1✔
90

91
            $entryPointLookupCollection->setArgument('defaultBuildName', '_default');
1✔
92
        }
93

94
        foreach ($config->builds as $name => $path) {
1✔
95
            $path .= '/' . self::EntrypointsFilename;
1✔
96
            $entryPointLookups[$name] = $this->createEntrypoint($name, $path, $config->cache, $config->strict_mode);
1✔
97
            $cacheKeys[rawurlencode($name)] = $path;
1✔
98
        }
99

100
        $this->buildNames = array_keys($entryPointLookups);
1✔
101

102
        $entryPointLookupCollection->setArgument('entryPointLookups', $entryPointLookups);
1✔
103

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

107
            $this->getServiceDefinition('console.command.warmup_cache')
1✔
108
                ->setArgument('cacheKeys', $cacheKeys)
1✔
109
                ->setArgument('cacheFile', $cacheFilename);
1✔
110
        }
111
    }
1✔
112

113
    public function beforeCompile(): void
114
    {
115
        if (null === $this->getContainerBuilder()->getByType(Packages::class, false)) {
1✔
116
            throw new RuntimeException('Symfony Asset component is not integrated with your application. Please use 68publishers/asset or another integration solution.');
×
117
        }
118

119
        $config = $this->getConfig();
1✔
120
        assert($config instanceof WebpackEncoreConfig);
121

122
        if ($this->compiler->getExtensions(ApplicationExtension::class)) {
1✔
123
            $this->beforeCompileApplicationHandlers($this->buildNames, $config->preload);
1✔
124
        }
125

126
        $this->beforeCompileLatte();
1✔
127
    }
1✔
128

129
    /**
130
     * @param list<string> $buildNames
131
     */
132
    private function beforeCompileApplicationHandlers(array $buildNames, bool $preload): void
133
    {
134
        $applicationDefinition = $this->getContainerBuilder()->getDefinitionByType(Application::class);
1✔
135
        assert($applicationDefinition instanceof ServiceDefinition);
136

137
        $applicationDefinition->addSetup('?::register(?, ?, ?)', [
1✔
138
            ContainerBuilder::literal(ApplicationErrorHandler::class),
1✔
139
            new Reference('self'),
1✔
140
            new Reference($this->prefix('entrypoint_lookup_collection')),
1✔
141
            $buildNames,
1✔
142
        ]);
143

144
        if ($preload) {
1✔
145
            $applicationDefinition->addSetup('?::register(?, ?, ?, ?, ?)', [
×
146
                ContainerBuilder::literal(ApplicationResponseHandler::class),
×
147
                new Reference('self'),
×
148
                new Reference(HttpResponse::class),
×
149
                new Reference($this->prefix('tag_renderer')),
×
150
                new Reference($this->prefix('entrypoint_lookup_collection')),
×
151
                $buildNames,
×
152
            ]);
153
        }
154
    }
1✔
155

156
    private function beforeCompileLatte(): void
157
    {
158
        $builder = $this->getContainerBuilder();
1✔
159
        $latteFactory = $builder->getDefinition($builder->getByType(Engine::class) ?? 'nette.latteFactory');
1✔
160
        assert($latteFactory instanceof FactoryDefinition);
161
        $resultDefinition = $latteFactory->getResultDefinition();
1✔
162

163
        $resultDefinition->addSetup('addExtension', [
1✔
164
            new Statement(WebpackEncoreLatteExtension::class, [
1✔
165
                new Reference($this->prefix('entrypoint_lookup_collection')),
1✔
166
                new Reference($this->prefix('tag_renderer')),
1✔
167
            ]),
168
        ]);
169
    }
1✔
170

171
    private function getServiceDefinition(string $name): ServiceDefinition
172
    {
173
        $definition = $this->getContainerBuilder()->getDefinition($this->prefix($name));
1✔
174
        assert($definition instanceof ServiceDefinition);
175

176
        return $definition;
1✔
177
    }
178

179
    private function createEntrypoint(string $name, string $path, bool $cacheEnabled, bool $strictMode): Reference
180
    {
181
        $serviceName = $this->prefix('entrypoint_lookup.' . $name);
1✔
182

183
        $this->getContainerBuilder()
1✔
184
            ->addDefinition($serviceName)
1✔
185
            ->setAutowired(false)
1✔
186
            ->setFactory(new Statement(EntryPointLookup::class, [
1✔
187
                'entrypointJsonPath' => $path,
1✔
188
                'cacheItemPool' => $cacheEnabled ? new Reference($this->prefix('cache')) : null,
1✔
189
                'cacheKey' => $name,
1✔
190
                'strictMode' => $strictMode,
1✔
191
            ]));
192

193
        return new Reference($serviceName);
1✔
194
    }
195
}
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