Skip to content

Commit f08dd22

Browse files
committed
[Toolkit] Improve InstallComponentCommand by asking/guessing which Kit to use, remove ux_toolkit.kit parameter, remove DependencyInjection configuration
1 parent cf9496e commit f08dd22

File tree

8 files changed

+74
-74
lines changed

8 files changed

+74
-74
lines changed

src/Toolkit/config/services.php

-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535

3636
->set('.ux_toolkit.command.install', InstallComponentCommand::class)
3737
->args([
38-
param('ux_toolkit.kit'),
3938
service('.ux_toolkit.registry.registry_factory'),
4039
service('filesystem'),
4140
])
@@ -55,7 +54,6 @@
5554
->args([
5655
service('.ux_toolkit.kit.kit_factory'),
5756
service('filesystem'),
58-
param('kernel.project_dir'),
5957
])
6058

6159
->set('.ux_toolkit.registry.github', GitHubRegistry::class)

src/Toolkit/doc/index.rst

+9-27
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,16 @@ Install the UX Toolkit using Composer and Symfony Flex:
4040
# If you want to keep `html_cva` and `tailwind_merge` in your Twig components:
4141
$ composer require twig/extra-bundle twig/html-extra:^3.12.0 tales-from-a-dev/twig-tailwind-extra
4242
43-
Configuration
44-
-------------
45-
46-
Configuration is done in your ``config/packages/ux_toolkit.yaml`` file:
47-
48-
.. code-block:: yaml
49-
50-
# config/packages/ux_toolkit.yaml
51-
ux_toolkit:
52-
kit: 'shadcn'
53-
5443
Usage
5544
-----
5645

5746
You may find a list of components in the `UX Components page`_, with the installation instructions for each of them.
5847

59-
For example, if you want to install the `Button` component, you will find the following instruction:
48+
For example, if you want to install a `Button` component, you will find the following instruction:
6049

6150
.. code-block:: terminal
6251
63-
$ php bin/console ux:toolkit:install-component Button
52+
$ php bin/console ux:toolkit:install-component Button --kit=<kitName>
6453
6554
It will create the ``templates/components/Button.html.twig`` file, and you will be able to use the `Button` component like this:
6655

@@ -121,24 +110,17 @@ A kit is composed of:
121110
- A ``templates/components`` directory, that contains the Twig components,
122111
- A ``docs/components`` directory, optional, that contains the documentation for each "root" Twig component.
123112

124-
Use your kit in a Symfony application
125-
-------------------------------------
113+
Using your kit
114+
~~~~~~~~~~~~~~
126115

127-
You can globally configure the kit to use in your application by setting the ``ux_toolkit.kit`` configuration:
128-
129-
.. code-block:: yaml
130-
131-
# config/packages/ux_toolkit.yaml
132-
ux_toolkit:
133-
kit: 'github.com/my-username/my-ux-kits'
134-
# or for a specific version
135-
kit: 'github.com/my-username/my-ux-kits:1.0.0'
136-
137-
If you do not want to globally configure the kit, you can pass the ``--kit`` option to the ``ux:toolkit:install-component`` command:
116+
Once your kit is published on GitHub, you can use it by specifying the ``--kit`` option when installing a component:
138117

139118
.. code-block:: terminal
140119
141-
$ php bin/console ux:toolkit:install-component Button --kit=github.com/my-username/my-ux-kits
120+
$ php bin/console ux:toolkit:install-component Button --kit=github.com/my-username/my-ux-toolkit-kit
121+
122+
# or for a specific version
123+
$ php bin/console ux:toolkit:install-component Button --kit=github.com/my-username/my-ux-toolkit-kit:1.0.0
142124
143125
Backward Compatibility promise
144126
------------------------------

src/Toolkit/src/Command/InstallComponentCommand.php

+40-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\UX\Toolkit\File\File;
2525
use Symfony\UX\Toolkit\Installer\Installer;
2626
use Symfony\UX\Toolkit\Kit\Kit;
27+
use Symfony\UX\Toolkit\Registry\LocalRegistry;
2728
use Symfony\UX\Toolkit\Registry\RegistryFactory;
2829

