Skip to content

Commit

Permalink
Extract more mock handling into MockMap
Browse files Browse the repository at this point in the history
  • Loading branch information
robhogan committed Dec 10, 2024
1 parent d8a09d5 commit 3125d6d
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 50 deletions.
71 changes: 22 additions & 49 deletions packages/metro-file-map/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,14 @@ import type {
Path,
PerfLogger,
PerfLoggerFactory,
RawMockMap,
ReadOnlyRawMockMap,
WatchmanClocks,
WorkerMetadata,
} from './flow-types';
import type {IJestWorker} from 'jest-worker';

import {DiskCacheManager} from './cache/DiskCacheManager';
import H from './constants';
import getMockName from './getMockName';
import checkWatchmanCapabilities from './lib/checkWatchmanCapabilities';
import {DuplicateError} from './lib/DuplicateError';
import MockMapImpl from './lib/MockMap';
import MutableHasteMap from './lib/MutableHasteMap';
import normalizePathSeparatorsToSystem from './lib/normalizePathSeparatorsToSystem';
Expand Down Expand Up @@ -380,13 +376,20 @@ export default class FileMap extends EventEmitter {
this._constructHasteMap(fileSystem),
]);

const mockMap = new MockMapImpl({
console: this._console,
rawMockMap: mocks,
rootDir,
throwOnModuleCollision: this._options.throwOnModuleCollision,
});

// Update `fileSystem`, `hasteMap` and `mocks` based on the file delta.
await this._applyFileDelta(fileSystem, hasteMap, mocks, fileDelta);
await this._applyFileDelta(fileSystem, hasteMap, mockMap, fileDelta);

await this._takeSnapshotAndPersist(
fileSystem,
fileDelta.clocks ?? new Map(),
mocks,
mockMap,
fileDelta.changedFiles,
fileDelta.removedFiles,
);
Expand All @@ -396,11 +399,11 @@ export default class FileMap extends EventEmitter {
fileDelta.removedFiles.size,
);

