mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-02-12 22:29:41 +08:00
Summary: Uses `fastfs.readWhile` to build the haste map - cuts the time needed to build the haste map in half - we don’t need to allocate memory for all JS files in the tree - we only read in as much as necessary during startup - we only read files completely that are part of the bundle - we will be able to move the transform before dependency extraction public Reviewed By: martinbigio Differential Revision: D2890933 fb-gh-sync-id: 5fef6b53458e8bc95d0251d0bcf16821581a3362
221 lines
5.2 KiB
JavaScript
221 lines
5.2 KiB
JavaScript
/**
|
|
* 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.
|
|
*/
|
|
'use strict';
|
|
|
|
const docblock = require('./DependencyGraph/docblock');
|
|
const isAbsolutePath = require('absolute-path');
|
|
const path = require('path');
|
|
const extractRequires = require('./lib/extractRequires');
|
|
|
|
class Module {
|
|
|
|
constructor({
|
|
file,
|
|
fastfs,
|
|
moduleCache,
|
|
cache,
|
|
extractor = extractRequires,
|
|
transformCode,
|
|
depGraphHelpers,
|
|
}) {
|
|
if (!isAbsolutePath(file)) {
|
|
throw new Error('Expected file to be absolute path but got ' + file);
|
|
}
|
|
|
|
this.path = path.resolve(file);
|
|
this.type = 'Module';
|
|
|
|
this._fastfs = fastfs;
|
|
this._moduleCache = moduleCache;
|
|
this._cache = cache;
|
|
this._extractor = extractor;
|
|
this._transformCode = transformCode;
|
|
this._depGraphHelpers = depGraphHelpers;
|
|
}
|
|
|
|
isHaste() {
|
|
return this._cache.get(
|
|
this.path,
|
|
'isHaste',
|
|
() => this._readDocBlock().then(data => !!data.id)
|
|
);
|
|
}
|
|
|
|
getCode() {
|
|
return this.read().then(({code}) => code);
|
|
}
|
|
|
|
getName() {
|
|
return this._cache.get(
|
|
this.path,
|
|
'name',
|
|
() => this._readDocBlock().then(({id}) => {
|
|
if (id) {
|
|
return id;
|
|
}
|
|
|
|
const p = this.getPackage();
|
|
|
|
if (!p) {
|
|
// Name is full path
|
|
return this.path;
|
|
}
|
|
|
|
return p.getName()
|
|
.then(name => {
|
|
if (!name) {
|
|
return this.path;
|
|
}
|
|
|
|
return path.join(name, path.relative(p.root, this.path)).replace(/\\/g, '/');
|
|
});
|
|
})
|
|
);
|
|
}
|
|
|
|
getPackage() {
|
|
return this._moduleCache.getPackageForModule(this);
|
|
}
|
|
|
|
getDependencies() {
|
|
return this._cache.get(
|
|
this.path,
|
|
'dependencies',
|
|
() => this.read().then(data => data.dependencies)
|
|
);
|
|
}
|
|
|
|
getAsyncDependencies() {
|
|
return this._cache.get(
|
|
this.path,
|
|
'asyncDependencies',
|
|
() => this.read().then(data => data.asyncDependencies)
|
|
);
|
|
}
|
|
|
|
invalidate() {
|
|
this._cache.invalidate(this.path);
|
|
}
|
|
|
|
_parseDocBlock(docBlock) {
|
|
// 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 = docblock.parseAsObject(docBlock);
|
|
const provides = moduleDocBlock.providesModule || moduleDocBlock.provides;
|
|
|
|
const id = provides && !this._depGraphHelpers.isNodeModulesDir(this.path)
|
|
? /^\S+/.exec(provides)[0]
|
|
: undefined;
|
|
return [id, moduleDocBlock];
|
|
}
|
|
|
|
_readDocBlock() {
|
|
const reading = this._reading || this._docBlock;
|
|
if (reading) {
|
|
return reading;
|
|
}
|
|
this._docBlock = this._fastfs.readWhile(this.path, whileInDocBlock)
|
|
.then(docBlock => {
|
|
const [id] = this._parseDocBlock(docBlock);
|
|
return {id};
|
|
});
|
|
return this._docBlock;
|
|
}
|
|
|
|
read() {
|
|
if (this._reading) {
|
|
return this._reading;
|
|
}
|
|
|
|
this._reading = this._fastfs.readFile(this.path).then(content => {
|
|
const [id, moduleDocBlock] = this._parseDocBlock(content);
|
|
|
|
// Ignore requires in JSON files or generated code. An example of this
|
|
// is prebuilt files like the SourceMap library.
|
|
if (this.isJSON() || 'extern' in moduleDocBlock) {
|
|
return {
|
|
id,
|
|
dependencies: [],
|
|
asyncDependencies: [],
|
|
code: content,
|
|
};
|
|
} else {
|
|
const transformCode = this._transformCode;
|
|
const codePromise = transformCode
|
|
? transformCode(this, content)
|
|
: Promise.resolve({code: content});
|
|
|
|
return codePromise.then(({code, dependencies, asyncDependencies}) => {
|
|
const {deps} = this._extractor(code);
|
|
return {
|
|
id,
|
|
code,
|
|
dependencies: dependencies || deps.sync,
|
|
asyncDependencies: asyncDependencies || deps.async,
|
|
};
|
|
});
|
|
}
|
|
});
|
|
|
|
return this._reading;
|
|
}
|
|
|
|
hash() {
|
|
return `Module : ${this.path}`;
|
|
}
|
|
|
|
isJSON() {
|
|
return path.extname(this.path) === '.json';
|
|
}
|
|
|
|
isAsset() {
|
|
return false;
|
|
}
|
|
|
|
isPolyfill() {
|
|
return false;
|
|
}
|
|
|
|
isAsset_DEPRECATED() {
|
|
return false;
|
|
}
|
|
|
|
toJSON() {
|
|
return {
|
|
hash: this.hash(),
|
|
isJSON: this.isJSON(),
|
|
isAsset: this.isAsset(),
|
|
isAsset_DEPRECATED: this.isAsset_DEPRECATED(),
|
|
type: this.type,
|
|
path: this.path,
|
|
};
|
|
}
|
|
}
|
|
|
|
function whileInDocBlock(chunk, i, result) {
|
|
// consume leading whitespace
|
|
if (!/\S/.test(result)) {
|
|
return true;
|
|
}
|
|
|
|
// check for start of doc block
|
|
if (!/^\s*\/(\*{2}|\*?$)/.test(result)) {
|
|
return false;
|
|
}
|
|
|
|
// check for end of doc block
|
|
return !/\*\//.test(result);
|
|
}
|
|
|
|
module.exports = Module;
|