Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.x] Theme Extender to Allow overriding LESS files #3008

Merged
merged 10 commits into from
Sep 10, 2021
4 changes: 2 additions & 2 deletions src/Extend/Frontend.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ private function registerAssets(Container $container, string $moduleName): void
}

if ($this->css) {
$assets->css(function (SourceCollector $sources) {
$assets->css(function (SourceCollector $sources) use ($moduleName) {
foreach ($this->css as $path) {
$sources->addFile($path);
$sources->addFile($path, $moduleName);
}
});
}
Expand Down
69 changes: 69 additions & 0 deletions src/Extend/Theme.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/

namespace Flarum\Extend;

use Flarum\Extension\Extension;
use Flarum\Frontend\Assets;
use Illuminate\Contracts\Container\Container;

class Theme implements ExtenderInterface
{
private $lessImportOverrides = [];
private $fileSourceOverrides = [];

/**
* This can be used to override LESS files that are imported within the code.
* For example, core's `forum.less` file imports a `forum/DiscussionListItem.less` file.
* The contents of this file can be overriden with this method.
*
* @param string $file : Relative path of the file to override, for example: `forum/Hero.less`
* @param string $newFilePath : Absolute path of the new file.
* @param string|null $extensionId : If overriding an extension file, specify its ID, for example: `flarum-tags`.
* @return self
*/
public function overrideLessImport(string $file, string $newFilePath, string $extensionId = null): self
{
$this->lessImportOverrides[] = compact('file', 'newFilePath', 'extensionId');

return $this;
}

/**
* This method allows overriding LESS file sources.
* For example `forum.less`, `admin.less`, `mixins.less` and `variables.less` are file sources,
* and can therefore be overriden using this method.
*
* @param string $file : Name of the file to override, for example: `admin.less`
* @param string $newFilePath : Absolute path of the new file.
* @param string|null $extensionId : If overriding an extension file, specify its ID, for example: `flarum-tags`.
* @return self
*/
public function overrideFileSource(string $file, string $newFilePath, string $extensionId = null): self
{
$this->fileSourceOverrides[] = compact('file', 'newFilePath', 'extensionId');

return $this;
}

public function extend(Container $container, Extension $extension = null)
{
$container->extend('flarum.assets.factory', function (callable $factory) {
return function (...$args) use ($factory) {
/** @var Assets $assets */
$assets = $factory(...$args);

$assets->addLessImportOverrides($this->lessImportOverrides);
$assets->addFileSourceOverrides($this->fileSourceOverrides);

return $assets;
};
});
}
}
28 changes: 28 additions & 0 deletions src/Frontend/Assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ class Assets
*/
protected $lessImportDirs;

/**
* @var array
*/
protected $lessImportOverrides = [];

/**
* @var array
*/
protected $fileSourceOverrides = [];

public function __construct(string $name, Filesystem $assetsDir, string $cacheDir = null, array $lessImportDirs = null)
{
$this->name = $name;
Expand Down Expand Up @@ -155,6 +165,14 @@ protected function makeLessCompiler(string $filename): LessCompiler
$compiler->setImportDirs($this->lessImportDirs);
}

if ($this->lessImportOverrides) {
$compiler->setLessImportOverrides($this->lessImportOverrides);
}

if ($this->fileSourceOverrides) {
$compiler->setFileSourceOverrides($this->fileSourceOverrides);
}

return $compiler;
}

Expand Down Expand Up @@ -197,4 +215,14 @@ public function setLessImportDirs(array $lessImportDirs)
{
$this->lessImportDirs = $lessImportDirs;
}

public function addLessImportOverrides(array $lessImportOverrides)
{
$this->lessImportOverrides = array_merge($this->lessImportOverrides, $lessImportOverrides);
}

public function addFileSourceOverrides(array $fileSourceOverrides)
{
$this->fileSourceOverrides = array_merge($this->fileSourceOverrides, $fileSourceOverrides);
}
}
77 changes: 76 additions & 1 deletion src/Frontend/Compiler/LessCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
namespace Flarum\Frontend\Compiler;

use Flarum\Frontend\Compiler\Source\FileSource;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Less_Parser;

/**
Expand All @@ -27,6 +29,16 @@ class LessCompiler extends RevisionCompiler
*/
protected $importDirs = [];

