-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
TwigUnusedCommand.php
109 lines (96 loc) · 4.19 KB
/
TwigUnusedCommand.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<?php
declare(strict_types=1);
namespace Drush\Commands\core;
use Consolidation\OutputFormatters\FormatterManager;
use Consolidation\OutputFormatters\Options\FormatterOptions;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Template\TwigEnvironment;
use Drush\Attributes as CLI;
use Drush\Boot\BootstrapManager;
use Drush\Commands\AutowireTrait;
use Drush\Formatters\FormatterTrait;
use Drush\Utils\StringUtils;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Path;
use Symfony\Component\Finder\Finder;
#[AsCommand(
name: self::NAME,
description: 'Find potentially unused Twig templates.',
aliases: ['twu']
)]
#[CLI\FilterDefaultField(field: 'template')]
final class TwigUnusedCommand extends Command
{
use AutowireTrait;
use FormatterTrait;
public const NAME = 'twig:unused';
public function __construct(
protected readonly FormatterManager $formatterManager,
protected readonly BootstrapManager $bootstrapManager,
protected readonly TwigEnvironment $twig,
private readonly LoggerInterface $logger
) {
parent::__construct();
}
protected function configure(): void
{
$this
->addArgument('searchpaths', InputArgument::REQUIRED, 'A comma delimited list of paths to recursively search')
// Usages can't have a description with plain Console :(. Use setHelp() if desired as per https://github.com/symfony/symfony/issues/45050
->addUsage('twig:unused /var/www/mass.local/docroot/modules/custom,/var/www/mass.local/docroot/themes/custom')
->setHelp('Immediately before running this command, web crawl your entire web site. Or use your Production PHPStorage dir for comparison.');
$this->configureFormatter(RowsOfFields::class, $this->getFormatterOptions());
}
public function execute(InputInterface $input, OutputInterface $output): int
{
$formatterOptions = $this->getFormatterOptions()->setInput($input);
$data = $this->doExecute($input, $output);
$data = $this->alterResult($data, $input);
$this->formatterManager->write($output, $input->getOption('format'), $data, $formatterOptions);
return self::SUCCESS;
}
public function doExecute(InputInterface $input, OutputInterface $output): RowsOfFields
{
$searchpaths = $input->getArgument('searchpaths');
$unused = [];
$phpstorage = PhpStorageFactory::get('twig');
// Find all templates in the codebase.
$files = Finder::create()
->files()
->name('*.html.twig')
->exclude('tests')
->in(StringUtils::csvToArray($searchpaths));
$this->logger->notice('Found {count} templates', ['count' => count($files)]);
// Check to see if a compiled equivalent exists in PHPStorage
foreach ($files as $file) {
$relative = Path::makeRelative($file->getRealPath(), $this->bootstrapManager->getRoot());
$mainCls = $this->twig->getTemplateClass($relative);
$cache = $this->twig->getCache();
if ($cache) {
$key = $cache->generateKey($relative, $mainCls);
if (!$phpstorage->exists($key)) {
$unused[$key] = [
'template' => $relative,
'compiled' => $key,
];
}
} else {
throw new \Exception('There was a problem, please ensure your twig cache is enabled.');
}
}
$this->logger->notice('Found {count} unused', ['count' => count($unused)]);
return new RowsOfFields($unused);
}
protected function getFormatterOptions(): FormatterOptions
{
return (new FormatterOptions())
->setFieldLabels(['template' => 'Template', 'compiled' => 'Compiled'])
->setTableDefaultFields(['template', 'compiled']);
}
}