-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Livewire set and first rule for QueryString to Url attributes (#222)
* Working * Rector and Lint Fixes * Configure package sets * Grammar fix * Docs generated * Fixes some typos * Handles keys * linting * Upgrade Rector * Working * Clean up * Adds an additional rule to the Livewire30 config --------- Co-authored-by: Geni Jaho <[email protected]>
- Loading branch information
Showing
17 changed files
with
540 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
use Rector\Config\RectorConfig; | ||
use RectorLaravel\Set\Packages\Livewire\LivewireSetList; | ||
|
||
return static function (RectorConfig $rectorConfig): void { | ||
$rectorConfig->sets([LivewireSetList::LIVEWIRE_30]); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
use Rector\Config\RectorConfig; | ||
use Rector\Renaming\Rector\Class_\RenameAttributeRector; | ||
use Rector\Renaming\ValueObject\RenameAttribute; | ||
use RectorLaravel\Rector\Class_\LivewireComponentQueryStringToUrlAttributeRector; | ||
|
||
return static function (RectorConfig $rectorConfig): void { | ||
$rectorConfig->import(__DIR__ . '/../../../config.php'); | ||
|
||
$rectorConfig->rule(LivewireComponentQueryStringToUrlAttributeRector::class); | ||
|
||
$rectorConfig->ruleWithConfiguration(RenameAttributeRector::class, [ | ||
new RenameAttribute('Livewire\Attributes\Rule', 'Livewire\Attributes\Validate'), | ||
]); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
230 changes: 230 additions & 0 deletions
230
src/Rector/Class_/LivewireComponentQueryStringToUrlAttributeRector.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
<?php | ||
|
||
namespace RectorLaravel\Rector\Class_; | ||
|
||
use PhpParser\Node; | ||
use PhpParser\Node\Arg; | ||
use PhpParser\Node\Attribute; | ||
use PhpParser\Node\AttributeGroup; | ||
use PhpParser\Node\Expr\Array_; | ||
use PhpParser\Node\Expr\ArrayItem; | ||
use PhpParser\Node\Identifier; | ||
use PhpParser\Node\Name\FullyQualified; | ||
use PhpParser\Node\Scalar; | ||
use PhpParser\Node\Scalar\String_; | ||
use PhpParser\Node\Stmt\Class_; | ||
use PhpParser\Node\Stmt\Property; | ||
use PHPStan\Type\ObjectType; | ||
use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; | ||
use Rector\Rector\AbstractRector; | ||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; | ||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; | ||
|
||
/** | ||
* @see RectorLaravel\Tests\Rector\Class_\LivewireComponentQueryStringToUrlAttributeRector\LivewireComponentQueryStringToUrlAttributeRectorTest | ||
*/ | ||
final class LivewireComponentQueryStringToUrlAttributeRector extends AbstractRector | ||
{ | ||
private const URL_ATTRIBUTE = 'Livewire\Attributes\Url'; | ||
|
||
private const COMPONENT_CLASS = 'Livewire\Component'; | ||
|
||
private const QUERY_STRING_PROPERTY_NAME = 'queryString'; | ||
|
||
public function __construct(private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer) | ||
{ | ||
|
||
} | ||
|
||
public function getRuleDefinition(): RuleDefinition | ||
{ | ||
return new RuleDefinition( | ||
'Converts the $queryString property of a Livewire component to use the Url Attribute', | ||
[ | ||
new CodeSample(<<<'CODE_SAMPLE' | ||
use Livewire\Component; | ||
class MyComponent extends Component | ||
{ | ||
public string $something = ''; | ||
public string $another = ''; | ||
protected $queryString = [ | ||
'something', | ||
'another', | ||
]; | ||
} | ||
CODE_SAMPLE, | ||
<<<'CODE_SAMPLE' | ||
use Livewire\Component; | ||
class MyComponent extends Component | ||
{ | ||
#[\Livewire\Attributes\Url] | ||
public string $something = ''; | ||
#[\Livewire\Attributes\Url] | ||
public string $another = ''; | ||
} | ||
CODE_SAMPLE | ||
), | ||
] | ||
); | ||
} | ||
|
||
public function getNodeTypes(): array | ||
{ | ||
return [Class_::class]; | ||
} | ||
|
||
/** | ||
* @param Class_ $node | ||
*/ | ||
public function refactor(Node $node): ?Class_ | ||
{ | ||
if (! $this->isObjectType($node, new ObjectType(self::COMPONENT_CLASS))) { | ||
return null; | ||
} | ||
|
||
$queryStringProperty = null; | ||
|
||
foreach ($node->stmts as $stmt) { | ||
if ($stmt instanceof Property && $this->isName($stmt, self::QUERY_STRING_PROPERTY_NAME)) { | ||
$queryStringProperty = $stmt; | ||
} | ||
} | ||
|
||
if (! $queryStringProperty instanceof Property) { | ||
return null; | ||
} | ||
|
||
// find the properties and add the attribute | ||
$urlPropertyNames = $this->findQueryStringProperties($queryStringProperty); | ||
|
||
if ($urlPropertyNames === []) { | ||
return null; | ||
} | ||
|
||
$propertyNodes = []; | ||
|
||
foreach ($node->stmts as $stmt) { | ||
if ($stmt instanceof Property && $this->isNames($stmt, array_keys((array) $urlPropertyNames))) { | ||
$propertyNodes[] = $stmt; | ||
} | ||
} | ||
|
||
foreach ($propertyNodes as $propertyNode) { | ||
$args = $urlPropertyNames[$this->getName($propertyNode)] ?? []; | ||
$this->addUrlAttributeToProperty($propertyNode, $args); | ||
} | ||
|
||
// remove the query string property if now empty | ||
$this->attemptQueryStringRemoval($node, $queryStringProperty); | ||
|
||
return $node; | ||
} | ||
|
||
/** | ||
* @return array<string, Node\Arg[]>|null | ||
*/ | ||
private function findQueryStringProperties(Property $property): ?array | ||
{ | ||
if ($property->props === []) { | ||
return null; | ||
} | ||
|
||
$array = $property->props[0]->default; | ||
|
||
if (! $array instanceof Array_ || $array->items === []) { | ||
return null; | ||
} | ||
|
||
$properties = []; | ||
$toFilter = []; | ||
|
||
foreach ($array->items as $item) { | ||
if ($item === null) { | ||
continue; | ||
} | ||
|
||
if ($item->key instanceof String_ && $item->value instanceof Array_) { | ||
$args = $this->processArrayOptionsIntoArgs($item->value); | ||
|
||
if ($args === null) { | ||
continue; | ||
} | ||
|
||
$properties[$item->key->value] = $args; | ||
$toFilter[] = $item; | ||
|
||
continue; | ||
} | ||
|
||
if ($item->value instanceof String_) { | ||
$properties[$item->value->value] = []; | ||
$toFilter[] = $item; | ||
} | ||
} | ||
|
||
if ($properties === []) { | ||
return null; | ||
} | ||
|
||
// we remove the array properties which will be converted | ||
$array->items = array_filter( | ||
$array->items, | ||
fn (?ArrayItem $arrayItem): bool => ! in_array($arrayItem, $toFilter, true), | ||
); | ||
|
||
return $properties; | ||
} | ||
|
||
/** | ||
* @param Node\Arg[] $args | ||
*/ | ||
private function addUrlAttributeToProperty(Property $property, array $args): void | ||
{ | ||
if ($this->phpAttributeAnalyzer->hasPhpAttribute($property, self::URL_ATTRIBUTE)) { | ||
return; | ||
} | ||
|
||
$property->attrGroups[] = new AttributeGroup([ | ||
new Attribute( | ||
new FullyQualified(self::URL_ATTRIBUTE), args: $args | ||
), | ||
]); | ||
} | ||
|
||
/** | ||
* @return Node\Arg[]|null | ||
*/ | ||
private function processArrayOptionsIntoArgs(Array_ $array): ?array | ||
{ | ||
$args = []; | ||
|
||
foreach ($array->items as $item) { | ||
if ($item === null) { | ||
continue; | ||
} | ||
if ($item->key instanceof String_ && $item->value instanceof Scalar && in_array($item->key->value, ['except', 'as'], true)) { | ||
$args[] = new Arg($item->value, name: new Identifier($item->key->value)); | ||
} | ||
} | ||
|
||
if (count($args) !== count($array->items)) { | ||
return null; | ||
} | ||
|
||
return $args; | ||
} | ||
|
||
private function attemptQueryStringRemoval(Class_ $class, Property $property): void | ||
{ | ||
$array = $property->props[0]->default; | ||
|
||
if ($array instanceof Array_ && $array->items === []) { | ||
$class->stmts = array_filter($class->stmts, fn (Node $node) => $node !== $property); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace RectorLaravel\Set\Packages\Livewire; | ||
|
||
use Rector\Set\Contract\SetListInterface; | ||
|
||
final class LivewireLevelSetList implements SetListInterface | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
final public const UP_TO_LIVEWIRE = __DIR__ . '/../../../../config/sets/packages/livewire/level/up-to-livewire-30.php'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
namespace RectorLaravel\Set\Packages\Livewire; | ||
|
||
final class LivewireSetList | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
final public const LIVEWIRE_30 = __DIR__ . '/../../../../config/sets/packages/livewire/livewire-30.php'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
|
||
namespace Livewire; | ||
|
||
if (class_exists('Livewire\Component')) { | ||
return; | ||
} | ||
|
||
class Component | ||
{ | ||
} |
37 changes: 37 additions & 0 deletions
37
...eComponentQueryStringToUrlAttributeRector/Fixture/does_not_double_apply_attribute.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
|
||
namespace RectorLaravel\Tests\Rector\Class_\LivewireComponentQueryStringToUrlAttributeRector\Fixture; | ||
|
||
use Livewire\Component; | ||
|
||
class DoesNotDoubleApplyAttributes extends Component | ||
{ | ||
#[\Livewire\Attributes\Url] | ||
public string $something = ''; | ||
|
||
public string $another = ''; | ||
|
||
protected $queryString = [ | ||
'something', | ||
'another', | ||
]; | ||
} | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace RectorLaravel\Tests\Rector\Class_\LivewireComponentQueryStringToUrlAttributeRector\Fixture; | ||
|
||
use Livewire\Component; | ||
|
||
class DoesNotDoubleApplyAttributes extends Component | ||
{ | ||
#[\Livewire\Attributes\Url] | ||
public string $something = ''; | ||
|
||
#[\Livewire\Attributes\Url] | ||
public string $another = ''; | ||
} | ||
|
||
?> |
Oops, something went wrong.