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

Allow provided config object to extend other configs #779

Merged
merged 2 commits into from Feb 25, 2017

Conversation

rcoy-v
Copy link
Member

@rcoy-v rcoy-v commented Feb 10, 2017

No description provided.

Copy link
Member

@nexdrew nexdrew left a 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!

var path = require('path')
var assign = require('./assign')

function applyExtends (config, cwd) {
Copy link
Member

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) {

delete config.extends

defaultConfig = JSON.parse(fs.readFileSync(pathToDefault, 'utf8'))
defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault))
Copy link
Member

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)
Copy link
Member

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
  }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

delete config.extends

defaultConfig = JSON.parse(fs.readFileSync(pathToDefault, 'utf8'))
defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault))
Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

var defaultConfig = {}

if (config.hasOwnProperty('extends')) {
var pathToDefault = path.join(cwd, config.extends)
Copy link
Member

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.

Copy link
Member Author

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.

@bcoe
Copy link
Member

bcoe commented Feb 18, 2017

@rcoy-v I'm going to try to put a few hours in against yargs tomorrow, and would love to work on landing this.

Do you have any time to address @nexdrew's suggestions, I could also work on making a few edits and landing this myself -- your call.

@rcoy-v
Copy link
Member Author

rcoy-v commented Feb 18, 2017

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.

@bcoe
Copy link
Member

bcoe commented Feb 18, 2017

@rcoy-v awesome, I'm really excited about this feature.

@bcoe bcoe changed the title Allow provided config object to extend other configs [WIP] Allow provided config object to extend other configs Feb 18, 2017
Copy link
Member

@JaKXz JaKXz left a 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)
Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@rcoy-v rcoy-v changed the title [WIP] Allow provided config object to extend other configs Allow provided config object to extend other configs Feb 19, 2017
@bcoe
Copy link
Member

bcoe commented Feb 20, 2017

@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.

Copy link
Member

@nexdrew nexdrew left a 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):

  1. Perhaps we should document the "extends" capability in the README (or wherever we're putting docs these days)
  2. 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
Copy link
Member

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!

Copy link
Member Author

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.

@bcoe
Copy link
Member

bcoe commented Feb 20, 2017

@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.

@nexdrew
Copy link
Member

nexdrew commented Feb 20, 2017

@bcoe

is this the new argument validation?

Negative. This error is thrown by the builtin Node path module. The argsert definition for .pkgConf() looks correct to me:

argsert('<string> [string]', [key, path], arguments.length)

The 2nd string is optional.

@JaKXz
Copy link
Member

JaKXz commented Feb 20, 2017

@nexdrew won't arguments.length give you a number [which doesn't match the type of optional string]?

@nexdrew
Copy link
Member

nexdrew commented Feb 20, 2017

@JaKXz As I understand it, the 3rd argument to argsert() should be the number of arguments given. The actual types are checked via the 2nd argument, which is an array representing the actual argument values.

@bcoe
Copy link
Member

bcoe commented Feb 20, 2017

@JaKXz, @nexdrew is correct -- the use of argsert in yargs is dead-simple, based on various performance bike-shedding discussions.

@nexdrew
Copy link
Member

nexdrew commented Feb 20, 2017

@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)
Copy link
Member Author

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.

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?

Copy link
Member

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('../')]
Copy link
Member Author

@rcoy-v rcoy-v Feb 23, 2017

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.

@stevezhu stevezhu merged commit e2a2133 into yargs:7.x Feb 25, 2017
bcoe pushed a commit that referenced this pull request Feb 26, 2017
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
@bcoe
Copy link
Member

bcoe commented Feb 27, 2017

@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:

npm cache clear; npm i yargs@next

@rcoy-v this means we can finally upstream this functionality to nyc 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants