Skip to content

SCSS/JS compilation breaks on TYPO3 v14 for any page.includeCSS/page.includeJS #1632

Description

@Make66

SCSS/JS compilation breaks on TYPO3 v14 for any page.includeCSS/page.includeJS entry pointing at fileadmin/... (or other FAL-resolved, non-EXT: paths)

Environment

  • bk2k/bootstrap-package: 16.0.0
  • typo3/cms-core: v14.3.4
  • PHP: 8.4

Summary

On TYPO3 v14, any .scss/.less file referenced in page.includeCSS (or .js in page.includeJS) via a fileadmin-relative path fails to compile with:

ScssPhp\ScssPhp\Exception\CompilerException: .../public/FAL:1:/templates/demo/theme.scss
file not found for @import: FAL:1:/templates/demo/theme.scss on line 1, at column 0

EXT:-relative paths (e.g. EXT:bootstrap_package/Resources/Public/Scss/bootstrap5/theme.scss) are unaffected — only paths resolved through TYPO3's file abstraction layer break.

Root cause

In TYPO3 v14, TYPO3\CMS\Frontend\Http\RequestHandler::generatePageContent() no longer passes plain path strings into PageRenderer::addCssFile()/addJsFile(). It now wraps every page.includeCSS/page.includeJS value in a SystemResourceInterface via:

$cssResource = $this->systemResourceFactory->createResource($cssResource);
(vendor/typo3/cms-frontend/Classes/Http/RequestHandler.php, includeCSS handling around line 446)

For anything that isn't an EXT: reference (e.g. a fileadmin/... path), the resultntation used as the PageRenderer cssFiles array key becomes a FAL "combinedidentifier" of the form:
FAL:: e.g. FAL:1:/templates/demo/theme.scss.
BK2K\BootstrapPackage\Hooks\PageRenderer\PreProcessHook::execute() (Classes/Hooksphp:42) takes that array key literally and passes it straight toCompileService::getCompiledFile(), which calls GeneralUtility::getFileAbsFileName($file). That function has no notion of the FAL: notation, so it just prepends the public path to the literal string, producing a bogus path like /var/www/html/public/FAL:1:/templates/demo/thesn't exist, hence the CompilerException.

This affects the extension's own documented usage pattern of per-site/per-mandanteadmin/ (not just custom setups) — any TYPO3 v14 site using a fileadmin-basedtheme.scss convention hits this immediately.

Suggested fix

Resolve the FAL: identifier back to a real filesystem path via ResourceFactory before handing it to CompileService, in PreProcessHook::execute(). Patch attached below — verified against a TYPO3
v14.3.4 + bootstrap-package 16.0.0 site using a fileadmin/templates/{mandant}/theme.scss convention succeeds and the compiled CSS is correct. EXT:-relative entries are untouched since they never start with FAL:.

Patch

--- a/Classes/Hooks/PageRenderer/PreProcessHook.php
+++ b/Classes/Hooks/PageRenderer/PreProcessHook.php
@@ -12,6 +12,7 @@
use BK2K\BootstrapPackage\Service\CompileService;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Http\ApplicationType;
+use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
@@ -39,7 +40,8 @@
$files = [];
if (is_array($params[$key])) {
foreach ($params[$key] as $file => $settings) {

  •                $compiledFile = $this->getCompileService()->getCompiledFile($GLOBALS['TYPO3_REQUEST'], $file);
    
  •                $resolvedFile = $this->resolveFalIdentifier($file);
    
  •                $compiledFile = $this->getCompileService()->getCompiledFile($GLOBALS['TYPO3_REQUEST'], $resolvedFile);
                   if ($compiledFile !== null) {
                       $settings['file'] = $compiledFile;
                       $files[$compiledFile] = $settings;
    

@@ -53,6 +55,33 @@
}

 /**
  • * TYPO3 >= 14 wraps page.includeCSS/includeJS values that point at fileadmin (or
    
  • * other non-EXT:) files in a SystemResource object before this hook ever se
    
  • * the array key we receive is then its string representation, e.g.
    
  • * "FAL:1:/templates/demo/theme.scss", which GeneralUtility::getFileAbsFileN
    
  • * cannot resolve. Resolve it back to a real filesystem path via FAL so the SCSS
    
  • * compiler can find the file. EXT:-relative values are untouched (unaffecte
    
  • *
    
  • * @param string $file
    
  • * @return string
    
  • */
    
  • protected function resolveFalIdentifier(string $file): string
  • {
  •    if (str_starts_with($file, 'FAL:')) {
    
  •        try {
    
  •            $fileObject = GeneralUtility::makeInstance(ResourceFactory::class)
    
  •                ->getFileObjectFromCombinedIdentifier(substr($file, 4));
    
  •            if ($fileObject !== null) {
    
  •                return $fileObject->getForLocalProcessing(false);
    
  •            }
    
  •        } catch (\Throwable) {
    
  •            // fall through and let the caller deal with the original value
    
  •        }
    
  •    }
    
  •    return $file;
    
  • }
  • /**
    • Get the compile service
    • @return CompileService

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions