ssh-config
Advanced tools
Comparing version 1.1.6 to 2.0.0-alpha.1
@@ -0,1 +1,9 @@ | ||
2.0.0 / 2019-06-?? | ||
================== | ||
* Breaking: parse `Host` values as an Array to hold multiple patterns | ||
* Fix: `Host` can contain spaces if quoted with double quotes | ||
* Fix: quoted values can contain double quotes once they are escaped with backslash | ||
1.1.6 / 2019-04-02 | ||
@@ -2,0 +10,0 @@ ================== |
168
index.js
'use strict' | ||
const glob = require('./lib/glob') | ||
const util = require('util') | ||
const glob = require('./src/glob') | ||
@@ -8,3 +9,2 @@ const RE_SPACE = /\s/ | ||
const RE_SECTION_DIRECTIVE = /^(Host|Match)$/i | ||
const RE_QUOTED = /^(")(.*)\1$/ | ||
@@ -14,2 +14,5 @@ const DIRECTIVE = 1 | ||
function match(line, opts) { | ||
return opts.hasOwnProperty(line.param) && opts[line.param] === line.value | ||
} | ||
@@ -23,9 +26,9 @@ class SSHConfig extends Array { | ||
compute(host) { | ||
let obj = {} | ||
let setProperty = (name, value) => { | ||
const obj = {} | ||
const setProperty = (name, value) => { | ||
if (name === 'IdentityFile') { | ||
let list = obj[name] || (obj[name] = []) | ||
const list = obj[name] || (obj[name] = []) | ||
list.push(value) | ||
} | ||
else if (obj[name] === undefined) { | ||
else if (obj[name] == null) { | ||
obj[name] = value | ||
@@ -35,9 +38,5 @@ } | ||
for (let i = 0; i < this.length; i++) { | ||
let line = this[i] | ||
if (line.type !== DIRECTIVE) { | ||
continue | ||
} | ||
else if (line.param === 'Host') { | ||
for (const line of this) { | ||
if (line.type !== DIRECTIVE) continue | ||
if (line.param === 'Host') { | ||
if (glob(line.value, host)) { | ||
@@ -62,24 +61,30 @@ setProperty(line.param, line.value) | ||
/** | ||
* find section by Host or Match | ||
*/ | ||
find(opts) { | ||
let result = this.constructor.find(this, opts) | ||
return result ? result[0] : null | ||
find(opts = {}) { | ||
if (typeof opts === 'function') return super.find(opts) | ||
if (!(opts && ('Host' in opts || 'Match' in opts))) { | ||
throw new Error('Can only find by Host or Match') | ||
} | ||
return super.find(line => match(line, opts)) | ||
} | ||
/** | ||
* Remove section | ||
*/ | ||
remove(opts) { | ||
let result = this.constructor.find(this, opts) | ||
remove(opts = {}) { | ||
if (!(opts && ('Host' in opts || 'Match' in opts))) { | ||
throw new Error('Can only remove by Host or Match') | ||
} | ||
if (result) { | ||
return this.splice(result[1], 1) | ||
} | ||
const index = typeof opts === 'function' | ||
? super.findIndex(opts) | ||
: super.findIndex(line => match(line, opts)) | ||
if (index >= 0) return this.splice(index, 1) | ||
} | ||
/** | ||
@@ -145,23 +150,2 @@ * toString() | ||
static find(config, opts) { | ||
if (!(opts && ('Host' in opts || 'Match' in opts))) { | ||
throw new Error('Can only find by Host or Match') | ||
} | ||
for (let i = 0; i < config.length; i++) { | ||
const line = config[i] | ||
if (line.type === DIRECTIVE && | ||
RE_SECTION_DIRECTIVE.test(line.param) && | ||
line.param in opts && | ||
opts[line.param] === line.value) { | ||
return [line, i] | ||
} | ||
} | ||
return null | ||
} | ||
/** | ||
@@ -173,3 +157,3 @@ * Stringify structured object into ssh config text | ||
let format = line => { | ||
const format = line => { | ||
str += line.before | ||
@@ -198,3 +182,2 @@ | ||
static get DIRECTIVE() { | ||
@@ -204,3 +187,2 @@ return DIRECTIVE | ||
static get COMMENT() { | ||
@@ -210,3 +192,2 @@ return COMMENT | ||
/** | ||
@@ -247,11 +228,11 @@ * Parse ssh config text into structured object. | ||
function option() { | ||
let opt = '' | ||
function parameter() { | ||
let param = '' | ||
while (chr && chr !== ' ' && chr !== '=') { | ||
opt += chr | ||
param += chr | ||
chr = next() | ||
} | ||
return opt | ||
return param | ||
} | ||
@@ -272,8 +253,23 @@ | ||
let val = '' | ||
let quoted = false | ||
let escaped = false | ||
while (chr && !RE_LINE_BREAK.test(chr)) { | ||
val += chr | ||
if (escaped) { | ||
val += chr === '"' ? chr : `\\${chr}` | ||
escaped = false | ||
} | ||
else if (chr === '"') { | ||
quoted = !quoted | ||
} | ||
else if (chr === '\\') { | ||
escaped = true | ||
} | ||
else { | ||
val += chr | ||
} | ||
chr = next() | ||
} | ||
if (quoted || escaped) throw new Error('Unexpected line break') | ||
return val.trim() | ||
@@ -283,3 +279,3 @@ } | ||
function comment() { | ||
let type = COMMENT | ||
const type = COMMENT | ||
let content = '' | ||
@@ -295,10 +291,50 @@ | ||
// Host *.co.uk | ||
// Host * !local.dev | ||
// Host "foo bar" | ||
function patterns() { | ||
const results = [] | ||
let val = '' | ||
let quoted = false | ||
let escaped = false | ||
while (chr && !RE_LINE_BREAK.test(chr)) { | ||
if (escaped) { | ||
val += chr === '"' ? chr : `\\${chr}` | ||
escaped = false | ||
} | ||
else if (chr === '"') { | ||
quoted = !quoted | ||
} | ||
else if (chr === '\\') { | ||
escaped = true | ||
} | ||
else if (quoted) { | ||
val += chr | ||
} | ||
else if (chr === ' ' && val) { | ||
results.push(val) | ||
val = '' | ||
} | ||
else { | ||
val += chr | ||
} | ||
chr = next() | ||
} | ||
if (quoted || escaped) throw new Error('Unexpected line break') | ||
results.push(val) | ||
return results.length > 1 ? results : results[0] | ||
} | ||
function directive() { | ||
let type = DIRECTIVE | ||
const type = DIRECTIVE | ||
const param = parameter() | ||
return { | ||
type, | ||
param: option(), | ||
param, | ||
separator: separator(), | ||
value: value() | ||
value: param.toLowerCase() === 'host' ? patterns() : value() | ||
} | ||
@@ -308,5 +344,5 @@ } | ||
function line() { | ||
let before = space() | ||
let node = chr === '#' ? comment() : directive() | ||
let after = linebreak() | ||
const before = space() | ||
const node = chr === '#' ? comment() : directive() | ||
const after = linebreak() | ||
@@ -316,7 +352,2 @@ node.before = before | ||
if (RE_QUOTED.test(node.value)) { | ||
node.value = node.value.replace(RE_QUOTED, '$2') | ||
node.quoted = true | ||
} | ||
return node | ||
@@ -343,3 +374,8 @@ } | ||
SSHConfig.find = util.deprecate((config, opts) => { | ||
const line = config.find(opts) | ||
if (line) return [line, config.indexOf(line)] | ||
return null | ||
}, 'SSHConfig.find() is deprected. Use SSHConfig.prototype.find() instead.') | ||
module.exports = SSHConfig |
{ | ||
"name": "ssh-config", | ||
"description": "SSH config parser and stringifier", | ||
"version": "1.1.6", | ||
"version": "2.0.0-alpha.1", | ||
"author": "Chen Yangjian (https://www.cyj.me)", | ||
@@ -10,2 +10,6 @@ "repository": { | ||
}, | ||
"files": [ | ||
"index.js", | ||
"src" | ||
], | ||
"devDependencies": { | ||
@@ -12,0 +16,0 @@ "expect.js": "^0.3.1", |
@@ -64,3 +64,2 @@ # SSH Config Parser & Stringifier | ||
### Iterating over Sections | ||
@@ -97,3 +96,2 @@ | ||
### `.find` sections by Host or Match | ||
@@ -110,11 +108,6 @@ | ||
config.find({ Host: 'example1' }) | ||
// or the ES2015 Array.prototype.find | ||
config.find(line => line.param == 'Host' && line.value == 'example1') | ||
``` | ||
Or you can just brew it yourself: | ||
```js | ||
config.filter(line => line.param == 'Host' && line.value == 'example1')[0] | ||
``` | ||
### `.remove` sections by Host or other criteria | ||
@@ -125,7 +118,5 @@ | ||
```js | ||
const config = SSHConfig.parse(/* ssh config text */) | ||
config.remove({ Host: 'example1' }) | ||
``` | ||
### `.append` sections | ||
@@ -169,3 +160,2 @@ | ||
## References | ||
@@ -172,0 +162,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
16895
326
2
168