packager: get rid of type `any` in ResolutionRequest

Summary:
One of my changeset broke the "ModuleGraph" code without warning earlier because we are using `any`, that equivalent to having no typing at all. This changeset fixes the types so that `ResolutionRequest` is exactly what it actually is: a class usable for any `Module`-looking class, including the normal one, and the "ModuleGraph" one used for Buck builds. That way, the ModuleGraph's `Module` is typechecked against `Moduleish`.

Concretely this change mostly migrates the `Module` to its generic parameter counterpart `TModule` inside `ResolutionRequest`.

Reviewed By: kentaromiura

Differential Revision: D4826256

fbshipit-source-id: fcd7ca08ac6c35e4e9ca983e2aab260e352bcb4e
This commit is contained in:
Jean Lauliac 2017-04-04 07:06:25 -07:00 committed by Facebook Github Bot
parent fc8c5130be
commit c1a2a5e942
10 changed files with 64 additions and 41 deletions

View File

@ -322,12 +322,12 @@ class Bundler {
moduleSystemDeps?: Array<Module>, moduleSystemDeps?: Array<Module>,
onProgress?: () => void, onProgress?: () => void,
platform?: ?string, platform?: ?string,
resolutionResponse?: ResolutionResponse, resolutionResponse?: ResolutionResponse<Module>,
runBeforeMainModule?: boolean, runBeforeMainModule?: boolean,
runModule?: boolean, runModule?: boolean,
unbundle?: boolean, unbundle?: boolean,
}) { }) {
const onResolutionResponse = (response: ResolutionResponse) => { const onResolutionResponse = (response: ResolutionResponse<Module>) => {
/* $FlowFixMe: looks like ResolutionResponse is monkey-patched /* $FlowFixMe: looks like ResolutionResponse is monkey-patched
* with `getModuleId`. */ * with `getModuleId`. */
bundle.setMainModuleId(response.getModuleId(getMainModule(response))); bundle.setMainModuleId(response.getModuleId(getMainModule(response)));
@ -342,7 +342,7 @@ class Bundler {
const finalizeBundle = ({bundle: finalBundle, transformedModules, response, modulesByName}: { const finalizeBundle = ({bundle: finalBundle, transformedModules, response, modulesByName}: {
bundle: Bundle, bundle: Bundle,
transformedModules: Array<{module: Module, transformed: ModuleTransport}>, transformedModules: Array<{module: Module, transformed: ModuleTransport}>,
response: ResolutionResponse, response: ResolutionResponse<Module>,
modulesByName: {[name: string]: Module}, modulesByName: {[name: string]: Module},
}) => }) =>
this._resolverPromise.then(resolver => Promise.all( this._resolverPromise.then(resolver => Promise.all(

View File

@ -44,6 +44,10 @@ module.exports = class Module {
isHaste() { isHaste() {
return this.hasteID.then(Boolean); return this.hasteID.then(Boolean);
} }
hash() {
throw new Error('not implemented');
}
}; };
function getName(path) { function getName(path) {

View File

@ -101,7 +101,7 @@ class Resolver {
transformOptions: TransformOptions, transformOptions: TransformOptions,
onProgress?: ?(finishedModules: number, totalModules: number) => mixed, onProgress?: ?(finishedModules: number, totalModules: number) => mixed,
getModuleId: mixed, getModuleId: mixed,
): Promise<ResolutionResponse> { ): Promise<ResolutionResponse<Module>> {
const {platform, recursive = true} = options; const {platform, recursive = true} = options;
return this._depGraph.getDependencies({ return this._depGraph.getDependencies({
entryPath, entryPath,
@ -151,7 +151,7 @@ class Resolver {
} }
resolveRequires( resolveRequires(
resolutionResponse: ResolutionResponse, resolutionResponse: ResolutionResponse<Module>,
module: Module, module: Module,
code: string, code: string,
dependencyOffsets: Array<number> = [], dependencyOffsets: Array<number> = [],
@ -195,7 +195,7 @@ class Resolver {
dev = true, dev = true,
minify = false, minify = false,
}: { }: {
resolutionResponse: ResolutionResponse, resolutionResponse: ResolutionResponse<Module>,
module: Module, module: Module,
name: string, name: string,
map: SourceMap, map: SourceMap,

View File

@ -309,7 +309,7 @@ class Server {
getDependencies(options: { getDependencies(options: {
entryFile: string, entryFile: string,
platform: ?string, platform: ?string,
}): Promise<ResolutionResponse> { }): Promise<ResolutionResponse<Module>> {
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
if (!options.platform) { if (!options.platform) {
options.platform = getPlatformExtension(options.entryFile); options.platform = getPlatformExtension(options.entryFile);

View File

@ -23,8 +23,6 @@ const getAssetDataFromName = require('../lib/getAssetDataFromName');
import type {HasteFS} from '../types'; import type {HasteFS} from '../types';
import type DependencyGraphHelpers from './DependencyGraphHelpers'; import type DependencyGraphHelpers from './DependencyGraphHelpers';
import type Module from '../Module';
import type ModuleCache from '../ModuleCache';
import type ResolutionResponse from './ResolutionResponse'; import type ResolutionResponse from './ResolutionResponse';
type DirExistsFn = (filePath: string) => boolean; type DirExistsFn = (filePath: string) => boolean;
@ -37,14 +35,31 @@ export type ModuleMap = {
getPackage(name: string, platform: string, supportsNativePlatform: boolean): ?string, getPackage(name: string, platform: string, supportsNativePlatform: boolean): ?string,
}; };
type Options = { type Packageish = {
redirectRequire(toModuleName: string): string | false,
getMain(): string,
+root: string,
};
type Moduleish = {
+path: string,
getPackage(): ?Packageish,
hash(): string,
};
type ModuleishCache<TModule, TPackage> = {
getPackage(name: string, platform?: string, supportsNativePlatform?: boolean): TPackage,
getModule(path: string): TModule,
getAssetModule(path: string): TModule,
};
type Options<TModule, TPackage> = {
dirExists: DirExistsFn, dirExists: DirExistsFn,
entryPath: string, entryPath: string,
extraNodeModules: ?Object, extraNodeModules: ?Object,
hasteFS: HasteFS, hasteFS: HasteFS,
helpers: DependencyGraphHelpers, helpers: DependencyGraphHelpers,
// TODO(cpojer): Remove 'any' type. This is used for ModuleGraph/node-haste moduleCache: ModuleishCache<TModule, TPackage>,
moduleCache: ModuleCache | any,
moduleMap: ModuleMap, moduleMap: ModuleMap,
platform: string, platform: string,
platforms: Set<string>, platforms: Set<string>,
@ -67,14 +82,14 @@ function tryResolveSync<T>(action: () => T, secondaryAction: () => T): T {
} }
} }
class ResolutionRequest { class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
_dirExists: DirExistsFn; _dirExists: DirExistsFn;
_entryPath: string; _entryPath: string;
_extraNodeModules: ?Object; _extraNodeModules: ?Object;
_hasteFS: HasteFS; _hasteFS: HasteFS;
_helpers: DependencyGraphHelpers; _helpers: DependencyGraphHelpers;
_immediateResolutionCache: {[key: string]: Module}; _immediateResolutionCache: {[key: string]: TModule};
_moduleCache: ModuleCache; _moduleCache: ModuleishCache<TModule, TPackage>;
_moduleMap: ModuleMap; _moduleMap: ModuleMap;
_platform: string; _platform: string;
_platforms: Set<string>; _platforms: Set<string>;
@ -92,7 +107,7 @@ class ResolutionRequest {
platform, platform,
platforms, platforms,
preferNativePlatform, preferNativePlatform,
}: Options) { }: Options<TModule, TPackage>) {
this._dirExists = dirExists; this._dirExists = dirExists;
this._entryPath = entryPath; this._entryPath = entryPath;
this._extraNodeModules = extraNodeModules; this._extraNodeModules = extraNodeModules;
@ -115,8 +130,7 @@ class ResolutionRequest {
}); });
} }
// TODO(cpojer): Remove 'any' type. This is used for ModuleGraph/node-haste resolveDependency(fromModule: TModule, toModuleName: string): TModule {
resolveDependency(fromModule: Module | any, toModuleName: string): Module {
const resHash = resolutionHash(fromModule.path, toModuleName); const resHash = resolutionHash(fromModule.path, toModuleName);
const immediateResolution = this._immediateResolutionCache[resHash]; const immediateResolution = this._immediateResolutionCache[resHash];
@ -141,7 +155,10 @@ class ResolutionRequest {
return cacheResult(this._resolveNodeDependency(fromModule, toModuleName)); return cacheResult(this._resolveNodeDependency(fromModule, toModuleName));
} }
resolveModuleDependencies(module: Module, dependencyNames: Array<string>): [Array<string>, Array<Module>] { resolveModuleDependencies(
module: TModule,
dependencyNames: Array<string>,
): [Array<string>, Array<TModule>] {
const dependencies = dependencyNames.map(name => this.resolveDependency(module, name)); const dependencies = dependencyNames.map(name => this.resolveDependency(module, name));
return [dependencyNames, dependencies]; return [dependencyNames, dependencies];
} }
@ -152,7 +169,7 @@ class ResolutionRequest {
onProgress, onProgress,
recursive = true, recursive = true,
}: { }: {
response: ResolutionResponse, response: ResolutionResponse<TModule>,
transformOptions: Object, transformOptions: Object,
onProgress?: ?(finishedModules: number, totalModules: number) => mixed, onProgress?: ?(finishedModules: number, totalModules: number) => mixed,
recursive: boolean, recursive: boolean,
@ -250,13 +267,14 @@ class ResolutionRequest {
}); });
} }
_resolveHasteDependency(fromModule: Module, toModuleName: string): Module { _resolveHasteDependency(fromModule: TModule, toModuleName: string): TModule {
toModuleName = normalizePath(toModuleName); toModuleName = normalizePath(toModuleName);
const pck = fromModule.getPackage(); const pck = fromModule.getPackage();
let realModuleName; let realModuleName;
if (pck) { if (pck) {
realModuleName = pck.redirectRequire(toModuleName); /* $FlowFixMe: redirectRequire can actually return `false` for exclusions */
realModuleName = (pck.redirectRequire(toModuleName): string);
} else { } else {
realModuleName = toModuleName; realModuleName = toModuleName;
} }
@ -308,7 +326,7 @@ class ResolutionRequest {
); );
} }
_redirectRequire(fromModule: Module, modulePath: string): string | false { _redirectRequire(fromModule: TModule, modulePath: string): string | false {
const pck = fromModule.getPackage(); const pck = fromModule.getPackage();
if (pck) { if (pck) {
return pck.redirectRequire(modulePath); return pck.redirectRequire(modulePath);
@ -316,7 +334,7 @@ class ResolutionRequest {
return modulePath; return modulePath;
} }
_resolveFileOrDir(fromModule: Module, toModuleName: string): Module { _resolveFileOrDir(fromModule: TModule, toModuleName: string): TModule {
const potentialModulePath = isAbsolutePath(toModuleName) ? const potentialModulePath = isAbsolutePath(toModuleName) ?
resolveWindowsPath(toModuleName) : resolveWindowsPath(toModuleName) :
path.join(path.dirname(fromModule.path), toModuleName); path.join(path.dirname(fromModule.path), toModuleName);
@ -336,7 +354,7 @@ class ResolutionRequest {
); );
} }
_resolveNodeDependency(fromModule: Module, toModuleName: string): Module { _resolveNodeDependency(fromModule: TModule, toModuleName: string): TModule {
if (isRelativeImport(toModuleName) || isAbsolutePath(toModuleName)) { if (isRelativeImport(toModuleName) || isAbsolutePath(toModuleName)) {
return this._resolveFileOrDir(fromModule, toModuleName); return this._resolveFileOrDir(fromModule, toModuleName);
} }
@ -408,7 +426,7 @@ class ResolutionRequest {
* This is written as a separate function because "try..catch" blocks cause * This is written as a separate function because "try..catch" blocks cause
* the entire surrounding function to be deoptimized. * the entire surrounding function to be deoptimized.
*/ */
_tryResolveNodeDep(searchPath: string, fromModule: Module, toModuleName: string): ?Module { _tryResolveNodeDep(searchPath: string, fromModule: TModule, toModuleName: string): ?TModule {
try { try {
return tryResolveSync( return tryResolveSync(
() => this._loadAsFile(searchPath, fromModule, toModuleName), () => this._loadAsFile(searchPath, fromModule, toModuleName),
@ -422,7 +440,7 @@ class ResolutionRequest {
} }
} }
_loadAsFile(potentialModulePath: string, fromModule: Module, toModule: string): Module { _loadAsFile(potentialModulePath: string, fromModule: TModule, toModule: string): TModule {
if (this._helpers.isAssetFile(potentialModulePath)) { if (this._helpers.isAssetFile(potentialModulePath)) {
let dirname = path.dirname(potentialModulePath); let dirname = path.dirname(potentialModulePath);
if (!this._dirExists(dirname)) { if (!this._dirExists(dirname)) {
@ -480,7 +498,7 @@ class ResolutionRequest {
return this._moduleCache.getModule(file); return this._moduleCache.getModule(file);
} }
_loadAsDir(potentialDirPath: string, fromModule: Module, toModule: string): Module { _loadAsDir(potentialDirPath: string, fromModule: TModule, toModule: string): TModule {
if (!this._dirExists(potentialDirPath)) { if (!this._dirExists(potentialDirPath)) {
throw new UnableToResolveError( throw new UnableToResolveError(
fromModule, fromModule,

View File

@ -16,10 +16,10 @@ import type Module from '../Module';
const NO_OPTIONS = {}; const NO_OPTIONS = {};
class ResolutionResponse { class ResolutionResponse<TModule: {hash(): string}> {
transformOptions: TransformOptions; transformOptions: TransformOptions;
dependencies: Array<Module>; dependencies: Array<TModule>;
mainModuleId: ?(number | string); mainModuleId: ?(number | string);
mocks: mixed; mocks: mixed;
numPrependedDependencies: number; numPrependedDependencies: number;
@ -29,7 +29,7 @@ class ResolutionResponse {
_mappings: {}; _mappings: {};
_finalized: boolean; _finalized: boolean;
_mainModule: ?Module; _mainModule: ?TModule;
constructor({transformOptions}: {transformOptions: TransformOptions}) { constructor({transformOptions}: {transformOptions: TransformOptions}) {
this.transformOptions = transformOptions; this.transformOptions = transformOptions;
@ -42,10 +42,10 @@ class ResolutionResponse {
} }
copy(properties: { copy(properties: {
dependencies?: Array<Module>, dependencies?: Array<TModule>,
mainModuleId?: number, mainModuleId?: number,
mocks?: mixed, mocks?: mixed,
}): ResolutionResponse { }): ResolutionResponse<TModule> {
const { const {
dependencies = this.dependencies, dependencies = this.dependencies,
mainModuleId = this.mainModuleId, mainModuleId = this.mainModuleId,
@ -80,7 +80,7 @@ class ResolutionResponse {
} }
} }
finalize(): ResolutionResponse { finalize(): ResolutionResponse<TModule> {
/* $FlowFixMe: _mainModule is not initialized in the constructor. */ /* $FlowFixMe: _mainModule is not initialized in the constructor. */
return this._mainModule.getName().then(id => { return this._mainModule.getName().then(id => {
this.mainModuleId = id; this.mainModuleId = id;
@ -89,7 +89,7 @@ class ResolutionResponse {
}); });
} }
pushDependency(module: Module) { pushDependency(module: TModule) {
this._assertNotFinalized(); this._assertNotFinalized();
if (this.dependencies.length === 0) { if (this.dependencies.length === 0) {
this._mainModule = module; this._mainModule = module;
@ -98,7 +98,7 @@ class ResolutionResponse {
this.dependencies.push(module); this.dependencies.push(module);
} }
prependDependency(module: Module) { prependDependency(module: TModule) {
this._assertNotFinalized(); this._assertNotFinalized();
this.dependencies.unshift(module); this.dependencies.unshift(module);
this.numPrependedDependencies += 1; this.numPrependedDependencies += 1;
@ -122,7 +122,7 @@ class ResolutionResponse {
this.mocks = mocks; this.mocks = mocks;
} }
getResolvedDependencyPairs(module: Module) { getResolvedDependencyPairs(module: TModule) {
this._assertFinalized(); this._assertFinalized();
return this._mappings[module.hash()]; return this._mappings[module.hash()];
} }

View File

@ -226,7 +226,8 @@ class Module {
if (hasteImpl !== undefined) { if (hasteImpl !== undefined) {
const {enforceHasteNameMatches} = hasteImpl; const {enforceHasteNameMatches} = hasteImpl;
if (enforceHasteNameMatches) { if (enforceHasteNameMatches) {
/* $FlowFixMe: this rely on the above if being executed, that is fragile. Rework the algo. */ /* $FlowFixMe: this rely on the above if being executed, that is fragile.
* Rework the algo. */
enforceHasteNameMatches(this.path, this._hasteNameCache.hasteName); enforceHasteNameMatches(this.path, this._hasteNameCache.hasteName);
} }
this._hasteNameCache = {hasteName: hasteImpl.getHasteName(this.path)}; this._hasteNameCache = {hasteName: hasteImpl.getHasteName(this.path)};

View File

@ -114,7 +114,7 @@ class ModuleCache {
return this._moduleCache[filePath]; return this._moduleCache[filePath];
} }
getPackage(filePath: string) { getPackage(filePath: string): Package {
if (!this._packageCache[filePath]) { if (!this._packageCache[filePath]) {
this._packageCache[filePath] = new Package({ this._packageCache[filePath] = new Package({
file: filePath, file: filePath,

View File

@ -44,7 +44,7 @@ class Package {
this._content = null; this._content = null;
} }
getMain() { getMain(): string {
const json = this.read(); const json = this.read();
var replacements = getReplacements(json); var replacements = getReplacements(json);
if (typeof replacements === 'string') { if (typeof replacements === 'string') {

View File

@ -211,7 +211,7 @@ class DependencyGraph extends EventEmitter {
transformOptions: TransformOptions, transformOptions: TransformOptions,
onProgress?: ?(finishedModules: number, totalModules: number) => mixed, onProgress?: ?(finishedModules: number, totalModules: number) => mixed,
recursive: boolean, recursive: boolean,
}): Promise<ResolutionResponse> { }): Promise<ResolutionResponse<Module>> {
platform = this._getRequestPlatform(entryPath, platform); platform = this._getRequestPlatform(entryPath, platform);
const absPath = this._getAbsolutePath(entryPath); const absPath = this._getAbsolutePath(entryPath);
const dirExists = filePath => { const dirExists = filePath => {