Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Lookup configPath in configFiles #128

Merged
merged 4 commits into from Mar 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 17 additions & 5 deletions README.md
Expand Up @@ -165,6 +165,8 @@ Default: `null`

An object of configuration files to find. Each property is keyed by the default basename of the file being found, and the value is an array of [path arguments](#path-arguments) of which the order indicates priority to find.

See [Config Files](#config-files) for the config file specification.

**Note:** This option is useful if, for example, you want to support an `.apprc` file in addition to an `appfile.js`. If you only need a single configuration file, you probably don't need this. In addition to letting you find multiple files, this option allows more fine-grained control over how configuration files are located.

Type: `Object`
Expand Down Expand Up @@ -337,11 +339,7 @@ const onExecute = function (env, argv) {
};
const onPrepare = function (env) {
const config = env.config['.hacker'];
if (config.hackerfile) {
env.configPath = path.resolve(config.hackerfile);
env.configBase = path.dirname(env.configPath);
}
Hacker.execute(env, onExecute);
Hacker.execute(env, config.forcedFlags, onExecute);
};
Hacker.prepare({}, onPrepare);
```
Expand Down Expand Up @@ -457,6 +455,7 @@ A function called after your environment is prepared. A good place to modify the
- `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)
- `config`: an object with keys matching `configFiles` but with the loaded config object

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

Expand Down Expand Up @@ -492,6 +491,7 @@ A function called after your application is executed. When invoked, `this` will
- `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)
- `config`: an object with keys matching `configFiles` but with the loaded config object

### events

Expand Down Expand Up @@ -582,6 +582,18 @@ Hacker.on('respawn', function (flags, child) {
Event will be triggered for this command:
`hacker --harmony commmand`

## Config files

Liftoff supports a small definition of config files, but all details provided by users will be available in `env.config`.

### `extends`

All `extends` properties will be traversed and become the basis for the resulting config object. Any path provided for `extends` will be loaded with node's `require`, so all extensions and loaders supported on the Liftoff instance will be available to them.

### Field matching the `configName`

Users can override the `configPath` via their config files by specifying a field with the same name as the primary `configName`. For example, the `hackerfile` property in a `configFile` will resolve the `configPath` and `configBase` against the path.

## Examples

Check out how [gulp][gulp-cli-index] uses Liftoff.
Expand Down
20 changes: 18 additions & 2 deletions index.js
Expand Up @@ -50,6 +50,8 @@ Liftoff.prototype.buildEnvironment = function (opts) {

// make a copy of search paths that can be mutated for this run
var searchPaths = this.searchPaths.slice();
// store the instance configName to use in closures without access to `this`
var configName = this.configName;

// calculate current cwd
var cwd = findCwd(opts);
Expand Down Expand Up @@ -113,6 +115,13 @@ Liftoff.prototype.buildEnvironment = function (opts) {
'Encountered error when loading config file: ' + configFilePath
);
}

// resolve something like `{ gulpfile: "./abc.xyz" }` to the absolute path
// based on the path of the configFile
if (Object.prototype.hasOwnProperty.call(configFile, configName)) {
configFile[configName] = path.resolve(path.dirname(configFilePath), configFile[configName]);
}

visited[configFilePath] = true;
if (configFile && configFile.extends) {
var nextCwd = path.dirname(configFilePath);
Expand Down Expand Up @@ -146,6 +155,13 @@ Liftoff.prototype.buildEnvironment = function (opts) {
return loadConfig(cwd, startingLocation, defaultConfig);
});

var configPathOverride = arrayFind(Object.keys(config), function (key) {
var cfg = config[key];
if (Object.prototype.hasOwnProperty.call(cfg, configName)) {
return cfg[configName];
}
});

// if cwd was provided explicitly, only use it for searching config
if (opts.cwd) {
searchPaths = [cwd];
Expand All @@ -156,15 +172,15 @@ Liftoff.prototype.buildEnvironment = function (opts) {

// calculate the regex to use for finding the config file
var configNameSearch = buildConfigName({
configName: this.configName,
configName: configName,
extensions: Object.keys(this.extensions),
});

// calculate configPath
var configPath = findConfig({
configNameSearch: configNameSearch,
searchPaths: searchPaths,
configPath: opts.configPath,
configPath: opts.configPath || configPathOverride,
});

// if we have a config path, save the directory it resides in.
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/configfiles/override-config-path-absolute.js
@@ -0,0 +1,5 @@
var path = require('path');

module.exports = {
myappfile: path.join(__dirname, "../override-the-config-path.js"),
};
3 changes: 3 additions & 0 deletions test/fixtures/configfiles/override-config-path-relative.json
@@ -0,0 +1,3 @@
{
"myappfile": "../override-the-config-path.js"
}
Empty file.
30 changes: 30 additions & 0 deletions test/index.js
Expand Up @@ -571,6 +571,36 @@ describe('Liftoff', function () {
);
});

it('overrides the configPath if the configName key exists in the config', function (done) {
var app = new Liftoff({
name: 'myapp',
configFiles: {
'override-config-path-absolute': [
{ path: 'test/fixtures/configfiles', extensions: ['.js'] }
],
},
});
app.prepare({}, function (env) {
expect(env.configPath).toMatch(/override-the-config-path\.js$/);
done();
});
});

it('resolves relative configPath if the configName key exists in the config', function (done) {
var app = new Liftoff({
name: 'myapp',
configFiles: {
'override-config-path-relative': [
{ path: 'test/fixtures/configfiles', extensions: ['.json'] }
],
},
});
app.prepare({}, function (env) {
expect(env.configPath).toMatch(/override-the-config-path\.js$/);
done();
});
});

it('should use dirname of configPath if no cwd is specified', function (done) {
var app = new Liftoff({
name: 'myapp',
Expand Down