Skip to content

Commit 4ca0373

Browse files
committed
Merge pull request #17 from whalebone/feature/assets-webpack-plugin
ManifestMapper feature
2 parents e8b0cae + 1583a68 commit 4ca0373

File tree

11 files changed

+216
-10
lines changed

11 files changed

+216
-10
lines changed

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ webpack:
8080

8181
You might want to include the Webpack's asset hash in its file name for assets caching (and automatic cache busting in new releases) in the user agent. But how do you reference the asset files in your code if their names are dynamic?
8282

83-
WebpackNetteAdapter comes to the rescue. You can employ the [webpack-manifest-plugin](https://www.npmjs.com/package/webpack-manifest-plugin) or some similar plugin to produce a manifest file, and then configure the adapter to use it:
83+
WebpackNetteAdapter comes to the rescue. You can employ the [`webpack-manifest-plugin`](https://www.npmjs.com/package/webpack-manifest-plugin) or some similar plugin (see below) to produce a manifest file, and then configure the adapter to use it:
8484

8585
```yaml
8686
webpack:
@@ -93,6 +93,20 @@ This way, you can keep using the original asset names, and they get expanded aut
9393
WebpackNetteAdapter automatically optimizes this in production environment by loading the manifest file in compile time.
9494

9595

96+
#### Manifest mappers
97+
98+
By default, WebpackNetteAdapter supports the aforementioned `webpack-manifest-plugin`. If you use a different plugin that produces the manifest in a different format, you can implement and configure a mapper for it. WebpackNetteAdapter comes bundled with a mapper for the [`assets-webpack-plugin`](https://www.npmjs.com/package/assets-webpack-plugin):
99+
100+
```yaml
101+
webpack:
102+
manifest:
103+
name: manifest.json
104+
mapper: Oops\WebpackNetteAdapter\Manifest\Mapper\AssetsWebpackPluginMapper
105+
```
106+
107+
You can also implement your own mapper, simply extend `Oops\WebpackNetteAdapter\Manifest\ManifestMapper` and implement its `map()` method. It takes the parsed JSON content of the manifest file and is expected to return a flat array mapping asset names to file names.
108+
109+
96110
### Debugger
97111

98112
In development environment, WebpackNetteAdapter registers its own debug bar panel into Tracy, giving you the overview of

src/DI/WebpackExtension.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Oops\WebpackNetteAdapter\BuildDirectoryProvider;
1919
use Oops\WebpackNetteAdapter\Debugging\WebpackPanel;
2020
use Oops\WebpackNetteAdapter\DevServer;
21+
use Oops\WebpackNetteAdapter\Manifest\Mapper\WebpackManifestPluginMapper;
2122
use Oops\WebpackNetteAdapter\Manifest\ManifestLoader;
2223
use Oops\WebpackNetteAdapter\PublicPathProvider;
2324
use Tracy;
@@ -49,6 +50,7 @@ class WebpackExtension extends CompilerExtension
4950
'manifest' => [
5051
'name' => NULL,
5152
'optimize' => NULL,
53+
'mapper' => WebpackManifestPluginMapper::class,
5254
]
5355
];
5456

@@ -81,7 +83,6 @@ public function loadConfiguration(): void
8183
throw new ConfigurationException('You need to specify the dev server URL.');
8284
}
8385

84-
8586
$basePathProvider = $builder->addDefinition($this->prefix('pathProvider.basePathProvider'))
8687
->setType(BasePathProvider::class)
8788
->setFactory(NetteHttpBasePathProvider::class)
@@ -163,7 +164,9 @@ private function setupAssetResolver(array $config): ServiceDefinition
163164
if ($config['manifest']['name'] !== NULL) {
164165
if ( ! $config['manifest']['optimize']) {
165166
$loader = $builder->addDefinition($this->prefix('manifestLoader'))
166-
->setFactory(ManifestLoader::class)
167+
->setFactory(ManifestLoader::class, [
168+
1 => new Statement($config['manifest']['mapper']),
169+
])
167170
->setAutowired(FALSE);
168171

169172
$assetResolver->setFactory(AssetNameResolver\ManifestAssetNameResolver::class, [
@@ -176,12 +179,14 @@ private function setupAssetResolver(array $config): ServiceDefinition
176179
$config['devServer']['enabled'],
177180
$config['devServer']['url'] ?? '',
178181
$config['devServer']['publicUrl'] ?? '',
179-
$config['devServer']['timeout'] ?? 0.1,
182+
$config['devServer']['timeout'],
180183
new Client()
181184
);
182185

186+
$mapperInstance = new $config['manifest']['mapper']();
187+
183188
$directoryProviderInstance = new BuildDirectoryProvider($config['build']['directory'], $devServerInstance);
184-
$loaderInstance = new ManifestLoader($directoryProviderInstance);
189+
$loaderInstance = new ManifestLoader($directoryProviderInstance, $mapperInstance);
185190
$manifestCache = $loaderInstance->loadManifest($config['manifest']['name']);
186191

187192
$assetResolver->setFactory(AssetNameResolver\StaticAssetNameResolver::class, [$manifestCache]);

src/Manifest/ManifestLoader.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@ class ManifestLoader
1919
*/
2020
private $directoryProvider;
2121

22+
/**
23+
* @var ManifestMapper
24+
*/
25+
private $manifestMapper;
26+
2227

23-
public function __construct(BuildDirectoryProvider $directoryProvider)
28+
public function __construct(BuildDirectoryProvider $directoryProvider, ManifestMapper $manifestMapper)
2429
{
2530
$this->directoryProvider = $directoryProvider;
31+
$this->manifestMapper = $manifestMapper;
2632
}
2733

2834

@@ -33,7 +39,7 @@ public function __construct(BuildDirectoryProvider $directoryProvider)
3339
public function loadManifest(string $fileName): array
3440
{
3541
$path = $this->getManifestPath($fileName);
36-
$context = \stream_context_create(['ssl' => ['verify_peer' => FALSE]]); // webpack-dev-server uses self-signed certificate
42+
$context = \stream_context_create(['ssl' => ['verify_peer' => FALSE, 'verify_peer_name' => FALSE]]); // webpack-dev-server uses self-signed certificate
3743
$manifest = @\file_get_contents($path, FALSE, $context); // @ - errors handled by custom exception
3844

3945
if ($manifest === FALSE) {
@@ -43,7 +49,7 @@ public function loadManifest(string $fileName): array
4349
));
4450
}
4551

46-
return Json::decode($manifest, Json::FORCE_ARRAY);
52+
return $this->manifestMapper->map(Json::decode($manifest, Json::FORCE_ARRAY));
4753
}
4854

4955

src/Manifest/ManifestMapper.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Oops\WebpackNetteAdapter\Manifest;
6+
7+
abstract class ManifestMapper
8+
{
9+
final public function __construct()
10+
{
11+
}
12+
13+
/**
14+
* Modifies manifest contents
15+
* @param array<mixed> $manifest
16+
* @return array<string, string>
17+
*/
18+
abstract public function map(array $manifest) : array;
19+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Oops\WebpackNetteAdapter\Manifest\Mapper;
6+
7+
use Oops\WebpackNetteAdapter\Manifest\ManifestMapper;
8+
9+
10+
/**
11+
* Maps from https://github.com/ztoben/assets-webpack-plugin format to flat files
12+
*
13+
* from
14+
* {
15+
* "main": {
16+
* "js": "/public/path/to/asset"
17+
* }
18+
* }
19+
*
20+
* to flat used in ManifestAssetNameResolver:
21+
* {
22+
* "main.js": "/public/path/to/asset"
23+
* }
24+
*
25+
*/
26+
class AssetsWebpackPluginMapper extends ManifestMapper
27+
{
28+
/**
29+
* @inheritDoc
30+
*/
31+
public function map(array $manifest): array
32+
{
33+
$result = [];
34+
foreach ($manifest as $main => $parts) {
35+
foreach ($parts as $name => $file) {
36+
$result[$main . '.' . $name] = $file;
37+
}
38+
}
39+
return $result;
40+
}
41+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Oops\WebpackNetteAdapter\Manifest\Mapper;
6+
7+
use Oops\WebpackNetteAdapter\Manifest\ManifestMapper;
8+
9+
/**
10+
* Default identity mapper compatible with webpack-manifest-mapper's flat structure.
11+
*/
12+
class WebpackManifestPluginMapper extends ManifestMapper
13+
{
14+
public function map(array $manifest): array
15+
{
16+
return $manifest;
17+
}
18+
}

tests/WebpackNetteAdapter/DI/WebpackExtensionTest.phpt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ class WebpackExtensionTest extends TestCase
8989
}
9090

9191

92+
public function testManifestResolverWithMapper(): void
93+
{
94+
$container = $this->createContainer('manifestWithMapper');
95+
Assert::type(ManifestAssetNameResolver::class, $container->getByType(AssetNameResolverInterface::class));
96+
}
97+
98+
9299
public function testOptimizedManifest(): void
93100
{
94101
\putenv('OOPS_WEBPACK_OPTIMIZE_MANIFEST=1');
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
extensions:
2+
webpack: Oops\WebpackNetteAdapter\DI\WebpackExtension(false)
3+
4+
5+
webpack:
6+
build:
7+
directory: %buildDir%
8+
publicPath: dist/
9+
10+
manifest:
11+
name: manifest.json
12+
optimize: false
13+
mapper: Oops\WebpackNetteAdapter\Manifest\Mapper\AssetsWebpackPluginMapper

tests/WebpackNetteAdapter/Manifest/ManifestLoaderTest.phpt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace OopsTests\WebpackNetteAdapter\Manifest;
77
use Oops\WebpackNetteAdapter\BuildDirectoryProvider;
88
use Oops\WebpackNetteAdapter\Manifest\CannotLoadManifestException;
99
use Oops\WebpackNetteAdapter\Manifest\ManifestLoader;
10+
use Oops\WebpackNetteAdapter\Manifest\ManifestMapper;
1011
use Tester\Assert;
1112
use Tester\TestCase;
1213

@@ -24,10 +25,16 @@ class ManifestLoaderTest extends TestCase
2425
{
2526
$buildDirProvider = \Mockery::mock(BuildDirectoryProvider::class);
2627
$buildDirProvider->shouldReceive('getBuildDirectory')->andReturn(__DIR__);
27-
$manifestLoader = new ManifestLoader($buildDirProvider);
28+
29+
$manifestMapper = \Mockery::mock(ManifestMapper::class);
30+
$manifestMapper->shouldReceive('map')
31+
->with(['asset.js' => 'resolved.asset.js'])
32+
->andReturn(['asset.js' => 'mapped.asset.js']);
33+
34+
$manifestLoader = new ManifestLoader($buildDirProvider, $manifestMapper);
2835

2936
Assert::same(__DIR__ . '/manifest.json', $manifestLoader->getManifestPath('manifest.json'));
30-
Assert::same(['asset.js' => 'resolved.asset.js'], $manifestLoader->loadManifest('manifest.json'));
37+
Assert::same(['asset.js' => 'mapped.asset.js'], $manifestLoader->loadManifest('manifest.json'));
3138

3239
Assert::throws(function () use ($manifestLoader) {
3340
$manifestLoader->loadManifest('unknown.js');
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace OopsTests\WebpackNetteAdapter\Manifest\Mapper;
6+
7+
use Oops\WebpackNetteAdapter\Manifest\Mapper\AssetsWebpackPluginMapper;
8+
use Tester\Assert;
9+
use Tester\TestCase;
10+
11+
12+
require_once __DIR__ . '/../../../bootstrap.php';
13+
14+
15+
/**
16+
* @testCase
17+
*/
18+
class AssetsWebpackPluginMapperTest extends TestCase
19+
{
20+
21+
public function testMapper(): void
22+
{
23+
$mapper = new AssetsWebpackPluginMapper();
24+
25+
$result = $mapper->map([
26+
'asset' => [
27+
'js' => 'resolved.asset.js',
28+
],
29+
]);
30+
31+
Assert::same([
32+
'asset.js' => 'resolved.asset.js',
33+
], $result);
34+
}
35+
36+
}
37+
38+
39+
(new AssetsWebpackPluginMapperTest())->run();

0 commit comments

Comments
 (0)