diff --git a/.docs/README.md b/.docs/README.md index 42a821a..13bc655 100644 --- a/.docs/README.md +++ b/.docs/README.md @@ -101,6 +101,19 @@ webpack: You can also implement your own mapper, simply extend `Contributte\Webpack\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. + +#### Manifest loading timeout + +You can specify a timeout for manifest loading from webpack-dev-server. The timeout defaults to 1 second. + +```neon +webpack: + manifest: + name: manifest.json + timeout: 0.5 +``` + + ### Debugger In development environment, this package registers its own debug bar panel into Tracy, giving you the overview of diff --git a/src/DI/WebpackExtension.php b/src/DI/WebpackExtension.php index 69510af..26ffbf5 100644 --- a/src/DI/WebpackExtension.php +++ b/src/DI/WebpackExtension.php @@ -64,6 +64,7 @@ public function getConfigSchema(): Schema 'name' => Expect::string()->nullable(), 'optimize' => Expect::bool(!$this->debugMode && (!$this->consoleMode || (bool) \getenv('CONTRIBUTTE_WEBPACK_OPTIMIZE_MANIFEST'))), 'mapper' => Expect::anyOf(Expect::string(), Expect::type(Statement::class))->default(WebpackManifestPluginMapper::class), + 'timeout' => Expect::anyOf(Expect::float(), Expect::int())->default(1), ])->castTo('array'), ])->castTo('array'); } @@ -151,7 +152,8 @@ private function setupAssetResolver(array $config): ServiceDefinition if (!$config['manifest']['optimize']) { $loader = $builder->addDefinition($this->prefix('manifestLoader')) ->setFactory(ManifestLoader::class, [ - 1 => new Statement($config['manifest']['mapper']), + 'manifestMapper' => new Statement($config['manifest']['mapper']), + 'timeout' => $config['manifest']['timeout'], ]) ->setAutowired(false); @@ -165,7 +167,7 @@ private function setupAssetResolver(array $config): ServiceDefinition $mapperInstance = new $config['manifest']['mapper'](); $directoryProviderInstance = new BuildDirectoryProvider($config['build']['directory'], $devServerInstance); - $loaderInstance = new ManifestLoader($directoryProviderInstance, $mapperInstance); + $loaderInstance = new ManifestLoader($directoryProviderInstance, $mapperInstance, $config['manifest']['timeout']); $manifestCache = $loaderInstance->loadManifest($config['manifest']['name']); $assetResolver->setFactory(AssetNameResolver\StaticAssetNameResolver::class, [$manifestCache]); diff --git a/src/Manifest/ManifestLoader.php b/src/Manifest/ManifestLoader.php index 05cedca..9404776 100644 --- a/src/Manifest/ManifestLoader.php +++ b/src/Manifest/ManifestLoader.php @@ -16,10 +16,16 @@ final class ManifestLoader private ManifestMapper $manifestMapper; - public function __construct(BuildDirectoryProvider $directoryProvider, ManifestMapper $manifestMapper) - { + private float $timeout; + + public function __construct( + BuildDirectoryProvider $directoryProvider, + ManifestMapper $manifestMapper, + float $timeout + ) { $this->directoryProvider = $directoryProvider; $this->manifestMapper = $manifestMapper; + $this->timeout = $timeout; } /** @@ -39,9 +45,16 @@ public function loadManifest(string $fileName): array $manifest = false; } else { \curl_setopt_array($ch, [ + \CURLOPT_CUSTOMREQUEST => 'GET', + \CURLOPT_PROTOCOLS => \CURLPROTO_HTTP | \CURLPROTO_HTTPS, + \CURLOPT_RETURNTRANSFER => true, \CURLOPT_FAILONERROR => true, + // setup timeout; this requires NOSIGNAL for values below 1s + \CURLOPT_TIMEOUT_MS => $this->timeout * 1000, + \CURLOPT_NOSIGNAL => $this->timeout < 1 && \PHP_OS_FAMILY !== 'Windows', + // allow self-signed certificates \CURLOPT_SSL_VERIFYHOST => 0, \CURLOPT_SSL_VERIFYPEER => false, @@ -61,7 +74,7 @@ public function loadManifest(string $fileName): array throw new CannotLoadManifestException(\sprintf( "Manifest file '%s' could not be loaded: %s", $path, - $errorMessage ?? (\error_get_last()['message'] ?? 'unknown error') + $errorMessage ?? \error_get_last()['message'] ?? 'unknown error', )); } diff --git a/tests/AssetNameResolver/ManifestAssetNameResolverTest.phpt b/tests/AssetNameResolver/ManifestAssetNameResolverTest.phpt index 7aa8af8..f90de14 100644 --- a/tests/AssetNameResolver/ManifestAssetNameResolverTest.phpt +++ b/tests/AssetNameResolver/ManifestAssetNameResolverTest.phpt @@ -24,6 +24,7 @@ final class ManifestAssetNameResolverTest extends TestCase $manifestLoader = new ManifestLoader( createBuildDirectoryProvider(__DIR__), new WebpackManifestPluginMapper(), + 1, ); $resolver = new ManifestAssetNameResolver('manifest.json', $manifestLoader); @@ -40,6 +41,7 @@ final class ManifestAssetNameResolverTest extends TestCase // parent directory doesn't contain manifest.json => throws CannotLoadManifestException createBuildDirectoryProvider(__DIR__ . '/Webpack'), new WebpackManifestPluginMapper(), + 1, ); $resolver = new ManifestAssetNameResolver('manifest.json', $manifestLoader); diff --git a/tests/Manifest/ManifestLoaderTest.phpt b/tests/Manifest/ManifestLoaderTest.phpt index bf0e7b7..689651a 100644 --- a/tests/Manifest/ManifestLoaderTest.phpt +++ b/tests/Manifest/ManifestLoaderTest.phpt @@ -30,7 +30,7 @@ final class ManifestLoaderTest extends TestCase } }; - $manifestLoader = new ManifestLoader($buildDirProvider, $manifestMapper); + $manifestLoader = new ManifestLoader($buildDirProvider, $manifestMapper, 1); Assert::same(__DIR__ . '/manifest.json', $manifestLoader->getManifestPath('manifest.json')); Assert::same(['asset.js' => 'mapped.asset.js'], $manifestLoader->loadManifest('manifest.json'));