Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 62 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

`bible-ref-parser` is a Node.js package that parses Bible references into structured objects. It supports complex references such as `Genesis 1:1-2; II Peter; John 1:1,3-5,8` and converts them into accessible data formats.
A Node.js package to parses Bible references into structured objects. It supports complex references such as `Genesis 1:1-2; II Peter; John 1:1,3-5,8`.


## Installation
Expand All @@ -13,38 +13,73 @@ npm install @idrw/bible-ref-parser

## Usage

### Example query, with errors
```typescript
import { parseQuery } from 'bible-ref-parser';
import { parseQuery } from '@idrw/bible-ref-parser';

const query = "Genesis 1:1-2; II Peter; John 1:1,3-5,8";
const result: BookData[] = parseQuery(query);
const result: QueryResult = parseQuery("! pasta;RM-RF/;1John1:2,2:1;IIPeter1:1;IJohn2:4;#--324;1 kebab 2:1;'_&%\"!\"¥");
```

console.log(result);
```json
{
"books": [
{
"name": "1 John",
"references": [
{ "chapter": 1, "verses": [{ "from": 2 }] },
{ "chapter": 2, "verses": [{ "from": 1 }] }
]
},
{
"name": "2 Peter",
"references": [
{ "chapter": 1, "verses": [{ "from": 1 }] }
]
},
{
"name": "1 John",
"references": [
{ "chapter": 2, "verses": [{ "from": 4 }] }
]
}
],
"errors": ["! pasta", "RM-RF/", "#--324", "1 kebab", "'_&%\"!\"¥"]
}
```

### Example query, without errors
```typescript
import { parseQuery } from '@idrw/bible-ref-parser';

const result: QueryResult = parseQuery("Genesis 1:1-2; II Peter; John 1:1,3-5,8");
```
[
{
book: "Genesis",
references: [
{ chapter: 1, verses: [{ from: 1, to: 2 }] }
]
},
{
book: "2 Peter",
references: []
},
{
book: "John",
references: [
{ chapter: 1, verses: [
{ from: 1, to: undefined },
{ from: 3, to: 5 },
{ from: 8, to: undefined }
] }
]
}
]

```json
{
"books": [
{
"name": "Genesis",
"references": [
{ "chapter": 1, "verses": [{ "from": 1, "to": 2 }] }
]
},
{ "name": "2 Peter", "references": [] },
{
"name": "John",
"references": [
{
"chapter": 1,
"verses": [
{ "from": 1 },
{ "from": 3, "to": 5 },
{ "from": 8 }
]
}
]
}
],
"errors": []
}
```

