Skip to content

Commit 7cafae4

Browse files
committed
iterate
1 parent 11bc9f0 commit 7cafae4

33 files changed

+246
-551
lines changed

src/Toolkit/schema-kit-recipe-v1.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@
4545
"description": "Package name and optional version constraint (e.g., 'vendor/package:^1.0')"
4646
}
4747
}
48+
},
49+
{
50+
"description": "A dependency on a Recipe",
51+
"type": "object",
52+
"required": ["type", "name"],
53+
"properties": {
54+
"type": {
55+
"type": "string",
56+
"enum": ["recipe"]
57+
},
58+
"name": {
59+
"type": "string",
60+
"description": "The name of the Recipe this component depends on, e.g., 'Button'"
61+
}
62+
}
4863
}
4964
]
5065
}

src/Toolkit/src/Command/CreateKitCommand.php

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
105105
</button>
106106
TWIG
107107
);
108-
$this->filesystem->dumpFile('Button/examples/Default.html.twig', <<<TWIG
109-
<twig:Button>
110-
Click me
111-
</twig:Button>
112-
TWIG,
113-
);
114-
$this->filesystem->dumpFile('Button/examples/With Variants.html.twig', <<<TWIG
115-
<twig:Button variant="default">Default</twig:Button>
116-
<twig:Button variant="secondary">Secondary</twig:Button>
117-
TWIG,
118-
);
119108

