Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions crates/node_binding/napi-binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,13 @@ export declare class NativeWatcher {
*/
close(): Promise<void>
pause(): void
/**
* Collect the full file/context time tables, mirroring watchpack's
* `collectTimeInfoEntries`. Called synchronously from JS after an aggregated
* event (or from `getInfo()`) to populate `compiler.fileTimestamps` /
* `contextTimestamps`.
*/
getTimeInfo(): NativeTimeInfo
}

export declare class NativeWatchResult {
Expand Down Expand Up @@ -1562,6 +1569,16 @@ export interface JsTap {
stage: number
}

/**
* watchpack-style time info for a single path, surfaced to webpack as a
* `FileSystemInfoEntry`. `timestamp` is absent for directory (context)
* entries, whose value is a derived `safe_time` only.
*/
export interface JsTimeInfoEntry {
safeTime: number
timestamp?: number
}

export interface JsVirtualFile {
path: string
content: string
Expand Down Expand Up @@ -1782,6 +1799,15 @@ export interface NapiResolveOptions {
enablePnp?: boolean
}

/**
* The full `fileTimeInfoEntries` / `contextTimeInfoEntries` snapshot, mirroring
* watchpack's `collectTimeInfoEntries` output.
*/
export interface NativeTimeInfo {
fileTimeInfoEntries: Record<string, JsTimeInfoEntry>
contextTimeInfoEntries: Record<string, JsTimeInfoEntry>
}

export interface NativeWatcherOptions {
followSymlinks?: boolean
pollInterval?: number
Expand Down
55 changes: 55 additions & 0 deletions crates/rspack_binding_api/src/native_watcher.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{
boxed::Box,
collections::HashMap,
path::{Path, PathBuf},
time::{Duration, SystemTime, UNIX_EPOCH},
};
Expand Down Expand Up @@ -44,6 +45,23 @@ pub struct NativeWatchResult {
pub removed_files: Vec<String>,
}

/// watchpack-style time info for a single path, surfaced to webpack as a
/// `FileSystemInfoEntry`. `timestamp` is absent for directory (context)
/// entries, whose value is a derived `safe_time` only.
#[napi(object)]
pub struct JsTimeInfoEntry {
pub safe_time: f64,
pub timestamp: Option<f64>,
}

/// The full `fileTimeInfoEntries` / `contextTimeInfoEntries` snapshot, mirroring
/// watchpack's `collectTimeInfoEntries` output.
#[napi(object)]
pub struct NativeTimeInfo {
pub file_time_info_entries: HashMap<String, JsTimeInfoEntry>,
pub context_time_info_entries: HashMap<String, JsTimeInfoEntry>,
}

#[napi]
pub struct NativeWatcher {
watcher: FsWatcher,
Expand Down Expand Up @@ -157,6 +175,43 @@ impl NativeWatcher {

Ok(())
}

/// Collect the full file/context time tables, mirroring watchpack's
/// `collectTimeInfoEntries`. Called synchronously from JS after an aggregated
/// event (or from `getInfo()`) to populate `compiler.fileTimestamps` /
/// `contextTimestamps`.
#[napi]
pub fn get_time_info(&self) -> NativeTimeInfo {
let (files, contexts) = self.watcher.collect_time_info();
let file_time_info_entries = files
.into_iter()
.map(|(path, entry)| {
(
path,
JsTimeInfoEntry {
safe_time: entry.safe_time as f64,
timestamp: Some(entry.timestamp as f64),
},
)
})
.collect();
let context_time_info_entries = contexts
.into_iter()
.map(|(path, safe_time)| {
(
path,
JsTimeInfoEntry {
safe_time: safe_time as f64,
timestamp: None,
},
)
})
.collect();
NativeTimeInfo {
file_time_info_entries,
context_time_info_entries,
}
}
}

fn to_tuple_path_iterator(
Expand Down
27 changes: 26 additions & 1 deletion crates/rspack_watcher/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod executor;
mod ignored;
mod paths;
mod scanner;
mod time_info;
mod trigger;

use std::{sync::Arc, time::SystemTime};
Expand All @@ -17,6 +18,7 @@ use rspack_error::Result;
use rspack_paths::ArcPath;
use rspack_util::fx_hash::FxHashSet as HashSet;
use scanner::Scanner;
pub use time_info::{TimeInfoEntry, TimeInfoTables};
use tokio::sync::mpsc;
use trigger::Trigger;

Expand Down Expand Up @@ -164,6 +166,14 @@ impl FsWatcher {
}
}

/// Collect the full webpack-shaped time tables for all watched files and
/// directories. Mirrors watchpack's `collectTimeInfoEntries`; consumed via
/// the napi `getTimeInfo()` to populate `fileTimeInfoEntries` /
/// `contextTimeInfoEntries`.
pub fn collect_time_info(&self) -> TimeInfoTables {
self.path_manager.collect_time_info()
}

/// Pauses the file system watcher, stopping the execution of the event loop.
pub fn pause(&self) -> Result<()> {
self.executor.pause();
Expand Down Expand Up @@ -224,7 +234,22 @@ impl FsWatcher {
.metadata()
.and_then(|m| m.modified().or_else(|_| m.created()))
{
self.path_manager.set_file_mtime_if_absent(path, mtime);
self
.path_manager
.set_file_mtime_if_absent(path.clone(), mtime);
// Seed the watchpack-style time entry for newly-added files so the
// first `getTimeInfo()` already carries their conservative safe_time.
self.path_manager.set_initial_file_time(path, mtime);
}
}

let dirs: Vec<_> = accessor.directories().1.iter().map(|p| p.clone()).collect();
for path in dirs {
if let Ok(mtime) = path
.metadata()
.and_then(|m| m.modified().or_else(|_| m.created()))
{
self.path_manager.set_initial_dir_time(path, mtime);
}
}
}
Expand Down
Loading
Loading