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
Allow provided config object to extend other configs #779
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With a couple tweaks, we could make use of this apply-extends
logic for the pkgConf(key, path)
method as well.
This would allow, for example, a project to define an "nyc"
stanza in their package.json file that extends from another "nyc"
stanza in a different package.json file.
node_modules/my-lib/package.json:
{
"nyc": {
"lines": 95,
"statements": 95,
"functions": 95,
"branches": 95,
"reporter": [
"cobertura",
"lcov"
],
"all": false,
"check-coverage": true,
"report-dir": "./coverage",
"cache": false
}
}
package.json:
{
"nyc": {
"extends": "node_modules/my-lib/package.json",
"lines": 80
}
}
Just a suggestion, worked for me locally with the suggested tweaks below, but isn't necessarily a requirement.
Besides that, just one other question and comment. Thanks!
lib/apply-extends.js
Outdated
var path = require('path') | ||
var assign = require('./assign') | ||
|
||
function applyExtends (config, cwd) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Tweak 1/3] Add optional third argument here, such as:
function applyExtends (config, cwd, subkey) {
lib/apply-extends.js
Outdated
delete config.extends | ||
|
||
defaultConfig = JSON.parse(fs.readFileSync(pathToDefault, 'utf8')) | ||
defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Tweak 2/3] Check for and use the optional third argument here:
if (subkey) defaultConfig = defaultConfig[subkey] || {}
defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault), subkey)
@@ -304,6 +305,7 @@ function Yargs (processArgs, cwd, parentRequire) { | |||
argsert('[object|string] [string|function] [function]', [key, msg, parseFn], arguments.length) | |||
// allow a config object to be provided directly. | |||
if (typeof key === 'object') { | |||
key = applyExtends(key, cwd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Tweak 3/3] Then we can make use of applyExtends
in the pkgConf
method as well:
self.pkgConf = function (key, path) {
argsert('<string> [string]', [key, path], arguments.length)
var conf = null
var obj = pkgUp(path)
// If an object exists in the key, add it to options.configObjects
if (obj[key] && typeof obj[key] === 'object') {
conf = applyExtends(obj[key], cwd, key) // <-- this line changed
options.configObjects = (options.configObjects || []).concat(conf)
}
return self
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lib/apply-extends.js
Outdated
delete config.extends | ||
|
||
defaultConfig = JSON.parse(fs.readFileSync(pathToDefault, 'utf8')) | ||
defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Question] I wonder if we should guard against circular references? It might be unlikely but would overflow the stack. Perhaps this submodule should keep track of the paths it has already loaded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lib/apply-extends.js
Outdated
var defaultConfig = {} | ||
|
||
if (config.hasOwnProperty('extends')) { | ||
var pathToDefault = path.join(cwd, config.extends) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Comment] I like @JaKXz's idea of supporting modules by name as well (e.g. "extends": "my-lib"
instead of just "extends": "node_modules/my-lib/some-file"
), but since yargs isn't always loading the original "some-file" and doesn't know its path, I like your general approach as the next best thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel yargs is too general of a tool to make assumptions for shorthand extends like my-lib
. For more context, my use case in nyc probably won't even extend a root .nycrc
in another package. Rather, it will extend something like my-lib/configs/.nycrc
.
I might be able to pick this up again tomorrow, but I can't promise that. I'm fine if you want to make edits to this pull request. |
@rcoy-v awesome, I'm really excited about this feature. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At a glance this is looking good :) but looks like it needs a rebase, since I believe there are a few files already in the 7.x
branch
|
||
argv.a.should.equal(1) | ||
argv.b.should.equal(22) | ||
argv.z.should.equal(15) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔥 this is great.
my only nitpick is about the indenting on line 1162.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
e07fac6
to
bf46a79
Compare
08ddd1b
to
10851a9
Compare
@rcoy-v @JaKXz I'm at Hack Illinois this coming weekend; I was thinking getting yargs@7 out over the weekend would be a great task for my team -- tldr; expect to see a bunch of these tickets land on the weekend. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great work, thanks Ryan!
Only one little change requested, which I'll attempt to submit a PR for against your branch.
A couple other things we might want to consider as well (not necessarily required right now):
- Perhaps we should document the
"extends"
capability in the README (or wherever we're putting docs these days) - Should we be concerned with OS-specific paths in
"extends"
? I'm guessing no at this point, but perhaps it's something we could accommodate for when loading files from"extends"
?
@@ -1411,6 +1433,13 @@ describe('yargs dsl tests', function () { | |||
|
|||
argv.foo.should.equal('a') | |||
}) | |||
|
|||
it('should apply default configurations from extended packages', function () { | |||
var argv = yargs().pkgConf('foo', 'test/fixtures/extends/packageA').argv |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The path
argument to the .pkgConf(key, path)
method is optional. When I don't pass a value, I get this error:
path.js:7
throw new TypeError('Path must be a string. Received ' + inspect(path));
^
TypeError: Path must be a string. Received undefined
at assertPath (path.js:7:11)
at Object.join (path.js:1211:7)
at applyExtends (/Users/abg/git/nexdrew/github/yargs/lib/apply-extends.js:18:30)
at Object.Yargs.self.pkgConf (/Users/abg/git/nexdrew/github/yargs/yargs.js:479:14)
at Object.<anonymous> (/Users/abg/git/nexdrew/github/demo/pr779/index.js:2:4)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
Perhaps we should change line 479 of yargs.js to this:
conf = applyExtends(obj[key], path || cwd, key) // <-- add the `|| cwd` part
and maybe add a test for this scenario as well?
Otherwise, this is looking really good!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with your help in 5353a2b.
@nexdrew is this the new argument validation, I think we might find a few edge-cases where we need to loosen the rules a bit. |
Negative. This error is thrown by the builtin Node argsert('<string> [string]', [key, path], arguments.length) The 2nd string is optional. |
@nexdrew won't |
@JaKXz As I understand it, the 3rd argument to |
@rcoy-v See this PR against your branch to add the changes I requested. I think that will put this over the finish line. Let me know what you think. Thanks! |
- handle absolute paths - add note about "extends" to config and pkgConf docs - use cwd when no path given to pkgConf(key) method
} | ||
|
||
function getPathToDefaultConfig (cwd, pathToExtend) { | ||
return path.isAbsolute(pathToExtend) ? pathToExtend : path.join(cwd, pathToExtend) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use path.isAbsolute
here. According to node docs this was introduced in v0.11.2. I'm not sure where the topic of v0.10 support has landed. Maybe it depends on outcome of latest discussion in #742. If path.isAbsolute
cannot be used, I will figure something else out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would path.resolve(pathToExtend)
work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I believe path.resolve()
would work here. I actually thought that path.join()
worked the same way, which is why I said "relative or absolute" in the docs, but apparently it doesn't.
}) | ||
|
||
afterEach(function () { | ||
delete require.cache[require.resolve('../')] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yargs.reset()
does not fully reset the yargs instance. Configs were bleeding into other tests, affecting assertions. So I made it that each test has a truly fresh yargs.
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
@rcoy-v, @JaKXz, @nexdrew, phew! thanks for all the hard-work on this feature, laded this weekend as part of our big push towards yargs 7.x. I would love some help testing, before we promote this to a wider audience:
@rcoy-v this means we can finally upstream this functionality to |
No description provided.