Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Generate on save #321

Closed
nacr opened this issue Sep 3, 2020 · 11 comments
Closed

Generate on save #321

nacr opened this issue Sep 3, 2020 · 11 comments

Comments

@nacr
Copy link

nacr commented Sep 3, 2020

Description

Is there a way to run ziggy:generate on save of any routes file?

Suggestion

May be inside a mix pipeline?

@bakerkretzmar bakerkretzmar self-assigned this Sep 4, 2020
@bakerkretzmar
Copy link
Collaborator

Cool idea! Ziggy doesn't currently provide any way to do this, no.

You might be able to do it with Mix, Mix provides a .then() method you can chain to do extra stuff every time it runs, but I don't think it watches PHP files by default and after some digging I can't figure out how to get it to either. Otherwise you could set up your own watcher to do this manually.

I think this is outside the scope of Ziggy itself, but let me know if you get it working and I'd be happy to add a note about it to the docs!

@nacr
Copy link
Author

nacr commented Sep 8, 2020

@bakerkretzmar sorry for the late reply 🙏

this was my solution inside I'm including it inside my webpack.mix.js file.

if (process.env.NODE_ENV === 'development') {
    const cmd = require('node-cmd');

    let command = function () {
        cmd.get(
            'php artisan ziggy:generate the/path/you/need/for/ziggy.js',
            function (err, data) {
                console.log('the current dir contains these files :\n\n', data);
            }
        );
    };

    command();

    let files = ['routes/**/*.php'];
    let chokidar = require('chokidar');
    let watcher = chokidar.watch(files);

    // Event listeners.
    watcher.on('change', path => {
        console.log(`${path}`);
        command();
    });
}

I hope it helps.

@bakerkretzmar
Copy link
Collaborator

@nacr nice! I think you could use child_process from Node, and Mix already requires chokidar. I did some more digging and you could 'integrate' it with Mix a bit like this:

const mix = require('laravel-mix');
const { exec } = require('child_process');

mix.extend('ziggy', new class {
    register(config = {}) {
        this.watch = config.watch ?? ['routes/**/*.php'];
        this.path = config.path ?? '';
        this.enabled = config.enabled ?? !Mix.inProduction();
    }

    boot() {
        if (!this.enabled) return;

        const command = () => exec(
            `php artisan ziggy:generate ${this.path}`,
            (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();
                });
        };
    }
}());

mix.js('resources/js/app.js', 'public/js')
    .postCss('resources/css/app.css', 'public/css', [])
    .ziggy();

Note also the Mix.isWatching() check so it doesn't keep the process alive forever if you run npm run dev or npm run prod or something.

@caturandi-labs
Copy link

may i know how to do that in Vite bundler ? @bakerkretzmar

@bakerkretzmar
Copy link
Collaborator

@caturandi-labs I'm still not very familiar with Vite so I don't know, sorry. Take a look at their Plugin API docs: https://vitejs.dev/guide/api-plugin.html

Vite uses Rollup for production builds so my guess would be that their plugin system is very similar to Rollup's.

@Zzombiee2361
Copy link

Mix is getting deprecated. Is it going to be ok to use this?

@nacr
Copy link
Author

nacr commented Jul 14, 2022

Hello,

@bakerkretzmar since Laravel now uses Vite.

This is my current solution.

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import laravel from 'laravel-vite-plugin';
import watchAndRun from '@kitql/vite-plugin-watch-and-run';


export default defineConfig({
    plugins: [
        laravel({
            input: [
                'resources/ts/app.ts',
            ],
            refresh: true,
        }),
        vue({
            template: {
                transformAssetUrls: {
                    base: null,
                    includeAbsolute: false,
                },
            },
        }),
        watchAndRun([
            {
                watch: '/var/www/html/routes/**/*.(php)',
                run: 'php artisan ziggy:generate',
                // delay: 300, Optional parameter to delay the run command.
                name: 'Ziggy', // Optional parameter to display a name in the terminal
            }
        ])
    ],
    resolve: {
        alias: {
            '@': '/resources/ts',
        },
    },
});

I'm using Laravel Sail in other setups the path may change.

You can find more info on the Vite Plugin used here:
KitQL - vite-plugin-watch-and-run

@bigsmalloverall
Copy link

I had a lot of problems with all solutions above. The main one was lack of types. I managed to figure out something like this:

I'm using Vue 2.7, but I'm sure something similar can be used in Vue 3.

Do the installation part from the docs.
Install @types/ziggy-js

app.blade.php:

