first commit
This commit is contained in:
commit
e81864e683
2984 files changed
+267797
No files matched your search
@@ -0,0 +1,133 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the package bk2k/bootstrap-package.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace BK2K\BootstrapPackage\Parser;
|
||||
|
||||
use TYPO3\CMS\Core\Cache\CacheManager;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* AbstractParser
|
||||
*/
|
||||
abstract class AbstractParser implements ParserInterface
|
||||
{
|
||||
/**
|
||||
* @param string $extension
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(string $extension): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array $settings
|
||||
* @return string
|
||||
*/
|
||||
public function compile(string $file, array $settings): string
|
||||
{
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array $settings
|
||||
* @return bool
|
||||
*/
|
||||
protected function isCached(string $file, array $settings): bool
|
||||
{
|
||||
$cacheIdentifier = $this->getCacheIdentifier($file, $settings);
|
||||
$cacheFile = $this->getCacheFile($cacheIdentifier, $settings['cache']['tempDirectory']);
|
||||
$cacheFileMeta = $this->getCacheFileMeta($cacheFile);
|
||||
|
||||
return file_exists($cacheFile) && file_exists($cacheFileMeta);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cacheFile
|
||||
* @param string $cacheFileMeta
|
||||
* @param array $settings
|
||||
* @return bool
|
||||
*/
|
||||
protected function needsCompile(string $cacheFile, string $cacheFileMeta, array $settings): bool
|
||||
{
|
||||
$needCompilation = false;
|
||||
$fileModificationTime = filemtime($cacheFile);
|
||||
$metadata = unserialize((string) file_get_contents($cacheFileMeta), ['allowed_classes' => false]);
|
||||
|
||||
foreach ($metadata['files'] as $file) {
|
||||
if (filemtime($file) > $fileModificationTime) {
|
||||
$needCompilation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$needCompilation && $settings['variables'] !== $metadata['variables']) {
|
||||
$needCompilation = true;
|
||||
}
|
||||
|
||||
if (!$needCompilation && $settings['options']['sourceMap'] !== $metadata['sourceMap']) {
|
||||
$needCompilation = true;
|
||||
}
|
||||
|
||||
return $needCompilation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cacheIdentifier
|
||||
* @param string $tempDirectory
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheFile(string $cacheIdentifier, string $tempDirectory): string
|
||||
{
|
||||
return $tempDirectory . $cacheIdentifier . '.css';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheFileMeta(string $filename)
|
||||
{
|
||||
return $filename . '.meta';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array $settings
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheIdentifier(string $file, array $settings): string
|
||||
{
|
||||
$filehash = md5($file);
|
||||
$hash = hash('sha256', $filehash . serialize($settings));
|
||||
$extension = pathinfo($file, PATHINFO_EXTENSION);
|
||||
|
||||
return basename($file, '.' . $extension) . '-' . $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getPathSite(): string
|
||||
{
|
||||
return Environment::getPublicPath() . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all page caches
|
||||
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheGroupException
|
||||
*/
|
||||
protected function clearPageCaches(): void
|
||||
{
|
||||
GeneralUtility::makeInstance(CacheManager::class)->flushCachesInGroup('pages');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the package bk2k/bootstrap-package.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace BK2K\BootstrapPackage\Parser;
|
||||
|
||||
/**
|
||||
* ParserInterface
|
||||
*/
|
||||
interface ParserInterface
|
||||
{
|
||||
/**
|
||||
* @param string $extension
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(string $extension): bool;
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array $settings
|
||||
* @return string
|
||||
*/
|
||||
public function compile(string $file, array $settings): string;
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the package bk2k/bootstrap-package.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace BK2K\BootstrapPackage\Parser;
|
||||
|
||||
use ScssPhp\ScssPhp\Compiler;
|
||||
use ScssPhp\ScssPhp\OutputStyle;
|
||||
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
|
||||
*/
|
||||
class ScssParser extends AbstractParser
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!class_exists('ScssPhp\ScssPhp\Version')) {
|
||||
require_once ExtensionManagementUtility::extPath('bootstrap_package') . '/Contrib/scssphp/scss.inc.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extension
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(string $extension): bool
|
||||
{
|
||||
return $extension === 'scss';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array $settings
|
||||
* @return string
|
||||
*/
|
||||
public function compile(string $file, array $settings): string
|
||||
{
|
||||
$cacheIdentifier = $this->getCacheIdentifier($file, $settings);
|
||||
$cacheFile = $this->getCacheFile($cacheIdentifier, $settings['cache']['tempDirectory']);
|
||||
$cacheFileMeta = $this->getCacheFileMeta($cacheFile);
|
||||
$compile = false;
|
||||
|
||||
if (!$this->isCached($file, $settings)
|
||||
|| $this->needsCompile($cacheFile, $cacheFileMeta, $settings)) {
|
||||
$compile = true;
|
||||
}
|
||||
|
||||
if ($compile) {
|
||||
$result = $this->parseFile($file, $settings);
|
||||
GeneralUtility::writeFile(GeneralUtility::getFileAbsFileName($cacheFile), $result['css']);
|
||||
GeneralUtility::writeFile(GeneralUtility::getFileAbsFileName($cacheFileMeta), serialize($result['cache']));
|
||||
$this->clearPageCaches();
|
||||
}
|
||||
|
||||
return $cacheFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param array $settings
|
||||
* @return array
|
||||
*/
|
||||
protected function parseFile(string $file, array $settings): array
|
||||
{
|
||||
$scss = new Compiler();
|
||||
$scss->setOutputStyle(OutputStyle::COMPRESSED);
|
||||
$scss->addVariables($settings['variables']);
|
||||
if ($settings['options']['sourceMap']) {
|
||||
$scss->setSourceMap(Compiler::SOURCE_MAP_INLINE);
|
||||
$scss->setSourceMapOptions([
|
||||
'sourceMapRootpath' => $settings['cache']['tempDirectoryRelativeToRoot'],
|
||||
'sourceMapBasepath' => '<PATH DOES NOT EXIST BUT SUPRESSES WARNINGS>'
|
||||
]);
|
||||
}
|
||||
$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): ?string {
|
||||
// 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) || PathUtility::isAbsolutePath($url);
|
||||
$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 = (bool) 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;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
// Make paths in url() statements relative to site root
|
||||
$absoluteFilePath = dirname($absoluteFilename);
|
||||
$relativeFilePath = PathUtility::getAbsoluteWebPath($absoluteFilePath);
|
||||
$absoluteBootstrapPackageThemePath =
|
||||
ExtensionManagementUtility::extPath('bootstrap_package') . 'Resources/Public/Scss/Theme';
|
||||
$relativeBootstrapPackageThemePath = PathUtility::getAbsoluteWebPath($absoluteBootstrapPackageThemePath);
|
||||
$scss->registerFunction(
|
||||
'url',
|
||||
function ($args) use (
|
||||
$scss,
|
||||
$absoluteFilePath,
|
||||
$relativeFilePath,
|
||||
$absoluteBootstrapPackageThemePath,
|
||||
$relativeBootstrapPackageThemePath
|
||||
) : string {
|
||||
$marker = $args[0][1];
|
||||
$args[0][1] = '';
|
||||
$result = $scss->compileValue($args[0]);
|
||||
if (substr_compare($result, 'data:', 0, 5, true) !== 0) {
|
||||
if (is_file(PathUtility::getCanonicalPath($absoluteFilePath . '/' . $result))) {
|
||||
$result = PathUtility::getCanonicalPath($relativeFilePath . '/' . $result);
|
||||
} elseif (is_file(PathUtility::getCanonicalPath($absoluteBootstrapPackageThemePath . '/' . $result))) {
|
||||
$result = PathUtility::getCanonicalPath($relativeBootstrapPackageThemePath . '/' . $result);
|
||||
}
|
||||
$result = substr($result, 0, 1) === '/' ? substr($result, 1) : $result;
|
||||
}
|
||||
return 'url(' . $marker . $result . $marker . ')';
|
||||
}
|
||||
);
|
||||
|
||||
// Compile file
|
||||
$compilationResult = $scss->compileString('@import "' . $absoluteFilename . '"');
|
||||
$css = $compilationResult->getCss();
|
||||
|
||||
// Fix paths in url() statements to be relative to temp directory
|
||||
$relativeTempPath = $settings['cache']['tempDirectoryRelativeToRoot'];
|
||||
$search = '%url\s*\(\s*[\\\'"]?(?!(((?:https?:)?\/\/)|(?:data:?:)))([^\\\'")]+)[\\\'"]?\s*\)%';
|
||||
$replace = 'url("' . $relativeTempPath . '$3")';
|
||||
$css = (string) preg_replace($search, $replace, $css);
|
||||
|
||||
return [
|
||||
'css' => $css,
|
||||
'cache' => [
|
||||
'version' => Version::VERSION,
|
||||
'date' => date('r'),
|
||||
'css' => $css,
|
||||
'etag' => md5($css),
|
||||
'files' => $compilationResult->getIncludedFiles(),
|
||||
'variables' => $settings['variables'],
|
||||
'sourceMap' => $settings['options']['sourceMap']
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user