Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types(jest-haste-map): Expose a minimal public API to TypeScript #13023

Merged
merged 5 commits into from Sep 10, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/jest-core/src/cli/index.ts
Expand Up @@ -13,7 +13,7 @@ import type {AggregatedResult, TestContext} from '@jest/test-result';
import type {Config} from '@jest/types';
import type {ChangedFilesPromise} from 'jest-changed-files';
import {readConfigs} from 'jest-config';
import type HasteMap from 'jest-haste-map';
import type {IHasteMap} from 'jest-haste-map';
import Runtime from 'jest-runtime';
import {createDirectory, preRunMessage} from 'jest-util';
import {TestWatcher} from 'jest-watcher';
Expand Down Expand Up @@ -236,7 +236,7 @@ const runWatch = async (
hasDeprecationWarnings: boolean,
globalConfig: Config.GlobalConfig,
outputStream: NodeJS.WriteStream,
hasteMapInstances: Array<HasteMap>,
hasteMapInstances: Array<IHasteMap>,
filter?: Filter,
) => {
if (hasDeprecationWarnings) {
Expand Down
4 changes: 2 additions & 2 deletions packages/jest-core/src/lib/createContext.ts
Expand Up @@ -7,12 +7,12 @@

import type {TestContext} from '@jest/test-result';
import type {Config} from '@jest/types';
import type {HasteMapObject} from 'jest-haste-map';
import type {IHasteFS, IModuleMap} from 'jest-haste-map';
import Runtime from 'jest-runtime';

export default function createContext(
config: Config.ProjectConfig,
{hasteFS, moduleMap}: HasteMapObject,
{hasteFS, moduleMap}: {hasteFS: IHasteFS; moduleMap: IModuleMap},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have an inline type here instead of using a named one?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mostly meant, why is this HasteMapObject thing not exported from jest-haste-map?

): TestContext {
return {
config,
Expand Down
50 changes: 22 additions & 28 deletions packages/jest-core/src/watch.ts
Expand Up @@ -12,10 +12,7 @@ import exit = require('exit');
import slash = require('slash');
import type {TestContext} from '@jest/test-result';
import type {Config} from '@jest/types';
import type {
ChangeEvent as HasteChangeEvent,
default as HasteMap,
} from 'jest-haste-map';
import type {IHasteMap as HasteMap} from 'jest-haste-map';
import {formatExecError} from 'jest-message-util';
import {
isInteractive,
Expand Down Expand Up @@ -241,31 +238,28 @@ export default async function watch(
emitFileChange();

hasteMapInstances.forEach((hasteMapInstance, index) => {
hasteMapInstance.on(
'change',
({eventsQueue, hasteFS, moduleMap}: HasteChangeEvent) => {
const validPaths = eventsQueue.filter(({filePath}) =>
isValidPath(globalConfig, filePath),
);
hasteMapInstance.on('change', ({eventsQueue, hasteFS, moduleMap}) => {
const validPaths = eventsQueue.filter(({filePath}) =>
isValidPath(globalConfig, filePath),
);

if (validPaths.length) {
const context = (contexts[index] = createContext(
contexts[index].config,
{hasteFS, moduleMap},
));

activePlugin = null;

searchSources = searchSources.slice();
searchSources[index] = {
context,
searchSource: new SearchSource(context),
};
emitFileChange();
startRun(globalConfig);
}
},
);
if (validPaths.length) {
const context = (contexts[index] = createContext(
contexts[index].config,
{hasteFS, moduleMap},
));

activePlugin = null;

searchSources = searchSources.slice();
searchSources[index] = {
context,
searchSource: new SearchSource(context),
};
emitFileChange();
startRun(globalConfig);
}
});
});

if (!hasExitListener) {
Expand Down
4 changes: 2 additions & 2 deletions packages/jest-haste-map/src/HasteFS.ts
Expand Up @@ -8,9 +8,9 @@
import {globsToMatcher, replacePathSepForGlob} from 'jest-util';
import H from './constants';
import * as fastPath from './lib/fast_path';
import type {FileData} from './types';
import type {FileData, IHasteFS} from './types';

export default class HasteFS {
export default class HasteFS implements IHasteFS {
private readonly _rootDir: string;
private readonly _files: FileData;

Expand Down
2 changes: 1 addition & 1 deletion packages/jest-haste-map/src/ModuleMap.ts
Expand Up @@ -19,7 +19,7 @@ import type {
const EMPTY_OBJ: Record<string, ModuleMetaData> = {};
const EMPTY_MAP = new Map();

export default class ModuleMap implements IModuleMap<SerializableModuleMap> {
export default class ModuleMap implements IModuleMap {
static DuplicateHasteCandidatesError: typeof DuplicateHasteCandidatesError;
private readonly _raw: RawModuleMap;
private json: SerializableModuleMap | undefined;
Expand Down
20 changes: 14 additions & 6 deletions packages/jest-haste-map/src/index.ts
Expand Up @@ -34,6 +34,8 @@ import type {
FileMetaData,
HasteMapStatic,
HasteRegExp,
IHasteMap,
IModuleMap,
InternalHasteMap,
HasteMap as InternalHasteMapObject,
MockData,
Expand Down Expand Up @@ -109,11 +111,12 @@ type Watcher = {

type HasteWorker = typeof import('./worker');

export type {default as FS} from './HasteFS';
export {default as ModuleMap} from './ModuleMap';
export const ModuleMap = HasteModuleMap as {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to cast?

create: (rootPath: string) => IModuleMap;
};
export type {
ChangeEvent,
HasteMap as HasteMapObject,
Comment on lines -115 to -116
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing these exports is a breaking change

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specifically HasteMapObject probably - if people get a newer version of jest-haste-map than @jest/core it might cause compilation issues.

But maybe not - since we bundle our TS types, lots of internal types are never present in d.ts files - https://www.runpkg.com/?@jest/core@29.0.2/build/index.d.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think this is safe, because as you say it's not referenced by the built declaration files there should be no observable change to consumers of jest.

(It's type-breaking to users of jest-haste-map directly of course, but I'm working on the assumption that Jest semver is at a higher level.)

IHasteFS,
IHasteMap,
IModuleMap,
SerializableModuleMap,
} from './types';
Expand Down Expand Up @@ -210,7 +213,7 @@ function invariant(condition: unknown, message?: string): asserts condition {
* Worker processes can directly access the cache through `HasteMap.read()`.
*
*/
export default class HasteMap extends EventEmitter {
class HasteMap extends EventEmitter implements IHasteMap {
private _buildPromise: Promise<InternalHasteMapObject> | null = null;
private _cachePath = '';
private _changeInterval?: ReturnType<typeof setInterval>;
Expand All @@ -227,7 +230,7 @@ export default class HasteMap extends EventEmitter {
return HasteMap;
}

static async create(options: Options): Promise<HasteMap> {
static async create(options: Options): Promise<IHasteMap> {
if (options.hasteMapModulePath) {
const CustomHasteMap = require(options.hasteMapModulePath);
return new CustomHasteMap(options);
Expand Down Expand Up @@ -1143,3 +1146,8 @@ function copy<T extends Record<string, unknown>>(object: T): T {
function copyMap<K, V>(input: Map<K, V>): Map<K, V> {
return new Map(input);
}

export default HasteMap as HasteMapStatic & {
SimenB marked this conversation as resolved.
Show resolved Hide resolved
create(options: Options): Promise<IHasteMap>;
getStatic(config: Config.ProjectConfig): HasteMapStatic;
};
18 changes: 18 additions & 0 deletions packages/jest-haste-map/src/types.ts
Expand Up @@ -39,6 +39,24 @@ export interface IModuleMap<S = SerializableModuleMap> {
toJSON(): S;
}

export interface IHasteFS {
exists(path: string): boolean;
getAbsoluteFileIterator(): Iterable<string>;
getAllFiles(): Array<string>;
getDependencies(file: string): Array<string> | null;
getSize(path: string): number | null;
matchFiles(pattern: RegExp | string): Array<string>;
matchFilesWithGlob(
globs: ReadonlyArray<string>,
root: string | null,
): Set<string>;
}

export interface IHasteMap {
on(eventType: 'change', handler: (event: ChangeEvent) => void): void;
build(): Promise<{hasteFS: IHasteFS; moduleMap: IModuleMap}>;
}

export type HasteMapStatic<S = SerializableModuleMap> = {
getCacheFilePath(
tmpdir: string,
Expand Down
6 changes: 3 additions & 3 deletions packages/jest-resolve-dependencies/src/index.ts
Expand Up @@ -6,7 +6,7 @@
*/

import * as path from 'path';
import type {FS as HasteFS} from 'jest-haste-map';
import type {IHasteFS} from 'jest-haste-map';
import type {ResolveModuleConfig, default as Resolver} from 'jest-resolve';
import {SnapshotResolver, isSnapshotPath} from 'jest-snapshot';

Expand All @@ -20,13 +20,13 @@ export type ResolvedModule = {
* to retrieve a list of all transitive inverse dependencies.
*/
export class DependencyResolver {
private _hasteFS: HasteFS;
private _hasteFS: IHasteFS;
private _resolver: Resolver;
private _snapshotResolver: SnapshotResolver;

constructor(
resolver: Resolver,
hasteFS: HasteFS,
hasteFS: IHasteFS,
snapshotResolver: SnapshotResolver,
) {
this._resolver = resolver;
Expand Down
7 changes: 4 additions & 3 deletions packages/jest-resolve/src/__tests__/resolve.test.ts
Expand Up @@ -10,6 +10,7 @@ import * as path from 'path';
import * as fs from 'graceful-fs';
import {sync as resolveSync} from 'resolve';
import {ModuleMap} from 'jest-haste-map';
import type {IModuleMap} from 'jest-haste-map';
import userResolver from '../__mocks__/userResolver';
import userResolverAsync from '../__mocks__/userResolverAsync';
import defaultResolver from '../defaultResolver';
Expand Down Expand Up @@ -358,7 +359,7 @@ describe('findNodeModuleAsync', () => {
});

describe('resolveModule', () => {
let moduleMap: ModuleMap;
let moduleMap: IModuleMap;
beforeEach(() => {
moduleMap = ModuleMap.create('/');
});
Expand Down Expand Up @@ -463,7 +464,7 @@ describe('resolveModule', () => {
});

describe('resolveModuleAsync', () => {
let moduleMap: ModuleMap;
let moduleMap: IModuleMap;
beforeEach(() => {
moduleMap = ModuleMap.create('/');
});
Expand Down Expand Up @@ -626,7 +627,7 @@ describe('nodeModulesPaths', () => {

describe('Resolver.getModulePaths() -> nodeModulesPaths()', () => {
const _path = path;
let moduleMap: ModuleMap;
let moduleMap: IModuleMap;

beforeEach(() => {
jest.resetModules();
Expand Down
4 changes: 2 additions & 2 deletions packages/jest-runtime/src/index.ts
Expand Up @@ -48,7 +48,7 @@ import {
shouldInstrument,
} from '@jest/transform';
import type {Config, Global} from '@jest/types';
import HasteMap, {IModuleMap} from 'jest-haste-map';
import HasteMap, {IHasteMap, IModuleMap} from 'jest-haste-map';
import {formatStackTrace, separateMessageFromStack} from 'jest-message-util';
import type {MockMetadata, ModuleMocker} from 'jest-mock';
import {escapePathForRegex} from 'jest-regex-util';
Expand Down Expand Up @@ -330,7 +330,7 @@ export default class Runtime {
static createHasteMap(
config: Config.ProjectConfig,
options?: HasteMapOptions,
): Promise<HasteMap> {
): Promise<IHasteMap> {
const ignorePatternParts = [
...config.modulePathIgnorePatterns,
...(options && options.watch ? config.watchPathIgnorePatterns : []),
Expand Down
6 changes: 3 additions & 3 deletions packages/jest-snapshot/src/index.ts
Expand Up @@ -8,7 +8,7 @@
import * as fs from 'graceful-fs';
import type {Config} from '@jest/types';
import type {MatcherFunctionWithContext} from 'expect';
import type {FS as HasteFS} from 'jest-haste-map';
import type {IHasteFS} from 'jest-haste-map';
import {
BOLD_WEIGHT,
EXPECTED_COLOR,
Expand Down Expand Up @@ -110,11 +110,11 @@ function stripAddedIndentation(inlineSnapshot: string) {
return inlineSnapshot;
}

const fileExists = (filePath: string, hasteFS: HasteFS): boolean =>
const fileExists = (filePath: string, hasteFS: IHasteFS): boolean =>
hasteFS.exists(filePath) || fs.existsSync(filePath);

export const cleanup = (
hasteFS: HasteFS,
hasteFS: IHasteFS,
update: Config.SnapshotUpdateState,
snapshotResolver: SnapshotResolver,
testPathIgnorePatterns?: Config.ProjectConfig['testPathIgnorePatterns'],
Expand Down
6 changes: 3 additions & 3 deletions packages/jest-test-result/src/types.ts
Expand Up @@ -9,7 +9,7 @@ import type {V8Coverage} from 'collect-v8-coverage';
import type {CoverageMap, CoverageMapData} from 'istanbul-lib-coverage';
import type {ConsoleBuffer} from '@jest/console';
import type {Config, TestResult, TransformTypes} from '@jest/types';
import type {FS as HasteFS, ModuleMap} from 'jest-haste-map';
import type {IHasteFS, IModuleMap} from 'jest-haste-map';
import type Resolver from 'jest-resolve';

export interface RuntimeTransformResult extends TransformTypes.TransformResult {
Expand Down Expand Up @@ -187,8 +187,8 @@ export type Test = {

export type TestContext = {
config: Config.ProjectConfig;
hasteFS: HasteFS;
moduleMap: ModuleMap;
hasteFS: IHasteFS;
moduleMap: IModuleMap;
resolver: Resolver;
};

Expand Down