2930
/**
@@ -42,7 +43,6 @@ class InstallComponentCommand extends Command
4243
private bool $isInteractive;
4344

4445
public function __construct(
45-
private readonly string $kitName,
4646
private readonly RegistryFactory $registryFactory,
4747
private readonly Filesystem $filesystem,
4848
) {
@@ -53,6 +53,7 @@ protected function configure(): void
5353
{
5454
$this
5555
->addArgument('component', InputArgument::OPTIONAL, 'The component name (Ex: Button)')
56+
->addOption('kit', 'k', InputOption::VALUE_OPTIONAL, 'The kit name (Ex: shadcn, or github.com/user/my-ux-toolkit-kit)')
5657
->addOption(
5758
'destination',
5859
'd',
@@ -61,7 +62,6 @@ protected function configure(): void
6162
Path::join('templates', 'components')
6263
)
6364
->addOption('force', 'f', InputOption::VALUE_NONE, 'Force the component installation, even if the component already exists')
64-
->addOption('kit', 'k', InputOption::VALUE_OPTIONAL, 'Override the kit name', $this->kitName)
6565
->setHelp(
6666
<<<EOF
6767
The <info>%command.name%</info> command will install a new UX Component in your project.
@@ -92,10 +92,44 @@ protected function execute(InputInterface $input, OutputInterface $output): int
9292
$io = new SymfonyStyle($input, $output);
9393

9494
$kitName = $input->getOption('kit');
95-
$registry = $this->registryFactory->getForKit($kitName);
96-
$kit = $registry->getKit($kitName);
95+
$componentName = $input->getArgument('component');
96+
97+
// If the kit name is not explicitly provided, we need to suggest one
98+
if (null === $kitName) {
99+
/** @var list<Kit> $availableKits */
100+
$availableKits = [];
101+
foreach (LocalRegistry::getAvailableKitsName() as $availableKitName) {
102+
$kit = $this->registryFactory->getForKit($availableKitName)->getKit($availableKitName);
103+
104+
if (null === $componentName) {
105+
$availableKits[] = $kit;
106+
} elseif (null !== $kit->getComponent($componentName)) {
107+
$availableKits[] = $kit;
108+
}
109+
}
110+
// If more than one kit is available, we ask the user which one to use
111+
if (($availableKitsCount = \count($availableKits)) > 1) {
112+
$kitName = $io->choice(null === $componentName ? 'Which kit do you want to use?' : \sprintf('The component "%s" exists in multiple kits. Which one do you want to use?', $componentName), array_map(fn (Kit $kit) => $kit->name, $availableKits));
113+
114+
foreach ($availableKits as $availableKit) {
115+
if ($availableKit->name === $kitName) {
116+
$kit = $availableKit;
117+
break;
118+
}
119+
}
120+
} elseif (1 === $availableKitsCount) {
121+
$kit = $availableKits[0];
122+
} else {
123+
$io->error('No kit found. Please provide a kit name using the --kit option.');
124+
125+
return Command::FAILURE;
126+
}
127+
} else {
128+
$registry = $this->registryFactory->getForKit($kitName);
129+
$kit = $registry->getKit($kitName);
130+
}
97131

98-
if (null === $componentName = $input->getArgument('component')) {
132+
if (null === $componentName) {
99133
// Ask for the component name if not provided
100134
$componentName = $io->choice('Which component do you want to install?', array_map(fn (Component $component) => $component->name, $this->getAvailableComponents($kit)));
101135
$component = $kit->getComponent($componentName);
@@ -124,7 +158,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
124158
}
125159
}
126160

127-
$io->writeln(\sprintf('Installing component <info>%s</> from the <info>%s</> kit...', $component->name, $kitName));
161+
$io->writeln(\sprintf('Installing component <info>%s</> from the <info>%s</> kit...', $component->name, $kit->name));
128162

129163
$installer = new Installer($this->filesystem, fn (string $question) => $this->io->confirm($question, $input->isInteractive()));
130164
$installationReport = $installer->installComponent($kit, $component, $destinationPath = $input->getOption('destination'), $input->getOption('force'));

src/Toolkit/src/Registry/LocalRegistry.php

+24-12
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Filesystem\Filesystem;
1515
use Symfony\Component\Filesystem\Path;
16+
use Symfony\Component\Finder\Finder;
1617
use Symfony\UX\Toolkit\Kit\Kit;
1718
use Symfony\UX\Toolkit\Kit\KitFactory;
1819

