123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- const fs = require('fs');
- const util = require('util');
- const path = require('path');
- const EE = require('events').EventEmitter;
- const extend = require('extend');
- const resolve = require('resolve');
- const flaggedRespawn = require('flagged-respawn');
- const isPlainObject = require('lodash.isplainobject');
- const mapValues = require('lodash.mapvalues');
- const fined = require('fined');
- const findCwd = require('./lib/find_cwd');
- const findConfig = require('./lib/find_config');
- const fileSearch = require('./lib/file_search');
- const parseOptions = require('./lib/parse_options');
- const silentRequire = require('./lib/silent_require');
- const buildConfigName = require('./lib/build_config_name');
- const registerLoader = require('./lib/register_loader');
- function Liftoff (opts) {
- EE.call(this);
- extend(this, parseOptions(opts));
- }
- util.inherits(Liftoff, EE);
- Liftoff.prototype.requireLocal = function (module, basedir) {
- try {
- var result = require(resolve.sync(module, {basedir: basedir}));
- this.emit('require', module, result);
- return result;
- } catch (e) {
- this.emit('requireFail', module, e);
- }
- };
- Liftoff.prototype.buildEnvironment = function (opts) {
- opts = opts || {};
- // get modules we want to preload
- var preload = opts.require || [];
- // ensure items to preload is an array
- if (!Array.isArray(preload)) {
- preload = [preload];
- }
- // make a copy of search paths that can be mutated for this run
- var searchPaths = this.searchPaths.slice();
- // calculate current cwd
- var cwd = findCwd(opts);
- // if cwd was provided explicitly, only use it for searching config
- if (opts.cwd) {
- searchPaths = [cwd];
- } else {
- // otherwise just search in cwd first
- searchPaths.unshift(cwd);
- }
- // calculate the regex to use for finding the config file
- var configNameSearch = buildConfigName({
- configName: this.configName,
- extensions: Object.keys(this.extensions)
- });
- // calculate configPath
- var configPath = findConfig({
- configNameSearch: configNameSearch,
- searchPaths: searchPaths,
- configPath: opts.configPath
- });
- // if we have a config path, save the directory it resides in.
- var configBase;
- if (configPath) {
- configBase = path.dirname(configPath);
- // if cwd wasn't provided explicitly, it should match configBase
- if (!opts.cwd) {
- cwd = configBase;
- }
- // resolve symlink if needed
- if (fs.lstatSync(configPath).isSymbolicLink()) {
- configPath = fs.realpathSync(configPath);
- }
- }
- // TODO: break this out into lib/
- // locate local module and package next to config or explicitly provided cwd
- var modulePath, modulePackage;
- try {
- var delim = (process.platform === 'win32' ? ';' : ':'),
- paths = (process.env.NODE_PATH ? process.env.NODE_PATH.split(delim) : []);
- modulePath = resolve.sync(this.moduleName, {basedir: configBase || cwd, paths: paths});
- modulePackage = silentRequire(fileSearch('package.json', [modulePath]));
- } catch (e) {}
- // if we have a configuration but we failed to find a local module, maybe
- // we are developing against ourselves?
- if (!modulePath && configPath) {
- // check the package.json sibling to our config to see if its `name`
- // matches the module we're looking for
- var modulePackagePath = fileSearch('package.json', [configBase]);
- modulePackage = silentRequire(modulePackagePath);
- if (modulePackage && modulePackage.name === this.moduleName) {
- // if it does, our module path is `main` inside package.json
- modulePath = path.join(path.dirname(modulePackagePath), modulePackage.main || 'index.js');
- cwd = configBase;
- } else {
- // clear if we just required a package for some other project
- modulePackage = {};
- }
- }
- // load any modules which were requested to be required
- if (preload.length) {
- // unique results first
- preload.filter(function (value, index, self) {
- return self.indexOf(value) === index;
- }).forEach(function (dep) {
- this.requireLocal(dep, findCwd(opts));
- }, this);
- }
- var exts = this.extensions;
- var eventEmitter = this;
- registerLoader(eventEmitter, exts, configPath, cwd);
- var configFiles = {};
- if (isPlainObject(this.configFiles)) {
- var notfound = { path: null };
- configFiles = mapValues(this.configFiles, function(prop, name) {
- var defaultObj = { name: name, cwd: cwd, extensions: exts };
- return mapValues(prop, function(pathObj) {
- var found = fined(pathObj, defaultObj) || notfound;
- if (isPlainObject(found.extension)) {
- registerLoader(eventEmitter, found.extension, found.path, cwd);
- }
- return found.path;
- });
- });
- }
- return {
- cwd: cwd,
- require: preload,
- configNameSearch: configNameSearch,
- configPath: configPath,
- configBase: configBase,
- modulePath: modulePath,
- modulePackage: modulePackage || {},
- configFiles: configFiles
- };
- };
- Liftoff.prototype.handleFlags = function (cb) {
- if (typeof this.v8flags === 'function') {
- this.v8flags(function (err, flags) {
- if (err) {
- cb(err);
- } else {
- cb(null, flags);
- }
- });
- } else {
- process.nextTick(function () {
- cb(null, this.v8flags);
- }.bind(this));
- }
- };
- Liftoff.prototype.launch = function (opts, fn) {
- if (typeof fn !== 'function') {
- throw new Error('You must provide a callback function.');
- }
- process.title = this.processTitle;
- var completion = opts.completion;
- if (completion && this.completions) {
- return this.completions(completion);
- }
- this.handleFlags(function (err, flags) {
- if (err) {
- throw err;
- } else {
- if (flags) {
- flaggedRespawn(flags, process.argv, function (ready, child) {
- if (child !== process) {
- this.emit('respawn', process.argv.filter(function (arg) {
- var flag = arg.split('=')[0];
- return flags.indexOf(flag) !== -1;
- }.bind(this)), child);
- }
- if (ready) {
- fn.call(this, this.buildEnvironment(opts));
- }
- }.bind(this));
- } else {
- fn.call(this, this.buildEnvironment(opts));
- }
- }
- }.bind(this));
- };
- module.exports = Liftoff;
|