SSG includes hostname on build links at root directory
Used statamic new to create a project named x including SSG. Set /resources/site.yaml default url to /foo. In other words, I am trying to generate a static site that is contained within the /foo folder of another host.
When I generate the static site I get a home page with the following...
<link rel="preload" as="style" href="http://x.test/build/assets/site-9bd4acd3.css" /><link rel="modulepreload" href="http://x.test/build/assets/site-4ed993c7.js" /><link rel="stylesheet" href="http://x.test/build/assets/site-9bd4acd3.css" /><script type="module" src="http://x.test/build/assets/site-4ed993c7.js"></script>
These links have two problems. One is that they include the http://x.text host, and I plan to serve the static site from another host. The other is that they reference the build directory from the root of the server, and I expect (and need) all the static files to be in the site's /foo directory.
These links are generated from the layout.antlers.html template's vite tag...
{{ vite src="resources/js/site.js|resources/css/site.css" }}
I also find the build directory at /storage/static/build.
What I expect is a site fully contained in the /storage/static/foo directory, with build at /storage/static/foo/build and links that do not include a host.
What all do I need to change in this plain new Statamic site to get this result?
Also ran in to this today.
Here is how I am working around this problem for now. I really would rather the problem didn't exist, but there has not been any response here for a few weeks, so I had to work around this somehow. I've added this code to the boot() method of the AppServiceProvider class in my /app/Providers/AppServiceProvider.php file...
SSG::after(function () {
$base = rtrim(config('statamic.ssg.base_url'), '/'); // remove trailing slash
$current_site = (string)Site::current();
$site_url = rtrim(Site::config()[$current_site]['url'], '/'); // remove trailing slash
$dest = rtrim(config('statamic.ssg.destination'), '/') . $site_url; // no trailing slash
$processed_count = $updated_count = 0;
echo "Rewriting absolute URLs to remove server name\n";
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dest));
foreach ($iterator as $fileInfo) {
if ($fileInfo->isFile() && $fileInfo->getExtension() === 'html') {
$filePath = $fileInfo->getPathname();
$htmlContent = file_get_contents($filePath);
$dom = new \DOMDocument();
// suppress errors due to malformed HTML
libxml_use_internal_errors(true);
$dom->loadHTML($htmlContent);
libxml_clear_errors();
$checks = [
'a' => 'href',
'link' => 'href',
'img' => 'src',
'script' => 'src',
];
foreach ($checks as $tag => $attribute) {
$elements = $dom->getElementsByTagName($tag);
foreach ($elements as $element) {
/** @disregard P1013 Undefined method */
$link = $element->getAttribute($attribute);
// there could probably be a more sophisticated check for absolute URLs
if (strpos($link, $base) === 0) {
/** @disregard P1013 Undefined method */
$element->setAttribute($attribute, substr($link, strlen($base)));
$updated_count++;
}
}
}
$modifiedHtml = $dom->saveHTML();
file_put_contents($filePath, $modifiedHtml);
$processed_count++;
}
}
echo "Processed $processed_count HTML files, rewrote $updated_count URLs\n";
});
This swoops in after SSG has done its thing to rewrite all the URLs to remove the hostname if it matches the base URL of the Statamic site.
I am pretty new to Statamic so please be very skeptical of this code and do let me know how it could be improved.
I learned there is an even simpler solution to this problem. Simply add ASSET_URL=/ to the .env file to tell Vite that it should use / as the custom base URL when building the site.
Note, this will not change anything when using npm run dev, but it will fix the site when using npm run build, so just make sure to do a real build of the site before using SSG to export it.
I have found a way to manage my site so that the SSG build properly honors the subdirectory in which I want it to build, including the links built by vite. I thought I'd share this here in case others are struggling with this. Note that in the example below I am building the site so that the static site will run from a directory called /web on the target server.
First make sure that CP > Site > URL is set to /web.
Second, add ASSET_URL=/ to .env so that Vite will know to build assets without a host name when run by npm run build.
In /config/filesystems.php change the assets root and url...
'root' => public_path('web/assets'),
'url' => '/web/assets',
Make sure the assets folder in the actual filesystem matches and is moved into /public/web/assets.
Update the vite.config.js file to use publicDirectory: 'public/web' and then make sure to include directory="web/build" on all vite tags in the layouts and templates.
Then make sure that the static site generator knows where to put the build and assets directories. Edit /config/statamic/ssg.php to inlcude:
'copy' => [
public_path('web/build') => 'web/build',
public_path('web/assets') => 'web/assets',
public_path('web/js') => 'web/js',
],
Also, remember to update your .gitignore file to ignore the new /public/web/build and /public/web/hot (hot will land in /public/web too, it seems) files/directories.
That should do the trick. No URL rewrites are necessary after this.