Skip to content

Commit 2c64fe6

Browse files
committed
add make:twig-component maker
1 parent e88dde4 commit 2c64fe6

File tree

8 files changed

+258
-0
lines changed

8 files changed

+258
-0
lines changed

src/Maker/MakeTwigComponent.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle\Maker;
13+
14+
use Symfony\Bundle\MakerBundle\ConsoleStyle;
15+
use Symfony\Bundle\MakerBundle\DependencyBuilder;
16+
use Symfony\Bundle\MakerBundle\Generator;
17+
use Symfony\Bundle\MakerBundle\InputConfiguration;
18+
use Symfony\Bundle\MakerBundle\Str;
19+
use Symfony\Component\Console\Command\Command;
20+
use Symfony\Component\Console\Input\InputArgument;
21+
use Symfony\Component\Console\Input\InputInterface;
22+
use Symfony\Component\Console\Input\InputOption;
23+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
24+
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
25+
26+
/**
27+
* @author Kevin Bond <kevinbond@gmail.com>
28+
*/
29+
final class MakeTwigComponent extends AbstractMaker
30+
{
31+
public static function getCommandName(): string
32+
{
33+
return 'make:twig-component';
34+
}
35+
36+
public static function getCommandDescription(): string
37+
{
38+
return 'Creates a twig (or live) component';
39+
}
40+
41+
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
42+
{
43+
$command
44+
->setDescription(self::getCommandDescription())
45+
->addArgument('name', InputArgument::OPTIONAL, 'The name of your twig component (ie <fg=yellow>NotificationComponent</>)')
46+
->addOption('live', null, InputOption::VALUE_NONE, 'Whether to create a live twig component (requires <fg=yellow>symfony/ux-live-component</>)')
47+
;
48+
}
49+
50+
public function configureDependencies(DependencyBuilder $dependencies): void
51+
{
52+
$dependencies->addClassDependency(AsTwigComponent::class, 'symfony/ux-twig-component');
53+
}
54+
55+
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
56+
{
57+
$name = $input->getArgument('name');
58+
$live = $input->getOption('live');
59+
60+
if ($live && !class_exists(AsLiveComponent::class)) {
61+
throw new \RuntimeException('You must install symfony/ux-live-component to create a live component (composer require symfony/ux-live-component)');
62+
}
63+
64+
$factory = $generator->createClassNameDetails(
65+
$name,
66+
'Twig\\Components',
67+
'Component'
68+
);
69+
70+
$shortName = Str::asSnakeCase(Str::removeSuffix($factory->getShortName(), 'Component'));
71+
72+
$generator->generateClass(
73+
$factory->getFullName(),
74+
sprintf('%s/../Resources/skeleton/twig/%s', __DIR__, $live ? 'LiveComponent.tpl.php' : 'Component.tpl.php'),
75+
[
76+
'live' => $live,
77+
'short_name' => $shortName,
78+
]
79+
);
80+
$generator->generateTemplate(
81+
"components/{$shortName}.html.twig",
82+
sprintf('%s/../Resources/skeleton/twig/%s', __DIR__, 'component_template.tpl.php')
83+
);
84+
85+
$generator->writeChanges();
86+
87+
$this->writeSuccessMessage($io);
88+
$io->newLine();
89+
$io->writeln(" To render the component, use {{ component('{$shortName}') }}.");
90+
$io->newLine();
91+
}
92+
93+
public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
94+
{
95+
if (!$input->getOption('live')) {
96+
$input->setOption('live', $io->confirm('Make this a live component?', class_exists(AsLiveComponent::class)));
97+
}
98+
}
99+
}

src/Resources/config/makers.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
<tag name="maker.command" />
2222
</service>
2323