<head>
    {{--  Other imports  --}}
    @routes {{--  as per ziggy docs --}}
</head>

This makes sure I always have up to date routes.

Create window.d.ts and ziggy-vue.d.ts files in root folder (near tsconfig.json)

// window.d.ts

// route definition from @types/ziggy-js
import route from "ziggy-js";

declare global {
    interface Window {
       // This hints the compiler that there will be globally defined route function
        route: typeof route;
    }
}
// ziggy-js.d.ts

import Vue from "vue"; // This import needs to be here
import route from "ziggy-js";

declare module "vue/types/vue" {
    // Hints type of $route for vue
    // This will be neded later
    interface Vue {
        $route: typeof route;
    }
}

You need to include this declarations in tsconfig.json

{
  "include": [
    "*.d.ts",
    "resources/ts/**/*",
    "resources/ts/**/*.d.ts",
    "resources/ts/**/*.vue",
  ],
}

I added $route to Vue manually:

// main.ts

// other imports

Vue.prototype.$route = window.route;

createInertiaApp({ ... });

Now I can use $route function in Vue with type hints:
image
image

I'm pretty sure this can be done simpler, but I have no idea how :)

@benmag
Copy link

benmag commented Dec 22, 2022

@nacr wasn't able to get your suggestion for @bakerkretzmar to work unfortunately

I eventually came across this vite-plugin-run plugin which has worked perfectly for me

//...
import { run } from 'vite-plugin-run';


export default defineConfig({
    plugins: [
        // ...
        run([
            {
                name: 'ziggy',
                run: ['php', 'artisan', 'ziggy:generate'],
                condition: (file) => file.includes('/routes/') && file.endsWith('.php')
            }
        ])
    ]
});

@PillFall
Copy link

PillFall commented Mar 21, 2023

I want to share my version of a plugin for auto-generation in Vite, is based on the Full Reload Plugin which Laravel uses for auto-refresh (so, it doesn't need any other dependency).

However, it lacks all the configs and tweaks from the Mix plugin.

// vite.ziggy.js

import { normalizePath } from 'vite';
import { exec } from 'child_process';
import { relative, resolve } from 'path';
import picomatch from 'picomatch';
import colors from 'picocolors';
import ziggy from 'ziggy-js/package.json';

export default function ZiggyPlugin(config) {
    const root = process.cwd();
    const log = config?.log ?? true;
    const delay = config?.delay ?? 0;

    return {
        name: 'ziggy-plugin',
        enforce: 'pre',
        apply: 'serve',
        // NOTE: Enable globbing so that Vite keeps track of the template files.
        config: () => ({ server: { watch: { disableGlobbing: false } } }),
        configureServer(server) {
            server.httpServer.once('listening', () => {
                setTimeout(() => {
                    server.config.logger.info(`\n  ${colors.blue(`${colors.bold('ZIGGY')} v${ziggy.version}`)}  ${colors.dim('ready')}`);
                }, 200);
            });

            const files = normalizePath(resolve(root, 'routes/**'));

            const command = () => exec('php artisan ziggy:generate');
            command();

            const shouldReload = picomatch(files);
            const checkReload = (path) => {
                if (shouldReload(path)) {
                    setTimeout(command, delay);
                    if (log) {
                        server.config.logger.info(`${colors.green(`${relative(root, path)} changed, regenarating ziggy file.`)}`, { clear: true, timestamp: true });
                    }
                }
            };

            // Ensure Vite keeps track of the files and triggers generation as needed.
            server.watcher.add(files);

            // Perform a generation if any of the watched files changes.
            server.watcher.on('add', checkReload);
            server.watcher.on('change', checkReload);
        },
    };
}
// vite.config.js

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import ziggy from './vite.ziggy.js';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/js/app.js', 'resources/scss/app.scss'],
        }),
        ziggy({
            // Indicates if the regenerations must be logged. Default true.
            log: false,

            // How many milliseconds to wait before regenerating after a file change. Default 0.
            delay: 100,
        }),
    ],
});

@gn306029
Copy link

不幸的是,無法得到您的工作建議

我最終遇到了這個vite-plugin-run 外掛程式,它對我來說非常有效

//...
import { run } from 'vite-plugin-run';


export default defineConfig({
    plugins: [
        // ...
        run([
            {
                name: 'ziggy',
                run: ['php', 'artisan', 'ziggy:generate'],
                condition: (file) => file.includes('/routes/') && file.endsWith('.php')
            }
        ])
    ]
});

This Answer is work for me

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

8 participants