196 lines
6.5 KiB
PHP
196 lines
6.5 KiB
PHP
<?php
|
|
|
|
namespace KupShop\ComponentsBundle\Command;
|
|
|
|
use KupShop\ComponentsBundle\Utils\ComponentsLocator;
|
|
use KupShop\ComponentsBundle\View\ComponentsViewInterface;
|
|
use Symfony\Component\Console\Attribute\AsCommand;
|
|
use Symfony\Component\Console\Command\Command;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Input\InputOption;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
use Symfony\Component\DependencyInjection\Attribute\AutowireIterator;
|
|
use Symfony\Component\Yaml\Yaml;
|
|
use Symfony\UX\TwigComponent\Twig\ComponentNode;
|
|
use Twig\Environment;
|
|
use Twig\Node\Expression\FunctionExpression;
|
|
use Twig\Node\IncludeNode;
|
|
use Twig\Node\Node;
|
|
use Twig\NodeVisitor\NodeVisitorInterface;
|
|
|
|
#[AsCommand(name: 'kupshop:components:generate', description: 'Generate app/components.yaml file from current shop templates')]
|
|
class GenerateComponentsYamlCommand extends Command implements NodeVisitorInterface
|
|
{
|
|
protected array $templates = [];
|
|
protected array $components = [];
|
|
protected string $entrypoint;
|
|
|
|
public function __construct(
|
|
protected ComponentsLocator $componentsLocator,
|
|
protected Environment $twig,
|
|
#[AutowireIterator('kupshop.components.view')] protected \IteratorAggregate $container,
|
|
) {
|
|
parent::__construct();
|
|
}
|
|
|
|
protected function configure(): void
|
|
{
|
|
$this->addOption(
|
|
name: 'load',
|
|
shortcut: 'l',
|
|
mode: InputOption::VALUE_OPTIONAL,
|
|
description: 'Cesta k souboru se starým components.yaml',
|
|
default: 'config/components.yaml'
|
|
);
|
|
}
|
|
|
|
protected function execute(InputInterface $input, OutputInterface $output)
|
|
{
|
|
$this->twig->addNodeVisitor($this);
|
|
|
|
/** @var ComponentsViewInterface $view */
|
|
foreach ($this->container as $view) {
|
|
$entrypoint = $view->getEntrypoint();
|
|
|
|
if (!$entrypoint) {
|
|
throw new \Exception(sprintf('%s does not have "protected string $entrypoint = \'neco\'" specified', $view::class));
|
|
}
|
|
|
|
foreach ($view->getTemplates() as $template) {
|
|
// Kopie z TemplateTrait
|
|
if (!str_ends_with($template, '.html.twig')) {
|
|
$template = 'view/'.$template;
|
|
}
|
|
|
|
$template = preg_replace('/\.tpl$/', '.html.twig', $template);
|
|
|
|
if (!$this->twig->getLoader()->exists($template)) {
|
|
continue;
|
|
}
|
|
|
|
$this->discoverComponents($template, $entrypoint);
|
|
}
|
|
}
|
|
|
|
// Load all components with #[Blocek] with entrypoint base
|
|
foreach ($this->componentsLocator->getComponents() as $component) {
|
|
$reflectionClass = new \ReflectionClass($component['class']);
|
|
if ($reflectionClass->getAttributes('KupShop\ComponentsBundle\Attributes\Blocek', \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null) {
|
|
$this->entrypoint = 'base';
|
|
$this->addComponent($component['name']);
|
|
}
|
|
|
|
if ($attribute = $this->checkAttributeExists($reflectionClass, 'KupShop\ComponentsBundle\Attributes\Entrypoint')) {
|
|
$this->entrypoint = $attribute->newInstance()->getEntrypoint();
|
|
$this->addComponent($component['name']);
|
|
}
|
|
}
|
|
|
|
$components = $this->components;
|
|
$oldComponents = Yaml::parseFile($input->getOption('load'))['components']['components'] ?? [];
|
|
|
|
ksort($components);
|
|
|
|
foreach ($components as $name => &$component) {
|
|
$version = $oldComponents[$name]['version'] ?? $this->componentsLocator->findComponentByName($name)['latest_version'] ?? $this->componentsLocator->findUnusedComponentByName($name)['latest_version'];
|
|
$component['entrypoint'] = array_keys($component['entrypoint']);
|
|
$component['version'] = $version;
|
|
}
|
|
|
|
$output->write(Yaml::dump(['components' => ['components' => $components]], inline: 4));
|
|
|
|
return 0;
|
|
}
|
|
|
|
private function checkAttributeExists(\ReflectionClass $class, string $name): ?\ReflectionAttribute
|
|
{
|
|
if ($result = ($class->getAttributes($name, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null)) {
|
|
return $result;
|
|
}
|
|
|
|
if ($parent = $class->getParentClass()) {
|
|
return $this->checkAttributeExists($parent, $name);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected function discoverComponents($template, $entrypoint = null): void
|
|
{
|
|
if ($entrypoint) {
|
|
$this->entrypoint = $entrypoint;
|
|
$this->templates = [];
|
|
}
|
|
|
|
if (array_key_exists($template, $this->templates)) {
|
|
return;
|
|
}
|
|
|
|
$this->templates[$template] = 1;
|
|
|
|
$source = $this->twig->getLoader()->getSourceContext($template);
|
|
$nodes = $this->twig->parse($this->twig->tokenize($source));
|
|
|
|
if ($nodes->hasNode('parent') && $parent = $nodes->getNode('parent')) {
|
|
$this->discoverComponents($parent->getAttribute('value'));
|
|
}
|
|
|
|
if ($entrypoint) {
|
|
unset($this->entrypoint);
|
|
}
|
|
}
|
|
|
|
protected function addComponent($name): void
|
|
{
|
|
$component = $this->componentsLocator->findComponentByName($name);
|
|
|
|
if (!$component) {
|
|
return;
|
|
}
|
|
|
|
$this->components[$name] ??= [
|
|
'version' => -1,
|
|
'entrypoint' => [],
|
|
];
|
|
|
|
$this->components[$name]['entrypoint'][$this->entrypoint] = 1;
|
|
|
|
$this->discoverComponents($component['template']);
|
|
}
|
|
|
|
public function enterNode(Node $node, Environment $env): Node
|
|
{
|
|
if ($node instanceof ComponentNode) {
|
|
$name = $node->getAttribute('component');
|
|
|
|
$this->addComponent($name);
|
|
}
|
|
|
|
if ($node instanceof FunctionExpression && $node->getAttribute('name') === 'component') {
|
|
$name = $node->getNode('arguments')->getNode(0)->getAttribute('value');
|
|
|
|
$this->addComponent($name);
|
|
}
|
|
|
|
if ($node instanceof IncludeNode) {
|
|
$this->discoverComponents($node->getNode('expr')->getAttribute('value'));
|
|
}
|
|
|
|
if ($node instanceof FunctionExpression && $node->getAttribute('name') === 'include') {
|
|
$this->discoverComponents($node->getNode('arguments')->getNode(0)->getAttribute('value'));
|
|
}
|
|
|
|
return $node;
|
|
}
|
|
|
|
public function leaveNode(Node $node, Environment $env): ?Node
|
|
{
|
|
return $node;
|
|
}
|
|
|
|
public function getPriority()
|
|
{
|
|
return 0;
|
|
}
|
|
}
|