From ae8a931c622167116599d2e09a8003d07f67b42a Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 23 Jan 2018 01:33:56 -0500 Subject: [PATCH 1/3] run update --- .editorconfig | 9 +++++---- .eslintrc.json | 5 ----- .gitignore | 10 ++++++++++ LICENSE | 2 +- index.js | 4 ++-- package.json | 6 +++--- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/.editorconfig b/.editorconfig index 818e072..449f0da 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,13 +1,14 @@ +# http://editorconfig.org/ root = true [*] -indent_style = space -end_of_line = lf charset = utf-8 +end_of_line = lf indent_size = 2 -trim_trailing_whitespace = true +indent_style = space insert_final_newline = true +trim_trailing_whitespace = true [{**/{actual,fixtures,expected,templates}/**,*.md}] trim_trailing_whitespace = false -insert_final_newline = false \ No newline at end of file +insert_final_newline = false diff --git a/.eslintrc.json b/.eslintrc.json index 948dbdb..61e8895 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,9 +1,4 @@ { - "ecmaFeatures": { - "modules": true, - "experimentalObjectRestSpread": true - }, - "env": { "browser": false, "es6": true, diff --git a/.gitignore b/.gitignore index 13bb9ba..f969a2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,30 @@ # always ignore files *.DS_Store +.idea +.vscode *.sublime-* # test related, or directories generated by tests test/actual actual coverage +.nyc* # npm node_modules npm-debug.log +# yarn +yarn.lock +yarn-error.log + # misc _gh_pages +_draft +_drafts bower_components vendor temp tmp TODO.md +package-lock.json \ No newline at end of file diff --git a/LICENSE b/LICENSE index 1e49edf..f8de063 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2016, Jon Schlinkert. +Copyright (c) 2015-2018, Jon Schlinkert. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/index.js b/index.js index 85e9f87..43cdfbf 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,8 @@ /*! * parse-git-config * - * Copyright (c) 2015 Jon Schlinkert. - * Licensed under the MIT license. + * Copyright (c) 2015-2018, Jon Schlinkert. + * Released under the MIT License. */ 'use strict'; diff --git a/package.json b/package.json index d855a40..b90f9f1 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,9 @@ "homepage": "https://github.com/jonschlinkert/parse-git-config", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", "contributors": [ - "Jason Denizac (https://jden.us)", - "Jon Schlinkert (http://twitter.com/jonschlinkert)", - "Sam Holmes (https://samholmes.net)" + "Jon Schlinkert (http://twitter.com/jonschlinkert)", + "Sam Holmes (https://samholmes.net)", + "(https://github.com/js-n)" ], "repository": "jonschlinkert/parse-git-config", "bugs": { From 4a32467144f909dc1364a7e66564dcc35ce3c3e4 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 23 Jan 2018 02:21:25 -0500 Subject: [PATCH 2/3] adds support for `include` --- index.js | 89 ++++++++++++++++++++---- package.json | 11 +-- test/expected/_gitconfig.js | 122 +++++++++++++++++++++++++++++++++ test/fixtures/_gitconfig | 86 +++++++++++++++++++++++ test/fixtures/_gitconfig.local | 11 +++ test.js => test/test.js | 43 ++++++++---- 6 files changed, 329 insertions(+), 33 deletions(-) create mode 100644 test/expected/_gitconfig.js create mode 100644 test/fixtures/_gitconfig create mode 100644 test/fixtures/_gitconfig.local rename test.js => test/test.js (64%) diff --git a/index.js b/index.js index 43cdfbf..4ac85e6 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ var fs = require('fs'); var path = require('path'); +var expand = require('expand-tilde'); var exists = require('fs-exists-sync'); var extend = require('extend-shallow'); var configPath = require('git-config-path'); @@ -33,23 +34,36 @@ var ini = require('ini'); function parse(options, cb) { if (typeof options === 'function') { cb = options; - options = {}; + options = null; } if (typeof cb !== 'function') { - throw new TypeError('parse-git-config async expects a callback function.'); + throw new TypeError('expected callback to be a function'); } - options = options || {}; - var filepath = parse.resolve(options); + var filepath = parse.resolveConfigPath(options); + if (filepath === null) { + cb(); + return; + } fs.stat(filepath, function(err, stat) { - if (err) return cb(err); + if (err) { + cb(err); + return; + } fs.readFile(filepath, 'utf8', function(err, str) { - if (err) return cb(err); - var parsed = ini.parse(str); - cb(null, parsed); + if (err) { + cb(err); + return; + } + + if (options && options.include === true) { + str = injectInclude(str, path.resolve(path.dirname(filepath))); + } + + cb(null, ini.parse(str)); }); }); } @@ -69,12 +83,17 @@ function parse(options, cb) { */ parse.sync = function parseSync(options) { - options = options || {}; - var filepath = parse.resolve(options); - + var filepath = parse.resolveConfigPath(options); if (filepath && exists(filepath)) { - var str = fs.readFileSync(filepath, 'utf8'); - return ini.parse(str); + var input = fs.readFileSync(filepath, 'utf8'); + + if (options && options.include === true) { + var cwd = path.resolve(path.dirname(filepath)); + var str = injectInclude(input, cwd); + return ini.parse(str); + } + + return ini.parse(input); } return {}; }; @@ -83,15 +102,23 @@ parse.sync = function parseSync(options) { * Resolve the git config path */ -parse.resolve = function resolve(options) { +parse.resolveConfigPath = function(options) { if (typeof options === 'string') { options = { type: options }; } var opts = extend({cwd: process.cwd()}, options); - var fp = opts.path || configPath(opts.type); + var fp = opts.path ? expand(opts.path) : configPath(opts.type); return fp ? path.resolve(opts.cwd, fp) : null; }; +/** + * Deprecated: use `.resolveConfigPath` instead + */ + +parse.resolve = function(options) { + return parse.resolveConfigPath(options); +}; + /** * Returns an object with only the properties that had ini-style keys * converted to objects (example below). @@ -118,6 +145,38 @@ parse.keys = function parseKeys(config) { return res; }; +function injectInclude(input, cwd) { + var pathRegex = /^\s*path\s*=\s*/; + var lines = input.split('\n'); + var len = lines.length; + var filepath = ''; + var res = []; + + for (var i = 0; i < len; i++) { + var line = lines[i]; + var n = i; + + if (line.indexOf('[include]') === 0) { + while (n < len && !pathRegex.test(filepath)) { + filepath = lines[++n]; + } + + if (!filepath) { + return input; + } + + filepath = filepath.replace(pathRegex, ''); + var fp = path.resolve(cwd, expand(filepath)); + res.push(fs.readFileSync(fp)); + + } else { + res.push(line); + } + } + + return res.join('\n'); +} + /** * Expose `parse` */ diff --git a/package.json b/package.json index b90f9f1..d595282 100644 --- a/package.json +++ b/package.json @@ -25,15 +25,16 @@ "test": "mocha" }, "dependencies": { - "extend-shallow": "^2.0.1", + "expand-tilde": "^2.0.2", + "extend-shallow": "^3.0.2", "fs-exists-sync": "^0.1.0", "git-config-path": "^1.0.1", - "ini": "^1.3.4" + "ini": "^1.3.5" }, "devDependencies": { - "gulp-format-md": "^0.1.11", - "homedir-polyfill": "^1.0.1", - "mocha": "^3.2.0" + "gulp-format-md": "^1.0.0", + "mocha": "^3.5.3", + "homedir-polyfill": "^1.0.1" }, "keywords": [ "config", diff --git a/test/expected/_gitconfig.js b/test/expected/_gitconfig.js new file mode 100644 index 0000000..70dd302 --- /dev/null +++ b/test/expected/_gitconfig.js @@ -0,0 +1,122 @@ +module.exports = { + user: { + email: 'email', + name: 'name', + signingkey: 'https://help.github.com/articles/generating-a-new-gpg-key/' + }, + github: { + user: 'name', + token: 'https://github.com/settings/tokens' + }, + commit: { + gpgsign: true + }, + tag: { + gpgsign: true, + path: '_gitconfig.local', + sort: 'version:refname' + }, + core: { + legacyheaders: false, + quotepath: false, + trustctime: false, + precomposeunicode: false, + pager: 'cat', + logAllRefUpdates: true, + excludesfile: '~/.gitignore' + }, + repack: { + usedeltabaseoffset: true + }, + merge: { + log: true, + conflictstyle: 'diff3' + }, + apply: { + whitespace: 'fix' + }, + help: { + autocorrect: '1' + }, + rerere: { + enabled: true + }, + color: { + diff: 'auto', + status: 'auto', + branch: 'auto', + interactive: 'auto', + ui: 'always' + }, + 'color "diff"': { + meta: 'yellow bold', + frag: 'magenta', + plain: 'white bold', + old: 'red bold', + new: 'green bold', + commit: 'yellow bold', + func: 'green dim', + whitespace: 'red reverse' + }, + 'color "status"': { + added: 'yellow', + changed: 'green', + untracked: 'cyan' + }, + 'color "branch"': { + current: 'yellow reverse', + local: 'yellow', + remote: 'green' + }, + diff: { + renames: 'copies', + algorithm: 'patience', + compactionHeuristic: true, + wsErrorHighlight: 'all' + }, + 'diff "bin"': { + textconv: 'hexdump -v -C' + }, + credential: { + helper: 'store' + }, + status: { + relativePaths: true, + showUntrackedFiles: 'no' + }, + pull: { + rebase: true + }, + push: { + default: 'current', + followTags: true + }, + alias: { + a: 'commit --amend', + c: 'commit -am', + d: '!git diff --exit-code && git diff --cached', + dif: 'diff', + git: '!exec git', + p: 'push -u', + r: 'reset --soft HEAD~1', + s: 'status', + sc: 'clone --depth=1', + l: 'log --graph --pretty=format:\'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset\' --abbrev-commit -n 15' + }, + 'remote "origin"': { + fetch: '+refs/tags/*:refs/tags/*' + }, + branch: { + autosetupmerge: 'always', + autosetuprebase: 'always' + }, + http: { + sslverify: false + }, + submodule: { + fetchJobs: '0' + }, + fetch: { + prune: true + } +}; diff --git a/test/fixtures/_gitconfig b/test/fixtures/_gitconfig new file mode 100644 index 0000000..7b999ac --- /dev/null +++ b/test/fixtures/_gitconfig @@ -0,0 +1,86 @@ +[include] + path = _gitconfig.local +[core] + legacyheaders = false + quotepath = false + trustctime = false + precomposeunicode = false + pager = cat + logAllRefUpdates = true + excludesfile = ~/.gitignore +[repack] + usedeltabaseoffset = true +[merge] + log = true + conflictstyle = diff3 +[apply] + whitespace = fix +[help] + autocorrect = 1 +[rerere] + enabled = true +[color] + diff = auto + status = auto + branch = auto + interactive = auto + ui = always +[color "diff"] + meta = yellow bold + frag = magenta + plain = white bold + old = red bold + new = green bold + commit = yellow bold + func = green dim + whitespace = red reverse +[color "status"] + added = yellow + changed = green + untracked = cyan +[color "branch"] + current = yellow reverse + local = yellow + remote = green +[diff] + renames = copies + algorithm = patience + compactionHeuristic = true + wsErrorHighlight = all +[diff "bin"] + textconv = hexdump -v -C +[credential] + helper = store +[status] + relativePaths = true + showUntrackedFiles = no +[pull] + rebase = true +[push] + default = current + followTags = true +[alias] + a = commit --amend + c = commit -am + d = !git diff --exit-code && git diff --cached + dif = diff + git = !exec git + p = push -u + r = reset --soft HEAD~1 + s = status + sc = clone --depth=1 + l = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit -n 15 +[remote "origin"] + fetch = +refs/pr/*/head:refs/remotes/origin/pr/* + fetch = +refs/tags/*:refs/tags/* +[branch] + autosetupmerge = always + autosetuprebase = always +[http] + sslverify = false +[submodule] + fetchJobs = 0 +[fetch] + prune = true +[tag] + sort = version:refname diff --git a/test/fixtures/_gitconfig.local b/test/fixtures/_gitconfig.local new file mode 100644 index 0000000..afa9a19 --- /dev/null +++ b/test/fixtures/_gitconfig.local @@ -0,0 +1,11 @@ +[user] + email = email + name = name + signingkey = https://help.github.com/articles/generating-a-new-gpg-key/ +[github] + user = name + token = https://github.com/settings/tokens +[commit] + gpgsign = true +[tag] + gpgsign = true \ No newline at end of file diff --git a/test.js b/test/test.js similarity index 64% rename from test.js rename to test/test.js index 1f63fc3..c00c66c 100644 --- a/test.js +++ b/test/test.js @@ -1,7 +1,7 @@ /*! * parse-git-config * - * Copyright (c) 2015 Jon Schlinkert. + * Copyright (c) 2015-2018 Jon Schlinkert. * Licensed under the MIT license. */ @@ -9,11 +9,16 @@ require('mocha'); var isTravis = process.env.TRAVIS || process.env.CLI; +var fs = require('fs'); var os = require('os'); var assert = require('assert'); var path = require('path'); var homedir = require('homedir-polyfill'); -var parse = require('./'); +var parse = require('..'); + +function read(filepath) { + return fs.readFileSync(path.join(__dirname, filepath), 'utf8'); +} describe('sync:', function() { it('should return an object', function() { @@ -22,14 +27,10 @@ describe('sync:', function() { }); describe('async:', function() { - it('should throw a callback is not passed:', function(cb) { - try { + it('should throw a callback is not passed:', function() { + assert.throws(function() { parse(); - cb(new Error('expected an error')); - } catch (err) { - assert.equal(err.message, 'parse-git-config async expects a callback function.'); - cb(); - } + }, /expected/); }); it('should parse .git/config', function(cb) { @@ -40,8 +41,18 @@ describe('async:', function() { }); }); + it('should include other config sources', function() { + var fp = path.join(__dirname, 'fixtures/_gitconfig'); + + parse({ path: fp, include: true }, function(err, config) { + assert(!err); + assert.deepEqual(config, require('./expected/_gitconfig.js')); + cb(); + }); + }); + it('should throw an error when .git/config does not exist:', function(cb) { - parse({path: 'foo'}, function(err, config) { + parse({ path: 'foo' }, function(err, config) { assert(err instanceof Error); assert(/ENOENT.*parse-git-config/.test(err.message)); cb(); @@ -56,11 +67,17 @@ describe('resolve:', function() { it('should allow override path', function() { var fp = path.resolve(homedir(), '.gitconfig'); - assert.equal(parse.resolve({path: fp}), fp); + assert.equal(parse.resolve({ path: fp }), fp); + }); + + it('should include other config sources', function() { + var fp = path.join(__dirname, 'fixtures/_gitconfig'); + var actual = parse.sync({ path: fp, include: true }); + assert.deepEqual(actual, require('./expected/_gitconfig.js')); }); it('should resolve relative path to cwd', function() { - assert.equal(parse.resolve({path: '.config'}), path.resolve(process.cwd(), '.config')); + assert.equal(parse.resolve({ path: '.config' }), path.resolve(process.cwd(), '.config')); }); it('should resolve relative path to the global git config when `global` is passed', function() { @@ -69,7 +86,7 @@ describe('resolve:', function() { }); it('should allow override of cwd', function() { - var actual = parse.resolve({path: '.config', cwd: '/opt/config'}); + var actual = parse.resolve({ path: '.config', cwd: '/opt/config' }); assert.equal(actual, path.resolve('/opt/config/.config')); }); }); From 7322fc740caf44eb6c6ad7e589978aa563e98139 Mon Sep 17 00:00:00 2001 From: jonschlinkert Date: Tue, 23 Jan 2018 03:12:13 -0500 Subject: [PATCH 3/3] adds promise support --- .travis.yml | 6 +- .verb.md | 12 ++-- README.md | 127 +++++++++++++++++++++-------------- example.js | 8 ++- index.js | 154 +++++++++++++++++++++++-------------------- package.json | 5 +- test/test.js | 182 +++++++++++++++++++++++++++++---------------------- 7 files changed, 279 insertions(+), 215 deletions(-) diff --git a/.travis.yml b/.travis.yml index f28113a..dedfdd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,5 @@ os: language: node_js node_js: - node - - '6' - - '5' - - '0.12' - - '0.10' + - '9' + - '8' diff --git a/.verb.md b/.verb.md index 9b25dd1..9088a2a 100644 --- a/.verb.md +++ b/.verb.md @@ -1,10 +1,10 @@ ## Usage ```js -var parse = require('{%= name %}'); +const parse = require('{%= name %}'); // sync -var config = parse.sync(); +const config = parse.sync(); // or async parse(function (err, config) { @@ -51,8 +51,8 @@ Converts ini-style keys into objects: **Example 1** ```js -var parse = require('parse-git-config'); -var config = { +const parse = require('parse-git-config'); +const config = { 'foo "bar"': { doStuff: true }, 'foo "baz"': { doStuff: true } }; @@ -74,8 +74,8 @@ Results in: **Example 2** ```js -var parse = require('parse-git-config'); -var config = { +const parse = require('parse-git-config'); +const config = { 'remote "origin"': { url: 'https://github.com/jonschlinkert/normalize-pkg.git', fetch: '+refs/heads/*:refs/remotes/origin/*' diff --git a/README.md b/README.md index 2244ccc..48c7ffc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ -# parse-git-config [![NPM version](https://img.shields.io/npm/v/parse-git-config.svg?style=flat)](https://www.npmjs.com/package/parse-git-config) [![NPM monthly downloads](https://img.shields.io/npm/dm/parse-git-config.svg?style=flat)](https://npmjs.org/package/parse-git-config) [![NPM total downloads](https://img.shields.io/npm/dt/parse-git-config.svg?style=flat)](https://npmjs.org/package/parse-git-config) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/parse-git-config.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/parse-git-config) +# parse-git-config [![NPM version](https://img.shields.io/npm/v/parse-git-config.svg?style=flat)](https://www.npmjs.com/package/parse-git-config) [![NPM monthly downloads](https://img.shields.io/npm/dm/parse-git-config.svg?style=flat)](https://npmjs.org/package/parse-git-config) [![NPM total downloads](https://img.shields.io/npm/dt/parse-git-config.svg?style=flat)](https://npmjs.org/package/parse-git-config) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/parse-git-config.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/parse-git-config) > Parse `.git/config` into a JavaScript object. sync or async. +Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support. + ## Install Install with [npm](https://www.npmjs.com/): @@ -13,10 +15,10 @@ $ npm install --save parse-git-config ## Usage ```js -var parse = require('parse-git-config'); +const parse = require('parse-git-config'); // sync -var config = parse.sync(); +const config = parse.sync(); // or async parse(function (err, config) { @@ -55,10 +57,16 @@ Config object will be something like: ## API -### [parse](index.js#L33) +### [parse](index.js#L35) Asynchronously parse a `.git/config` file. If only the callback is passed, the `.git/config` file relative to `process.cwd()` is used. +**Params** + +* `options` **{Object|String|Function}**: Options with `cwd` or `path`, the cwd to use, or the callback function. +* `cb` **{Function}**: callback function if the first argument is options or cwd. +* `returns` **{Object}** + **Example** ```js @@ -68,43 +76,53 @@ parse(function(err, config) { }); ``` -**Params** +### [.sync](index.js#L64) -* `options` **{Object|String|Function}**: Options with `cwd` or `path`, the cwd to use, or the callback function. -* `cb` **{Function}**: callback function if the first argument is options or cwd. -* `returns` **{Object}** +Parse the given -### [.sync](index.js#L71) +**Params** -Synchronously parse a `.git/config` file. If no arguments are passed, the `.git/config` file relative to `process.cwd()` is used. +* `options` **{Object|String}**: Options with `cwd` or `path`, or the cwd to use. +* `returns` **{Object}** **Example** ```js -var config = parse.sync(); +parse.promise({ path: '/path/to/.gitconfig' }) + .then(config => console.log(config)); ``` +### [.sync](index.js#L98) + +Synchronously parse a `.git/config` file. If no arguments are passed, the `.git/config` file relative to `process.cwd()` is used. + **Params** * `options` **{Object|String}**: Options with `cwd` or `path`, or the cwd to use. * `returns` **{Object}** -### [.keys](index.js#L109) - -Returns an object with only the properties that had ini-style keys converted to objects (example below). - **Example** ```js -var config = parse.sync(); -var obj = parse.keys(config); +const config = parse.sync(); ``` +### [.keys](index.js#L150) + +Returns an object with only the properties that had ini-style keys converted to objects (example below). + **Params** * `config` **{Object}**: The parsed git config object. * `returns` **{Object}** +**Example** + +```js +const config = parse.sync(); +const obj = parse.keys(config); +``` + ### .keys examples Converts ini-style keys into objects: @@ -112,8 +130,8 @@ Converts ini-style keys into objects: **Example 1** ```js -var parse = require('parse-git-config'); -var config = { +const parse = require('parse-git-config'); +const config = { 'foo "bar"': { doStuff: true }, 'foo "baz"': { doStuff: true } }; @@ -135,8 +153,8 @@ Results in: **Example 2** ```js -var parse = require('parse-git-config'); -var config = { +const parse = require('parse-git-config'); +const config = { 'remote "origin"': { url: 'https://github.com/jonschlinkert/normalize-pkg.git', fetch: '+refs/heads/*:refs/remotes/origin/*' @@ -181,57 +199,68 @@ Results in: ## About -### Related projects +
+Contributing -* [git-user-name](https://www.npmjs.com/package/git-user-name): Get a user's name from git config at the project or global scope, depending on… [more](https://github.com/jonschlinkert/git-user-name) | [homepage](https://github.com/jonschlinkert/git-user-name "Get a user's name from git config at the project or global scope, depending on what git uses in the current context.") -* [git-username](https://www.npmjs.com/package/git-username): Get the username from a git remote origin URL. | [homepage](https://github.com/jonschlinkert/git-username "Get the username from a git remote origin URL.") -* [parse-author](https://www.npmjs.com/package/parse-author): Parse a string into an object with `name`, `email` and `url` properties following npm conventions… [more](https://github.com/jonschlinkert/parse-author) | [homepage](https://github.com/jonschlinkert/parse-author "Parse a string into an object with `name`, `email` and `url` properties following npm conventions. Useful for the `authors` property in package.json or for parsing an AUTHORS file into an array of authors objects.") -* [parse-authors](https://www.npmjs.com/package/parse-authors): Parse a string into an array of objects with `name`, `email` and `url` properties following… [more](https://github.com/jonschlinkert/parse-authors) | [homepage](https://github.com/jonschlinkert/parse-authors "Parse a string into an array of objects with `name`, `email` and `url` properties following npm conventions. Useful for the `authors` property in package.json or for parsing an AUTHORS file into an array of authors objects.") -* [parse-github-url](https://www.npmjs.com/package/parse-github-url): Parse a github URL into an object. | [homepage](https://github.com/jonschlinkert/parse-github-url "Parse a github URL into an object.") -* [parse-gitignore](https://www.npmjs.com/package/parse-gitignore): Parse a gitignore file into an array of patterns. Comments and empty lines are stripped. | [homepage](https://github.com/jonschlinkert/parse-gitignore "Parse a gitignore file into an array of patterns. Comments and empty lines are stripped.") +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). -### Contributing +
-Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). +
+Running Tests -### Contributors +Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: -| **Commits** | **Contributor**
| -| --- | --- | -| 48 | [jonschlinkert](https://github.com/jonschlinkert) | -| 1 | [sam3d](https://github.com/sam3d) | -| 1 | [jsdnxx](https://github.com/jsdnxx) | +```sh +$ npm install && npm test +``` -### Building docs +
+
+Building docs -_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_ +_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ -To generate the readme and API documentation with [verb](https://github.com/verbose/verb): +To generate the readme, run the following command: ```sh -$ npm install -g verb verb-generate-readme && verb +$ npm install -g verbose/verb#dev verb-generate-readme && verb ``` -### Running tests +
-Install dev dependencies: +### Related projects -```sh -$ npm install -d && npm test -``` +You might also be interested in these projects: + +* [git-user-name](https://www.npmjs.com/package/git-user-name): Get a user's name from git config at the project or global scope, depending on… [more](https://github.com/jonschlinkert/git-user-name) | [homepage](https://github.com/jonschlinkert/git-user-name "Get a user's name from git config at the project or global scope, depending on what git uses in the current context.") +* [git-username](https://www.npmjs.com/package/git-username): Get the username from a git remote origin URL. | [homepage](https://github.com/jonschlinkert/git-username "Get the username from a git remote origin URL.") +* [parse-author](https://www.npmjs.com/package/parse-author): Parse an author, contributor, maintainer or other 'person' string into an object with name, email… [more](https://github.com/jonschlinkert/parse-author) | [homepage](https://github.com/jonschlinkert/parse-author "Parse an author, contributor, maintainer or other 'person' string into an object with name, email and url properties following npm conventions.") +* [parse-authors](https://www.npmjs.com/package/parse-authors): Parse a string into an array of objects with `name`, `email` and `url` properties following… [more](https://github.com/jonschlinkert/parse-authors) | [homepage](https://github.com/jonschlinkert/parse-authors "Parse a string into an array of objects with `name`, `email` and `url` properties following npm conventions. Useful for the `authors` property in package.json or for parsing an AUTHORS file into an array of authors objects.") +* [parse-github-url](https://www.npmjs.com/package/parse-github-url): Parse a github URL into an object. | [homepage](https://github.com/jonschlinkert/parse-github-url "Parse a github URL into an object.") +* [parse-gitignore](https://www.npmjs.com/package/parse-gitignore): Parse a gitignore file into an array of patterns. Comments and empty lines are stripped. | [homepage](https://github.com/jonschlinkert/parse-gitignore "Parse a gitignore file into an array of patterns. Comments and empty lines are stripped.") + +### Contributors + +| **Commits** | **Contributor** | +| --- | --- | +| 51 | [jonschlinkert](https://github.com/jonschlinkert) | +| 1 | [sam3d](https://github.com/sam3d) | +| 1 | [js-n](https://github.com/js-n) | ### Author **Jon Schlinkert** +* [linkedin/in/jonschlinkert](https://linkedin.com/in/jonschlinkert) * [github/jonschlinkert](https://github.com/jonschlinkert) -* [twitter/jonschlinkert](http://twitter.com/jonschlinkert) +* [twitter/jonschlinkert](https://twitter.com/jonschlinkert) ### License -Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert). -Released under the [MIT license](https://github.com/jonschlinkert/parse-git-config/blob/master/LICENSE). +Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert). +Released under the [MIT License](LICENSE). *** -_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.2.0, on December 14, 2016._ \ No newline at end of file +_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on January 23, 2018._ \ No newline at end of file diff --git a/example.js b/example.js index 6ee3ba2..50597f2 100644 --- a/example.js +++ b/example.js @@ -1,7 +1,8 @@ 'use strict'; -var parse = require('./'); -var config = { +const parse = require('./'); +const gitconfig = { + email: 'email', 'remote "origin"': { url: 'https://github.com/jonschlinkert/normalize-pkg.git', fetch: '+refs/heads/*:refs/remotes/origin/*' @@ -14,4 +15,5 @@ var config = { } }; -console.log(parse.keys(config)); +const config = parse.expandKeys(gitconfig); +console.log(config); diff --git a/index.js b/index.js index 4ac85e6..bd3e409 100644 --- a/index.js +++ b/index.js @@ -7,13 +7,14 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var expand = require('expand-tilde'); -var exists = require('fs-exists-sync'); -var extend = require('extend-shallow'); -var configPath = require('git-config-path'); -var ini = require('ini'); +const fs = require('fs'); +const path = require('path'); +const util = require('util'); +const ini = require('ini'); +const configPath = require('git-config-path'); +const expand = require('expand-tilde'); +const read = util.promisify(fs.readFile); +const stat = util.promisify(fs.stat); /** * Asynchronously parse a `.git/config` file. If only the callback is passed, @@ -38,42 +39,54 @@ function parse(options, cb) { } if (typeof cb !== 'function') { - throw new TypeError('expected callback to be a function'); + return parse.promise(options); } - var filepath = parse.resolveConfigPath(options); - if (filepath === null) { - cb(); - return; - } + return parse.promise(options) + .then(config => cb(null, config)) + .catch(cb); +} - fs.stat(filepath, function(err, stat) { - if (err) { - cb(err); - return; - } +/** + * 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 + */ - fs.readFile(filepath, 'utf8', function(err, str) { - if (err) { - cb(err); - return; - } +parse.promise = function(options) { + const opts = Object.assign({}, options); + const filepath = parse.resolveConfigPath(opts); - if (options && options.include === true) { + if (!filepath) { + return Promise.resolve(null); + } + + return stat(filepath) + .then(() => { + return read(filepath, 'utf8'); + }) + .then(str => { + if (opts.include === true) { str = injectInclude(str, path.resolve(path.dirname(filepath))); } - - cb(null, ini.parse(str)); + return parseIni(str, opts); }); - }); -} +}; /** * Synchronously parse a `.git/config` file. If no arguments are passed, * the `.git/config` file relative to `process.cwd()` is used. * * ```js - * var config = parse.sync(); + * const config = parse.sync(); * ``` * * @name .sync @@ -82,18 +95,19 @@ function parse(options, cb) { * @api public */ -parse.sync = function parseSync(options) { - var filepath = parse.resolveConfigPath(options); - if (filepath && exists(filepath)) { - var input = fs.readFileSync(filepath, 'utf8'); +parse.sync = function(options) { + const opts = Object.assign({}, options); + const filepath = parse.resolveConfigPath(opts); + if (filepath && fs.existsSync(filepath)) { + const input = fs.readFileSync(filepath, 'utf8'); - if (options && options.include === true) { - var cwd = path.resolve(path.dirname(filepath)); - var str = injectInclude(input, cwd); - return ini.parse(str); + if (opts.include === true) { + const cwd = path.resolve(path.dirname(filepath)); + const str = injectInclude(input, cwd); + return parseIni(str, opts); } - return ini.parse(input); + return parseIni(input, opts); } return {}; }; @@ -106,8 +120,8 @@ parse.resolveConfigPath = function(options) { if (typeof options === 'string') { options = { type: options }; } - var opts = extend({cwd: process.cwd()}, options); - var fp = opts.path ? expand(opts.path) : configPath(opts.type); + const opts = Object.assign({cwd: process.cwd()}, options); + const fp = opts.path ? expand(opts.path) : configPath(opts.type); return fp ? path.resolve(opts.cwd, fp) : null; }; @@ -121,59 +135,57 @@ parse.resolve = function(options) { /** * Returns an object with only the properties that had ini-style keys - * converted to objects (example below). + * converted to objects. * * ```js - * var config = parse.sync(); - * var obj = parse.keys(config); + * const config = parse.sync({ path: '/path/to/.gitconfig' }); + * const obj = parse.expandKeys(config); * ``` - * @name .keys * @param {Object} `config` The parsed git config object. * @return {Object} * @api public */ -parse.keys = function parseKeys(config) { - var res = {}; - for (var key in config) { - var m = /(\S+) "(.*)"/.exec(key); +parse.expandKeys = function(config) { + for (const key of Object.keys(config)) { + const m = /(\S+) "(.*)"/.exec(key); if (!m) continue; - var prop = m[1]; - res[prop] = res[prop] || {}; - res[prop][m[2]] = config[key]; + const prop = m[1]; + config[prop] = config[prop] || {}; + config[prop][m[2]] = config[key]; + delete config[key]; } - return res; + return config; }; +function parseIni(str, options) { + const opts = Object.assign({}, options); + const config = ini.parse(str); + if (opts.expandKeys === true) { + return parse.expandKeys(config); + } + return config; +} + function injectInclude(input, cwd) { - var pathRegex = /^\s*path\s*=\s*/; - var lines = input.split('\n'); - var len = lines.length; - var filepath = ''; - var res = []; + const lines = input.split('\n').filter(function(line) { + return line.trim() !== ''; + }); - for (var i = 0; i < len; i++) { - var line = lines[i]; - var n = i; + const len = lines.length; + const res = []; + for (let i = 0; i < len; i++) { + const line = lines[i]; if (line.indexOf('[include]') === 0) { - while (n < len && !pathRegex.test(filepath)) { - filepath = lines[++n]; - } - - if (!filepath) { - return input; - } - - filepath = filepath.replace(pathRegex, ''); - var fp = path.resolve(cwd, expand(filepath)); + const filepath = lines[i + 1].replace(/^\s*path\s*=\s*/, ''); + const fp = path.resolve(cwd, expand(filepath)); res.push(fs.readFileSync(fp)); } else { res.push(line); } } - return res.join('\n'); } diff --git a/package.json b/package.json index d595282..78c0e35 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ ], "main": "index.js", "engines": { - "node": ">=0.10.0" + "node": ">=8" }, "scripts": { "test": "mocha" @@ -61,9 +61,6 @@ "parse-gitignore" ] }, - "reflinks": [ - "verb" - ], "lint": { "reflinks": true } diff --git a/test/test.js b/test/test.js index c00c66c..61090b0 100644 --- a/test/test.js +++ b/test/test.js @@ -1,108 +1,134 @@ -/*! - * parse-git-config - * - * Copyright (c) 2015-2018 Jon Schlinkert. - * Licensed under the MIT license. - */ - 'use strict'; require('mocha'); -var isTravis = process.env.TRAVIS || process.env.CLI; -var fs = require('fs'); -var os = require('os'); -var assert = require('assert'); -var path = require('path'); -var homedir = require('homedir-polyfill'); -var parse = require('..'); +const isTravis = process.env.TRAVIS || process.env.CLI; +const fs = require('fs'); +const os = require('os'); +const assert = require('assert'); +const path = require('path'); +const homedir = require('homedir-polyfill'); +const parse = require('..'); + +const cwd = (...args) => path.resolve(__dirname, ...args); +const fixture = name => cwd('fixtures', name); function read(filepath) { return fs.readFileSync(path.join(__dirname, filepath), 'utf8'); } -describe('sync:', function() { - it('should return an object', function() { - assert(parse.sync().hasOwnProperty('core')); - }); -}); +describe('parse-git-config', function() { + describe('async', function() { + it('should return a promise when callback is not passed', function(cb) { + parse({ path: fixture('_gitconfig') }) + .then(config => { + assert(config.hasOwnProperty('core')); + cb(); + }) + .catch(cb); + }); -describe('async:', function() { - it('should throw a callback is not passed:', function() { - assert.throws(function() { - parse(); - }, /expected/); - }); + it('should parse .git/config', function(cb) { + parse({ path: fixture('_gitconfig') }, function(err, config) { + assert(!err); + assert(config.hasOwnProperty('core')); + cb(); + }); + }); - it('should parse .git/config', function(cb) { - parse(function(err, config) { - assert(!err); - assert(config.hasOwnProperty('core')); - cb(); + it('should expand keys in config', function(cb) { + parse({ path: fixture('_gitconfig'), expandKeys: true }) + .then(config => { + assert(config.hasOwnProperty('color')); + assert(config.color.hasOwnProperty('diff')); + cb(); + }) + .catch(cb); }); - }); - it('should include other config sources', function() { - var fp = path.join(__dirname, 'fixtures/_gitconfig'); + it('should include other config sources', function(cb) { + parse({ path: fixture('_gitconfig'), include: true }, function(err, config) { + assert(!err); + assert.deepEqual(config, require('./expected/_gitconfig.js')); + cb(); + }); + }); - parse({ path: fp, include: true }, function(err, config) { - assert(!err); - assert.deepEqual(config, require('./expected/_gitconfig.js')); - cb(); + it('should throw an error when .git/config does not exist', function(cb) { + parse({ path: 'foo' }, function(err, config) { + assert(err instanceof Error); + assert(/ENOENT.*parse-git-config/.test(err.message)); + cb(); + }); }); }); - it('should throw an error when .git/config does not exist:', function(cb) { - parse({ path: 'foo' }, function(err, config) { - assert(err instanceof Error); - assert(/ENOENT.*parse-git-config/.test(err.message)); - cb(); + describe('promise', function() { + it('should return a promise', function(cb) { + parse.promise({ path: fixture('_gitconfig') }) + .then(config => { + assert(config.hasOwnProperty('core')); + cb(); + }); }); - }); -}); -describe('resolve:', function() { - it('should resolve the git config in the cwd by default', function() { - assert.equal(parse.resolve(), path.resolve(process.cwd(), '.git/config')); + it('should include other config sources', function() { + return parse.promise({ path: fixture('_gitconfig'), include: true }) + .then(config => { + assert.deepEqual(config, require('./expected/_gitconfig.js')); + }); + }); }); - it('should allow override path', function() { - var fp = path.resolve(homedir(), '.gitconfig'); - assert.equal(parse.resolve({ path: fp }), fp); + describe('sync', function() { + it('should return an object', function() { + assert(parse.sync({path: fixture('_gitconfig') }).hasOwnProperty('core')); + }); }); - it('should include other config sources', function() { - var fp = path.join(__dirname, 'fixtures/_gitconfig'); - var actual = parse.sync({ path: fp, include: true }); - assert.deepEqual(actual, require('./expected/_gitconfig.js')); + describe('.expandKeys', function() { + it('should expand ini-style keys', function() { + const config = { + 'foo "bar"': { doStuff: true }, + 'foo "baz"': { doStuff: true } + }; + + assert.deepEqual(parse.expandKeys(config), { + foo: { + bar: { doStuff: true }, + baz: { doStuff: true } + } + }); + }); }); - it('should resolve relative path to cwd', function() { - assert.equal(parse.resolve({ path: '.config' }), path.resolve(process.cwd(), '.config')); - }); + describe('resolve', function() { + it('should resolve the git config in the cwd by default', function() { + assert.equal(parse.resolve(), path.resolve(process.cwd(), '.git/config')); + }); - it('should resolve relative path to the global git config when `global` is passed', function() { - if (isTravis && os.platform() === 'darwin') return this.skip(); - assert.equal(parse.resolve('global'), path.resolve(homedir(), '.gitconfig')); - }); + it('should allow override path', function() { + const fp = path.resolve(homedir(), '.gitconfig'); + assert.equal(parse.resolve({ path: fp }), fp); + }); - it('should allow override of cwd', function() { - var actual = parse.resolve({ path: '.config', cwd: '/opt/config' }); - assert.equal(actual, path.resolve('/opt/config/.config')); - }); -}); + it('should include other config sources', function() { + const fp = path.join(__dirname, 'fixtures/_gitconfig'); + const actual = parse.sync({ path: fp, include: true }); + assert.deepEqual(actual, require('./expected/_gitconfig.js')); + }); + + it('should resolve relative path to cwd', function() { + assert.equal(parse.resolve({ path: '.config' }), path.resolve(process.cwd(), '.config')); + }); + + it('should resolve relative path to the global git config when `global` is passed', function() { + if (isTravis && os.platform() === 'darwin') return this.skip(); + assert.equal(parse.resolve('global'), path.resolve(homedir(), '.gitconfig')); + }); -describe('.keys:', function() { - it('should parse ini-style keys', function() { - var config = { - 'foo "bar"': { doStuff: true }, - 'foo "baz"': { doStuff: true } - }; - - assert.deepEqual(parse.keys(config), { - foo: { - bar: { doStuff: true }, - baz: { doStuff: true } - } + it('should allow override of cwd', function() { + const actual = parse.resolve({ path: '.config', cwd: '/opt/config' }); + assert.equal(actual, path.resolve('/opt/config/.config')); }); }); });