Skip to content

[AssetMapper] Search & filter assets in debug:asset-mapper command #58141

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 97 additions & 24 deletions src/Symfony/Component/AssetMapper/Command/DebugAssetMapperCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
use Symfony\Component\AssetMapper\AssetMapperRepository;
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\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

Expand All @@ -40,10 +42,41 @@ public function __construct(
protected function configure(): void
{
$this
->addArgument('name', InputArgument::OPTIONAL, 'An asset name (or a path) to search for (e.g. "app")')
->addOption('ext', null, InputOption::VALUE_REQUIRED, 'Filter assets by extension (e.g. "css")', null, ['js', 'css', 'png'])
->addOption('full', null, null, 'Whether to show the full paths')
->addOption('vendor', null, InputOption::VALUE_NEGATABLE, 'Only show assets from vendor packages')
->setHelp(<<<'EOT'
The <info>%command.name%</info> command outputs all of the assets in
asset mapper for debugging purposes.
The <info>%command.name%</info> command displays information about the Asset
Mapper for debugging purposes.

To list all configured paths (with local paths and their namespace prefixes) and
all mapped assets (with their logical path and filesystem path), run:

<info>php %command.full_name%</info>

You can filter the results by providing a name to search for in the asset name
or path:

<info>php %command.full_name% bootstrap.js</info>
<info>php %command.full_name% style/</info>

To filter the assets by extension, use the <info>--ext</info> option:

<info>php %command.full_name% --ext=css</info>

To show only assets from vendor packages, use the <info>--vendor</info> option:

<info>php %command.full_name% --vendor</info>

To exclude assets from vendor packages, use the <info>--no-vendor</info> option:

<info>php %command.full_name% --no-vendor</info>

To see the full paths, use the <info>--full</info> option:

<info>php %command.full_name% --full</info>

EOT
);
}
Expand All @@ -52,43 +85,83 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$allAssets = $this->assetMapper->allAssets();
$name = $input->getArgument('name');
$extensionFilter = $input->getOption('ext');
$vendorFilter = $input->getOption('vendor');

if (!$extensionFilter) {
$io->section($name ? 'Matched Paths' : 'Asset Mapper Paths');
$pathRows = [];
foreach ($this->assetMapperRepository->allDirectories() as $path => $namespace) {
$path = $this->relativizePath($path);
if (!$input->getOption('full')) {
$path = $this->shortenPath($path);
}
if ($name && !str_contains($path, $name) && !str_contains($namespace, $name)) {
continue;
}
$pathRows[] = [$path, $namespace];
}
uasort($pathRows, static function (array $a, array $b): int {
return [(bool) $a[1], ...$a] <=> [(bool) $b[1], ...$b];
});
if ($pathRows) {
$io->table(['Path', 'Namespace prefix'], $pathRows);
} else {
$io->warning('No paths found.');
}
}

$pathRows = [];
foreach ($this->assetMapperRepository->allDirectories() as $path => $namespace) {
$path = $this->relativizePath($path);
$io->section($name ? 'Matched Assets' : 'Mapped Assets');
$rows = $this->searchAssets($name, $extensionFilter, $vendorFilter);
if ($rows) {
if (!$input->getOption('full')) {
$path = $this->shortenPath($path);
$rows = array_map(fn (array $row): array => [
$this->shortenPath($row[0]),
$this->shortenPath($row[1]),
], $rows);
}

$pathRows[] = [$path, $namespace];
uasort($rows, static function (array $a, array $b): int {
return [$a] <=> [$b];
});
$io->table(['Logical Path', 'Filesystem Path'], $rows);
if ($this->didShortenPaths) {
$io->note('To see the full paths, re-run with the --full option.');
}
} else {
$io->warning('No assets found.');
}
$io->section('Asset Mapper Paths');
$io->table(['Path', 'Namespace prefix'], $pathRows);

return 0;
}

/**
* @return list<array{0:string, 1:string}>
*/
private function searchAssets(?string $name, ?string $extension, ?bool $vendor): array
{
$rows = [];
foreach ($allAssets as $asset) {
foreach ($this->assetMapper->allAssets() as $asset) {
if ($extension && $extension !== $asset->publicExtension) {
continue;
}
if (null !== $vendor && $vendor !== $asset->isVendor) {
continue;
}
if ($name && !str_contains($asset->logicalPath, $name) && !str_contains($asset->sourcePath, $name)) {
continue;
}

$logicalPath = $asset->logicalPath;
$sourcePath = $this->relativizePath($asset->sourcePath);

if (!$input->getOption('full')) {
$logicalPath = $this->shortenPath($logicalPath);
$sourcePath = $this->shortenPath($sourcePath);
}

$rows[] = [
$logicalPath,
$sourcePath,
];
}
$io->section('Mapped Assets');
$io->table(['Logical Path', 'Filesystem Path'], $rows);

if ($this->didShortenPaths) {
$io->note('To see the full paths, re-run with the --full option.');
}

return 0;
return $rows;
}

private function relativizePath(string $path): string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,57 @@ public function testCommandDumpsInformation()
$this->assertStringContainsString('subdir/file6.js', $tester->getDisplay());
$this->assertStringContainsString('dir2'.\DIRECTORY_SEPARATOR.'subdir'.\DIRECTORY_SEPARATOR.'file6.js', $tester->getDisplay());
}

public function testCommandFiltersName()
{
$application = new Application(new AssetMapperTestAppKernel('test', true));
$command = $application->find('debug:asset-map');
$tester = new CommandTester($command);
$res = $tester->execute(['name' => 'stimulus']);

$this->assertSame(0, $res);
$this->assertStringContainsString('stimulus', $tester->getDisplay());
$this->assertStringNotContainsString('lodash', $tester->getDisplay());

$res = $tester->execute(['name' => 'lodash']);
$this->assertSame(0, $res);
$this->assertStringNotContainsString('stimulus', $tester->getDisplay());
$this->assertStringContainsString('lodash', $tester->getDisplay());
}

public function testCommandFiltersExtension()
{
$application = new Application(new AssetMapperTestAppKernel('test', true));
$command = $application->find('debug:asset-map');
$tester = new CommandTester($command);
$res = $tester->execute(['--ext' => 'css']);

$this->assertSame(0, $res);
$this->assertStringNotContainsString('.js', $tester->getDisplay());

$this->assertStringContainsString('file1.css', $tester->getDisplay());
$this->assertStringContainsString('file3.css', $tester->getDisplay());
}

public function testCommandFiltersVendor()
{
$application = new Application(new AssetMapperTestAppKernel('test', true));
$command = $application->find('debug:asset-map');

$tester = new CommandTester($command);
$res = $tester->execute(['--vendor' => true]);

$this->assertSame(0, $res);
$this->assertStringContainsString('vendor/lodash/', $tester->getDisplay());
$this->assertStringContainsString('@hotwired/stimulus', $tester->getDisplay());
$this->assertStringNotContainsString('dir2'.\DIRECTORY_SEPARATOR, $tester->getDisplay());

$tester = new CommandTester($command);
$res = $tester->execute(['--no-vendor' => true]);

$this->assertSame(0, $res);
$this->assertStringNotContainsString('vendor/lodash/', $tester->getDisplay());
$this->assertStringNotContainsString('@hotwired/stimulus', $tester->getDisplay());
$this->assertStringContainsString('dir2'.\DIRECTORY_SEPARATOR, $tester->getDisplay());
}
}
Loading