11ty / eleventy

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

Eleventy "files watching" bug (1.0.1-canary.3) #2270

Open genemars opened 2 years ago

genemars commented 2 years ago

Describe the bug While it correctly copies and builds files during startup, Eleventy doesn't watch .md/.liquid files in a given folder if a glob pattern containing that folder (but different file extensions) is used as addPassthroughCopy.

To Reproduce Steps to reproduce the behavior: 1, create a folder called pages inside the input folder

  1. add eleventyConfig.addPassthroughCopy("pages/**/*.webp") to .eleventy.js
  2. create a test.md file inside the pages/docs/ folder
  3. start eleventy npx @11ty/eleventy --serve
  4. try editing the pages/docs/test.md file

Expected behavior Should rebuild the test.md file if modified.

Environment:

genemars commented 2 years ago

From a quick look to the EleventyFiles.js file I see that files to watch are merged here with addPassthroughCopy files (line 426):

https://github.com/11ty/eleventy/blob/1df6299d2b985ded402d43d8fb0e763dea37dc93/src/EleventyFiles.js#L422-L428

this might be the issue with chokidar and overlapping globs.

So I was able to fix this issue by creating a separate watcher for addPassthroughCopy files in Eleventy.js.

In a very quick and dirty way, as a test, I added these lines with hard-coded array of pass-through files to watch:

    // TODO improve unwatching if JS dependencies are removed (or files are deleted)
    let rawFiles = await this.getWatchedFiles();
    debug("Watching for changes to: %o", rawFiles);

    let watcher = chokidar.watch(rawFiles, this.getChokidarConfig());

// ------- second watcher for addPassthroughCopy files ----------
    let watcher2 = chokidar.watch(['pages/**/*.webp'], this.getChokidarConfig());
    watcher2.on("change", async (path) => {
      this.logger.forceLog(`File changed: ${path}`);
      await watchRun(path);
    });
    watcher2.on("add", async (path) => {
      this.logger.forceLog(`File added: ${path}`);
      await watchRun(path);
    });
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    initWatchBench.after();

and it worked. I would contribute writing the proper code and making a PR, but I think it's best to wait for some feedback about this from @zachleat .

MWDelaney commented 2 years ago

Related to #2267 maybe

zachleat commented 2 years ago

Hmm—I’m not current able to reproduce this on my machine. Can you try to get a reproduction going on Stackblitz or something that I can see?

I tried on 1.0.0, 1.0.1-canary.3 and the upcoming 2.0.0-canary.5

genemars commented 2 years ago

This is the Stackblitz url:

https://stackblitz.com/github/genemars/web-starter-old?file=source%2Fpages%2Fdocs%2Fquick-start%2Findex.md

Try editing the index.md file. Eleventy will not detect the changes.

Then try removing the following lines from config/default.json (15-17)

Schermata da 2022-04-15 03-50-16

and CTRL-C and restart Eleventy.

Modifying the index.md file will now work.

Those entries in the config/default.json are added as addPassthroughCopy in the .eleventy.js file:

https://github.com/genemars/web-starter-old/blob/61d77c8a7ad1d11fef59b57aba2c6c1b70f366ad/.eleventy.js#L92-L97

zachleat commented 2 years ago

I can reproduce that on stackblitz! However, my attempt to get a reduced test going today is failing thus far.

chokidar docs state that it uses picomatch for inputs and anymatch for ignores, so I tried to use the paths from your project to get something to fail but got expected results on stackblitz:

// watching
const pm = require('picomatch');
const isMatch = pm([
  './templates/tags/**',
  './source/**/*.html',
  './source/**/*.liquid',
  './source/**/*.ejs',
  './source/**/*.md',
  './source/**/*.hbs',
  './source/**/*.mustache',
  './source/**/*.haml',
  './source/**/*.pug',
  './source/**/*.njk',
  './source/**/*.11ty.js',
  './source/**/*.11ty.cjs',
  './source/**/*.less',
  './source/css',
  './source/images/**',
  './source/.nojekyll',
  './source/browserconfig.xml',
  './source/manifest.json',
  './source/humans.txt',
  './source/favicon.ico',
  './source/robots.txt',
  './source/pages/**/*.webp',
  './source/pages/**/*.jpeg',
  './source/pages/**/*.png',
  './source/**/*.css',
  './source/**/*.js',
  './source/_inc/**',
  './source/_inc/layouts/**',
  './source/_data/**',
  './.eleventy.js',
  './source/**/*.json',
  './source/**/*.11tydata.cjs',
  './source/**/*.11tydata.js',
]);

console.log(isMatch('source/pages/docs/quick-start/index.md')); //=> should be true

// ignoring
const anymatch = require('anymatch');

const matchers = [
  'node_modules/**',
  'source/_filters',
  'source/app',
  'source/lib',
  '.idea',
  '.zuix',
  'logs',
  '*.log',
  'npm-debug.log*',
  'yarn-debug.log*',
  'yarn-error.log*',
  'pids',
  '*.pid',
  '*.seed',
  '*.pid.lock',
  'lib-cov',
  'coverage',
  '.nyc_output',
  '.grunt',
  'bower_components',
  '.lock-wscript',
  'build/Release',
  'node_modules/**',
  'jspm_packages',
  'typings',
  '.npm',
  '.eslintcache',
  '.node_repl_history',
  '*.tgz',
  '.yarn-integrity',
  '.env',
  'docs/**',
];

console.log(anymatch(matchers, 'source/pages/docs/quick-start/index.md')); // should be false

https://stackblitz.com/edit/node-pejvth?file=index.js

zachleat commented 2 years ago

(For me) note that the stackblitz test case is also failing on 1.0.0