Skip to content

Commit

Permalink
fix: Module Filter
Browse files Browse the repository at this point in the history
  • Loading branch information
marcreichel committed Feb 6, 2025
1 parent f42a9af commit 4602df2
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 5 deletions.
28 changes: 28 additions & 0 deletions src/Module/ModuleFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Artemeon\Composer\Module;

final readonly class ModuleFilter implements ModuleFilterInterface
{
private function __construct(
private ?array $activeModules,
) {
}

public static function unrestricted(): self
{
return new self(null);
}

public static function restrictedTo(array $activeModules): self
{
return new self($activeModules);
}

public function shouldLoad(string $moduleName): bool
{
return !isset($this->activeModules) || in_array($moduleName, $this->activeModules, true);
}
}
10 changes: 10 additions & 0 deletions src/Module/ModuleFilterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Artemeon\Composer\Module;

interface ModuleFilterInterface
{
public function shouldLoad(string $moduleName): bool;
}
53 changes: 53 additions & 0 deletions src/Module/ModuleFilterLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace Artemeon\Composer\Module;

use Composer\IO\IOInterface;
use JsonException;

final readonly class ModuleFilterLoader
{
public function __construct(private IOInterface $io)
{
}

public function load(string $configurationFilePath, string $localConfigurationFilePath): ModuleFilterInterface
{
$this->io->debug(
sprintf('Loading module filter configuration at <comment>%s</comment>', $configurationFilePath),
);

$configurationData = $this->readJsonFile($configurationFilePath);
if (!isset($configurationData)) {
return ModuleFilter::unrestricted();
}

$localConfigurationData = $this->readJsonFile($localConfigurationFilePath);

$mergedConfiguration = array_values(array_unique([...$configurationData->core, ...($localConfigurationData ?? [])]));

return ModuleFilter::restrictedTo($mergedConfiguration);
}

private function readJsonFile(string $filePath): ?object
{
$fileContents = @file_get_contents($filePath);
if ($fileContents === false) {
$this->io->warning('No module filter configuration found');

return null;
}

try {
$jsonData = json_decode($fileContents, false, 512, JSON_OBJECT_AS_ARRAY | JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
$this->io->warning(sprintf('Invalid module filter configuration: %s', $exception->getMessage()));

return null;
}

return $jsonData;
}
}
13 changes: 13 additions & 0 deletions src/Module/ModuleIncludeAllFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Artemeon\Composer\Module;

final class ModuleIncludeAllFilter implements ModuleFilterInterface
{
public function shouldLoad(string $moduleName): bool
{
return true;
}
}
12 changes: 9 additions & 3 deletions src/Module/ModulePackageLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,21 @@
final class ModulePackageLoader
{
private const MODULE_COMPOSER_FILE_PATTERN = 'module_*/composer.json';
private ModuleFilterInterface $moduleFilter;
private IOInterface $io;

private array $modulePackageCache = [];

public function __construct(IOInterface $io)
public function __construct(ModuleFilterInterface $moduleFilter, IOInterface $io)
{
$this->moduleFilter = $moduleFilter;
$this->io = $io;
}

/**
* @return ModulePackage[]
*/
public function load(string $basePath): iterable
public function load(string $basePath, bool $forceAll = false): iterable
{
$this->io->debug(
sprintf(
Expand All @@ -40,7 +42,11 @@ public function load(string $basePath): iterable
);

foreach ($this->scanForComposerFiles($basePath) as $composerFile) {
yield $this->loadModule($composerFile);
$moduleName = basename(dirname($composerFile));

if ($forceAll || $this->moduleFilter->shouldLoad($moduleName)) {
yield $this->loadModule($composerFile);
}
}
}

Expand Down
26 changes: 24 additions & 2 deletions src/Plugin/MergePlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Artemeon\Composer\Plugin;

use Artemeon\Composer\Module\ModuleFilterLoader;
use Artemeon\Composer\Module\ModuleIncludeAllFilter;
use Artemeon\Composer\Module\ModulePackageLoader;
use Composer\Composer;
use Composer\DependencyResolver\Operation\InstallOperation;
Expand All @@ -27,6 +29,8 @@ final class MergePlugin implements PluginInterface, EventSubscriberInterface
private const CALLBACK_PRIORITY = 50000;
private const MODULES_BASE_PATH = 'core';
private const OVERRIDDEN_MODULES = './module_*';
private const FILTER_CONFIGURATION_PATH = './packageconfig.json';
private const PROJECT = './.projectrc';

private Composer $composer;
private IOInterface $io;
Expand All @@ -39,7 +43,25 @@ public function activate(Composer $composer, IOInterface $io): void
$this->composer = $composer;
$this->io = $io;

$this->modulePackageLoader = new ModulePackageLoader($io);
$packageConfig = $composer->getPackage()->getExtra()['packageconfig'] ?? true;
if ($packageConfig) {
$project = 'default';
if (is_file(self::PROJECT)) {
$project = trim(file_get_contents(self::PROJECT) ?? $project);
}
if (count($apps = glob('apps/*', GLOB_ONLYDIR)) === 1) {
$project = basename($apps[0]);
}

$filterFilePath = sprintf('./apps/%s/%s', $project, self::FILTER_CONFIGURATION_PATH);
$localFilePath = self::FILTER_CONFIGURATION_PATH;

$moduleFilter = (new ModuleFilterLoader($io))->load($filterFilePath, $localFilePath);
} else {
$moduleFilter = new ModuleIncludeAllFilter();
}

$this->modulePackageLoader = new ModulePackageLoader($moduleFilter, $io);
}

public function deactivate(Composer $composer, IOInterface $io): void
Expand Down Expand Up @@ -117,7 +139,7 @@ private function mergeAutoloadOverrides(RootPackageInterface $rootPackage): void

private function mergeRequires(RootPackageInterface $rootPackage): void
{
foreach ($this->modulePackageLoader->load($this->getBasePath($rootPackage)) as $modulePackage) {
foreach ($this->modulePackageLoader->load($this->getBasePath($rootPackage), true) as $modulePackage) {
$modulePackage->mergeRequires($rootPackage);
}
}
Expand Down

0 comments on commit 4602df2

Please sign in to comment.