Skip to content

Commit

Permalink
[BUGFIX] Resolve SCSS files correctly when using symlinked paths (#866)
Browse files Browse the repository at this point in the history
* [BUGFIX] Resolve SCSS files correctly when using symlinked paths

When an extension is symlinked, e.g. by using Composer path repositories,
importing scss files from other extensions can fail.

This is now fixed in two ways. The backwards compatible way properly
resolves the path to the final file using the visual path instead
of the real path. for future imports, the extension path is added as import path
so that imports can be done relative to this directory as well.

As a last option for users only relying on the PHP parser,
files can also be imported using the well know TYPO3 syntax
EXT:ext_key/Resources/Public/Scss/my.scss

* [TASK] Add tests for relative and core syntax handling

* [BUGFIX] Correct paths to files

* [BUGFIX] Fix relative path detection

* [BUGFIX] Use absolute path to files

* [BUGFIX] Ensure relative paths start with dots

* [TASK] Use existing $absoluteFilename

* [TASK] Additional test for absolute include containing a relative path

* [TASK] Bump actions/checkout to v2

* [BUGFIX] Use absolute tempDirectory

* [TASK] Trigger error if file not found

* [TASK] Throw exception if file not found

* [BUGFIX] Use exception class

* [TASK] Show full path in exception

* [TASK] Sleep before every funnctional test

* [TASK] Remove debugging exception

* [TASK] Remove sleep

* [BUGFIX] Use settings instead of recalculation

* [TASK] Add test for legacy workaround include

Co-authored-by: Benjamin Kott <benjamin.kott@outlook.com>
Co-authored-by: Gilbertsoft <25326036+gilbertsoft@users.noreply.github.com>
  • Loading branch information
3 people committed May 12, 2020
1 parent bf94f47 commit 1e4d71e
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 11 deletions.
37 changes: 34 additions & 3 deletions Classes/Parser/ScssParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
use ScssPhp\ScssPhp\Compiler;
use ScssPhp\ScssPhp\Formatter\Crunched;
use ScssPhp\ScssPhp\Version;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;

/**
* ScssParser
Expand Down Expand Up @@ -83,10 +85,39 @@ protected function parseFile($file, $settings)
'sourceMapBasepath' => '<PATH DOES NOT EXIST BUT SUPRESSES WARNINGS>'
]);
}
$css = $scss->compile('@import "' . $file . '"');
$absoluteFilename = $settings['file']['absolute'];
// Adds visual directory path of the initial file as import path
// This scenarios happens, when e.g. developing packages using the `path`
// repository feature of Composer - having one package in `public/typo3conf/ext/`
// and the other one symlinked in e.g. `packages/`.
// Since the PHP SCSS parser works on resolved real paths, the symlinked context is lost.
$visualImportPath = dirname($absoluteFilename);
$scss->addImportPath(function ($url) use ($visualImportPath) {
// Resolve potential back paths manually using PathUtility::getCanonicalPath,
// but make sure we do not break out of TYPO3 application path using GeneralUtility::getFileAbsFileName
// Also resolve EXT: paths if given
$isTypo3Absolute = strpos($url, 'EXT:') === 0;
$fileName = $isTypo3Absolute ? $url : $visualImportPath . '/' . $url;
$full = GeneralUtility::getFileAbsFileName(PathUtility::getCanonicalPath($fileName));
// The API forces us to check the existence of files paths, with or without url.
// We must only return a string if the file to be imported actually exists.
$hasExtension = preg_match('/[.]s?css$/', $url);
if (
is_file($file = $full . '.scss') ||
($hasExtension && is_file($file = $full))
) {
// We could trigger a deprecation message here at some point
return $file;
}

$absoluteFilename = GeneralUtility::getFileAbsFileName($file);
$relativePath = $settings['cache']['tempDirectoryRelativeToRoot'] . dirname(substr($absoluteFilename, strlen($this->getPathSite()))) . '/';
return null;
});
// Add extensions path to import paths, so that we can use paths relative to this directory to resolve imports
$scss->addImportPath(Environment::getExtensionsPath());
$css = $scss->compile('@import "' . $absoluteFilename . '"');

// Fix url() statements
$relativePath = $settings['cache']['tempDirectoryRelativeToRoot'] . dirname($settings['file']['relative']) . '/';
$search = '%url\s*\(\s*[\\\'"]?(?!(((?:https?:)?\/\/)|(?:data:?:)))([^\\\'")]+)[\\\'"]?\s*\)%';
$replace = 'url("' . $relativePath . '$3")';
$css = preg_replace($search, $replace, $css);
Expand Down
6 changes: 3 additions & 3 deletions Classes/Service/CompileService.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public function getCompiledFile($file)
$configuration = ($GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_bootstrappackage.']['settings.'] ?: []);

// Ensure cache directory exists
if (!file_exists($this->tempDirectory)) {
GeneralUtility::mkdir_deep($this->tempDirectory);
if (!file_exists(Environment::getPublicPath() . '/' . $this->tempDirectory)) {
GeneralUtility::mkdir_deep(Environment::getPublicPath() . '/' . $this->tempDirectory);
}

// Settings
Expand All @@ -51,7 +51,7 @@ public function getCompiledFile($file)
'info' => pathinfo($absoluteFile)
],
'cache' => [
'tempDirectory' => $this->tempDirectory,
'tempDirectory' => Environment::getPublicPath() . '/' . $this->tempDirectory,
'tempDirectoryRelativeToRoot' => $this->tempDirectoryRelativeToRoot,
],
'options' => [
Expand Down
13 changes: 9 additions & 4 deletions Tests/Functional/Parser/ScssParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,15 @@ public function scssParserCanCompileTestDataProvider()
'direct include' => [
'inputFile' => 'typo3conf/ext/bootstrap_package/Resources/Public/Scss/Theme/theme.scss'
],
// @TODO FIX: Currently Failing
// 'relative include from symlinked package' => [
// 'inputFile' => 'typo3conf/ext/demo_package/Resources/Public/Scss/TestRelative/theme.scss'
// ]
'relative include from symlinked package' => [
'inputFile' => 'typo3conf/ext/demo_package/Resources/Private/Scss/Relative/theme.scss'
],
'core syntax' => [
'inputFile' => 'typo3conf/ext/demo_package/Resources/Private/Scss/CoreSyntax/theme.scss'
],
'legacy include' => [
'inputFile' => 'typo3conf/ext/demo_package/Resources/Private/Scss/Legacy/theme.scss'
],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "EXT:bootstrap_package/Resources/Public/Scss/Theme/theme";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "../../../../../../../.build/web/typo3conf/ext/bootstrap_package/Resources/Public/Scss/Theme/theme";
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@
],
"test": [
"@test:php:lint",
"@test:php:unit"
"@test:php:unit",
"@test:php:functional"
],
"cgl": [
"php-cs-fixer --diff -v fix"
Expand Down

0 comments on commit 1e4d71e

Please sign in to comment.