Commit 37c54d24 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

feat: build hashed static files to renew browser cache

- replace rollup config with vitejs
- use vite dev server during development to take advantage of
hot module replacement (HMR)
- add vite service using Vite library to load css and js assets
- update package.json scripts and remove unnecessary
dependencies
- update scripts/bundle-prepare.sh

closes #107
parent cdc660e3
Loading
Loading
Loading
Loading
Loading

.babelrc

deleted100644 → 0
+0 −4
Original line number Diff line number Diff line
{
  "presets": ["@babel/preset-typescript", "@babel/preset-env"],
  "plugins": ["@babel/plugin-proposal-class-properties"]
}
+3 −3
Original line number Diff line number Diff line
@@ -5,12 +5,12 @@
  "dockerComposeFile": ["../docker-compose.yml", "./docker-compose.yml"],
  "service": "app",
  "workspaceFolder": "/castopod-host",
  "postCreateCommand": "composer install && npm install && npm run build:dev",
  "postStartCommand": "crontab ./crontab && cron && php spark serve --host 0.0.0.0",
  "postCreateCommand": "composer install && npm install && npm run build:static",
  "postStartCommand": "crontab ./crontab && cron && php spark serve --host 0.0.0.0 & npm run dev",
  "postAttachCommand": "crontab ./crontab && service cron reload",
  "shutdownAction": "stopCompose",
  "settings": {
    "terminal.integrated.defaultProfile.linux": "/bin/bash",
    "terminal.integrated.defaultProfile.linux": "bash",
    "editor.formatOnSave": true,
    "[php]": {
      "editor.defaultFormatter": "bmewburn.vscode-intelephense-client",
+2 −1
Original line number Diff line number Diff line
@@ -47,7 +47,8 @@ performance improvements ⚡.
   - cf.
     [I haven't updated my instance in a long time… What should I do?](#i-havent-updated-my-instance-in-a-long-time-what-should-i-do)

5. ✨ Enjoy your fresh instance, you're all done!
5. If you are using redis, clear your cache.
6. ✨ Enjoy your fresh instance, you're all done!

## Automatic update instructions

+10 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ use App\Authorization\PermissionModel;
use App\Libraries\Breadcrumb;
use App\Libraries\Negotiate;
use App\Libraries\Router;
use App\Libraries\Vite;
use App\Models\UserModel;
use CodeIgniter\Config\BaseService;
use CodeIgniter\HTTP\Request;
@@ -138,4 +139,13 @@ class Services extends BaseService

        return new Breadcrumb();
    }

    public static function vite(bool $getShared = true): Vite
    {
        if ($getShared) {
            return self::getSharedInstance('vite');
        }

        return new Vite();
    }
}

app/Libraries/Vite.php

0 → 100644
+118 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace App\Libraries;

class Vite
{
    protected string $manifestPath = 'assets/manifest.json';

    protected string $manifestCSSPath = 'assets/manifest-css.json';

    /**
     * @var array<string, mixed>
     */
    protected ?array $manifestData = null;

    /**
     * @var array<string, mixed>
     */
    protected ?array $manifestCSSData = null;

    public function asset(string $path, string $type): string
    {
        if (ENVIRONMENT !== 'production') {
            return $this->loadDev($path, $type);
        }

        // @phpstan-ignore-next-line
        return $this->loadProd($path, $type);
    }

    private function loadDev(string $path, string $type): string
    {
        return $this->getHtmlTag("http://localhost:3000/assets/{$path}", $type);
    }

    private function loadProd(string $path, string $type): string
    {
        if ($type === 'css') {
            if ($this->manifestCSSData === null) {
                $cacheName = 'vite-manifest-css';
                if (! ($cachedManifestCSS = cache($cacheName))) {
                    if (($manifestCSSContent = file_get_contents($this->manifestCSSPath)) !== false) {
                        $cachedManifestCSS = json_decode($manifestCSSContent, true);
                        cache()
                            ->save($cacheName, $cachedManifestCSS, DECADE);
                    } else {
                        // ERROR when getting the manifest-css file
                        $manifestCSSPath = $this->manifestCSSPath;
                        die("Could not load Vite's <pre>{$manifestCSSPath}</pre> file.");
                    }
                }
                $this->manifestCSSData = $cachedManifestCSS;
            }

            if (array_key_exists($path, $this->manifestCSSData)) {
                return $this->getHtmlTag('/assets/' . $this->manifestCSSData[$path]['file'], 'css');
            }
        }

        if ($this->manifestData === null) {
            $cacheName = 'vite-manifest';
            if (! ($cachedManifest = cache($cacheName))) {
                if (($manifestContents = file_get_contents($this->manifestPath)) !== false) {
                    $cachedManifest = json_decode($manifestContents, true);
                    cache()
                        ->save($cacheName, $cachedManifest, DECADE);
                } else {
                    // ERROR when retrieving the manifest file
                    $manifestPath = $this->manifestPath;
                    die("Could not load Vite's <pre>{$manifestPath}</pre> file.");
                }
            }
            $this->manifestData = $cachedManifest;
        }

        $html = '';
        if (array_key_exists($path, $this->manifestData)) {
            $manifestElement = $this->manifestData[$path];

            // import css dependencies if any
            if (array_key_exists('css', $manifestElement)) {
                foreach ($manifestElement['css'] as $cssFile) {
                    $html .= $this->getHtmlTag('/assets/' . $cssFile, 'css');
                }
            }

            // import dependencies first for faster js loading
            if (array_key_exists('imports', $manifestElement)) {
                foreach ($manifestElement['imports'] as $importPath) {
                    if (array_key_exists($importPath, $this->manifestData)) {
                        $html .= $this->getHtmlTag('/assets/' . $this->manifestData[$importPath]['file'], 'js');
                    }
                }
            }

            $html .= $this->getHtmlTag('/assets/' . $manifestElement['file'], $type);
        }

        return $html;
    }

    private function getHtmlTag(string $assetUrl, string $type): string
    {
        return match ($type) {
            'css' => <<<CODE_SAMPLE
                <link rel="stylesheet" href="{$assetUrl}"/>
            CODE_SAMPLE
,
            'js' => <<<CODE_SAMPLE
                    <script type="module" src="{$assetUrl}"></script>
                CODE_SAMPLE
,
            default => '',
        };
    }
}
Loading