Skip to content

Commit 35024f0

Browse files
wip
1 parent 08b571a commit 35024f0

17 files changed

+197
-133
lines changed

src/Actions/FormatFilesAction.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@ public function __construct(
1515
}
1616

1717
/**
18-
* @param array<WrittenFile> $writtenFiles
18+
* @param array<WrittenFile> $writtenFiles
1919
*/
2020
public function execute(array $writtenFiles): void
2121
{
22+
if ($this->config->formatter === null) {
23+
return;
24+
}
2225

26+
foreach ($writtenFiles as $writtenFile) {
27+
$this->config->formatter->format($writtenFile->path);
28+
}
2329
}
2430
}

src/Formatters/EslintFormatter.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Spatie\TypeScriptTransformer\Formatters;
4+
5+
use Symfony\Component\Process\Exception\ProcessFailedException;
6+
use Symfony\Component\Process\Process;
7+
8+
class EslintFormatter implements Formatter
9+
{
10+
public function format(string $file): void
11+
{
12+
$process = new Process(['npx', '--yes', 'eslint', '--fix', '--no-ignore', $file]);
13+
$process->run();
14+
15+
if (! $process->isSuccessful()) {
16+
throw new ProcessFailedException($process);
17+
}
18+
}
19+
}

src/Formatters/Formatter.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Spatie\TypeScriptTransformer\Formatters;
4+
5+
interface Formatter
6+
{
7+
public function format(string $file): void;
8+
}

