Added assets hashing after build, added favicon

This commit is contained in:
Igor Scheller 2021-08-10 11:19:58 +02:00
parent 59e54fc1f5
commit b842466b3a
14 changed files with 2063 additions and 1905 deletions

View File

@ -22,6 +22,7 @@ return [
\Engelsystem\Http\ResponseServiceProvider::class,
\Engelsystem\Http\Psr7ServiceProvider::class,
\Engelsystem\Helpers\AuthenticatorServiceProvider::class,
\Engelsystem\Helpers\AssetsServiceProvider::class,
\Engelsystem\Renderer\TwigServiceProvider::class,
\Engelsystem\Middleware\RouteDispatcherServiceProvider::class,
\Engelsystem\Middleware\RequestHandlerServiceProvider::class,
@ -33,7 +34,7 @@ return [
\Engelsystem\Helpers\VersionServiceProvider::class,
\Engelsystem\Mail\MailerServiceProvider::class,
\Engelsystem\Http\HttpClientServiceProvider::class,
\Engelsystem\Helpers\DumpServerServiceProvider::class
\Engelsystem\Helpers\DumpServerServiceProvider::class,
],
// Application middleware

View File

@ -39,6 +39,7 @@
"style-loader": "^3.3.1",
"terser-webpack-plugin": "^5.1.2",
"webpack": "^5.37.1",
"webpack-cli": "^4.7.0"
"webpack-cli": "^4.7.0",
"webpack-manifest-plugin": "^5.0.0"
}
}

View File

@ -8,6 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<link rel="icon" href="{{ asset('assets/angel.svg') }}">
<link rel="stylesheet" type="text/css" href="{{ asset('assets/theme' ~ themeId ~ '.css') }}"/>
<script type="text/javascript" src="{{ asset('assets/vendor.js') }}"></script>

View File

@ -109,7 +109,9 @@ class Application extends Container
$this->instance('path', $appPath);
$this->instance('path.config', $appPath . DIRECTORY_SEPARATOR . 'config');
$this->instance('path.resources', $appPath . DIRECTORY_SEPARATOR . 'resources');
$this->instance('path.public', $appPath . DIRECTORY_SEPARATOR . 'public');
$this->instance('path.assets', $this->get('path.resources') . DIRECTORY_SEPARATOR . 'assets');
$this->instance('path.assets.public', $this->get('path.public') . DIRECTORY_SEPARATOR . 'assets');
$this->instance('path.lang', $this->get('path.resources') . DIRECTORY_SEPARATOR . 'lang');
$this->instance('path.views', $this->get('path.resources') . DIRECTORY_SEPARATOR . 'views');
$this->instance('path.storage', $appPath . DIRECTORY_SEPARATOR . 'storage');

38
src/Helpers/Assets.php Normal file
View File

@ -0,0 +1,38 @@
<?php
namespace Engelsystem\Helpers;
class Assets
{
/** @var string */
protected string $assetsPath;
/** @var string */
protected string $manifestFile = 'manifest.json';
/**
* @param string $assetsPath Directory containing assets
*/
public function __construct(string $assetsPath)
{
$this->assetsPath = $assetsPath;
}
/**
* @param string $asset
* @return string
*/
public function getAssetPath(string $asset): string
{
$manifest = $this->assetsPath . DIRECTORY_SEPARATOR . $this->manifestFile;
if (is_readable($manifest)) {
$manifest = json_decode(file_get_contents($manifest), true);
if (isset($manifest[$asset])) {
$asset = $manifest[$asset];
}
}
return $asset;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Engelsystem\Helpers;
use Engelsystem\Container\ServiceProvider;
class AssetsServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->when(Assets::class)
->needs('$assetsPath')
->give($this->app->get('path.assets.public'));
}
}

View File

