Skip to content

Commit

Permalink
feat: allow extends to inherit from a module (#865)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: extends functionality now always loads the JSON provided, rather than reading from a specific key
  • Loading branch information
bcoe committed May 1, 2017
1 parent e28ded3 commit 89456d9
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 21 deletions.
38 changes: 35 additions & 3 deletions README.md
Expand Up @@ -1008,7 +1008,41 @@ $ node test.js
'$0': 'test.js' }
```

Note that a configuration object may extend from a JSON file using the `"extends"` property. When doing so, the `"extends"` value should be a path (relative or absolute) to the extended JSON file.
### `extends` Keyword

`config` and `pkgConf` can provide the `extends` keyword to
indicate that configuration should inherit from another location.

The value of extends can be either a relative or absolute path to a JSON
configuration file, e.g.,

```js
yargs.config({
extends: './configs/a.json',
logLevel: 'verbose'
})
```

Or, a module can be provided (this is useful for creating functionality like
[babel-presets](https://babeljs.io/docs/plugins/)).

**my-library.js**

```js
yargs.pkgConf('nyc')
```

**consuming package.json**

```json
{
"nyc": {
"extends": "nyc-babel-config"
}
}
```

Where `nyc-babel-config` is a package that exports configuration in its index.

<a name="conflicts"></a>.conflicts(x, y)
----------------------------------------------
Expand Down Expand Up @@ -1679,8 +1713,6 @@ as a configuration object.
`cwd` can optionally be provided, the package.json will be read
from this location.

Note that a configuration stanza in package.json may extend from an identically keyed stanza in another package.json file using the `"extends"` property. When doing so, the `"extends"` value should be a path (relative or absolute) to the extended package.json file.

.recommendCommands()
---------------------------

Expand Down
27 changes: 19 additions & 8 deletions lib/apply-extends.js
Expand Up @@ -15,22 +15,33 @@ function getPathToDefaultConfig (cwd, pathToExtend) {
return path.resolve(cwd, pathToExtend)
}

function applyExtends (config, cwd, subKey) {
function applyExtends (config, cwd) {
var defaultConfig = {}

if (config.hasOwnProperty('extends')) {
var pathToDefault = getPathToDefaultConfig(cwd, config.extends)
if (typeof config.extends !== 'string') return defaultConfig
var isPath = /\.json$/.test(config.extends)
var pathToDefault = null
if (!isPath) {
try {
pathToDefault = require.resolve(config.extends)
} catch (err) {
// most likely this simply isn't a module.
}
} else {
pathToDefault = getPathToDefaultConfig(cwd, config.extends)
}
// maybe the module uses key for some other reason,
// err on side of caution.
if (!pathToDefault && !isPath) return config

checkForCircularExtends(pathToDefault)

previouslyVisitedConfigs.push(pathToDefault)
delete config.extends

defaultConfig = JSON.parse(fs.readFileSync(pathToDefault, 'utf8'))
if (subKey) {
defaultConfig = defaultConfig[subKey] || {}
}
defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault), subKey)
defaultConfig = isPath ? JSON.parse(fs.readFileSync(pathToDefault, 'utf8')) : require(config.extends)
delete config.extends
defaultConfig = applyExtends(defaultConfig, path.dirname(pathToDefault))
}

previouslyVisitedConfigs = []
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -39,7 +39,8 @@
"rimraf": "^2.5.0",
"standard": "^8.6.0",
"standard-version": "^3.0.0",
"which": "^1.2.9"
"which": "^1.2.9",
"yargs-test-extends": "^1.0.1"
},
"scripts": {
"pretest": "standard",
Expand Down
4 changes: 2 additions & 2 deletions test/fixtures/extends/packageA/package.json
@@ -1,6 +1,6 @@
{
"foo": {
"a": 80,
"extends": "../packageB/package.json"
"extends": "../packageB/index.json"
}
}
}
4 changes: 4 additions & 0 deletions test/fixtures/extends/packageB/index.json
@@ -0,0 +1,4 @@
{
"a": 90,
"b": "riffiwobbles"
}
6 changes: 0 additions & 6 deletions test/fixtures/extends/packageB/package.json

This file was deleted.

25 changes: 25 additions & 0 deletions test/yargs.js
Expand Up @@ -1216,6 +1216,31 @@ describe('yargs dsl tests', function () {
argv.b.should.equal(22)
argv.z.should.equal(15)
})

// see: https://www.npmjs.com/package/yargs-test-extends
it('allows a module to be extended, rather than a JSON file', () => {
var argv = yargs()
.config({
a: 2,
extends: 'yargs-test-extends'
})
.argv

argv.a.should.equal(2)
argv.c.should.equal(201)
})

it('ignores an extends key that does not look like a path or module', () => {
var argv = yargs()
.config({
a: 2,
extends: 'batman'
})
.argv

argv.a.should.equal(2)
argv.extends.should.equal('batman')
})
})
})

Expand Down
2 changes: 1 addition & 1 deletion yargs.js
Expand Up @@ -478,7 +478,7 @@ function Yargs (processArgs, cwd, parentRequire) {

// If an object exists in the key, add it to options.configObjects
if (obj[key] && typeof obj[key] === 'object') {
conf = applyExtends(obj[key], path || cwd, key)
conf = applyExtends(obj[key], path || cwd)
options.configObjects = (options.configObjects || []).concat(conf)
}

Expand Down

0 comments on commit 89456d9

Please sign in to comment.