## Contributing
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@idrw/bible-ref-parser",
"version": "1.0.1",
"version": "2.0.0",
"main": "./build/index.js",
"type": "module",
"scripts": {
Expand Down
43 changes: 33 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ import { books } from "./books.js"

const QUERY_SEPARATOR = ";"

export type QueryResult = {
books: BookData[],
errors: string[]
}

export type ParseBookResult = {
book: BookData | null,
error: string | null
}


export type BookData = {
name: string,
references: ChapterData[]
Expand All @@ -20,24 +31,33 @@ export type VerseRange = {
}


export function parseQuery(query: string): BookData[] {
export function parseQuery(query: string): QueryResult {
const bookQueries = splitQueryByBooks(query)
const queryResult: QueryResult = {
books: [],
errors: []
}

let bookDataList: BookData[] = []

for (const bookQuery of bookQueries) {
if (!isValidQuery(bookQuery)) {
queryResult.errors.push(bookQuery)
continue
}

const bookData = parseBook(bookQuery)
const parseResult = parseBook(bookQuery)

if (bookData) {
bookDataList.push(bookData)
if (parseResult.error) {
queryResult.errors.push(parseResult.error)
continue
}

if (parseResult.book) {
queryResult.books.push(parseResult.book)
}
}

return bookDataList
return queryResult
}


Expand All @@ -55,19 +75,22 @@ function isValidPositiveNumber(n: string) {
}


function parseBook(query: string): BookData | null {
function parseBook(query: string): ParseBookResult {
query = replaceRomanNumbers(query)

let { bookName, chapterBeginIndex } = parseBookName(query)

const validatedName = validateBookName(bookName)
if (!validatedName) {
return null
return { book: null, error: bookName }
}

let references = parseReferences(query.slice(chapterBeginIndex))

return { name: validatedName, references }
return {
book: { name: validatedName, references },
error: null
}
}


Expand Down Expand Up @@ -147,7 +170,7 @@ function parseBookName(query: string) {

for (let i = nameBeginIndex; i < query.length; i++) {
const char = query[i]
if (char == " ") {
if (char === " " && char === bookName.charAt(bookName.length - 1)) {
continue
}

Expand Down
142 changes: 82 additions & 60 deletions test/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,14 @@ test('replaceRomanNumbers() should return the original string if no book number

test('parseBookName() should return the book name (including sequence nr) along with the index at which first chapter reference begins', async (t) => {
const cases = [
{ input: "Genesis 1:1", expected: { bookName: "Genesis", chapterBeginIndex: 8 } },
{ input: "Genesis", expected: { bookName: "Genesis", chapterBeginIndex: 0 } },
{ input: "1 Peter 1:1", expected: { bookName: "1 Peter", chapterBeginIndex: 8 } },
{ input: "1 John 1", expected: { bookName: "1 John", chapterBeginIndex: 7 } },
{ input: "1 John 1", expected: { bookName: "1 John", chapterBeginIndex: 10 } },
{ input: "1 John 1", expected: { bookName: "1 John", chapterBeginIndex: 10 } },
{ input: "1John", expected: { bookName: "1 John", chapterBeginIndex: 0 } },
{ input: "Genesis 1:1", expected: { bookName: "Genesis", chapterBeginIndex: 8 } },
{ input: "Genesis", expected: { bookName: "Genesis", chapterBeginIndex: 0 } },
{ input: "1 Peter 1:1", expected: { bookName: "1 Peter", chapterBeginIndex: 8 } },
{ input: "1 John 1", expected: { bookName: "1 John", chapterBeginIndex: 7 } },
{ input: "1 John 1", expected: { bookName: "1 John", chapterBeginIndex: 10 } },
{ input: "1 John 1", expected: { bookName: "1 John", chapterBeginIndex: 10 } },
{ input: "1John", expected: { bookName: "1 John", chapterBeginIndex: 0 } },
{ input: "Song of solomon", expected: { bookName: "Song of solomon", chapterBeginIndex: 0 } }
]

for (const { input, expected } of cases) {
Expand Down Expand Up @@ -258,35 +259,43 @@ test('validateBookName() should return true for books that are in the list', asy
test('parseBook() should return the expected bookData', async (t) => {
const cases = [
{ input: "1Kings1:2", expected: {
name: "1 Kings",
references: [
{ chapter: 1, verses: [{ from: 2, to: undefined }] }
]
book: { name: "1 Kings", references: [{ chapter: 1, verses: [{ from: 2, to: undefined }] }] },
error: null
} },

{ input: "III John 1", expected: {
book: { name: "3 John", references: [] },
error: null
}},

{ input: "III John 1", expected: { name: "3 John", references: [] }},

{ input: "Gen 1", expected: { name: "Genesis", references: [] } },

{ input: "2 Pt 1:2", expected: { name: "2 Peter", references: [
{ chapter: 1, verses: [{ from: 2, to: undefined }]}
] } },

{ input: "2 Pet 1:2", expected: { name: "2 Peter", references: [
{ chapter: 1, verses: [{ from: 2, to: undefined }]}
] } },

{ input: "2nd Peter", expected: { name: "2 Peter", references: [] } },
{ input: "First John", expected: { name: "1 John", references: [] } },
{ input: "1 John", expected: { name: "1 John", references: [] } },
{ input: "1st John", expected: { name: "1 John", references: [] } },
{ input: "first JOHN", expected: { name: "1 John", references: [] } },
{ input: "2ndtim", expected: { name: "2 Timothy", references: [] } },
{ input: "isa", expected: { name: "Isaiah", references: [] } },
{ input: "isam", expected: { name: "1 Samuel", references: [] } },
{ input: "Ism", expected: { name: "1 Samuel", references: [] } },
{ input: "IIsm", expected: { name: "2 Samuel", references: [] } },
{ input: "iSaIAH", expected: { name: "Isaiah", references: [] } },
{ input: "Gen 1", expected: {
book: { name: "Genesis", references: [] },
error: null
} },

{ input: "2 Pt 1:2", expected: {
book: { name: "2 Peter", references: [{ chapter: 1, verses: [{ from: 2, to: undefined }]}] },
error: null
} },

{ input: "2 Pet 1:2", expected: {
book: { name: "2 Peter", references: [{ chapter: 1, verses: [{ from: 2, to: undefined }]}] },
error: null
} },

{ input: "2nd Peter", expected: { book: { name: "2 Peter", references: [] }, error: null } },
{ input: "First John", expected: { book: { name: "1 John", references: [] }, error: null } },
{ input: "1 John", expected: { book: { name: "1 John", references: [] }, error: null } },
{ input: "1st John", expected: { book: { name: "1 John", references: [] }, error: null } },
{ input: "first JOHN", expected: { book: { name: "1 John", references: [] }, error: null } },
{ input: "2ndtim", expected: { book: { name: "2 Timothy", references: [] }, error: null } },
{ input: "isa", expected: { book: { name: "Isaiah", references: [] }, error: null } },
{ input: "isam", expected: { book: { name: "1 Samuel", references: [] }, error: null } },
{ input: "Ism", expected: { book: { name: "1 Samuel", references: [] }, error: null } },
{ input: "IIsm", expected: { book: { name: "2 Samuel", references: [] }, error: null } },
{ input: "iSaIAH", expected: { book: { name: "Isaiah", references: [] }, error: null } },

// { input: "! Genesis", expected: { book: null, error: "!Genesis" } },
]

for (const { input, expected } of cases) {
Expand All @@ -299,39 +308,52 @@ test('parseQuery() should return the expected bookData[]', async (t) => {
const cases = [
{
input: "1Kings1:2;IIIJohn1",
expected: [
{
name: "1 Kings",
references: [
{ chapter: 1, verses: [{ from: 2, to: undefined }] }
]
},
{ name: "3 John", references: [] }
]
expected: {
books: [
{
name: "1 Kings",
references: [
{ chapter: 1, verses: [{ from: 2, to: undefined }] }
]
},
{ name: "3 John", references: [] }
],
errors: []
}
},

{
input: "III John 1:1-2;Genesis 1:10-12",
expected: [
{
name: "3 John",
references: [{ chapter: 1, verses: [{ from: 1, to: 2 }] }]
},
{
name: "Genesis",
references: [{ chapter: 1, verses: [{ from: 10, to: 12 }] }]
}
]
expected: {
books: [
{
name: "3 John",
references: [{ chapter: 1, verses: [{ from: 1, to: 2 }] }]
},
{
name: "Genesis",
references: [{ chapter: 1, verses: [{ from: 10, to: 12 }] }]
}
],
errors: []
}
},

{
input: ";##;#!\"@,;Genesis 1:10-12",
expected: [
{
name: "Genesis",
references: [{ chapter: 1, verses: [{ from: 10, to: 12 }] }]
}
]
input: ";##;#!\"@,;Genesis 1:10-12;song of solomon",
expected: {
books: [
{
name: "Genesis",
references: [{ chapter: 1, verses: [{ from: 10, to: 12 }] }]
},
{
name: "Song of Solomon",
references: []
}
],
errors: ["##", "#!\"@,"]
}
}
]

Expand Down