Remove HasteMap

Reviewed By: davidaurelio

Differential Revision: D5803275

fbshipit-source-id: 2e64733d8ef400a61d116b21cd53185934dd3d57
This commit is contained in:
Christoph Nakazawa 2017-09-11 23:50:12 -07:00 committed by Facebook Github Bot
parent 61a0116dd4
commit b3e817285b
4 changed files with 67 additions and 292 deletions

View File

@ -18,7 +18,7 @@ import type ModuleCache from './ModuleCache';
module.exports = class Module { module.exports = class Module {
hasteID: ?string; hasteID: ?string;
moduleCache: ModuleCache; moduleCache: ModuleCache;
name: Promise<string>; name: string;
path: string; path: string;
type: 'Module'; type: 'Module';
@ -29,7 +29,7 @@ module.exports = class Module {
) { ) {
this.hasteID = info.hasteID; this.hasteID = info.hasteID;
this.moduleCache = moduleCache; this.moduleCache = moduleCache;
this.name = Promise.resolve(this.hasteID || getName(path)); this.name = this.hasteID || getName(path);
this.path = path; this.path = path;
this.type = 'Module'; this.type = 'Module';
} }
@ -42,8 +42,8 @@ module.exports = class Module {
return Promise.reject(new Error('not implemented')); return Promise.reject(new Error('not implemented'));
} }
getName() { getName(): Promise<string> {
return this.name; return Promise.resolve(this.name);
} }
getPackage() { getPackage() {

View File

@ -16,21 +16,20 @@ const AssetResolutionCache = require('../../node-haste/AssetResolutionCache');
const DependencyGraphHelpers = require('../../node-haste/DependencyGraph/DependencyGraphHelpers'); const DependencyGraphHelpers = require('../../node-haste/DependencyGraph/DependencyGraphHelpers');
const FilesByDirNameIndex = require('../../node-haste/FilesByDirNameIndex'); const FilesByDirNameIndex = require('../../node-haste/FilesByDirNameIndex');
const HasteFS = require('./HasteFS'); const HasteFS = require('./HasteFS');
const HasteMap = require('../../node-haste/DependencyGraph/HasteMap');
const Module = require('./Module'); const Module = require('./Module');
const ModuleCache = require('./ModuleCache'); const ModuleCache = require('./ModuleCache');
const ResolutionRequest = require('../../node-haste/DependencyGraph/ResolutionRequest'); const ResolutionRequest = require('../../node-haste/DependencyGraph/ResolutionRequest');
const defaults = require('../../defaults'); const defaults = require('../../defaults');
const parsePlatformFilePath = require('../../node-haste/lib/parsePlatformFilePath');
const path = require('path');
const { const {
ModuleResolver, ModuleResolver,
} = require('../../node-haste/DependencyGraph/ModuleResolution'); } = require('../../node-haste/DependencyGraph/ModuleResolution');
const {ModuleMap} = require('jest-haste-map');
import type { import type {Moduleish} from '../../node-haste/DependencyGraph/ResolutionRequest';
Moduleish,
Packageish,
} from '../../node-haste/DependencyGraph/ResolutionRequest';
import type {ResolveFn, TransformedCodeFile} from '../types.flow'; import type {ResolveFn, TransformedCodeFile} from '../types.flow';
import type { import type {
// eslint-disable-line sort-requires // eslint-disable-line sort-requires
@ -47,25 +46,9 @@ type ResolveOptions = {|
const platforms = new Set(defaults.platforms); const platforms = new Set(defaults.platforms);
/** const GENERIC_PLATFORM = 'g';
* We don't need to crawl the filesystem all over again so we just mock const PACKAGE_JSON = path.sep + 'package.json';
* a jest-haste-map's ModuleMap instance. Eventually, though, we'll const NULL_MODULE: Moduleish = {
* want to figure out how to reunify and get rid of `HasteMap`.
*/
function getFakeModuleMap(hasteMap: HasteMap<Module, Packageish>) {
return {
getModule(name: string, platform: ?string): ?string {
const module = hasteMap.getModule(name, platform);
return module && module.type === 'Module' ? module.path : null;
},
getPackage(name: string, platform: ?string): ?string {
const pkg = hasteMap.getPackage(name);
return pkg && pkg.path;
},
};
}
const nullModule: Moduleish = {
path: '/', path: '/',
getPackage() {}, getPackage() {},
hash() { hash() {
@ -85,9 +68,49 @@ const nullModule: Moduleish = {
}, },
}; };
exports.createResolveFn = function( // This function maps the ModuleGraph data structure to jest-haste-map's ModuleMap
options: ResolveOptions, const createModuleMap = ({files, helpers, moduleCache, sourceExts}) => {
): Promise<ResolveFn> { const map = Object.create(null);
files.forEach(filePath => {
if (!helpers.isNodeModulesDir(filePath)) {
let id;
let module;
if (filePath.endsWith(PACKAGE_JSON)) {
module = moduleCache.getPackage(filePath);
id = module.data.name;
} else if (sourceExts.indexOf(path.extname(filePath).substr(1)) !== -1) {
module = moduleCache.getModule(filePath);
id = module.name;
}
if (id && module && module.isHaste()) {
if (!map[id]) {
map[id] = Object.create(null);
}
const platform =
parsePlatformFilePath(filePath, platforms).platform ||
GENERIC_PLATFORM;
const existingModule = map[id][platform];
// 0 = Module, 1 = Package in jest-haste-map
map[id][platform] = [filePath, module.type === 'Package' ? 1 : 0];
if (existingModule && existingModule.path !== filePath) {
throw new Error(
`@providesModule naming collision:\n` +
` Duplicate module name: ${id}\n` +
` Paths: ${filePath} collides with ${existingModule.path}\n\n` +
'This error is caused by a @providesModule declaration ' +
'with the same name across two different files.',
);
}
}
}
});
return map;
};
exports.createResolveFn = function(options: ResolveOptions): ResolveFn {
const {assetExts, extraNodeModules, transformedFiles, sourceExts} = options; const {assetExts, extraNodeModules, transformedFiles, sourceExts} = options;
const files = Object.keys(transformedFiles); const files = Object.keys(transformedFiles);
function getTransformedFile(path) { function getTransformedFile(path) {
@ -108,18 +131,9 @@ exports.createResolveFn = function(
filePath => hasteFS.closest(filePath, 'package.json'), filePath => hasteFS.closest(filePath, 'package.json'),
getTransformedFile, getTransformedFile,
); );
const hasteMap = new HasteMap({
extensions: sourceExts,
files,
helpers,
moduleCache,
platforms,
preferNativePlatform: true,
});
const hasteMapBuilt = hasteMap.build();
const resolutionRequests = {}; const resolutionRequests = {};
const filesByDirNameIndex = new FilesByDirNameIndex(hasteMap.getAllFiles()); const filesByDirNameIndex = new FilesByDirNameIndex(files);
const assetResolutionCache = new AssetResolutionCache({ const assetResolutionCache = new AssetResolutionCache({
assetExtensions: new Set(assetExts), assetExtensions: new Set(assetExts),
getDirFiles: dirPath => filesByDirNameIndex.getAllFiles(dirPath), getDirFiles: dirPath => filesByDirNameIndex.getAllFiles(dirPath),
@ -131,14 +145,18 @@ exports.createResolveFn = function(
extraNodeModules, extraNodeModules,
helpers, helpers,
moduleCache, moduleCache,
moduleMap: getFakeModuleMap(hasteMap), moduleMap: new ModuleMap({
duplicates: Object.create(null),
map: createModuleMap({files, helpers, moduleCache, sourceExts}),
mocks: Object.create(null),
}),
preferNativePlatform: true, preferNativePlatform: true,
resolveAsset: (dirPath, assetName, platform) => resolveAsset: (dirPath, assetName, platform) =>
assetResolutionCache.resolve(dirPath, assetName, platform), assetResolutionCache.resolve(dirPath, assetName, platform),
sourceExts, sourceExts,
}); });
return hasteMapBuilt.then(() => (id, source, platform, _, callback) => { return (id, sourcePath, platform, _, callback) => {
let resolutionRequest = resolutionRequests[platform]; let resolutionRequest = resolutionRequests[platform];
if (!resolutionRequest) { if (!resolutionRequest) {
resolutionRequest = resolutionRequests[platform] = new ResolutionRequest({ resolutionRequest = resolutionRequests[platform] = new ResolutionRequest({
@ -151,9 +169,9 @@ exports.createResolveFn = function(
} }
const from = const from =
source != null sourcePath != null
? new Module(source, moduleCache, getTransformedFile(source)) ? new Module(sourcePath, moduleCache, getTransformedFile(sourcePath))
: nullModule; : NULL_MODULE;
return resolutionRequest.resolveDependency(from, id).path; return resolutionRequest.resolveDependency(from, id).path;
}); };
}; };

View File

@ -1,243 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
* @format
*/
'use strict';
const EventEmitter = require('events');
const parsePlatformFilePath = require('../lib/parsePlatformFilePath');
const path = require('path');
const throat = require('throat');
const GENERIC_PLATFORM = 'generic';
const NATIVE_PLATFORM = 'native';
const PACKAGE_JSON = path.sep + 'package.json';
import type {Moduleish, Packageish, ModuleishCache} from './ResolutionRequest';
import type DependencyGraphHelpers from './DependencyGraphHelpers';
type Options<TModule, TPackage> = {|
extensions: Array<string>,
files: Array<string>,
helpers: DependencyGraphHelpers,
moduleCache: ModuleishCache<TModule, TPackage>,
platforms: Set<string>,
preferNativePlatform: boolean,
|};
class HasteMap<TModule: Moduleish, TPackage: Packageish> extends EventEmitter {
_extensions: Array<string>;
_files: Array<string>;
_helpers: DependencyGraphHelpers;
_map: {};
_moduleCache: ModuleishCache<TModule, TPackage>;
_packages: {};
_platforms: Set<string>;
_preferNativePlatform: boolean;
constructor({
extensions,
files,
helpers,
moduleCache,
platforms,
preferNativePlatform,
}: Options<TModule, TPackage>) {
super();
this._extensions = extensions;
this._files = files;
this._helpers = helpers;
this._moduleCache = moduleCache;
this._platforms = platforms;
this._preferNativePlatform = preferNativePlatform;
(this: any)._processHastePackage = throat(
1,
this._processHastePackage.bind(this),
);
(this: any)._processHasteModule = throat(
1,
this._processHasteModule.bind(this),
);
}
build() {
this._map = Object.create(null);
this._packages = Object.create(null);
const promises = [];
this._files.forEach(filePath => {
if (!this._helpers.isNodeModulesDir(filePath)) {
if (filePath.endsWith(PACKAGE_JSON)) {
promises.push(this._processHastePackage(filePath));
} else if (
this._extensions.indexOf(path.extname(filePath).substr(1)) !== -1
) {
promises.push(this._processHasteModule(filePath));
}
}
});
/* $FlowFixMe(>=0.54.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.54 was deployed. To see the error delete this
* comment and run Flow. */
return Promise.all(promises).then(() => this._map);
}
getAllFiles(): Array<string> {
return this._files;
}
processFileChange(type: string, absPath: string) {
/* $FlowFixMe(>=0.54.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.54 was deployed. To see the error delete this
* comment and run Flow. */
return Promise.resolve().then(() => {
/*eslint no-labels: 0 */
let invalidated;
if (type === 'delete' || type === 'change') {
loop: for (const name in this._map) {
const modulesMap = this._map[name];
for (const platform in modulesMap) {
const module = modulesMap[platform];
if (module.path === absPath) {
delete modulesMap[platform];
invalidated = name;
break loop;
}
}
}
if (type === 'delete') {
if (invalidated) {
this.emit('change');
}
return null;
}
}
if (
type !== 'delete' &&
this._extensions.indexOf(this._helpers.extname(absPath)) !== -1
) {
if (path.basename(absPath) === 'package.json') {
return this._processHastePackage(absPath, invalidated);
} else {
return this._processHasteModule(absPath, invalidated);
}
}
return null;
});
}
getModule(name: string, platform: ?string): ?TModule {
const modulesMap = this._map[name];
if (modulesMap == null) {
return null;
}
// If platform is 'ios', we prefer .ios.js to .native.js which we prefer to
// a plain .js file.
let module;
if (module == null && platform != null) {
module = modulesMap[platform];
}
if (module == null && this._preferNativePlatform) {
module = modulesMap[NATIVE_PLATFORM];
}
if (module == null) {
module = modulesMap[GENERIC_PLATFORM];
}
return module;
}
getPackage(name: string): TPackage {
return this._packages[name];
}
_processHasteModule(file: string, previousName: ?string) {
const module = this._moduleCache.getModule(file);
/* $FlowFixMe(>=0.54.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.54 was deployed. To see the error delete this
* comment and run Flow. */
return Promise.resolve().then(() => {
const isHaste = module.isHaste();
return (
isHaste &&
module.getName().then(name => {
const result = this._updateHasteMap(name, module);
if (previousName && name !== previousName) {
this.emit('change');
}
return result;
})
);
});
}
_processHastePackage(file: string, previousName: ?string) {
const p = this._moduleCache.getPackage(file);
/* $FlowFixMe(>=0.54.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.54 was deployed. To see the error delete this
* comment and run Flow. */
return Promise.resolve()
.then(() => {
const isHaste = p.isHaste();
return (
isHaste &&
p.getName().then(name => {
const result = this._updateHasteMap(name, p);
if (previousName && name !== previousName) {
this.emit('change');
}
return result;
})
);
})
.catch(e => {
if (e instanceof SyntaxError) {
// Malformed package.json.
return;
}
throw e;
});
}
_updateHasteMap(name: string, mod: TModule | TPackage) {
let existingModule;
if (mod.type === 'Package') {
existingModule = this._packages[name];
this._packages[name] = mod;
} else {
if (this._map[name] == null) {
this._map[name] = Object.create(null);
}
const moduleMap = this._map[name];
const modulePlatform =
parsePlatformFilePath(mod.path, this._platforms).platform ||
GENERIC_PLATFORM;
existingModule = moduleMap[modulePlatform];
moduleMap[modulePlatform] = mod;
}
if (existingModule && existingModule.path !== mod.path) {
throw new Error(
`@providesModule naming collision:\n` +
` Duplicate module name: ${name}\n` +
` Paths: ${mod.path} collides with ${existingModule.path}\n\n` +
'This error is caused by a @providesModule declaration ' +
'with the same name across two different files.',
);
}
}
}
module.exports = HasteMap;

View File

@ -30,12 +30,12 @@ export type ModuleMap = {
getModule( getModule(
name: string, name: string,
platform: string | null, platform: string | null,
supportsNativePlatform: boolean, supportsNativePlatform: ?boolean,
): ?string, ): ?string,
getPackage( getPackage(
name: string, name: string,
platform: string | null, platform: string | null,
supportsNativePlatform: boolean, supportsNativePlatform: ?boolean,
): ?string, ): ?string,
}; };