Skip to content

Commit

Permalink
chore: migrate jest-resolve-dependencies to TypeScript (#7922)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeysal authored and SimenB committed Feb 19, 2019
1 parent d490e2d commit b8a9a71
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 86 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -48,6 +48,7 @@
- `[@jest/core]` Create new package, which is `jest-cli` minus `yargs` and `prompts` ([#7696](https://github.com/facebook/jest/pull/7696))
- `[@jest/transform]`: Migrate to TypeScript ([#7918](https://github.com/facebook/jest/pull/7918))
- `[docs]` Add missing import to docs ([#7928](https://github.com/facebook/jest/pull/7928))
- `[jest-resolve-dependencies]`: Migrate to TypeScript ([#7922](https://github.com/facebook/jest/pull/7922))

### Performance

Expand Down
11 changes: 11 additions & 0 deletions packages/jest-resolve-dependencies/package.json
Expand Up @@ -8,10 +8,21 @@
},
"license": "MIT",
"main": "build/index.js",
"types": "build/index.d.ts",
"dependencies": {
"@jest/types": "^24.1.0",
"jest-regex-util": "^24.0.0",
"jest-snapshot": "^24.1.0"
},
"devDependencies": {
"jest-haste-map": "^24.0.0",
"jest-resolve": "^24.1.0",
"jest-runtime": "^24.1.0"
},
"peerDependencies": {
"jest-haste-map": "^24.0.0",
"jest-resolve": "^24.1.0"
},
"engines": {
"node": ">= 6"
},
Expand Down
Expand Up @@ -3,41 +3,44 @@
*
* 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 {normalize} = require('jest-config');
const {buildSnapshotResolver} = require('jest-snapshot');
const DependencyResolver = require('../index');
import {tmpdir} from 'os';
import path from 'path';
import {Config} from '@jest/types';
import {buildSnapshotResolver} from 'jest-snapshot';
import {makeProjectConfig} from '../../../../TestUtils';

import DependencyResolver from '../index';

const maxWorkers = 1;
let dependencyResolver;
let dependencyResolver: DependencyResolver;
let Runtime;
let config;
const cases = {
let config: Config.ProjectConfig;
const cases: {[key: string]: jest.Mock} = {
fancyCondition: jest.fn(path => path.length > 10),
testRegex: jest.fn(path => /.test.js$/.test(path)),
};
const filter = path => Object.keys(cases).every(key => cases[key](path));
const filter = (path: Config.Path) =>
Object.keys(cases).every(key => cases[key](path));

beforeEach(() => {
Runtime = require('jest-runtime');
config = normalize(
{
rootDir: '.',
roots: ['./packages/jest-resolve-dependencies'],
},
{},
).options;
return Runtime.createContext(config, {maxWorkers}).then(hasteMap => {
dependencyResolver = new DependencyResolver(
hasteMap.resolver,
hasteMap.hasteFS,
buildSnapshotResolver(config),
);
config = makeProjectConfig({
cacheDirectory: path.resolve(tmpdir(), 'jest-resolve-dependencies-test'),
moduleDirectories: ['node_modules'],
rootDir: '.',
roots: ['./packages/jest-resolve-dependencies'],
});
return Runtime.createContext(config, {maxWorkers, watchman: false}).then(
(hasteMap: any) => {
dependencyResolver = new DependencyResolver(
hasteMap.resolver,
hasteMap.hasteFS,
buildSnapshotResolver(config),
);
},
);
});

test('resolves no dependencies for non-existent path', () => {
Expand Down
Expand Up @@ -3,46 +3,42 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {HasteFS} from 'types/HasteMap';
import type {Path} from 'types/Config';
import type {
Resolver,
ResolveModuleConfig,
ResolvedModule,
} from 'types/Resolve';
import type {SnapshotResolver} from 'types/SnapshotResolver';
import {Config, Resolve, Snapshot} from '@jest/types';
import {FS as HasteFS} from 'jest-haste-map';
import Resolver from 'jest-resolve';
import {isSnapshotPath} from 'jest-snapshot';

/**
* DependencyResolver is used to resolve the direct dependencies of a module or
* to retrieve a list of all transitive inverse dependencies.
*/
class DependencyResolver {
_hasteFS: HasteFS;
_resolver: Resolver;
_snapshotResolver: SnapshotResolver;
private _hasteFS: HasteFS;
private _resolver: Resolver;
private _snapshotResolver: Snapshot.SnapshotResolver;

constructor(
resolver: Resolver,
hasteFS: HasteFS,
snapshotResolver: SnapshotResolver,
snapshotResolver: Snapshot.SnapshotResolver,
) {
this._resolver = resolver;
this._hasteFS = hasteFS;
this._snapshotResolver = snapshotResolver;
}

resolve(file: Path, options?: ResolveModuleConfig): Array<Path> {
resolve(
file: Config.Path,
options?: Resolve.ResolveModuleConfig,
): Array<Config.Path> {
const dependencies = this._hasteFS.getDependencies(file);
if (!dependencies) {
return [];
}

return dependencies.reduce((acc, dependency) => {
return dependencies.reduce<Array<Config.Path>>((acc, dependency) => {
if (this._resolver.isCoreModule(dependency)) {
return acc;
}
Expand All @@ -66,23 +62,27 @@ class DependencyResolver {
}

resolveInverseModuleMap(
paths: Set<Path>,
filter: (file: Path) => boolean,
options?: ResolveModuleConfig,
): Array<ResolvedModule> {
paths: Set<Config.Path>,
filter: (file: Config.Path) => boolean,
options?: Resolve.ResolveModuleConfig,
): Array<Resolve.ResolvedModule> {
if (!paths.size) {
return [];
}

const collectModules = (related, moduleMap, changed) => {
const collectModules = (
related: Set<Config.Path>,
moduleMap: Array<Resolve.ResolvedModule>,
changed: Set<Config.Path>,
) => {
const visitedModules = new Set();
const result: Array<ResolvedModule> = [];
const result: Array<Resolve.ResolvedModule> = [];
while (changed.size) {
changed = new Set(
moduleMap.reduce((acc, module) => {
moduleMap.reduce<Array<Config.Path>>((acc, module) => {
if (
visitedModules.has(module.file) ||
!module.dependencies.some(dep => dep && changed.has(dep))
!module.dependencies.some(dep => changed.has(dep))
) {
return acc;
}
Expand All @@ -98,11 +98,13 @@ class DependencyResolver {
}, []),
);
}
return result.concat(Array.from(related).map(file => ({file})));
return result.concat(
Array.from(related).map(file => ({dependencies: [], file})),
);
};

const relatedPaths = new Set<Path>();
const changed = new Set();
const relatedPaths = new Set<Config.Path>();
const changed: Set<Config.Path> = new Set();
for (const path of paths) {
if (this._hasteFS.exists(path)) {
const modulePath = isSnapshotPath(path)
Expand All @@ -114,7 +116,7 @@ class DependencyResolver {
}
}
}
const modules = [];
const modules: Array<Resolve.ResolvedModule> = [];
for (const file of this._hasteFS.getAbsoluteFileIterator()) {
modules.push({
dependencies: this.resolve(file, options),
Expand All @@ -125,14 +127,14 @@ class DependencyResolver {
}

resolveInverse(
paths: Set<Path>,
filter: (file: Path) => boolean,
options?: ResolveModuleConfig,
): Array<Path> {
paths: Set<Config.Path>,
filter: (file: Config.Path) => boolean,
options?: Resolve.ResolveModuleConfig,
): Array<Config.Path> {
return this.resolveInverseModuleMap(paths, filter, options).map(
module => module.file,
);
}
}

module.exports = DependencyResolver;
export = DependencyResolver;
12 changes: 12 additions & 0 deletions packages/jest-resolve-dependencies/tsconfig.json
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "build"
},
"references": [
{"path": "../jest-regex-util"},
{"path": "../jest-snapshot"},
{"path": "../jest-types"}
]
}
11 changes: 3 additions & 8 deletions packages/jest-resolve/src/index.ts
Expand Up @@ -6,7 +6,7 @@
*/

import path from 'path';
import {Config} from '@jest/types';
import {Config, Resolve} from '@jest/types';
import {ModuleMap} from 'jest-haste-map';
import {sync as realpath} from 'realpath-native';
import chalk from 'chalk';
Expand All @@ -15,11 +15,6 @@ import isBuiltinModule from './isBuiltinModule';
import defaultResolver from './defaultResolver';
import {ResolverConfig} from './types';

type ResolveModuleConfig = {
skipNodeResolution?: boolean;
paths?: Config.Path[];
};

type FindNodeModuleConfig = {
basedir: Config.Path;
browser?: boolean;
Expand Down Expand Up @@ -102,7 +97,7 @@ class Resolver {
resolveModuleFromDirIfExists(
dirname: Config.Path,
moduleName: string,
options?: ResolveModuleConfig,
options?: Resolve.ResolveModuleConfig,
): Config.Path | null {
const paths = (options && options.paths) || this._options.modulePaths;
const moduleDirectory = this._options.moduleDirectories;
Expand Down Expand Up @@ -185,7 +180,7 @@ class Resolver {
resolveModule(
from: Config.Path,
moduleName: string,
options?: ResolveModuleConfig,
options?: Resolve.ResolveModuleConfig,
): Config.Path {
const dirname = path.dirname(from);
const module = this.resolveModuleFromDirIfExists(
Expand Down
5 changes: 2 additions & 3 deletions packages/jest-snapshot/src/index.ts
Expand Up @@ -6,15 +6,14 @@
*/

import fs from 'fs';
import {Config, Matchers} from '@jest/types';
import {Config, Matchers, Snapshot} from '@jest/types';
import {FS as HasteFS} from 'jest-haste-map';

import diff from 'jest-diff';
import {EXPECTED_COLOR, matcherHint, RECEIVED_COLOR} from 'jest-matcher-utils';
import {
buildSnapshotResolver,
isSnapshotPath,
SnapshotResolver,
EXTENSION,
} from './snapshot_resolver';
import SnapshotState from './State';
Expand All @@ -31,7 +30,7 @@ const fileExists = (filePath: Config.Path, hasteFS: HasteFS): boolean =>
const cleanup = (
hasteFS: HasteFS,
update: Config.SnapshotUpdateState,
snapshotResolver: SnapshotResolver,
snapshotResolver: Snapshot.SnapshotResolver,
) => {
const pattern = '\\.' + EXTENSION + '$';
const files = hasteFS.matchFiles(pattern);
Expand Down
24 changes: 9 additions & 15 deletions packages/jest-snapshot/src/snapshot_resolver.ts
Expand Up @@ -6,25 +6,19 @@
*/

import path from 'path';
import {Config} from '@jest/types';
import {Config, Snapshot} from '@jest/types';
import chalk from 'chalk';

export type SnapshotResolver = {
testPathForConsistencyCheck: string;
resolveSnapshotPath(testPath: Config.Path, extension?: string): Config.Path;
resolveTestPath(snapshotPath: Config.Path, extension?: string): Config.Path;
};

export const EXTENSION = 'snap';
export const DOT_EXTENSION = '.' + EXTENSION;

export const isSnapshotPath = (path: string): boolean =>
path.endsWith(DOT_EXTENSION);

const cache: Map<Config.Path, SnapshotResolver> = new Map();
const cache: Map<Config.Path, Snapshot.SnapshotResolver> = new Map();
export const buildSnapshotResolver = (
config: Config.ProjectConfig,
): SnapshotResolver => {
): Snapshot.SnapshotResolver => {
const key = config.rootDir;
if (!cache.has(key)) {
cache.set(key, createSnapshotResolver(config.snapshotResolver));
Expand All @@ -34,13 +28,13 @@ export const buildSnapshotResolver = (

function createSnapshotResolver(
snapshotResolverPath?: Config.Path | null,
): SnapshotResolver {
): Snapshot.SnapshotResolver {
return typeof snapshotResolverPath === 'string'
? createCustomSnapshotResolver(snapshotResolverPath)
: createDefaultSnapshotResolver();
}

function createDefaultSnapshotResolver(): SnapshotResolver {
function createDefaultSnapshotResolver(): Snapshot.SnapshotResolver {
return {
resolveSnapshotPath: (testPath: Config.Path) =>
path.join(
Expand All @@ -65,10 +59,10 @@ function createDefaultSnapshotResolver(): SnapshotResolver {

function createCustomSnapshotResolver(
snapshotResolverPath: Config.Path,
): SnapshotResolver {
const custom: SnapshotResolver = require(snapshotResolverPath);
): Snapshot.SnapshotResolver {
const custom: Snapshot.SnapshotResolver = require(snapshotResolverPath);

const keys: [keyof SnapshotResolver, string][] = [
const keys: [keyof Snapshot.SnapshotResolver, string][] = [
['resolveSnapshotPath', 'function'],
['resolveTestPath', 'function'],
['testPathForConsistencyCheck', 'string'],
Expand Down Expand Up @@ -101,7 +95,7 @@ function mustImplement(propName: string, requiredType: string) {
);
}

function verifyConsistentTransformations(custom: SnapshotResolver) {
function verifyConsistentTransformations(custom: Snapshot.SnapshotResolver) {
const resolvedSnapshotPath = custom.resolveSnapshotPath(
custom.testPathForConsistencyCheck,
);
Expand Down

0 comments on commit b8a9a71

Please sign in to comment.