Skip to content

Instantly share code, notes, and snippets.

@FroggyWeb
Forked from 0test/MainServiceProvider.php
Last active January 19, 2026 15:17
Show Gist options
  • Select an option

  • Save FroggyWeb/420bae3983829bd71b75b9cacc0b3254 to your computer and use it in GitHub Desktop.

Select an option

Save FroggyWeb/420bae3983829bd71b75b9cacc0b3254 to your computer and use it in GitHub Desktop.
npm run build/dev
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Загрузка...</title>
<link rel="stylesheet" href="/template/app/public/water.css">
</head>
<body>
<div id="app"></div>
@vite_assets()
</body>
</html>
<?php
return [
'dev' => [
'host' => 'localhost',
'port' => 5173,
'entry' => 'src/main.js',
],
'prod' => [
'base_path' => '/template/dist/',
],
];
<?php namespace EvolutionCMS\Main;
use EvolutionCMS\Main\Services\Util;
use EvolutionCMS\ServiceProvider;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Blade;
class MainServiceProvider extends ServiceProvider
{
protected $namespace = 'main';
public function boot(){
Blade::directive('vite_assets', function ($params) {
return '<?php echo EvolutionCMS\Main\Services\Util::ViteAssets(' . $params . '); ?>';
});
}
public function register()
{
Route::group(['middleware' => 'bindings'], function () {
$this->loadRoutesFrom(__DIR__ . '/../routes.php');
});
}
}
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
base: '', // чтобы пути были относительные
build: {
outDir: '../dist', // кладём рядом с app/
emptyOutDir: true,
manifest: true,
rollupOptions: {
output: {
entryFileNames: `assets/index.js`, // имя основного JS-файла.
chunkFileNames: `assets/[name].js`, // имена асинхронных чанков (если они есть).
assetFileNames: `assets/[name].[ext]` // имена других ассетов (изображений, шрифтов и т.п.).
}
}
},
server: {
proxy: {
'/api': {
target: 'http://evoreact.localhost', // Evo сайт
changeOrigin: true,
},
},
},
})
<?php
namespace EvolutionCMS\FrMain\Services;
use RuntimeException;
class Util {
public static function ViteAssets($params = []): string {
$viteConfig = self::getViteConfig();
$isDev = self::isDevRunning(
$viteConfig['dev']['host'],
$viteConfig['dev']['port']
);
// В Laravel Vite entry point передается в функцию, здесь он берется из конфига.
// Для Laravel-like поведения, мы будем использовать его как ключ в manifest.json
$entry = $params['entry'] ?? $viteConfig['dev']['entry'];
if ($isDev) {
$host = $viteConfig['dev']['host'];
$port = $viteConfig['dev']['port'];
$scripts = [
sprintf(
'<script type="module" crossorigin src="http://%s:%d/@vite/client"></script>',
htmlspecialchars($host, ENT_QUOTES, 'UTF-8'),
(int) $port
),
sprintf(
'<script type="module" crossorigin src="http://%s:%d/%s"></script>',
htmlspecialchars($host, ENT_QUOTES, 'UTF-8'),
(int) $port,
htmlspecialchars($entry, ENT_QUOTES, 'UTF-8')
),
];
return implode("\n", $scripts);
}
// Prod
$basePath = $viteConfig['prod']['base_path'];
$manifestPath = MODX_BASE_PATH . rtrim($basePath, '/') . '/manifest.json';
if (! file_exists($manifestPath)) {
throw new RuntimeException('Vite manifest not found at ' . $manifestPath . '. Run `npm run build`.');
}
static $manifest = null;
if ($manifest === null) {
$manifest = json_decode(file_get_contents($manifestPath), true, 512, JSON_THROW_ON_ERROR);
}
if (! isset($manifest[$entry])) {
throw new RuntimeException("Entry '{$entry}' not found in Vite manifest.");
}
$tags = [];
$generated = []; // Для отслеживания уже добавленных ассетов
self::generateTagsForEntry($entry, $manifest, $basePath, $tags, $generated);
return implode("\n", $tags);
}
private static function generateTagsForEntry(string $entry, array &$manifest, string $basePath, array &$tags, array &$generated) {
// Если ассета нет в манифесте или он уже обработан, пропускаем
if (! isset($manifest[$entry]) || isset($generated[$entry])) {
return;
}
$generated[$entry] = true; // Помечаем как обработанный
$entryData = $manifest[$entry];
// Рекурсивно обрабатываем импорты, чтобы зависимости были загружены раньше
foreach ($entryData['imports'] ?? [] as $import) {
self::generateTagsForEntry($import, $manifest, $basePath, $tags, $generated);
}
// Добавляем CSS для текущего ассета
foreach ($entryData['css'] ?? [] as $cssFile) {
$url = rtrim($basePath, '/') . '/' . ltrim($cssFile, '/');
if (! isset($generated[$url])) {
$tags[] = '<link rel="stylesheet" href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '">';
$generated[$url] = true;
}
}
// Добавляем тег script (modulepreload для зависимостей, script для точки входа)
$url = rtrim($basePath, '/') . '/' . ltrim($entryData['file'], '/');
if (! isset($generated[$url])) {
// Используем флаг isEntry из манифеста для определения типа тега
if (! empty($entryData['isEntry'])) {
$tags[] = '<script type="module" src="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '"></script>';
} else {
$tags[] = '<link rel="modulepreload" href="' . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . '">';
}
$generated[$url] = true;
}
}
private static function isDevRunning(string $host, int $port): bool {
$sock = @fsockopen($host, $port, $errno, $errstr, 0.1);
if ($sock) {
fclose($sock);
return true;
}
return false;
}
private static function getViteConfig() {
return evo()['config']['vite']['env'] ?? [
'dev' => [
'host' => 'localhost',
'port' => 3000,
'entry' => 'theme/src/js/index.js',
],
'prod' => [
'base_path' => 'theme/dist/',
],
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment