Skip to content
New issue

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

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

Already on GitHub? Sign in 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

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

No branches or pull requests

8 participants