From 08115d3c15cf9705f6c5daed06bda5767ac4f383 Mon Sep 17 00:00:00 2001 From: Jonathan Cammisuli Date: Tue, 7 Nov 2023 13:51:42 -0500 Subject: [PATCH] fix(core): always load nxignore last for highest priority --- packages/nx/src/native/tests/watcher.spec.ts | 124 +++++++++++-------- packages/nx/src/native/watch/utils.rs | 35 +++--- packages/nx/src/native/watch/watch_config.rs | 12 +- 3 files changed, 102 insertions(+), 69 deletions(-) diff --git a/packages/nx/src/native/tests/watcher.spec.ts b/packages/nx/src/native/tests/watcher.spec.ts index f4065b1dfefdd..07a0c515176b5 100644 --- a/packages/nx/src/native/tests/watcher.spec.ts +++ b/packages/nx/src/native/tests/watcher.spec.ts @@ -27,10 +27,13 @@ describe('watcher', () => { temp.cleanup(); }); - it('should trigger the callback for files that are not ignored', (done) => { - watcher = new Watcher(temp.tempDir); - watcher.watch((error, paths) => { - expect(paths).toMatchInlineSnapshot(` + it('should trigger the callback for files that are not ignored', async () => { + return new Promise(async (done) => { + await wait(); + + watcher = new Watcher(temp.tempDir); + watcher.watch((error, paths) => { + expect(paths).toMatchInlineSnapshot(` [ { "path": "app1/main.html", @@ -38,21 +41,25 @@ describe('watcher', () => { }, ] `); - done(); - }); + done(); + }); - wait().then(() => { + await wait(); temp.createFileSync('node_modules/my-file.json', JSON.stringify({})); + await wait(); temp.createFileSync('app2/main.css', JSON.stringify({})); + await wait(); temp.createFileSync('app1/main.html', JSON.stringify({})); }); - }); + }, 10000); - it('should trigger the callback when files are updated', (done) => { - watcher = new Watcher(temp.tempDir); + it('should trigger the callback when files are updated', async () => { + return new Promise(async (done) => { + await wait(); + watcher = new Watcher(temp.tempDir); - watcher.watch((err, paths) => { - expect(paths).toMatchInlineSnapshot(` + watcher.watch((err, paths) => { + expect(paths).toMatchInlineSnapshot(` [ { "path": "app1/main.js", @@ -60,46 +67,51 @@ describe('watcher', () => { }, ] `); - done(); - }); + done(); + }); - wait(1000).then(() => { + await wait(); // nxignored file should not trigger a callback temp.appendFile('app2/main.js', 'update'); + await wait(); temp.appendFile('app1/main.js', 'update'); }); - }); + }, 10000); - it('should watch file renames', (done) => { - watcher = new Watcher(temp.tempDir); + it('should watch file renames', async () => { + return new Promise(async (done) => { + await wait(); + watcher = new Watcher(temp.tempDir); - watcher.watch((err, paths) => { - expect(paths.length).toBe(2); - expect(paths.find((p) => p.type === 'create')).toMatchInlineSnapshot(` + watcher.watch((err, paths) => { + expect(paths.length).toBe(2); + expect(paths.find((p) => p.type === 'create')).toMatchInlineSnapshot(` { "path": "app1/rename.js", "type": "create", } `); - expect(paths.find((p) => p.type === 'delete')).toMatchInlineSnapshot(` + expect(paths.find((p) => p.type === 'delete')).toMatchInlineSnapshot(` { "path": "app1/main.js", "type": "delete", } `); - done(); - }); + done(); + }); - wait().then(() => { + await wait(); temp.renameFile('app1/main.js', 'app1/rename.js'); }); - }); + }, 10000); - it('should trigger on deletes', (done) => { - watcher = new Watcher(temp.tempDir); + it('should trigger on deletes', async () => { + return new Promise(async (done) => { + await wait(); + watcher = new Watcher(temp.tempDir); - watcher.watch((err, paths) => { - expect(paths).toMatchInlineSnapshot(` + watcher.watch((err, paths) => { + expect(paths).toMatchInlineSnapshot(` [ { "path": "app1/main.js", @@ -107,19 +119,22 @@ describe('watcher', () => { }, ] `); - done(); - }); + done(); + }); - wait().then(() => { + await wait(); temp.removeFileSync('app1/main.js'); }); - }); + }, 10000); + + it('should ignore nested gitignores', async () => { + return new Promise(async (done) => { + await wait(); - it('should ignore nested gitignores', (done) => { - watcher = new Watcher(temp.tempDir); + watcher = new Watcher(temp.tempDir); - watcher.watch((err, paths) => { - expect(paths).toMatchInlineSnapshot(` + watcher.watch((err, paths) => { + expect(paths).toMatchInlineSnapshot(` [ { "path": "boo.txt", @@ -127,30 +142,33 @@ describe('watcher', () => { }, ] `); - done(); - }); + done(); + }); - wait().then(() => { + await wait(); // should not be triggered temp.createFileSync('nested-ignore/hello1.txt', ''); + await wait(); temp.createFileSync('boo.txt', ''); }); - }); - - it('should include files that are negated in nxignore but are ignored in gitignore', (done) => { - watcher = new Watcher(temp.tempDir); - watcher.watch((err, paths) => { - expect(paths.some(({ path }) => path === '.env.local')).toBeTruthy(); - done(); - }); - - wait().then(() => { + }, 10000); + + it('should include files that are negated in nxignore but are ignored in gitignore', async () => { + return new Promise(async (done) => { + await wait(); + watcher = new Watcher(temp.tempDir); + watcher.watch((err, paths) => { + expect(paths.some(({ path }) => path === '.env.local')).toBeTruthy(); + done(); + }); + + await wait(2000); temp.appendFile('.env.local', 'hello'); }); - }); + }, 15000); }); -function wait(timeout = 500) { +function wait(timeout = 1000) { return new Promise((res) => { setTimeout(() => { res(); diff --git a/packages/nx/src/native/watch/utils.rs b/packages/nx/src/native/watch/utils.rs index e511f7e4578c9..8db6d0274b345 100644 --- a/packages/nx/src/native/watch/utils.rs +++ b/packages/nx/src/native/watch/utils.rs @@ -1,10 +1,11 @@ use ignore::WalkBuilder; use ignore_files::IgnoreFile; +use std::path::Path; use std::{fs, path::PathBuf}; use tracing::trace; use watchexec_events::{Event, Tag}; -pub(super) fn get_ignore_files>(root: T) -> Vec { +pub(super) fn get_ignore_files>(root: T) -> (Vec, Option) { let root = root.as_ref(); let mut walker = WalkBuilder::new(root); @@ -13,20 +14,12 @@ pub(super) fn get_ignore_files>(root: T) -> Vec { let node_folder = PathBuf::from(root).join("node_modules"); walker.filter_entry(move |entry| !entry.path().starts_with(&node_folder)); - let mut ignores = walker + let gitignore_files = walker .build() .flatten() - .filter(|result| { - result.path().ends_with(".nxignore") || result.path().ends_with(".gitignore") - }) - .map(|result| result.path().into()) - .collect::>(); - - ignores.sort(); - - ignores - .into_iter() - .map(|path| { + .filter(|result| result.path().ends_with(".gitignore")) + .map(|result| { + let path: PathBuf = result.path().into(); let parent: PathBuf = path.parent().unwrap_or(&path).into(); IgnoreFile { path, @@ -34,7 +27,21 @@ pub(super) fn get_ignore_files>(root: T) -> Vec { applies_to: None, } }) - .collect() + .collect(); + (gitignore_files, get_nx_ignore(root)) +} + +fn get_nx_ignore>(origin: P) -> Option { + let nx_ignore_path = PathBuf::from(origin.as_ref()).join(".nxignore"); + if nx_ignore_path.exists() { + Some(IgnoreFile { + path: nx_ignore_path, + applies_in: Some(origin.as_ref().into()), + applies_to: None, + }) + } else { + None + } } // /// Get only the root level folders to watch. diff --git a/packages/nx/src/native/watch/watch_config.rs b/packages/nx/src/native/watch/watch_config.rs index 7ed93a29af231..25775d1839680 100644 --- a/packages/nx/src/native/watch/watch_config.rs +++ b/packages/nx/src/native/watch/watch_config.rs @@ -11,10 +11,10 @@ pub(super) async fn create_runtime( additional_globs: &[&str], use_ignore: bool, ) -> napi::Result { - let ignore_files = if use_ignore { + let (ignore_files, nx_ignore_file) = if use_ignore { get_ignore_files(origin) } else { - vec![] + (vec![], None) }; trace!( @@ -31,6 +31,14 @@ pub(super) async fn create_runtime( .add_globs(additional_globs, Some(&origin.into())) .map_err(anyhow::Error::from)?; + // always add the .nxignore file after all other ignores are loaded so that it has the highest priority + if let Some(nx_ignore_file) = nx_ignore_file { + filter + .add_file(&nx_ignore_file) + .await + .map_err(anyhow::Error::from)?; + } + let mut runtime = RuntimeConfig::default(); runtime.filterer(Arc::new(WatchFilterer { inner: IgnoreFilterer(filter),