@ -2,20 +2,27 @@
namespace Engelsystem\Renderer\Twig\Extensions;
use Engelsystem\Helpers\Assets as AssetsProvider;
use Engelsystem\Http\UrlGeneratorInterface;
use Illuminate\Support\Str;
use Twig\Extension\AbstractExtension as TwigExtension;
use Twig\TwigFunction;
class Assets extends TwigExtension
{
/** @var AssetsProvider */
protected $assets;
/** @var UrlGeneratorInterface */
protected $urlGenerator;
/**
* @param AssetsProvider $assets
* @param UrlGeneratorInterface $urlGenerator
*/
public function __construct(UrlGeneratorInterface $urlGenerator)
public function __construct(AssetsProvider $assets, UrlGeneratorInterface $urlGenerator)
{
$this->assets = $assets;
$this->urlGenerator = $urlGenerator;
}
@ -36,6 +43,10 @@ class Assets extends TwigExtension
public function getAsset(string $path): string
{
$path = ltrim($path, '/');
if (Str::startsWith($path, 'assets/')) {
$asset = Str::replaceFirst('assets/', '', $path);
$path = 'assets/' . $this->assets->getAssetPath($asset);
}
return $this->urlGenerator->to('/' . $path);
}

View File

@ -55,6 +55,8 @@ class ApplicationTest extends TestCase
$this->assertTrue($app->has('path.cache'));
$this->assertTrue($app->has('path.cache.routes'));
$this->assertTrue($app->has('path.cache.views'));
$this->assertTrue($app->has('path.public'));
$this->assertTrue($app->has('path.assets.public'));
$this->assertEquals(realpath('.'), $app->path());
$this->assertEquals(realpath('.') . '/config', $app->get('path.config'));

View File

@ -0,0 +1,25 @@
<?php
namespace Engelsystem\Test\Unit\Helpers;
use Engelsystem\Application;
use Engelsystem\Helpers\Assets;
use Engelsystem\Helpers\AssetsServiceProvider;
use Engelsystem\Test\Unit\ServiceProviderTest;
class AssetsServiceProviderTest extends ServiceProviderTest
{
/**
* @covers \Engelsystem\Helpers\AssetsServiceProvider::register
*/
public function testRegister()
{
$app = new Application();
$app->instance('path.assets.public', '/tmp');
$serviceProvider = new AssetsServiceProvider($app);
$serviceProvider->register();
$this->assertArrayHasKey(Assets::class, $app->contextual);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Engelsystem\Test\Unit\Helpers;
use Engelsystem\Helpers\Assets;
use Engelsystem\Test\Unit\TestCase;
class AssetsTest extends TestCase
{
/**
* @covers \Engelsystem\Helpers\Assets::__construct
* @covers \Engelsystem\Helpers\Assets::getAssetPath
*/
public function testGetAssetPath()
{
$assets = new Assets('/foo/bar');
$this->assertEquals('lorem.bar', $assets->getAssetPath('lorem.bar'));
$assets = new Assets(__DIR__ . '/Stub/files');
$this->assertEquals('something.xyz', $assets->getAssetPath('something.xyz'));
$this->assertEquals('lorem-hashed.ipsum', $assets->getAssetPath('foo.bar'));
}
}

View File

@ -0,0 +1,3 @@
{
"foo.bar": "lorem-hashed.ipsum"
}

View File

@ -2,6 +2,7 @@
namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions;
use Engelsystem\Helpers\Assets as AssetsProvider;
use Engelsystem\Http\UrlGenerator;
use Engelsystem\Renderer\Twig\Extensions\Assets;
use PHPUnit\Framework\MockObject\MockObject;
@ -14,10 +15,12 @@ class AssetsTest extends ExtensionTest
*/
public function testGetFunctions()
{
/** @var UrlGenerator|MockObject $urlGenerator */
/** @var UrlGenerator&MockObject $urlGenerator */
$urlGenerator = $this->createMock(UrlGenerator::class);
/** @var AssetsProvider&MockObject $assets */
$assets = $this->createMock(AssetsProvider::class);
$extension = new Assets($urlGenerator);
$extension = new Assets($assets, $urlGenerator);
$functions = $extension->getFunctions();
$this->assertExtensionExists('asset', [$extension, 'getAsset'], $functions);
@ -28,20 +31,35 @@ class AssetsTest extends ExtensionTest
*/
public function testGetAsset()
{
/** @var UrlGenerator|MockObject $urlGenerator */
/** @var UrlGenerator&MockObject $urlGenerator */
$urlGenerator = $this->createMock(UrlGenerator::class);
/** @var AssetsProvider&MockObject $assets */
$assets = $this->createMock(AssetsProvider::class);
$urlGenerator->expects($this->exactly(2))
$urlGenerator->expects($this->exactly(4))
->method('to')
->with('/assets/foo.css')
->willReturn('https://foo.bar/project/assets/foo.css');
->withConsecutive(['/test.png'], ['/assets/foo.css'], ['/assets/bar.css'], ['/assets/lorem-hashed.js'])
->willReturnCallback(function ($path) {
return 'https://foo.bar/project' . $path;
});
$extension = new Assets($urlGenerator);
$assets->expects($this->exactly(3))
->method('getAssetPath')
->withConsecutive(['foo.css'], ['bar.css'], ['lorem.js'])
->willReturnOnConsecutiveCalls('foo.css', 'bar.css', 'lorem-hashed.js');
$extension = new Assets($assets, $urlGenerator);
$return = $extension->getAsset('test.png');
$this->assertEquals('https://foo.bar/project/test.png', $return);
$return = $extension->getAsset('assets/foo.css');
$this->assertEquals('https://foo.bar/project/assets/foo.css', $return);
$return = $extension->getAsset('/assets/foo.css');
$this->assertEquals('https://foo.bar/project/assets/foo.css', $return);
$return = $extension->getAsset('/assets/bar.css');
$this->assertEquals('https://foo.bar/project/assets/bar.css', $return);
$return = $extension->getAsset('assets/lorem.js');
$this->assertEquals('https://foo.bar/project/assets/lorem-hashed.js', $return);
}
}

View File

@ -4,6 +4,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const nodeEnv = (process.env.NODE_ENV || 'development').trim();
const {WebpackManifestPlugin} = require('webpack-manifest-plugin');
const fs = require('fs');
// eslint-disable-next-line
@ -18,9 +19,10 @@ const plugins = [
},
}),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id]-[hash].css',
filename: '[name]-[contenthash].css',
chunkFilename: '[id]-[contenthash].css',
}),
new WebpackManifestPlugin({}),
];
let themeFileNameRegex = /theme\d+/;
@ -50,7 +52,7 @@ module.exports = {
},
output: {
path: path.resolve('public/assets'),
filename: '[name].js',
filename: '[name]-[contenthash].js',
publicPath: '',
clean: true,
},

3796
yarn.lock

File diff suppressed because it is too large Load Diff