dimti
2 years ago
11 changed files with 402 additions and 78 deletions
-
24Plugin.php
-
14README.md
-
2classes/ManifestReader.php
-
77classes/TwigFilters.php
-
45classes/bundlers/Bundler.php
-
166classes/bundlers/ViteBundler.php
-
6classes/bundlers/WebpackEncoreBundler.php
-
72classes/bundlers/vite/ViteEntrypoint.php
-
61components/Manifest.php
-
3components/manifest/default.htm
-
10config/config.php
@ -0,0 +1,14 @@ |
|||
# Env file example |
|||
|
|||
## Vitejs |
|||
```bash |
|||
VITE_DEV_SERVER_PROTOCOL=http |
|||
VITE_DEV_SERVER_HOST=localhost |
|||
VITE_DEV_SERVER_PORT=3000 |
|||
|
|||
VITE_PATH_SRC=assets/src |
|||
VITE_DIR_ENTRYPOINTS=entrypoints |
|||
VITE_PATH_BUILD=assets/build |
|||
VITE_ENTRYPOINTS_FILE=entrypoints.json |
|||
VITE_DEV_ENABLED=true |
|||
``` |
@ -0,0 +1,45 @@ |
|||
<?php namespace Wpstudio\AssetsManifest\Classes\Bundlers; |
|||
|
|||
use Wpstudio\AssetsManifest\Classes\AssetsManifestException; |
|||
use Wpstudio\AssetsManifest\Classes\ManifestReader; |
|||
|
|||
abstract class Bundler |
|||
{ |
|||
const BUNDLER_WEBPACK_ENCORE = 'webpack-encore'; |
|||
const BUNDLER_VITE = 'vite'; |
|||
|
|||
public static array $bundlers = [ |
|||
self::BUNDLER_WEBPACK_ENCORE => WebpackEncoreBundler::class, |
|||
self::BUNDLER_VITE => ViteBundler::class, |
|||
]; |
|||
|
|||
protected ManifestReader $manifestReader; |
|||
|
|||
public function __construct(ManifestReader $manifestReader) |
|||
{ |
|||
$this->manifestReader = $manifestReader; |
|||
} |
|||
|
|||
public function getBundlerType(): string |
|||
{ |
|||
return array_flip(self::$bundlers)[get_class($this)]; |
|||
} |
|||
|
|||
public function validateBundlerType(string $bundlerType) |
|||
{ |
|||
if ($this->getBundlerType() != $bundlerType) { |
|||
throw new AssetsManifestException( |
|||
sprintf( |
|||
'Expected bundler type is %s, but actual %s', |
|||
$bundlerType, |
|||
$this->getBundlerType() |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
public function getEntrypoint(string $entrypointPathInManifest): array |
|||
{ |
|||
return $this->manifestReader->get($entrypointPathInManifest); |
|||
} |
|||
} |
@ -0,0 +1,166 @@ |
|||
<?php namespace Wpstudio\AssetsManifest\Classes\Bundlers; |
|||
|
|||
use Cms\Classes\Theme; |
|||
use Illuminate\Support\Collection; |
|||
use Wpstudio\AssetsManifest\Classes\AssetsManifestException; |
|||
use Config; |
|||
use Wpstudio\AssetsManifest\Classes\Bundlers\Vite\ViteEntrypoint; |
|||
|
|||
class ViteBundler extends Bundler |
|||
{ |
|||
private array $assetsInjected = []; |
|||
|
|||
public function getEntrypointAssets(string $entrypointName): string |
|||
{ |
|||
$entrypoint = $this->getViteEntrypoint($entrypointName); |
|||
|
|||
$tags = collect(); |
|||
|
|||
if ($entrypoint->hasImports()) { |
|||
$entrypoint->getImports()->filter(fn(string $entrypointNameFromImports) => !$this->hasInjected($entrypointNameFromImports))->each( |
|||
fn(string $entrypointNameFromImports) => $this->injectAsset( |
|||
$entrypointNameFromImports, |
|||
$tags, |
|||
$this->getEntrypointAssets($entrypointNameFromImports) |
|||
) |
|||
); |
|||
} |
|||
if ($entrypoint->hasCss()) { |
|||
$entrypoint->getCss()->filter(fn(string $cssAssetRelativeFilePath) => !$this->hasInjected($cssAssetRelativeFilePath))->each( |
|||
fn(string $cssAssetRelativeFilePath) => $this->injectAsset( |
|||
$cssAssetRelativeFilePath, |
|||
$tags, |
|||
$this->getStylesheetTag($this->getViteAssetUrl($cssAssetRelativeFilePath)) |
|||
) |
|||
); |
|||
} |
|||
|
|||
if (!$this->hasInjected($entrypointName)) { |
|||
$this->injectAsset($entrypointName, $tags, $this->getScriptTag($entrypointName, $entrypoint)); |
|||
} |
|||
|
|||
return $tags->implode(PHP_EOL); |
|||
} |
|||
|
|||
public function getViteDevClientScriptTag(): string |
|||
{ |
|||
return sprintf( |
|||
'<script type="module" src="%s"></script>', |
|||
sprintf( |
|||
'%s/@vite/client', |
|||
$this->getViteDevServerAddress(), |
|||
) |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* @param string $entrypointName |
|||
* @return string |
|||
* @throws AssetsManifestException |
|||
*/ |
|||
public function getEntrypointStylesheets(string $entrypointName): string |
|||
{ |
|||
if ($this->isViteDevEnabled()) { |
|||
return ''; |
|||
} |
|||
|
|||
$entrypoint = $this->getViteEntrypoint($entrypointName); |
|||
|
|||
$stylesheetTags = collect(); |
|||
|
|||
if ($entrypoint->hasCss()) { |
|||
$entrypoint |
|||
->getCss() |
|||
->filter(fn(string $cssAssetRelativeFilePath) => !$this->hasInjected($cssAssetRelativeFilePath)) |
|||
->each( |
|||
fn(string $cssAssetRelativeFilePath) => $this->injectAsset( |
|||
$cssAssetRelativeFilePath, |
|||
$stylesheetTags, |
|||
$this->getStylesheetTag($this->getViteAssetUrl($cssAssetRelativeFilePath) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
|
|||
return $stylesheetTags->implode(PHP_EOL); |
|||
} |
|||
|
|||
private function getStylesheetTag(string $href): string |
|||
{ |
|||
return sprintf( |
|||
'<link rel="stylesheet" href="%s">', |
|||
$href, |
|||
); |
|||
} |
|||
|
|||
private function getScriptTag(string $entrypointName, ViteEntrypoint $entrypoint): string |
|||
{ |
|||
return sprintf( |
|||
'<script type="module" src="%s" %s></script>', |
|||
$this->isViteDevEnabled() ? |
|||
sprintf( |
|||
'%s/%s', |
|||
$this->getViteDevServerAddress(), |
|||
$this->getViteEntrypointSrc($entrypointName) |
|||
) : |
|||
$this->getViteAssetUrl($entrypoint->getFile()), |
|||
!$this->isViteDevEnabled() ? 'async defer' : '', |
|||
); |
|||
} |
|||
|
|||
private function getViteEntrypointSrc(string $entrypointName): string |
|||
{ |
|||
return sprintf( |
|||
'%s/%s/%s', |
|||
Config::get('wpstudio.assetsmanifest::vite_path_src'), |
|||
Config::get('wpstudio.assetsmanifest::vite_dir_entrypoints'), |
|||
$entrypointName, |
|||
); |
|||
} |
|||
|
|||
private function getViteEntrypoint(string $entrypointName): ViteEntrypoint |
|||
{ |
|||
return new ViteEntrypoint($this->getEntrypoint( |
|||
!starts_with($entrypointName, '_') ? |
|||
$this->getViteEntrypointSrc($entrypointName) : |
|||
$entrypointName |
|||
)); |
|||
} |
|||
|
|||
private function getViteAssetUrl(string $relativeAssetsFilePath): string |
|||
{ |
|||
return sprintf( |
|||
'/themes/%s/%s/%s', |
|||
Theme::getActiveTheme()->getDirName(), |
|||
Config::get('wpstudio.assetsmanifest::vite_path_build'), |
|||
$relativeAssetsFilePath |
|||
); |
|||
} |
|||
|
|||
public function isViteDevEnabled(): bool |
|||
{ |
|||
return Config::get('wpstudio.assetsmanifest::vite_dev_enabled'); |
|||
} |
|||
|
|||
private function getViteDevServerAddress(): string |
|||
{ |
|||
return sprintf( |
|||
'%s://%s:%s', |
|||
Config::get('wpstudio.assetsmanifest::vite_dev_server_protocol'), |
|||
Config::get('wpstudio.assetsmanifest::vite_dev_server_host'), |
|||
Config::get('wpstudio.assetsmanifest::vite_dev_server_port'), |
|||
); |
|||
} |
|||
|
|||
private function hasInjected(string $assetsUid): bool |
|||
{ |
|||
return in_array($assetsUid, $this->assetsInjected); |
|||
} |
|||
|
|||
private function injectAsset(string $assetsUid, Collection $tags, string $assetTag): void |
|||
{ |
|||
$this->assetsInjected[] = $assetsUid; |
|||
|
|||
$tags->add($assetTag); |
|||
} |
|||
} |
@ -0,0 +1,6 @@ |
|||
<?php namespace Wpstudio\AssetsManifest\Classes\Bundlers; |
|||
|
|||
class WebpackEncoreBundler extends Bundler |
|||
{ |
|||
|
|||
} |
@ -0,0 +1,72 @@ |
|||
<?php namespace Wpstudio\AssetsManifest\Classes\Bundlers\Vite; |
|||
|
|||
use Illuminate\Support\Collection; |
|||
|
|||
class ViteEntrypoint |
|||
{ |
|||
public string $file; |
|||
public ?string $src; |
|||
public ?bool $isEntry; |
|||
public ?Collection $imports; |
|||
public ?Collection $css; |
|||
|
|||
public function __construct(array $entrypoint) |
|||
{ |
|||
$this->file = $entrypoint['file']; |
|||
|
|||
if (array_key_exists('src', $entrypoint)) { |
|||
$this->isEntry = $entrypoint['src']; |
|||
} |
|||
|
|||
if (array_key_exists('isEntry', $entrypoint)) { |
|||
$this->isEntry = $entrypoint['isEntry']; |
|||
} |
|||
|
|||
if (array_key_exists('imports', $entrypoint)) { |
|||
$this->imports = collect($entrypoint['imports']); |
|||
} |
|||
|
|||
if (array_key_exists('css', $entrypoint)) { |
|||
$this->css = collect($entrypoint['css']); |
|||
} |
|||
} |
|||
|
|||
public function getIsEntry(): bool |
|||
{ |
|||
if (isset($this->isEntry) && $this->isEntry) { |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public function hasImports() |
|||
{ |
|||
return isset($this->imports) && $this->imports; |
|||
} |
|||
|
|||
public function getImports(): Collection |
|||
{ |
|||
return $this->imports; |
|||
} |
|||
|
|||
public function hasCss() |
|||
{ |
|||
return isset($this->css) && $this->css; |
|||
} |
|||
|
|||
public function getCss(): Collection |
|||
{ |
|||
return $this->css; |
|||
} |
|||
|
|||
public function getFile(): string |
|||
{ |
|||
return $this->file; |
|||
} |
|||
|
|||
public function getSrc(): string |
|||
{ |
|||
return $this->src; |
|||
} |
|||
} |
@ -1,61 +0,0 @@ |
|||
<?php namespace Wpstudio\Assetsmanifest\Components; |
|||
|
|||
use Cms\Classes\ComponentBase; |
|||
use Cms\Classes\Theme; |
|||
use Wpstudio\AssetsManifest\Classes\ManifestReader; |
|||
|
|||
class Manifest extends ComponentBase |
|||
{ |
|||
const LANG_PREFIX = 'wpstudio.assetsmanifest::lang.components.manifest.'; |
|||
|
|||
private ?ManifestReader $manifestReader; |
|||
|
|||
public function componentDetails(): array |
|||
{ |
|||
return [ |
|||
'name' => self::LANG_PREFIX . 'name', |
|||
'description' => self::LANG_PREFIX . 'description', |
|||
]; |
|||
} |
|||
|
|||
public function defineProperties(): array |
|||
{ |
|||
return [ |
|||
'path' => [ |
|||
'title' => self::LANG_PREFIX . 'properties.path.title', |
|||
'description' => self::LANG_PREFIX . 'properties.path.description', |
|||
'validationPattern' => '^[^/].*/manifest.json', |
|||
'validationMessage' => self::LANG_PREFIX . 'properties.path.validationMessage', |
|||
'placeholder' => 'assets/build/manifest.json', |
|||
'default' => 'assets/build/manifest.json', |
|||
'showExternalParam' => false, |
|||
'required' => true, |
|||
] |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @return void |
|||
* @throws \Wpstudio\AssetsManifest\Classes\AssetsManifestException |
|||
*/ |
|||
public function init(): void |
|||
{ |
|||
app()->instance(Manifest::class, $this); |
|||
|
|||
$this->prepareVars(); |
|||
} |
|||
|
|||
/** |
|||
* @return void |
|||
* @throws \Wpstudio\AssetsManifest\Classes\AssetsManifestException |
|||
*/ |
|||
private function prepareVars(): void |
|||
{ |
|||
$this->manifestReader = new ManifestReader(Theme::getActiveTheme()->getPath() . '/' . $this->property('path')); |
|||
} |
|||
|
|||
public function getManifestReader(): ManifestReader |
|||
{ |
|||
return $this->manifestReader; |
|||
} |
|||
} |
@ -1,3 +0,0 @@ |
|||
<p>This is the default markup for component manifest</p> |
|||
|
|||
<small>You can delete this file if you want</small> |
@ -0,0 +1,10 @@ |
|||
<?php return [ |
|||
'vite_dev_enabled' => env('VITE_DEV_ENABLED', true), |
|||
'vite_dev_server_protocol' => env('VITE_DEV_SERVER_PROTOCOL', 'http'), |
|||
'vite_dev_server_host' => env('VITE_DEV_SERVER_HOST', 'localhost'), |
|||
'vite_dev_server_port' => env('VITE_DEV_SERVER_PORT', '3000'), |
|||
'vite_path_src' => env('VITE_PATH_SRC', 'assets/src'), |
|||
'vite_dir_entrypoints' => env('VITE_DIR_ENTRYPOINTS', 'entrypoints'), |
|||
'vite_path_build' => env('VITE_PATH_BUILD', 'assets/build'), |
|||
'bundler' => \Wpstudio\AssetsManifest\Classes\Bundlers\ViteBundler::class |
|||
]; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue