Skip to content

Commit

Permalink
Custom haste map module config
Browse files Browse the repository at this point in the history
  • Loading branch information
DiZy committed May 10, 2021
1 parent b7c968a commit b30f4db
Show file tree
Hide file tree
Showing 15 changed files with 274 additions and 14 deletions.
30 changes: 30 additions & 0 deletions e2e/__tests__/customHaste.test.ts
@@ -0,0 +1,30 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import * as path from 'path';
import runJest from '../runJest';

describe('Custom Haste Integration', () => {
test('valid test with fake module resolutions', () => {
const config = {
haste: {
hasteMapModulePath: path.resolve(
__dirname,
'..',
'custom-haste-map/hasteMap.js',
),
},
};

const {exitCode} = runJest('custom-haste-map', [
'--config',
JSON.stringify(config),
'hasteExample.test.js',
]);
expect(exitCode).toBe(0);
});
});
18 changes: 18 additions & 0 deletions e2e/custom-haste-map/__tests__/hasteExample.test.js
@@ -0,0 +1,18 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

'use strict';

const add = require('fakeModuleName');

describe('Custom Haste', () => {
test('adds ok', () => {
expect(true).toBe(true);
expect(add(1, 2)).toBe(3);
});
});
5 changes: 5 additions & 0 deletions e2e/custom-haste-map/__tests__/hasteExampleHelper.js
@@ -0,0 +1,5 @@
function add(a, b) {
return a + b;
}

module.exports = add;
139 changes: 139 additions & 0 deletions e2e/custom-haste-map/hasteMap.js
@@ -0,0 +1,139 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

const path = require('path');
const fakeFile = {
file: path.resolve(__dirname, '__tests__/hasteExampleHelper.js'),
moduleName: 'fakeModuleName',
sha1: 'fakeSha1',
};

const fakeJSON = 'fakeJSON';

const testPath = path.resolve(__dirname, '__tests__/hasteExample.test.js');

const allFiles = [fakeFile.file, testPath];

class HasteFS {
getModuleName(file) {
if (file === fakeFile.file) {
return fakeFile.moduleName;
}
return null;
}

getSize(file) {
return null;
}

getDependencies(file) {
if (file === testPath) {
return fakeFile.file;
}
return [];
}

getSha1(file) {
if (file === fakeFile.file) {
return fakeFile.sha1;
}
return null;
}

exists(file) {
return allFiles.includes(file);
}

getAllFiles() {
return allFiles;
}

getFileIterator() {
return allFiles;
}

getAbsoluteFileIterator() {
return allFiles;
}

matchFiles(pattern) {
if (!(pattern instanceof RegExp)) {
pattern = new RegExp(pattern);
}
const files = [];
for (const file of this.getAbsoluteFileIterator()) {
if (pattern.test(file)) {
files.push(file);
}
}
return files;
}

matchFilesWithGlob(globs, root) {
return [];
}
}

class ModuleMap {
getModule(name, platform, supportsNativePlatform, type) {
if (name === fakeFile.moduleName) {
return fakeFile.file;
}
return null;
}

getPackage() {
return null;
}

getMockModule() {
return undefined;
}

getRawModuleMap() {
return {};
}

toJSON() {
return fakeJSON;
}
}

class HasteMap {
constructor(options) {
this._cachePath = HasteMap.getCacheFilePath(
options.cacheDirectory,
options.name,
);
}

async build() {
return {
hasteFS: new HasteFS(),
moduleMap: new ModuleMap(),
};
}

static getCacheFilePath(tmpdir, name) {
return path.join(tmpdir, name);
}

getCacheFilePath() {
return this._cachePath;
}

static getModuleMapFromJSON(json) {
if (json === fakeJSON) {
return new ModuleMap();
}
throw new Error('Failed to parse serialized module map');
}
}

module.exports = HasteMap;
7 changes: 7 additions & 0 deletions e2e/custom-haste-map/package.json
@@ -0,0 +1,7 @@
{
"jest": {
"haste": {
"hasteMapModulePath": "<rootDir>/hasteMap.js"
}
}
}
1 change: 1 addition & 0 deletions packages/jest-config/src/ValidConfig.ts
Expand Up @@ -58,6 +58,7 @@ const initialOptions: Config.InitialOptions = {
enableSymlinks: false,
forceNodeFilesystemAPI: false,
hasteImplModulePath: '<rootDir>/haste_impl.js',
hasteMapModulePath: '',
platforms: ['ios', 'android'],
throwOnModuleCollision: false,
},
Expand Down
23 changes: 22 additions & 1 deletion packages/jest-haste-map/src/ModuleMap.ts
Expand Up @@ -29,7 +29,28 @@ export type SerializableModuleMap = {
rootDir: Config.Path;
};

export default class ModuleMap {
export interface IModuleMap<S = SerializableModuleMap> {
getModule(
name: string,
platform?: string | null,
supportsNativePlatform?: boolean | null,
type?: HTypeValue | null,
): Config.Path | null;

getPackage(
name: string,
platform: string | null | undefined,
_supportsNativePlatform: boolean | null,
): Config.Path | null;

getMockModule(name: string): Config.Path | undefined;

getRawModuleMap(): RawModuleMap;

toJSON(): S;
}

export default class ModuleMap implements IModuleMap<SerializableModuleMap> {
static DuplicateHasteCandidatesError: typeof DuplicateHasteCandidatesError;
private readonly _raw: RawModuleMap;
private json: SerializableModuleMap | undefined;
Expand Down
16 changes: 15 additions & 1 deletion packages/jest-haste-map/src/index.ts
Expand Up @@ -18,7 +18,7 @@ import {escapePathForRegex} from 'jest-regex-util';
import serializer from 'jest-serializer';
import {Worker} from 'jest-worker';
import HasteFS from './HasteFS';
import HasteModuleMap from './ModuleMap';
import HasteModuleMap, {IModuleMap, SerializableModuleMap} from './ModuleMap';
import H from './constants';
import nodeCrawl = require('./crawlers/node');
import watchmanCrawl = require('./crawlers/watchman');
Expand Down Expand Up @@ -132,6 +132,16 @@ function invariant(condition: unknown, message?: string): asserts condition {
}
}

export type HasteMapStatic<S = SerializableModuleMap> = {
new (options: Options): HasteMap;
getCacheFilePath(
tmpdir: Config.Path,
name: string,
...extra: Array<string>
): string;
getModuleMapFromJSON(json: S): IModuleMap<S>;
};

/**
* HasteMap is a JavaScript implementation of Facebook's haste module system.
*
Expand Down Expand Up @@ -324,6 +334,10 @@ export default class HasteMap extends EventEmitter {
);
}

static getModuleMapFromJSON(json: SerializableModuleMap): HasteModuleMap {
return HasteModuleMap.fromJSON(json);
}

getCacheFilePath(): string {
return this._cachePath;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/jest-haste-map/src/types.ts
Expand Up @@ -8,7 +8,10 @@
import type {Stats} from 'graceful-fs';
import type {Config} from '@jest/types';
import type HasteFS from './HasteFS';
// eslint-disable-next-line import/no-duplicates
import type ModuleMap from './ModuleMap';
// eslint-disable-next-line import/no-duplicates
import type {IModuleMap} from './ModuleMap';

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

Expand Down Expand Up @@ -71,6 +74,12 @@ export type InternalHasteMap = {
mocks: MockData;
};

export type IHasteMap = {
hasteFS: HasteFS;
moduleMap: IModuleMap;
__hasteMapForTest?: InternalHasteMap | null;
};

export type HasteMap = {
hasteFS: HasteFS;
moduleMap: ModuleMap;
Expand Down
6 changes: 3 additions & 3 deletions packages/jest-resolve/src/index.ts
Expand Up @@ -9,9 +9,9 @@

import * as path from 'path';
import chalk = require('chalk');
import type {IModuleMap} from 'jest-haste-map/src/ModuleMap';
import slash = require('slash');
import type {Config} from '@jest/types';
import type {ModuleMap} from 'jest-haste-map';
import {tryRealpath} from 'jest-util';
import ModuleNotFoundError from './ModuleNotFoundError';
import defaultResolver, {clearDefaultResolverCache} from './defaultResolver';
Expand Down Expand Up @@ -50,13 +50,13 @@ const nodePaths = NODE_PATH

export default class Resolver {
private readonly _options: ResolverConfig;
private readonly _moduleMap: ModuleMap;
private readonly _moduleMap: IModuleMap;
private readonly _moduleIDCache: Map<string, string>;
private readonly _moduleNameCache: Map<string, Config.Path>;
private readonly _modulePathCache: Map<string, Array<Config.Path>>;
private readonly _supportsNativePlatform: boolean;

constructor(moduleMap: ModuleMap, options: ResolverConfig) {
constructor(moduleMap: IModuleMap, options: ResolverConfig) {
this._options = {
defaultPlatform: options.defaultPlatform,
extensions: options.extensions,
Expand Down
7 changes: 5 additions & 2 deletions packages/jest-runner/src/testWorker.ts
Expand Up @@ -9,7 +9,7 @@
import exit = require('exit');
import type {SerializableError, TestResult} from '@jest/test-result';
import type {Config} from '@jest/types';
import {ModuleMap, SerializableModuleMap} from 'jest-haste-map';
import HasteMap, {HasteMapStatic, SerializableModuleMap} from 'jest-haste-map';
import {separateMessageFromStack} from 'jest-message-util';
import type Resolver from 'jest-resolve';
import Runtime from 'jest-runtime';
Expand Down Expand Up @@ -74,7 +74,10 @@ export function setup(setupData: {
config,
serializableModuleMap,
} of setupData.serializableResolvers) {
const moduleMap = ModuleMap.fromJSON(serializableModuleMap);
const HasteMapClass = config.haste.hasteMapModulePath
? (require(config.haste.hasteMapModulePath) as HasteMapStatic)
: HasteMap;
const moduleMap = HasteMapClass.getModuleMapFromJSON(serializableModuleMap);
resolvers.set(config.name, Runtime.createResolver(config, moduleMap));
}
}
Expand Down
11 changes: 8 additions & 3 deletions packages/jest-runtime/src/index.ts
Expand Up @@ -21,6 +21,7 @@ import {
import {parse as parseCjs} from 'cjs-module-lexer';
import {CoverageInstrumenter, V8Coverage} from 'collect-v8-coverage';
import * as fs from 'graceful-fs';
import type {IModuleMap} from 'jest-haste-map/src/ModuleMap';
import stripBOM = require('strip-bom');
import type {
Jest,
Expand All @@ -42,7 +43,7 @@ import {
shouldInstrument,
} from '@jest/transform';
import type {Config, Global} from '@jest/types';
import HasteMap, {ModuleMap} from 'jest-haste-map';
import HasteMap, {HasteMapStatic} from 'jest-haste-map';
import {formatStackTrace, separateMessageFromStack} from 'jest-message-util';
import type {MockFunctionMetadata, ModuleMocker} from 'jest-mock';
import {escapePathForRegex} from 'jest-regex-util';
Expand Down Expand Up @@ -314,7 +315,11 @@ export default class Runtime {
? new RegExp(ignorePatternParts.join('|'))
: undefined;

return new HasteMap({
const HasteMapClass = config.haste.hasteMapModulePath
? (require(config.haste.hasteMapModulePath) as HasteMapStatic)
: HasteMap;

return new HasteMapClass({
cacheDirectory: config.cacheDirectory,
computeSha1: config.haste.computeSha1,
console: options?.console,
Expand All @@ -340,7 +345,7 @@ export default class Runtime {

static createResolver(
config: Config.ProjectConfig,
moduleMap: ModuleMap,
moduleMap: IModuleMap,
): Resolver {
return new Resolver(moduleMap, {
defaultPlatform: config.haste.defaultPlatform,
Expand Down

0 comments on commit b30f4db

Please sign in to comment.