11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
17.1k stars 493 forks source link

Add support for a "cool-down" period before triggering a regeneration. #559

Closed veleek closed 4 years ago

veleek commented 5 years ago

I have a site which contains multiple uniquely styled sub-pages which each have their own SASS files. When running eleventy alongside gulp, when I save a single SASS file, gulp regenerates all of the css files which triggers eleventy to regenerate. Unfortunately, the first file change triggers a regeneration, and the second file change queues up another one. So as any css file causes eleventy to regenerate everything twice which really slows down iteration.

For example, given this super basic site example:

/site
    /events
        /birthday
            index.md
            style.scss
            /media
                thing.ttf, etc.
        /christmas
            index.md
            style.scss
            etc.

I have a gulp task that processes all the .scss files to produce style.css in the same folder. The actual sub-sections are relatively complicated but self-contained and they include things like fonts, images, additional sub-pages etc. Moving all the styles and images to folders outside of the root site would make it difficult to manage each of these sites independently so I would like to keep everything self-contained if possible.

I would love if there was some sort of configurable delay that would wait after receiving a file modification event before regenerating the site. The more complex version would be don't regenerate until X ms after the last file change event, so if my gulp task is long running each generated file would keep pushing back the regeneration time a little bit until everything was done.

This is similar to the awaitWriteFinish property in chokidar which I manually enabled in Eleventy.js and was hoping would work by queuing up file change events quickly enough to cause only a single eleventy rebuild, but no luck.

miklb commented 5 years ago

are you running the gulp tasks in a series or parallel ? Seems you should be able to control it there unless I'm misunderstanding.

veleek commented 5 years ago

Both gulp and eleventy are running in parallel using the following npm scripts:

"scripts": {
        "dev": "npm-run-all --parallel dev:eleventy dev:gulp",
        "dev:eleventy": "eleventy --serve",
        "dev:gulp": "gulp"
}

Both eleventy --serve and gulp run in watch mode, so gulp watches for changes to CSS files and recompiles them, eleventy watches for changes to Markdown an Nunjucks files and re-complies them when they change. Neither one of the tasks is exiting and restarting, so there's no way to run them in serial (because they never exit).

miklb commented 5 years ago

"dev": "npm-run-all --parallel dev:eleventy dev:gulp" can you swap the order here and run them --series

untested

"dev": "npm-run-all --series dev:gulp dev:eleventy"

in theory that will run the gulp task first, then eleventy which I think is what you were asking.

veleek commented 5 years ago

You can't run them in series. Gulp is being run in watch mode (watching for changes) so it never exits and series waits until the first task exits before running the second. If I don't run gulp in watch mode then it won't recompile scss changes. And the same is true for eleventy. Its running in watch mode and never exits because its running the local test web server.

miklb commented 5 years ago

Gotcha. Sorry for misunderstanding your configuration.

danfascia commented 5 years ago

I have this same problem with my 11ty tailwind starter.

PostCSS is watching and rebuilding the CSS and Eleventy Serve is watching the *.md but the problem imho is that 11ty blocks the command execution chain.

The only solutions I could think of both involved relying on only one of the processes to do all of the watching and rebuilding:

  1. I could setup eleventy to run postcss and compile tailwind on every build from within config but that would be slow

  2. I could use gulp to do it all but personally I don't know how to make it watch for *.md changes

miklb commented 5 years ago

Here is how I’m using Gulp with Jekyll to watch files https://github.com/miklb/jekyll-indieweb/blob/b313ce907b03eb34d59164eaef60bf3fc03fa90f/gulpfile.js#L78

I also use Gulp to run Jekyll which I’m sure could be adapted for Eleventy . Not saying it’s the most elegant solution however

zachleat commented 5 years ago

Some discussion for this is also happening at the PR: https://github.com/11ty/eleventy/pull/564

veleek commented 5 years ago

@danfascia, re: option 2, making gulp to everything could potentially work, but since I'm using eleventy as the web server I would have to have an instance of eleventy running as the server (and not processing any files) and a second instance executed by gulp whenever any input files change. And then I would need to go through all the work of duplicating a bunch of eleventy config into gulp in order to make sure it was watching the same files in the same places as eleventy already is.

@miklb, the reason that this works is that you're manually running browserSync and the thing that watches the files (gulp in this case). In the eleventy case, eleventy --serve runs both the eleventy file watcher and the browser sync. The specific behavior that I'm looking for is that I want eleventy to wait until after all files have been changed before executing, and the only way to do that would be to have some control over those parts individually, which either means pulling the server portion out of eleventy, or pulling the template processing part out so I can choose when to run them. This behavior is already supported in gulp (see here, https://gulpjs.com/docs/en/getting-started/watching-files#delay) because of the same reason I want it in eleventy - to avoid starting too early when multiple files are being changed at once.

Additionally, it looks like you have a bug in your script here (https://github.com/miklb/jekyll-indieweb/blob/b313ce907b03eb34d59164eaef60bf3fc03fa90f/gulpfile.js#L81) where this line is calling gulp.series(...) but it's not doing anything with the task, so this specific instance isn't doing anything. The only thing that gets executed when any of the CSS files change is the css task, never the browserSync one.

danfascia commented 5 years ago

@veleek yes actually you're right, the fact that 11ty is also the webserver scuppers it. However in my 11ty starter I use lightserver as the webserver instead of the built in one since it is nice to decouple processes.

I feel like you have to do it one of 2 ways ...

  1. Everything, all in on 11ty config/npx eleventy --serve
  2. Decouple all parts into build processes

It's the only way you can not block the render chain with eleventy --serve

zachleat commented 4 years ago

Fixed by https://github.com/11ty/eleventy/commit/5ee0cdea6e5f4ad82fe079286ee67de804e12d93 Please also see my comment on https://github.com/11ty/eleventy/pull/696

This will ship with 0.11.0

jeromecoupe commented 4 years ago

Just adding my 2 cents here.

When using Gulp, I generally make it watch njk and md files and use child process to run Eleventy. That way, you can control pretty much everything in terms of tasks and subtasks order.

Here is a simple .gulpfile as an example.

veleek commented 4 years ago

Thanks @jeromecoupe. I tried switching over to a similar gulpfile and everything seems to be working well (after working around a few issues with childprocess on Windows; the cross-spawn package is your friend!).

I'm pretty happy with this solution so I'll probably keep it, but I approve of having the cool-down period in eleventy even if it's only useful for the small subset of people who might only use eleventy and not any other external build tool.

jeromecoupe commented 4 years ago

Sure @veleek, sorry if I came across as not supporting the feature request. Was just detailing an approach that would sidestep the need for a delay.

veleek commented 4 years ago

:) No worries, that's not what I thought you were trying to do. I just wish I'd know how easy it was to do it your way before!

jeromecoupe commented 4 years ago

As a side note, setting the shell option to true with child process. Might be worth a try to dispense with the cross.spawn package

const cp = require("child_process");

// Eleventy
function eleventyBuild() {
  return cp.spawn("npx", ["eleventy", "--quiet"], {
    stdio: "inherit", shell: true
  });
}
veleek commented 4 years ago

Aaaaaah, so THAT'S how you do it! Thanks.

zachleat commented 4 years ago

Note also that this was included to solve a few problems in --incremental, not specifically for this gulp issue. Two birds, one stone!