await this._watch(fileSystem, hasteMap, mocks);
await this._watch(fileSystem, hasteMap, mockMap);
return {
fileSystem,
hasteMap,
mockMap: new MockMapImpl({rootDir, rawMockMap: mocks}),
mockMap,
};
})();
}
Expand Down Expand Up @@ -521,7 +524,7 @@ export default class FileMap extends EventEmitter {
*/
_processFile(
hasteMap: MutableHasteMap,
mockMap: RawMockMap,
mockMap: MockMapImpl,
filePath: Path,
fileMetadata: FileMetaData,
workerOptions?: {forceInBand?: ?boolean, perfLogger?: ?PerfLogger},
Expand All @@ -542,8 +545,6 @@ export default class FileMap extends EventEmitter {

const rootDir = this._options.rootDir;

const relativeFilePath = this._pathUtils.absoluteToNormal(filePath);

const computeSha1 =
this._options.computeSha1 && fileMetadata[H.SHA1] == null;

Expand Down Expand Up @@ -611,33 +612,7 @@ export default class FileMap extends EventEmitter {
this._options.mocksPattern &&
this._options.mocksPattern.test(filePath)
) {
const mockPath = getMockName(filePath);
const existingMockPath = mockMap.get(mockPath);

if (existingMockPath != null) {
const secondMockPath = this._pathUtils.absoluteToNormal(filePath);
if (existingMockPath !== secondMockPath) {
const method = this._options.throwOnModuleCollision
? 'error'
: 'warn';

this._console[method](
[
'metro-file-map: duplicate manual mock found: ' + mockPath,
' The following files share their name; please delete one of them:',
' * <rootDir>' + path.sep + existingMockPath,
' * <rootDir>' + path.sep + secondMockPath,
'',
].join('\n'),
);

if (this._options.throwOnModuleCollision) {
throw new DuplicateError(existingMockPath, secondMockPath);
}
}
}

mockMap.set(mockPath, relativeFilePath);
mockMap.addMockModule(filePath);
}

return this._getWorker(workerOptions)
Expand All @@ -656,7 +631,7 @@ export default class FileMap extends EventEmitter {
async _applyFileDelta(
fileSystem: MutableFileSystem,
hasteMap: MutableHasteMap,
mockMap: RawMockMap,
mockMap: MockMapImpl,
delta: $ReadOnly<{
changedFiles: FileData,
removedFiles: $ReadOnlySet<CanonicalPath>,
Expand Down Expand Up @@ -758,7 +733,7 @@ export default class FileMap extends EventEmitter {
async _takeSnapshotAndPersist(
fileSystem: FileSystem,
clocks: WatchmanClocks,
mockMap: ReadOnlyRawMockMap,
mockMap: MockMapImpl,
changed: FileData,
removed: Set<CanonicalPath>,
) {
Expand All @@ -767,7 +742,7 @@ export default class FileMap extends EventEmitter {
{
fileSystemData: fileSystem.getSerializableSnapshot(),
clocks: new Map(clocks),
mocks: new Map(mockMap),
mocks: mockMap.getSerializableSnapshot(),
},
{changed, removed},
);
Expand Down Expand Up @@ -809,7 +784,7 @@ export default class FileMap extends EventEmitter {
_removeIfExists(
fileSystem: MutableFileSystem,
hasteMap: MutableHasteMap,
mockMap: RawMockMap,
mockMap: MockMapImpl,
relativeFilePath: Path,
) {
const fileMetadata = fileSystem.remove(relativeFilePath);
Expand All @@ -824,16 +799,13 @@ export default class FileMap extends EventEmitter {
hasteMap.removeModule(moduleName, relativeFilePath);

if (this._options.mocksPattern) {
const absoluteFilePath = path.join(
this._options.rootDir,
normalizePathSeparatorsToSystem(relativeFilePath),
);
const absoluteFilePath =
this._pathUtils.normalToAbsolute(relativeFilePath);
if (
this._options.mocksPattern &&
this._options.mocksPattern.test(absoluteFilePath)
) {
const mockName = getMockName(absoluteFilePath);
mockMap.delete(mockName);
mockMap.deleteMockModule(absoluteFilePath);
}
}
}
Expand All @@ -844,7 +816,7 @@ export default class FileMap extends EventEmitter {
async _watch(
fileSystem: MutableFileSystem,
hasteMap: MutableHasteMap,
mockMap: RawMockMap,
mockMap: MockMapImpl,
): Promise<void> {
this._startupPerfLogger?.point('watch_start');
if (!this._options.watch) {
Expand All @@ -856,6 +828,7 @@ export default class FileMap extends EventEmitter {
// all files, even changes to node_modules.
this._options.throwOnModuleCollision = false;
hasteMap.setThrowOnModuleCollision(false);
mockMap.setThrowOnModuleCollision(false);
this._options.retainAllFiles = true;

const hasWatchedExtension = (filePath: string) =>
Expand Down
60 changes: 59 additions & 1 deletion packages/metro-file-map/src/lib/MockMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,79 @@

import type {MockMap as IMockMap, Path, RawMockMap} from '../flow-types';

import getMockName from '../getMockName';
import {DuplicateError} from './DuplicateError';
import {RootPathUtils} from './RootPathUtils';
import path from 'path';

export default class MockMap implements IMockMap {
+#raw: RawMockMap;
+#rootDir: Path;
+#pathUtils: RootPathUtils;
+#console: typeof console;
#throwOnModuleCollision: boolean;

constructor({rawMockMap, rootDir}: {rawMockMap: RawMockMap, rootDir: Path}) {
constructor({
console,
rawMockMap,
rootDir,
throwOnModuleCollision,
}: {
console: typeof console,
rawMockMap: RawMockMap,
rootDir: Path,
throwOnModuleCollision: boolean,
}) {
this.#raw = rawMockMap;
this.#rootDir = rootDir;
this.#console = console;
this.#pathUtils = new RootPathUtils(rootDir);
this.#throwOnModuleCollision = throwOnModuleCollision;
}

getMockModule(name: string): ?Path {
const mockPath = this.#raw.get(name) || this.#raw.get(name + '/index');
return mockPath != null ? this.#pathUtils.normalToAbsolute(mockPath) : null;
}

addMockModule(absoluteFilePath: Path): void {
const mockName = getMockName(absoluteFilePath);
const existingMockPath = this.#raw.get(mockName);
const newMockPath = this.#pathUtils.absoluteToNormal(absoluteFilePath);

if (existingMockPath != null) {
if (existingMockPath !== newMockPath) {
const method = this.#throwOnModuleCollision ? 'error' : 'warn';

this.#console[method](
[
'metro-file-map: duplicate manual mock found: ' + mockName,
' The following files share their name; please delete one of them:',
' * <rootDir>' + path.sep + existingMockPath,
' * <rootDir>' + path.sep + newMockPath,
'',
].join('\n'),
);

if (this.#throwOnModuleCollision) {
throw new DuplicateError(existingMockPath, newMockPath);
}
}
}

this.#raw.set(mockName, newMockPath);
}

deleteMockModule(absoluteFilePath: Path): void {
const mockName = getMockName(absoluteFilePath);
this.#raw.delete(mockName);
}

setThrowOnModuleCollision(throwOnModuleCollision: boolean): void {
this.#throwOnModuleCollision = throwOnModuleCollision;
}

getSerializableSnapshot(): RawMockMap {
return new Map(this.#raw);
}
}

0 comments on commit 3125d6d

Please sign in to comment.