Initial commit
[yaffs-website] / node_modules / liftoff / index.js
1 const fs = require('fs');
2 const util = require('util');
3 const path = require('path');
4 const EE = require('events').EventEmitter;
5
6 const extend = require('extend');
7 const resolve = require('resolve');
8 const flaggedRespawn = require('flagged-respawn');
9 const isPlainObject = require('lodash.isplainobject');
10 const mapValues = require('lodash.mapvalues');
11 const fined = require('fined');
12
13 const findCwd = require('./lib/find_cwd');
14 const findConfig = require('./lib/find_config');
15 const fileSearch = require('./lib/file_search');
16 const parseOptions = require('./lib/parse_options');
17 const silentRequire = require('./lib/silent_require');
18 const buildConfigName = require('./lib/build_config_name');
19 const registerLoader = require('./lib/register_loader');
20
21
22 function Liftoff (opts) {
23   EE.call(this);
24   extend(this, parseOptions(opts));
25 }
26 util.inherits(Liftoff, EE);
27
28 Liftoff.prototype.requireLocal = function (module, basedir) {
29   try {
30     var result = require(resolve.sync(module, {basedir: basedir}));
31     this.emit('require', module, result);
32     return result;
33   } catch (e) {
34     this.emit('requireFail', module, e);
35   }
36 };
37
38 Liftoff.prototype.buildEnvironment = function (opts) {
39   opts = opts || {};
40
41   // get modules we want to preload
42   var preload = opts.require || [];
43
44   // ensure items to preload is an array
45   if (!Array.isArray(preload)) {
46     preload = [preload];
47   }
48
49   // make a copy of search paths that can be mutated for this run
50   var searchPaths = this.searchPaths.slice();
51
52   // calculate current cwd
53   var cwd = findCwd(opts);
54
55   // if cwd was provided explicitly, only use it for searching config
56   if (opts.cwd) {
57     searchPaths = [cwd];
58   } else {
59     // otherwise just search in cwd first
60     searchPaths.unshift(cwd);
61   }
62
63   // calculate the regex to use for finding the config file
64   var configNameSearch = buildConfigName({
65     configName: this.configName,
66     extensions: Object.keys(this.extensions)
67   });
68
69   // calculate configPath
70   var configPath = findConfig({
71     configNameSearch: configNameSearch,
72     searchPaths: searchPaths,
73     configPath: opts.configPath
74   });
75
76   // if we have a config path, save the directory it resides in.
77   var configBase;
78   if (configPath) {
79     configBase = path.dirname(configPath);
80     // if cwd wasn't provided explicitly, it should match configBase
81     if (!opts.cwd) {
82       cwd = configBase;
83     }
84     // resolve symlink if needed
85     if (fs.lstatSync(configPath).isSymbolicLink()) {
86       configPath = fs.realpathSync(configPath);
87     }
88   }
89
90   // TODO: break this out into lib/
91   // locate local module and package next to config or explicitly provided cwd
92   var modulePath, modulePackage;
93   try {
94     var delim = (process.platform === 'win32' ? ';' : ':'),
95         paths = (process.env.NODE_PATH ? process.env.NODE_PATH.split(delim) : []);
96     modulePath = resolve.sync(this.moduleName, {basedir: configBase || cwd, paths: paths});
97     modulePackage = silentRequire(fileSearch('package.json', [modulePath]));
98   } catch (e) {}
99
100   // if we have a configuration but we failed to find a local module, maybe
101   // we are developing against ourselves?
102   if (!modulePath && configPath) {
103     // check the package.json sibling to our config to see if its `name`
104     // matches the module we're looking for
105     var modulePackagePath = fileSearch('package.json', [configBase]);
106     modulePackage = silentRequire(modulePackagePath);
107     if (modulePackage && modulePackage.name === this.moduleName) {
108       // if it does, our module path is `main` inside package.json
109       modulePath = path.join(path.dirname(modulePackagePath), modulePackage.main || 'index.js');
110       cwd = configBase;
111     } else {
112       // clear if we just required a package for some other project
113       modulePackage = {};
114     }
115   }
116
117   // load any modules which were requested to be required
118   if (preload.length) {
119     // unique results first
120     preload.filter(function (value, index, self) {
121       return self.indexOf(value) === index;
122     }).forEach(function (dep) {
123       this.requireLocal(dep, findCwd(opts));
124     }, this);
125   }
126
127   var exts = this.extensions;
128   var eventEmitter = this;
129   registerLoader(eventEmitter, exts, configPath, cwd);
130
131   var configFiles = {};
132   if (isPlainObject(this.configFiles)) {
133     var notfound = { path: null };
134     configFiles = mapValues(this.configFiles, function(prop, name) {
135       var defaultObj = { name: name, cwd: cwd, extensions: exts };
136       return mapValues(prop, function(pathObj) {
137         var found = fined(pathObj, defaultObj) || notfound;
138         if (isPlainObject(found.extension)) {
139           registerLoader(eventEmitter, found.extension, found.path, cwd);
140         }
141         return found.path;
142       });
143     });
144   }
145
146   return {
147     cwd: cwd,
148     require: preload,
149     configNameSearch: configNameSearch,
150     configPath: configPath,
151     configBase: configBase,
152     modulePath: modulePath,
153     modulePackage: modulePackage || {},
154     configFiles: configFiles
155   };
156 };
157
158 Liftoff.prototype.handleFlags = function (cb) {
159   if (typeof this.v8flags === 'function') {
160     this.v8flags(function (err, flags) {
161       if (err) {
162         cb(err);
163       } else {
164         cb(null, flags);
165       }
166     });
167   } else {
168     process.nextTick(function () {
169       cb(null, this.v8flags);
170     }.bind(this));
171   }
172 };
173
174 Liftoff.prototype.launch = function (opts, fn) {
175   if (typeof fn !== 'function') {
176     throw new Error('You must provide a callback function.');
177   }
178   process.title = this.processTitle;
179
180   var completion = opts.completion;
181   if (completion && this.completions) {
182     return this.completions(completion);
183   }
184
185   this.handleFlags(function (err, flags) {
186     if (err) {
187       throw err;
188     } else {
189       if (flags) {
190         flaggedRespawn(flags, process.argv, function (ready, child) {
191           if (child !== process) {
192             this.emit('respawn', process.argv.filter(function (arg) {
193               var flag = arg.split('=')[0];
194               return flags.indexOf(flag) !== -1;
195             }.bind(this)), child);
196           }
197           if (ready) {
198             fn.call(this, this.buildEnvironment(opts));
199           }
200         }.bind(this));
201       } else {
202         fn.call(this, this.buildEnvironment(opts));
203       }
204     }
205   }.bind(this));
206 };
207
208
209
210 module.exports = Liftoff;