packager: Module.js: make read()-based API sync

Reviewed By: davidaurelio

Differential Revision: D4746218

fbshipit-source-id: 1a36bd00a47a6b871cc77433b2325a11e90035b2
This commit is contained in:
Jean Lauliac 2017-03-22 08:28:18 -07:00 committed by Facebook Github Bot
parent e22898bcff
commit 5eb954f660

View File

@ -74,6 +74,8 @@ export type ConstructorArgs = {
transformCode: ?TransformCode, transformCode: ?TransformCode,
}; };
type DocBlock = {+[key: string]: string};
class Module { class Module {
path: string; path: string;
@ -88,9 +90,9 @@ class Module {
_reporter: Reporter; _reporter: Reporter;
_globalCache: ?GlobalTransformCache; _globalCache: ?GlobalTransformCache;
_docBlock: Promise<{[key: string]: string}>; _docBlock: ?DocBlock;
_hasteName: Promise<string | void>; _hasteNameCache: ?{+hasteName: ?string};
_readSourceCodePromise: Promise<string>; _sourceCode: ?string;
_readPromises: Map<string, Promise<ReadResult>>; _readPromises: Map<string, Promise<ReadResult>>;
constructor({ constructor({
@ -127,7 +129,7 @@ class Module {
return this._cache.get( return this._cache.get(
this.path, this.path,
'isHaste', 'isHaste',
() => this._getHasteName().then(name => !!name), () => Promise.resolve(this._getHasteName() != null),
); );
} }
@ -143,8 +145,9 @@ class Module {
return this._cache.get( return this._cache.get(
this.path, this.path,
'name', 'name',
() => this._getHasteName().then(name => { () => Promise.resolve().then(() => {
if (name !== undefined) { const name = this._getHasteName();
if (name != null) {
return name; return name;
} }
@ -183,67 +186,63 @@ class Module {
invalidate() { invalidate() {
this._cache.invalidate(this.path); this._cache.invalidate(this.path);
this._readPromises.clear(); this._readPromises.clear();
this._sourceCode = null;
this._docBlock = null;
this._hasteNameCache = null;
} }
_readSourceCode() { _readSourceCode(): string {
if (!this._readSourceCodePromise) { if (this._sourceCode == null) {
this._readSourceCodePromise = new Promise( this._sourceCode = fs.readFileSync(this.path, 'utf8');
resolve => resolve(fs.readFileSync(this.path, 'utf8'))
);
} }
return this._readSourceCodePromise; return this._sourceCode;
} }
_readDocBlock() { _readDocBlock(): DocBlock {
if (!this._docBlock) { if (this._docBlock == null) {
this._docBlock = this._readSourceCode() this._docBlock = docblock.parseAsObject(this._readSourceCode());
.then(source => docblock.parseAsObject(source));
} }
return this._docBlock; return this._docBlock;
} }
_getHasteName(): Promise<string | void> { _getHasteName(): ?string {
if (!this._hasteName) { if (this._hasteNameCache != null) {
const hasteImpl = this._options.hasteImpl; return this._hasteNameCache.hasteName;
if (hasteImpl === undefined || hasteImpl.enforceHasteNameMatches) {
this._hasteName = this._readDocBlock().then(moduleDocBlock => {
const {providesModule} = moduleDocBlock;
return providesModule
&& !this._depGraphHelpers.isNodeModulesDir(this.path)
? /^\S+/.exec(providesModule)[0]
: undefined;
});
}
if (hasteImpl !== undefined) {
const {enforceHasteNameMatches} = hasteImpl;
if (enforceHasteNameMatches) {
this._hasteName = this._hasteName.then(providesModule => {
enforceHasteNameMatches(
this.path,
providesModule,
);
return hasteImpl.getHasteName(this.path);
});
} else {
this._hasteName = Promise.resolve(hasteImpl.getHasteName(this.path));
}
} else {
// Extract an id for the module if it's using @providesModule syntax
// and if it's NOT in node_modules (and not a whitelisted node_module).
// This handles the case where a project may have a dep that has @providesModule
// docblock comments, but doesn't want it to conflict with whitelisted @providesModule
// modules, such as react-haste, fbjs-haste, or react-native or with non-dependency,
// project-specific code that is using @providesModule.
this._hasteName = this._readDocBlock().then(moduleDocBlock => {
const {providesModule} = moduleDocBlock;
return providesModule
&& !this._depGraphHelpers.isNodeModulesDir(this.path)
? /^\S+/.exec(providesModule)[0]
: undefined;
});
}
} }
return this._hasteName; const hasteImpl = this._options.hasteImpl;
if (hasteImpl === undefined || hasteImpl.enforceHasteNameMatches) {
const moduleDocBlock = this._readDocBlock();
const {providesModule} = moduleDocBlock;
this._hasteNameCache = {
hasteName: providesModule && !this._depGraphHelpers.isNodeModulesDir(this.path)
? /^\S+/.exec(providesModule)[0]
: undefined,
};
}
if (hasteImpl !== undefined) {
const {enforceHasteNameMatches} = hasteImpl;
if (enforceHasteNameMatches) {
/* $FlowFixMe: this rely on the above if being executed, that is fragile. Rework the algo. */
enforceHasteNameMatches(this.path, this._hasteNameCache.hasteName);
}
this._hasteNameCache = {hasteName: hasteImpl.getHasteName(this.path)};
} else {
// Extract an id for the module if it's using @providesModule syntax
// and if it's NOT in node_modules (and not a whitelisted node_module).
// This handles the case where a project may have a dep that has @providesModule
// docblock comments, but doesn't want it to conflict with whitelisted @providesModule
// modules, such as react-haste, fbjs-haste, or react-native or with non-dependency,
// project-specific code that is using @providesModule.
const moduleDocBlock = this._readDocBlock();
const {providesModule} = moduleDocBlock;
this._hasteNameCache = {
hasteName:
providesModule && !this._depGraphHelpers.isNodeModulesDir(this.path)
? /^\S+/.exec(providesModule)[0]
: undefined,
};
}
return this._hasteNameCache.hasteName;
} }
/** /**
@ -251,7 +250,7 @@ class Module {
*/ */
_finalizeReadResult( _finalizeReadResult(
source: string, source: string,
id?: string, id: ?string,
extern: boolean, extern: boolean,
result: TransformedCode, result: TransformedCode,
): ReadResult { ): ReadResult {
@ -345,11 +344,10 @@ class Module {
if (promise != null) { if (promise != null) {
return promise; return promise;
} }
const freshPromise = Promise.all([ const freshPromise = Promise.resolve().then(() => {
this._readSourceCode(), const sourceCode = this._readSourceCode();
this._readDocBlock(), const moduleDocBlock = this._readDocBlock();
this._getHasteName(), const id = this._getHasteName();
]).then(([sourceCode, moduleDocBlock, id]) => {
// Ignore requires in JSON files or generated code. An example of this // Ignore requires in JSON files or generated code. An example of this
// is prebuilt files like the SourceMap library. // is prebuilt files like the SourceMap library.
const extern = this.isJSON() || 'extern' in moduleDocBlock; const extern = this.isJSON() || 'extern' in moduleDocBlock;