Skip to content

Commit

Permalink
[FEATURE] Prevent duplicate cache busting with TYPO3 13
Browse files Browse the repository at this point in the history
By default, TYPO3's asset renderer makes sure that all asset files are served
with an appended timestamp to prevent client-side caching issues (also known
as cache busting). For assets generated by vite, this is really not necessary
because these file names already contain a hash that is derived from the file's
contents and thus serves the same purpose as the timestamp.

TYPO3 13 introduced a new flag for the asset collector API which allows to
disable the default cache busting. This change makes use of that new feature,
while leaving the behavior in TYPO3 12 as-is. With TYPO3 13, this also
(marginally) reduces PHP processing of theses files since TYPO3 doesn't
need to fetch and append the timestamp anymore.

Because of cross-version compatibility, the functional tests unfortunately
have a side effect now: They behave differently depending on the TYPO3
version they are executed in. However, this also tests the real behavior of the
extension, so all in all it serves its purpose.

Resolves: #76
  • Loading branch information
s2b committed Oct 31, 2024
1 parent 5fcf75a commit 72b87ef
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 56 deletions.
43 changes: 39 additions & 4 deletions Classes/Service/ViteService.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public function addAssetsFromDevServer(
array $scriptTagAttributes = []
): void {
$entry = $this->determineAssetIdentifierFromExtensionPath($entry);
$assetOptions = ['external' => $this->useExternalFlag(), ...$assetOptions];

$scriptTagAttributes = $this->prepareScriptAttributes($scriptTagAttributes);
$this->assetCollector->addJavaScript(
Expand Down Expand Up @@ -130,35 +131,58 @@ public function addAssetsFromManifest(
), 1683200524);
}

// The "external" flag has been introduced with TYPO3 v13. It allows bypassing
// of the default path preparation by AssetRenderer, including the addition of
// cache-busting parameters to all asset files. As this is not necessary for files
// generated by vite, which already contain a hash in their file name, this behavior
// is avoided with v13. This also improves the behavior of dynamic imports, which
// could result in duplicate requests before.
$assetOptions = ['external' => $this->useExternalFlag(), ...$assetOptions];

$entryPoint = $manifest->get($entry);

if (!$entryPoint->isCss()) {
$scriptTagAttributes = $this->prepareScriptAttributes($scriptTagAttributes);

$this->assetCollector->addJavaScript(
"vite:{$entry}",
$outputDir . $entryPoint->file,
$this->prepareAssetPath($outputDir . $entryPoint->file, $assetOptions['external']),
['type' => 'module', ...$scriptTagAttributes],
$assetOptions
);
}

if ($addCss) {
if ($entryPoint->isCss()) {
$this->assetCollector->addStyleSheet("vite:{$entry}", $outputDir . $entryPoint->file, $cssTagAttributes, $assetOptions);
$this->assetCollector->addStyleSheet(
"vite:{$entry}",
$this->prepareAssetPath($outputDir . $entryPoint->file, $assetOptions['external']),
$cssTagAttributes,
$assetOptions
);
}

$cssTagAttributes = $this->prepareCssAttributes($cssTagAttributes);

foreach ($manifest->getImportsForEntrypoint($entry) as $import) {
$identifier = md5($import->identifier . '|' . serialize($cssTagAttributes) . '|' . serialize($assetOptions));
foreach ($import->css as $file) {
$this->assetCollector->addStyleSheet("vite:{$identifier}:{$file}", $outputDir . $file, $cssTagAttributes, $assetOptions);
$this->assetCollector->addStyleSheet(
"vite:{$identifier}:{$file}",
$this->prepareAssetPath($outputDir . $file, $assetOptions['external']),
$cssTagAttributes,
$assetOptions
);
}
}

foreach ($manifest->get($entry)->css as $file) {
$this->assetCollector->addStyleSheet("vite:{$entry}:{$file}", $outputDir . $file, $cssTagAttributes, $assetOptions);
$this->assetCollector->addStyleSheet(
"vite:{$entry}:{$file}",
$this->prepareAssetPath($outputDir . $file, $assetOptions['external']),
$cssTagAttributes,
$assetOptions
);
}
}
}
Expand Down Expand Up @@ -244,6 +268,17 @@ protected function determineOutputDirFromManifestFile(string $manifestFile): str
return $outputDir . '/';
}

protected function prepareAssetPath(string $assetPath, bool $external): string
{
return $external ? PathUtility::getAbsoluteWebPath($assetPath) : $assetPath;
}

