diff --git a/README.md b/README.md index 28041dd..4ea4502 100644 --- a/README.md +++ b/README.md @@ -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` @@ -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); ``` @@ -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)) @@ -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 @@ -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. diff --git a/index.js b/index.js index 92c437b..a0b60c7 100644 --- a/index.js +++ b/index.js @@ -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); @@ -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); @@ -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]; @@ -156,7 +172,7 @@ 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), }); @@ -164,7 +180,7 @@ Liftoff.prototype.buildEnvironment = function (opts) { 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. diff --git a/test/fixtures/configfiles/override-config-path-absolute.js b/test/fixtures/configfiles/override-config-path-absolute.js new file mode 100644 index 0000000..7d9b36a --- /dev/null +++ b/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"), +}; diff --git a/test/fixtures/configfiles/override-config-path-relative.json b/test/fixtures/configfiles/override-config-path-relative.json new file mode 100644 index 0000000..c221f9a --- /dev/null +++ b/test/fixtures/configfiles/override-config-path-relative.json @@ -0,0 +1,3 @@ +{ + "myappfile": "../override-the-config-path.js" +} diff --git a/test/fixtures/override-the-config-path.js b/test/fixtures/override-the-config-path.js new file mode 100644 index 0000000..e69de29 diff --git a/test/index.js b/test/index.js index aa6264e..9b26e01 100644 --- a/test/index.js +++ b/test/index.js @@ -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',