Skip to content

Instantly share code, notes, and snippets.

@darkcolonist
Last active August 8, 2023 02:50
Show Gist options
  • Save darkcolonist/dacdfe335ada59106303ba2ef10e6a72 to your computer and use it in GitHub Desktop.
Save darkcolonist/dacdfe335ada59106303ba2ef10e6a72 to your computer and use it in GitHub Desktop.
Vite Helper for PHP
<?php
namespace App\Helpers;
class Vite
{
/**
* ensure below matches base: '/dist/' in vite.config.js otherwise HMR will not work
* found in https://github.com/vitejs/vite/issues/7839#issuecomment-1350538408
*/
const SUB_DIRECTORY = "dist";
static function getDevHost(){
return env("VITE_URL", "http://localhost:5173/".self::SUB_DIRECTORY);
}
static function getProdPath($file){
return env("VITE_PROD_PATH", base_path('public/'. self::SUB_DIRECTORY.'/'.$file));
}
// For a real-world example check here:
// https://github.com/wp-bond/bond/blob/master/src/Tooling/Vite.php
// https://github.com/wp-bond/boilerplate/tree/master/app/themes/boilerplate
// you might check @vitejs/plugin-legacy if you need to support older browsers
// https://github.com/vitejs/vite/tree/main/packages/plugin-legacy
// Prints all the html entries needed for Vite
static function assets(string $entry): string
{
$assets = "";
if(self::isDev($entry))
$assets .= "\n" . self::reactRefreshDependency();
$assets .= "\n" . self::jsTag($entry);
$assets .= "\n" . self::jsPreloadImports($entry);
$assets .= "\n" . self::cssTag($entry);
return $assets;
}
// Some dev/prod mechanism would exist in your project
static function isDev(string $entry): bool
{
/**
* check .env also. this is fast compared to curl below which is
* slow as it will wait for a timeout on local dev host
*/
if(env("APP_ENV") !== "local")
return false;
// This method is very useful for the local server
// if we try to access it, and by any means, didn't started Vite yet
// it will fallback to load the production files from manifest
// so you still navigate your site as you intended!
static $exists = null;
if ($exists !== null) {
return $exists;
}
$handle = curl_init(self::getDevHost() . '/' . $entry);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($handle, CURLOPT_NOBODY, true);
curl_exec($handle);
$error = curl_errno($handle);
curl_close($handle);
return $exists = !$error;
}
static function reactRefreshDependency(){
return '<script type="module">
import RefreshRuntime from "'.self::getDevHost().'/@react-refresh"
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true
</script>';
}
// Helpers to print tags
static function jsTag(string $entry): string
{
$url = self::isDev($entry)
? self::getDevHost() . '/' . $entry
: self::assetUrl($entry);
if (!$url) {
return '';
}
return '<script type="module" crossorigin src="'
. $url
. '"></script>';
}
static function jsPreloadImports(string $entry): string
{
if (self::isDev($entry)) {
return '';
}
$res = '';
foreach (self::importsUrls($entry) as $url) {
$res .= '<link rel="modulepreload" href="'
. $url
. '">';
}
return $res;
}
static function cssTag(string $entry): string
{
// not needed on dev, it's inject by Vite
if (self::isDev($entry)) {
return '';
}
$tags = '';
foreach (self::cssUrls($entry) as $url) {
$tags .= '<link rel="stylesheet" href="'
. $url
. '">';
}
return $tags;
}
// Helpers to locate files
static function getManifest(): array
{
$content = file_get_contents(self::getProdPath('manifest.json'));
return json_decode($content, true);
}
static function assetUrl(string $entry): string
{
$manifest = self::getManifest();
return isset($manifest[$entry])
? '/'.self::SUB_DIRECTORY.'/' . $manifest[$entry]['file']
: '';
}
static function importsUrls(string $entry): array
{
$urls = [];
$manifest = self::getManifest();
if (!empty($manifest[$entry]['imports'])) {
foreach ($manifest[$entry]['imports'] as $imports) {
$urls[] = '/' . self::SUB_DIRECTORY . '/' . $manifest[$imports]['file'];
}
}
return $urls;
}
static function cssUrls(string $entry): array
{
$urls = [];
$manifest = self::getManifest();
if (!empty($manifest[$entry]['css'])) {
foreach ($manifest[$entry]['css'] as $file) {
$urls[] = '/' . self::SUB_DIRECTORY . '/' . $file;
}
}
return $urls;
}
}
{
"name": "vite",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {},
"devDependencies": {
"@emotion/react": "^11.11.0",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.11.16",
"@mui/material": "^5.13.1",
"@vitejs/plugin-react": "^3.1.0",
"axios": "^1.1.2",
"laravel-vite-plugin": "^0.7.2",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"moment-timezone": "^0.5.43",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-transition-group": "^4.4.5",
"uniqid": "^5.4.0",
"vite": "^4.0.0"
}
}
<!doctype html>
<html lang="en">
<head>
<?php echo \App\Helpers\Vite::assets('resources/js/main.jsx'); ?>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><?php echo env("APP_NAME") ?></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
base: '/dist/',
build: {
// generate manifest.json in outDir
manifest: true,
rollupOptions: {
// overwrite default .html entry
input: './resources/js/main.jsx'
},
outDir: 'public/dist/'
},
plugins: [react()],
})
<?php
/**
* @source https://github.com/andrefelipe/vite-php-setup/blob/master/public/helpers.php
*/
// Helpers here serve as example. Change to suit your needs.
const VITE_HOST = 'http://localhost:5133';
// For a real-world example check here:
// https://github.com/wp-bond/bond/blob/master/src/Tooling/Vite.php
// https://github.com/wp-bond/boilerplate/tree/master/app/themes/boilerplate
// you might check @vitejs/plugin-legacy if you need to support older browsers
// https://github.com/vitejs/vite/tree/main/packages/plugin-legacy
// Prints all the html entries needed for Vite
function vite(string $entry): string
{
return "\n" . jsTag($entry)
. "\n" . jsPreloadImports($entry)
. "\n" . cssTag($entry);
}
// Some dev/prod mechanism would exist in your project
function isDev(string $entry): bool
{
// This method is very useful for the local server
// if we try to access it, and by any means, didn't started Vite yet
// it will fallback to load the production files from manifest
// so you still navigate your site as you intended!
static $exists = null;
if ($exists !== null) {
return $exists;
}
$handle = curl_init(VITE_HOST . '/' . $entry);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($handle, CURLOPT_NOBODY, true);
curl_exec($handle);
$error = curl_errno($handle);
curl_close($handle);
return $exists = !$error;
}
// Helpers to print tags
function jsTag(string $entry): string
{
$url = isDev($entry)
? VITE_HOST . '/' . $entry
: assetUrl($entry);
if (!$url) {
return '';
}
return '<script type="module" crossorigin src="'
. $url
. '"></script>';
}
function jsPreloadImports(string $entry): string
{
if (isDev($entry)) {
return '';
}
$res = '';
foreach (importsUrls($entry) as $url) {
$res .= '<link rel="modulepreload" href="'
. $url
. '">';
}
return $res;
}
function cssTag(string $entry): string
{
// not needed on dev, it's inject by Vite
if (isDev($entry)) {
return '';
}
$tags = '';
foreach (cssUrls($entry) as $url) {
$tags .= '<link rel="stylesheet" href="'
. $url
. '">';
}
return $tags;
}
// Helpers to locate files
function getManifest(): array
{
$content = file_get_contents(__DIR__ . '/dist/manifest.json');
return json_decode($content, true);
}
function assetUrl(string $entry): string
{
$manifest = getManifest();
return isset($manifest[$entry])
? '/dist/' . $manifest[$entry]['file']
: '';
}
function importsUrls(string $entry): array
{
$urls = [];
$manifest = getManifest();
if (!empty($manifest[$entry]['imports'])) {
foreach ($manifest[$entry]['imports'] as $imports) {
$urls[] = '/dist/' . $manifest[$imports]['file'];
}
}
return $urls;
}
function cssUrls(string $entry): array
{
$urls = [];
$manifest = getManifest();
if (!empty($manifest[$entry]['css'])) {
foreach ($manifest[$entry]['css'] as $file) {
$urls[] = '/dist/' . $file;
}
}
return $urls;
}
<?php
namespace App\Http\Helpers;
class Vite{
static function resources($entryPointJS){
$viteURL = env("VITE_URL", "http://localhost:5173");
$js = [
/**
* this element is required
*/
$entryPointJS
];
$css = [
/**
* add your css files here (optional)
*/
];
$return = "";
if(env("APP_ENV") === "local" && self::checkIfAlive("{$viteURL}/{$js[0]}",2)){
/**
* dev/local mode
*/
$return = "<script type=\"module\" src=\"{$viteURL}/@vite/client\"></script>";
foreach ($js as $jskey => $jsval) {
$return .= "</script><script type=\"module\" src=\"{$viteURL}/{$jsval}\"></script>";
}
}else{
/**
* prod mode
*/
$manifest = json_decode(file_get_contents(public_path('build/manifest.json')), true);
foreach ($js as $jskey => $jsval) {
$return .= "<script type=\"module\" src=\"".env("APP_URL")."/build/".$manifest[$jsval]['file']."\"></script>";
}
foreach ($css as $csskey => $cssval) {
$return .= "<link rel=\"stylesheet\" href=\"" . env("APP_URL") . "/build/{{ $manifest[$cssval]['file'] }}\" />";
}
}
return $return;
}
private static function checkIfAlive($url, $timeout = 10){
$ch = curl_init($url);
// Set request options
curl_setopt_array($ch, array(
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_NOBODY => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_USERAGENT => "page-check/1.0"
));
// Execute request
curl_exec($ch);
// Check if an error occurred
if (curl_errno($ch)) {
curl_close($ch);
return false;
}
// Get HTTP response code
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Page is alive if 200 OK is received
return $code === 200;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment