From 0d3986625b693e0915f927fe06ceb2179e02d377 Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Tue, 4 Feb 2025 05:28:22 -0800 Subject: [PATCH] Cache auto-save 3/3: Expose watcher.unstable_autoSaveCache, default enabled (#1434) Summary: Pull Request resolved: https://github.com/facebook/metro/pull/1434 Configures a new mechanism for the file map cache to re-save after changes made to the file system while Metro is running. Currently, If you make a change while Metro is running, Metro will re-process (hash) that file and update an in-memory record, but won't save it. The next time Metro starts up, it will process that file again, and write the cache at the end of startup. In most cases this provides a modest reduction in startup work/time - proportional to the number of changes in the previous Metro session. However, it unlocks the potential of doing more processing lazily - ie, we don't need to hash everything up front if we can hash only what we need for a bundle, and then save those hashes in a cache. We'll use this to implement lazy hashing. ## Implementation The auto save mechanism is contained within `DiskCacheManager` via generic listening APIs. When the file map emits a `change` event to Metro, we start/restart a configurable debounce timer, and save the cache asynchronously. The 5 second default is chosen so that we don't try to save while Metro may be busy - eg, a change is likely to be followed by an HMR exchange over the next second or two. Changelog: ``` - **[Experimental]**: Auto-save file cache via `config.watcher.unstable_autoSaveCache` ``` Reviewed By: huntie Differential Revision: D69024198 fbshipit-source-id: 1ab06fba567e981bfa59f0e2f045ee3bcb75e3d5 --- .../__snapshots__/loadConfig-test.js.snap | 16 ++++++++++++++++ packages/metro-config/src/configTypes.flow.js | 7 +++++++ packages/metro-config/src/defaults/index.js | 4 ++++ packages/metro-config/src/loadConfig.js | 5 +++++ packages/metro-config/types/configTypes.d.ts | 9 ++++++++- .../node-haste/DependencyGraph/createFileMap.js | 8 +++++++- 6 files changed, 47 insertions(+), 2 deletions(-) diff --git a/packages/metro-config/src/__tests__/__snapshots__/loadConfig-test.js.snap b/packages/metro-config/src/__tests__/__snapshots__/loadConfig-test.js.snap index fa73975b8f..5468e71d7c 100644 --- a/packages/metro-config/src/__tests__/__snapshots__/loadConfig-test.js.snap +++ b/packages/metro-config/src/__tests__/__snapshots__/loadConfig-test.js.snap @@ -173,6 +173,10 @@ Object { "interval": 30000, "timeout": 5000, }, + "unstable_autoSaveCache": Object { + "debounceMs": 5000, + "enabled": true, + }, "unstable_workerThreads": false, "watchman": Object { "deferStates": Array [ @@ -356,6 +360,10 @@ Object { "interval": 30000, "timeout": 5000, }, + "unstable_autoSaveCache": Object { + "debounceMs": 5000, + "enabled": true, + }, "unstable_workerThreads": false, "watchman": Object { "deferStates": Array [ @@ -539,6 +547,10 @@ Object { "interval": 30000, "timeout": 5000, }, + "unstable_autoSaveCache": Object { + "debounceMs": 5000, + "enabled": true, + }, "unstable_workerThreads": false, "watchman": Object { "deferStates": Array [ @@ -722,6 +734,10 @@ Object { "interval": 30000, "timeout": 5000, }, + "unstable_autoSaveCache": Object { + "debounceMs": 5000, + "enabled": true, + }, "unstable_workerThreads": false, "watchman": Object { "deferStates": Array [ diff --git a/packages/metro-config/src/configTypes.flow.js b/packages/metro-config/src/configTypes.flow.js index fa35901df8..3d4fca03cf 100644 --- a/packages/metro-config/src/configTypes.flow.js +++ b/packages/metro-config/src/configTypes.flow.js @@ -201,6 +201,10 @@ type WatcherConfigT = { timeout: number, filePrefix: string, }>, + unstable_autoSaveCache: $ReadOnly<{ + enabled: boolean, + debounceMs?: number, + }>, unstable_workerThreads: boolean, watchman: $ReadOnly<{ deferStates: $ReadOnlyArray, @@ -223,6 +227,9 @@ export type InputConfigT = $ReadOnly< Partial<{ ...WatcherConfigT, healthCheck?: $ReadOnly>, + unstable_autoSaveCache?: $ReadOnly< + Partial, + >, }>, >, }>, diff --git a/packages/metro-config/src/defaults/index.js b/packages/metro-config/src/defaults/index.js index 07192a8273..0a24c21f90 100644 --- a/packages/metro-config/src/defaults/index.js +++ b/packages/metro-config/src/defaults/index.js @@ -148,6 +148,10 @@ const getDefaultValues = (projectRoot: ?string): ConfigT => ({ timeout: 5000, }, unstable_workerThreads: false, + unstable_autoSaveCache: { + enabled: true, + debounceMs: 5000, + }, watchman: { deferStates: ['hg.update'], }, diff --git a/packages/metro-config/src/loadConfig.js b/packages/metro-config/src/loadConfig.js index 2db3874056..cd5a95d343 100644 --- a/packages/metro-config/src/loadConfig.js +++ b/packages/metro-config/src/loadConfig.js @@ -176,6 +176,11 @@ function mergeConfig>( // $FlowFixMe: Spreading shapes creates an explosion of union types ...nextConfig.watcher?.healthCheck, }, + unstable_autoSaveCache: { + // $FlowFixMe[exponential-spread] + ...totalConfig.watcher?.unstable_autoSaveCache, + ...nextConfig.watcher?.unstable_autoSaveCache, + }, }, }), defaultConfig, diff --git a/packages/metro-config/types/configTypes.d.ts b/packages/metro-config/types/configTypes.d.ts index 06a462a73c..15f3676531 100644 --- a/packages/metro-config/types/configTypes.d.ts +++ b/packages/metro-config/types/configTypes.d.ts @@ -200,11 +200,18 @@ export interface WatcherConfigT { timeout: number; filePrefix: string; }; + unstable_autoSaveCache: { + enabled: boolean; + debounceMs?: number; + }; } export interface WatcherInputConfigT - extends Partial> { + extends Partial< + Omit + > { healthCheck?: Partial; + unstable_autoSaveCache?: Partial; } export interface InputConfigT diff --git a/packages/metro/src/node-haste/DependencyGraph/createFileMap.js b/packages/metro/src/node-haste/DependencyGraph/createFileMap.js index 65268abfdb..905c0a8f96 100644 --- a/packages/metro/src/node-haste/DependencyGraph/createFileMap.js +++ b/packages/metro/src/node-haste/DependencyGraph/createFileMap.js @@ -68,6 +68,11 @@ function createFileMap( : config.resolver.dependencyExtractor; const computeDependencies = dependencyExtractor != null; + const watch = options?.watch == null ? !ci.isCI : options.watch; + const {enabled: autoSaveEnabled, ...autoSaveOpts} = + config.watcher.unstable_autoSaveCache ?? {}; + const autoSave = watch && autoSaveEnabled ? autoSaveOpts : false; + return MetroFileMap.create({ cacheManagerFactory: config?.unstable_fileMapCacheManagerFactory ?? @@ -76,6 +81,7 @@ function createFileMap( cacheDirectory: config.fileMapCacheDirectory ?? config.hasteMapCacheDirectory, cacheFilePrefix: options?.cacheFilePrefix, + autoSave, })), perfLoggerFactory: config.unstable_perfLoggerFactory, computeDependencies, @@ -104,7 +110,7 @@ function createFileMap( roots: config.watchFolders, throwOnModuleCollision: options?.throwOnModuleCollision ?? true, useWatchman: config.resolver.useWatchman, - watch: options?.watch == null ? !ci.isCI : options.watch, + watch, watchmanDeferStates: config.watcher.watchman.deferStates, }); }