Skip to content

Commit

Permalink
Separate build config from runtime config, extract cache key generation
Browse files Browse the repository at this point in the history
Summary:
This lays some groundwork for the decoupling of the main `metro-file-map` class from management of serialisation and caching in the next diff.

It attempts to introduce a more explicit distinction between cache-breaking options, ie `BuildParameters`, and other configuration, and extracts (and simplifies) config/root hash generation.

Note that this *does* change the cache key and cache file path in a number of ways, so will result in temporary cache breakage when it lands. This in part to fix an ongoing issue where the cache key changes with each Metro release, regardless of changes to `metro-file-map`.

Reviewed By: motiz88

Differential Revision: D36596534

fbshipit-source-id: ff716cc7bc60f5181549af91b213b1ee29c3bcce
  • Loading branch information
robhogan authored and facebook-github-bot committed Jun 6, 2022
1 parent a0f99e1 commit c7fc436
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 144 deletions.
28 changes: 17 additions & 11 deletions packages/metro-file-map/src/__tests__/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ jest.mock('graceful-fs', () => ({
}),
}));

const cacheFilePath = '/cache-file';
const mockCacheFilePath = '/cache-file';
const object = data => Object.assign(Object.create(null), data);
const createMap = obj => new Map(Object.keys(obj).map(key => [key, obj[key]]));

Expand Down Expand Up @@ -202,7 +202,7 @@ describe('HasteMap', () => {
H = HasteMap.H;

getCacheFilePath = HasteMap.getCacheFilePath;
HasteMap.getCacheFilePath = jest.fn(() => cacheFilePath);
HasteMap.getCacheFilePath = jest.fn(() => mockCacheFilePath);

defaultConfig = {
extensions: ['js', 'json'],
Expand Down Expand Up @@ -234,11 +234,11 @@ describe('HasteMap', () => {
HasteMap = require('../').default;

expect(
HasteMap.getCacheFilePath('/', '@scoped/package', 'random-value'),
HasteMap.getCacheFilePath('/some-dir', 'file-prefix', defaultConfig),
).toMatch(
process.platform === 'win32'
? /^\\-scoped-package-(.*)$/
: /^\/-scoped-package-(.*)$/,
? /^\\some-dir\\file-prefix-(.*)$/
: /^\/some-dir\/file-prefix-(.*)$/,
);
});

Expand Down Expand Up @@ -293,8 +293,14 @@ describe('HasteMap', () => {
it('creates different cache file paths for different projects', () => {
jest.resetModules();
const HasteMap = require('../').default;
const hasteMap1 = new HasteMap({...defaultConfig, name: '@scoped/package'});
const hasteMap2 = new HasteMap({...defaultConfig, name: '-scoped-package'});
const hasteMap1 = new HasteMap({
...defaultConfig,
cacheFilePrefix: 'prefix-a',
});
const hasteMap2 = new HasteMap({
...defaultConfig,
cacheFilePrefix: 'prefix-b',
});
expect(hasteMap1.getCacheFilePath()).not.toBe(hasteMap2.getCacheFilePath());
});

Expand Down Expand Up @@ -878,9 +884,9 @@ describe('HasteMap', () => {
const {__hasteMapForTest: data} = await new HasteMap(defaultConfig).build();
expect(fs.readFileSync.mock.calls.length).toBe(1);
if (require('v8').deserialize) {
expect(fs.readFileSync).toBeCalledWith(cacheFilePath);
expect(fs.readFileSync).toBeCalledWith(mockCacheFilePath);
} else {
expect(fs.readFileSync).toBeCalledWith(cacheFilePath, 'utf8');
expect(fs.readFileSync).toBeCalledWith(mockCacheFilePath, 'utf8');
}
expect(deepNormalize(data.clocks)).toEqual(mockClocks);
expect(deepNormalize(data.files)).toEqual(initialData.files);
Expand Down Expand Up @@ -911,9 +917,9 @@ describe('HasteMap', () => {
expect(fs.readFileSync.mock.calls.length).toBe(2);

if (require('v8').serialize) {
expect(fs.readFileSync).toBeCalledWith(cacheFilePath);
expect(fs.readFileSync).toBeCalledWith(mockCacheFilePath);
} else {
expect(fs.readFileSync).toBeCalledWith(cacheFilePath, 'utf8');
expect(fs.readFileSync).toBeCalledWith(mockCacheFilePath, 'utf8');
}
expect(fs.readFileSync).toBeCalledWith(
path.join('/', 'project', 'fruits', 'Banana.js'),
Expand Down
4 changes: 2 additions & 2 deletions packages/metro-file-map/src/crawlers/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
CrawlerOptions,
FileData,
IgnoreMatcher,
InternalHasteMap,
InternalData,
} from '../flow-types';

import H from '../constants';
Expand Down Expand Up @@ -206,7 +206,7 @@ function findNative(

module.exports = async function nodeCrawl(options: CrawlerOptions): Promise<{
removedFiles: FileData,
hasteMap: InternalHasteMap,
hasteMap: InternalData,
}> {
const {
data,
Expand Down
4 changes: 2 additions & 2 deletions packages/metro-file-map/src/crawlers/watchman.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
CrawlerOptions,
FileData,
FileMetaData,
InternalHasteMap,
InternalData,
Path,
} from '../flow-types';

Expand Down Expand Up @@ -106,7 +106,7 @@ module.exports = async function watchmanCrawl(
): Promise<{
changedFiles?: FileData,
removedFiles: FileData,
hasteMap: InternalHasteMap,
hasteMap: InternalData,
}> {
const fields = ['name', 'exists', 'mtime_ms', 'size'];
const {data, extensions, ignore, rootDir, roots, perfLogger} = options;
Expand Down
35 changes: 27 additions & 8 deletions packages/metro-file-map/src/flow-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,27 @@ import type HasteFS from './HasteFS';
import type ModuleMap from './ModuleMap';
import type {Stats} from 'graceful-fs';

// These inputs affect the internal data collected for a given filesystem
// state, and changes may invalidate a cache.
export type BuildParameters = $ReadOnly<{
computeDependencies: boolean,
computeSha1: boolean,
enableSymlinks: boolean,
extensions: $ReadOnlyArray<string>,
forceNodeFilesystemAPI: boolean,
ignorePattern: RegExp,
mocksPattern: ?RegExp,
platforms: $ReadOnlyArray<string>,
retainAllFiles: boolean,
rootDir: string,
roots: $ReadOnlyArray<string>,
skipPackageJson: boolean,

// Module paths that should export a 'getCacheKey' method
dependencyExtractor: ?string,
hasteImplModulePath: ?string,
}>;

export type ChangeEvent = {
eventsQueue: EventsQueue,
hasteFS: HasteFS,
Expand All @@ -25,7 +46,7 @@ export type Console = typeof global.console;
export type CrawlerOptions = {
computeSha1: boolean,
enableSymlinks: boolean,
data: InternalHasteMap,
data: InternalData,
extensions: $ReadOnlyArray<string>,
forceNodeFilesystemAPI: boolean,
ignore: IgnoreMatcher,
Expand All @@ -46,20 +67,18 @@ export type EventsQueue = Array<{
export type HasteMap = {
hasteFS: HasteFS,
moduleMap: ModuleMap,
__hasteMapForTest?: ?InternalHasteMap,
__hasteMapForTest?: ?InternalData,
};

export type HasteMapStatic<S = SerializableModuleMap> = {
getCacheFilePath(
tmpdir: Path,
name: string,
...extra: $ReadOnlyArray<string>
cacheDirectory: string,
cacheFilePrefix: string,
buildParameters: BuildParameters,
): string,
getModuleMapFromJSON(json: S): IModuleMap<S>,
};

export type HasteRegExp = RegExp | ((str: string) => boolean);

export type HType = {
ID: 0,
MTIME: 1,
Expand All @@ -80,7 +99,7 @@ export type HTypeValue = $Values<HType>;

export type IgnoreMatcher = (item: string) => boolean;

export type InternalHasteMap = {
export type InternalData = {
clocks: WatchmanClocks,
duplicates: DuplicatesIndex,
files: FileData,
Expand Down
Loading

0 comments on commit c7fc436

Please sign in to comment.