Skip to content

Instantly share code, notes, and snippets.

@iDevelopThings
Created June 28, 2021 12:56
Show Gist options
  • Save iDevelopThings/aa0a288a4075371d875782c72f2c0389 to your computer and use it in GitHub Desktop.
Save iDevelopThings/aa0a288a4075371d875782c72f2c0389 to your computer and use it in GitHub Desktop.
Generate typescript definitions for ziggy-js so you get auto completion for the route() helper
mix
.js('resources/ts/app.ts', 'public/js')
.vue({
version : 3,
})
.ziggy();
const mix = require('laravel-mix');
const {exec} = require('child_process');
mix.extend('ziggy', new class {
register(config = {})
{
this.watch = config.watch ?? ['routes/**/*.php'];
this.enabled = config.enabled ?? !Mix.inProduction();
}
boot()
{
if (!this.enabled) return;
const command = () => exec(
`php artisan ziggy:ts-definitions`,
(error, stdout, stderr) => console.log(stdout)
);
command();
if (Mix.isWatching() && this.watch) {
((require('chokidar')).watch(this.watch))
.on('change', (path) => {
console.log(`${path} changed...`);
command();
});
}
}
}());
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Tightenco\Ziggy\Ziggy;
class ZiggyTsDefinitionsCommand extends Command
{
protected $signature = 'ziggy:ts-definitions';
protected $description = 'Generate TS definitions for ziggy';
protected $files;
public function __construct(Filesystem $files)
{
parent::__construct();
$this->files = $files;
}
public function handle()
{
$path = './resources/ts/shims-ziggy.d.ts';
$generatedRoutes = $this->generate();
$this->makeDirectory($path);
$this->files->put(base_path($path), $generatedRoutes);
$this->info('File generated!');
}
private function generate(): string
{
$ziggy = (new Ziggy(false, null));
$routes = collect($ziggy->toArray()['routes'])
->map(function ($route, $key) {
$methods = json_encode($route['methods'] ?? []);
return <<<TYPESCRIPT
"{$key}": {
"uri": "{$route['uri']}",
"methods": {$methods}
},
TYPESCRIPT;
})
->join("\n");
return <<<TYPESCRIPT
import {Config, InputParams, Router} from "ziggy-js";
type LaravelRoutes = {
{$routes}
[key: string]: string;
}
declare global {
declare interface ZiggyLaravelRoutes extends LaravelRoutes {}
declare function route(): Router;
declare function route(name: keyof ZiggyLaravelRoutes, params?: InputParams, absolute?: boolean, customZiggy?: Config): string;
}
export { LaravelRoutes };
TYPESCRIPT;
}
protected function makeDirectory($path)
{
if (!$this->files->isDirectory(dirname(base_path($path)))) {
$this->files->makeDirectory(dirname(base_path($path)), 0755, true, true);
}
return $path;
}
}
@ryugonomura
Copy link

ryugonomura commented Nov 17, 2021

Thank you for providing the source code.
I would like to use it with react typescript with Laravel Inertia, could you please provide the sample ts code when using LaravelRoutes and route function ?

@iDevelopThings
Copy link
Author

This is for https://github.com/tighten/ziggy package - I'm not quite understanding what you mean by the sample code, for which bit?

@matt-breeden
Copy link

matt-breeden commented Oct 11, 2022

Hey this is super cool. I was going to write something similar and you saved me a bunch of time.

I added a separate const to type the route parameters. Also thinking of trying to use reflection to type the shapes of posted/put objects, inspired by your gist and trpc.


<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Log;
use Tightenco\Ziggy\Ziggy;

class ZiggyTsDefinitionsCommand extends Command
{
    protected $signature = 'ziggy:ts-definitions';

    protected $description = 'Generate TS definitions for ziggy';

    protected $files;

    public function __construct(Filesystem $files)
    {
        parent::__construct();

        $this->files = $files;
    }

    public function handle()
    {
        $path = './resources/js/ziggy-shims.d.ts';
        $generatedRoutes = $this->generate();

        $this->makeDirectory($path);
        $this->files->put(base_path($path), $generatedRoutes);

        $this->info('File generated!');
    }

    private function generate(): string
    {
        $ziggy = (new Ziggy(false, null));
        $collectedRoutes = collect($ziggy->toArray()['routes']);
        $routes = $collectedRoutes
            ->map(function ($route, $key) {
                $methods = json_encode($route['methods'] ?? []);
                return <<<TYPESCRIPT
    "{$key}": {
        "uri": "{$route['uri']}",
        "methods": {$methods},
    },
TYPESCRIPT;

            })
            ->join("\n");

        $params = $collectedRoutes->map(function ($route, $key) {
            $matches = [];
            preg_match_all('/(?<=\{)(.*?)(?=\})/', $route['uri'], $matches);
            if (!count($matches) || !count($matches[0])) {
                return <<<TYPESCRIPT
    "{$key}": {

    },
TYPESCRIPT;
            } else {
                $unencodedParams = collect($matches[0])->reduce(function ($carry, $match) {
                    $carry[$match] = $match;
                    return $carry;
                }, []);

                $params = json_encode($unencodedParams);
                return <<<TYPESCRIPT
    "{$key}":
        {$params}
    ,
TYPESCRIPT;
            }
        })
            ->join("\n");
//Log::debug($params);
        return <<<TYPESCRIPT
import {Config, InputParams, Router} from "ziggy-js";

type LaravelRoutes = {
    {$routes}
}
const LaravelParams = {
    ${params}
}
declare global {
    declare interface ZiggyLaravelRoutes extends LaravelRoutes {}
    declare function route(): Router;
    declare function route<RouteKey extends keyof LaravelRoutes>(name: RouteKey, params?: LaravelRoutes[RouteKey], absolute?: boolean, customZiggy?: Config): string;
}
export { LaravelRoutes, LaravelParams };
TYPESCRIPT;
    }

    protected function makeDirectory($path)
    {
        if (!$this->files->isDirectory(dirname(base_path($path)))) {
            $this->files->makeDirectory(dirname(base_path($path)), 0755, true, true);
        }

        return $path;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment