mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-26 20:53:31 +00:00
Merge pull request #3539 from rectorphp/single-rule
Re-add --only option
This commit is contained in:
commit
2ecd056bca
16
README.md
16
README.md
|
@ -211,6 +211,22 @@ class SomeClass
|
|||
}
|
||||
```
|
||||
|
||||
### Run Just 1 Rector Rule
|
||||
|
||||
Do you have config that includes many sets and Rectors? You might want to run only a single Rector. The `--only` argument allows that, e.g.:
|
||||
|
||||
```bash
|
||||
vendor/bin/rector process src --set solid --only Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector
|
||||
```
|
||||
|
||||
Or just short name:
|
||||
|
||||
```bash
|
||||
vendor/bin/rector process src --set solid --only FinalizeClassesWithoutChildrenRector
|
||||
```
|
||||
|
||||
Both will run only `Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector`.
|
||||
|
||||
### Provide PHP Version
|
||||
|
||||
By default Rector uses the language features matching your system version of PHP. You can configure it for a different PHP version:
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
imports:
|
||||
- { resource: "create-rector.yaml", ignore_errors: 'not_found' }
|
||||
|
||||
services:
|
||||
# @todo add compiler pass that makes every Rector public service
|
||||
# _defaults:
|
||||
# public: true
|
||||
|
||||
Rector\Php72\Rector\Each\ListEachRector: null
|
||||
|
||||
parameters:
|
||||
# bleeding edge feature
|
||||
# is_cache_enabled: true
|
||||
|
|
|
@ -11,6 +11,7 @@ use Rector\Core\Application\FileSystem\RemovedAndAddedFilesCollector;
|
|||
use Rector\Core\Application\FileSystem\RemovedAndAddedFilesProcessor;
|
||||
use Rector\Core\Configuration\Configuration;
|
||||
use Rector\Core\EventDispatcher\Event\AfterProcessEvent;
|
||||
use Rector\Core\Testing\Application\EnabledRectorsProvider;
|
||||
use Rector\FileSystemRector\FileSystemFileProcessor;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
@ -70,6 +71,11 @@ final class RectorApplication
|
|||
*/
|
||||
private $removedAndAddedFilesProcessor;
|
||||
|
||||
/**
|
||||
* @var EnabledRectorsProvider
|
||||
*/
|
||||
private $enabledRectorsProvider;
|
||||
|
||||
/**
|
||||
* @var NodeScopeResolver
|
||||
*/
|
||||
|
@ -86,6 +92,7 @@ final class RectorApplication
|
|||
ErrorAndDiffCollector $errorAndDiffCollector,
|
||||
Configuration $configuration,
|
||||
FileProcessor $fileProcessor,
|
||||
EnabledRectorsProvider $enabledRectorsProvider,
|
||||
RemovedAndAddedFilesCollector $removedAndAddedFilesCollector,
|
||||
RemovedAndAddedFilesProcessor $removedAndAddedFilesProcessor,
|
||||
NodeScopeResolver $nodeScopeResolver,
|
||||
|
@ -98,6 +105,7 @@ final class RectorApplication
|
|||
$this->fileProcessor = $fileProcessor;
|
||||
$this->removedAndAddedFilesCollector = $removedAndAddedFilesCollector;
|
||||
$this->removedAndAddedFilesProcessor = $removedAndAddedFilesProcessor;
|
||||
$this->enabledRectorsProvider = $enabledRectorsProvider;
|
||||
$this->nodeScopeResolver = $nodeScopeResolver;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
@ -129,6 +137,12 @@ final class RectorApplication
|
|||
}, 'parsing');
|
||||
}
|
||||
|
||||
// active only one rule
|
||||
if ($this->configuration->getOnlyRector() !== null) {
|
||||
$onlyRector = $this->configuration->getOnlyRector();
|
||||
$this->enabledRectorsProvider->addEnabledRector($onlyRector);
|
||||
}
|
||||
|
||||
// 2. change nodes with Rectors
|
||||
foreach ($fileInfos as $fileInfo) {
|
||||
$this->tryCatchWrapper($fileInfo, function (SmartFileInfo $smartFileInfo): void {
|
||||
|
|
|
@ -29,6 +29,11 @@ final class Configuration
|
|||
*/
|
||||
private $showProgressBar = true;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $onlyRector;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
|
@ -84,16 +89,27 @@ final class Configuration
|
|||
*/
|
||||
private $paths = [];
|
||||
|
||||
/**
|
||||
* @var OnlyRuleResolver
|
||||
*/
|
||||
private $onlyRuleResolver;
|
||||
|
||||
/**
|
||||
* @param string[] $fileExtensions
|
||||
* @param string[] $paths
|
||||
*/
|
||||
public function __construct(CiDetector $ciDetector, bool $isCacheEnabled, array $fileExtensions, array $paths)
|
||||
{
|
||||
public function __construct(
|
||||
CiDetector $ciDetector,
|
||||
bool $isCacheEnabled,
|
||||
array $fileExtensions,
|
||||
array $paths,
|
||||
OnlyRuleResolver $onlyRuleResolver
|
||||
) {
|
||||
$this->ciDetector = $ciDetector;
|
||||
$this->isCacheEnabled = $isCacheEnabled;
|
||||
$this->fileExtensions = $fileExtensions;
|
||||
$this->paths = $paths;
|
||||
$this->onlyRuleResolver = $onlyRuleResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,6 +128,12 @@ final class Configuration
|
|||
|
||||
$this->outputFormat = (string) $input->getOption(Option::OPTION_OUTPUT_FORMAT);
|
||||
|
||||
/** @var string|null $onlyRector */
|
||||
$onlyRector = $input->getOption(Option::OPTION_ONLY);
|
||||
if ($onlyRector !== null) {
|
||||
$this->setOnlyRector($onlyRector);
|
||||
}
|
||||
|
||||
$commandLinePaths = (array) $input->getArgument(Option::SOURCE);
|
||||
// manual command line value has priority
|
||||
if (count($commandLinePaths) > 0) {
|
||||
|
@ -184,6 +206,11 @@ final class Configuration
|
|||
return $this->mustMatchGitDiff;
|
||||
}
|
||||
|
||||
public function getOnlyRector(): ?string
|
||||
{
|
||||
return $this->onlyRector;
|
||||
}
|
||||
|
||||
public function getOutputFile(): ?string
|
||||
{
|
||||
return $this->outputFile;
|
||||
|
@ -253,4 +280,9 @@ final class Configuration
|
|||
}
|
||||
return $input->getOption(Option::OPTION_OUTPUT_FORMAT) !== CheckstyleOutputFormatter::NAME;
|
||||
}
|
||||
|
||||
private function setOnlyRector(string $rector): void
|
||||
{
|
||||
$this->onlyRector = $this->onlyRuleResolver->resolve($rector);
|
||||
}
|
||||
}
|
||||
|
|
60
src/Configuration/OnlyRuleResolver.php
Normal file
60
src/Configuration/OnlyRuleResolver.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\Configuration;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Core\Exception\Configuration\RectorRuleNotFoundException;
|
||||
|
||||
/**
|
||||
* @see \Rector\Core\Tests\Configuration\OnlyRuleResolverTest
|
||||
*/
|
||||
final class OnlyRuleResolver
|
||||
{
|
||||
/**
|
||||
* @var RectorClassesProvider
|
||||
*/
|
||||
private $rectorClassesProvider;
|
||||
|
||||
public function __construct(RectorClassesProvider $rectorClassesProvider)
|
||||
{
|
||||
$this->rectorClassesProvider = $rectorClassesProvider;
|
||||
}
|
||||
|
||||
public function resolve(string $rule): string
|
||||
{
|
||||
$rule = ltrim($rule, '\\');
|
||||
|
||||
$rectorClasses = $this->rectorClassesProvider->provide();
|
||||
|
||||
// 1. exact class
|
||||
foreach ($rectorClasses as $rectorClass) {
|
||||
if (is_a($rectorClass, $rule, true)) {
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. short class
|
||||
foreach ($rectorClasses as $rectorClass) {
|
||||
if (Strings::endsWith($rectorClass, '\\' . $rule)) {
|
||||
return $rectorClass;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. class without slashes
|
||||
foreach ($rectorClasses as $rectorClass) {
|
||||
$rectorClassWithoutSlashes = Strings::replace($rectorClass, '#\\\\#');
|
||||
if ($rectorClassWithoutSlashes === $rule) {
|
||||
return $rectorClass;
|
||||
}
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
'Rule "%s" was not found.%sMake sure it is registered in your config or in one of the sets',
|
||||
$rule,
|
||||
PHP_EOL
|
||||
);
|
||||
throw new RectorRuleNotFoundException($message);
|
||||
}
|
||||
}
|
|
@ -36,6 +36,11 @@ final class Option
|
|||
*/
|
||||
public const PHP_VERSION_FEATURES = 'php_version_features';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const OPTION_ONLY = 'only';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
52
src/Configuration/RectorClassesProvider.php
Normal file
52
src/Configuration/RectorClassesProvider.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\Configuration;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Rector\Core\Contract\Rector\RectorInterface;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
|
||||
final class RectorClassesProvider
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface&Container
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* This is only to prevent circular dependencies, where this service is being used.
|
||||
* We only need list of classes.
|
||||
*
|
||||
* @param ContainerInterface&Container $container
|
||||
*/
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string[]
|
||||
*/
|
||||
public function provide(): array
|
||||
{
|
||||
$rectorClasses = [];
|
||||
|
||||
foreach ($this->container->getServiceIds() as $class) {
|
||||
if (! class_exists($class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_a($class, RectorInterface::class, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rectorClasses[] = $class;
|
||||
}
|
||||
|
||||
sort($rectorClasses);
|
||||
|
||||
return $rectorClasses;
|
||||
}
|
||||
}
|
|
@ -168,6 +168,13 @@ final class ProcessCommand extends AbstractCommand
|
|||
'Execute only on file(s) matching the git diff.'
|
||||
);
|
||||
|
||||
$this->addOption(
|
||||
Option::OPTION_ONLY,
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Run only one single Rector from the loaded Rectors (in services, sets, etc).'
|
||||
);
|
||||
|
||||
$availableOutputFormatters = $this->outputFormatterCollector->getNames();
|
||||
$this->addOption(
|
||||
Option::OPTION_OUTPUT_FORMAT,
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\DependencyInjection\CompilerPass;
|
||||
|
||||
use Rector\Core\Contract\Rector\RectorInterface;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Needed for @see \Rector\Core\Configuration\RectorClassesProvider
|
||||
*/
|
||||
final class MakeRectorsPublicCompilerPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $containerBuilder): void
|
||||
{
|
||||
foreach ($containerBuilder->getDefinitions() as $definition) {
|
||||
if ($definition->getClass() === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_a($definition->getClass(), RectorInterface::class, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$definition->setPublic(true);
|
||||
}
|
||||
}
|
||||
}
|
11
src/Exception/Configuration/RectorRuleNotFoundException.php
Normal file
11
src/Exception/Configuration/RectorRuleNotFoundException.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\Exception\Configuration;
|
||||
|
||||
use Exception;
|
||||
|
||||
final class RectorRuleNotFoundException extends Exception
|
||||
{
|
||||
}
|
|
@ -6,6 +6,7 @@ namespace Rector\Core\HttpKernel;
|
|||
|
||||
use Rector\Core\Contract\Rector\RectorInterface;
|
||||
use Rector\Core\DependencyInjection\Collector\RectorServiceArgumentCollector;
|
||||
use Rector\Core\DependencyInjection\CompilerPass\MakeRectorsPublicCompilerPass;
|
||||
use Rector\Core\DependencyInjection\CompilerPass\MergeImportedRectorServiceArgumentsCompilerPass;
|
||||
use Rector\Core\DependencyInjection\CompilerPass\RemoveExcludedRectorsCompilerPass;
|
||||
use Rector\Core\DependencyInjection\Loader\TolerantRectorYamlFileLoader;
|
||||
|
@ -91,6 +92,7 @@ final class RectorKernel extends Kernel implements ExtraConfigAwareKernelInterfa
|
|||
$containerBuilder->addCompilerPass(new AutowireInterfacesCompilerPass([RectorInterface::class]));
|
||||
|
||||
$containerBuilder->addCompilerPass(new AutoBindParameterCompilerPass());
|
||||
$containerBuilder->addCompilerPass(new MakeRectorsPublicCompilerPass());
|
||||
|
||||
// add all merged arguments of Rector services
|
||||
$containerBuilder->addCompilerPass(
|
||||
|
|
47
tests/Configuration/OnlyRuleResolverTest.php
Normal file
47
tests/Configuration/OnlyRuleResolverTest.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\Tests\Configuration;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Core\Configuration\OnlyRuleResolver;
|
||||
use Rector\Core\HttpKernel\RectorKernel;
|
||||
use Rector\Core\Tests\Configuration\Source\CustomLocalRector;
|
||||
use Rector\Php72\Rector\Each\ListEachRector;
|
||||
use Symplify\PackageBuilder\Tests\AbstractKernelTestCase;
|
||||
|
||||
final class OnlyRuleResolverTest extends AbstractKernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var OnlyRuleResolver
|
||||
*/
|
||||
private $onlyRuleResolver;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->bootKernelWithConfigs(RectorKernel::class, [__DIR__ . '/Source/config/some_config.yaml']);
|
||||
$this->onlyRuleResolver = self::$container->get(OnlyRuleResolver::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(string $rule, string $expectedRectorClass): void
|
||||
{
|
||||
$resolveRectorClass = $this->onlyRuleResolver->resolve($rule);
|
||||
$this->assertSame($expectedRectorClass, $resolveRectorClass);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
yield [ListEachRector::class, ListEachRector::class];
|
||||
yield ['ListEachRector', ListEachRector::class];
|
||||
yield [CustomLocalRector::class, CustomLocalRector::class];
|
||||
yield ['CustomLocalRector', CustomLocalRector::class];
|
||||
|
||||
// miss-formats
|
||||
yield ['\\' . ListEachRector::class, ListEachRector::class];
|
||||
yield ['RectorCoreTestsConfigurationSourceCustomLocalRector', CustomLocalRector::class];
|
||||
}
|
||||
}
|
16
tests/Configuration/Source/CustomLocalRector.php
Normal file
16
tests/Configuration/Source/CustomLocalRector.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\Tests\Configuration\Source;
|
||||
|
||||
use Rector\Core\Contract\Rector\RectorInterface;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
|
||||
final class CustomLocalRector implements RectorInterface
|
||||
{
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
// TODO: Implement getDefinition() method.
|
||||
}
|
||||
}
|
3
tests/Configuration/Source/config/some_config.yaml
Normal file
3
tests/Configuration/Source/config/some_config.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
services:
|
||||
Rector\Php72\Rector\Each\ListEachRector: null
|
||||
Rector\Core\Tests\Configuration\Source\CustomLocalRector: null
|
Loading…
Reference in New Issue
Block a user