Last active
August 8, 2023 02:50
-
-
Save darkcolonist/dacdfe335ada59106303ba2ef10e6a72 to your computer and use it in GitHub Desktop.
Vite Helper for PHP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()], | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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