UNPKG

4.26 kBJavaScriptView Raw
1var detective = require('detective'),
2 detectiveAmd = require('detective-amd'),
3 gmt = require('module-definition'),
4 required = require('required'),
5 fs = require('fs'),
6 path = require('path'),
7 q = require('q');
8
9// Calls the passed callback with a list of candidtate root filenames
10module.exports = function (directory, opt, cb) {
11 // opt is an optional configuration object
12 if (typeof opt === 'function') {
13 cb = opt;
14 opt = {};
15 } else {
16 opt = opt || {};
17 }
18
19 var jsFiles = getAllJSFiles(directory, opt);
20
21 // Given a directory, get the cumulative non-core degrees of all .js files
22 var getAllDegrees = jsFiles.map(getCumulativeDegree);
23
24 q.all(getAllDegrees)
25 .then(function (results) {
26 // The app root is the file with the largest cumulative degree
27 // There might be more than one root if there are independent apps
28 // located within the same directory (perhaps lazily loaded)
29 var maxDegree = Math.max.apply(Math, results),
30 candidateRootFiles = jsFiles.filter(function (jsFile, idx) {
31 // Its degree is the max degree
32 return results[idx] === maxDegree;
33 });
34
35 cb && cb(candidateRootFiles);
36 });
37};
38
39// Resolves with the sum of non-core dependencies for the file's dependency graph
40function getCumulativeDegree(jsFile) {
41 return getDependencies(jsFile)
42 // Get degree of root file + each non-core dependency
43 .then(getNonCoreDegree);
44}
45
46// Resolve with a list of dependencies for the given file
47function getDependencies(jsFile) {
48 return getModuleType(jsFile)
49 // Configure required options
50 .then(function (moduleType) {
51 return {
52 detective: getAppropriateDetective(moduleType),
53 ignoreMissing: true
54 };
55 })
56 // Use required to get the file's dependency list
57 .then(function (options) {
58 var deferred = q.defer();
59
60 required(jsFile, options, function (err, deps) {
61 if (err) console.log(jsFile, err);
62
63 deferred.resolve(deps || []);
64 });
65
66 return deferred.promise;
67 });
68}
69
70// Returns the number (degree) of non-core dependencies
71// by traversing the entire dependency tree
72function getNonCoreDegree(deps) {
73 var count = 0;
74
75 if (! deps || ! deps.length) return count;
76
77 deps.forEach(function (dep) {
78 if (! dep || dep.core) return;
79
80 // Count the current dependency
81 count++;
82
83 // Count the sub dependencies
84 if (dep.deps) {
85 count += getNonCoreDegree(dep.deps);
86 }
87 });
88
89 return count;
90}
91
92// Returns the detective function appropriate to
93// the given module type
94// Precond: moduleType is 'commonjs' or 'amd'
95function getAppropriateDetective(moduleType) {
96 switch (moduleType) {
97 case 'commonjs':
98 return detective;
99 case 'amd':
100 return detectiveAmd;
101 }
102
103 return null;
104}
105
106// Promisified wrapper of gmt
107function getModuleType(jsFile) {
108 var deferred = q.defer();
109
110 gmt(jsFile, function (moduleType) {
111 deferred.resolve(moduleType);
112 });
113
114 return deferred.promise;
115}
116
117// Returns a list of all JavaScript filepaths
118// relative to the given directory
119function getAllJSFiles(directory, opt) {
120 var jsFilePaths = [];
121
122 fs.readdirSync(directory).forEach(function (filename) {
123 var fullName = directory + '/' + filename,
124 isDirectory = fs.lstatSync(fullName).isDirectory(),
125 ext = path.extname(filename);
126
127 if (isDirectory) {
128 if (opt.ignore && ! shouldBeIgnored(filename, opt.ignore) ||
129 (! opt.ignore || ! opt.ignore.length)) {
130
131 jsFilePaths = jsFilePaths.concat(getAllJSFiles(fullName, opt));
132 }
133 } else if (ext === '.js') {
134 jsFilePaths.push(fullName);
135 }
136 });
137
138 return jsFilePaths;
139}
140
141// This is shared with node-unique-extensions but should be
142// within exposed within its own module.
143function shouldBeIgnored(filename, exclusions) {
144 var result = false;
145
146 exclusions = exclusions || [];
147
148 for (var i = 0, l = exclusions.length; i < l; i++) {
149 // If any part of the file's name (absolute or relative)
150 // contains an excluded folder, it should be ignored
151 if (filename.indexOf(exclusions[i]) !== -1) {
152 result = true;
153 break;
154 }
155 }
156
157 return result;
158}
\No newline at end of file