/**
* @var Collection
*/
protected $lessImportOverrides;

/**
* @var Collection
*/
protected $fileSourceOverrides;

public function getCacheDir(): string
{
return $this->cacheDir;
Expand All @@ -47,6 +59,16 @@ public function setImportDirs(array $importDirs)
$this->importDirs = $importDirs;
}

public function setLessImportOverrides(array $lessImportOverrides)
{
$this->lessImportOverrides = new Collection($lessImportOverrides);
}

public function setFileSourceOverrides(array $fileSourceOverrides)
{
$this->fileSourceOverrides = new Collection($fileSourceOverrides);
}

/**
* @throws \Less_Exception_Parser
*/
Expand All @@ -61,9 +83,14 @@ protected function compile(array $sources): string
$parser = new Less_Parser([
'compress' => true,
'cache_dir' => $this->cacheDir,
'import_dirs' => $this->importDirs
'import_dirs' => $this->importDirs,
'import_callback' => $this->lessImportOverrides ? $this->overrideImports($sources) : null,
]);

if ($this->fileSourceOverrides) {
$sources = $this->overrideSources($sources);
}

foreach ($sources as $source) {
if ($source instanceof FileSource) {
$parser->parseFile($source->getPath());
Expand All @@ -75,6 +102,54 @@ protected function compile(array $sources): string
return $parser->getCss();
}

protected function overrideSources(array $sources): array
{
foreach ($sources as $source) {
if ($source instanceof FileSource) {
$basename = basename($source->getPath());
$override = $this->fileSourceOverrides
->where('file', $basename)
->firstWhere('extensionId', $source->getExtensionId());

if ($override) {
$source->setPath($override['newFilePath']);
}
}
}

return $sources;
}

protected function overrideImports(array $sources): callable
{
$baseSources = (new Collection($sources))->filter(function ($source) {
return $source instanceof Source\FileSource;
})->map(function (FileSource $source) {
$path = realpath($source->getPath());
$path = Str::beforeLast($path, '/less/');

return [
'path' => $path,
'extensionId' => $source->getExtensionId(),
];
})->unique('path');

return function ($evald) use ($baseSources): ?array {
$relativeImportPath = Str::of($evald->PathAndUri()[0])->split('/\/less\//');
$extensionId = $baseSources->where('path', $relativeImportPath->first())->pluck('extensionId')->first();

$overrideImport = $this->lessImportOverrides
->where('file', $relativeImportPath->last())
->firstWhere('extensionId', $extensionId);

if (! $overrideImport) {
return null;
}

return [$overrideImport['newFilePath'], $evald->PathAndUri()[1]];
};
}

protected function getCacheDifferentiator(): ?array
{
return [
Expand Down
18 changes: 17 additions & 1 deletion src/Frontend/Compiler/Source/FileSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,22 @@ class FileSource implements SourceInterface
*/
protected $path;

/**
* @var string
*/
protected $extensionId;

/**
* @param string $path
*/
public function __construct(string $path)
public function __construct(string $path, ?string $extensionId = null)
{
if (! file_exists($path)) {
throw new InvalidArgumentException("File not found at path: $path");
}

$this->path = $path;
$this->extensionId = $extensionId;
}

/**
Expand All @@ -56,4 +62,14 @@ public function getPath(): string
{
return $this->path;
}

public function setPath(string $path): void
{
$this->path = $path;
}

public function getExtensionId(): ?string
{
return $this->extensionId;
}
}
4 changes: 2 additions & 2 deletions src/Frontend/Compiler/Source/SourceCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ class SourceCollector
* @param string $file
* @return $this
*/
public function addFile(string $file)
public function addFile(string $file, string $extensionId = null)
{
$this->sources[] = new FileSource($file);
$this->sources[] = new FileSource($file, $extensionId);

return $this;
}
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/less/Imported.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.Imported {
// ...
}
1 change: 1 addition & 0 deletions tests/fixtures/less/dummy.less
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.dummy_test_case{color:red}
5 changes: 5 additions & 0 deletions tests/fixtures/less/forum.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import 'Imported';

.dummy {
color: yellow;
}
3 changes: 3 additions & 0 deletions tests/fixtures/less/override_filesource.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
color: orange;
}
Loading