src/Formatters/PrettierFormatter.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Spatie\TypeScriptTransformer\Formatters;
4+
5+
use Symfony\Component\Process\Exception\ProcessFailedException;
6+
use Symfony\Component\Process\Process;
7+
8+
class PrettierFormatter implements Formatter
9+
{
10+
public function format(string $file): void
11+
{
12+
$process = new Process(['npx', '--yes', 'prettier', '--write', $file]);
13+
$process->run();
14+
15+
if (! $process->isSuccessful()) {
16+
throw new ProcessFailedException($process);
17+
}
18+
}
19+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
namespace Spatie\TypeScriptTransformer\Laravel\Actions;
4+
5+
use Illuminate\Routing\Route;
6+
use Illuminate\Routing\Router;
7+
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteInvokableController;
8+
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteController;
9+
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteControllerAction;
10+
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteControllerCollection;
11+
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteParameter;
12+
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteParameterCollection;
13+
14+
class ResolveLaravelRoutControllerCollectionsAction
15+
{
16+
public function execute(): RouteControllerCollection
17+
{
18+
/** @var array<string, RouteController> $controllers */
19+
$controllers = [];
20+
21+
foreach (app(Router::class)->getRoutes()->getRoutes() as $route) {
22+
$controllerClass = $route->getControllerClass();
23+
24+
if ($controllerClass === null) {
25+
continue;
26+
}
27+
28+
$controllerClass = str_replace('\\', '.', $controllerClass);
29+
30+
if ($route->getActionMethod() === $route->getControllerClass()) {
31+
$controllers[$controllerClass] = new RouteInvokableController(
32+
$this->resolveRouteParameters($route),
33+
$route->methods,
34+
$this->resolveUrl($route),
35+
);
36+
37+
continue;
38+
}
39+
40+
if (! array_key_exists($controllerClass, $controllers)) {
41+
$controllers[$controllerClass] = new RouteController([]);
42+
}
43+
44+
$controllers[$controllerClass]->actions[$route->getActionMethod()] = new RouteControllerAction(
45+
$route->getActionMethod(),
46+
$this->resolveRouteParameters($route),
47+
$route->methods,
48+
$this->resolveUrl($route),
49+
);
50+
}
51+
52+
return new RouteControllerCollection($controllers);
53+
}
54+
55+
protected function resolveRouteParameters(
56+
Route $route
57+
): RouteParameterCollection {
58+
preg_match_all('/\{(.*?)\}/', $route->getDomain().$route->uri, $matches);
59+
60+
$parameters = array_map(fn (string $match) => new RouteParameter(
61+
trim($match, '?'),
62+
str_ends_with($match, '?')
63+
), $matches[1]);
64+
65+
return new RouteParameterCollection($parameters);
66+
}
67+
68+
protected function resolveUrl(Route $route): string
69+
{
70+
return str_replace('?}', '}', $route->getDomain().$route->uri);
71+
}
72+
}

src/Laravel/RouterGenerator.php renamed to src/Laravel/LaravelActionDefaultTypesProvider.php

Lines changed: 58 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22

33
namespace Spatie\TypeScriptTransformer\Laravel;
44

5-
use Illuminate\Routing\Route;
6-
use Illuminate\Routing\Router;
75
use Spatie\TypeScriptTransformer\DefaultTypeProviders\DefaultTypesProvider;
8-
use Spatie\TypeScriptTransformer\Laravel\Routes\InvokableRouteController;
6+
use Spatie\TypeScriptTransformer\Laravel\Actions\ResolveLaravelRoutControllerCollectionsAction;
97
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteController;
108
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteControllerAction;
119
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteControllerCollection;
10+
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteInvokableController;
1211
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteParameter;
1312
use Spatie\TypeScriptTransformer\Laravel\Routes\RouteParameterCollection;
1413
use Spatie\TypeScriptTransformer\Transformed\Transformed;
@@ -20,17 +19,20 @@
2019
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptGenericTypeVariable;
2120
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptIdentifier;
2221
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptIndexedAccess;
22+
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptLiteral;
23+
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptNode;
24+
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptNumber;
2325
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptObject;
2426
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptOperator;
2527
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptParameter;
2628
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptProperty;
2729
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptRaw;
2830
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptString;
31+
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptUnion;
2932

3033
// @todo implement the method, probably using a RawTypeScriptNode, creating individual notes for each JS construct is probably a bit far fetched
3134
// @todo make sure we support __invoke routes without action
3235
// @todo add support for nullable parameters, these should be inferred
33-
// @todo a syntax like route(['Controller', 'action'], {params}), route(['Controller', 'action'], param), route(InvokeableController) would be even cooler but maybe too complicated at the moment
3436

3537
/**
3638
* function route<
@@ -41,16 +43,21 @@
4143
*
4244
* }
4345
*/
44-
class RouterGenerator implements DefaultTypesProvider
46+
class LaravelActionDefaultTypesProvider implements DefaultTypesProvider
4547
{
48+
public function __construct(
49+
protected ResolveLaravelRoutControllerCollectionsAction $resolveLaravelRoutControllerCollectionsAction = new ResolveLaravelRoutControllerCollectionsAction()
50+
) {
51+
}
52+
4653
public function provide(): array
4754
{
48-
$controllers = $this->resolveRoutes();
55+
$controllers = $this->resolveLaravelRoutControllerCollectionsAction->execute();
4956

5057
$transformedRoutes = new Transformed(
5158
new TypeScriptAlias(
5259
new TypeScriptIdentifier('Routes'),
53-
$controllers->toTypeScriptNode(),
60+
$this->parseRouteControllerCollection($controllers),
5461
),
5562
null,
5663
'Routes',
@@ -131,60 +138,59 @@ public function provide(): array
131138
return [$transformedRoutes, $actionParam, $transformedAction];
132139
}
133140

134-
private function resolveRoutes(): RouteControllerCollection
141+
protected function parseRouteControllerCollection(RouteControllerCollection $collection): TypeScriptNode
135142
{
136-
/** @var array<string, RouteController> $controllers */
137-
$controllers = [];
138-
139-
foreach (app(Router::class)->getRoutes()->getRoutes() as $route) {
140-
$controllerClass = $route->getControllerClass();
141-
142-
if ($controllerClass === null) {
143-
continue;
144-
}
145-
146-
$controllerClass = str_replace('\\', '.', $controllerClass);
147-
148-
if ($route->getActionMethod() === $route->getControllerClass()) {
149-
$controllers[$controllerClass] = new InvokableRouteController(
150-
$this->resolveRouteParameters($route),
151-
$route->methods,
152-
$this->resolveUrl($route),
153-
);
154-
155-
continue;
156-
}
157-
158-
if (! array_key_exists($controllerClass, $controllers)) {
159-
$controllers[$controllerClass] = new RouteController([]);
160-
}
161-
162-
$controllers[$controllerClass]->actions[$route->getActionMethod()] = new RouteControllerAction(
163-
$route->getActionMethod(),
164-
$this->resolveRouteParameters($route),
165-
$route->methods,
166-
$this->resolveUrl($route),
143+
return new TypeScriptObject(collect($collection->controllers)->map(function (RouteController|RouteInvokableController $controller, string $name) {
144+
return new TypeScriptProperty(
145+
$name,
146+
$controller instanceof RouteInvokableController
147+
? $this->parseInvokableController($controller)
148+
: $this->parseController($controller),
167149
);
168-
}
150+
})->all());
151+
}
169152

170-
return new RouteControllerCollection($controllers);
153+
protected function parseController(RouteController $controller): TypeScriptNode
154+
{
155+
return new TypeScriptObject([
156+
new TypeScriptProperty('actions', new TypeScriptObject(collect($controller->actions)->map(function (RouteControllerAction $action, string $name) {
157+
return new TypeScriptProperty(
158+
$name,
159+
$this->parseControllerAction($action)
160+
);
161+
})->all())),
162+
]);
171163
}
172164

173-
protected function resolveRouteParameters(
174-
Route $route
175-
): RouteParameterCollection {
176-
preg_match_all('/\{(.*?)\}/', $route->getDomain().$route->uri, $matches);
165+
protected function parseControllerAction(RouteControllerAction $action): TypeScriptNode
166+
{
167+
return new TypeScriptObject([
168+
new TypeScriptProperty('name', new TypeScriptLiteral($action->name)),
169+
new TypeScriptProperty('parameters', $this->parseRouteParameterCollection($action->parameters)),
170+
]);
171+
}
177172

178-
$parameters = array_map(fn (string $match) => new RouteParameter(
179-
trim($match, '?'),
180-
str_ends_with($match, '?')
181-
), $matches[1]);
173+
protected function parseInvokableController(RouteInvokableController $controller): TypeScriptNode
174+
{
175+
return new TypeScriptObject([
176+
new TypeScriptProperty('invokable', new TypeScriptRaw('true')),
177+
new TypeScriptProperty('parameters', $this->parseRouteParameterCollection($controller->parameters)),
178+
]);
179+
}
182180

183-
return new RouteParameterCollection($parameters);
181+
protected function parseRouteParameterCollection(RouteParameterCollection $collection): TypeScriptNode
182+
{
183+
return new TypeScriptObject(array_map(function (RouteParameter $parameter) {
184+
return $this->parseRouteParameter($parameter);
185+
}, $collection->parameters));
184186
}
185187

186-
protected function resolveUrl(Route $route): string
188+
protected function parseRouteParameter(RouteParameter $parameter): TypeScriptNode
187189
{
188-
return str_replace('?}', '}', $route->getDomain().$route->uri);
190+
return new TypeScriptProperty(
191+
$parameter->name,
192+
new TypeScriptUnion([new TypeScriptString(), new TypeScriptNumber()]),
193+
isOptional: $parameter->optional,
194+
);
189195
}
190196
}

src/Laravel/Routes/RouteController.php

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptObject;
77
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptProperty;
88

9-
class RouteController implements RouterStructure
9+
readonly class RouteController implements RouterStructure
1010
{
1111
/**
1212
* @param array<string, RouteControllerAction> $actions
@@ -16,18 +16,6 @@ public function __construct(
1616
) {
1717
}
1818

19-
public function toTypeScriptNode(): TypeScriptNode
20-
{
21-
return new TypeScriptObject([
22-
new TypeScriptProperty('actions', new TypeScriptObject(collect($this->actions)->map(function (RouteControllerAction $controller, string $name) {
23-
return new TypeScriptProperty(
24-
$name,
25-
$controller->toTypeScriptNode(),
26-
);
27-
})->all())),
28-
]);
29-
}
30-
3119
public function toJsObject(): array
3220
{
3321
return [

src/Laravel/Routes/RouteControllerAction.php

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,10 @@
22

33
namespace Spatie\TypeScriptTransformer\Laravel\Routes;
44

5-
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptLiteral;
6-
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptNode;
7-
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptObject;
8-
use Spatie\TypeScriptTransformer\TypeScript\TypeScriptProperty;
9-
10-
class RouteControllerAction implements RouterStructure
5+
readonly class RouteControllerAction implements RouterStructure
116
{
127
/**
13-
* @param array<string> $methods
8+
* @param array<string> $methods
149
*/
1510
public function __construct(
1611
public string $name,
@@ -20,14 +15,6 @@ public function __construct(
2015
) {
2116
}
2217

23-
public function toTypeScriptNode(): TypeScriptNode
24-
{
25-
return new TypeScriptObject([
26-
new TypeScriptProperty('name', new TypeScriptLiteral($this->name)),
27-
new TypeScriptProperty('parameters', $this->parameters->toTypeScriptNode()),
28-
]);
29-
}
30-
3118
public function toJsObject(): array
3219
{
3320
return [

0 commit comments

Comments
 (0)