24+
<service id="maker.maker.make_twig_component" class="Symfony\Bundle\MakerBundle\Maker\MakeTwigComponent">
25+
<tag name="maker.command" />
26+
</service>
27+
2428
<service id="maker.maker.make_controller" class="Symfony\Bundle\MakerBundle\Maker\MakeController">
2529
<argument type="service" id="maker.php_compat_util" />
2630
<tag name="maker.command" />
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?= "<?php\n" ?>
2+
3+
namespace <?= $namespace; ?>;
4+
5+
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
6+
7+
#[AsTwigComponent('<?= $short_name; ?>')]
8+
final class <?= $class_name."\n" ?>
9+
{
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?= "<?php\n" ?>
2+
3+
namespace <?= $namespace; ?>;
4+
5+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
6+
use Symfony\UX\LiveComponent\DefaultActionTrait;
7+
8+
#[AsLiveComponent('<?= $short_name; ?>')]
9+
final class <?= $class_name."\n" ?>
10+
{
11+
use DefaultActionTrait;
12+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div{{ attributes }}>
2+
<!-- component html -->
3+
</div>

tests/Maker/MakeTwigComponentTest.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle\Tests\Maker;
13+
14+
use Symfony\Bundle\MakerBundle\Maker\MakeTwigComponent;
15+
use Symfony\Bundle\MakerBundle\Test\MakerTestCase;
16+
use Symfony\Bundle\MakerBundle\Test\MakerTestRunner;
17+
18+
class MakeTwigComponentTest extends MakerTestCase
19+
{
20+
public function getTestDetails(): \Generator
21+
{
22+
yield 'it_generates_twig_component' => [$this->createMakerTest()
23+
->addExtraDependencies('symfony/ux-twig-component', 'symfony/twig-bundle')
24+
->run(function (MakerTestRunner $runner) {
25+
$output = $runner->runMaker(['Alert']);
26+
27+
$this->assertStringContainsString('created: src/Twig/Components/AlertComponent.php', $output);
28+
$this->assertStringContainsString('created: templates/components/alert.html.twig', $output);
29+
$this->assertStringContainsString("To render the component, use {{ component('alert') }}.", $output);
30+
31+
$runner->copy(
32+
'make-twig-component/tests/it_generates_twig_component.php',
33+
'tests/GeneratedTwigComponentTest.php'
34+
);
35+
$runner->replaceInFile('tests/GeneratedTwigComponentTest.php', '{name}', 'alert');
36+
$runner->runTests();
37+
})
38+
];
39+
40+
yield 'it_generates_pascal_case_twig_component' => [$this->createMakerTest()
41+
->addExtraDependencies('symfony/ux-twig-component', 'symfony/twig-bundle')
42+
->run(function (MakerTestRunner $runner) {
43+
$output = $runner->runMaker(['FormInput']);
44+
45+
$this->assertStringContainsString('created: src/Twig/Components/FormInputComponent.php', $output);
46+
$this->assertStringContainsString('created: templates/components/form_input.html.twig', $output);
47+
$this->assertStringContainsString("To render the component, use {{ component('form_input') }}.", $output);
48+
49+
$runner->copy(
50+
'make-twig-component/tests/it_generates_twig_component.php',
51+
'tests/GeneratedTwigComponentTest.php'
52+
);
53+
$runner->replaceInFile('tests/GeneratedTwigComponentTest.php', '{name}', 'form_input');
54+
$runner->runTests();
55+
})
56+
];
57+
58+
yield 'it_generates_live_component' => [$this->createMakerTest()
59+
->addExtraDependencies('symfony/ux-live-component', 'symfony/twig-bundle')
60+
->run(function (MakerTestRunner $runner) {
61+
$output = $runner->runMaker(['Alert']);
62+
63+
$this->assertStringContainsString('created: src/Twig/Components/AlertComponent.php', $output);
64+
$this->assertStringContainsString('created: templates/components/alert.html.twig', $output);
65+
$this->assertStringContainsString("To render the component, use {{ component('alert') }}.", $output);
66+
67+
$runner->copy(
68+
'make-twig-component/tests/it_generates_live_component.php',
69+
'tests/GeneratedLiveComponentTest.php'
70+
);
71+
$runner->replaceInFile('tests/GeneratedLiveComponentTest.php', '{name}', 'alert');
72+
$runner->runTests();
73+
})
74+
];
75+
76+
yield 'it_generates_pascal_case_live_component' => [$this->createMakerTest()
77+
->addExtraDependencies('symfony/ux-live-component', 'symfony/twig-bundle')
78+
->run(function (MakerTestRunner $runner) {
79+
$output = $runner->runMaker(['FormInput']);
80+
81+
$this->assertStringContainsString('created: src/Twig/Components/FormInputComponent.php', $output);
82+
$this->assertStringContainsString('created: templates/components/form_input.html.twig', $output);
83+
$this->assertStringContainsString("To render the component, use {{ component('form_input') }}.", $output);
84+
85+
$runner->copy(
86+
'make-twig-component/tests/it_generates_live_component.php',
87+
'tests/GeneratedLiveComponentTest.php'
88+
);
89+
$runner->replaceInFile('tests/GeneratedLiveComponentTest.php', '{name}', 'form_input');
90+
$runner->runTests();
91+
})
92+
];
93+
}
94+
95+
protected function getMakerClass(): string
96+
{
97+
return MakeTwigComponent::class;
98+
}
99+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace App\Tests;
4+
5+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
6+
7+
class GeneratedTwigComponentTest extends KernelTestCase
8+
{
9+
public function testController()
10+
{
11+
$output = self::getContainer()->get('twig')->createTemplate("{{ component('{name}') }}")->render();
12+
13+
$this->assertStringContainsString('<div data-controller="live" data-live-url-value=', $output);
14+
$this->assertStringContainsString('<!-- component html -->', $output);
15+
}
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace App\Tests;
4+
5+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
6+
7+
class GeneratedTwigComponentTest extends KernelTestCase
8+
{
9+
public function testController()
10+
{
11+
$output = self::getContainer()->get('twig')->createTemplate("{{ component('{name}') }}")->render();
12+
13+
$this->assertSame("<div>\n <!-- component html -->\n</div>\n", $output);
14+
}
15+
}

0 commit comments

Comments
 (0)