diff --git a/README.md b/README.md index 78b31b4..0b57407 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ -This is an example code, simulating how to run Vite on traditional PHP sites. - -A bare-minimum setup to serve as example to adapt to other scenarios ([WordPress](https://github.com/wp-bond/boilerplate/tree/master/app/themes/boilerplate), Laravel, etc). +This is my implementation of Vite with PHP. + +making use of Vite in a Wordpress theme is as easy as: +```php +echo new Vite( + 'main.js', + WP_DEBUG, + get_template_directory() . '/public/dist/', + get_template_directory_uri() . '/public/dist/' +); +``` [Vite is amazing](https://github.com/vitejs/vite). Credits go to Evan You [@yyx990803](https://github.com/yyx990803) and the Vue team. @@ -40,7 +48,10 @@ The solution is here, adjust the paths and run in terminal: ``` ln -s {path_to_vite}/src/assets {path_to_public_html}/assets ``` - +Another solution would be importing assets in script tag which will consider the server.origin config to generate absolute urls: +```javascript +import logoUrl from '../assets/logo.png' +``` ### Tips for multiple entries You may find the need to handle multiple entries, for example, one js/css for the backend and another js/css for frontend. For that, it depends directly on how you want to organize your code. diff --git a/includes/Vite.php b/includes/Vite.php new file mode 100644 index 0000000..f86fe4b --- /dev/null +++ b/includes/Vite.php @@ -0,0 +1,129 @@ +entry = $entry; + $this->isDev = $isDev; + $this->publicPath = $publicPath; // basically the filesystem dir the manifest.json is written to + $this->publicUrl = $publicUrl; + $this->viteServer = $viteServer; + } + + // Prints all the html entries needed for Vite + public function __toString(): string + { + if ($this->isDev) { + return $this->devEnv(); + } + return implode("\n", [$this->jsTag(), $this->jsPreloadImports(), $this->cssTag()]); + } + + + // Helpers to print tags + public function jsTag(string $options = '' /* e.g. 'defer' or 'async' */): string + { + return ""; + } + + public function jsPreloadImports(string $options = ''): string + { + $html = ''; + foreach ($this->importsUrls() as $url) { + $html .= ""; + } + return $html; + } + + public function cssTag(string $options = ''): string + { + $html = ''; + foreach ($this->cssUrls() as $url) { + $html .= ""; + } + return $html; + } + + /* + * The server running PHP might not have direct access to localhost, so the check is handled client-side. + * Checks for the vite dev server being available and loads built assets if necessary. + */ + protected function devEnv(): string + { + $hash = substr(sha1(uniqid($this->viteServer)), -6); + return " + + {$this->jsTag()} + {$this->jsPreloadImports()} + {$this->cssTag()} + + + + "; + } + + // Helpers to locate files + public function getManifest(): array + { + $content = @file_get_contents($this->publicPath . 'manifest.json'); + + return json_decode($content, true) ?? []; + } + + public function assetUrl(): string + { + $manifest = $this->getManifest(); + + return isset($manifest[$this->entry]) + ? $this->publicUrl . $manifest[$this->entry]['file'] + : ''; + } + + public function importsUrls(): array + { + $urls = []; + $manifest = $this->getManifest(); + + if (!empty($manifest[$this->entry]['imports'])) { + foreach ($manifest[$this->entry]['imports'] as $imports) { + $urls[] = $this->publicUrl . $manifest[$imports]['file']; + } + } + return $urls; + } + + public function cssUrls(): array + { + $urls = []; + $manifest = $this->getManifest(); + + if (!empty($manifest[$this->entry]['css'])) { + foreach ($manifest[$this->entry]['css'] as $file) { + $urls[] = $this->publicUrl . $file; + } + } + return $urls; + } +} diff --git a/public/dist/assets/main.8a7eae23.css b/public/dist/assets/main.800647f8.css similarity index 78% rename from public/dist/assets/main.8a7eae23.css rename to public/dist/assets/main.800647f8.css index eb119c0..9063133 100644 --- a/public/dist/assets/main.8a7eae23.css +++ b/public/dist/assets/main.800647f8.css @@ -1 +1 @@ -a[data-v-467e25ec]{color:#42b983}*{margin:0;padding:0}body{font-family:sans-serif;text-align:center;color:#000}button{border:0;margin:20px 0;background-color:#000;color:#fff;font-size:14px;padding:10px;cursor:pointer}.message{color:#bbb;padding:20px;margin:20px 0;font-size:14px;background-color:#eee} +a[data-v-2ca91351]{color:#42b983}*{margin:0;padding:0}body{font-family:sans-serif;text-align:center;color:#000}button{border:0;margin:20px 0;background-color:#000;color:#fff;font-size:14px;padding:10px;cursor:pointer}.message{color:#bbb;padding:20px;margin:20px 0;font-size:14px;background-color:#eee} diff --git a/public/dist/assets/main.a24bdb0b.js b/public/dist/assets/main.a24bdb0b.js new file mode 100644 index 0000000..2cc06ef --- /dev/null +++ b/public/dist/assets/main.a24bdb0b.js @@ -0,0 +1 @@ +import{r as g,c as y,a as n,u as a,t as i,F as k,p as b,b as S,d as c,o as E,e as V}from"./vendor.a085cd8d.js";const $="modulepreload",d={},j="/dist/",x=function(o,r){return!r||r.length===0?o():Promise.all(r.map(t=>{if(t=`${j}${t}`,t in d)return;d[t]=!0;const l=t.endsWith(".css"),h=l?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${t}"]${h}`))return;const s=document.createElement("link");if(s.rel=l?"stylesheet":$,l||(s.as="script",s.crossOrigin=""),s.href=t,document.head.appendChild(s),l)return new Promise((v,f)=>{s.addEventListener("load",v),s.addEventListener("error",f)})})).then(()=>o())};var p="/dist/assets/logo.03d6d6da.png";var H=(e,o)=>{for(const[r,t]of o)e[r]=t;return e};const u=e=>(b("data-v-2ca91351"),e=e(),S(),e),I=u(()=>n("img",{alt:"use import to workaround this",src:p,height:"40"},null,-1)),W=["src"],w=u(()=>n("p",null,[n("a",{href:"https://vitejs.dev/guide/features.html",target:"_blank"}," Vite Documentation "),c(" | "),n("a",{href:"https://v3.vuejs.org/",target:"_blank"},"Vue 3 Documentation")],-1)),B=u(()=>n("p",null,[c(" Edit "),n("code",null,"components/HelloWorld.vue"),c(" to test hot module replacement. ")],-1)),C={props:{msg:String},setup(e){const o=g({count:0});return(r,t)=>(E(),y(k,null,[I,n("img",{alt:"Vue logo",src:a(p),height:"40"},null,8,W),n("h1",null,"Vue "+i(e.msg),1),w,n("button",{type:"button",onClick:t[0]||(t[0]=l=>a(o).count++)}," count is: "+i(a(o).count),1),B],64))}};var D=H(C,[["__scopeId","data-v-2ca91351"]]),L=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:D});x(()=>import("./modulepreload-polyfill.b7f2da20.js"),[]);const _={"./components/HelloWorld.vue":L},m={};for(const e in _){const o=e.split("/").pop().replace(/\.\w+$/,"");m[o]=_[e].default}for(const e of document.getElementsByClassName("vue-app"))V({template:e.innerHTML,components:m}).mount(e); diff --git a/public/dist/assets/main.dcdeeede.js b/public/dist/assets/main.dcdeeede.js deleted file mode 100644 index f926bad..0000000 --- a/public/dist/assets/main.dcdeeede.js +++ /dev/null @@ -1 +0,0 @@ -import{r as h,c as g,a as n,t as u,u as i,F as y,p as b,b as k,d as a,o as S,e as E}from"./vendor.a085cd8d.js";const V="modulepreload",d={},$="/dist/",j=function(o,r){return!r||r.length===0?o():Promise.all(r.map(t=>{if(t=`${$}${t}`,t in d)return;d[t]=!0;const l=t.endsWith(".css"),m=l?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${t}"]${m}`))return;const s=document.createElement("link");if(s.rel=l?"stylesheet":V,l||(s.as="script",s.crossOrigin=""),s.href=t,document.head.appendChild(s),l)return new Promise((v,f)=>{s.addEventListener("load",v),s.addEventListener("error",f)})})).then(()=>o())};var x="/dist/assets/logo.03d6d6da.png";var H=(e,o)=>{for(const[r,t]of o)e[r]=t;return e};const c=e=>(b("data-v-467e25ec"),e=e(),k(),e),I=c(()=>n("img",{alt:"Vue logo",src:x,height:"40"},null,-1)),W=c(()=>n("p",null,[n("a",{href:"https://vitejs.dev/guide/features.html",target:"_blank"}," Vite Documentation "),a(" | "),n("a",{href:"https://v3.vuejs.org/",target:"_blank"},"Vue 3 Documentation")],-1)),B=c(()=>n("p",null,[a(" Edit "),n("code",null,"components/HelloWorld.vue"),a(" to test hot module replacement. ")],-1)),C={props:{msg:String},setup(e){const o=h({count:0});return(r,t)=>(S(),g(y,null,[I,n("h1",null,"Vue "+u(e.msg),1),W,n("button",{type:"button",onClick:t[0]||(t[0]=l=>i(o).count++)}," count is: "+u(i(o).count),1),B],64))}};var D=H(C,[["__scopeId","data-v-467e25ec"]]),L=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:D});j(()=>import("./modulepreload-polyfill.b7f2da20.js"),[]);const p={"./components/HelloWorld.vue":L},_={};for(const e in p){const o=e.split("/").pop().replace(/\.\w+$/,"");_[o]=p[e].default}for(const e of document.getElementsByClassName("vue-app"))E({template:e.innerHTML,components:_}).mount(e); diff --git a/public/dist/manifest.json b/public/dist/manifest.json index 5e69a8e..53ece4e 100644 --- a/public/dist/manifest.json +++ b/public/dist/manifest.json @@ -1,6 +1,6 @@ { "main.js": { - "file": "assets/main.dcdeeede.js", + "file": "assets/main.a24bdb0b.js", "src": "main.js", "isEntry": true, "imports": [ @@ -10,7 +10,7 @@ "../vite/modulepreload-polyfill" ], "css": [ - "assets/main.8a7eae23.css" + "assets/main.800647f8.css" ], "assets": [ "assets/logo.03d6d6da.png" diff --git a/public/helpers.php b/public/helpers.php deleted file mode 100644 index b6e11db..0000000 --- a/public/helpers.php +++ /dev/null @@ -1,137 +0,0 @@ -'; -} - -function jsPreloadImports(string $entry): string -{ - if (isDev($entry)) { - return ''; - } - - $res = ''; - foreach (importsUrls($entry) as $url) { - $res .= ''; - } - 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 .= ''; - } - 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; -} diff --git a/public/index.php b/public/index.php index c5e94d8..1ab2b7a 100644 --- a/public/index.php +++ b/public/index.php @@ -4,7 +4,7 @@ // During dev, this file would be hit when accessing your local host, like: // http://vite-php-setup.test -require_once __DIR__ . '/helpers.php'; +require_once __DIR__ . '/../includes/Vite.php'; ?> @@ -15,24 +15,24 @@