diff --git a/src/Common/Service/Asset.php b/src/Common/Service/Asset.php index e2fdb4a8..55336119 100644 --- a/src/Common/Service/Asset.php +++ b/src/Common/Service/Asset.php @@ -563,6 +563,7 @@ public function addLibrary(string $sKey, array $aUrls): self * @param string|null $sForceType The asset's file type (e.g., JS or CSS) * @param bool $bAsync Whether to load the asset asynchronously * @param bool $bDefer Whether to defer loading the asset + * @param bool|null $bModule Whether asset is a JS module or not (null = undefined) * * @return $this * @throws AssetException @@ -572,7 +573,8 @@ public function load( string $sAssetLocation = null, string $sForceType = null, bool $bAsync = false, - bool $bDefer = false + bool $bDefer = false, + bool $bModule = null ): self { $aAssets = (array) $mAssets; @@ -600,13 +602,13 @@ public function load( foreach ($aAssets as $sAsset) { if (preg_match('#^https?://#', $sAsset)) { - $this->loadUrl($sAsset, $sForceType, $bAsync, $bDefer); + $this->loadUrl($sAsset, $sForceType, $bAsync, $bDefer, $bModule); } elseif (substr($sAsset, 0, 0) === '/') { - $this->loadAbsolute(substr($sAsset, 1), $sForceType, $bAsync, $bDefer); + $this->loadAbsolute(substr($sAsset, 1), $sForceType, $bAsync, $bDefer, $bModule); } else { - $this->{$sAssetLocationMethod}($sAsset, $sForceType, $bAsync, $bDefer, $sAssetLocation); + $this->{$sAssetLocationMethod}($sAsset, $sForceType, $bAsync, $bDefer, $bModule, $sAssetLocation); } } @@ -624,11 +626,12 @@ public function load( * @param string|null $sForceType Force a particular type of asset (i.e. JS or CSS) * @param bool $bAsync Whether to load the asset asynchronously * @param bool $bDefer Whether to defer loading the asset + * @param bool|null $bModule Whether asset is a JS module or not (null = undefined) * * @return $this * @throws AssetException */ - protected function loadUrl(string $sAsset, ?string $sForceType, bool $bAsync, bool $bDefer): self + protected function loadUrl(string $sAsset, ?string $sForceType, bool $bAsync, bool $bDefer, bool $bModule = null): self { $sType = $this->determineType($sAsset, $sForceType); @@ -640,11 +643,11 @@ protected function loadUrl(string $sAsset, ?string $sForceType, bool $bAsync, bo case static::TYPE_JS: case static::TYPE_JS_FOOTER: - $this->aJs['URL-' . $sAsset] = [$sAsset, $bAsync, $bDefer]; + $this->aJs['URL-' . $sAsset] = [$sAsset, $bAsync, $bDefer, $bModule]; break; case static::TYPE_JS_HEADER: - $this->aJsHeader['URL-' . $sAsset] = [$sAsset, $bAsync, $bDefer]; + $this->aJsHeader['URL-' . $sAsset] = [$sAsset, $bAsync, $bDefer, $bModule]; break; } @@ -660,11 +663,12 @@ protected function loadUrl(string $sAsset, ?string $sForceType, bool $bAsync, bo * @param string|null $sForceType Force a particular type of asset (i.e. JS or CSS) * @param bool $bAsync Whether to load the asset asynchronously * @param bool $bDefer Whether to defer loading the asset + * @param bool|null $bModule Whether asset is a JS module or not (null = undefined) * * @return $this * @throws AssetException */ - protected function loadAbsolute(string $sAsset, ?string $sForceType, bool $bAsync, bool $bDefer): self + protected function loadAbsolute(string $sAsset, ?string $sForceType, bool $bAsync, bool $bDefer, bool $bModule = null): self { $sType = $this->determineType($sAsset, $sForceType); @@ -676,11 +680,11 @@ protected function loadAbsolute(string $sAsset, ?string $sForceType, bool $bAsyn case static::TYPE_JS: case static::TYPE_JS_FOOTER: - $this->aJs['ABSOLUTE-' . $sAsset] = [$this->buildUrl($sAsset), $bAsync, $bDefer]; + $this->aJs['ABSOLUTE-' . $sAsset] = [$this->buildUrl($sAsset), $bAsync, $bDefer, $bModule]; break; case static::TYPE_JS_HEADER: - $this->aJsHeader['ABSOLUTE-' . $sAsset] = [$this->buildUrl($sAsset), $bAsync, $bDefer]; + $this->aJsHeader['ABSOLUTE-' . $sAsset] = [$this->buildUrl($sAsset), $bAsync, $bDefer, $bModule]; break; } @@ -757,15 +761,31 @@ public function output(string $sType = self::TYPE_ALL, bool $bOutput = true): ar // Linked JS if (!empty($this->aJs) && ($sType === static::TYPE_JS || $sType === static::TYPE_ALL)) { foreach ($this->aJs as $aAsset) { - [$sAsset, $bAsync, $bDefer] = $aAsset; - $aOut[] = ''; + [$sAsset, $bAsync, $bDefer, $bModule] = $aAsset; + $aOut[] = sprintf( + '', + $sAsset, + $bAsync ? 'async ' : '', + $bDefer ? 'defer ' : '', + isset($bModule) + ? ($bModule ? 'type="module"' : 'nomodule') . ' ' + : '' + ); } } if (!empty($this->aJsHeader) && ($sType === static::TYPE_JS_HEADER || $sType === static::TYPE_ALL)) { foreach ($this->aJsHeader as $aAsset) { - [$sAsset, $bAsync, $bDefer] = $aAsset; - $aOut[] = ''; + [$sAsset, $bAsync, $bDefer, $bModule] = $aAsset; + $aOut[] = sprintf( + '', + $sAsset, + $bAsync ? ' async' : '', + $bDefer ? ' defer' : '', + isset($bModule) + ? ' ' . ($bModule ? 'type="module"' : 'nomodule') + : '' + ); } } @@ -935,11 +955,12 @@ public function inline($mScript = null, string $sForceType = null, $sJsLocation * @param bool $bAsync Whether to load the asset asynchronously * @param bool $bDefer Whether to defer loading the asset * @param array|string $mModule The module to load from + * @param bool|null $bModule Whether asset is a JS module or not (null = undefined) * * @return $this * @throws AssetException */ - protected function loadModule(string $sAsset, ?string $sForceType, bool $bAsync, bool $bDefer, $mModule): self + protected function loadModule(string $sAsset, ?string $sForceType, bool $bAsync, bool $bDefer, bool $bModule = null, $mModule): self { if (is_array($mModule)) { $sModule = !empty($mModule[0]) ? $mModule[0] : null; @@ -964,6 +985,7 @@ protected function loadModule(string $sAsset, ?string $sForceType, bool $bAsync, $this->addCacheBuster($this->sBaseModuleUrl . $sModule . '/assets/js/' . $sAsset), $bAsync, $bDefer, + $bModule, ]; break; @@ -972,6 +994,7 @@ protected function loadModule(string $sAsset, ?string $sForceType, bool $bAsync, $this->addCacheBuster($this->sBaseModuleUrl . $sModule . '/assets/js/' . $sAsset), $bAsync, $bDefer, + $bModule, ]; break; } @@ -1111,11 +1134,12 @@ public function getCacheBuster(): ?string * @param string|null $sForceType Force a particular type of asset (i.e. JS or CSS) * @param bool $bAsync Whether to load the asset asynchronously * @param bool $bDefer Whether to defer loading the asset + * @param bool|null $bModule Whether asset is a JS module or not (null = undefined) * * @return $this * @throws AssetException */ - protected function loadApp(string $sAsset, ?string $sForceType, bool $bAsync, bool $bDefer): self + protected function loadApp(string $sAsset, ?string $sForceType, bool $bAsync, bool $bDefer, bool $bModule = null): self { $sType = $this->determineType($sAsset, $sForceType); @@ -1127,11 +1151,21 @@ protected function loadApp(string $sAsset, ?string $sForceType, bool $bAsync, bo case static::TYPE_JS: case static::TYPE_JS_FOOTER: - $this->aJs['APP-' . $sAsset] = [$this->buildUrl($this->sJsDir . $sAsset), $bAsync, $bDefer]; + $this->aJs['APP-' . $sAsset] = [ + $this->buildUrl($this->sJsDir . $sAsset), + $bAsync, + $bDefer, + $bModule, + ]; break; case static::TYPE_JS_HEADER: - $this->aJsHeader['APP-' . $sAsset] = [$this->buildUrl($this->sJsDir . $sAsset), $bAsync, $bDefer]; + $this->aJsHeader['APP-' . $sAsset] = [ + $this->buildUrl($this->sJsDir . $sAsset), + $bAsync, + $bDefer, + $bModule, + ]; break; }