fix(builder): hand replay dedupe to core and watch shared-mapping dirs - #112
Merged
Conversation
Bumps @softarc/native-federation to ^4.4.0, which takes ownership of
replay dedupe and collapses watch handles per directory.
- Delete stale-watch-event-filter. Core's dedupe keys on mtime *and*
byte size and holds a grace window, so it lets through a second save
landing inside one mtime tick; the adapter's mtime-only copy dropped
exactly that event on coarse-granularity filesystems (WSL2 drvfs, NFS,
gRPC-FUSE, HFS+). Keeping both would run the weaker filter second.
- Pass paths to syncNfFileWatcher directly, retiring the { keys() } shim
core widened WatchSources to accept.
- Drop the dirty-buffer refill in onChange: core now buffers every change
unconditionally, including when onChange is set.
- Watch sharedMappingDirs, and wake for them too. A file created since
the last build is in no compiled-inputs watch set, so registering the
watch alone left the change buffered with nothing to consume it — the
file is not imported yet, so Angular's iterator never emits for it.
- Clear federationFresh beside advanceAngular, so the failed-build and
first-output branches can no longer leave it set for an unrelated
Angular output.
- Extract the trigger decisions into watch-decisions.ts and cover the
wake-up and rebuild-skip behaviour with specs.
federation-source-files.ts stays: core owns replay dedupe, not the
Angular cache expansion that encodes which compilation path recorded
the tracked files.
The wake-up predicate spliced in `path.sep`, but core delivers watch paths through `toPosix`, so no wake dir could ever match on Windows. Pre-existing for `linkedDirs`; Item 5 routed `sharedMappingDirs` through the same comparison, which would have shipped new-file pickup broken there. Core already exports the predicate for this — `isUnderAnyDir` normalizes both sides, and its docstring exists to stop exactly this splice. The `watchedFiles` lookup keeps `path.normalize`, which does resolve a delivered posix path on Windows. Covered by specs that pass backslash dirs, so the regression reproduces off Windows.
Brings in #118, which re-applies the NG_BUILD_* settings onto an already-loaded @angular/build. Both conflicts were the same import block, with a deletion on each side: main removed describeFederationCache, this branch removed createStaleWatchEventFilter. Resolved to honour both, leaving only federationSourceFiles. #118 dropped the NG_BUILD_PARALLEL_TS verbose log from build/builder.ts but not from remote/builder.ts, where its comment then pointed at two deleted symbols. Removed here. No work item on this branch is invalidated: #118 governs env-var timing, these govern the watcher. It does change which TS compilation path an Nx workspace takes, so the end-to-end verification needs re-running.
With `sharedMappings` unset core promotes every tsconfig `paths` entry to a mapping, so an entry point near the workspace root makes `sharedMappingDirs` yield a dir containing the federation output. A rebuild writes there with a fresh mtime — not a replay, so core delivers the event — and the wake set turned that write into the next rebuild, forever. Filter the event path against outputPath rather than dropping the dir from wakeDirs: that dir also holds the sources the wake set exists to cover.
A wake dir is the dirname of a mapping's *resolved* entry point, not of a raw tsconfig `paths` entry: wildcards expand per used entry point (`@internal/*` yields libs/internal/src/logging, never libs/internal/src) and ignoreUnusedDeps prunes unused mappings before sharedMappingDirs sees them. The old wording implied a workspace-root wake dir was routine. Record the measurement behind the guard's spec: with the output moved into a mapping dir, one source edit produced 88 rebuilds in 60s and was still climbing, against 1 with the guard on.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps @softarc/native-federation to ^4.4.0, which takes ownership of replay dedupe and collapses watch handles per directory.
federation-source-files.ts stays: core owns replay dedupe, not the Angular cache expansion that encodes which compilation path recorded the tracked files.
Verified on the nx playground
nx serve mfe1, whose@internal/*mapping is a real shared lib, against a local build of this branch with core 4.4.0.console-logger.service.tsreacheddist/mfe1/browser/_internal_logging.jsin ~1s.--outputPath=libs/internal/src/logging/__out), one source edit produced 14 → 29 → 44 → 66 → 88 rebuilds over 60s and was still climbing at ~1.5/s. With the guard on and nothing else changed: 1 rebuild, then idle. The same 49 output-file events are delivered either way; the guard is what stops them waking the loop.How narrow that last precondition is, since the trigger is easy to overstate: wildcard mappings resolve per used entry point (
@internal/*yieldslibs/internal/src/logging, neverlibs/internal/src), andignoreUnusedDeps— on by default — prunes unused mappings before sharedMappingDirs sees them. It takes a used, non-wildcard mapping whose entry-point directory contains the build output, e.g.'@shared': ['src/index.ts']in a project that also emits undersrc. Rare, but it pins a core until the dev server is killed.Two things this deliberately does not close. The remote builder has no wake set — every buffered path rebuilds — so it keeps the same output-directory exposure the guard above closes on the build side; that predates this PR and wants its own issue. And the sharedMappingDirs → addPaths wiring is covered only by the pure predicate's specs, not end to end in either builder.