ykmnkmi / jinja.dart

Jinja2 template engine port for Dart.
https://pub.dev/packages/jinja
MIT License
51 stars 11 forks source link

FileSystemLoader crashes when autoReload is true #8

Closed vaetas closed 3 years ago

vaetas commented 3 years ago

Hi! First of all, sincere thanks for your work on this package.

Problem

The FileSystemLoader uses Directory(path).watch to listen for modify events and load templates again. However, on macOS this crashes on every event. For example, when I make one change to my template, I receive 4 almost identical events.

Watch event: FileSystemModifyEvent('templates/section.html', contentChanged=true)
Watch event: FileSystemCreateEvent('templates/section.html~')
Watch event: FileSystemModifyEvent('templates/section.html~', contentChanged=true)
Watch event: FileSystemDeleteEvent('templates/section.html~')

Unfortunately, only the first one is valid. Subsequent events have path that ends with ~ character. When the method tries to load this template it fails, because there isn't one with that name. I'm getting following error.

Unhandled exception:
Exception: template not found: section.html~

I suppose this is some temporary files that my Android Studio creates.

Fix

The easiest fix is to add following line into the file system event listener.

if (event.path.endsWith('~')) return;

The code would look like this:

  @override
  void load(Environment env) {
    super.load(env);

    if (autoReload) {
      for (final path in paths) {
        Directory(path).watch(recursive: true).listen((event) {
          if (event.path.endsWith('~')) return;
          // ....
        });
      }
    }
  }

Furthermore, we could filter the stream to listen for only the correct events. This would lower the number of events and should improve the performance.

Directory(path)
    .watch(recursive: true)
    .where((event) => !event.path.endsWith('~'))
    .listen((event) {});

If you wouldn't mind, you could use the watcher package. It's from the Dart teams and doesn't seem to have this issue.

I can create a PR if you don't have the time to make these changes. Let me know.

Ideas

I'm using this for live-reload for my static site generator blake. I already have one file system watcher active. When the user edits a file I trigger rebuild. However, when I set autoReload to true in FileSystemLoader it spawns another watcher. Due to this I'm unable to ensure that the template is already updated at the time I trigger rebuild.

Do you have any ideas for solutions? One solution I have in my mind is to extend the Loader class and add custom method that would take a path for updated template and called env.fromString on it. Could this be correct? Or I could just call env.fromString right in my watcher.

Anyway, sorry for this long post (I tried to cover the whole problem) and thanks for help!

ykmnkmi commented 3 years ago

Hi! Thanks for usage and feedback. I added missing extension filter to watch. This must be fix.

vaetas commented 3 years ago

Thanks!