Added assets hashing after build, added favicon
This commit is contained in:
parent
59e54fc1f5
commit
b842466b3a
|
@ -22,6 +22,7 @@ return [
|
||||||
\Engelsystem\Http\ResponseServiceProvider::class,
|
\Engelsystem\Http\ResponseServiceProvider::class,
|
||||||
\Engelsystem\Http\Psr7ServiceProvider::class,
|
\Engelsystem\Http\Psr7ServiceProvider::class,
|
||||||
\Engelsystem\Helpers\AuthenticatorServiceProvider::class,
|
\Engelsystem\Helpers\AuthenticatorServiceProvider::class,
|
||||||
|
\Engelsystem\Helpers\AssetsServiceProvider::class,
|
||||||
\Engelsystem\Renderer\TwigServiceProvider::class,
|
\Engelsystem\Renderer\TwigServiceProvider::class,
|
||||||
\Engelsystem\Middleware\RouteDispatcherServiceProvider::class,
|
\Engelsystem\Middleware\RouteDispatcherServiceProvider::class,
|
||||||
\Engelsystem\Middleware\RequestHandlerServiceProvider::class,
|
\Engelsystem\Middleware\RequestHandlerServiceProvider::class,
|
||||||
|
@ -33,7 +34,7 @@ return [
|
||||||
\Engelsystem\Helpers\VersionServiceProvider::class,
|
\Engelsystem\Helpers\VersionServiceProvider::class,
|
||||||
\Engelsystem\Mail\MailerServiceProvider::class,
|
\Engelsystem\Mail\MailerServiceProvider::class,
|
||||||
\Engelsystem\Http\HttpClientServiceProvider::class,
|
\Engelsystem\Http\HttpClientServiceProvider::class,
|
||||||
\Engelsystem\Helpers\DumpServerServiceProvider::class
|
\Engelsystem\Helpers\DumpServerServiceProvider::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
// Application middleware
|
// Application middleware
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"terser-webpack-plugin": "^5.1.2",
|
"terser-webpack-plugin": "^5.1.2",
|
||||||
"webpack": "^5.37.1",
|
"webpack": "^5.37.1",
|
||||||
"webpack-cli": "^4.7.0"
|
"webpack-cli": "^4.7.0",
|
||||||
|
"webpack-manifest-plugin": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
<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') }}"/>
|
<link rel="stylesheet" type="text/css" href="{{ asset('assets/theme' ~ themeId ~ '.css') }}"/>
|
||||||
<script type="text/javascript" src="{{ asset('assets/vendor.js') }}"></script>
|
<script type="text/javascript" src="{{ asset('assets/vendor.js') }}"></script>
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,9 @@ class Application extends Container
|
||||||
$this->instance('path', $appPath);
|
$this->instance('path', $appPath);
|
||||||
$this->instance('path.config', $appPath . DIRECTORY_SEPARATOR . 'config');
|
$this->instance('path.config', $appPath . DIRECTORY_SEPARATOR . 'config');
|
||||||
$this->instance('path.resources', $appPath . DIRECTORY_SEPARATOR . 'resources');
|
$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', $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.lang', $this->get('path.resources') . DIRECTORY_SEPARATOR . 'lang');
|
||||||
$this->instance('path.views', $this->get('path.resources') . DIRECTORY_SEPARATOR . 'views');
|
$this->instance('path.views', $this->get('path.resources') . DIRECTORY_SEPARATOR . 'views');
|
||||||
$this->instance('path.storage', $appPath . DIRECTORY_SEPARATOR . 'storage');
|
$this->instance('path.storage', $appPath . DIRECTORY_SEPARATOR . 'storage');
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,20 +2,27 @@
|
||||||
|
|
||||||
namespace Engelsystem\Renderer\Twig\Extensions;
|
namespace Engelsystem\Renderer\Twig\Extensions;
|
||||||
|
|
||||||
|
use Engelsystem\Helpers\Assets as AssetsProvider;
|
||||||
use Engelsystem\Http\UrlGeneratorInterface;
|
use Engelsystem\Http\UrlGeneratorInterface;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Twig\Extension\AbstractExtension as TwigExtension;
|
use Twig\Extension\AbstractExtension as TwigExtension;
|
||||||
use Twig\TwigFunction;
|
use Twig\TwigFunction;
|
||||||
|
|
||||||
class Assets extends TwigExtension
|
class Assets extends TwigExtension
|
||||||
{
|
{
|
||||||
|
/** @var AssetsProvider */
|
||||||
|
protected $assets;
|
||||||
|
|
||||||
/** @var UrlGeneratorInterface */
|
/** @var UrlGeneratorInterface */
|
||||||
protected $urlGenerator;
|
protected $urlGenerator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param AssetsProvider $assets
|
||||||
* @param UrlGeneratorInterface $urlGenerator
|
* @param UrlGeneratorInterface $urlGenerator
|
||||||
*/
|
*/
|
||||||
public function __construct(UrlGeneratorInterface $urlGenerator)
|
public function __construct(AssetsProvider $assets, UrlGeneratorInterface $urlGenerator)
|
||||||
{
|
{
|
||||||
|
$this->assets = $assets;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +43,10 @@ class Assets extends TwigExtension
|
||||||
public function getAsset(string $path): string
|
public function getAsset(string $path): string
|
||||||
{
|
{
|
||||||
$path = ltrim($path, '/');
|
$path = ltrim($path, '/');
|
||||||
|
if (Str::startsWith($path, 'assets/')) {
|
||||||
|
$asset = Str::replaceFirst('assets/', '', $path);
|
||||||
|
$path = 'assets/' . $this->assets->getAssetPath($asset);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->urlGenerator->to('/' . $path);
|
return $this->urlGenerator->to('/' . $path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,8 @@ class ApplicationTest extends TestCase
|
||||||
$this->assertTrue($app->has('path.cache'));
|
$this->assertTrue($app->has('path.cache'));
|
||||||
$this->assertTrue($app->has('path.cache.routes'));
|
$this->assertTrue($app->has('path.cache.routes'));
|
||||||
$this->assertTrue($app->has('path.cache.views'));
|
$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('.'), $app->path());
|
||||||
$this->assertEquals(realpath('.') . '/config', $app->get('path.config'));
|
$this->assertEquals(realpath('.') . '/config', $app->get('path.config'));
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"foo.bar": "lorem-hashed.ipsum"
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions;
|
namespace Engelsystem\Test\Unit\Renderer\Twig\Extensions;
|
||||||
|
|
||||||
|
use Engelsystem\Helpers\Assets as AssetsProvider;
|
||||||
use Engelsystem\Http\UrlGenerator;
|
use Engelsystem\Http\UrlGenerator;
|
||||||
use Engelsystem\Renderer\Twig\Extensions\Assets;
|
use Engelsystem\Renderer\Twig\Extensions\Assets;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
@ -14,10 +15,12 @@ class AssetsTest extends ExtensionTest
|
||||||
*/
|
*/
|
||||||
public function testGetFunctions()
|
public function testGetFunctions()
|
||||||
{
|
{
|
||||||
/** @var UrlGenerator|MockObject $urlGenerator */
|
/** @var UrlGenerator&MockObject $urlGenerator */
|
||||||
$urlGenerator = $this->createMock(UrlGenerator::class);
|
$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();
|
$functions = $extension->getFunctions();
|
||||||
|
|
||||||
$this->assertExtensionExists('asset', [$extension, 'getAsset'], $functions);
|
$this->assertExtensionExists('asset', [$extension, 'getAsset'], $functions);
|
||||||
|
@ -28,20 +31,35 @@ class AssetsTest extends ExtensionTest
|
||||||
*/
|
*/
|
||||||
public function testGetAsset()
|
public function testGetAsset()
|
||||||
{
|
{
|
||||||
/** @var UrlGenerator|MockObject $urlGenerator */
|
/** @var UrlGenerator&MockObject $urlGenerator */
|
||||||
$urlGenerator = $this->createMock(UrlGenerator::class);
|
$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')
|
->method('to')
|
||||||
->with('/assets/foo.css')
|
->withConsecutive(['/test.png'], ['/assets/foo.css'], ['/assets/bar.css'], ['/assets/lorem-hashed.js'])
|
||||||
->willReturn('https://foo.bar/project/assets/foo.css');
|
->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');
|
$return = $extension->getAsset('assets/foo.css');
|
||||||
$this->assertEquals('https://foo.bar/project/assets/foo.css', $return);
|
$this->assertEquals('https://foo.bar/project/assets/foo.css', $return);
|
||||||
|
|
||||||
$return = $extension->getAsset('/assets/foo.css');
|
$return = $extension->getAsset('/assets/bar.css');
|
||||||
$this->assertEquals('https://foo.bar/project/assets/foo.css', $return);
|
$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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
const nodeEnv = (process.env.NODE_ENV || 'development').trim();
|
const nodeEnv = (process.env.NODE_ENV || 'development').trim();
|
||||||
|
const {WebpackManifestPlugin} = require('webpack-manifest-plugin');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
@ -18,9 +19,10 @@ const plugins = [
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
filename: '[name].css',
|
filename: '[name]-[contenthash].css',
|
||||||
chunkFilename: '[id]-[hash].css',
|
chunkFilename: '[id]-[contenthash].css',
|
||||||
}),
|
}),
|
||||||
|
new WebpackManifestPlugin({}),
|
||||||
];
|
];
|
||||||
|
|
||||||
let themeFileNameRegex = /theme\d+/;
|
let themeFileNameRegex = /theme\d+/;
|
||||||
|
@ -50,7 +52,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve('public/assets'),
|
path: path.resolve('public/assets'),
|
||||||
filename: '[name].js',
|
filename: '[name]-[contenthash].js',
|
||||||
publicPath: '',
|
publicPath: '',
|
||||||
clean: true,
|
clean: true,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue