UNPKG

7.72 kBJavaScriptView Raw
1// © Copyright Mocoolka Corporation 2015,2017.
2// Node module: mocoolka-tools
3// LICENSE: MIT
4
5/** @module mocoolka-tools-module */
6let debug = require('debug')('mocoolka-tools');
7const fileTools = require('./file-tools.js');
8const fs = require('fs');
9const md5 = require('md5');
10const _ = require('lodash');
11const path = require('path');
12const assert = require('assert');
13/**
14 * get module name defined in package.json.
15 * @param {string} root module root path
16 */
17let getModuleName = (root)=> (
18 getModuleItem(root, 'name')
19);
20/**
21 * get package version defined in package.json
22 * @param {string} root module root path
23 */
24let getModuleVersion = (root)=> (
25 getModuleItem(root, 'version')
26);
27/**
28 * get Modolue item defined in package.json
29 * @param {string}root root module root path
30 * @param {string}itemName name defined in package
31 * @returns {*}
32 */
33let getModuleItem = (root, itemName)=> {
34 root = root ;
35 let item = null;
36 try {
37 item = require(path.join(root, 'package.json'))[itemName];
38 } catch (e) {}
39
40 return item;
41};
42
43/**
44 * Enumerate all JS files in this application
45 * @param {Function}
46 * param.content is a UTF8 string of each JS source file.
47 */
48const showDotCount = 500;
49const showCountCount = 10000;
50let enumeratedFilesCount = 0;
51
52/**
53 * enumerate Files in sync
54 * @param {string}rootDir enumerate Files in root directory
55 * @param {Array}blackList path array,path in blackList will skip
56 * @param {Array}targetFileType file will skip when file not in targetFileType
57 * @param {boolean}checkNodeModules if true check node_modules
58 * @param {Function}callback
59 */
60const enumerateFilesSync = (rootDir, blackList, targetFileType, checkNodeModules, callback)=> {
61 enumeratedFilesCount = 0;
62 let scannedFileNameHash = [];
63 assert(rootDir);
64 assert(typeof callback === 'function');
65 if (checkNodeModules === undefined)
66 checkNodeModules = false;
67
68 return enumerateFilesSyncPriv(rootDir, rootDir, blackList, targetFileType,
69 checkNodeModules, scannedFileNameHash, callback);
70};
71
72/**
73 * if file be scanned,return true,or add to scannedFileNameHash return false
74 * @param {string}fileName
75 * @param {Array}scannedFileNameHash
76 * @returns {boolean}
77 */
78let fileSyncScanned = (fileName, scannedFileNameHash)=> {
79 let realFileName = process.browser ? fileName : fs.realpathSync(fileName);
80 let fileNameHash = md5(realFileName);
81 if (scannedFileNameHash.indexOf(fileNameHash) >= 0) {
82 return true;
83 } else {
84 scannedFileNameHash.push(fileNameHash);
85 return false;
86 }
87};
88
89const enumerateFilesExcludeDicNames = ['test', 'node_modules', 'coverage'];
90/**
91 * enumerate file in module.
92 * @param {string}currentPath
93 * @param {string}rootDir
94 * @param {Array}blackList path array,path in blackList will skip
95 * @param {Array}targetFileType file will skip when file not in targetFileType
96 * @param {boolean}checkNodeModules if true check node_modules
97 * @param {Array}scannedFileNameHash
98 * @param {Function}callback
99 */
100const enumerateFilesSyncPriv = (currentPath, rootDir, blackList, targetFileType, checkNodeModules,
101 scannedFileNameHash, callback)=> {
102
103 currentPath = path.resolve(currentPath);
104
105
106 //if file scanned
107 if (fileSyncScanned(currentPath, scannedFileNameHash)) {
108 return;
109 }
110 rootDir = path.resolve(rootDir);
111 blackList = Array.isArray(blackList) ? blackList : [];
112 if (!Array.isArray(targetFileType)) targetFileType = [targetFileType];
113
114 //if currentPath in blackList skip
115 let skipDir = false;
116 blackList.map(function (part) {
117 if (typeof part !== 'string') return;
118 if (currentPath.indexOf(part) >= 0) skipDir = true;
119 });
120
121 if (skipDir) {
122
123 debug('*** skipping directory:%s', currentPath);
124 return;
125 }
126
127 let files = null;
128 try {
129 files = fs.readdirSync(currentPath);
130
131 } catch (e) {
132 return;
133 }
134
135 //each file in currentPath
136 files.map(item=> {
137 if (item.indexOf('.') === 0) return;
138 let child = path.join(currentPath, item);
139 let stats = null;
140 try {
141 stats = fs.statSync(child);
142 } catch (e) {
143 return;
144 }
145
146 if (stats.isDirectory()) {
147 item = item.toLowerCase();
148 if (enumerateFilesExcludeDicNames.includes(item))
149 return;
150
151 enumerateFilesSyncPriv(child, rootDir, blackList, targetFileType, checkNodeModules,
152 scannedFileNameHash, callback);
153 } else {
154 let fileType = fileTools.getFileExtName(item);
155 let fileMKType = fileTools.getMKFileExtName(item);
156
157 //check file type
158 if (!targetFileType.includes(fileType) && !targetFileType.includes(fileMKType)) return;
159 let content = fileTools.stripBom(fs.readFileSync(child, 'utf8'));
160 debug('examining file:%s', child);
161 if (checkNodeModules) {
162 enumeratedFilesCount++;
163 if (enumeratedFilesCount % showDotCount === 0) {
164 process.stdout.write('.');
165 if (enumeratedFilesCount % showCountCount === 0) {
166 process.stdout.write(' ' + enumeratedFilesCount.toString() + '\n');
167 }
168 }
169 }
170
171 callback(content, child);
172 };
173 });
174 if (checkNodeModules) {
175 let moduleRootPaths = resolveDependencies(currentPath, rootDir);
176
177 if (moduleRootPaths) {
178 moduleRootPaths.map(modulePath=> {
179
180 if (fileTools.IsExceedMaxDirectoryDepth(modulePath, rootDir)){
181 return;
182 }
183
184 enumerateFilesSyncPriv(modulePath, rootDir, blackList, targetFileType, checkNodeModules,
185 scannedFileNameHash, callback);
186 });
187 }
188 }
189};
190
191/**
192 * resolve module dependencies
193 * @param {string}currentDir function process current dictionary
194 * @param {string}rootDir function process root dictionary
195 * @param {Array}moduleRootPaths
196 * @returns {Array}
197 */
198const resolveDependencies = (currentDir, rootDir, moduleRootPaths)=> {
199 moduleRootPaths = moduleRootPaths || [];
200
201 let packageJson = path.join(currentDir, 'package.json');
202 let deps = null;
203 try {
204 //resolve package.json
205 deps = require(packageJson).dependencies;
206 } catch (e) {
207 return null;
208 }
209
210 if (deps === undefined || !deps) return null;
211 deps = Object.keys(deps);
212 if (deps.length === 0) return null;
213 deps.map(dep=> {
214 let depPath = moduleFileResolve(dep, currentDir, rootDir);
215 if (depPath && moduleRootPaths.indexOf(depPath) < 0) {
216 moduleRootPaths.push(depPath);
217 resolveDependencies(depPath, rootDir, moduleRootPaths);
218 }
219 });
220 moduleRootPaths = _.uniq(_.compact(moduleRootPaths));
221
222 return moduleRootPaths;
223};
224
225/**
226 * resolve module file
227 * @param {string}depName module name
228 * @param {string}currentDir
229 * @param {string}rootDir
230 * @returns {string|null}
231 */
232const moduleFileResolve = (depName, currentDir, rootDir)=> {
233 // simulates npm v3 dependency resolution
234 let depPath = null;
235 let stats = null;
236 try {
237 depPath = path.join(currentDir, 'node_modules', depName);
238 stats = fs.statSync(depPath);
239 } catch (e) {
240 stats = null;
241 try {
242 depPath = path.join(rootDir, 'node_modules', depName);
243 stats = fs.statSync(depPath);
244 } catch (e) {
245 return null;
246 }
247 }
248
249 if (!stats) return null;
250 return fileTools.unsymbolLink(depPath);
251};
252
253/**
254 * if path is root
255 * @param {string} path
256 * @returns {boolean}
257 */
258const isRootModule = (path)=> {
259 if (!global.MOCOOLKA_GLB) return false;
260 return path === global.MOCOOLKA_GLB.MASTER_ROOT_DIR;
261};
262
263const moduleTools = {
264 getModuleName,
265 getModuleVersion,
266 isRootModule,
267 enumerateFilesSync,
268};
269
270module.exports = moduleTools;
271