Skip to content

Commit

Permalink
fix(core): correctly output created and update events for the watcher…
Browse files Browse the repository at this point in the history
… on macos (#18186)

Co-authored-by: FrozenPandaz <[email protected]>
(cherry picked from commit fb0f1f0)
  • Loading branch information
Cammisuli authored and FrozenPandaz committed Jul 20, 2023
1 parent bd02995 commit 6b6038c
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 31 deletions.
36 changes: 20 additions & 16 deletions packages/nx/src/native/tests/watcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(`
[
Expand All @@ -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(`
Expand All @@ -62,26 +62,30 @@ 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');
});
});

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();
});

Expand All @@ -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(`
Expand All @@ -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(`
Expand All @@ -133,10 +137,10 @@ describe('watcher', () => {
});
});

function wait() {
function wait(timeout = 500) {
return new Promise<void>((res) => {
setTimeout(() => {
res();
}, 500);
}, timeout);
});
}
40 changes: 34 additions & 6 deletions packages/nx/src/native/watch/types.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down Expand Up @@ -67,11 +69,37 @@ impl From<&Event> for WatchEventInternal {
let event_type = if matches!(path.1, None) && !path_ref.exists() {
EventType::delete
} else {
match event_kind {
FileEventKind::Create(_) => EventType::create,
FileEventKind::Modify(_) => EventType::update,
FileEventKind::Remove(_) => EventType::delete,
_ => EventType::update,
#[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,
_ => EventType::update,
}
}
};

Expand Down
1 change: 0 additions & 1 deletion packages/nx/src/native/watch/watch_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion packages/nx/src/native/watch/watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 7 additions & 7 deletions packages/nx/src/utils/testing/temp-fs.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { basename, dirname, join } from 'path';
import { dirname, join } from 'path';
import { tmpdir } from 'os';
import {
emptyDirSync,
mkdirpSync,
mkdtempSync,
readFile,
outputFile,
readFile,
realpathSync,
rmSync,
emptyDirSync,
outputFileSync,
unlinkSync,
mkdirpSync,
} 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;
Expand All @@ -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;
}
Expand Down

0 comments on commit 6b6038c

Please sign in to comment.