Skip to content

Commit

Permalink
feat: Add prepare/execute methods, deprecate launch method (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
sttk authored and phated committed Nov 22, 2021
1 parent de65669 commit a9d07b1
Show file tree
Hide file tree
Showing 14 changed files with 770 additions and 123 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ node_modules
# Optional npm cache directory
.npm

# Test creations
test/fixtures/symlink/mochafile.js

# Files generated by platform.
.DS_Store

Expand Down
124 changes: 104 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,25 +262,64 @@ const MyApp = new Liftoff({
});
```

## launch(opts, callback(env))
Launches your application with provided options, builds an environment, and invokes your callback, passing the calculated environment as the first argument.
### prepare(opts, callback(env))

Prepares the environment for your application with provided options, and invokes your callback with the calculated environment as the first argument. The environment can be modified before using it as the first argument to `execute`.

**Example Configuration w/ Options Parsing:**

##### Example Configuration w/ Options Parsing:
```js
const Liftoff = require('liftoff');
const MyApp = new Liftoff({name:'myapp'});
const argv = require('minimist')(process.argv.slice(2));
const invoke = function (env) {
const onExecute = function (env, argv) {
// Do post-execute things
};
const onPrepare = function (env) {
console.log('my environment is:', env);
console.log('my cli options are:', argv);
console.log('my liftoff config is:', this);
MyApp.execute(env, onExecute);
};
MyApp.launch({
MyApp.prepare({
cwd: argv.cwd,
configPath: argv.myappfile,
require: argv.require,
completion: argv.completion
}, invoke);
}, onPrepare);
```

**Example w/ modified environment**

```js
const Liftoff = require('liftoff');
const Hacker = new Liftoff({
name: 'hacker',
configFiles: {
'.hacker': {
home: { path: '.', cwd: '~' }
}
}
});
const onExecute = function (env, argv) {
// Do post-execute things
};
const onPrepare = function (env) {
env.configProps = ['home', 'cwd'].map(function(dirname) {
return env.configFiles['.hacker'][dirname]
}).filter(function(filePath) {
return Boolean(filePath);
}).reduce(function(config, filePath) {
return mergeDeep(config, require(filePath));
}, {});

if (env.configProps.hackerfile) {
env.configPath = path.resolve(env.configProps.hackerfile);
env.configBase = path.dirname(env.configPath);
}

Hacker.execute(env, onExecute);
};
Hacker.prepare({}, onPrepare);
```

#### opts.cwd
Expand Down Expand Up @@ -357,8 +396,66 @@ MyApp.launch({
myapp --require coffee-script/register
```

#### callback(env)

A function called after your environment is prepared. A good place to modify the environment before calling `execute`. When invoked, `this` will be your instance of Liftoff. The `env` param will contain the following keys:

- `cwd`: the current working directory
- `require`: an array of modules that liftoff tried to pre-load
- `configNameSearch`: the config files searched for
- `configPath`: the full path to your configuration file (if found)
- `configBase`: the base directory of your configuration file (if found)
- `modulePath`: the full path to the local module your project relies on (if found)
- `modulePackage`: the contents of the local module's package.json (if found)
- `configFiles`: an object of filepaths for each found config file (filepath values will be null if not found)

### execute(env, [forcedFlags], callback(env, argv))

A function to start your application, based on the `env` given. Optionally takes an array of `forcedFlags`, which will force a respawn with those node or V8 flags during startup. Invokes your callback with the environment and command-line arguments (minus node & v8 flags) after the application has been executed.

**Example:**

```js
const Liftoff = require('liftoff');
const MyApp = new Liftoff({name:'myapp'});
const onExecute = function (env, argv) {
// Do post-execute things
console.log('my environment is:', env);
console.log('my cli options are:', argv);
console.log('my liftoff config is:', this);
};
const onPrepare = function (env) {
var forcedFlags = ['--trace-deprecation'];
MyApp.execute(env, forcedFlags, onExecute);
};
MyApp.prepare({}, onPrepare);
```

#### callback(env, argv)

A function called after your application is executed. When invoked, `this` will be your instance of Liftoff, `argv` will be all command-line arguments (minus node & v8 flags), and `env` will contain the following keys:

- `cwd`: the current working directory
- `require`: an array of modules that liftoff tried to pre-load
- `configNameSearch`: the config files searched for
- `configPath`: the full path to your configuration file (if found)
- `configBase`: the base directory of your configuration file (if found)
- `modulePath`: the full path to the local module your project relies on (if found)
- `modulePackage`: the contents of the local module's package.json (if found)
- `configFiles`: an object of filepaths for each found config file (filepath values will be null if not found)

### launch(opts, callback(env, argv))

**Deprecated:** Please use `prepare` followed by `execute`. That's all this module does internally but those give you more control.

Launches your application with provided options, builds an environment, and invokes your callback, passing the calculated environment and command-line arguments (minus node & v8 flags) as the arguments.

Accepts any options that `prepare` allows, plus `opt.forcedFlags`.

#### opts.forcedFlags

**Deprecated:** If using `prepare`/`execute`, pass forcedFlags as the 2nd argument instead of using this option.

Allows you to force node or V8 flags during the launch. This is useful if you need to make sure certain flags will always be enabled or if you need to enable flags that don't show up in `opts.v8flags` (as these flags aren't validated against `opts.v8flags`).

If this is specified as a function, it will receive the built `env` as its only argument and must return a string or array of flags to force.
Expand All @@ -378,19 +475,6 @@ MyApp.launch({
myapp --trace-deprecation
```

#### callback(env)

A function to start your application. When invoked, `this` will be your instance of Liftoff. The `env` param will contain the following keys:

- `cwd`: the current working directory
- `require`: an array of modules that liftoff tried to pre-load
- `configNameSearch`: the config files searched for
- `configPath`: the full path to your configuration file (if found)
- `configBase`: the base directory of your configuration file (if found)
- `modulePath`: the full path to the local module your project relies on (if found)
- `modulePackage`: the contents of the local module's package.json (if found)
- `configFiles`: an object of filepaths for each found config file (filepath values will be null if not found)

### events

#### require(name, module)
Expand Down
53 changes: 42 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
var fs = require('fs');
var util = require('util');
var path = require('path');
var EE = require('events').EventEmitter;
Expand All @@ -18,7 +17,6 @@ var silentRequire = require('./lib/silent_require');
var buildConfigName = require('./lib/build_config_name');
var registerLoader = require('./lib/register_loader');
var getNodeFlags = require('./lib/get_node_flags');
var prepareConfig = require('./lib/prepare_config');

function Liftoff(opts) {
EE.call(this);
Expand Down Expand Up @@ -82,10 +80,6 @@ Liftoff.prototype.buildEnvironment = function(opts) {
if (!opts.cwd) {
cwd = configBase;
}
// resolve symlink if needed
if (fs.lstatSync(configPath).isSymbolicLink()) {
configPath = fs.realpathSync(configPath);
}
}

// TODO: break this out into lib/
Expand Down Expand Up @@ -162,26 +156,38 @@ Liftoff.prototype.handleFlags = function(cb) {
}
};

Liftoff.prototype.launch = function(opts, fn) {
Liftoff.prototype.prepare = 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);
}

var env = this.buildEnvironment(opts);

fn.call(this, env);
};

Liftoff.prototype.execute = function(env, forcedFlags, fn) {
if (typeof forcedFlags === 'function') {
fn = forcedFlags;
forcedFlags = undefined;
}
if (typeof fn !== 'function') {
throw new Error('You must provide a callback function.');
}

this.handleFlags(function(err, flags) {
if (err) {
throw err;
}
flags = flags || [];

var env = this.buildEnvironment(opts);

var forcedFlags = getNodeFlags.arrayOrFunction(opts.forcedFlags, env);
flaggedRespawn(flags, process.argv, forcedFlags, execute.bind(this));

function execute(ready, child, argv) {
Expand All @@ -190,11 +196,36 @@ Liftoff.prototype.launch = function(opts, fn) {
this.emit('respawn', execArgv, child);
}
if (ready) {
prepareConfig(this, env, opts);
preloadModules(this, env);
registerLoader(this, this.extensions, env.configPath, env.cwd);
fn.call(this, env, argv);
}
}
}.bind(this));
};

Liftoff.prototype.launch = function(opts, fn) {
if (typeof fn !== 'function') {
throw new Error('You must provide a callback function.');
}

var self = this;

self.prepare(opts, function(env) {
var forcedFlags = getNodeFlags.arrayOrFunction(opts.forcedFlags, env);
self.execute(env, forcedFlags, fn);
});
};

function preloadModules(inst, env) {
var basedir = env.cwd;
env.require.filter(toUnique).forEach(function(module) {
inst.requireLocal(module, basedir);
});
}

function toUnique(elem, index, array) {
return array.indexOf(elem) === index;
}

module.exports = Liftoff;
17 changes: 0 additions & 17 deletions lib/prepare_config.js

This file was deleted.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
],
"scripts": {
"pretest": "eslint .",
"test": "mocha -t 5000 -b -R spec test/index",
"test": "mocha -t 5000 -b -R spec test/index && npm run legacy-test",
"legacy-test": "mocha -t 5000 -b -R spec test/launch",
"cover": "nyc --reporter=lcov --reporter=text-summary npm test"
},
"dependencies": {
Expand Down
16 changes: 16 additions & 0 deletions test/fixtures/prepare-execute/nodeflags_only.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var Liftoff = require('../../..');

var Test = new Liftoff({
name: 'test',
});

Test.on('respawn', function(execArgv) {
console.log('saw respawn', execArgv);
});

Test.prepare({}, function(env) {
var forcedFlags = ['--lazy'];
Test.execute(env, forcedFlags, function(env, argv) {
console.error(argv.slice(1).join(' '));
});
});
15 changes: 15 additions & 0 deletions test/fixtures/prepare-execute/v8flags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var Liftoff = require('../../..');

var Test = new Liftoff({
name: 'test',
v8flags: ['--lazy']
});
Test.on('respawn', function(flags, proc) {
console.log('saw respawn');
});

Test.prepare({}, function(env) {
Test.execute(env, function(env) {
console.error(process.execArgv.join(''));
});
});
17 changes: 17 additions & 0 deletions test/fixtures/prepare-execute/v8flags_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
var Liftoff = require('../../..');

var Test = new Liftoff({
name: 'test',
v8flags: ['--harmony'],
});

Test.on('respawn', function(flags, proc) {
console.log('saw respawn', flags);
});

Test.prepare({}, function(env) {
var forcedFlags = ['--lazy'];
Test.execute(env, forcedFlags, function(env, argv) {
console.error(argv.slice(1).join(' '));
});
});
19 changes: 19 additions & 0 deletions test/fixtures/prepare-execute/v8flags_error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var Liftoff = require('../../..');

var Test = new Liftoff({
name: 'test',
v8flags: function(cb) {
process.nextTick(function() {
cb(new Error('v8flags error!'), ['--lazy']);
})
}
});
Test.on('respawn', function(flags, proc) {
console.log('saw respawn');
});

Test.prepare({}, function(env) {
Test.execute(env, function(env) {
console.error(process.execArgv.join(''));
});
});
19 changes: 19 additions & 0 deletions test/fixtures/prepare-execute/v8flags_function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var Liftoff = require('../../..');

var Test = new Liftoff({
name: 'test',
v8flags: function(cb) {
process.nextTick(function() {
cb(null, ['--lazy']);
})
}
});
Test.on('respawn', function(flags, proc) {
console.log('saw respawn');
});

Test.prepare({}, function(env) {
Test.execute(env, function(env) {
console.error(process.execArgv.join(''));
});
});
Loading

0 comments on commit a9d07b1

Please sign in to comment.