@@ -24,6 +25,8 @@
2425
*/
2526
final class LocalRegistry implements Registry
2627
{
28+
private static string $kitsDir = __DIR__.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'kits';
29+
2730
public static function supports(string $kitName): bool
2831
{
2932
return 1 === preg_match('/^[a-zA-Z0-9_-]+$/', $kitName);
@@ -32,25 +35,34 @@ public static function supports(string $kitName): bool
3235
public function __construct(
3336
private readonly KitFactory $kitFactory,
3437
private readonly Filesystem $filesystem,
35-
private readonly string $projectDir,
3638
) {
3739
}
3840

3941
public function getKit(string $kitName): Kit
4042
{
41-
$possibleKitDirs = [
42-
// Local kit
43-
Path::join($this->projectDir, 'kits', $kitName),
44-
// From vendor
45-
Path::join($this->projectDir, 'vendor', 'symfony', 'ux-toolkit', 'kits', $kitName),
46-
];
47-
48-
foreach ($possibleKitDirs as $kitDir) {
49-
if ($this->filesystem->exists($kitDir)) {
50-
return $this->kitFactory->createKitFromAbsolutePath($kitDir);
51-
}
43+
$kitDir = Path::join(self::$kitsDir, $kitName);
44+
if ($this->filesystem->exists($kitDir)) {
45+
return $this->kitFactory->createKitFromAbsolutePath($kitDir);
5246
}
5347

5448
throw new \RuntimeException(\sprintf('Unable to find the kit "%s" in the following directories: "%s"', $kitName, implode('", "', $possibleKitDirs)));
5549
}
50+
51+
/**
52+
* @return array<string>
53+
*/
54+
public static function getAvailableKitsName(): array
55+
{
56+
$availableKitsName = [];
57+
$finder = (new Finder())->directories()->in(self::$kitsDir)->depth(0);
58+
59+
foreach ($finder as $directory) {
60+
$kitName = $directory->getRelativePathname();
61+
if (self::supports($kitName)) {
62+
$availableKitsName[] = $kitName;
63+
}
64+
}
65+
66+
return $availableKitsName;
67+
}
5668
}

src/Toolkit/src/UXToolkitBundle.php

-23
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Symfony\UX\Toolkit;
1313

14-
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
1514
use Symfony\Component\DependencyInjection\ContainerBuilder;
1615
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
1716
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
@@ -24,30 +23,8 @@ class UXToolkitBundle extends AbstractBundle
2423
{
2524
protected string $extensionAlias = 'ux_toolkit';
2625

27-
public function configure(DefinitionConfigurator $definition): void
28-
{
29-
$rootNode = $definition->rootNode();
30-
$rootNode
31-
->children()
32-
->scalarNode('kit')
33-
->info('The kit to use, it can be from the official UX Toolkit repository, or an external GitHub repository')
34-
->defaultValue('shadcn')
35-
->example([
36-
'shadcn',
37-
'github.com/user/repository@my-kit',
38-
'github.com/user/repository@my-kit:main',
39-
'https://github.com/user/repository@my-kit',
40-
])
41-
->end()
42-
->end();
43-
}
44-
4526
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
4627
{
47-
$container->parameters()
48-
->set('ux_toolkit.kit', $config['kit'])
49-
;
50-
5128
$container->import('../config/services.php');
5229
}
5330
}

src/Toolkit/tests/Command/InstallComponentCommandTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public function testShouldAbleToInstallComponentTableAndItsDependencies(): void
5353
$testCommand = $this->consoleCommand('ux:toolkit:install-component Table --destination='.$this->tmpDir)
5454
->execute()
5555
->assertSuccessful()
56-
->assertOutputContains('Installing component Table from the shadcn kit...')
56+
->assertOutputContains('Installing component Table from the Shadcn UI kit...')
5757
->assertOutputContains('[OK] The component has been installed.')
5858
;
5959

src/Toolkit/tests/UXToolkitBundleTest.php

-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,5 @@ public function testBundleBuildsSuccessfully(): void
2222
$container = self::$kernel->getContainer();
2323

2424
$this->assertInstanceOf(UXToolkitBundle::class, $container->get('kernel')->getBundles()['UXToolkitBundle']);
25-
$this->assertEquals('shadcn', $container->getParameter('ux_toolkit.kit'));
2625
}
2726
}

ux.symfony.com/config/packages/ux_toolkit.yaml

-2
This file was deleted.

0 commit comments

Comments
 (0)