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

nette / assets / 15128047270

20 May 2025 03:17AM UTC coverage: 95.089% (-0.3%) from 95.395%
15128047270

push

github

dg
DIExtension: parameters are passed by constructor (BC break)

5 of 5 new or added lines in 1 file covered. (100.0%)

10 existing lines in 2 files now uncovered.

426 of 448 relevant lines covered (95.09%)

0.95 hits per line

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

91.23
/src/Assets/ViteMapper.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Nette\Assets;
6

7
use Nette\Utils\FileSystem;
8
use Nette\Utils\Json;
9

10

11
/**
12
 * Maps asset references to Vite-generated files using a Vite manifest.json.
13
 * Supports both development mode (Vite dev server) and production mode.
14
 */
15
class ViteMapper implements Mapper
16
{
17
        private array $chunks;
18
        private array $dependencies = [];
19

20

21
        public function __construct(
1✔
22
                private readonly string $baseUrl,
23
                private readonly string $basePath,
24
                private readonly ?string $manifestPath = null,
25
                private readonly ?string $devServer = null,
26
        ) {
27
        }
1✔
28

29

30
        /**
31
         * Retrieves an Asset for a given Vite entry point.
32
         * In dev mode, returns assets pointing to Vite dev server.
33
         * In production, uses the manifest to find the file and its dependencies.
34
         * @throws AssetNotFoundException when the asset cannot be found in the manifest
35
         */
36
        public function getAsset(string $reference, array $options = []): Asset
1✔
37
        {
38
                Helpers::checkOptions($options);
1✔
39

40
                if (!empty($this->devServer)) {
1✔
41
                        $url = $this->devServer . '/' . $reference;
1✔
42
                        return new EntryAsset(
1✔
43
                                url: $url,
1✔
44
                                mimeType: Helpers::guessMimeTypeFromExtension($url),
1✔
45
                                dependencies: [],
1✔
46
                                file: null,
1✔
47
                                integrity: null,
1✔
48
                        );
49
                }
50

51
                $this->chunks ??= $this->readChunks();
1✔
52
                $entry = $this->chunks[$id = $reference] ?? $this->chunks[$id = $this->findByName($reference)] ?? null;
1✔
53

54
                if ($entry) {
1✔
55
                        if (str_starts_with($reference, '_') && !isset($entry['isEntry']) && !isset($entry['isDynamicEntry'])) {
1✔
56
                                throw new AssetNotFoundException("Cannot directly access internal chunk '$reference'");
1✔
57
                        }
58

59
                        $dependencies = $this->collectDependencies($id);
1✔
60
                        unset($dependencies[$entry['file']]);
1✔
61

62
                        return $dependencies
1✔
63
                                ? new EntryAsset(
1✔
64
                                        url: $this->baseUrl . '/' . $entry['file'],
1✔
65
                                        mimeType: Helpers::guessMimeTypeFromExtension($entry['file']),
1✔
66
                                        file: $this->basePath . '/' . $entry['file'],
1✔
67
                                        dependencies: array_values($dependencies),
1✔
68
                                )
69
                                : $this->createAsset($entry['file']);
1✔
70

71
                } elseif (is_file($path = $this->basePath . '/' . $reference)) {
1✔
72
                        return Helpers::createAssetFromUrl($this->baseUrl . '/' . $reference, $path);
1✔
73

74
                } else {
75
                        throw new AssetNotFoundException("File '$reference' not found in Vite manifest or at path: '$path'");
1✔
76
                }
77
        }
78

79

80
        /**
81
         * Recursively collects all imports (including nested) from a chunk.
82
         */
83
        private function collectDependencies(string $chunkId): array
1✔
84
        {
85
                $deps = &$this->dependencies[$chunkId];
1✔
86
                if ($deps === null) {
1✔
87
                        $deps = [];
1✔
88
                        $entry = $this->chunks[$chunkId] ?? [];
1✔
89
                        foreach ($entry['css'] ?? [] as $file) {
1✔
90
                                $deps[$file] = $this->createAsset($file);
1✔
91
                        }
92
                        foreach ($entry['imports'] ?? [] as $id) {
1✔
93
                                $file = $this->chunks[$id]['file'];
1✔
94
                                $deps[$file] = $this->createAsset($file);
1✔
95
                                $deps += $this->collectDependencies($id);
1✔
96
                        }
97
                }
98
                return $deps;
1✔
99
        }
100

101

102
        private function findByName(string $name): ?string
1✔
103
        {
104
                foreach ($this->chunks as $id => $entry) {
1✔
105
                        if (($entry['name'] ?? null) === $name && (isset($entry['isEntry']) || isset($entry['isDynamicEntry']))) {
1✔
106
                                return $id;
1✔
107
                        }
108
                }
109
                return null;
1✔
110
        }
111

112

113
        private function readChunks(): array
114
        {
115
                $path = $this->manifestPath ?? $this->basePath . '/.vite/manifest.json';
1✔
116
                try {
117
                        $res = Json::decode(FileSystem::read($path), forceArrays: true);
1✔
UNCOV
118
                } catch (\Throwable $e) {
×
UNCOV
119
                        throw new \RuntimeException('Failed to parse Vite manifest: ' . $e->getMessage(), 0, $e);
×
120
                }
121
                if (!is_array($res)) {
1✔
UNCOV
122
                        throw new \RuntimeException('Invalid Vite manifest format in ' . $path);
×
123
                }
124
                return $res;
1✔
125
        }
126

127

128
        private function createAsset(string $file): Asset
1✔
129
        {
130
                return Helpers::createAssetFromUrl($this->baseUrl . '/' . $file, $this->basePath . '/' . $file);
1✔
131
        }
132

133

134
        /**
135
         * Returns the base URL for this mapper.
136
         */
137
        public function getBaseUrl(): string
138
        {
UNCOV
139
                return $this->baseUrl;
×
140
        }
141

142

143
        /**
144
         * Returns the base path for this mapper.
145
         */
146
        public function getBasePath(): string
147
        {
UNCOV
148
                return $this->basePath;
×
149
        }
150
}
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

© 2026 Coveralls, Inc