Skip to content

Commit 553b2b6

Browse files
Merge pull request #58 from spatie/v3
V3
2 parents 0f849a8 + 620052a commit 553b2b6

File tree

82 files changed

+2709
-1027
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+2709
-1027
lines changed

.claude/settings.local.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(./vendor/bin/pest:*)",
5+
"Bash(composer show:*)"
6+
]
7+
}
8+
}

.github/workflows/php-cs-fixer.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88

99
steps:
1010
- name: Checkout code
11-
uses: actions/checkout@v2
11+
uses: actions/checkout@v5
1212
with:
1313
ref: ${{ github.head_ref }}
1414

.github/workflows/phpstan.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: PHPStan
2+
3+
on:
4+
push:
5+
paths:
6+
- '**.php'
7+
- 'phpstan.neon.dist'
8+
- 'phpstan-baseline.neon'
9+
pull_request:
10+
paths:
11+
- '**.php'
12+
- 'phpstan.neon.dist'
13+
- 'phpstan-baseline.neon'
14+
15+
jobs:
16+
phpstan:
17+
name: phpstan
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v5
21+
22+
- name: Setup PHP
23+
uses: shivammathur/setup-php@v2
24+
with:
25+
php-version: '8.5'
26+
coverage: none
27+
28+
- name: Install dependencies
29+
run: |
30+
composer update --prefer-dist --no-interaction --no-suggest
31+
32+
- name: Run PHPStan
33+
run: ./vendor/bin/phpstan --error-format=github

.github/workflows/run-tests.yml

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,31 @@ jobs:
99
fail-fast: true
1010
matrix:
1111
os: [ ubuntu-latest ]
12-
php: [8.3, 8.4 ]
13-
laravel: [ 8.*, 9.* , 10.*, 11.*, 12.* ]
12+
php: [ 8.2, 8.3, 8.4, 8.5 ]
13+
laravel: [ 11.*, 12.* ]
1414
dependency-version: [ prefer-lowest, prefer-stable ]
1515
include:
16-
- laravel: 8.*
17-
testbench: ^6.25
18-
- laravel: 9.*
19-
testbench: 7.*
20-
- laravel: 10.*
21-
testbench: 8.*
2216
- laravel: 11.*
2317
testbench: 9.*
2418
- laravel: 12.*
2519
testbench: 10.*
2620
exclude:
27-
- php: 8.1
28-
laravel: [11.*, 12.*]
2921
- php: 8.2
30-
laravel: [8.*, 12.*]
31-
- php: 8.3
32-
laravel: [8.*,9.*]
22+
laravel: 12.*
23+
- php: 8.5
24+
laravel: 11.*
3325

3426
name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ${{ matrix.os }}
3527

3628
steps:
3729
- name: Checkout code
38-
uses: actions/checkout@v3
30+
uses: actions/checkout@v5
31+
32+
- name: Cache dependencies
33+
uses: actions/cache@v4
34+
with:
35+
path: ~/.composer/cache/files
36+
key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
3937

4038
- name: Setup PHP
4139
uses: shivammathur/setup-php@v2

README.md

Lines changed: 215 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,252 @@
1-
<div align="left">
2-
<a href="https://spatie.be/open-source?utm_source=github&utm_medium=banner&utm_campaign=laravel-typescript-transformer">
3-
<picture>
4-
<source media="(prefers-color-scheme: dark)" srcset="https://spatie.be/packages/header/laravel-typescript-transformer/html/dark.webp?1744124203">
5-
<img alt="Logo for laravel-typescript-transformer" src="https://spatie.be/packages/header/laravel-typescript-transformer/html/light.webp?1744124203">
6-
</picture>
7-
</a>
1+
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/support-ukraine.svg?t=1" />](https://supportukrainenow.org)
82

9-
<h1>Transform PHP types to TypeScript</h1>
3+
# Transform PHP types to TypeScript
104

