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

chore: use Maps throughout jest-runtime #10968

Merged
merged 4 commits into from Dec 22, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -62,6 +62,7 @@
- `[jest-resolve]` [**BREAKING**] Migrate to ESM ([#10688](https://github.com/facebook/jest/pull/10688))
- `[jest-resolve-dependencies]` [**BREAKING**] Migrate to ESM ([#10876](https://github.com/facebook/jest/pull/10876))
- `[jest-mock]` [**BREAKING**] Migrate to ESM ([#10887](https://github.com/facebook/jest/pull/10887))
- `[jest-resolve, jest-runtime]` [**BREAKING**] Use `Map`s instead of objects for all cached resources ([#10968](https://github.com/facebook/jest/pull/10968))
- `[jest-runner]` [**BREAKING**] Migrate to ESM ([#10900](https://github.com/facebook/jest/pull/10900))
- `[jest-runtime]` [**BREAKING**] Remove deprecated and unnused `getSourceMapInfo` from Runtime ([#9969](https://github.com/facebook/jest/pull/9969))
- `[jest-util]` No longer checking `enumerable` when adding `process.domain` ([#10862](https://github.com/facebook/jest/pull/10862))
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-repl/src/cli/runtime-cli.ts
Expand Up @@ -91,7 +91,7 @@ export async function run(
config,
environment,
hasteMap.resolver,
undefined,
new Map(),
undefined,
filePath,
);
Expand Down
11 changes: 4 additions & 7 deletions packages/jest-resolve/src/index.ts
Expand Up @@ -31,9 +31,6 @@ type FindNodeModuleConfig = {
throwIfNotFound?: boolean;
};

// TODO: replace with a Map in Jest 27
type BooleanObject = Record<string, boolean>;

export type ResolveModuleConfig = {
skipNodeResolution?: boolean;
paths?: Array<Config.Path>;
Expand Down Expand Up @@ -314,7 +311,7 @@ class Resolver {
}

getModuleID(
virtualMocks: BooleanObject,
virtualMocks: Map<string, boolean>,
from: Config.Path,
_moduleName?: string,
): string {
Expand Down Expand Up @@ -346,7 +343,7 @@ class Resolver {
}

private _getAbsolutePath(
virtualMocks: BooleanObject,
virtualMocks: Map<string, boolean>,
from: Config.Path,
moduleName: string,
): Config.Path | null {
Expand All @@ -368,12 +365,12 @@ class Resolver {
}

private _getVirtualMockPath(
virtualMocks: BooleanObject,
virtualMocks: Map<string, boolean>,
from: Config.Path,
moduleName: string,
): Config.Path {
const virtualMockPath = this.getModulePath(from, moduleName);
return virtualMocks[virtualMockPath]
return virtualMocks.get(virtualMockPath)
? virtualMockPath
: moduleName
? this.resolveModule(from, moduleName)
Expand Down
5 changes: 2 additions & 3 deletions packages/jest-runner/src/runTest.ts
Expand Up @@ -146,7 +146,7 @@ async function runTestInternal(
? new LeakDetector(environment)
: null;

const cacheFS = {[path]: testSource};
const cacheFS = new Map([[path, testSource]]);
setGlobal(environment.global, 'console', testConsole);

const runtime = new Runtime(
Expand Down Expand Up @@ -182,8 +182,7 @@ async function runTestInternal(
environment: 'node',
handleUncaughtExceptions: false,
retrieveSourceMap: source => {
const sourceMaps = runtime.getSourceMaps();
const sourceMapSource = sourceMaps && sourceMaps[source];
const sourceMapSource = runtime.getSourceMaps()?.get(source);

if (sourceMapSource) {
try {
Expand Down
66 changes: 25 additions & 41 deletions packages/jest-runtime/src/__mocks__/createRuntime.js
Expand Up @@ -35,27 +35,14 @@ const setupModuleNameMapper = (config, rootDir) => {
};

const setupTransform = (config, rootDir) => {
if (config && config.transform) {
if (config?.transform) {
const transform = config.transform;
return Object.keys(transform).map(regex => [
regex,
path.resolve(rootDir, transform[regex]),
]);
}
return [
[
'^.+\\.[jt]sx?$',
path.resolve(
__dirname,
'..',
'..',
'..',
'babel-jest',
'build',
'index.js',
),
],
];
return [['^.+\\.[jt]sx?$', require.resolve('babel-jest')]];
};

module.exports = async function createRuntime(filename, config) {
Expand All @@ -67,32 +54,29 @@ module.exports = async function createRuntime(filename, config) {
const moduleNameMapper = setupModuleNameMapper(config, rootDir);
const transform = setupTransform(config, rootDir);

config = makeProjectConfig(
{
cacheDirectory: getCacheDirectory(),
cwd: path.resolve(__dirname, '..', '..', '..', '..'),
haste: {
hasteImplModulePath: path.resolve(
__dirname,
'..',
'..',
'..',
'jest-haste-map',
'src',
'__tests__',
'haste_impl.js',
),
},
moduleDirectories: ['node_modules'],
moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'],
name: 'Runtime-' + filename.replace(/\W/, '-') + '.tests',
rootDir,
...config,
moduleNameMapper,
transform,
config = makeProjectConfig({
cacheDirectory: getCacheDirectory(),
cwd: path.resolve(__dirname, '..', '..', '..', '..'),
haste: {
hasteImplModulePath: path.resolve(
__dirname,
'..',
'..',
'..',
'jest-haste-map',
'src',
'__tests__',
'haste_impl.js',
),
},
{},
);
moduleDirectories: ['node_modules'],
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'node'],
name: 'Runtime-' + filename.replace(/\W/, '-') + '.tests',
rootDir,
...config,
moduleNameMapper,
transform,
});

if (!config.roots.length) {
config.roots = [config.rootDir];
Expand All @@ -110,7 +94,7 @@ module.exports = async function createRuntime(filename, config) {
config,
environment,
Runtime.createResolver(config, hasteMap.moduleMap),
undefined,
new Map(),
undefined,
filename,
);
Expand Down
Expand Up @@ -6,7 +6,7 @@ exports[`Runtime requireModule with no extension throws error pointing out file
However, Jest was able to find:
'./RegularModuleWithWrongExt.txt'
You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'json', 'jsx', 'ts', 'tsx', 'node'].
You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'jsx', 'ts', 'tsx', 'json', 'node'].
See https://jestjs.io/docs/en/configuration#modulefileextensions-arraystring"
`;
66 changes: 31 additions & 35 deletions packages/jest-runtime/src/index.ts
Expand Up @@ -102,18 +102,6 @@ type ResolveOptions = Parameters<typeof require.resolve>[1] & {
[JEST_RESOLVE_OUTSIDE_VM_OPTION]?: true;
};

type StringMap = Map<string, string>;
type BooleanMap = Map<string, boolean>;

const fromEntries: typeof Object.fromEntries =
Object.fromEntries ??
function fromEntries<T>(iterable: Iterable<[string, T]>) {
return [...iterable].reduce<Record<string, T>>((obj, [key, val]) => {
obj[key] = val;
return obj;
}, {});
};

const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL');
const retryTimesSymbol = Symbol.for('RETRY_TIMES');

Expand Down Expand Up @@ -154,12 +142,12 @@ const supportsTopLevelAwait =
})();

export default class Runtime {
private readonly _cacheFS: StringMap;
private readonly _cacheFS: Map<string, string>;
private readonly _config: Config.ProjectConfig;
private readonly _coverageOptions: ShouldInstrumentOptions;
private _currentlyExecutingModulePath: string;
private readonly _environment: JestEnvironment;
private readonly _explicitShouldMock: BooleanMap;
private readonly _explicitShouldMock: Map<string, boolean>;
private _fakeTimersImplementation:
| LegacyFakeTimers<unknown>
| ModernFakeTimers
Expand All @@ -181,16 +169,19 @@ export default class Runtime {
private readonly _testPath: Config.Path | undefined;
private readonly _resolver: Resolver;
private _shouldAutoMock: boolean;
private readonly _shouldMockModuleCache: BooleanMap;
private readonly _shouldUnmockTransitiveDependenciesCache: BooleanMap;
private readonly _sourceMapRegistry: StringMap;
private readonly _shouldMockModuleCache: Map<string, boolean>;
private readonly _shouldUnmockTransitiveDependenciesCache: Map<
string,
boolean
>;
private readonly _sourceMapRegistry: Map<string, string>;
private readonly _scriptTransformer: ScriptTransformer;
private readonly _fileTransforms: Map<string, RuntimeTransformResult>;
private _v8CoverageInstrumenter: CoverageInstrumenter | undefined;
private _v8CoverageResult: V8Coverage | undefined;
private readonly _transitiveShouldMock: BooleanMap;
private readonly _transitiveShouldMock: Map<string, boolean>;
private _unmockList: RegExp | undefined;
private readonly _virtualMocks: BooleanMap;
private readonly _virtualMocks: Map<string, boolean>;
private _moduleImplementation?: typeof nativeModule.Module;
private readonly jestObjectCaches: Map<string, Jest>;
private jestGlobals?: JestGlobals;
Expand All @@ -199,12 +190,12 @@ export default class Runtime {
config: Config.ProjectConfig,
environment: JestEnvironment,
resolver: Resolver,
cacheFS: Record<string, string> = {},
cacheFS: Map<string, string>,
coverageOptions?: ShouldInstrumentOptions,
// TODO: Make mandatory in Jest 27
testPath?: Config.Path,
) {
this._cacheFS = new Map(Object.entries(cacheFS));
this._cacheFS = cacheFS;
this._config = config;
this._coverageOptions = coverageOptions || {
changedFiles: undefined,
Expand All @@ -222,8 +213,11 @@ export default class Runtime {
this._mainModule = null;
this._mockFactories = new Map();
this._mockRegistry = new Map();
// during setup, this cannot be null (and it's fine to explode if it is)
this._moduleMocker = this._environment.moduleMocker!;
invariant(
this._environment.moduleMocker,
'`moduleMocker` must be set on an environment when created',
);
this._moduleMocker = this._environment.moduleMocker;
this._isolatedModuleRegistry = null;
this._isolatedMockRegistry = null;
this._moduleRegistry = new Map();
Expand Down Expand Up @@ -256,10 +250,12 @@ export default class Runtime {
}

if (config.automock) {
const virtualMocks = fromEntries(this._virtualMocks);
config.setupFiles.forEach(filePath => {
if (filePath && filePath.includes(NODE_MODULES)) {
const moduleID = this._resolver.getModuleID(virtualMocks, filePath);
if (filePath.includes(NODE_MODULES)) {
const moduleID = this._resolver.getModuleID(
this._virtualMocks,
filePath,
);
this._transitiveShouldMock.set(moduleID, false);
}
});
Expand Down Expand Up @@ -575,7 +571,7 @@ export default class Runtime {
isRequireActual?: boolean | null,
): T {
const moduleID = this._resolver.getModuleID(
fromEntries(this._virtualMocks),
this._virtualMocks,
from,
moduleName,
);
Expand Down Expand Up @@ -670,7 +666,7 @@ export default class Runtime {

requireMock<T = unknown>(from: Config.Path, moduleName: string): T {
const moduleID = this._resolver.getModuleID(
fromEntries(this._virtualMocks),
this._virtualMocks,
from,
moduleName,
);
Expand Down Expand Up @@ -922,7 +918,7 @@ export default class Runtime {
}

getSourceMaps(): SourceMapRegistry {
return fromEntries(this._sourceMapRegistry);
return this._sourceMapRegistry;
}

setMock(
Expand All @@ -937,7 +933,7 @@ export default class Runtime {
this._virtualMocks.set(mockPath, true);
}
const moduleID = this._resolver.getModuleID(
fromEntries(this._virtualMocks),
this._virtualMocks,
from,
moduleName,
);
Expand Down Expand Up @@ -1365,7 +1361,7 @@ export default class Runtime {
private _shouldMock(from: Config.Path, moduleName: string): boolean {
const explicitShouldMock = this._explicitShouldMock;
const moduleID = this._resolver.getModuleID(
fromEntries(this._virtualMocks),
this._virtualMocks,
from,
moduleName,
);
Expand Down Expand Up @@ -1408,7 +1404,7 @@ export default class Runtime {

// transitive unmocking for package managers that store flat packages (npm3)
const currentModuleID = this._resolver.getModuleID(
fromEntries(this._virtualMocks),
this._virtualMocks,
from,
);
if (
Expand Down Expand Up @@ -1493,7 +1489,7 @@ export default class Runtime {
};
const unmock = (moduleName: string) => {
const moduleID = this._resolver.getModuleID(
fromEntries(this._virtualMocks),
this._virtualMocks,
from,
moduleName,
);
Expand All @@ -1502,7 +1498,7 @@ export default class Runtime {
};
const deepUnmock = (moduleName: string) => {
const moduleID = this._resolver.getModuleID(
fromEntries(this._virtualMocks),
this._virtualMocks,
from,
moduleName,
);
Expand All @@ -1516,7 +1512,7 @@ export default class Runtime {
}

const moduleID = this._resolver.getModuleID(
fromEntries(this._virtualMocks),
this._virtualMocks,
from,
moduleName,
);
Expand Down
4 changes: 2 additions & 2 deletions packages/jest-source-map/src/__tests__/getCallsite.test.ts
Expand Up @@ -31,7 +31,7 @@ describe('getCallsite', () => {
throw new Error('Mock error');
});

const site = getCallsite(0, {[__filename]: 'mockedSourceMapFile'});
const site = getCallsite(0, new Map([[__filename, 'mockedSourceMapFile']]));

expect(site.getFileName()).toEqual(__filename);
expect(site.getColumnNumber()).toEqual(expect.any(Number));
Expand Down Expand Up @@ -59,7 +59,7 @@ describe('getCallsite', () => {
}
};

const site = getCallsite(0, {[__filename]: 'mockedSourceMapFile'});
const site = getCallsite(0, new Map([[__filename, 'mockedSourceMapFile']]));

expect(site.getFileName()).toEqual(__filename);
expect(site.getColumnNumber()).toEqual(sourceMapColumn);
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-source-map/src/getCallsite.ts
Expand Up @@ -52,7 +52,7 @@ export default (
): callsites.CallSite => {
const levelAfterThisCall = level + 1;
const stack = callsites()[levelAfterThisCall];
const sourceMapFileName = sourceMaps && sourceMaps[stack.getFileName() || ''];
const sourceMapFileName = sourceMaps?.get(stack.getFileName() || '');

if (sourceMapFileName) {
try {
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-source-map/src/types.ts
Expand Up @@ -5,4 +5,4 @@
* LICENSE file in the root directory of this source tree.
*/

export type SourceMapRegistry = Record<string, string>;
export type SourceMapRegistry = Map<string, string>;