fix(core): correctly output created and update events for the watcher on macos (#18186)

Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
This commit is contained in:
Jonathan Cammisuli 2023-07-19 18:05:52 -04:00 committed by GitHub
parent 28df2057fd
commit fb0f1f0ec8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 33 deletions

View File

@ -27,7 +27,7 @@ describe('watcher', () => {
});
it('should trigger the callback for files that are not ignored', (done) => {
watcher = new Watcher(realpathSync(temp.tempDir));
watcher = new Watcher(temp.tempDir);
watcher.watch((error, paths) => {
expect(paths).toMatchInlineSnapshot(`
[
@ -48,7 +48,7 @@ describe('watcher', () => {
});
it('should trigger the callback when files are updated', (done) => {
watcher = new Watcher(realpathSync(temp.tempDir));
watcher = new Watcher(temp.tempDir);
watcher.watch((err, paths) => {
expect(paths).toMatchInlineSnapshot(`
@ -62,7 +62,7 @@ describe('watcher', () => {
done();
});
wait().then(() => {
wait(1000).then(() => {
// nxignored file should not trigger a callback
temp.appendFile('app2/main.js', 'update');
temp.appendFile('app1/main.js', 'update');
@ -70,18 +70,22 @@ describe('watcher', () => {
});
it('should watch file renames', (done) => {
watcher = new Watcher(realpathSync(temp.tempDir));
watcher = new Watcher(temp.tempDir);
watcher.watch((err, paths) => {
expect(paths.length).toBe(2);
expect(paths.find((p) => p.type === 'update')).toMatchObject({
path: 'app1/rename.js',
type: 'update',
});
expect(paths.find((p) => p.type === 'delete')).toMatchObject({
path: 'app1/main.js',
type: 'delete',
});
expect(paths.find((p) => p.type === 'create')).toMatchInlineSnapshot(`
{
"path": "app1/rename.js",
"type": "create",
}
`);
expect(paths.find((p) => p.type === 'delete')).toMatchInlineSnapshot(`
{
"path": "app1/main.js",
"type": "delete",
}
`);
done();
});
@ -91,7 +95,7 @@ describe('watcher', () => {
});
it('should trigger on deletes', (done) => {
watcher = new Watcher(realpathSync(temp.tempDir));
watcher = new Watcher(temp.tempDir);
watcher.watch((err, paths) => {
expect(paths).toMatchInlineSnapshot(`
@ -111,7 +115,7 @@ describe('watcher', () => {
});
it('should ignore nested gitignores', (done) => {
watcher = new Watcher(realpathSync(temp.tempDir));
watcher = new Watcher(temp.tempDir);
watcher.watch((err, paths) => {
expect(paths).toMatchInlineSnapshot(`
@ -133,10 +137,10 @@ describe('watcher', () => {
});
});
function wait() {
function wait(timeout = 500) {
return new Promise<void>((res) => {
setTimeout(() => {
res();
}, 500);
}, timeout);
});
}

View File

@ -1,7 +1,9 @@
use napi::bindgen_prelude::*;
use std::path::PathBuf;
use tracing::trace;
use watchexec_events::filekind::FileEventKind;
use watchexec_events::filekind::ModifyKind::Name;
use watchexec_events::filekind::RenameMode;
use watchexec_events::{Event, Tag};
#[napi(string_enum)]
@ -67,12 +69,38 @@ impl From<&Event> for WatchEventInternal {
let event_type = if matches!(path.1, None) && !path_ref.exists() {
EventType::delete
} else {
#[cfg(target_os = "macos")]
{
use std::fs;
use std::os::macos::fs::MetadataExt;
let t = fs::metadata(path_ref).expect("metadata should be available");
let modified_time = t.st_mtime();
let birth_time = t.st_birthtime();
// if a file is created and updated near the same time, we always get a create event
// so we need to check the timestamps to see if it was created or updated
// if the modified time is the same as birth_time then it was created
if modified_time == birth_time {
EventType::create
} else {
EventType::update
}
}
#[cfg(not(target_os = "macos"))]
{
use watchexec_events::filekind::FileEventKind;
match event_kind {
FileEventKind::Create(_) => EventType::create,
FileEventKind::Modify(Name(RenameMode::To)) => EventType::create,
FileEventKind::Modify(Name(RenameMode::From)) => EventType::delete,
FileEventKind::Modify(_) => EventType::update,
FileEventKind::Remove(_) => EventType::delete,
_ => EventType::update,
}
}
};
trace!(?path, ?event_kind, ?event_type, "event kind -> event type");

View File

@ -2,7 +2,6 @@ use crate::native::watch::utils::get_ignore_files;
use crate::native::watch::watch_filterer::WatchFilterer;
use ignore_files::IgnoreFilter;
use std::sync::Arc;
use std::time::Duration;
use tracing::trace;
use watchexec::config::RuntimeConfig;
use watchexec_filterer_ignore::IgnoreFilterer;

View File

@ -5,7 +5,6 @@ use std::path::MAIN_SEPARATOR;
use std::sync::Arc;
use crate::native::watch::types::{EventType, WatchEvent, WatchEventInternal};
use itertools::Itertools;
use napi::bindgen_prelude::*;
use napi::threadsafe_function::{
ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,

View File

@ -1,17 +1,17 @@
import { basename, dirname, join } from 'path';
import { dirname, join } from 'path';
import { tmpdir } from 'os';
import {
mkdtempSync,
readFile,
outputFile,
rmSync,
emptyDirSync,
outputFileSync,
unlinkSync,
mkdirpSync,
mkdtempSync,
outputFile,
readFile,
realpathSync,
rmSync,
unlinkSync,
} from 'fs-extra';
import { joinPathFragments } from '../path';
import { appendFileSync, writeFileSync, renameSync, existsSync } from 'fs';
import { appendFileSync, existsSync, renameSync, writeFileSync } from 'fs';
type NestedFiles = {
[fileName: string]: string;
@ -20,7 +20,7 @@ type NestedFiles = {
export class TempFs {
readonly tempDir: string;
constructor(private dirname: string, overrideWorkspaceRoot = true) {
this.tempDir = mkdtempSync(join(tmpdir(), this.dirname));
this.tempDir = realpathSync(mkdtempSync(join(tmpdir(), this.dirname)));
if (overrideWorkspaceRoot) {
process.env.NX_WORKSPACE_ROOT_PATH = this.tempDir;
}