Skip to content

Commit

Permalink
Improved performance of path manipulations
Browse files Browse the repository at this point in the history
  • Loading branch information
rubennorte committed Oct 1, 2018
1 parent 2536b04 commit f5df606
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 18 deletions.
3 changes: 2 additions & 1 deletion packages/jest-haste-map/src/crawlers/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import fs from 'fs';
import path from 'path';
import {spawn} from 'child_process';
import H from '../constants';
import * as fastPath from '../lib/fast_path';

type Callback = (result: Array<[/* id */ string, /* mtime */ number]>) => void;

Expand Down Expand Up @@ -141,7 +142,7 @@ module.exports = function nodeCrawl(
const files = new Map();
list.forEach(fileData => {
const filePath = fileData[0];
const relativeFilePath = path.relative(rootDir, filePath);
const relativeFilePath = fastPath.relative(rootDir, filePath);
const mtime = fileData[1];
const existingFile = data.files.get(relativeFilePath);
if (existingFile && existingFile[H.MTIME] === mtime) {
Expand Down
8 changes: 5 additions & 3 deletions packages/jest-haste-map/src/crawlers/watchman.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import type {InternalHasteMap} from 'types/HasteMap';
import type {CrawlerOptions} from '../types';

import * as fastPath from '../lib/fast_path';
import normalizePathSep from '../lib/normalize_path_sep';
import path from 'path';
import watchman from 'fb-watchman';
Expand Down Expand Up @@ -112,7 +113,7 @@ module.exports = async function watchmanCrawl(
}
}

const relativeRoot = path.relative(rootDir, root);
const relativeRoot = fastPath.relative(rootDir, root);
const query = clocks.has(relativeRoot)
? // Use the `since` generator if we have a clock available
{expression, fields, since: clocks.get(relativeRoot)}
Expand Down Expand Up @@ -160,11 +161,12 @@ module.exports = async function watchmanCrawl(

for (const [watchRoot, response] of watchmanFiles) {
const fsRoot = normalizePathSep(watchRoot);
const relativeFsRoot = path.relative(rootDir, fsRoot);
const relativeFsRoot = fastPath.relative(rootDir, fsRoot);
clocks.set(relativeFsRoot, response.clock);

for (const fileData of response.files) {
const filePath = fsRoot + path.sep + normalizePathSep(fileData.name);
const relativeFilePath = path.relative(rootDir, filePath);
const relativeFilePath = fastPath.relative(rootDir, filePath);

if (!fileData.exists) {
files.delete(relativeFilePath);
Expand Down
8 changes: 4 additions & 4 deletions packages/jest-haste-map/src/haste_fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import type {Glob, Path} from 'types/Config';
import type {FileData} from 'types/HasteMap';

import path from 'path';
import * as fastPath from './lib/fast_path';
import micromatch from 'micromatch';
import H from './constants';

Expand Down Expand Up @@ -48,7 +48,7 @@ export default class HasteFS {

*getFileIterator(): Iterator<string> {
for (const file of this._files.keys()) {
yield path.resolve(this._rootDir, file);
yield fastPath.resolve(this._rootDir, file);
}
}

Expand All @@ -68,7 +68,7 @@ export default class HasteFS {
matchFilesWithGlob(globs: Array<Glob>, root: ?Path): Set<Path> {
const files = new Set();
for (const file of this.getFileIterator()) {
const filePath = root ? path.relative(root, file) : file;
const filePath = root ? fastPath.relative(root, file) : file;
if (micromatch([filePath], globs).length) {
files.add(file);
}
Expand All @@ -77,7 +77,7 @@ export default class HasteFS {
}

_getFileData(file: Path) {
const relativePath = path.relative(this._rootDir, file);
const relativePath = fastPath.relative(this._rootDir, file);
return this._files.get(relativePath);
}
}
19 changes: 13 additions & 6 deletions packages/jest-haste-map/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import serializer from 'jest-serializer';
// eslint-disable-next-line import/default
import watchmanCrawl from './crawlers/watchman';
import WatchmanWatcher from './lib/watchman_watcher';
import * as fastPath from './lib/fast_path';
import Worker from 'jest-worker';

import type {Console} from 'console';
Expand Down Expand Up @@ -259,7 +260,7 @@ class HasteMap extends EventEmitter {
`haste-map-${this._options.name}`,
VERSION,
this._options.roots
.map(root => path.relative(options.rootDir, root))
.map(root => fastPath.relative(options.rootDir, root))
.join(':'),
this._options.extensions.join(':'),
this._options.platforms.join(':'),
Expand Down Expand Up @@ -395,8 +396,11 @@ class HasteMap extends EventEmitter {
const message =
`jest-haste-map: @providesModule naming collision:\n` +
` Duplicate module name: ${id}\n` +
` Paths: ${path.resolve(rootDir, module[H.PATH])} collides with ` +
`${path.resolve(rootDir, existingModule[H.PATH])}\n\nThis ` +
` Paths: ${fastPath.resolve(
rootDir,
module[H.PATH],
)} collides with ` +
`${fastPath.resolve(rootDir, existingModule[H.PATH])}\n\nThis ` +
`${this._options.throwOnModuleCollision ? 'error' : 'warning'} ` +
`is caused by a @providesModule declaration ` +
`with the same name across two different files.`;
Expand Down Expand Up @@ -432,7 +436,7 @@ class HasteMap extends EventEmitter {
moduleMap[platform] = module;
};

const relativeFilePath = path.relative(rootDir, filePath);
const relativeFilePath = fastPath.relative(rootDir, filePath);
const fileMetadata = hasteMap.files.get(relativeFilePath);
if (!fileMetadata) {
throw new Error(
Expand Down Expand Up @@ -574,7 +578,10 @@ class HasteMap extends EventEmitter {

for (const relativeFilePath of hasteMap.files.keys()) {
// SHA-1, if requested, should already be present thanks to the crawler.
const filePath = path.resolve(this._options.rootDir, relativeFilePath);
const filePath = fastPath.resolve(
this._options.rootDir,
relativeFilePath,
);
const promise = this._processFile(hasteMap, map, mocks, filePath);
if (promise) {
promises.push(promise);
Expand Down Expand Up @@ -802,7 +809,7 @@ class HasteMap extends EventEmitter {

const add = () => eventsQueue.push({filePath, stat, type});

const relativeFilePath = path.relative(rootDir, filePath);
const relativeFilePath = fastPath.relative(rootDir, filePath);
const fileMetadata = hasteMap.files.get(relativeFilePath);

// If it's not an addition, delete the file and all its metadata
Expand Down
45 changes: 45 additions & 0 deletions packages/jest-haste-map/src/lib/__tests__/fast_path.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* 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';

import path from 'path';
import {relative, resolve} from '../fast_path';

describe('fastPath.relative', () => {
it('should get relative paths inside the root', () => {
const root = path.join(__dirname, 'foo', 'bar');
const filename = path.join(__dirname, 'foo', 'bar', 'baz', 'foobar');
const relativeFilename = path.join('baz', 'foobar');
expect(relative(root, filename)).toBe(relativeFilename);
});

it('should get relative paths outside the root', () => {
const root = path.join(__dirname, 'foo', 'bar');
const filename = path.join(__dirname, 'foo', 'baz', 'foobar');
const relativeFilename = path.join('..', 'baz', 'foobar');
expect(relative(root, filename)).toBe(relativeFilename);
});
});

describe('fastPath.resolve', () => {
it('should get the absolute path for paths inside the root', () => {
const root = path.join(__dirname, 'foo', 'bar');
const relativeFilename = path.join('baz', 'foobar');
const filename = path.join(__dirname, 'foo', 'bar', 'baz', 'foobar');
expect(resolve(root, relativeFilename)).toBe(filename);
});

it('should get the absolute path for paths outside the root', () => {
const root = path.join(__dirname, 'foo', 'bar');
const relativeFilename = path.join('..', 'baz', 'foobar');
const filename = path.join(__dirname, 'foo', 'baz', 'foobar');
expect(resolve(root, relativeFilename)).toBe(filename);
});
});
24 changes: 24 additions & 0 deletions packages/jest-haste-map/src/lib/fast_path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. 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.
*
* @flow
*/

import path from 'path';

export function relative(rootDir: string, filename: string): string {
return filename.indexOf(rootDir) === 0
? filename.substr(rootDir.length + 1)
: path.relative(rootDir, filename);
}

const INDIRECTION_FRAGMENT = '..' + path.sep;

export function resolve(rootDir: string, relativeFilename: string): string {
return relativeFilename.indexOf(INDIRECTION_FRAGMENT) === 0
? path.resolve(rootDir, relativeFilename)
: rootDir + path.sep + relativeFilename;
}
8 changes: 4 additions & 4 deletions packages/jest-haste-map/src/module_map.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type {
MockData,
} from 'types/HasteMap';

import path from 'path';
import * as fastPath from './lib/fast_path';
import H from './constants';

const EMPTY_MAP = {};
Expand Down Expand Up @@ -58,7 +58,7 @@ export default class ModuleMap {
);
if (module && module[H.TYPE] === type) {
const modulePath = module[H.PATH];
return modulePath && path.resolve(this._raw.rootDir, modulePath);
return modulePath && fastPath.resolve(this._raw.rootDir, modulePath);
}
return null;
}
Expand All @@ -74,7 +74,7 @@ export default class ModuleMap {
getMockModule(name: string): ?Path {
const mockPath =
this._raw.mocks.get(name) || this._raw.mocks.get(name + '/index');
return mockPath && path.resolve(this._raw.rootDir, mockPath);
return mockPath && fastPath.resolve(this._raw.rootDir, mockPath);
}

getRawModuleMap(): RawModuleMap {
Expand Down Expand Up @@ -165,7 +165,7 @@ export default class ModuleMap {
// Force flow refinement
const previousSet: DuplicatesSet = relativePathSet;
const set = Object.keys(previousSet).reduce((set, relativePath) => {
const duplicatePath = path.resolve(this._raw.rootDir, relativePath);
const duplicatePath = fastPath.resolve(this._raw.rootDir, relativePath);
set[duplicatePath] = previousSet[relativePath];
return set;
}, Object.create(null));
Expand Down

0 comments on commit f5df606

Please sign in to comment.