This repository has been archived by the owner on Jan 13, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 70
/
Module.js
232 lines (199 loc) · 5.96 KB
/
Module.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/**
* 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 crypto = require('crypto');
const docblock = require('./DependencyGraph/docblock');
const isAbsolutePath = require('absolute-path');
const jsonStableStringify = require('json-stable-stringify');
const path = require('./fastpath');
const extractRequires = require('./lib/extractRequires');
class Module {
constructor({
file,
fastfs,
moduleCache,
cache,
extractor = extractRequires,
transformCode,
depGraphHelpers,
options,
}) {
if (!isAbsolutePath(file)) {
throw new Error('Expected file to be absolute path but got ' + file);
}
this.path = file;
this.type = 'Module';
this._fastfs = fastfs;
this._moduleCache = moduleCache;
this._cache = cache;
this._extractor = extractor;
this._transformCode = transformCode;
this._depGraphHelpers = depGraphHelpers;
this._options = options;
}
isHaste() {
return this._cache.get(
this.path,
'isHaste',
() => this._readDocBlock().then(({id}) => !!id)
);
}
getCode(transformOptions) {
return this.read(transformOptions).then(({code}) => code);
}
getMap(transformOptions) {
return this.read(transformOptions).then(({map}) => map);
}
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(transformOptions) {
return this.read(transformOptions).then(({dependencies}) => dependencies);
}
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(contentPromise) {
if (!this._docBlock) {
if (!contentPromise) {
contentPromise = this._fastfs.readWhile(this.path, whileInDocBlock);
}
this._docBlock = contentPromise
.then(docBlock => this._parseDocBlock(docBlock));
}
return this._docBlock;
}
read(transformOptions) {
return this._cache.get(
this.path,
cacheKey('moduleData', transformOptions),
() => {
const fileContentPromise = this._fastfs.readFile(this.path);
return Promise.all([
fileContentPromise,
this._readDocBlock(fileContentPromise),
]).then(([source, {id, moduleDocBlock}]) => {
// Ignore requires in JSON files or generated code. An example of this
// is prebuilt files like the SourceMap library.
const extern = this.isJSON() || 'extern' in moduleDocBlock;
if (extern) {
transformOptions = {...transformOptions, extern};
}
const transformCode = this._transformCode;
const codePromise = transformCode
? transformCode(this, source, transformOptions)
: Promise.resolve({code: source});
return codePromise.then(result => {
const {
code,
dependencies = extern ? [] : this._extractor(code).deps.sync,
} = result;
if (this._options && this._options.cacheTransformResults === false) {
return {dependencies};
} else {
return {...result, dependencies, id, source};
}
});
});
}
);
}
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);
}
// use weak map to speed up hash creation of known objects
const knownHashes = new WeakMap();
function stableObjectHash(object) {
let digest = knownHashes.get(object);
if (!digest) {
digest = crypto.createHash('md5')
.update(jsonStableStringify(object))
.digest('base64');
knownHashes.set(object, digest);
}
return digest;
}
function cacheKey(field, transformOptions) {
return transformOptions !== undefined
? stableObjectHash(transformOptions) + '\0' + field
: field;
}
module.exports = Module;