@@ -49,6 +49,71 @@ yarn warm-feed
4949npm|nuget` , ` --dry-run` , ` --verify` (warm even already-cached targets),
5050` --concurrency <n> ` , ` -v ` / ` --verbose ` . See ` --help ` for the full list.
5151
52+ ## Closure (graph) warming
53+
54+ Enumeration keeps ** known** package lines fresh but can't introduce a ** brand-new**
55+ name the feed has never seen — e.g. the packages a bumped
56+ ` create-react-native-library ` pulls into a generated project. Closure warming
57+ closes that gap: it resolves a dependency ** graph** and warms every version in it.
58+
59+ The graph is resolved with ` npm install --package-lock-only ` (metadata only; the
60+ feed save still happens per version). The lockfile covers the ** resolve host's own
61+ platform** (Windows x64 in CI). It can also list other-OS/CPU optional variants, but
62+ not dependably against an Azure Artifacts upstream proxy: npm 11+ writes
63+ not-yet-cached, non-current-platform optional deps without a ` version `
64+ (npm/cli #9342 ) and the parser drops version-less entries. Warming every platform's
65+ optional deps would need a per-OS/CPU resolve.
66+
67+ Three ways to feed it a graph:
68+
69+ ``` powershell
70+ # 1. Roots — warm the full npm graph of one or more packages:
71+ yarn warm-feed --closure npm:create-react-native-library@0.63.0
72+
73+ # 2. Manifest — warm the external-dependency graph of a package.json:
74+ yarn warm-feed --closure-manifest ./some/package.json
75+
76+ # 3. Special module — a registered reproducer for a case enumeration can't see:
77+ yarn warm-feed --closure-module create-react-native-library
78+ yarn warm-feed --closure-module all # every enabled module
79+ ```
80+
81+ ### Special modules (pluggable)
82+
83+ A ** special module** reproduces a specific install closure and turns it into a
84+ dependency graph to warm. They live in ` src/specialModules/ ` and are registered
85+ in ` src/specialModules/index.ts ` ; add a module there and it's available to config
86+ (` closure.modules.<name> ` ) and ` --closure-module <name> ` .
87+
88+ The first module, ** ` create-react-native-library ` ** , reproduces the CLI-init lib
89+ test: the test scaffolds a library (+ vanilla example app) and installs * that
90+ generated project* , so its closure — not cRNL's own dependencies — is what the
91+ feed needs. warm-feed runs only from ` main ` , but the test runs on every release
92+ branch pinned to a different React Native, so the module's config manifest lists
93+ all branches and, per branch, derives the RN/CLI versions (nightly for ` main `
94+ from the working-tree ` vnext/package.json ` ; for ` 0.NN-stable ` , the exact
95+ ` react-native ` that branch pins in its own ` vnext/package.json ` , read via git),
96+ scaffolds, and reads the generated manifests. Mirrors
97+ ` vnext/Scripts/creaternwlib.cmd ` .
98+
99+ The scheduled pipeline run warms every ** enabled** configured module in addition
100+ to the latest-patch sync, so brand-new closures stay warm automatically.
101+
102+ ### NuGet lock closure
103+
104+ The special modules above are npm-only. NuGet has the same gap — enumeration only
105+ refreshes lines already in the feed — for which the repo's committed
106+ ` packages.lock.json ` files are the source of truth: they pin the full resolved
107+ NuGet closure (incl. transitives) every project restores. warm-feed scans them and
108+ warms every ` name@resolved ` they list, so a brand-new NuGet package (or an exact
109+ non-latest pinned version) restores under isolation.
110+
111+ The scheduled pass does this automatically (unless ` closure.nugetLocks.enabled ` is
112+ ` false ` , or ` --only npm ` ); ` --nuget-locks ` runs just this pass one-off. Scanning is
113+ local (no feed access), so it is included in a ` --dry-run ` plan. By default it
114+ scans the repo root (` --repo-root ` , default cwd), pruning ` node_modules ` ; narrow it
115+ with ` closure.nugetLocks.roots ` .
116+
52117## Pipeline usage
53118
54119` .ado/warm-feed-pipeline.yml ` runs the tool on a schedule (and on manual queue)
@@ -76,13 +141,19 @@ maintainer queues the pipeline with the `packages` parameter
76141| ` expand.maxMajorsBack ` | Limit to the N most-recent majors already in use (0 = no limit). |
77142| ` concurrency ` | Parallel requests. |
78143| ` ignore ` | ` id ` , ` id@version ` , or ` eco:id@version ` entries to skip. |
144+ | ` closure.registry ` | npm registry for closure resolution (defaults to ` feeds.npm.registry ` ). |
145+ | ` closure.modules ` | Per-module config blocks (e.g. ` create-react-native-library ` ), keyed by module name; each may set ` enabled: false ` . |
146+ | ` closure.nugetLocks ` | NuGet ` packages.lock.json ` closure: ` enabled ` (default true) and ` roots ` (repo-relative dirs to scan; default repo root). |
79147
80148## Scope and limitations
81149
82- - Warms ** latest patch per in-use line** , not a specific build's exact
83- lockfile-pinned closure. A build pinning an older patch, or a version whose
84- transitive graph differs, is not guaranteed by this pass alone.
85- - Cannot introduce a ** brand-new package name** the feed has never seen (that name
86- is not in the feed's list). First use is covered by the authenticated CI build
87- that restores it, or by a one-off ` --packages ` warm.
88- - Does not resolve transitive closures (each warmed version is fetched on its own).
150+ - The enumeration pass warms ** latest patch per in-use line** , not a specific
151+ build's exact lockfile-pinned closure. A build pinning an older patch is not
152+ guaranteed by that pass alone — use closure warming for exact graphs.
153+ - Enumeration cannot introduce a ** brand-new package name** the feed has never
154+ seen, nor an exact non-latest pinned version. Covered instead by closure warming:
155+ for ** npm** a special module, ` --closure ` , or ` --closure-manifest ` ; for ** NuGet**
156+ the ` packages.lock.json ` closure (above). The authenticated CI build that first
157+ restores a package, or a one-off ` --packages ` warm, also cover it.
158+ - Closure resolution needs ` npm ` on ` PATH ` (bundled with Node) and, for special
159+ modules that scaffold, network access to the feed for the generator.
0 commit comments