mimmi20/laminasviewrenderer-vite-url

View on GitHub
src/View/Helper/ViteUrl.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php
/**
 * This file is part of the mimmi20/laminasviewrenderer-vite-url package.
 *
 * Copyright (c) 2023-2024, Thomas Mueller <mimmi20@live.de>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types = 1);

namespace Mimmi20\LaminasView\ViteUrl\View\Helper;

use JsonException;
use Laminas\View\Exception\RuntimeException;
use Laminas\View\Helper\AbstractHelper;
use Laminas\View\Renderer\PhpRenderer;

use function file_get_contents;
use function is_file;
use function json_decode;
use function rtrim;
use function sprintf;

use const JSON_THROW_ON_ERROR;

final class ViteUrl extends AbstractHelper
{
    /** @throws void */
    public function __construct(private readonly string | null $publicDir, private readonly string | null $buildDir)
    {
        // nothing to do
    }

    /** @throws void */
    public function __invoke(): self
    {
        return $this;
    }

    /**
     * @throws void
     *
     * @api
     */
    public function getPublicDir(): string | null
    {
        return $this->publicDir;
    }

    /**
     * @throws void
     *
     * @api
     */
    public function getBuildDir(): string | null
    {
        return $this->buildDir;
    }

    /**
     * @throws RuntimeException
     *
     * @api
     */
    public function js(string $name): string
    {
        if ($this->publicDir === null) {
            throw new RuntimeException('A Public Dir is required');
        }

        $view = $this->getView();

        if (!$view instanceof PhpRenderer) {
            throw new RuntimeException('A PHP View Renderer is required');
        }

        $server = $this->hotServer();

        if ($server) {
            return $server . '/' . $name;
        }

        $manifest = $this->manifestContents();

        if (!isset($manifest[$name]['file'])) {
            throw new RuntimeException('Unknown Vite JS entrypoint ' . $name);
        }

        return $view->serverUrl('/' . $this->buildDir . '/' . $manifest[$name]['file']);
    }

    /**
     * @throws RuntimeException
     *
     * @api
     */
    public function css(string $name): string
    {
        if ($this->publicDir === null) {
            throw new RuntimeException('A Public Dir is required');
        }

        $view = $this->getView();

        if (!$view instanceof PhpRenderer) {
            throw new RuntimeException('A PHP View Renderer is required');
        }

        $server = $this->hotServer();

        if ($server) {
            return $server . '/' . $name;
        }

        $manifest = $this->manifestContents();

        if (!isset($manifest[$name]['file'])) {
            throw new RuntimeException('Unknown Vite CSS entrypoint ' . $name);
        }

        return $view->serverUrl('/' . $this->buildDir . '/' . $manifest[$name]['file']);
    }

    /** @throws void */
    private function hotServer(): string | null
    {
        if (!is_file($this->publicDir . '/hot')) {
            return null;
        }

        $content = file_get_contents($this->publicDir . '/hot');

        if (!$content) {
            return null;
        }

        return rtrim($content);
    }

    /**
     * Retrieve our manifest file contents.
     *
     * @return array<string, array{file: string, imports: array<string, mixed>, css: array<string, mixed>}>
     *
     * @throws RuntimeException
     */
    private function manifestContents(): array
    {
        if ($this->buildDir === null) {
            throw new RuntimeException('A Build Dir is required');
        }

        $manifestPathV4 = $this->publicDir . '/' . $this->buildDir . '/manifest.json';
        $manifestPathV5 = $this->publicDir . '/' . $this->buildDir . '/.vite/manifest.json';

        if (is_file($manifestPathV5)) {
            $manifestPath = $manifestPathV5;
        } elseif (is_file($manifestPathV4)) {
            $manifestPath = $manifestPathV4;
        } else {
            throw new RuntimeException(
                sprintf('Vite manifest not found at %s or at %s', $manifestPathV4, $manifestPathV5),
            );
        }

        $content = file_get_contents($manifestPath);

        if (!$content) {
            throw new RuntimeException(
                sprintf('Could not read Vite manifest at: %s', $manifestPath),
            );
        }

        try {
            return json_decode($content, associative: true, flags: JSON_THROW_ON_ERROR);
        } catch (JsonException $e) {
            throw new RuntimeException(
                sprintf('Could not decode Vite manifest at: %s', $manifestPath),
                0,
                $e,
            );
        }
    }
}