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

nette / assets / 15081146475

17 May 2025 03:22AM UTC coverage: 95.056% (-0.5%) from 95.506%
15081146475

push

github

dg
readme: added info about custom mappers

423 of 445 relevant lines covered (95.06%)

0.95 hits per line

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

89.8
/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
 */
14
class ViteMapper implements Mapper
15
{
16
        private array $chunks;
17
        private array $dependencies = [];
18

19

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

27

28
        /**
29
         * Retrieves an Asset for a given Vite entry point.
30
         * @throws AssetNotFoundException when the asset cannot be found in the manifest
31
         */
32
        public function getAsset(string $reference, array $options = []): Asset
1✔
33
        {
34
                Helpers::checkOptions($options);
1✔
35

36
                $this->chunks ??= $this->readChunks();
1✔
37
                $entry = $this->chunks[$id = $reference] ?? $this->chunks[$id = $this->findByName($reference)] ?? null;
1✔
38

39
                if ($entry) {
1✔
40
                        if (str_starts_with($reference, '_') && !isset($entry['isEntry']) && !isset($entry['isDynamicEntry'])) {
1✔
41
                                throw new AssetNotFoundException("Cannot directly access internal chunk '$reference'");
1✔
42
                        }
43

44
                        $dependencies = $this->collectDependencies($id);
1✔
45
                        unset($dependencies[$entry['file']]);
1✔
46

47
                        return $dependencies
1✔
48
                                ? new EntryAsset(
1✔
49
                                        url: $this->baseUrl . '/' . $entry['file'],
1✔
50
                                        mimeType: Helpers::guessMimeTypeFromExtension($entry['file']),
1✔
51
                                        sourcePath: $this->basePath . '/' . $entry['file'],
1✔
52
                                        dependencies: array_values($dependencies),
1✔
53
                                )
54
                                : $this->createAsset($entry['file']);
1✔
55

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

59
                } else {
60
                        throw new AssetNotFoundException("File '$reference' not found in Vite manifest or at path: '$path'");
1✔
61
                }
62
        }
63

64

65
        /**
66
         * Recursively collects all imports (including nested) from a chunk.
67
         */
68
        private function collectDependencies(string $chunkId): array
1✔
69
        {
70
                $deps = &$this->dependencies[$chunkId];
1✔
71
                if ($deps === null) {
1✔
72
                        $deps = [];
1✔
73
                        $entry = $this->chunks[$chunkId] ?? [];
1✔
74
                        foreach ($entry['css'] ?? [] as $file) {
1✔
75
                                $deps[$file] = $this->createAsset($file);
1✔
76
                        }
77
                        foreach ($entry['imports'] ?? [] as $id) {
1✔
78
                                $file = $this->chunks[$id]['file'];
1✔
79
                                $deps[$file] = $this->createAsset($file);
1✔
80
                                $deps += $this->collectDependencies($id);
1✔
81
                        }
82
                }
83
                return $deps;
1✔
84
        }
85

86

87
        private function findByName(string $name): ?string
1✔
88
        {
89
                foreach ($this->chunks as $id => $entry) {
1✔
90
                        if (($entry['name'] ?? null) === $name && (isset($entry['isEntry']) || isset($entry['isDynamicEntry']))) {
1✔
91
                                return $id;
1✔
92
                        }
93
                }
94
                return null;
1✔
95
        }
96

97

98
        private function readChunks(): array
99
        {
100
                $path = $this->manifestPath ?? $this->basePath . '/.vite/manifest.json';
1✔
101
                try {
102
                        $res = Json::decode(FileSystem::read($path), forceArrays: true);
1✔
103
                } catch (\Throwable $e) {
×
104
                        throw new \RuntimeException('Failed to parse Vite manifest: ' . $e->getMessage(), 0, $e);
×
105
                }
106
                if (!is_array($res)) {
1✔
107
                        throw new \RuntimeException('Invalid Vite manifest format in ' . $path);
×
108
                }
109
                return $res;
1✔
110
        }
111

112

113
        private function createAsset(string $file): Asset
1✔
114
        {
115
                return Helpers::createAssetFromUrl($this->baseUrl . '/' . $file, $this->basePath . '/' . $file);
1✔
116
        }
117

118

119
        /**
120
         * Returns the base URL for this mapper.
121
         */
122
        public function getBaseUrl(): string
123
        {
124
                return $this->baseUrl;
×
125
        }
126

127

128
        /**
129
         * Returns the base path for this mapper.
130
         */
131
        public function getBasePath(): string
132
        {
133
                return $this->basePath;
×
134
        }
135
}
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