115
[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/laravel-typescript-transformer.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-typescript-transformer)
126
[![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/spatie/laravel-typescript-transformer/run-tests?label=tests)](https://github.com/spatie/laravel-typescript-transformer/actions?query=workflow%3Arun-tests+branch%3Amaster)
137
[![Styling](https://github.com/spatie/laravel-typescript-transformer/workflows/Check%20&%20fix%20styling/badge.svg)](https://github.com/spatie/laravel-typescript-transformer/actions?query=workflow%3A%22Check+%26+fix+styling%22)
148
[![Psalm](https://github.com/spatie/laravel-typescript-transformer/workflows/Psalm/badge.svg)](https://github.com/spatie/laravel-typescript-transformer/actions?query=workflow%3APsalm)
159
[![Total Downloads](https://img.shields.io/packagist/dt/spatie/laravel-typescript-transformer.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-typescript-transformer)
16-
17-
</div>
1810

19-
Always wanted type safety within PHP and TypeScript without duplicating a lot of code? Then you will like this package! Let's say you have an enum:
11+
A
12+
This package allows you to convert PHP classes to TypeScript.
13+
14+
This class...
2015

2116
```php
22-
class Languages extends Enum
17+
#[TypeScript]
18+
class User
2319
{
24-
const TYPESCRIPT = 'typescript';
25-
const PHP = 'php';
20+
public int $id;
21+
public string $name;
22+
public ?string $address;
2623
}
2724
```
2825

29-
Wouldn't it be cool if you could have an automatically generated TypeScript definition like this:
26+
... will be converted to this TypeScript type:
3027

3128
```ts
32-
export type Languages = 'typescript' | 'php';
29+
export type User = {
30+
id: number;
31+
name: string;
32+
address: string | null;
33+
}
3334
```
3435
35-
This package will automatically generate such definitions for you, the only thing you have to do is adding this annotation:
36+
Here's another example.
3637
3738
```php
38-
/** @typescript **/
39-
class Languages extends Enum
39+
enum Languages: string
4040
{
41-
const TYPESCRIPT = 'typescript';
42-
const PHP = 'php';
41+
case TYPESCRIPT = 'typescript';
42+
case PHP = 'php';
4343
}
4444
```
4545

46-
You can even take it a bit further and generate TypeScript from classes:
46+
The `Languages` enum will be converted to:
47+
48+
```tsx
49+
export type Languages = 'typescript' | 'php';
50+
```
51+
52+
And that's just the beginning! TypeScript transformer can handle complex types, generics and even allows you to create
53+
TypeScript functions.
54+
55+
## Installation
56+
57+
You can install the package via composer:
58+
59+
```bash
60+
composer require spatie/laravel-typescript-transformer
61+
```
62+
63+
## Setting up TypeScript transformer
64+
65+
When using Laravel, first install the specific `TypeScriptTransformerServiceProvider`:
66+
67+
```bash
68+
php artisan typescript:install
69+
```
70+
71+
This command will create a `TypeScriptTransformerServiceProvider` in your `app/Providers` directory. Which looks like
72+
this:
4773

4874
```php
49-
/** @typescript */
50-
class User
75+
class TypeScriptTransformerServiceProvider extends BaseTypeScriptTransformerServiceProvider
5176
{
52-
public int $id;
77+
protected function configure(TypeScriptTransformerConfigFactory $config): void
78+
{
79+
$config; // We'll come back to this in a minute
80+
}
81+
}
82+
```
5383

54-
public string $name;
84+
And it will also register the service provider in your `bootstrap/providers.php` file (when running Laravel 11 or
85+
above). Or in your `config/app.php` file when running Laravel 10 or below.
5586

56-
public ?string $address;
87+
Now you can transform types as such:
88+
89+
```bash
90+
php artisan typescript:transform
91+
```
92+
93+
Since we haven't configured TypeScript transformer yet, this command won't do anything.
94+
95+
In order to configure TypeScript Transformer, we recommand you to now continue reading the documentation on the
96+
framework-agnostic [typescript-transformer](https://github.com/spatie/typescript-transformer) package. The docs will
97+
explain how to configure the package which is by modifying the `$config` object we saw earlier in the
98+
`TypeScriptTransformerServiceProvider`.
99+
100+
After you're done reading the framework-agnostic docs, you can return here to read about Laravel-specific features this
101+
package provides.
102+
103+
## Watching changes and live updating TypeScript
104+
105+
It is possible to have TypeScript transformer watch your PHP files for changes and automatically update the generated
106+
TypeScript files. You can do this by running:
107+
108+
```bash
109+
php artisan typescript:transform --watch
110+
```
111+
112+
## Laravel-specific features
113+
114+
This package provides some extra features on top of the base TypeScript transformer package tailed for Laravel
115+
applications. Let's go through them.
116+
117+
### Your routes in TypeScript
118+
119+
Laravel provides a great way to define routes and then generate URLs to those routes in PHP using the `route()` helper.
120+
While this all works in PHP, it can be a bit of a pain to do the same in TypeScript. TypeScript transformer can help you
121+
here by providing exact copy of the `route()` helper in TypeScript.
122+
123+
To add the helper, add the following provider to your `TypeScriptTransformerServiceProvider`:
124+
125+
```php
126+
use Spatie\LaravelTypeScriptTransformer\TransformedProviders\LaravelRouteTransformedProvider;
127+
128+
protected function configure(TypeScriptTransformerConfigFactory $config): void
129+
{
130+
$config->provider(new LaravelRouteTransformedProvider());
57131
}
58132
```
59133

60-
This will be transformed to:
134+
The next time you run the `typescript:transform` command, a TypeScript function called `route` will be generated in
135+
the `helpers/route.ts` file.
136+
137+
You can now use the `route` function in your TypeScript code like this:
61138

62139
```ts
63-
export type User = {
64-
id: number;
65-
name: string;
66-
address: string | null;
67-
}
140+
import {route} from './helpers/route';
141+
142+
// Without parameters
143+
const indexUrl = route('users.index');
144+
// https://laravel.dev/users
145+
146+
// With parameters
147+
const userUrl = route('users.show', {user: 1});
148+
// https://laravel.dev/users/1
149+
```
150+
151+
TypeScript will be smart enough to provide you autocompletion on these controllers and their parameters.
152+
153+
Sometimes you might want to exclude certain routes from being included in the generated TypeScript. You can do this by
154+
adding a route filter. The package provides three types of route filters:
155+
156+
**NamedRouteFilter**
157+
158+
Allows you to remove routes by their name. It is possible to use wildcards.
159+
160+
```php
161+
use Spatie\LaravelTypeScriptTransformer\RouteFilters\NamedRouteFilter;
162+
163+
$config->provider(new LaravelRouteTransformedProvider(
164+
routeFilters: [
165+
new NamedRouteFilter('debugbar.*', 'hidden'),
166+
],
167+
));
168+
```
169+
170+
**ControllerRouteFilter**
171+
172+
Allows you to remove routes by their controller class or namespace using wildcards.
173+
174+
```php
175+
use Spatie\LaravelTypeScriptTransformer\RouteFilters\ControllerRouteFilter;
176+
177+
$config->provider(new LaravelRouteTransformedProvider(
178+
routeFilters: [
179+
new ControllerRouteFilter(['App\Http\Controllers\Admin\*', 'HiddenController']),
180+
],
181+
));
182+
```
183+
184+
**ClosureRouteFilter**
185+
186+
Allows you to provide a closure that will be called for each route. If the closure returns `true`, the route will be
187+
excluded.
188+
189+
```php
190+
use Spatie\LaravelTypeScriptTransformer\RouteFilters\ClosureRouteFilter;
191+
192+
$config->provider(new LaravelRouteTransformedProvider(
193+
routeFilters: [
194+
new ClosureRouteFilter(function (Route $route) {
195+
return str_starts_with($route->uri(), 'internal/');
196+
}),
197+
],
198+
));
199+
```
200+
201+
By default, the helper will generate absolute URLs meaning it includes the app URL. This URL will be fetched from the
202+
window object in JavaScript. If you want to generate relative URLs instead, you can pass `false` as the third parameter,
203+
indicating you don't want absolute URLs:
204+
205+
```ts
206+
const indexUrl = route('users.index', {}, false);
207+
// /users
208+
```
209+
210+
The default value of the absolute parameter can be changed by setting a default for the provider:
211+
212+
```php
213+
$config->provider(new LaravelRouteTransformedProvider(
214+
absoluteUrlsByDefault: false,
215+
));
68216
```
69217

70-
Want to know more? You can find the documentation [here](https://docs.spatie.be/typescript-transformer/v2/introduction/).
218+
Now when using the `route` helper in TypeScript, URLs will be relative by default:
219+
220+
```ts
221+
const indexUrl = route('users.index');
222+
// /users
223+
```
224+
225+
TypeScript transformer will automatically generate the `helpers/route.ts` file in the output directory you configured
226+
for TypeScript transformer. It is possible to change the path of this file as such:
227+
228+
```php
229+
$config->provider(new LaravelRouteTransformedProvider(
230+
path: 'route.ts',
231+
));
232+
```
233+
234+
When running in the watch mode of the package, the generated `route.ts` file will automatically be updated when you
235+
change your routes in Laravel. By default the watcher will monitor the following directories for changes:
236+
237+
- `routes`
238+
- `bootstrap`
239+
- `app/Providers`
240+
241+
It is possible to customize the directories that are monitored as such:
242+
243+
```php
244+
$config->provider(new LaravelRouteTransformedProvider(
245+
routeDirectories: [
246+
'custom/routes/directory',
247+
'another/directory/to/watch',
248+
],
249+
));
71250

72251
## Testing
73252

@@ -85,7 +264,8 @@ Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTI
85264

86265
## Security
87266

88-
If you've found a bug regarding security please mail [security@spatie.be](mailto:security@spatie.be) instead of using the issue tracker.
267+
If you've found a bug regarding security please mail [security@spatie.be](mailto:security@spatie.be) instead of using
268+
the issue tracker.
89269

90270
## Credits
91271

0 commit comments

Comments
 (0)