Skip to content

Instantly share code, notes, and snippets.

@royteusink
Last active April 4, 2025 11:20
Show Gist options
  • Select an option

  • Save royteusink/e685cc82a0ba5d1bb19f4f245d9a1fc2 to your computer and use it in GitHub Desktop.

Select an option

Save royteusink/e685cc82a0ba5d1bb19f4f245d9a1fc2 to your computer and use it in GitHub Desktop.

Revisions

  1. royteusink revised this gist Apr 4, 2025. 1 changed file with 14 additions and 0 deletions.
    14 changes: 14 additions & 0 deletions GenerateTypescriptRoutes.md
    Original file line number Diff line number Diff line change
    @@ -190,4 +190,18 @@ php artisan app:generate-typescript-routes
    <script lang="ts" setup">
    import { route_to_login } from './routes';
    </script>
    ```

    **vite.config.js**
    ```js
    import { watch } from 'vite-plugin-watch';

    export default defineConfig({
    plugins: [
    watch({
    pattern: 'routes/{web,manage}.php',
    command: 'php artisan app:generate-typescript-routes',
    }),
    ],
    });
    ```
  2. royteusink revised this gist Nov 19, 2024. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion GenerateTypescriptRoutes.md
    Original file line number Diff line number Diff line change
    @@ -187,5 +187,7 @@ php artisan app:generate-typescript-routes
    <a :href="route_to_login()">Login</a>
    <a :href="route_to_login({ backUrl: '/homepage' })">Login</a>
    </template>
    <script lang="ts" setup"></script>
    <script lang="ts" setup">
    import { route_to_login } from './routes';
    </script>
    ```
  3. royteusink revised this gist Nov 19, 2024. 1 changed file with 71 additions and 3 deletions.
    74 changes: 71 additions & 3 deletions GenerateTypescriptRoutes.md
    Original file line number Diff line number Diff line change
    @@ -33,17 +33,28 @@ class GenerateTypescriptRoutes extends Command
    public function handle()
    {
    $contents = collect(Route::getRoutes()->getRoutesByName())->map(function ($route, $name) {
    $routeIngoreList = [
    'filament.',
    'debugbar.',
    'livewire.',
    'ignition.',
    'sanctum.csrf-cookie',
    ];

    if (Str::startsWith($name, $routeIngoreList)) {
    return '';
    }

    preg_match_all('/\{(.*?)\}/', $route->uri(), $args, PREG_SET_ORDER);

    $argstr = collect($args)
    ->map(fn ($a) => $a[1] . ": string | number")
    ->map(fn ($a) => $this->mapReservedWord($a[1]) . ": string | number")
    ->add('query?: QueryParams')
    ->join(', ');

    $path = preg_replace_callback('/\{(.*?)\}/', function ($match) {
    $isOptional = strpos($match[1], '?') !== false;
    return '${' . str_replace('?', '', $match[1]) . ($isOptional ? " ?? ''" : '') . '}';
    return '${' . str_replace('?', '', $this->mapReservedWord($match[1])) . ($isOptional ? " ?? ''" : '') . '}';
    }, $route->uri());

    if (substr($path, 0, 1) !== '/') {
    @@ -54,7 +65,7 @@ class GenerateTypescriptRoutes extends Command

    return "/** {$name} */
    export function route_to_{$safeName}({$argstr}) { return withQuery(`{$path}`, query); }\n";
    })->join("\n");
    })->filter()->join("\n");

    $header = '
    // _______ _______ __ _ _______ ______ _______ _______ _______ ______
    @@ -78,6 +89,63 @@ function withQuery(path: string, query: QueryParams) {
    File::put(resource_path('js/routes.ts'), $header . "\n" . $contents);
    $this->info(resource_path('js/routes.ts') . ' succesfully generated');
    }

    private function mapReservedWord($word) {
    $jsReservedWords = [
    'break',
    'case',
    'catch',
    'class',
    'const',
    'continue',
    'debugger',
    'default',
    'delete',
    'do',
    'else',
    'enum',
    'export',
    'extends',
    'false',
    'finally',
    'for',
    'function',
    'if',
    'import',
    'in',
    'instanceof',
    'let',
    'new',
    'null',
    'return',
    'super',
    'switch',
    'this',
    'throw',
    'true',
    'try',
    'typeof',
    'var',
    'void',
    'while',
    'with',
    'yield',
    'await',
    'implements',
    'interface',
    'package',
    'private',
    'protected',
    'public',
    'static',
    ];

    if (in_array($word, $jsReservedWords)) {
    return '_' . $word;
    }

    return $word;
    }
    }
    ```

  4. royteusink revised this gist Apr 19, 2024. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion GenerateTypescriptRoutes.md
    Original file line number Diff line number Diff line change
    @@ -42,7 +42,8 @@ class GenerateTypescriptRoutes extends Command
    ->join(', ');

    $path = preg_replace_callback('/\{(.*?)\}/', function ($match) {
    return '$' . str_replace('?', '', $match[0]);
    $isOptional = strpos($match[1], '?') !== false;
    return '${' . str_replace('?', '', $match[1]) . ($isOptional ? " ?? ''" : '') . '}';
    }, $route->uri());

    if (substr($path, 0, 1) !== '/') {
  5. royteusink revised this gist Apr 3, 2024. 1 changed file with 24 additions and 7 deletions.
    31 changes: 24 additions & 7 deletions GenerateTypescriptRoutes.md
    Original file line number Diff line number Diff line change
    @@ -38,6 +38,7 @@ class GenerateTypescriptRoutes extends Command

    $argstr = collect($args)
    ->map(fn ($a) => $a[1] . ": string | number")
    ->add('query?: QueryParams')
    ->join(', ');

    $path = preg_replace_callback('/\{(.*?)\}/', function ($match) {
    @@ -51,10 +52,10 @@ class GenerateTypescriptRoutes extends Command
    $safeName = Str::snake(str_replace(['-', '.'], '_', $name));

    return "/** {$name} */
    export function route_to_{$safeName}({$argstr}) { return `{$path}`; }\n";
    export function route_to_{$safeName}({$argstr}) { return withQuery(`{$path}`, query); }\n";
    })->join("\n");

    $header = "
    $header = '
    // _______ _______ __ _ _______ ______ _______ _______ _______ ______
    // | || || | | || || _ | | _ || || || |
    // | ___|| ___|| |_| || ___|| | || | |_| ||_ _|| ___|| _ |
    @@ -64,9 +65,16 @@ $header = "
    // |_______||_______||_| |__||_______||___| |_||__| |__| |___| |_______||______|
    //
    // Generated with:
    // php artisan app:generate-typescript-routes\n
    ";
    File::put(resource_path('js/routes.ts'), $header . $contents);
    // php artisan app:generate-typescript-routes

    export type QueryParams = string | string[][] | Record<string, string> | URLSearchParams | undefined;

    function withQuery(path: string, query: QueryParams) {
    const searchParams = new URLSearchParams(query);
    return `${path}${searchParams.size > 0 ? `?${searchParams.toString()}` : \'\'}`;
    }
    ';
    File::put(resource_path('js/routes.ts'), $header . "\n" . $contents);
    $this->info(resource_path('js/routes.ts') . ' succesfully generated');
    }
    }
    @@ -85,21 +93,30 @@ $header = "
    // Generated with:
    // php artisan app:generate-typescript-routes

    export type QueryParams = string | string[][] | Record<string, string> | URLSearchParams | undefined;

    function withQuery(path: string, query: QueryParams) {
    const searchParams = new URLSearchParams(query);
    return `${path}${searchParams.size > 0 ? `?${searchParams.toString()}` : ''}`;
    }

    /** register */
    export function route_to_register() { return `/register`; }
    export function route_to_register(query?: QueryParams) { return withQuery(`/register`, query); }

    /** login */
    export function route_to_login() { return `/login`; }
    export function route_to_login(query?: QueryParams) { return withQuery(`/login`, query); }
    ```

    **Usage**
    ```sh
    php artisan app:generate-typescript-routes
    ```

    **App.vue**
    ```vue
    <template>
    <a :href="route_to_login()">Login</a>
    <a :href="route_to_login({ backUrl: '/homepage' })">Login</a>
    </template>
    <script lang="ts" setup"></script>
    ```
  6. royteusink revised this gist Apr 3, 2024. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions GenerateTypescriptRoutes.md
    Original file line number Diff line number Diff line change
    @@ -93,6 +93,10 @@ export function route_to_login() { return `/login`; }
    ```

    **Usage**
    ```sh
    php artisan app:generate-typescript-routes
    ```
    **App.vue**
    ```vue
    <template>
    <a :href="route_to_login()">Login</a>
  7. royteusink revised this gist Apr 3, 2024. 1 changed file with 28 additions and 0 deletions.
    28 changes: 28 additions & 0 deletions GenerateTypescriptRoutes.md
    Original file line number Diff line number Diff line change
    @@ -70,4 +70,32 @@ $header = "
    $this->info(resource_path('js/routes.ts') . ' succesfully generated');
    }
    }
    ```

    **Example generated routes.ts**
    ```ts
    // _______ _______ __ _ _______ ______ _______ _______ _______ ______
    // | || || | | || || _ | | _ || || || |
    // | ___|| ___|| |_| || ___|| | || | |_| ||_ _|| ___|| _ |
    // | | __ | |___ | || |___ | |_||_ | | | | | |___ | | | |
    // | || || ___|| _ || ___|| __ || | | | | ___|| |_| |
    // | |_| || |___ | | | || |___ | | | || _ | | | | |___ | |
    // |_______||_______||_| |__||_______||___| |_||__| |__| |___| |_______||______|
    //
    // Generated with:
    // php artisan app:generate-typescript-routes

    /** register */
    export function route_to_register() { return `/register`; }

    /** login */
    export function route_to_login() { return `/login`; }
    ```

    **Usage**
    ```vue
    <template>
    <a :href="route_to_login()">Login</a>
    </template>
    <script lang="ts" setup"></script>
    ```
  8. royteusink created this gist Apr 3, 2024.
    73 changes: 73 additions & 0 deletions GenerateTypescriptRoutes.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,73 @@
    Laravel Command to generate route endpoints that can be used in typescript fully threeshakable.

    **GenerateTypescriptRoutes.php**
    ```php
    <?php

    namespace App\Console\Commands;

    use Illuminate\Console\Command;
    use Illuminate\Support\Str;
    use Illuminate\Support\Facades\File;
    use Illuminate\Support\Facades\Route;

    class GenerateTypescriptRoutes extends Command
    {
    /**
    * The name and signature of the console command.
    *
    * @var string
    */
    protected $signature = 'app:generate-typescript-routes';

    /**
    * The console command description.
    *
    * @var string
    */
    protected $description = 'Generate a typescript file with all the routes (treeshakable)';

    /**
    * Execute the console command.
    */
    public function handle()
    {
    $contents = collect(Route::getRoutes()->getRoutesByName())->map(function ($route, $name) {

    preg_match_all('/\{(.*?)\}/', $route->uri(), $args, PREG_SET_ORDER);

    $argstr = collect($args)
    ->map(fn ($a) => $a[1] . ": string | number")
    ->join(', ');

    $path = preg_replace_callback('/\{(.*?)\}/', function ($match) {
    return '$' . str_replace('?', '', $match[0]);
    }, $route->uri());

    if (substr($path, 0, 1) !== '/') {
    $path = '/'. $path;
    }

    $safeName = Str::snake(str_replace(['-', '.'], '_', $name));

    return "/** {$name} */
    export function route_to_{$safeName}({$argstr}) { return `{$path}`; }\n";
    })->join("\n");

    $header = "
    // _______ _______ __ _ _______ ______ _______ _______ _______ ______
    // | || || | | || || _ | | _ || || || |
    // | ___|| ___|| |_| || ___|| | || | |_| ||_ _|| ___|| _ |
    // | | __ | |___ | || |___ | |_||_ | | | | | |___ | | | |
    // | || || ___|| _ || ___|| __ || | | | | ___|| |_| |
    // | |_| || |___ | | | || |___ | | | || _ | | | | |___ | |
    // |_______||_______||_| |__||_______||___| |_||__| |__| |___| |_______||______|
    //
    // Generated with:
    // php artisan app:generate-typescript-routes\n
    ";
    File::put(resource_path('js/routes.ts'), $header . $contents);
    $this->info(resource_path('js/routes.ts') . ' succesfully generated');
    }
    }
    ```