Skip to content

Commit d4176d3

Browse files
jrmykolynungoldman
authored andcommitted
feat: add promise support (#18) (#19)
* return Promise if 'parseChangelog()' is invoked without a callback. * stub out tests for Promise functonality/support. * update 'parseChangelog()' so that concurrent invocations do not mutate the same global vars. * update tests: ensure that 'callback' and 'Promise' approaches return same value. * docs(readme): update 'Usage' section with Promise info. * fix linting errors.
1 parent f034a3c commit d4176d3

File tree

3 files changed

+87
-24
lines changed

3 files changed

+87
-24
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,25 @@ npm install changelog-parser
2323
```js
2424
var parseChangelog = require('changelog-parser')
2525

26+
// CALLBACK
27+
// If provided with a callback, `parseChangelog` will invoke the function with the parsed changelog.
2628
parseChangelog('path/to/CHANGELOG.md', function (err, result) {
2729
if (err) throw err
2830

2931
// changelog object
3032
console.log(result)
3133
})
34+
35+
// Promise
36+
// If no callback is provided, `parseChangelog` will return a Promise.
37+
parseChangelog('path/to/CHANGELOG.md')
38+
.then(function(result) {
39+
// changelog object
40+
console.log(result)
41+
})
42+
.catch(() {
43+
// Whoops, something went wrong!
44+
})
3245
```
3346

3447
### Command-line interface

index.js

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,39 @@ var EOL = require('os').EOL
22
var lineReader = require('line-reader')
33
var semver = /\[?v?([\w\d.-]+\.[\w\d.-]+[a-zA-Z0-9])\]?/
44
var date = /.*([\d]{4}-[\d]{2}-[\d]{2}).*/
5-
var log
6-
var current
75

86
function parseChangelog (file, callback) {
9-
log = { versions: [] }
10-
current = null
7+
// return a Promise if invoked without a `callback`
8+
if (!callback || typeof callback !== 'function') {
9+
return doParse(file)
10+
}
1111

12-
lineReader.eachLine(file, handleLine, EOL).then(function () {
13-
// push last version into log
14-
pushCurrent()
12+
// otherwise, parse log and invoke callback
13+
doParse(file).then(function (log) {
14+
callback(null, log)
15+
})
16+
}
1517

16-
// clean up description
17-
log.description = clean(log.description)
18-
if (log.description === '') delete log.description
18+
function doParse (file) {
19+
var data = {
20+
log: { versions: [] },
21+
current: null
22+
}
1923

20-
callback(null, log)
24+
// allow `handleLine` to mutate log/current data as `this`.
25+
var cb = handleLine.bind(data)
26+
27+
return new Promise(function (resolve, reject) {
28+
lineReader.eachLine(file, cb, EOL).then(function () {
29+
// push last version into log
30+
pushCurrent(data)
31+
32+
// clean up description
33+
data.log.description = clean(data.log.description)
34+
if (data.log.description === '') delete data.log.description
35+
36+
resolve(data.log)
37+
})
2138
})
2239
}
2340

@@ -26,31 +43,31 @@ function handleLine (line) {
2643
if (line.match(/^\[[^[\]]*\] *?:/)) return
2744

2845
// set title if it's there
29-
if (!log.title && line.match(/^# ?[^#]/)) {
30-
log.title = line.substring(1).trim()
46+
if (!this.log.title && line.match(/^# ?[^#]/)) {
47+
this.log.title = line.substring(1).trim()
3148
return
3249
}
3350

3451
// new version found!
3552
if (line.match(/^## ?[^#]/)) {
36-
if (current && current.title) pushCurrent()
53+
if (this.current && this.current.title) pushCurrent(this)
3754

38-
current = versionFactory()
55+
this.current = versionFactory()
3956

40-
if (semver.exec(line)) current.version = semver.exec(line)[1]
57+
if (semver.exec(line)) this.current.version = semver.exec(line)[1]
4158

42-
current.title = line.substring(2).trim()
59+
this.current.title = line.substring(2).trim()
4360

44-
if (current.title && date.exec(current.title)) current.date = date.exec(current.title)[1]
61+
if (this.current.title && date.exec(this.current.title)) this.current.date = date.exec(this.current.title)[1]
4562

4663
return
4764
}
4865

4966
// deal with body or description content
50-
if (current) {
51-
current.body += line + EOL
67+
if (this.current) {
68+
this.current.body += line + EOL
5269
} else {
53-
log.description = (log.description || '') + line + EOL
70+
this.log.description = (this.log.description || '') + line + EOL
5471
}
5572
}
5673

@@ -63,9 +80,9 @@ function versionFactory () {
6380
}
6481
}
6582

66-
function pushCurrent () {
67-
current.body = clean(current.body)
68-
log.versions.push(current)
83+
function pushCurrent (data) {
84+
data.current.body = clean(data.current.body)
85+
data.log.versions.push(data.current)
6986
}
7087

7188
function clean (str) {

test/index.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,36 @@ test('meet expectations', function (t) {
4848
t.end()
4949
})
5050
})
51+
52+
test('returns a Promise when invoked without a valid `callback`', function (t) {
53+
t.plan(2)
54+
55+
var result = parseChangelog(path.join(__dirname, 'CHANGELOG.md'))
56+
57+
t.true(typeof result === 'object')
58+
t.true(result instanceof Promise)
59+
})
60+
61+
test('resolved Promise contains a "CHANGELOG" object', function (t) {
62+
t.plan(1)
63+
64+
parseChangelog(path.join(__dirname, 'CHANGELOG.md')).then(function (result) {
65+
t.deepEqual(result, expected)
66+
t.end()
67+
})
68+
})
69+
70+
test('callback and Promise methods should yield identical values', function (t) {
71+
t.plan(1)
72+
73+
parseChangelog(path.join(__dirname, 'CHANGELOG.md'), function (err, resultA) {
74+
if (err) t.fail()
75+
76+
parseChangelog(path.join(__dirname, 'CHANGELOG.md'))
77+
.then(function (resultB) {
78+
t.deepEqual(resultA, resultB)
79+
t.end()
80+
})
81+
.catch(t.fail)
82+
})
83+
})

0 commit comments

Comments
 (0)