Jump to content

Tutorial: Browser sync or Live reload (small footprint)


ukyo
 Share

Recommended Posts

This hook is used to monitor file changes and page updates in your ProcessWire project. It operates when debug mode is active and returns file changes and latest page update information in JSON format.

The script hook is automatically injected before the </body> tag in your HTML. It periodically checks for any file or page updates in your system at the interval you specify. If the previous check time is older than the new check time (indicating changes), the page automatically refreshes.

~/site/ready.php

if ($wire->config->debug) {

    $wire->addHookAfter('Page::render', function (HookEvent $event) {
        $script = <<<HTML
        <script>
        const browserSync = {
            mtime: 0,
            init() {
                this.sync();
            },
            sync() {
                fetch('/browser-sync')
                .then(response => response.json())
                .then(data => {
                    if (!this.mtime) {
                        this.mtime = data.max;
                    } else {
                        if (data.max > this.mtime) {
                            location.reload();
                            }
                        }
                    });
                }
            };
            browserSync.init();
            setInterval(() => browserSync.sync(), 2500);
        </script>
        HTML;

        $event->return = str_replace('</body>', "{$script}</body>", $event->return);
    });

    $wire->addHook('/browser-sync', function (HookEvent $event) {

        $wire = $event->wire();
        $root = $wire->config->paths->root;
        $siteRoot = $wire->config->paths->site;
        $extensions = '*.{php,js,css}';

        $paths = [
            "{$root}src/{$extensions}", // custom composer package
            "{$root}src/src/{$extensions}", // custom composer package
            "{$siteRoot}{$extensions}", // ready.php, init.php, finished.php
            "{$siteRoot}classes/{$extensions}", // page classes
            "{$siteRoot}templates/{$extensions}",
            "{$siteRoot}templates/*/{$extensions}",
            "{$siteRoot}templates/*/*/{$extensions}",
            "{$siteRoot}templates/*/*/*/{$extensions}",
            "{$siteRoot}templates/*/*/*/*/{$extensions}",
        ];

        $files = [];
        foreach ($paths as $pattern) {
            // Try with GLOB_BRACE first
            $result = glob($pattern, GLOB_NOSORT | GLOB_BRACE);
            // If no results with GLOB_BRACE, try without it
            if (!$result) {
                $result = glob($pattern, GLOB_NOSORT) ?: [];
            }
            $files = array_merge($files, $result);
        }

        $filemtime = 0;
        foreach ($files as $file) {
            if (!is_file($file)) {
                continue;
            }
            try {
                $mtime = @filemtime($file);
                if ($mtime && $mtime > $filemtime) {
                    $filemtime = $mtime;
                }
            } catch (\Exception $e) {
                continue;
            }
        }

        $latest = $wire->pages->get('sort=-created|-modified');
        $latestTime = $latest->modified > $latest->created ? $latest->modified : $latest->created;
        
        header('Content-Type: application/javascript');
        echo json_encode([
            'max' => max($filemtime, $latestTime),
            'mtime' => $filemtime,
            'modified' => $latest->modified,
            'created' => $latest->created
        ]);
        exit;
    });
}

This development tool helps streamline your workflow by automatically refreshing the browser when you make changes to your files or content, eliminating the need for manual page refreshes during development.

  • Like 12
Link to comment
Share on other sites

@bernhard

Yes, I know about it. However, I prefer not to use modules for these kinds of things, as my work approach would need to adapt to each module used.

I am using Vite for these purposes instead.

Before developing this script, I was using the AllInOneMinify module for ProcessWire. Later, I switched to JavaScript-based compilation solutions:

  1. First moved to Gulp with Browser Sync
  2. Then migrated to Webpack
  3. Currently using Vite

Among these tools, Vite has proven to be the lightest solution, and I'm getting exactly the results I want with my Vite configuration. While using Vite, I wondered "Why don't I implement my own browser sync solution?" This led me to write this lightweight script, which works remarkably well.

And I'm happy with this setup!

  • Like 3
Link to comment
Share on other sites

  • 5 months later...

For bypass the admin page i have added two lines

 

if ($wire->config->debug) {

    $wire->addHookAfter('Page::render', function (HookEvent $event) {
        $page = $event->object;
        if($page->template->name == 'admin') return;

	(...)

Thank you for the snippet

  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

Also, for those using DDEV  there's a  ddev-browsersync add-on, that's simple to use:

ddev add-on get ddev/ddev-browsersync
ddev restart

And then, open .ddev/browser-sync.cjs and add the files you want to watch (the more specific, the better), e.g:

files: ["/site/templates/**/*.php", "/site/templates/**/*.css"]

 Start browsersync, and launch the browser

ddev browsersync
ddev launch :3000 //Or just open https://yoursite.ddev.site:3000

Update a template and test it! You will notice a brief connected message: 

image.png.b63bd1909eebcfce87b1aea2f6fc7e6f.png

Edit: Adding this, in case it could be useful for someone else

Here's an example of some useful settings:

// #ddev-generated
let docroot = process.env.DDEV_DOCROOT;
let filesdir = process.env.DDEV_FILES_DIR;
let url = process.env.DDEV_HOSTNAME;
let nonSslUrl = process.env.DDEV_PRIMARY_URL.replace(/^https:/, 'http:');

if (filesdir === "") {
    filesdir = null;
}

module.exports = {
    files: [
        "./site/templates/**/*.{latte,php,js,css}" // watch these file types in templates
    ],
    ignore: ["node_modules", filesdir, "vendor"],
    open: false,
    ui: false,
    server: false,
    notify: false, // disable Browsersync notification
    proxy: {
        target: nonSslUrl
    },
    host: url,
    snippetOptions: {
        blacklist: ["/processwire/**"] // avoid refreshing admin backend
    },
};

 

 

Edited by Sanyaissues
Adding more context
  • Like 4
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...