120109
$this->filesystem->dumpFile('Button/manifest.json', json_encode([
121110
'$schema' => '../vendor/symfony/ux-toolkit/schema-kit-recipe-v1.json',

src/Toolkit/src/Command/InstallComponentCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
136136
$recipe = $kit->getRecipe(name: $componentName, type: RecipeType::Component);
137137
} elseif (null === $recipe = $kit->getRecipe($componentName, type: RecipeType::Component)) {
138138
// Suggest alternatives if component does not exist
139-
$message = \sprintf('The component "%s" does not exist.', $componentName);
139+
$message = \sprintf('The recipe "%s" does not exist.', $componentName);
140140

141141
$alternativeRecipes = $this->getAlternativeRecipes($kit, $componentName);
142142
$alternativeComponentsCount = \count($alternativeRecipes);

src/Toolkit/src/File.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ public function __construct(
2828
public readonly string $destinationRelativePathName,
2929
) {
3030
if (!Path::isRelative($this->sourceRelativePathName)) {
31-
throw new \InvalidArgumentException(\sprintf('The path "%s" must be relative.', $this->sourceRelativePathName));
31+
throw new \InvalidArgumentException(\sprintf('The source path "%s" must be relative.', $this->sourceRelativePathName));
3232
}
3333

3434
if (!Path::isRelative($this->destinationRelativePathName)) {
35-
throw new \InvalidArgumentException(\sprintf('The path name "%s" must be relative.', $this->destinationRelativePathName));
35+
throw new \InvalidArgumentException(\sprintf('The destination path "%s" must be relative.', $this->destinationRelativePathName));
3636
}
3737
}
3838

src/Toolkit/src/Installer/PoolResolver.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,25 @@ public function resolveForRecipe(Kit $kit, Recipe $recipe): Pool
3434
$visitedRecipes = new \SplObjectStorage();
3535

3636
while (!empty($recipesStack)) {
37-
$currentComponent = array_pop($recipesStack);
37+
$currentRecipe = array_pop($recipesStack);
3838

3939
// Skip circular references
40-
if ($visitedRecipes->contains($currentComponent)) {
40+
if ($visitedRecipes->contains($currentRecipe)) {
4141
continue;
4242
}
4343

44-
$visitedRecipes->attach($currentComponent);
44+
$visitedRecipes->attach($currentRecipe);
4545

46-
foreach ($recipe->getFiles() as $file) {
47-
$pool->addFile($recipe, $file);
46+
foreach ($currentRecipe->getFiles() as $file) {
47+
$pool->addFile($currentRecipe, $file);
4848
}
4949

50-
foreach ($recipe->manifest->dependencies as $dependency) {
50+
foreach ($currentRecipe->manifest->dependencies as $dependency) {
5151
if ($dependency instanceof PhpPackageDependency) {
5252
$pool->addPhpPackageDependency($dependency);
5353
} elseif ($dependency instanceof RecipeDependency) {
5454
if (null === $recipeDependency = $kit->getRecipe($dependency->name)) {
55-
throw new \LogicException(\sprintf('The recipe "%s" has a dependency on unregistered recipe "%s".', $recipe->manifest->name, $dependency->name));
55+
throw new \LogicException(\sprintf('The recipe "%s" has a dependency on unregistered recipe "%s".', $currentRecipe->manifest->name, $dependency->name));
5656
}
5757

5858
$recipesStack[] = $recipeDependency;

src/Toolkit/src/Kit/KitContextRunner.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ private function contextualizeServicesForKit(Kit $kit): callable
5858
$initialTwigLoader = $this->twig->getLoader();
5959
$loaders = [];
6060
foreach ($kit->getRecipes(type: RecipeType::Component) as $recipe) {
61-
// TODO: We assume that components are in the "templates/components" directory
62-
$loaders[] = new FilesystemLoader(Path::join($recipe->absolutePath, 'templates/components'));
61+
$loaders[] = new FilesystemLoader($recipe->absolutePath);
6362
}
6463
$this->twig->setLoader(new ChainLoader([...$loaders, $initialTwigLoader]));
6564

src/Toolkit/src/Kit/KitManifest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313

1414
use Symfony\UX\Toolkit\Assert;
1515

16+
/**
17+
* @author Hugo Alliaume <[email protected]>
18+
*/
1619
final class KitManifest
1720
{
1821
public function __construct(

src/Toolkit/src/Recipe/Recipe.php

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,29 +35,16 @@ public function __construct(
3535
}
3636

3737
/**
38-
* @return list<File>
38+
* @return iterable<File>
3939
*/
4040
public function getFiles(): iterable
4141
{
4242
foreach ($this->manifest->copyFiles as $source => $destination) {
43-
$finder = (new Finder())->in(Path::join($this->absolutePath, $source))->files();
43+
$finder = (new Finder())->in(Path::join($this->absolutePath, $source))->sortByName()->files();
4444

4545
foreach ($finder as $file) {
4646
yield new File(Path::join($source, $file->getRelativePathname()), Path::join($destination, $file->getRelativePathname()));
4747
}
4848
}
4949
}
50-
51-
public function getFilesExample(): iterable
52-
{
53-
if ($this->manifest->examplesDir === null) {
54-
return [];
55-
}
56-
57-
$finder = (new Finder())->in(Path::join($this->absolutePath, $this->manifest->examplesDir))->name('*.html.twig')->files();
58-
59-
foreach ($finder as $file) {
60-
yield $file->getRelativePathname() => $file->getContents();
61-
}
62-
}
6350
}

src/Toolkit/src/Recipe/RecipeManifest.php

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Filesystem\Path;
1515
use Symfony\UX\Toolkit\Dependency\DependencyInterface;
1616
use Symfony\UX\Toolkit\Dependency\PhpPackageDependency;
17+
use Symfony\UX\Toolkit\Dependency\RecipeDependency;
1718
use Symfony\UX\Toolkit\Dependency\Version;
1819

1920
/**
@@ -27,15 +28,13 @@ final class RecipeManifest
2728
* @param non-empty-string $name
2829
* @param non-empty-string $description
2930
* @param array<non-empty-string, non-empty-string> $copyFiles
30-
* @param non-empty-string|null $examplesDir
3131
* @param list<DependencyInterface> $dependencies
3232
*/
3333
public function __construct(
3434
public readonly RecipeType $type,
3535
public readonly string $name,
3636
public readonly string $description,
3737
public readonly array $copyFiles,
38-
public readonly ?string $examplesDir = null,
3938
public readonly array $dependencies = [],
4039
) {
4140
foreach ($this->copyFiles as $source => $destination) {
@@ -46,10 +45,6 @@ public function __construct(
4645
throw new \InvalidArgumentException(\sprintf('Copy file destination "%s" must be a relative path.', $destination));
4746
}
4847
}
49-
50-
if (null !== $this->examplesDir && !Path::isRelative($this->examplesDir)) {
51-
throw new \InvalidArgumentException(\sprintf('Examples directory "%s" must be a relative path.', $this->examplesDir));
52-
}
5348
}
5449

5550
/**
@@ -68,25 +63,33 @@ public static function fromJson(string $json): self
6863
if (!isset($dependency['type'])) {
6964
throw new \InvalidArgumentException(\sprintf('The dependency type is missing for dependency #%d, add "type" key.', $i));
7065
}
66+
7167
if ('php' === $dependency['type']) {
72-
$package = $dependency['package'] ?? throw new \InvalidArgumentException(\sprintf('The package name is missing for dependency #%d.', $i));
68+
$package = $dependency['package'] ?? throw new \InvalidArgumentException(\sprintf('The package name is missing for dependency #%d, add "package" key.', $i));
7369
if (str_contains($package, ':')) {
7470
[$name, $version] = explode(':', $package, 2);
7571
$dependencies[] = new PhpPackageDependency($name, new Version($version));
7672
} else {
7773
$dependencies[] = new PhpPackageDependency($package);
7874
}
75+
} else if ('recipe' === $dependency['type']) {
76+
$name = $dependency['name'] ?? throw new \InvalidArgumentException(\sprintf('The recipe name is missing for dependency #%d, add "name" key.', $i));
77+
$dependencies[] = new RecipeDependency($name);
7978
} else {
8079
throw new \InvalidArgumentException(\sprintf('The dependency type "%s" is not supported.', $dependency['type']));
8180
}
8281
}
8382

83+
$type = $data['type'] ?? throw new \InvalidArgumentException('Property "type" is required.');
84+
if (null === $type = RecipeType::tryFrom($type)) {
85+
throw new \InvalidArgumentException(\sprintf('The recipe type "%s" is not supported.', $data['type']));
86+
}
87+
8488
return new self(
85-
type: RecipeType::from($data['type'] ?? throw new \InvalidArgumentException('Property "type" is required.')),
89+
type: $type,
8690
name: $data['name'] ?? throw new \InvalidArgumentException('Property "name" is required.'),
8791
description: $data['description'] ?? throw new \InvalidArgumentException('Property "description" is required.'),
8892
copyFiles: $data['copy-files'] ?? throw new \InvalidArgumentException('Property "copy-files" is required.'),
89-
examplesDir: $data['examples-dir'] ?? null,
9093
dependencies: $dependencies,
9194
);
9295
}

src/Toolkit/src/Registry/LocalRegistry.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function getKit(string $kitName): Kit
5454
public static function getAvailableKitsName(): array
5555
{
5656
$availableKitsName = [];
57-
$finder = (new Finder())->directories()->in(self::$kitsDir)->depth(0);
57+
$finder = (new Finder())->directories()->in(self::$kitsDir)->sortByName()->depth(0);
5858

5959
foreach ($finder as $directory) {
6060
$kitName = $directory->getRelativePathname();

0 commit comments

Comments
 (0)