protected function useExternalFlag(): bool
{
// TODO remove this when support for TYPO3 v12 is dropped
return (new \TYPO3\CMS\Core\Information\Typo3Version())->getMajorVersion() >= 13;
}

protected function prepareScriptAttributes(array $attributes): array
{
foreach (['async', 'defer', 'nomodule'] as $attr) {
Expand Down
42 changes: 26 additions & 16 deletions Tests/Functional/ViewHelpers/Asset/ViteViewHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,27 @@ public function setUp(): void

public static function renderDataProvider(): array
{
$manifestDir = self::getInstancePath() . '/fileadmin/Fixtures/';
$manifestDir = 'fileadmin/Fixtures/';
// TODO remove this when support for TYPO3 v12 is dropped
if (!self::useExternalFlag()) {
$manifestDir = self::getInstancePath() . '/' . $manifestDir;
}
return [
'basic' => [
'<vac:asset.vite manifest="fileadmin/Fixtures/ValidManifest/manifest.json" entry="Main.js" />',
[
'vite:Main.js' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-4483b920.js',
'attributes' => ['type' => 'module'],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
[],
[
'vite:Main.js:assets/Main-973bb662.css' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-973bb662.css',
'attributes' => [],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
[],
Expand All @@ -68,7 +72,7 @@ public static function renderDataProvider(): array
'vite:Main.js' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-4483b920.js',
'attributes' => ['type' => 'module'],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
[],
Expand All @@ -81,15 +85,15 @@ public static function renderDataProvider(): array
'vite:Default.js' => [
'source' => $manifestDir . 'DefaultManifest/assets/Default-4483b920.js',
'attributes' => ['type' => 'module'],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
[],
[
'vite:Default.js:assets/Default-973bb662.css' => [
'source' => $manifestDir . 'DefaultManifest/assets/Default-973bb662.css',
'attributes' => [],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
[],
Expand All @@ -100,15 +104,15 @@ public static function renderDataProvider(): array
'vite:Main.js' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-4483b920.js',
'attributes' => ['type' => 'module'],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
[],
[
'vite:Main.js:assets/Main-973bb662.css' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-973bb662.css',
'attributes' => [],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
[],
Expand All @@ -124,15 +128,15 @@ public static function renderDataProvider(): array
'vite:Main.js' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-4483b920.js',
'attributes' => ['type' => 'module', 'async' => 'async'],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
[],
[
'vite:Main.js:assets/Main-973bb662.css' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-973bb662.css',
'attributes' => ['media' => 'print'],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
[],
Expand All @@ -144,15 +148,15 @@ public static function renderDataProvider(): array
'vite:Main.js' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-4483b920.js',
'attributes' => ['type' => 'module'],
'options' => ['priority' => true, 'useNonce' => false],
'options' => ['priority' => true, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
[],
[
'vite:Main.js:assets/Main-973bb662.css' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-973bb662.css',
'attributes' => [],
'options' => ['priority' => true, 'useNonce' => false],
'options' => ['priority' => true, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
],
Expand All @@ -162,15 +166,15 @@ public static function renderDataProvider(): array
'vite:Main.js' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-4483b920.js',
'attributes' => ['type' => 'module'],
'options' => ['priority' => false, 'useNonce' => true],
'options' => ['priority' => false, 'useNonce' => true, 'external' => self::useExternalFlag()],
],
],
[],
[
'vite:Main.js:assets/Main-973bb662.css' => [
'source' => $manifestDir . 'ValidManifest/assets/Main-973bb662.css',
'attributes' => [],
'options' => ['priority' => false, 'useNonce' => true],
'options' => ['priority' => false, 'useNonce' => true, 'external' => self::useExternalFlag()],
],
],
[],
Expand Down Expand Up @@ -230,12 +234,12 @@ public function renderWithDevServer(): void
'vite' => [
'source' => 'https://localhost:5173/@vite/client',
'attributes' => ['type' => 'module'],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
'vite:Main.js' => [
'source' => 'https://localhost:5173/Main.js',
'attributes' => ['type' => 'module'],
'options' => ['priority' => false, 'useNonce' => false],
'options' => ['priority' => false, 'useNonce' => false, 'external' => self::useExternalFlag()],
],
],
$assetCollector->getJavaScripts(false)
Expand Down Expand Up @@ -275,4 +279,10 @@ protected function createRenderingContext(): RenderingContextInterface

return $context;
}

protected static function useExternalFlag(): bool
{
// TODO remove this when support for TYPO3 v12 is dropped
return (new \TYPO3\CMS\Core\Information\Typo3Version())->getMajorVersion() >= 13;
}
}
Loading

0 comments on commit 72b87ef

Please sign in to comment.