|
|
@ -8,29 +8,38 @@
|
|
|
|
'use strict';
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
|
|
const fs = require('fs');
|
|
|
|
const fs = require('fs');
|
|
|
|
|
|
|
|
const os = require('os');
|
|
|
|
const path = require('path');
|
|
|
|
const path = require('path');
|
|
|
|
const util = require('util');
|
|
|
|
const util = require('util');
|
|
|
|
const ini = require('ini');
|
|
|
|
const ini = require('ini');
|
|
|
|
const configPath = require('git-config-path');
|
|
|
|
const configPath = require('git-config-path');
|
|
|
|
const expand = require('expand-tilde');
|
|
|
|
const expand = str => (str ? str.replace(/^~/, os.homedir()) : '');
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Asynchronously parse a `.git/config` file. If only the callback is passed,
|
|
|
|
* Asynchronously parse a `.git/config` file. If only the callback is passed,
|
|
|
|
* the `.git/config` file relative to `process.cwd()` is used.
|
|
|
|
* the `.git/config` file relative to `process.cwd()` is used.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* ```js
|
|
|
|
* parse(function(err, config) {
|
|
|
|
* parse((err, config) => {
|
|
|
|
* if (err) throw err;
|
|
|
|
* if (err) throw err;
|
|
|
|
* // do stuff with config
|
|
|
|
* // do stuff with config
|
|
|
|
* });
|
|
|
|
* });
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* // or, using async/await
|
|
|
|
|
|
|
|
* (async () => {
|
|
|
|
|
|
|
|
* console.log(await parse());
|
|
|
|
|
|
|
|
* console.log(await parse({ cwd: 'foo' }));
|
|
|
|
|
|
|
|
* console.log(await parse({ cwd: 'foo', path: 'some/.git/config' }));
|
|
|
|
|
|
|
|
* })();
|
|
|
|
* ```
|
|
|
|
* ```
|
|
|
|
|
|
|
|
* @name parse
|
|
|
|
* @param {Object|String|Function} `options` Options with `cwd` or `path`, the cwd to use, or the callback function.
|
|
|
|
* @param {Object|String|Function} `options` Options with `cwd` or `path`, the cwd to use, or the callback function.
|
|
|
|
* @param {Function} `callback` callback function if the first argument is options or cwd.
|
|
|
|
* @param {Function} `callback` callback function if the first argument is options or cwd.
|
|
|
|
* @return {Object}
|
|
|
|
* @return {Object}
|
|
|
|
* @api public
|
|
|
|
* @api public
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
function parse(options, callback) {
|
|
|
|
const parse = (options, callback) => {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
callback = options;
|
|
|
|
callback = options;
|
|
|
|
options = null;
|
|
|
|
options = null;
|
|
|
@ -43,30 +52,13 @@ function parse(options, callback) {
|
|
|
|
return parse.promise(options)
|
|
|
|
return parse.promise(options)
|
|
|
|
.then(config => callback(null, config))
|
|
|
|
.then(config => callback(null, config))
|
|
|
|
.catch(callback);
|
|
|
|
.catch(callback);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Parse the given
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* ```js
|
|
|
|
|
|
|
|
* parse.promise({ path: '/path/to/.gitconfig' })
|
|
|
|
|
|
|
|
* .then(config => console.log(config));
|
|
|
|
|
|
|
|
* ```
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @name .sync
|
|
|
|
|
|
|
|
* @param {Object|String} `options` Options with `cwd` or `path`, or the cwd to use.
|
|
|
|
|
|
|
|
* @return {Object}
|
|
|
|
|
|
|
|
* @api public
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
parse.promise = options => {
|
|
|
|
parse.promise = options => {
|
|
|
|
const filepath = parse.resolveConfigPath(options);
|
|
|
|
let filepath = parse.resolveConfigPath(options);
|
|
|
|
const read = util.promisify(fs.readFile);
|
|
|
|
let read = util.promisify(fs.readFile);
|
|
|
|
const stat = util.promisify(fs.stat);
|
|
|
|
let stat = util.promisify(fs.stat);
|
|
|
|
|
|
|
|
if (!filepath) return Promise.resolve(null);
|
|
|
|
if (!filepath) {
|
|
|
|
|
|
|
|
return Promise.resolve(null);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return stat(filepath)
|
|
|
|
return stat(filepath)
|
|
|
|
.then(() => read(filepath, 'utf8'))
|
|
|
|
.then(() => read(filepath, 'utf8'))
|
|
|
@ -83,9 +75,10 @@ parse.promise = options => {
|
|
|
|
* the `.git/config` file relative to `process.cwd()` is used.
|
|
|
|
* the `.git/config` file relative to `process.cwd()` is used.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* ```js
|
|
|
|
* const config = parse.sync();
|
|
|
|
* console.log(parse.sync());
|
|
|
|
|
|
|
|
* console.log(parse.sync({ cwd: 'foo' }));
|
|
|
|
|
|
|
|
* console.log(parse.sync({ cwd: 'foo', path: 'some/.git/config' }));
|
|
|
|
* ```
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
|
|
|
|
* @name .sync
|
|
|
|
* @name .sync
|
|
|
|
* @param {Object|String} `options` Options with `cwd` or `path`, or the cwd to use.
|
|
|
|
* @param {Object|String} `options` Options with `cwd` or `path`, or the cwd to use.
|
|
|
|
* @return {Object}
|
|
|
|
* @return {Object}
|
|
|
@ -93,19 +86,17 @@ parse.promise = options => {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
parse.sync = options => {
|
|
|
|
parse.sync = options => {
|
|
|
|
const opts = Object.assign({}, options);
|
|
|
|
let filepath = parse.resolveConfigPath(options);
|
|
|
|
const filepath = parse.resolveConfigPath(opts);
|
|
|
|
|
|
|
|
if (filepath && fs.existsSync(filepath)) {
|
|
|
|
|
|
|
|
const input = fs.readFileSync(filepath, 'utf8');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (opts.include === true) {
|
|
|
|
if (filepath && fs.existsSync(filepath)) {
|
|
|
|
const cwd = path.resolve(path.dirname(filepath));
|
|
|
|
let input = fs.readFileSync(filepath, 'utf8');
|
|
|
|
const str = injectInclude(input, cwd);
|
|
|
|
if (options && options.include === true) {
|
|
|
|
return parseIni(str, opts);
|
|
|
|
let cwd = path.resolve(path.dirname(filepath));
|
|
|
|
|
|
|
|
input = injectInclude(input, cwd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return parseIni(input, options);
|
|
|
|
return parseIni(input, opts);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@ -114,10 +105,8 @@ parse.sync = options => {
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
parse.resolveConfigPath = options => {
|
|
|
|
parse.resolveConfigPath = options => {
|
|
|
|
if (typeof options === 'string') {
|
|
|
|
if (typeof options === 'string') options = { type: options };
|
|
|
|
options = { type: options };
|
|
|
|
const opts = Object.assign({ cwd: process.cwd() }, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
const opts = Object.assign({cwd: process.cwd()}, options);
|
|
|
|
|
|
|
|
const fp = opts.path ? expand(opts.path) : configPath(opts.type);
|
|
|
|
const fp = opts.path ? expand(opts.path) : configPath(opts.type);
|
|
|
|
return fp ? path.resolve(opts.cwd, fp) : null;
|
|
|
|
return fp ? path.resolve(opts.cwd, fp) : null;
|
|
|
|
};
|
|
|
|
};
|
|
|
@ -136,16 +125,17 @@ parse.resolve = options => parse.resolveConfigPath(options);
|
|
|
|
* const config = parse.sync({ path: '/path/to/.gitconfig' });
|
|
|
|
* const config = parse.sync({ path: '/path/to/.gitconfig' });
|
|
|
|
* const obj = parse.expandKeys(config);
|
|
|
|
* const obj = parse.expandKeys(config);
|
|
|
|
* ```
|
|
|
|
* ```
|
|
|
|
|
|
|
|
* @name .expandKeys
|
|
|
|
* @param {Object} `config` The parsed git config object.
|
|
|
|
* @param {Object} `config` The parsed git config object.
|
|
|
|
* @return {Object}
|
|
|
|
* @return {Object}
|
|
|
|
* @api public
|
|
|
|
* @api public
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
parse.expandKeys = config => {
|
|
|
|
parse.expandKeys = config => {
|
|
|
|
for (const key of Object.keys(config)) {
|
|
|
|
for (let key of Object.keys(config)) {
|
|
|
|
const m = /(\S+) "(.*)"/.exec(key);
|
|
|
|
let m = /(\S+) "(.*)"/.exec(key);
|
|
|
|
if (!m) continue;
|
|
|
|
if (!m) continue;
|
|
|
|
const prop = m[1];
|
|
|
|
let prop = m[1];
|
|
|
|
config[prop] = config[prop] || {};
|
|
|
|
config[prop] = config[prop] || {};
|
|
|
|
config[prop][m[2]] = config[key];
|
|
|
|
config[prop][m[2]] = config[key];
|
|
|
|
delete config[key];
|
|
|
|
delete config[key];
|
|
|
@ -154,13 +144,13 @@ parse.expandKeys = config => {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function parseIni(str, options) {
|
|
|
|
function parseIni(str, options) {
|
|
|
|
const opts = Object.assign({}, options);
|
|
|
|
let opts = Object.assign({}, options);
|
|
|
|
|
|
|
|
|
|
|
|
str = str.replace(/\[(\S+) "(.*)"\]/g, function(m, $1, $2) {
|
|
|
|
str = str.replace(/\[(\S+) "(.*)"\]/g, (m, $1, $2) => {
|
|
|
|
return $1 && $2 ? `[${$1} "${$2.split('.').join('\\.')}"]` : m;
|
|
|
|
return $1 && $2 ? `[${$1} "${$2.split('.').join('\\.')}"]` : m;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const config = ini.parse(str);
|
|
|
|
let config = ini.parse(str);
|
|
|
|
if (opts.expandKeys === true) {
|
|
|
|
if (opts.expandKeys === true) {
|
|
|
|
return parse.expandKeys(config);
|
|
|
|
return parse.expandKeys(config);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -168,17 +158,16 @@ function parseIni(str, options) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function injectInclude(input, cwd) {
|
|
|
|
function injectInclude(input, cwd) {
|
|
|
|
const lines = input.split('\n').filter(line => line.trim() !== '');
|
|
|
|
let lines = input.split('\n').filter(line => line.trim() !== '');
|
|
|
|
const len = lines.length;
|
|
|
|
let len = lines.length;
|
|
|
|
const res = [];
|
|
|
|
let res = [];
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
|
|
const line = lines[i];
|
|
|
|
let line = lines[i];
|
|
|
|
if (line.indexOf('[include]') === 0) {
|
|
|
|
if (line.indexOf('[include]') === 0) {
|
|
|
|
const filepath = lines[i + 1].replace(/^\s*path\s*=\s*/, '');
|
|
|
|
let filepath = lines[i + 1].replace(/^\s*path\s*=\s*/, '');
|
|
|
|
const fp = path.resolve(cwd, expand(filepath));
|
|
|
|
let fp = path.resolve(cwd, expand(filepath));
|
|
|
|
res.push(fs.readFileSync(fp));
|
|
|
|
res.push(fs.readFileSync(fp));
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
res.push(line);
|
|
|
|
res.push(line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|