Skip to content

Instantly share code, notes, and snippets.

@stefanfisk
Created May 11, 2022 11:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stefanfisk/bb4e045f3d15829a377916dc5fee9acc to your computer and use it in GitHub Desktop.
Save stefanfisk/bb4e045f3d15829a377916dc5fee9acc to your computer and use it in GitHub Desktop.
PHP parser for `@wordpress/dependency-extraction-webpack-plugin` / `'webpack-bundle-analyzer`
<?php
namespace App\Support\Assets;
use Illuminate\Support\Str;
use InvalidArgumentException;
use RuntimeException;
use function base64_encode;
use function file_exists;
use function file_get_contents;
use function json_decode;
use function rtrim;
use function sprintf;
/**
* Adapter for /public/build/manifest.json.
*
* Resolves entrypoints to chunk filenames.
*/
class AssetManifest
{
private string $dir;
private string $dirUrl;
protected array $entrypoints = [];
protected array $chunks = [];
protected array $assets = [];
/**
* @param string $buildDir
* @param string $buildDirUrl
*/
public function __construct($buildDir, $buildDirUrl)
{
$this->dir = rtrim($buildDir, '/');
$this->dirUrl = rtrim($buildDirUrl, '/');
$manifestFile = "$this->dir/manifest.json";
if (! file_exists($manifestFile)) {
throw new InvalidArgumentException('Asset manifest not found.');
}
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$manifest = json_decode(file_get_contents($manifestFile), true);
if (! $manifest) {
throw new RuntimeException('Failed to load asset manifest.');
}
foreach ($manifest['chunks'] as $chunk) {
$this->chunks[ $chunk['id'] ] = $chunk;
}
$this->entrypoints = $manifest['entrypoints'];
foreach ($manifest['assets'] as $asset) {
$sourceFilename = $asset['info']['sourceFilename'] ?? null;
if (! $sourceFilename) {
continue;
}
$this->assets[$sourceFilename] = $asset;
}
}
/**
* @param string $handle
* @return string
*/
public function getStyleFilename($handle)
{
$filename = null;
$entrypoint = $this->entrypoints[ $handle ] ?? null;
if (! $entrypoint) {
throw new RuntimeException("No entrypoint found for style $handle");
}
foreach ($entrypoint['chunks'] as $chunkId) {
$chunk = $this->chunks[ $chunkId ];
foreach ($chunk['files'] as $file) {
if (! Str::endsWith($file, '.css')) {
continue;
}
$filename = $file;
break;
}
}
return $filename;
}
/**
* @param string $sourceFilename
* @return string
*/
public function getStylePath($sourceFilename)
{
return $this->dir . '/' . $this->getStyleFilename($sourceFilename);
}
/**
* @param string $handle
* @return string
*/
public function getStyleUrl($handle)
{
return $this->dirUrl . '/' . $this->getStyleFilename($handle);
}
/**
* @param string $entrypointName
* @return array<array{handle:string,url:string,deps:string[]}>
*/
public function getStyles($entrypointName)
{
if (empty($this->entrypoints[ $entrypointName ])) {
throw new InvalidArgumentException("Entrypoint $entrypointName not found");
}
$entrypoint = $this->entrypoints[ $entrypointName ];
$styles = [];
foreach ($entrypoint['chunks'] as $chunkId) {
$chunk = $this->chunks[ $chunkId ];
$i = 0;
foreach ($chunk['files'] as $file) {
if (! Str::endsWith($file, '.css')) {
continue;
}
$fileHandle = 'berghs-' . sanitize_title($chunk['names'][0]);
if (0 !== $i) {
$fileHandle .= "-$i";
}
$styles[] = [
'handle' => $fileHandle,
'file' => "$this->dir/$file",
'url' => "$this->dirUrl/$file",
'deps' => [],
];
$i++;
}
}
return $styles;
}
/**
* Gets all scripts for the entrypoint.
*
* Includes deps as provided by `@wordpress/dependency-extraction-webpack-plugin`.
*
* @param string $entrypointName
* @return array<array{handle:string,url:string,deps:string[]}>
*/
public function getScripts($entrypointName)
{
$entrypoint = $this->entrypoints[ $entrypointName ] ?? null;
if (! $entrypoint) {
throw new InvalidArgumentException("Entrypoint $entrypointName not found");
}
$assetFilename = null;
foreach ($entrypoint['assets'] as $asset) {
if (! Str::endsWith($asset['name'], '.asset.php')) {
continue;
}
$assetFilename = $asset['name'];
break;
}
if (! $assetFilename) {
throw new InvalidArgumentException("Asset file not found for entrypoint $entrypointName.");
}
$asset = require "$this->dir/$assetFilename";
$deps = $asset['dependencies'];
$entrypoint = $this->entrypoints[ $entrypointName ];
$scripts = [];
foreach ($entrypoint['chunks'] as $chunkId) {
$chunk = $this->chunks[ $chunkId ];
$i = 0;
foreach ($chunk['files'] as $file) {
if (! Str::endsWith($file, '.js')) {
continue;
}
$fileHandle = 'berghs-' . sanitize_title($chunk['names'][0]);
if (0 !== $i) {
$fileHandle .= "-$i";
}
$scripts[] = [
'handle' => $fileHandle,
'file' => "$this->dir/$file",
'url' => "$this->dirUrl/$file",
'deps' => $deps,
'version' => null,
];
$i++;
}
}
return $scripts;
}
/**
* @param string $sourceFilename
* @return string
*/
public function getAssetFilename($sourceFilename)
{
$asset = $this->assets[$sourceFilename] ?? null;
if (! $asset) {
throw new InvalidArgumentException("Asset $sourceFilename not found.");
}
return $asset['name'];
}
/**
* @param string $sourceFilename
* @return string
*/
public function getAssetPath($sourceFilename)
{
return $this->dir . '/' . $this->getAssetFilename($sourceFilename);
}
/**
* @param string $sourceFilename
* @return string
*/
public function getAssetUrl($sourceFilename)
{
return $this->dirUrl . '/' . $this->getAssetFilename($sourceFilename);
}
/**
* @param string $sourceFilename
* @param string $mediaType
* @return string
*/
public function getAssetDataUri($sourceFilename, $mediaType = 'application/octet-stream')
{
$filename = $this->getAssetFilename($sourceFilename);
$data = file_get_contents($this->dir . '/' . $filename);
return sprintf('data:%s;base64,%s', $mediaType, base64_encode($data));
}
}
<?php
function enqueueScript(string $entrypoint, bool $inFooter = true)
{
$scripts = $this->getManifest()->getScripts($entrypoint);
foreach ($scripts as $script) {
wp_enqueue_script(
$script['handle'],
$script['url'],
$script['deps'],
null,
$inFooter
);
}
}
function enqueueStyle(string $entrypoint, array $deps = []): void
{
$styles = $this->getManifest()->getStyles($entrypoint);
foreach ($styles as $style) {
wp_enqueue_style(
$style['handle'],
$style['url'],
array_merge(
$deps,
$style['deps'],
),
null
);
}
}
module.exports = {
...,
plugins: [
new DependencyExtractionWebpackPlugin({
outputFilename: `[name].asset.php`,
}),
// Used by App\Support\Assets\AssetManifest
new BundleAnalyzerPlugin({
analyzerMode: 'disabled',
generateStatsFile: true,
statsFilename: 'manifest.json',
statsOptions: {
all: false,
ids: true,
entrypoints: true,
chunks: true,
assets: true,
},
}),
],
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment