@helloao/tools 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/generation/api.cjs +727 -2
- package/dist/cjs/generation/api.cjs.map +2 -2
- package/dist/cjs/generation/audio.cjs +52 -5
- package/dist/cjs/generation/audio.cjs.map +2 -2
- package/dist/cjs/generation/book-order.cjs +1 -1
- package/dist/cjs/generation/book-order.cjs.map +2 -2
- package/dist/cjs/generation/common-types.cjs +651 -0
- package/dist/cjs/generation/common-types.cjs.map +2 -2
- package/dist/cjs/generation/dataset.cjs +31 -8
- package/dist/cjs/generation/dataset.cjs.map +2 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/log.cjs.map +1 -1
- package/dist/cjs/parser/codex-parser.cjs.map +1 -1
- package/dist/cjs/parser/commentary-csv-parser.cjs.map +1 -1
- package/dist/cjs/parser/iterators.cjs.map +1 -1
- package/dist/cjs/parser/lockman-parser.cjs +334 -0
- package/dist/cjs/parser/lockman-parser.cjs.map +7 -0
- package/dist/cjs/parser/tyndale-xml-parser.cjs.map +1 -1
- package/dist/cjs/parser/types.cjs.map +1 -1
- package/dist/cjs/parser/usfm-parser.cjs.map +1 -1
- package/dist/cjs/parser/usx-parser.cjs +24 -7
- package/dist/cjs/parser/usx-parser.cjs.map +2 -2
- package/dist/cjs/utils.cjs +152 -0
- package/dist/cjs/utils.cjs.map +3 -3
- package/dist/esm/generation/api.js +696 -2
- package/dist/esm/generation/api.js.map +2 -2
- package/dist/esm/generation/audio.js +46 -3
- package/dist/esm/generation/audio.js.map +2 -2
- package/dist/esm/generation/book-order.js +1 -1
- package/dist/esm/generation/book-order.js.map +2 -2
- package/dist/esm/generation/common-types.js +590 -0
- package/dist/esm/generation/common-types.js.map +3 -3
- package/dist/esm/generation/dataset.js +29 -8
- package/dist/esm/generation/dataset.js.map +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/log.js.map +1 -1
- package/dist/esm/parser/codex-parser.js.map +1 -1
- package/dist/esm/parser/commentary-csv-parser.js.map +1 -1
- package/dist/esm/parser/iterators.js.map +1 -1
- package/dist/esm/parser/lockman-parser.js +306 -0
- package/dist/esm/parser/lockman-parser.js.map +7 -0
- package/dist/esm/parser/tyndale-xml-parser.js.map +1 -1
- package/dist/esm/parser/usfm-parser.js.map +1 -1
- package/dist/esm/parser/usx-parser.js +24 -7
- package/dist/esm/parser/usx-parser.js.map +2 -2
- package/dist/esm/utils.js +138 -0
- package/dist/esm/utils.js.map +2 -2
- package/dist/types/generation/api.d.ts +2515 -470
- package/dist/types/generation/audio.d.ts +5 -0
- package/dist/types/generation/common-types.d.ts +1222 -413
- package/dist/types/generation/dataset.d.ts +1 -0
- package/dist/types/parser/codex-parser.d.ts +18 -138
- package/dist/types/parser/lockman-parser.d.ts +14 -0
- package/dist/types/parser/types.d.ts +4 -0
- package/dist/types/parser/usx-parser.d.ts +2 -2
- package/dist/types/utils.d.ts +223 -18
- package/package.json +5 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../parser/usfm-parser.ts"],
|
|
4
|
-
"sourcesContent": ["import { findLast } from 'lodash';\nimport {\n ParseTree,\n Chapter,\n Verse,\n HebrewSubtitle,\n FootnoteReference,\n Footnote,\n Text,\n} from './types.js';\nimport { getLogger } from '../log.js';\n\n/**\n * Defines a class that can tokenize a stream of characters into tokens.\n */\nexport class UsfmTokenizer {\n private _input: string = '';\n private _index: number = 0;\n private _start: number = 0;\n\n private get _tokenLength() {\n return this._index - this._start;\n }\n\n /**\n * Converts the given input into a list of tokens.\n * @param input The input that should be tokenized.\n */\n tokenize(input: string): SimpleToken[] {\n this._input = input;\n this._index = 0;\n\n return this._parseTokens();\n }\n\n private _parseTokens() {\n let tokens: SimpleToken[] = [];\n let token = this._parseToken();\n while (token) {\n tokens.push(token);\n token = this._parseToken();\n }\n\n return tokens;\n }\n\n private _parseToken(): SimpleToken | null {\n let state:\n | 'none'\n | 'marker_start'\n | 'marker_number'\n | 'whitespace'\n | 'word' = 'none';\n let kind: T | null = null;\n this._start = this._index;\n\n while (this._index < this._input.length) {\n const codePointNumber = this._input.codePointAt(this._index);\n\n if (typeof codePointNumber === 'undefined') {\n throw new Error('Unable to get code point!');\n }\n\n const codePoint = String.fromCodePoint(codePointNumber);\n\n if (state === 'none') {\n if (codePoint === '\\\\') {\n state = 'marker_start';\n } else if (isWhitespace(codePoint)) {\n state = 'whitespace';\n } else {\n state = 'word';\n }\n } else if (state === 'marker_start') {\n if (isDigit(codePoint)) {\n if (this._tokenLength === 0) {\n throw new Error(\n 'Invalid Marker: Markers must not contain only digits.'\n );\n }\n state = 'marker_number';\n } else if (codePoint === '*') {\n this._index += codePoint.length;\n kind = 'marker';\n break;\n } else if (isWhitespace(codePoint)) {\n kind = 'marker';\n break;\n }\n } else if (state === 'marker_number') {\n if (codePoint === '*') {\n this._index += codePoint.length;\n kind = 'marker';\n break;\n } else if (!isDigit(codePoint)) {\n kind = 'marker';\n break;\n }\n } else if (state === 'whitespace') {\n if (!isWhitespace(codePoint)) {\n kind = 'whitespace';\n break;\n }\n } else if (state === 'word') {\n if (isWhitespace(codePoint) || codePoint === '\\\\') {\n kind = 'word';\n break;\n }\n }\n\n this._index += codePoint.length;\n }\n\n if (!kind) {\n if (this._index >= this._input.length) {\n if (state == 'marker_start' || state === 'marker_number') {\n kind = 'marker';\n } else if (state === 'word') {\n kind = 'word';\n } else if (state === 'whitespace') {\n kind = 'whitespace';\n }\n }\n }\n\n if (kind) {\n return t(loc(this._start, this._index), kind);\n }\n\n return null;\n }\n}\n\nexport interface UsfmParseOptions {\n paragraphs: Set<string>;\n}\n\n/**\n * Defines a USFM Parser.\n */\nexport class UsfmParser {\n private _poem: number | null = null;\n private _wordsOfJesus: boolean = false;\n\n tokenize(input: string): Token[] {\n const simpleTokens = new UsfmTokenizer().tokenize(input);\n let tokens: Token[] = [];\n\n for (let t of simpleTokens) {\n if (t.kind === 'marker') {\n let source = input.substring(t.loc.start, t.loc.end);\n const isEnd = source.endsWith('*');\n\n if (isEnd) {\n source = source.substring(0, source.length - 1);\n }\n\n let numberIndex = -1;\n for (let i = 0; i < source.length; i++) {\n if (isDigit(source[i])) {\n numberIndex = i;\n break;\n }\n }\n\n let number: number | null = null;\n if (numberIndex === 1) {\n throw new Error(\n 'Markers must not be made only of numbers!'\n );\n }\n if (numberIndex > 0) {\n number = parseInt(source.substring(numberIndex));\n source = source.substring(0, numberIndex);\n }\n\n if (source.length === 1) {\n if (isEnd) {\n // Ending marker does not have a command.\n // We should look for a matching start marker.\n const startMarker = findLast(\n tokens,\n (t) => t.kind === 'marker' && t.type === 'start'\n ) as MarkerToken;\n if (startMarker) {\n source = startMarker.command;\n }\n }\n\n if (source.length === 1) {\n throw new Error(\n `Markers must have a command! Token: ${t.loc.start}-${t.loc.end}`\n );\n }\n }\n\n tokens.push(\n marker(t.loc, source, number, isEnd ? 'end' : 'start')\n );\n } else if (t.kind === 'whitespace') {\n let source = input.substring(t.loc.start, t.loc.end);\n tokens.push(whitespace(t.loc, source));\n } else if (t.kind === 'word') {\n let source = input.substring(t.loc.start, t.loc.end);\n tokens.push(word(t.loc, source));\n }\n }\n\n return tokens;\n }\n\n parse(input: string): ParseTree {\n let root: ParseTree = {\n type: 'root',\n content: [],\n };\n\n const tokens = this.tokenize(input);\n\n let expectingId = 0;\n let expectingName = 0;\n let expectingTitle = 0;\n let expectingSectionHeading = 0;\n let expectingFootnote = 0;\n let expectingFootnoteReference = 0;\n let expectingFootnoteText = 0;\n let expectingReferenceText = 0;\n let expectingWordAttribute = 0;\n let expectingNestedWordAttribute = 0;\n let expectingWordsOfJesus = 0;\n let expectingIntroParagraph = 0;\n let expectingCrossReference = 0;\n let expectingUnknownCommand = 0;\n\n let canParseFootnotes = true;\n let chapter: Chapter | null = null;\n let lastVerse: Verse | null = null;\n let verse: Verse | null = null;\n let subtitle: HebrewSubtitle | null = null;\n let words: string[] = [];\n let verseContent: (Text | FootnoteReference | string)[] = [];\n let sectionContent: string = '';\n let currentFootnoteId = 0;\n let footnote: Footnote | null = null;\n\n this._poem = null;\n\n const addWordsToVerseOrSubtitle = () => {\n if (words.length > 0) {\n const text = this._text(words.join('').trimEnd());\n if (verse) {\n verse.content.push(text);\n } else if (subtitle) {\n subtitle.content.push(text);\n } else {\n verseContent.push(text);\n }\n words = [];\n }\n };\n\n const addVerseContentToChapter = (token: Token | null) => {\n if (!chapter) {\n return;\n }\n if (verseContent.length > 0) {\n if (chapter.content.some((c) => c.type === 'verse')) {\n this._throwError(\n input,\n token,\n 'Cannot infer first verse after other verses have been added to the chapter!'\n );\n }\n // Implicit first verse\n verse = {\n type: 'verse',\n number: 1,\n content: verseContent,\n };\n chapter.content.push(verse);\n verseContent = [];\n }\n };\n\n const cleanupVerse = () => {\n if (!verse || !chapter) {\n return;\n }\n let chapterContent: Chapter['content'] = [];\n for (let i = verse.content.length - 1; i >= 0; i--) {\n let content = verse.content[i];\n if (typeof content === 'object' && 'heading' in content) {\n // move headings that occur at the end of a verse to the chapter\n chapterContent.unshift({\n type: 'heading',\n content: [content.heading],\n });\n verse.content.splice(i, 1);\n } else if (\n typeof content === 'object' &&\n 'lineBreak' in content &&\n content.lineBreak\n ) {\n // move line breaks that occur at the end of a verse to the chapter\n chapterContent.unshift({\n type: 'line_break',\n });\n verse.content.splice(i, 1);\n } else {\n break;\n }\n }\n\n for (let content of chapterContent) {\n chapter.content.push(content);\n }\n };\n\n const completeVerseOrSubtitle = (token: Token | null) => {\n if (verse && isNaN(verse.number)) {\n // Verse is invalid for some reason.\n const index = chapter!.content.indexOf(verse);\n if (index >= 0) {\n chapter!.content.splice(index, 1);\n }\n verse = null;\n }\n if (verse || subtitle) {\n addWordsToVerseOrSubtitle();\n }\n\n addVerseContentToChapter(token);\n cleanupVerse();\n };\n\n const completeSection = () => {\n if (expectingSectionHeading > 0) {\n if (verse) {\n addWordsToVerseOrSubtitle();\n verse.content.push({\n heading: sectionContent,\n });\n } else if (chapter) {\n chapter.content.push({\n type: 'heading',\n content: [sectionContent],\n });\n } else {\n root.content.push({\n type: 'heading',\n content: [sectionContent],\n });\n }\n sectionContent = '';\n expectingSectionHeading = 0;\n }\n };\n\n const addWordsToFootnote = () => {\n if (footnote && words.length > 0) {\n footnote.text += words.join(' ');\n words = [];\n }\n };\n\n for (let token of tokens) {\n if (token.kind === 'marker') {\n if (token.command === '\\\\c') {\n addWordsToVerseOrSubtitle();\n cleanupVerse();\n\n chapter = {\n type: 'chapter',\n number: NaN,\n content: [],\n footnotes: [],\n };\n verse = null;\n verseContent = [];\n\n root.content.push(chapter);\n } else if (token.command === '\\\\v') {\n if (!chapter) {\n this._throwError(\n input,\n token,\n 'Cannot parse a verse without chapter information!'\n );\n } else {\n completeSection();\n completeVerseOrSubtitle(token);\n\n lastVerse = verse;\n verse = {\n type: 'verse',\n number: NaN,\n content: [],\n };\n\n chapter.content.push(verse);\n }\n } else if (token.command === '\\\\d') {\n if (!chapter) {\n this._throwError(\n input,\n token,\n 'Cannot parse a hebrew subtitle without chapter information!'\n );\n } else {\n completeVerseOrSubtitle(token);\n\n subtitle = {\n type: 'hebrew_subtitle',\n content: [],\n };\n\n chapter.content.push(subtitle);\n }\n } else if (token.command === '\\\\b' || token.command === '\\\\p') {\n if (!chapter) {\n this._throwError(\n input,\n token,\n 'Cannot parse a line break without chapter information!'\n );\n } else {\n if (verse) {\n addWordsToVerseOrSubtitle();\n verse.content.push({\n lineBreak: true,\n });\n } else {\n completeVerseOrSubtitle(token);\n chapter.content.push({\n type: 'line_break',\n });\n }\n }\n } else if (token.command === '\\\\q') {\n addWordsToVerseOrSubtitle();\n this._poem = token.number;\n } else if (token.command === '\\\\p') {\n addWordsToVerseOrSubtitle();\n this._poem = null;\n } else if (token.command === '\\\\id') {\n expectingId = 1;\n } else if (token.command === '\\\\h') {\n expectingName = 1;\n root.header = undefined;\n } else if (\n token.command === '\\\\mt' ||\n token.command === '\\\\+mt'\n ) {\n expectingTitle = 1;\n } else if (token.command === '\\\\s') {\n expectingSectionHeading = 1;\n } else if (token.command === '\\\\r') {\n expectingReferenceText = 1;\n } else if (token.command === '\\\\f' && canParseFootnotes) {\n if (token.type === 'start') {\n if (!chapter) {\n this._throwError(\n input,\n token,\n 'Cannot start a footnote outside of a chapter!',\n true\n );\n } else {\n addWordsToVerseOrSubtitle();\n footnote = {\n noteId: currentFootnoteId,\n text: '',\n caller: null,\n };\n const ref: FootnoteReference = {\n noteId: footnote.noteId,\n };\n expectingFootnote = 1;\n\n chapter.footnotes.push(footnote);\n\n if (verse) {\n verse.content.push(ref);\n } else if (subtitle) {\n subtitle.content.push(ref);\n } else {\n verseContent.push(ref);\n }\n\n currentFootnoteId += 1;\n }\n } else {\n addWordsToFootnote();\n expectingFootnote = 0;\n expectingFootnoteText = 0;\n expectingFootnoteReference = 0;\n footnote = null;\n }\n } else if (token.command === '\\\\fr' && canParseFootnotes) {\n if (!footnote) {\n this._throwError(\n input,\n token,\n 'Cannot start a footnote reference outside of a footnote!',\n true\n );\n } else {\n expectingFootnoteReference = 1;\n }\n } else if (token.command === '\\\\ft' && canParseFootnotes) {\n if (!footnote) {\n this._throwError(\n input,\n token,\n 'Cannot start footnote text outside of a footnote!',\n true\n );\n } else {\n expectingFootnoteText = 1;\n }\n } else if (token.command === '\\\\w') {\n if (token.type === 'start') {\n expectingWordAttribute = 1;\n } else {\n expectingWordAttribute = 0;\n }\n } else if (token.command === '\\\\+w') {\n if (token.type === 'start') {\n expectingNestedWordAttribute = 1;\n } else {\n expectingNestedWordAttribute = 0;\n }\n } else if (\n token.command === '\\\\wj' ||\n token.command === '\\\\+wj'\n ) {\n if (token.type === 'start') {\n addWordsToVerseOrSubtitle();\n this._wordsOfJesus = true;\n } else {\n addWordsToVerseOrSubtitle();\n this._wordsOfJesus = false;\n }\n } else if (token.command === '\\\\ip') {\n expectingIntroParagraph = 1;\n canParseFootnotes = false;\n } else if (token.command === '\\\\x') {\n if (token.type === 'start') {\n expectingCrossReference = 1;\n } else {\n expectingCrossReference = 0;\n }\n } else if (token.command.indexOf('-') >= 0) {\n if (token.type === 'start') {\n expectingUnknownCommand = 1;\n }\n } else if (token.type === 'end') {\n expectingUnknownCommand = 0;\n }\n } else if (token.kind === 'word') {\n if (expectingId > 0) {\n if (expectingId === 1) {\n root.id = token.word;\n expectingId = 2;\n }\n } else if (expectingName > 0) {\n if (root.header) {\n root.header += ' ' + token.word;\n } else {\n root.header = token.word;\n }\n } else if (expectingTitle > 0) {\n if (root.title) {\n root.title += ' ' + token.word;\n } else {\n root.title = token.word;\n }\n } else if (expectingSectionHeading > 0) {\n if (sectionContent) {\n sectionContent += ' ' + token.word;\n } else {\n sectionContent = token.word;\n }\n } else if (expectingFootnoteReference > 0) {\n if ((expectingFootnoteReference = 1)) {\n const [chapter, verse] = token.word.split(/[\\.\\:]/);\n\n if (footnote) {\n footnote.reference = {\n chapter: parseInt(chapter),\n verse: parseInt(verse),\n };\n }\n\n expectingFootnoteReference = 0;\n }\n } else if (expectingFootnoteText > 0) {\n words.push(token.word);\n } else if (expectingFootnote > 0) {\n if (expectingFootnote === 1) {\n if (token.word) {\n if (footnote) {\n if (token.word === '+' || token.word !== '-') {\n footnote.caller = token.word;\n } else {\n footnote.caller = null;\n }\n }\n // this._throwError(input, token, 'Footnotes must use the \"+\" caller.');\n }\n expectingFootnote = 2;\n } else {\n words.push(token.word);\n }\n } else if (expectingReferenceText > 0) {\n // Skip processing words for references\n // because references aren't included in the JSON format\n // (for now)\n } else if (expectingCrossReference > 0) {\n // Skip processing words for cross references\n } else if (chapter && isNaN(chapter.number)) {\n chapter.number = parseInt(token.word);\n if (isNaN(chapter.number)) {\n this._throwError(\n input,\n token,\n 'The first word token after a chapter marker must be parsable to an integer!'\n );\n }\n } else if (verse && isNaN(verse.number)) {\n verse.number = parseInt(token.word);\n if (isNaN(verse.number)) {\n this._throwError(\n input,\n token,\n 'The first word token after a verse marker must be parsable to an integer!'\n );\n }\n } else if (expectingWordAttribute > 0) {\n if (expectingWordAttribute === 1) {\n const firstVerticalBarIndex = token.word.indexOf('|');\n\n if (firstVerticalBarIndex >= 0) {\n const name = token.word.slice(\n 0,\n firstVerticalBarIndex\n );\n // const rest = token.word.slice(firstVerticalBarIndex + '|'.length);\n words.push(name);\n expectingWordAttribute = 2;\n } else {\n words.push(token.word);\n }\n }\n } else if (expectingNestedWordAttribute > 0) {\n if (expectingNestedWordAttribute === 1) {\n const firstVerticalBarIndex = token.word.indexOf('|');\n\n if (firstVerticalBarIndex >= 0) {\n const name = token.word.slice(\n 0,\n firstVerticalBarIndex\n );\n // const rest = token.word.slice(firstVerticalBarIndex + '|'.length);\n words.push(name);\n expectingNestedWordAttribute = 2;\n } else {\n words.push(token.word);\n }\n }\n } else if (expectingUnknownCommand > 0) {\n } else if (expectingIntroParagraph > 0) {\n // Skip processing words for intro paragraphs\n } else {\n words.push(token.word);\n }\n } else if (token.kind === 'whitespace') {\n if (expectingId > 0) {\n if (token.whitespace.includes('\\n')) {\n expectingId = 0;\n }\n } else if (expectingName > 0) {\n if (token.whitespace.includes('\\n')) {\n expectingName = 0;\n }\n } else if (expectingTitle > 0) {\n if (token.whitespace.includes('\\n')) {\n expectingTitle = 0;\n }\n } else if (expectingSectionHeading > 0) {\n if (token.whitespace.includes('\\n')) {\n completeSection();\n }\n } else if (expectingReferenceText > 0) {\n if (token.whitespace.includes('\\n')) {\n expectingReferenceText = 0;\n }\n } else if (expectingIntroParagraph > 0) {\n if (token.whitespace.includes('\\n')) {\n expectingIntroParagraph = 0;\n canParseFootnotes = true;\n }\n } else if (\n expectingId > 0 ||\n expectingName > 0 ||\n expectingTitle > 0 ||\n expectingSectionHeading > 0 ||\n expectingFootnote > 0 ||\n expectingFootnoteReference > 0 ||\n expectingFootnoteText > 0 ||\n expectingReferenceText > 0 ||\n expectingCrossReference > 0 ||\n expectingWordAttribute > 0 ||\n expectingNestedWordAttribute > 0\n ) {\n // Skip\n } else if (expectingUnknownCommand > 0) {\n if (token.whitespace.includes('\\n')) {\n expectingUnknownCommand = 0;\n }\n } else if (words.length > 0) {\n let lastWord = words[words.length - 1];\n if (lastWord !== ' ') {\n words.push(' ');\n }\n }\n }\n }\n\n completeVerseOrSubtitle(null);\n\n return root;\n }\n\n renderMarkdown(tree: ParseTree): string {\n let md = '';\n\n if (tree.header) {\n md += `# ${tree.header}\\n`;\n }\n\n for (let c of tree.content) {\n if (c.type === 'heading') {\n md += `## ${c.content.join(' ')}\\n`;\n } else if (c.type === 'chapter') {\n md += `### ${c.number}\\n`;\n\n for (let content of c.content) {\n if (content.type === 'heading') {\n md += `#### ${content.content.join(' ')}\\n`;\n } else if (content.type === 'line_break') {\n md += '\\n\\n';\n } else if (content.type === 'verse') {\n md += `<em>${content.number}</em>`;\n for (let v of content.content) {\n if (typeof v === 'string') {\n md += v + ' ';\n } else if ('text' in v) {\n md += v.text + ' ';\n }\n }\n md += '\\n';\n }\n }\n }\n }\n\n return md;\n }\n\n private _hasAttribute() {\n return this._poem !== null || this._wordsOfJesus;\n }\n\n private _text(text: string): Text | string {\n if (!this._hasAttribute()) {\n return text;\n }\n const t: Text = {\n text,\n };\n\n if (this._poem !== null) {\n t.poem = this._poem;\n }\n\n if (this._wordsOfJesus) {\n t.wordsOfJesus = true;\n }\n\n return t;\n }\n\n private _throwError(\n source: string,\n token: Token | null,\n message: string,\n warn: boolean = false\n ): void {\n const logger = getLogger();\n if (token) {\n let line = 1;\n let column = 1;\n\n let start = token.loc.start;\n\n for (let i = 0; i < start; i++) {\n let char = source[i];\n if (char === '\\n') {\n line += 1;\n column = 1;\n } else {\n column += 1;\n }\n }\n\n let tokenDebug = '';\n if (token.kind === 'word') {\n tokenDebug = ', word';\n } else if (token.kind === 'marker') {\n tokenDebug = ', ' + token.command;\n } else {\n tokenDebug = '';\n }\n\n if (warn) {\n logger.warn(`(${line}, ${column}${tokenDebug}) ${message}`);\n } else {\n throw new Error(`(${line}, ${column}${tokenDebug}) ${message}`);\n }\n } else {\n if (warn) {\n logger.warn(message);\n } else {\n throw new Error(message);\n }\n }\n }\n}\n\n/**\n * Determines if the given character is a digit.\n * @param char The character.\n */\nexport function isDigit(char: string): boolean {\n return char.length === 1 && char >= '0' && char <= '9';\n}\n\n/**\n * Determines if the given character is considered whitespace.\n * @param char The character.\n */\nexport function isWhitespace(char: string): boolean {\n return char === ' ' || char === '\\t' || char === '\\n' || char === '\\r';\n}\n\nexport function t(loc: SourceLocation, kind: T): SimpleToken {\n return {\n loc,\n kind,\n };\n}\n\n/**\n * Creates a new source location.\n * @param start The start of the location.\n * @param end The end of the location.\n */\nexport function loc(start: number, end: number): SourceLocation {\n return {\n start,\n end,\n };\n}\n\n/**\n * Creates a new marker token.\n * @param loc The location for the token.\n * @param command The command that the token contains.\n * @param number The number that the marker contains.\n * @param type The type of the marker.\n */\nexport function marker(\n loc: SourceLocation,\n command: string,\n number: number | null = null,\n type: MarkerToken['type'] = 'start'\n): MarkerToken {\n return {\n kind: 'marker',\n loc,\n command,\n number,\n type,\n };\n}\n\n/**\n * Creates a new word token.\n * @param loc The location for the token.\n * @param word The word contained by the token.\n */\nexport function word(loc: SourceLocation, word: string): WordToken {\n return {\n kind: 'word',\n loc,\n word,\n };\n}\n\nexport function whitespace(\n loc: SourceLocation,\n whitespace: string\n): WhitespaceToken {\n return {\n kind: 'whitespace',\n loc,\n whitespace,\n };\n}\n\nexport type T = Token['kind'];\n\n/**\n * Defines a simple token.\n */\nexport interface SimpleToken {\n kind: T;\n loc: SourceLocation;\n}\n\nexport type Token = MarkerToken | WordToken | WhitespaceToken;\n\n/**\n * Defines an interface for a USFM marker node.\n * That is, the syntax for a marker.\n */\nexport interface MarkerToken {\n kind: 'marker';\n\n /**\n * The command that the marker represents.\n * This is generally the name of the marker (like \"p\" or \"v\" for paragraph and verse markers)\n */\n command: string;\n\n /**\n * The number that the marker contains.\n * Null if no number was specified.\n */\n number: number | null;\n\n /**\n * Whether the marker represents a start or an end.\n */\n type: 'start' | 'end';\n\n /**\n * The location of the node.\n */\n loc: SourceLocation;\n}\n\n/**\n * Defines an interface for a Whitespace node.\n */\nexport interface WhitespaceToken {\n kind: 'whitespace';\n\n /**\n * The whitespace contained by the node.\n */\n whitespace: string;\n\n /**\n * The location of the node.\n */\n loc: SourceLocation;\n}\n\n/**\n * Defines an interface for a word token.\n */\nexport interface WordToken {\n kind: 'word';\n\n /**\n * The word contained by the token.\n */\n word: string;\n\n /**\n * The location of the word.\n */\n loc: SourceLocation;\n}\n\n/**\n * The source location of the node.\n */\nexport interface SourceLocation {\n start: number;\n end: number;\n}\n"],
|
|
4
|
+
"sourcesContent": ["import { findLast } from 'lodash';\r\nimport {\r\n ParseTree,\r\n Chapter,\r\n Verse,\r\n HebrewSubtitle,\r\n FootnoteReference,\r\n Footnote,\r\n Text,\r\n} from './types.js';\r\nimport { getLogger } from '../log.js';\r\n\r\n/**\r\n * Defines a class that can tokenize a stream of characters into tokens.\r\n */\r\nexport class UsfmTokenizer {\r\n private _input: string = '';\r\n private _index: number = 0;\r\n private _start: number = 0;\r\n\r\n private get _tokenLength() {\r\n return this._index - this._start;\r\n }\r\n\r\n /**\r\n * Converts the given input into a list of tokens.\r\n * @param input The input that should be tokenized.\r\n */\r\n tokenize(input: string): SimpleToken[] {\r\n this._input = input;\r\n this._index = 0;\r\n\r\n return this._parseTokens();\r\n }\r\n\r\n private _parseTokens() {\r\n let tokens: SimpleToken[] = [];\r\n let token = this._parseToken();\r\n while (token) {\r\n tokens.push(token);\r\n token = this._parseToken();\r\n }\r\n\r\n return tokens;\r\n }\r\n\r\n private _parseToken(): SimpleToken | null {\r\n let state:\r\n | 'none'\r\n | 'marker_start'\r\n | 'marker_number'\r\n | 'whitespace'\r\n | 'word' = 'none';\r\n let kind: T | null = null;\r\n this._start = this._index;\r\n\r\n while (this._index < this._input.length) {\r\n const codePointNumber = this._input.codePointAt(this._index);\r\n\r\n if (typeof codePointNumber === 'undefined') {\r\n throw new Error('Unable to get code point!');\r\n }\r\n\r\n const codePoint = String.fromCodePoint(codePointNumber);\r\n\r\n if (state === 'none') {\r\n if (codePoint === '\\\\') {\r\n state = 'marker_start';\r\n } else if (isWhitespace(codePoint)) {\r\n state = 'whitespace';\r\n } else {\r\n state = 'word';\r\n }\r\n } else if (state === 'marker_start') {\r\n if (isDigit(codePoint)) {\r\n if (this._tokenLength === 0) {\r\n throw new Error(\r\n 'Invalid Marker: Markers must not contain only digits.'\r\n );\r\n }\r\n state = 'marker_number';\r\n } else if (codePoint === '*') {\r\n this._index += codePoint.length;\r\n kind = 'marker';\r\n break;\r\n } else if (isWhitespace(codePoint)) {\r\n kind = 'marker';\r\n break;\r\n }\r\n } else if (state === 'marker_number') {\r\n if (codePoint === '*') {\r\n this._index += codePoint.length;\r\n kind = 'marker';\r\n break;\r\n } else if (!isDigit(codePoint)) {\r\n kind = 'marker';\r\n break;\r\n }\r\n } else if (state === 'whitespace') {\r\n if (!isWhitespace(codePoint)) {\r\n kind = 'whitespace';\r\n break;\r\n }\r\n } else if (state === 'word') {\r\n if (isWhitespace(codePoint) || codePoint === '\\\\') {\r\n kind = 'word';\r\n break;\r\n }\r\n }\r\n\r\n this._index += codePoint.length;\r\n }\r\n\r\n if (!kind) {\r\n if (this._index >= this._input.length) {\r\n if (state == 'marker_start' || state === 'marker_number') {\r\n kind = 'marker';\r\n } else if (state === 'word') {\r\n kind = 'word';\r\n } else if (state === 'whitespace') {\r\n kind = 'whitespace';\r\n }\r\n }\r\n }\r\n\r\n if (kind) {\r\n return t(loc(this._start, this._index), kind);\r\n }\r\n\r\n return null;\r\n }\r\n}\r\n\r\nexport interface UsfmParseOptions {\r\n paragraphs: Set<string>;\r\n}\r\n\r\n/**\r\n * Defines a USFM Parser.\r\n */\r\nexport class UsfmParser {\r\n private _poem: number | null = null;\r\n private _wordsOfJesus: boolean = false;\r\n\r\n tokenize(input: string): Token[] {\r\n const simpleTokens = new UsfmTokenizer().tokenize(input);\r\n let tokens: Token[] = [];\r\n\r\n for (let t of simpleTokens) {\r\n if (t.kind === 'marker') {\r\n let source = input.substring(t.loc.start, t.loc.end);\r\n const isEnd = source.endsWith('*');\r\n\r\n if (isEnd) {\r\n source = source.substring(0, source.length - 1);\r\n }\r\n\r\n let numberIndex = -1;\r\n for (let i = 0; i < source.length; i++) {\r\n if (isDigit(source[i])) {\r\n numberIndex = i;\r\n break;\r\n }\r\n }\r\n\r\n let number: number | null = null;\r\n if (numberIndex === 1) {\r\n throw new Error(\r\n 'Markers must not be made only of numbers!'\r\n );\r\n }\r\n if (numberIndex > 0) {\r\n number = parseInt(source.substring(numberIndex));\r\n source = source.substring(0, numberIndex);\r\n }\r\n\r\n if (source.length === 1) {\r\n if (isEnd) {\r\n // Ending marker does not have a command.\r\n // We should look for a matching start marker.\r\n const startMarker = findLast(\r\n tokens,\r\n (t) => t.kind === 'marker' && t.type === 'start'\r\n ) as MarkerToken;\r\n if (startMarker) {\r\n source = startMarker.command;\r\n }\r\n }\r\n\r\n if (source.length === 1) {\r\n throw new Error(\r\n `Markers must have a command! Token: ${t.loc.start}-${t.loc.end}`\r\n );\r\n }\r\n }\r\n\r\n tokens.push(\r\n marker(t.loc, source, number, isEnd ? 'end' : 'start')\r\n );\r\n } else if (t.kind === 'whitespace') {\r\n let source = input.substring(t.loc.start, t.loc.end);\r\n tokens.push(whitespace(t.loc, source));\r\n } else if (t.kind === 'word') {\r\n let source = input.substring(t.loc.start, t.loc.end);\r\n tokens.push(word(t.loc, source));\r\n }\r\n }\r\n\r\n return tokens;\r\n }\r\n\r\n parse(input: string): ParseTree {\r\n let root: ParseTree = {\r\n type: 'root',\r\n content: [],\r\n };\r\n\r\n const tokens = this.tokenize(input);\r\n\r\n let expectingId = 0;\r\n let expectingName = 0;\r\n let expectingTitle = 0;\r\n let expectingSectionHeading = 0;\r\n let expectingFootnote = 0;\r\n let expectingFootnoteReference = 0;\r\n let expectingFootnoteText = 0;\r\n let expectingReferenceText = 0;\r\n let expectingWordAttribute = 0;\r\n let expectingNestedWordAttribute = 0;\r\n let expectingWordsOfJesus = 0;\r\n let expectingIntroParagraph = 0;\r\n let expectingCrossReference = 0;\r\n let expectingUnknownCommand = 0;\r\n\r\n let canParseFootnotes = true;\r\n let chapter: Chapter | null = null;\r\n let lastVerse: Verse | null = null;\r\n let verse: Verse | null = null;\r\n let subtitle: HebrewSubtitle | null = null;\r\n let words: string[] = [];\r\n let verseContent: (Text | FootnoteReference | string)[] = [];\r\n let sectionContent: string = '';\r\n let currentFootnoteId = 0;\r\n let footnote: Footnote | null = null;\r\n\r\n this._poem = null;\r\n\r\n const addWordsToVerseOrSubtitle = () => {\r\n if (words.length > 0) {\r\n const text = this._text(words.join('').trimEnd());\r\n if (verse) {\r\n verse.content.push(text);\r\n } else if (subtitle) {\r\n subtitle.content.push(text);\r\n } else {\r\n verseContent.push(text);\r\n }\r\n words = [];\r\n }\r\n };\r\n\r\n const addVerseContentToChapter = (token: Token | null) => {\r\n if (!chapter) {\r\n return;\r\n }\r\n if (verseContent.length > 0) {\r\n if (chapter.content.some((c) => c.type === 'verse')) {\r\n this._throwError(\r\n input,\r\n token,\r\n 'Cannot infer first verse after other verses have been added to the chapter!'\r\n );\r\n }\r\n // Implicit first verse\r\n verse = {\r\n type: 'verse',\r\n number: 1,\r\n content: verseContent,\r\n };\r\n chapter.content.push(verse);\r\n verseContent = [];\r\n }\r\n };\r\n\r\n const cleanupVerse = () => {\r\n if (!verse || !chapter) {\r\n return;\r\n }\r\n let chapterContent: Chapter['content'] = [];\r\n for (let i = verse.content.length - 1; i >= 0; i--) {\r\n let content = verse.content[i];\r\n if (typeof content === 'object' && 'heading' in content) {\r\n // move headings that occur at the end of a verse to the chapter\r\n chapterContent.unshift({\r\n type: 'heading',\r\n content: [content.heading],\r\n });\r\n verse.content.splice(i, 1);\r\n } else if (\r\n typeof content === 'object' &&\r\n 'lineBreak' in content &&\r\n content.lineBreak\r\n ) {\r\n // move line breaks that occur at the end of a verse to the chapter\r\n chapterContent.unshift({\r\n type: 'line_break',\r\n });\r\n verse.content.splice(i, 1);\r\n } else {\r\n break;\r\n }\r\n }\r\n\r\n for (let content of chapterContent) {\r\n chapter.content.push(content);\r\n }\r\n };\r\n\r\n const completeVerseOrSubtitle = (token: Token | null) => {\r\n if (verse && isNaN(verse.number)) {\r\n // Verse is invalid for some reason.\r\n const index = chapter!.content.indexOf(verse);\r\n if (index >= 0) {\r\n chapter!.content.splice(index, 1);\r\n }\r\n verse = null;\r\n }\r\n if (verse || subtitle) {\r\n addWordsToVerseOrSubtitle();\r\n }\r\n\r\n addVerseContentToChapter(token);\r\n cleanupVerse();\r\n };\r\n\r\n const completeSection = () => {\r\n if (expectingSectionHeading > 0) {\r\n if (verse) {\r\n addWordsToVerseOrSubtitle();\r\n verse.content.push({\r\n heading: sectionContent,\r\n });\r\n } else if (chapter) {\r\n chapter.content.push({\r\n type: 'heading',\r\n content: [sectionContent],\r\n });\r\n } else {\r\n root.content.push({\r\n type: 'heading',\r\n content: [sectionContent],\r\n });\r\n }\r\n sectionContent = '';\r\n expectingSectionHeading = 0;\r\n }\r\n };\r\n\r\n const addWordsToFootnote = () => {\r\n if (footnote && words.length > 0) {\r\n footnote.text += words.join(' ');\r\n words = [];\r\n }\r\n };\r\n\r\n for (let token of tokens) {\r\n if (token.kind === 'marker') {\r\n if (token.command === '\\\\c') {\r\n addWordsToVerseOrSubtitle();\r\n cleanupVerse();\r\n\r\n chapter = {\r\n type: 'chapter',\r\n number: NaN,\r\n content: [],\r\n footnotes: [],\r\n };\r\n verse = null;\r\n verseContent = [];\r\n\r\n root.content.push(chapter);\r\n } else if (token.command === '\\\\v') {\r\n if (!chapter) {\r\n this._throwError(\r\n input,\r\n token,\r\n 'Cannot parse a verse without chapter information!'\r\n );\r\n } else {\r\n completeSection();\r\n completeVerseOrSubtitle(token);\r\n\r\n lastVerse = verse;\r\n verse = {\r\n type: 'verse',\r\n number: NaN,\r\n content: [],\r\n };\r\n\r\n chapter.content.push(verse);\r\n }\r\n } else if (token.command === '\\\\d') {\r\n if (!chapter) {\r\n this._throwError(\r\n input,\r\n token,\r\n 'Cannot parse a hebrew subtitle without chapter information!'\r\n );\r\n } else {\r\n completeVerseOrSubtitle(token);\r\n\r\n subtitle = {\r\n type: 'hebrew_subtitle',\r\n content: [],\r\n };\r\n\r\n chapter.content.push(subtitle);\r\n }\r\n } else if (token.command === '\\\\b' || token.command === '\\\\p') {\r\n if (!chapter) {\r\n this._throwError(\r\n input,\r\n token,\r\n 'Cannot parse a line break without chapter information!'\r\n );\r\n } else {\r\n if (verse) {\r\n addWordsToVerseOrSubtitle();\r\n verse.content.push({\r\n lineBreak: true,\r\n });\r\n } else {\r\n completeVerseOrSubtitle(token);\r\n chapter.content.push({\r\n type: 'line_break',\r\n });\r\n }\r\n }\r\n } else if (token.command === '\\\\q') {\r\n addWordsToVerseOrSubtitle();\r\n this._poem = token.number;\r\n } else if (token.command === '\\\\p') {\r\n addWordsToVerseOrSubtitle();\r\n this._poem = null;\r\n } else if (token.command === '\\\\id') {\r\n expectingId = 1;\r\n } else if (token.command === '\\\\h') {\r\n expectingName = 1;\r\n root.header = undefined;\r\n } else if (\r\n token.command === '\\\\mt' ||\r\n token.command === '\\\\+mt'\r\n ) {\r\n expectingTitle = 1;\r\n } else if (token.command === '\\\\s') {\r\n expectingSectionHeading = 1;\r\n } else if (token.command === '\\\\r') {\r\n expectingReferenceText = 1;\r\n } else if (token.command === '\\\\f' && canParseFootnotes) {\r\n if (token.type === 'start') {\r\n if (!chapter) {\r\n this._throwError(\r\n input,\r\n token,\r\n 'Cannot start a footnote outside of a chapter!',\r\n true\r\n );\r\n } else {\r\n addWordsToVerseOrSubtitle();\r\n footnote = {\r\n noteId: currentFootnoteId,\r\n text: '',\r\n caller: null,\r\n };\r\n const ref: FootnoteReference = {\r\n noteId: footnote.noteId,\r\n };\r\n expectingFootnote = 1;\r\n\r\n chapter.footnotes.push(footnote);\r\n\r\n if (verse) {\r\n verse.content.push(ref);\r\n } else if (subtitle) {\r\n subtitle.content.push(ref);\r\n } else {\r\n verseContent.push(ref);\r\n }\r\n\r\n currentFootnoteId += 1;\r\n }\r\n } else {\r\n addWordsToFootnote();\r\n expectingFootnote = 0;\r\n expectingFootnoteText = 0;\r\n expectingFootnoteReference = 0;\r\n footnote = null;\r\n }\r\n } else if (token.command === '\\\\fr' && canParseFootnotes) {\r\n if (!footnote) {\r\n this._throwError(\r\n input,\r\n token,\r\n 'Cannot start a footnote reference outside of a footnote!',\r\n true\r\n );\r\n } else {\r\n expectingFootnoteReference = 1;\r\n }\r\n } else if (token.command === '\\\\ft' && canParseFootnotes) {\r\n if (!footnote) {\r\n this._throwError(\r\n input,\r\n token,\r\n 'Cannot start footnote text outside of a footnote!',\r\n true\r\n );\r\n } else {\r\n expectingFootnoteText = 1;\r\n }\r\n } else if (token.command === '\\\\w') {\r\n if (token.type === 'start') {\r\n expectingWordAttribute = 1;\r\n } else {\r\n expectingWordAttribute = 0;\r\n }\r\n } else if (token.command === '\\\\+w') {\r\n if (token.type === 'start') {\r\n expectingNestedWordAttribute = 1;\r\n } else {\r\n expectingNestedWordAttribute = 0;\r\n }\r\n } else if (\r\n token.command === '\\\\wj' ||\r\n token.command === '\\\\+wj'\r\n ) {\r\n if (token.type === 'start') {\r\n addWordsToVerseOrSubtitle();\r\n this._wordsOfJesus = true;\r\n } else {\r\n addWordsToVerseOrSubtitle();\r\n this._wordsOfJesus = false;\r\n }\r\n } else if (token.command === '\\\\ip') {\r\n expectingIntroParagraph = 1;\r\n canParseFootnotes = false;\r\n } else if (token.command === '\\\\x') {\r\n if (token.type === 'start') {\r\n expectingCrossReference = 1;\r\n } else {\r\n expectingCrossReference = 0;\r\n }\r\n } else if (token.command.indexOf('-') >= 0) {\r\n if (token.type === 'start') {\r\n expectingUnknownCommand = 1;\r\n }\r\n } else if (token.type === 'end') {\r\n expectingUnknownCommand = 0;\r\n }\r\n } else if (token.kind === 'word') {\r\n if (expectingId > 0) {\r\n if (expectingId === 1) {\r\n root.id = token.word;\r\n expectingId = 2;\r\n }\r\n } else if (expectingName > 0) {\r\n if (root.header) {\r\n root.header += ' ' + token.word;\r\n } else {\r\n root.header = token.word;\r\n }\r\n } else if (expectingTitle > 0) {\r\n if (root.title) {\r\n root.title += ' ' + token.word;\r\n } else {\r\n root.title = token.word;\r\n }\r\n } else if (expectingSectionHeading > 0) {\r\n if (sectionContent) {\r\n sectionContent += ' ' + token.word;\r\n } else {\r\n sectionContent = token.word;\r\n }\r\n } else if (expectingFootnoteReference > 0) {\r\n if ((expectingFootnoteReference = 1)) {\r\n const [chapter, verse] = token.word.split(/[\\.\\:]/);\r\n\r\n if (footnote) {\r\n footnote.reference = {\r\n chapter: parseInt(chapter),\r\n verse: parseInt(verse),\r\n };\r\n }\r\n\r\n expectingFootnoteReference = 0;\r\n }\r\n } else if (expectingFootnoteText > 0) {\r\n words.push(token.word);\r\n } else if (expectingFootnote > 0) {\r\n if (expectingFootnote === 1) {\r\n if (token.word) {\r\n if (footnote) {\r\n if (token.word === '+' || token.word !== '-') {\r\n footnote.caller = token.word;\r\n } else {\r\n footnote.caller = null;\r\n }\r\n }\r\n // this._throwError(input, token, 'Footnotes must use the \"+\" caller.');\r\n }\r\n expectingFootnote = 2;\r\n } else {\r\n words.push(token.word);\r\n }\r\n } else if (expectingReferenceText > 0) {\r\n // Skip processing words for references\r\n // because references aren't included in the JSON format\r\n // (for now)\r\n } else if (expectingCrossReference > 0) {\r\n // Skip processing words for cross references\r\n } else if (chapter && isNaN(chapter.number)) {\r\n chapter.number = parseInt(token.word);\r\n if (isNaN(chapter.number)) {\r\n this._throwError(\r\n input,\r\n token,\r\n 'The first word token after a chapter marker must be parsable to an integer!'\r\n );\r\n }\r\n } else if (verse && isNaN(verse.number)) {\r\n verse.number = parseInt(token.word);\r\n if (isNaN(verse.number)) {\r\n this._throwError(\r\n input,\r\n token,\r\n 'The first word token after a verse marker must be parsable to an integer!'\r\n );\r\n }\r\n } else if (expectingWordAttribute > 0) {\r\n if (expectingWordAttribute === 1) {\r\n const firstVerticalBarIndex = token.word.indexOf('|');\r\n\r\n if (firstVerticalBarIndex >= 0) {\r\n const name = token.word.slice(\r\n 0,\r\n firstVerticalBarIndex\r\n );\r\n // const rest = token.word.slice(firstVerticalBarIndex + '|'.length);\r\n words.push(name);\r\n expectingWordAttribute = 2;\r\n } else {\r\n words.push(token.word);\r\n }\r\n }\r\n } else if (expectingNestedWordAttribute > 0) {\r\n if (expectingNestedWordAttribute === 1) {\r\n const firstVerticalBarIndex = token.word.indexOf('|');\r\n\r\n if (firstVerticalBarIndex >= 0) {\r\n const name = token.word.slice(\r\n 0,\r\n firstVerticalBarIndex\r\n );\r\n // const rest = token.word.slice(firstVerticalBarIndex + '|'.length);\r\n words.push(name);\r\n expectingNestedWordAttribute = 2;\r\n } else {\r\n words.push(token.word);\r\n }\r\n }\r\n } else if (expectingUnknownCommand > 0) {\r\n } else if (expectingIntroParagraph > 0) {\r\n // Skip processing words for intro paragraphs\r\n } else {\r\n words.push(token.word);\r\n }\r\n } else if (token.kind === 'whitespace') {\r\n if (expectingId > 0) {\r\n if (token.whitespace.includes('\\n')) {\r\n expectingId = 0;\r\n }\r\n } else if (expectingName > 0) {\r\n if (token.whitespace.includes('\\n')) {\r\n expectingName = 0;\r\n }\r\n } else if (expectingTitle > 0) {\r\n if (token.whitespace.includes('\\n')) {\r\n expectingTitle = 0;\r\n }\r\n } else if (expectingSectionHeading > 0) {\r\n if (token.whitespace.includes('\\n')) {\r\n completeSection();\r\n }\r\n } else if (expectingReferenceText > 0) {\r\n if (token.whitespace.includes('\\n')) {\r\n expectingReferenceText = 0;\r\n }\r\n } else if (expectingIntroParagraph > 0) {\r\n if (token.whitespace.includes('\\n')) {\r\n expectingIntroParagraph = 0;\r\n canParseFootnotes = true;\r\n }\r\n } else if (\r\n expectingId > 0 ||\r\n expectingName > 0 ||\r\n expectingTitle > 0 ||\r\n expectingSectionHeading > 0 ||\r\n expectingFootnote > 0 ||\r\n expectingFootnoteReference > 0 ||\r\n expectingFootnoteText > 0 ||\r\n expectingReferenceText > 0 ||\r\n expectingCrossReference > 0 ||\r\n expectingWordAttribute > 0 ||\r\n expectingNestedWordAttribute > 0\r\n ) {\r\n // Skip\r\n } else if (expectingUnknownCommand > 0) {\r\n if (token.whitespace.includes('\\n')) {\r\n expectingUnknownCommand = 0;\r\n }\r\n } else if (words.length > 0) {\r\n let lastWord = words[words.length - 1];\r\n if (lastWord !== ' ') {\r\n words.push(' ');\r\n }\r\n }\r\n }\r\n }\r\n\r\n completeVerseOrSubtitle(null);\r\n\r\n return root;\r\n }\r\n\r\n renderMarkdown(tree: ParseTree): string {\r\n let md = '';\r\n\r\n if (tree.header) {\r\n md += `# ${tree.header}\\n`;\r\n }\r\n\r\n for (let c of tree.content) {\r\n if (c.type === 'heading') {\r\n md += `## ${c.content.join(' ')}\\n`;\r\n } else if (c.type === 'chapter') {\r\n md += `### ${c.number}\\n`;\r\n\r\n for (let content of c.content) {\r\n if (content.type === 'heading') {\r\n md += `#### ${content.content.join(' ')}\\n`;\r\n } else if (content.type === 'line_break') {\r\n md += '\\n\\n';\r\n } else if (content.type === 'verse') {\r\n md += `<em>${content.number}</em>`;\r\n for (let v of content.content) {\r\n if (typeof v === 'string') {\r\n md += v + ' ';\r\n } else if ('text' in v) {\r\n md += v.text + ' ';\r\n }\r\n }\r\n md += '\\n';\r\n }\r\n }\r\n }\r\n }\r\n\r\n return md;\r\n }\r\n\r\n private _hasAttribute() {\r\n return this._poem !== null || this._wordsOfJesus;\r\n }\r\n\r\n private _text(text: string): Text | string {\r\n if (!this._hasAttribute()) {\r\n return text;\r\n }\r\n const t: Text = {\r\n text,\r\n };\r\n\r\n if (this._poem !== null) {\r\n t.poem = this._poem;\r\n }\r\n\r\n if (this._wordsOfJesus) {\r\n t.wordsOfJesus = true;\r\n }\r\n\r\n return t;\r\n }\r\n\r\n private _throwError(\r\n source: string,\r\n token: Token | null,\r\n message: string,\r\n warn: boolean = false\r\n ): void {\r\n const logger = getLogger();\r\n if (token) {\r\n let line = 1;\r\n let column = 1;\r\n\r\n let start = token.loc.start;\r\n\r\n for (let i = 0; i < start; i++) {\r\n let char = source[i];\r\n if (char === '\\n') {\r\n line += 1;\r\n column = 1;\r\n } else {\r\n column += 1;\r\n }\r\n }\r\n\r\n let tokenDebug = '';\r\n if (token.kind === 'word') {\r\n tokenDebug = ', word';\r\n } else if (token.kind === 'marker') {\r\n tokenDebug = ', ' + token.command;\r\n } else {\r\n tokenDebug = '';\r\n }\r\n\r\n if (warn) {\r\n logger.warn(`(${line}, ${column}${tokenDebug}) ${message}`);\r\n } else {\r\n throw new Error(`(${line}, ${column}${tokenDebug}) ${message}`);\r\n }\r\n } else {\r\n if (warn) {\r\n logger.warn(message);\r\n } else {\r\n throw new Error(message);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Determines if the given character is a digit.\r\n * @param char The character.\r\n */\r\nexport function isDigit(char: string): boolean {\r\n return char.length === 1 && char >= '0' && char <= '9';\r\n}\r\n\r\n/**\r\n * Determines if the given character is considered whitespace.\r\n * @param char The character.\r\n */\r\nexport function isWhitespace(char: string): boolean {\r\n return char === ' ' || char === '\\t' || char === '\\n' || char === '\\r';\r\n}\r\n\r\nexport function t(loc: SourceLocation, kind: T): SimpleToken {\r\n return {\r\n loc,\r\n kind,\r\n };\r\n}\r\n\r\n/**\r\n * Creates a new source location.\r\n * @param start The start of the location.\r\n * @param end The end of the location.\r\n */\r\nexport function loc(start: number, end: number): SourceLocation {\r\n return {\r\n start,\r\n end,\r\n };\r\n}\r\n\r\n/**\r\n * Creates a new marker token.\r\n * @param loc The location for the token.\r\n * @param command The command that the token contains.\r\n * @param number The number that the marker contains.\r\n * @param type The type of the marker.\r\n */\r\nexport function marker(\r\n loc: SourceLocation,\r\n command: string,\r\n number: number | null = null,\r\n type: MarkerToken['type'] = 'start'\r\n): MarkerToken {\r\n return {\r\n kind: 'marker',\r\n loc,\r\n command,\r\n number,\r\n type,\r\n };\r\n}\r\n\r\n/**\r\n * Creates a new word token.\r\n * @param loc The location for the token.\r\n * @param word The word contained by the token.\r\n */\r\nexport function word(loc: SourceLocation, word: string): WordToken {\r\n return {\r\n kind: 'word',\r\n loc,\r\n word,\r\n };\r\n}\r\n\r\nexport function whitespace(\r\n loc: SourceLocation,\r\n whitespace: string\r\n): WhitespaceToken {\r\n return {\r\n kind: 'whitespace',\r\n loc,\r\n whitespace,\r\n };\r\n}\r\n\r\nexport type T = Token['kind'];\r\n\r\n/**\r\n * Defines a simple token.\r\n */\r\nexport interface SimpleToken {\r\n kind: T;\r\n loc: SourceLocation;\r\n}\r\n\r\nexport type Token = MarkerToken | WordToken | WhitespaceToken;\r\n\r\n/**\r\n * Defines an interface for a USFM marker node.\r\n * That is, the syntax for a marker.\r\n */\r\nexport interface MarkerToken {\r\n kind: 'marker';\r\n\r\n /**\r\n * The command that the marker represents.\r\n * This is generally the name of the marker (like \"p\" or \"v\" for paragraph and verse markers)\r\n */\r\n command: string;\r\n\r\n /**\r\n * The number that the marker contains.\r\n * Null if no number was specified.\r\n */\r\n number: number | null;\r\n\r\n /**\r\n * Whether the marker represents a start or an end.\r\n */\r\n type: 'start' | 'end';\r\n\r\n /**\r\n * The location of the node.\r\n */\r\n loc: SourceLocation;\r\n}\r\n\r\n/**\r\n * Defines an interface for a Whitespace node.\r\n */\r\nexport interface WhitespaceToken {\r\n kind: 'whitespace';\r\n\r\n /**\r\n * The whitespace contained by the node.\r\n */\r\n whitespace: string;\r\n\r\n /**\r\n * The location of the node.\r\n */\r\n loc: SourceLocation;\r\n}\r\n\r\n/**\r\n * Defines an interface for a word token.\r\n */\r\nexport interface WordToken {\r\n kind: 'word';\r\n\r\n /**\r\n * The word contained by the token.\r\n */\r\n word: string;\r\n\r\n /**\r\n * The location of the word.\r\n */\r\n loc: SourceLocation;\r\n}\r\n\r\n/**\r\n * The source location of the node.\r\n */\r\nexport interface SourceLocation {\r\n start: number;\r\n end: number;\r\n}\r\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["t", "chapter", "verse", "loc", "word", "whitespace"]
|
|
7
7
|
}
|
|
@@ -28,7 +28,7 @@ var NodeType = /* @__PURE__ */ ((NodeType2) => {
|
|
|
28
28
|
NodeType2[NodeType2["Text"] = 3] = "Text";
|
|
29
29
|
return NodeType2;
|
|
30
30
|
})(NodeType || {});
|
|
31
|
-
const PARSER_VERSION =
|
|
31
|
+
const PARSER_VERSION = 3;
|
|
32
32
|
class USXParser {
|
|
33
33
|
_domParser;
|
|
34
34
|
_noteCounter = 0;
|
|
@@ -159,7 +159,7 @@ class USXParser {
|
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
|
-
*iterateVerseContent(chapter, verse, nodes) {
|
|
162
|
+
*iterateVerseContent(chapter, verse, nodes, previousVerse) {
|
|
163
163
|
let lastParent = null;
|
|
164
164
|
while (true) {
|
|
165
165
|
const { done, value: node } = nodes.next();
|
|
@@ -185,9 +185,20 @@ class USXParser {
|
|
|
185
185
|
);
|
|
186
186
|
if (previousStyle === style && lastParent !== parent) {
|
|
187
187
|
lastParent = parent;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
const firstChild = parent.firstElementChild;
|
|
189
|
+
const startsWithVerse = firstChild?.nodeName === "verse" && !firstChild.hasAttribute("eid") && parseInt(
|
|
190
|
+
firstChild.getAttribute("number") || "0",
|
|
191
|
+
10
|
|
192
|
+
) === verse.number;
|
|
193
|
+
if (startsWithVerse && previousVerse && previousVerse.number !== verse.number) {
|
|
194
|
+
addOrJoin(previousVerse.content, {
|
|
195
|
+
lineBreak: true
|
|
196
|
+
});
|
|
197
|
+
} else {
|
|
198
|
+
yield {
|
|
199
|
+
lineBreak: true
|
|
200
|
+
};
|
|
201
|
+
}
|
|
191
202
|
}
|
|
192
203
|
}
|
|
193
204
|
} else if (style === "d") {
|
|
@@ -260,11 +271,17 @@ class USXParser {
|
|
|
260
271
|
}
|
|
261
272
|
}
|
|
262
273
|
}
|
|
263
|
-
this._lastVerse
|
|
264
|
-
for (let content of this.iterateVerseContent(
|
|
274
|
+
const previousVerse = this._currentChapter?.number === chapter.number ? this._lastVerse : null;
|
|
275
|
+
for (let content of this.iterateVerseContent(
|
|
276
|
+
chapter,
|
|
277
|
+
verse,
|
|
278
|
+
nodes,
|
|
279
|
+
previousVerse
|
|
280
|
+
)) {
|
|
265
281
|
addOrJoin(verse.content, content);
|
|
266
282
|
}
|
|
267
283
|
trimContent(verse.content);
|
|
284
|
+
this._lastVerse = verse;
|
|
268
285
|
return verse;
|
|
269
286
|
}
|
|
270
287
|
*parseHebrewSubtitle(para, chapter, nodes) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../parser/usx-parser.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n Chapter,\n ChapterContent,\n Footnote,\n FootnoteReference,\n ParseTree,\n Verse,\n Text,\n HebrewSubtitle,\n InlineLineBreak,\n InlineHeading,\n ParseMessage,\n} from './types.js';\nimport {\n iterateAll,\n children,\n RewindableIterator,\n isParent,\n} from './iterators.js';\nimport { KNOWN_SKIPPED_VERSES } from '../utils.js';\n\nenum NodeType {\n Text = 3,\n}\n\n/**\n * The version of the parser.\n * Used to determine whether input files need to be re-parsed.\n */\nexport const PARSER_VERSION = '2';\n\n/**\n * Defines a class that is able to parse USX content.\n */\nexport class USXParser {\n private _domParser: DOMParser;\n private _noteCounter: number = 0;\n private _messages: ParseMessage[] = [];\n private _lastVerse: Verse | null = null;\n private _currentChapter: Chapter | null = null;\n private _bookId: string | null = null;\n\n constructor(domParser: DOMParser) {\n this._domParser = domParser;\n }\n\n /**\n * Parses the specified USX content.\n *\n * @param usx The USX content to parse.\n * @returns The parse tree that was generated.\n */\n public parse(usx: string): ParseTree {\n this._noteCounter = 0;\n this._messages = [];\n this._lastVerse = null;\n this._currentChapter = null;\n this._bookId = null;\n const parser = this._domParser;\n const doc = parser.parseFromString(usx, 'application/xml');\n const usxElement = doc.documentElement;\n\n let root: ParseTree = {\n type: 'root',\n content: [],\n };\n\n const bookElement = usxElement.querySelector('book[code]');\n\n if (!bookElement) {\n throw new Error('The USX content does not contain a book element.');\n }\n\n const bookCode = bookElement.getAttribute('code') || '';\n\n if (!bookCode) {\n throw new Error(\n 'The book element does not contain a code attribute.'\n );\n }\n\n this._bookId = root.id = bookCode;\n\n const header = usxElement.querySelector('para[style=\"h\"]');\n if (header) {\n root.header = header.textContent || '';\n }\n\n const titles = usxElement.querySelectorAll(\n 'para[style=\"mt1\"], para[style=\"mt2\"], para[style=\"mt3\"]'\n );\n // const title2 = usxElement.querySelector('para[style=\"mt2\"]');\n // const title3 = usxElement.querySelector('para[style=\"mt3\"]');\n\n if (titles.length > 0) {\n root.title = [...titles]\n .map((t) => t.textContent)\n .filter((t) => t)\n .join(' ');\n }\n\n for (let content of this.iterateRootContent(usxElement)) {\n root.content.push(content);\n }\n\n if (this._messages.length > 0) {\n root.parseMessages = this._messages.slice();\n }\n\n return root;\n }\n\n *iterateRootContent(\n usxElement: Element\n ): Generator<ParseTree['content'][0]> {\n const iterator = iterateAll(usxElement);\n while (true) {\n const { done, value: child } = iterator.next();\n if (done) {\n break;\n }\n\n if (!(child instanceof Element)) {\n continue;\n }\n\n if (child.nodeName === 'chapter') {\n if (child.hasAttribute('eid')) {\n continue;\n }\n\n const chapter: Chapter = {\n type: 'chapter',\n number: parseInt(child.getAttribute('number') || '0', 10),\n content: [],\n footnotes: [],\n };\n this._currentChapter = chapter;\n\n for (let content of this.iterateChapterContent(\n chapter,\n iterator\n )) {\n chapter.content.push(content);\n }\n\n yield chapter;\n } else if (child.nodeName === 'para') {\n const style = child.getAttribute('style');\n if (\n style === 's1' ||\n style === 's2' ||\n style === 's3' ||\n style === 's4'\n ) {\n yield {\n type: 'heading',\n content: child.textContent ? [child.textContent] : [],\n };\n }\n }\n }\n }\n\n *iterateChapterContent(\n chapter: Chapter,\n nodes: RewindableIterator<Node>\n ): IterableIterator<ChapterContent> {\n while (true) {\n const { done, value: element } = nodes.next();\n if (done) {\n break;\n }\n\n if (!(element instanceof Element)) {\n continue;\n }\n\n if (element.nodeName === 'chapter') {\n break;\n } else if (element.nodeName === 'para') {\n const style = element.getAttribute('style');\n if (\n style === 's1' ||\n style === 's2' ||\n style === 's3' ||\n style === 's4'\n ) {\n yield {\n type: 'heading',\n content: element.textContent\n ? [element.textContent]\n : [],\n };\n } else if (style === 'b') {\n yield {\n type: 'line_break',\n };\n } else if (style === 'd') {\n yield* this.parseHebrewSubtitle(element, chapter, nodes);\n }\n } else if (element.nodeName === 'verse') {\n if (element.hasAttribute('eid')) {\n continue;\n }\n\n yield this.parseVerse(element, chapter, nodes);\n }\n }\n }\n\n *iterateVerseContent(\n chapter: Chapter,\n verse: Verse,\n nodes: RewindableIterator<Node>\n ): IterableIterator<string | FootnoteReference | Text | InlineLineBreak> {\n let lastParent: Element | null = null;\n while (true) {\n const { done, value: node } = nodes.next();\n if (done) {\n break;\n }\n\n if (node.nodeName === 'verse') {\n if (!(node instanceof Element) || !node.hasAttribute('eid')) {\n nodes.rewind(1);\n }\n break;\n }\n\n const parent = node.parentElement!;\n let poem: number | null = null;\n let descriptive: boolean | null = null;\n\n if (parent.nodeName === 'para') {\n const style = parent.getAttribute('style');\n if (\n style === 'q1' ||\n style === 'q2' ||\n style === 'q3' ||\n style === 'q4'\n ) {\n poem =\n style === 'q1'\n ? 1\n : style === 'q2'\n ? 2\n : style === 'q3'\n ? 3\n : 4;\n\n // Send explicit line breaks\n // if we are in a new paragraph but the previous paragraph had the same poem style\n if (parent.previousElementSibling?.nodeName === 'para') {\n const previousStyle =\n parent.previousElementSibling?.getAttribute(\n 'style'\n );\n if (previousStyle === style && lastParent !== parent) {\n lastParent = parent;\n yield {\n lineBreak: true,\n };\n }\n }\n } else if (style === 'd') {\n descriptive = true;\n }\n }\n\n for (let content of this.iterateNodeTextContent(\n nodes,\n node,\n chapter,\n verse\n )) {\n if (poem !== null || descriptive !== null) {\n if (typeof content === 'string') {\n let text: Text = {\n text: content,\n };\n\n if (poem !== null) {\n text.poem = poem;\n }\n\n if (descriptive !== null) {\n text.descriptive = true;\n }\n\n yield text;\n } else {\n let text:\n | InlineLineBreak\n | InlineHeading\n | FootnoteReference\n | Text = {\n ...content,\n };\n\n if ('text' in text) {\n if (poem !== null) {\n text.poem = poem;\n }\n\n if (descriptive !== null) {\n text.descriptive = true;\n }\n }\n yield text;\n }\n } else {\n yield content;\n }\n }\n }\n }\n\n parseVerse(\n element: Element,\n chapter: Chapter,\n nodes: RewindableIterator<Node>\n ): Verse {\n const verse: Verse = {\n type: 'verse',\n number: parseInt(element.getAttribute('number') || '0', 10),\n content: [],\n };\n\n if (this._currentChapter && this._lastVerse) {\n if (this._currentChapter.number === chapter.number) {\n // We're parsing the same chapter as the last one\n const delta = verse.number - this._lastVerse.number;\n if (delta > 1) {\n // Delta is greater than 1, so we might have missed some verses\n for (\n let v = this._lastVerse.number + 1;\n v < verse.number;\n v++\n ) {\n let isExpected = false;\n if (this._bookId) {\n const missingVerse = `${this._bookId} ${chapter.number}:${v}`;\n if (KNOWN_SKIPPED_VERSES.has(missingVerse)) {\n isExpected = true;\n }\n }\n\n if (!isExpected) {\n this._messages.push({\n type: 'warning',\n message: `Verse ${this._bookId ?? '(null)'} ${chapter.number}:${v} is missing.`,\n });\n }\n }\n }\n }\n }\n this._lastVerse = verse;\n\n for (let content of this.iterateVerseContent(chapter, verse, nodes)) {\n addOrJoin(verse.content, content);\n }\n\n trimContent(verse.content);\n return verse;\n }\n\n *parseHebrewSubtitle(\n para: Element,\n chapter: Chapter,\n nodes: RewindableIterator<Node>\n ): IterableIterator<HebrewSubtitle | Verse> {\n const subtitle: HebrewSubtitle = {\n type: 'hebrew_subtitle',\n content: [],\n };\n\n for (let content of this.iterateHebrewSubtitleContent(\n para,\n chapter,\n nodes\n )) {\n if (typeof content === 'object' && 'number' in content) {\n yield content;\n continue;\n }\n addOrJoin(subtitle.content, content);\n }\n\n trimContent(subtitle.content);\n if (subtitle.content.length > 0) {\n yield subtitle;\n }\n }\n\n *iterateHebrewSubtitleContent(\n element: Element,\n chapter: Chapter,\n nodes: RewindableIterator<Node>\n ): IterableIterator<\n Verse | string | Text | FootnoteReference | InlineLineBreak\n > {\n while (true) {\n const { done, value: node } = nodes.next();\n if (done) {\n break;\n }\n\n if (!isParent(node, element)) {\n nodes.rewind(1);\n break;\n }\n\n if (node instanceof Element && node.nodeName === 'verse') {\n yield this.parseVerse(node, chapter, nodes);\n } else {\n yield* this.iterateNodeTextContent(nodes, node, chapter);\n }\n }\n }\n\n *iterateNodeTextContent(\n nodes: RewindableIterator<Node>,\n node: Node,\n chapter: Chapter,\n verse?: Verse\n ): IterableIterator<string | Text | FootnoteReference | InlineLineBreak> {\n if (node instanceof Element && node.nodeName === 'note') {\n yield* this.iterateNote(nodes, node, chapter, verse);\n } else if (node instanceof Element && node.nodeName === 'char') {\n yield* this.iterateChar(nodes, node);\n } else if (\n node instanceof Element &&\n node.nodeName === 'para' &&\n node.getAttribute('style') === 'b'\n ) {\n for (let _ of children(nodes, node)) {\n // iterate through all the children to prevent iterating over them multiple times\n }\n yield {\n lineBreak: true,\n };\n } else if (node.nodeType === NodeType.Text) {\n yield node.textContent || '';\n }\n }\n\n *iterateCharContent(char: Element): IterableIterator<string | Text> {\n const style = char.getAttribute('style');\n const text = trimText(char.textContent || '');\n if (style === 'wj') {\n yield {\n text,\n wordsOfJesus: true,\n };\n } else {\n yield text;\n }\n }\n\n *iterateNote(\n nodes: RewindableIterator<Node>,\n node: Element,\n chapter: Chapter,\n verse?: Verse\n ): IterableIterator<FootnoteReference> {\n const style = node.getAttribute('style');\n if (style === 'f') {\n const verseReferenceRegex = /^[0-9]{1,3}:[0-9]{1,3}/;\n\n let text = '';\n for (let child of children(nodes, node)) {\n if (child.nodeType === NodeType.Text) {\n text += child.textContent || '';\n }\n }\n\n text = text.trim();\n if (verseReferenceRegex.test(text)) {\n text = text.replace(verseReferenceRegex, '').trim();\n }\n\n const note: Footnote = {\n noteId: this._noteCounter++,\n caller: node.getAttribute('caller') || null,\n text,\n reference: {\n chapter: chapter.number,\n verse: verse?.number ?? 0,\n },\n };\n\n chapter.footnotes.push(note);\n\n yield {\n noteId: note.noteId,\n };\n } else {\n for (let _ of children(nodes, node)) {\n // iterate through all the children\n // so that we don't end up with duplicates\n }\n }\n }\n\n *iterateChar(\n nodes: RewindableIterator<Node>,\n node: Element\n ): IterableIterator<string | Text> {\n const style = node.getAttribute('style');\n let text = '';\n\n for (let char of children(nodes, node)) {\n if (char.nodeType === NodeType.Text) {\n text += char.textContent || '';\n }\n }\n\n if (style === 'wj') {\n yield {\n text,\n wordsOfJesus: true,\n };\n } else {\n yield text;\n }\n\n // if (!parentChar(node) && !parentNote(node)) {\n // yield *iterateCharContent(node);\n // }\n }\n}\n\n// Taken from https://github.com/gracious-tech/fetch/blob/1576cc4eafb32bf347a09332094cf17c2231c90c/converters/usx-to-json/src/elements.ts#L16\nconst ignoredParaStyles = new Set([\n // <para> Identification [exclude all] - Running headings & table of contents\n 'ide', // See https://github.com/schierlm/BibleMultiConverter/issues/67\n 'rem', // Remarks (valid in schema though missed in docs)\n 'h',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'toc1',\n 'toc2',\n 'toc3',\n 'toca1',\n 'toca2',\n 'toca3',\n\n /* <para> Introductions [exclude all] - Introductionary (non-biblical) content\n Which might be helpful in a printed book, but intro material in apps is usually bad UX,\n and users that really care can research a translations methodology themselves\n */\n 'imt',\n 'imt1',\n 'imt2',\n 'imt3',\n 'imt4',\n 'is',\n 'is1',\n 'is2',\n 'is3',\n 'is4',\n 'ip',\n 'ipi',\n 'im',\n 'imi',\n 'ipq',\n 'imq',\n 'ipr',\n 'iq',\n 'iq1',\n 'iq2',\n 'iq3',\n 'iq4',\n 'ib',\n 'ili',\n 'ili1',\n 'ili2',\n 'ili3',\n 'ili4',\n 'iot',\n 'io',\n 'io1',\n 'io2',\n 'io3',\n 'io4',\n 'iex',\n 'imte',\n 'ie',\n\n /* <para> Headings [exclude some] - Exclude book & chapter headings but keep section headings\n Not excluded: ms# | mr | s# | sr | d | sp | sd#\n */\n 'mt',\n 'mt1',\n 'mt2',\n 'mt3',\n 'mt4',\n 'mte',\n 'mte1',\n 'mte2',\n 'mte3',\n 'mte4',\n 'cl',\n 'cd', // Non-biblical chapter summary, more than heading\n 'r', // Parallels to be provided by external data\n]);\n\nfunction* iterateCharContent(char: Element): IterableIterator<string | Text> {\n const style = char.getAttribute('style');\n const text = trimText(char.textContent || '');\n if (style === 'wj') {\n yield {\n text,\n wordsOfJesus: true,\n };\n } else {\n yield text;\n }\n}\n\nfunction trimText(text: string): string {\n return text.replace(/\\s+/g, ' ');\n}\n\nfunction trimContent<T extends string | unknown>(content: T[]): T[] {\n for (let i = 0; i < content.length; i++) {\n const value = content[i];\n if (typeof value === 'string') {\n content[i] = trimText(value as string).trim() as T;\n if (content[i] === '') {\n content.splice(i, 1);\n i--;\n continue;\n }\n } else if (isVerseText(value)) {\n value.text = trimText(value.text).trim();\n if (value.text === '') {\n content.splice(i, 1);\n i--;\n continue;\n }\n }\n }\n return content;\n}\n\nfunction addOrJoin(array: (string | unknown)[], value: string | unknown) {\n if (array.length === 0) {\n array.push(value);\n } else {\n const last = array[array.length - 1];\n if (typeof last === 'string' && typeof value === 'string') {\n array[array.length - 1] = last + value;\n } else if (\n isVerseText(last) &&\n isVerseText(value) &&\n hasSameFormatting(last, value)\n ) {\n last.text += value.text;\n } else {\n array.push(value);\n }\n }\n}\n\nfunction isVerseText(value: unknown): value is Text {\n return typeof value === 'object' && value !== null && 'text' in value;\n}\n\nfunction hasSameFormatting(a: Text, b: Text): boolean {\n return a.poem === b.poem && a.wordsOfJesus === b.wordsOfJesus;\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import {\r\n Chapter,\r\n ChapterContent,\r\n Footnote,\r\n FootnoteReference,\r\n ParseTree,\r\n Verse,\r\n Text,\r\n HebrewSubtitle,\r\n InlineLineBreak,\r\n InlineHeading,\r\n ParseMessage,\r\n} from './types.js';\r\nimport {\r\n iterateAll,\r\n children,\r\n RewindableIterator,\r\n isParent,\r\n} from './iterators.js';\r\nimport { KNOWN_SKIPPED_VERSES } from '../utils.js';\r\n\r\nenum NodeType {\r\n Text = 3,\r\n}\r\n\r\n/**\r\n * The version of the parser.\r\n * Used to determine whether input files need to be re-parsed.\r\n */\r\nexport const PARSER_VERSION = 3;\r\n\r\n/**\r\n * Defines a class that is able to parse USX content.\r\n */\r\nexport class USXParser {\r\n private _domParser: DOMParser;\r\n private _noteCounter: number = 0;\r\n private _messages: ParseMessage[] = [];\r\n private _lastVerse: Verse | null = null;\r\n private _currentChapter: Chapter | null = null;\r\n private _bookId: string | null = null;\r\n\r\n constructor(domParser: DOMParser) {\r\n this._domParser = domParser;\r\n }\r\n\r\n /**\r\n * Parses the specified USX content.\r\n *\r\n * @param usx The USX content to parse.\r\n * @returns The parse tree that was generated.\r\n */\r\n public parse(usx: string): ParseTree {\r\n this._noteCounter = 0;\r\n this._messages = [];\r\n this._lastVerse = null;\r\n this._currentChapter = null;\r\n this._bookId = null;\r\n const parser = this._domParser;\r\n const doc = parser.parseFromString(usx, 'application/xml');\r\n const usxElement = doc.documentElement;\r\n\r\n let root: ParseTree = {\r\n type: 'root',\r\n content: [],\r\n };\r\n\r\n const bookElement = usxElement.querySelector('book[code]');\r\n\r\n if (!bookElement) {\r\n throw new Error('The USX content does not contain a book element.');\r\n }\r\n\r\n const bookCode = bookElement.getAttribute('code') || '';\r\n\r\n if (!bookCode) {\r\n throw new Error(\r\n 'The book element does not contain a code attribute.'\r\n );\r\n }\r\n\r\n this._bookId = root.id = bookCode;\r\n\r\n const header = usxElement.querySelector('para[style=\"h\"]');\r\n if (header) {\r\n root.header = header.textContent || '';\r\n }\r\n\r\n const titles = usxElement.querySelectorAll(\r\n 'para[style=\"mt1\"], para[style=\"mt2\"], para[style=\"mt3\"]'\r\n );\r\n // const title2 = usxElement.querySelector('para[style=\"mt2\"]');\r\n // const title3 = usxElement.querySelector('para[style=\"mt3\"]');\r\n\r\n if (titles.length > 0) {\r\n root.title = [...titles]\r\n .map((t) => t.textContent)\r\n .filter((t) => t)\r\n .join(' ');\r\n }\r\n\r\n for (let content of this.iterateRootContent(usxElement)) {\r\n root.content.push(content);\r\n }\r\n\r\n if (this._messages.length > 0) {\r\n root.parseMessages = this._messages.slice();\r\n }\r\n\r\n return root;\r\n }\r\n\r\n *iterateRootContent(\r\n usxElement: Element\r\n ): Generator<ParseTree['content'][0]> {\r\n const iterator = iterateAll(usxElement);\r\n while (true) {\r\n const { done, value: child } = iterator.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (!(child instanceof Element)) {\r\n continue;\r\n }\r\n\r\n if (child.nodeName === 'chapter') {\r\n if (child.hasAttribute('eid')) {\r\n continue;\r\n }\r\n\r\n const chapter: Chapter = {\r\n type: 'chapter',\r\n number: parseInt(child.getAttribute('number') || '0', 10),\r\n content: [],\r\n footnotes: [],\r\n };\r\n this._currentChapter = chapter;\r\n\r\n for (let content of this.iterateChapterContent(\r\n chapter,\r\n iterator\r\n )) {\r\n chapter.content.push(content);\r\n }\r\n\r\n yield chapter;\r\n } else if (child.nodeName === 'para') {\r\n const style = child.getAttribute('style');\r\n if (\r\n style === 's1' ||\r\n style === 's2' ||\r\n style === 's3' ||\r\n style === 's4'\r\n ) {\r\n yield {\r\n type: 'heading',\r\n content: child.textContent ? [child.textContent] : [],\r\n };\r\n }\r\n }\r\n }\r\n }\r\n\r\n *iterateChapterContent(\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): IterableIterator<ChapterContent> {\r\n while (true) {\r\n const { done, value: element } = nodes.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (!(element instanceof Element)) {\r\n continue;\r\n }\r\n\r\n if (element.nodeName === 'chapter') {\r\n break;\r\n } else if (element.nodeName === 'para') {\r\n const style = element.getAttribute('style');\r\n if (\r\n style === 's1' ||\r\n style === 's2' ||\r\n style === 's3' ||\r\n style === 's4'\r\n ) {\r\n yield {\r\n type: 'heading',\r\n content: element.textContent\r\n ? [element.textContent]\r\n : [],\r\n };\r\n } else if (style === 'b') {\r\n yield {\r\n type: 'line_break',\r\n };\r\n } else if (style === 'd') {\r\n yield* this.parseHebrewSubtitle(element, chapter, nodes);\r\n }\r\n } else if (element.nodeName === 'verse') {\r\n if (element.hasAttribute('eid')) {\r\n continue;\r\n }\r\n\r\n yield this.parseVerse(element, chapter, nodes);\r\n }\r\n }\r\n }\r\n\r\n *iterateVerseContent(\r\n chapter: Chapter,\r\n verse: Verse,\r\n nodes: RewindableIterator<Node>,\r\n previousVerse?: Verse | null\r\n ): IterableIterator<string | FootnoteReference | Text | InlineLineBreak> {\r\n let lastParent: Element | null = null;\r\n while (true) {\r\n const { done, value: node } = nodes.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (node.nodeName === 'verse') {\r\n if (!(node instanceof Element) || !node.hasAttribute('eid')) {\r\n nodes.rewind(1);\r\n }\r\n break;\r\n }\r\n\r\n const parent = node.parentElement!;\r\n let poem: number | null = null;\r\n let descriptive: boolean | null = null;\r\n\r\n if (parent.nodeName === 'para') {\r\n const style = parent.getAttribute('style');\r\n if (\r\n style === 'q1' ||\r\n style === 'q2' ||\r\n style === 'q3' ||\r\n style === 'q4'\r\n ) {\r\n poem =\r\n style === 'q1'\r\n ? 1\r\n : style === 'q2'\r\n ? 2\r\n : style === 'q3'\r\n ? 3\r\n : 4;\r\n\r\n // Send explicit line breaks\r\n // if we are in a new paragraph but the previous paragraph had the same poem style\r\n if (parent.previousElementSibling?.nodeName === 'para') {\r\n const previousStyle =\r\n parent.previousElementSibling?.getAttribute(\r\n 'style'\r\n );\r\n if (previousStyle === style && lastParent !== parent) {\r\n lastParent = parent;\r\n\r\n const firstChild = parent.firstElementChild;\r\n const startsWithVerse =\r\n firstChild?.nodeName === 'verse' &&\r\n !firstChild.hasAttribute('eid') &&\r\n parseInt(\r\n firstChild.getAttribute('number') || '0',\r\n 10\r\n ) === verse.number;\r\n\r\n if (\r\n startsWithVerse &&\r\n previousVerse &&\r\n previousVerse.number !== verse.number\r\n ) {\r\n addOrJoin(previousVerse.content, {\r\n lineBreak: true,\r\n });\r\n } else {\r\n yield {\r\n lineBreak: true,\r\n };\r\n }\r\n }\r\n }\r\n } else if (style === 'd') {\r\n descriptive = true;\r\n }\r\n }\r\n\r\n for (let content of this.iterateNodeTextContent(\r\n nodes,\r\n node,\r\n chapter,\r\n verse\r\n )) {\r\n if (poem !== null || descriptive !== null) {\r\n if (typeof content === 'string') {\r\n let text: Text = {\r\n text: content,\r\n };\r\n\r\n if (poem !== null) {\r\n text.poem = poem;\r\n }\r\n\r\n if (descriptive !== null) {\r\n text.descriptive = true;\r\n }\r\n\r\n yield text;\r\n } else {\r\n let text:\r\n | InlineLineBreak\r\n | InlineHeading\r\n | FootnoteReference\r\n | Text = {\r\n ...content,\r\n };\r\n\r\n if ('text' in text) {\r\n if (poem !== null) {\r\n text.poem = poem;\r\n }\r\n\r\n if (descriptive !== null) {\r\n text.descriptive = true;\r\n }\r\n }\r\n yield text;\r\n }\r\n } else {\r\n yield content;\r\n }\r\n }\r\n }\r\n }\r\n\r\n parseVerse(\r\n element: Element,\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): Verse {\r\n const verse: Verse = {\r\n type: 'verse',\r\n number: parseInt(element.getAttribute('number') || '0', 10),\r\n content: [],\r\n };\r\n\r\n if (this._currentChapter && this._lastVerse) {\r\n if (this._currentChapter.number === chapter.number) {\r\n // We're parsing the same chapter as the last one\r\n const delta = verse.number - this._lastVerse.number;\r\n if (delta > 1) {\r\n // Delta is greater than 1, so we might have missed some verses\r\n for (\r\n let v = this._lastVerse.number + 1;\r\n v < verse.number;\r\n v++\r\n ) {\r\n let isExpected = false;\r\n if (this._bookId) {\r\n const missingVerse = `${this._bookId} ${chapter.number}:${v}`;\r\n if (KNOWN_SKIPPED_VERSES.has(missingVerse)) {\r\n isExpected = true;\r\n }\r\n }\r\n\r\n if (!isExpected) {\r\n this._messages.push({\r\n type: 'warning',\r\n message: `Verse ${this._bookId ?? '(null)'} ${chapter.number}:${v} is missing.`,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n }\r\n const previousVerse =\r\n this._currentChapter?.number === chapter.number\r\n ? this._lastVerse\r\n : null;\r\n\r\n for (let content of this.iterateVerseContent(\r\n chapter,\r\n verse,\r\n nodes,\r\n previousVerse\r\n )) {\r\n addOrJoin(verse.content, content);\r\n }\r\n\r\n trimContent(verse.content);\r\n this._lastVerse = verse;\r\n return verse;\r\n }\r\n\r\n *parseHebrewSubtitle(\r\n para: Element,\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): IterableIterator<HebrewSubtitle | Verse> {\r\n const subtitle: HebrewSubtitle = {\r\n type: 'hebrew_subtitle',\r\n content: [],\r\n };\r\n\r\n for (let content of this.iterateHebrewSubtitleContent(\r\n para,\r\n chapter,\r\n nodes\r\n )) {\r\n if (typeof content === 'object' && 'number' in content) {\r\n yield content;\r\n continue;\r\n }\r\n addOrJoin(subtitle.content, content);\r\n }\r\n\r\n trimContent(subtitle.content);\r\n if (subtitle.content.length > 0) {\r\n yield subtitle;\r\n }\r\n }\r\n\r\n *iterateHebrewSubtitleContent(\r\n element: Element,\r\n chapter: Chapter,\r\n nodes: RewindableIterator<Node>\r\n ): IterableIterator<\r\n Verse | string | Text | FootnoteReference | InlineLineBreak\r\n > {\r\n while (true) {\r\n const { done, value: node } = nodes.next();\r\n if (done) {\r\n break;\r\n }\r\n\r\n if (!isParent(node, element)) {\r\n nodes.rewind(1);\r\n break;\r\n }\r\n\r\n if (node instanceof Element && node.nodeName === 'verse') {\r\n yield this.parseVerse(node, chapter, nodes);\r\n } else {\r\n yield* this.iterateNodeTextContent(nodes, node, chapter);\r\n }\r\n }\r\n }\r\n\r\n *iterateNodeTextContent(\r\n nodes: RewindableIterator<Node>,\r\n node: Node,\r\n chapter: Chapter,\r\n verse?: Verse\r\n ): IterableIterator<string | Text | FootnoteReference | InlineLineBreak> {\r\n if (node instanceof Element && node.nodeName === 'note') {\r\n yield* this.iterateNote(nodes, node, chapter, verse);\r\n } else if (node instanceof Element && node.nodeName === 'char') {\r\n yield* this.iterateChar(nodes, node);\r\n } else if (\r\n node instanceof Element &&\r\n node.nodeName === 'para' &&\r\n node.getAttribute('style') === 'b'\r\n ) {\r\n for (let _ of children(nodes, node)) {\r\n // iterate through all the children to prevent iterating over them multiple times\r\n }\r\n yield {\r\n lineBreak: true,\r\n };\r\n } else if (node.nodeType === NodeType.Text) {\r\n yield node.textContent || '';\r\n }\r\n }\r\n\r\n *iterateCharContent(char: Element): IterableIterator<string | Text> {\r\n const style = char.getAttribute('style');\r\n const text = trimText(char.textContent || '');\r\n if (style === 'wj') {\r\n yield {\r\n text,\r\n wordsOfJesus: true,\r\n };\r\n } else {\r\n yield text;\r\n }\r\n }\r\n\r\n *iterateNote(\r\n nodes: RewindableIterator<Node>,\r\n node: Element,\r\n chapter: Chapter,\r\n verse?: Verse\r\n ): IterableIterator<FootnoteReference> {\r\n const style = node.getAttribute('style');\r\n if (style === 'f') {\r\n const verseReferenceRegex = /^[0-9]{1,3}:[0-9]{1,3}/;\r\n\r\n let text = '';\r\n for (let child of children(nodes, node)) {\r\n if (child.nodeType === NodeType.Text) {\r\n text += child.textContent || '';\r\n }\r\n }\r\n\r\n text = text.trim();\r\n if (verseReferenceRegex.test(text)) {\r\n text = text.replace(verseReferenceRegex, '').trim();\r\n }\r\n\r\n const note: Footnote = {\r\n noteId: this._noteCounter++,\r\n caller: node.getAttribute('caller') || null,\r\n text,\r\n reference: {\r\n chapter: chapter.number,\r\n verse: verse?.number ?? 0,\r\n },\r\n };\r\n\r\n chapter.footnotes.push(note);\r\n\r\n yield {\r\n noteId: note.noteId,\r\n };\r\n } else {\r\n for (let _ of children(nodes, node)) {\r\n // iterate through all the children\r\n // so that we don't end up with duplicates\r\n }\r\n }\r\n }\r\n\r\n *iterateChar(\r\n nodes: RewindableIterator<Node>,\r\n node: Element\r\n ): IterableIterator<string | Text> {\r\n const style = node.getAttribute('style');\r\n let text = '';\r\n\r\n for (let char of children(nodes, node)) {\r\n if (char.nodeType === NodeType.Text) {\r\n text += char.textContent || '';\r\n }\r\n }\r\n\r\n if (style === 'wj') {\r\n yield {\r\n text,\r\n wordsOfJesus: true,\r\n };\r\n } else {\r\n yield text;\r\n }\r\n\r\n // if (!parentChar(node) && !parentNote(node)) {\r\n // yield *iterateCharContent(node);\r\n // }\r\n }\r\n}\r\n\r\n// Taken from https://github.com/gracious-tech/fetch/blob/1576cc4eafb32bf347a09332094cf17c2231c90c/converters/usx-to-json/src/elements.ts#L16\r\nconst ignoredParaStyles = new Set([\r\n // <para> Identification [exclude all] - Running headings & table of contents\r\n 'ide', // See https://github.com/schierlm/BibleMultiConverter/issues/67\r\n 'rem', // Remarks (valid in schema though missed in docs)\r\n 'h',\r\n 'h1',\r\n 'h2',\r\n 'h3',\r\n 'h4',\r\n 'toc1',\r\n 'toc2',\r\n 'toc3',\r\n 'toca1',\r\n 'toca2',\r\n 'toca3',\r\n\r\n /* <para> Introductions [exclude all] - Introductionary (non-biblical) content\r\n Which might be helpful in a printed book, but intro material in apps is usually bad UX,\r\n and users that really care can research a translations methodology themselves\r\n */\r\n 'imt',\r\n 'imt1',\r\n 'imt2',\r\n 'imt3',\r\n 'imt4',\r\n 'is',\r\n 'is1',\r\n 'is2',\r\n 'is3',\r\n 'is4',\r\n 'ip',\r\n 'ipi',\r\n 'im',\r\n 'imi',\r\n 'ipq',\r\n 'imq',\r\n 'ipr',\r\n 'iq',\r\n 'iq1',\r\n 'iq2',\r\n 'iq3',\r\n 'iq4',\r\n 'ib',\r\n 'ili',\r\n 'ili1',\r\n 'ili2',\r\n 'ili3',\r\n 'ili4',\r\n 'iot',\r\n 'io',\r\n 'io1',\r\n 'io2',\r\n 'io3',\r\n 'io4',\r\n 'iex',\r\n 'imte',\r\n 'ie',\r\n\r\n /* <para> Headings [exclude some] - Exclude book & chapter headings but keep section headings\r\n Not excluded: ms# | mr | s# | sr | d | sp | sd#\r\n */\r\n 'mt',\r\n 'mt1',\r\n 'mt2',\r\n 'mt3',\r\n 'mt4',\r\n 'mte',\r\n 'mte1',\r\n 'mte2',\r\n 'mte3',\r\n 'mte4',\r\n 'cl',\r\n 'cd', // Non-biblical chapter summary, more than heading\r\n 'r', // Parallels to be provided by external data\r\n]);\r\n\r\nfunction* iterateCharContent(char: Element): IterableIterator<string | Text> {\r\n const style = char.getAttribute('style');\r\n const text = trimText(char.textContent || '');\r\n if (style === 'wj') {\r\n yield {\r\n text,\r\n wordsOfJesus: true,\r\n };\r\n } else {\r\n yield text;\r\n }\r\n}\r\n\r\nfunction trimText(text: string): string {\r\n return text.replace(/\\s+/g, ' ');\r\n}\r\n\r\nfunction trimContent<T extends string | unknown>(content: T[]): T[] {\r\n for (let i = 0; i < content.length; i++) {\r\n const value = content[i];\r\n if (typeof value === 'string') {\r\n content[i] = trimText(value as string).trim() as T;\r\n if (content[i] === '') {\r\n content.splice(i, 1);\r\n i--;\r\n continue;\r\n }\r\n } else if (isVerseText(value)) {\r\n value.text = trimText(value.text).trim();\r\n if (value.text === '') {\r\n content.splice(i, 1);\r\n i--;\r\n continue;\r\n }\r\n }\r\n }\r\n return content;\r\n}\r\n\r\nfunction addOrJoin(array: (string | unknown)[], value: string | unknown) {\r\n if (array.length === 0) {\r\n array.push(value);\r\n } else {\r\n const last = array[array.length - 1];\r\n if (typeof last === 'string' && typeof value === 'string') {\r\n array[array.length - 1] = last + value;\r\n } else if (\r\n isVerseText(last) &&\r\n isVerseText(value) &&\r\n hasSameFormatting(last, value)\r\n ) {\r\n last.text += value.text;\r\n } else {\r\n array.push(value);\r\n }\r\n }\r\n}\r\n\r\nfunction isVerseText(value: unknown): value is Text {\r\n return typeof value === 'object' && value !== null && 'text' in value;\r\n}\r\n\r\nfunction hasSameFormatting(a: Text, b: Text): boolean {\r\n return a.poem === b.poem && a.wordsOfJesus === b.wordsOfJesus;\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["NodeType"]
|
|
7
7
|
}
|
package/dist/cjs/utils.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,10 +17,20 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
var utils_exports = {};
|
|
20
30
|
__export(utils_exports, {
|
|
31
|
+
BookIdSchema: () => BookIdSchema,
|
|
21
32
|
KNOWN_SKIPPED_VERSES: () => KNOWN_SKIPPED_VERSES,
|
|
33
|
+
VerseRefSchema: () => VerseRefSchema,
|
|
22
34
|
getBookId: () => getBookId,
|
|
23
35
|
getFirstNonEmpty: () => getFirstNonEmpty,
|
|
24
36
|
getTranslationId: () => getTranslationId,
|
|
@@ -27,6 +39,7 @@ __export(utils_exports, {
|
|
|
27
39
|
parseVerseReference: () => parseVerseReference
|
|
28
40
|
});
|
|
29
41
|
module.exports = __toCommonJS(utils_exports);
|
|
42
|
+
var import_zod = __toESM(require("zod"));
|
|
30
43
|
function normalizeLanguage(language) {
|
|
31
44
|
return language;
|
|
32
45
|
}
|
|
@@ -52,6 +65,143 @@ function getFirstNonEmpty(...values) {
|
|
|
52
65
|
}
|
|
53
66
|
throw new Error("All values are empty or whitespace!");
|
|
54
67
|
}
|
|
68
|
+
const BookIdSchema = import_zod.default.enum([
|
|
69
|
+
// 'FRT',
|
|
70
|
+
"GEN",
|
|
71
|
+
"EXO",
|
|
72
|
+
"LEV",
|
|
73
|
+
"NUM",
|
|
74
|
+
"DEU",
|
|
75
|
+
"JOS",
|
|
76
|
+
"JDG",
|
|
77
|
+
"RUT",
|
|
78
|
+
"1SA",
|
|
79
|
+
"2SA",
|
|
80
|
+
"1KI",
|
|
81
|
+
"2KI",
|
|
82
|
+
"1CH",
|
|
83
|
+
"2CH",
|
|
84
|
+
"EZR",
|
|
85
|
+
"NEH",
|
|
86
|
+
"EST",
|
|
87
|
+
"JOB",
|
|
88
|
+
"PSA",
|
|
89
|
+
"PRO",
|
|
90
|
+
"ECC",
|
|
91
|
+
"SNG",
|
|
92
|
+
"ISA",
|
|
93
|
+
"JER",
|
|
94
|
+
"LAM",
|
|
95
|
+
"EZK",
|
|
96
|
+
"DAN",
|
|
97
|
+
"HOS",
|
|
98
|
+
"JOL",
|
|
99
|
+
"AMO",
|
|
100
|
+
"OBA",
|
|
101
|
+
"JON",
|
|
102
|
+
"MIC",
|
|
103
|
+
"NAM",
|
|
104
|
+
"HAB",
|
|
105
|
+
"ZEP",
|
|
106
|
+
"HAG",
|
|
107
|
+
"ZEC",
|
|
108
|
+
"MAL",
|
|
109
|
+
"MAT",
|
|
110
|
+
"MRK",
|
|
111
|
+
"LUK",
|
|
112
|
+
"JHN",
|
|
113
|
+
"ACT",
|
|
114
|
+
"ROM",
|
|
115
|
+
"1CO",
|
|
116
|
+
"2CO",
|
|
117
|
+
"GAL",
|
|
118
|
+
"EPH",
|
|
119
|
+
"PHP",
|
|
120
|
+
"COL",
|
|
121
|
+
"1TH",
|
|
122
|
+
"2TH",
|
|
123
|
+
"1TI",
|
|
124
|
+
"2TI",
|
|
125
|
+
"TIT",
|
|
126
|
+
"PHM",
|
|
127
|
+
"HEB",
|
|
128
|
+
"JAS",
|
|
129
|
+
"1PE",
|
|
130
|
+
"2PE",
|
|
131
|
+
"1JN",
|
|
132
|
+
"2JN",
|
|
133
|
+
"3JN",
|
|
134
|
+
"JUD",
|
|
135
|
+
"REV",
|
|
136
|
+
"TOB",
|
|
137
|
+
"JDT",
|
|
138
|
+
"ESG",
|
|
139
|
+
"WIS",
|
|
140
|
+
"SIR",
|
|
141
|
+
"BAR",
|
|
142
|
+
"LJE",
|
|
143
|
+
"S3Y",
|
|
144
|
+
"SUS",
|
|
145
|
+
"BEL",
|
|
146
|
+
"1MA",
|
|
147
|
+
"2MA",
|
|
148
|
+
"3MA",
|
|
149
|
+
"4MA",
|
|
150
|
+
"1ES",
|
|
151
|
+
"2ES",
|
|
152
|
+
"MAN",
|
|
153
|
+
"PS2",
|
|
154
|
+
"ODA",
|
|
155
|
+
"PSS",
|
|
156
|
+
"EZA",
|
|
157
|
+
"5EZ",
|
|
158
|
+
"6EZ",
|
|
159
|
+
"DAG",
|
|
160
|
+
"PS3",
|
|
161
|
+
"2BA",
|
|
162
|
+
"LBA",
|
|
163
|
+
"JUB",
|
|
164
|
+
"ENO",
|
|
165
|
+
"1MQ",
|
|
166
|
+
"2MQ",
|
|
167
|
+
"3MQ",
|
|
168
|
+
"REP",
|
|
169
|
+
"4BA",
|
|
170
|
+
"LAO"
|
|
171
|
+
]).meta({
|
|
172
|
+
id: "BookId",
|
|
173
|
+
description: "IDs for books. Follows the USFM standard (https://ubsicap.github.io/usfm/identification/books.html)"
|
|
174
|
+
});
|
|
175
|
+
const VerseRefSchema = import_zod.default.object({
|
|
176
|
+
book: BookIdSchema,
|
|
177
|
+
chapter: import_zod.default.number().meta({
|
|
178
|
+
description: "The chapter number that the reference starts at."
|
|
179
|
+
}),
|
|
180
|
+
verse: import_zod.default.number().meta({
|
|
181
|
+
description: "The verse number that the reference starts at."
|
|
182
|
+
}),
|
|
183
|
+
/**
|
|
184
|
+
* The rest of the content of the verse.
|
|
185
|
+
*/
|
|
186
|
+
content: import_zod.default.string().optional().meta({
|
|
187
|
+
description: 'The rest of the content of the verse reference. This is the part of the string that comes after the verse reference. For example, in "GEN 1:1 In the beginning, God created the Heavens and the Earth.", the content would be "In the beginning, God created the Heavens and the Earth."'
|
|
188
|
+
}),
|
|
189
|
+
/**
|
|
190
|
+
* The chapter that the verse reference ends at.
|
|
191
|
+
*/
|
|
192
|
+
endChapter: import_zod.default.number().optional().meta({
|
|
193
|
+
description: 'The chapter that the verse reference ends at. This is used for references that span multiple chapters, such as "GEN 1:1-2:3". In this case, the endChapter would be 2.'
|
|
194
|
+
}),
|
|
195
|
+
/**
|
|
196
|
+
* The verse that the verse reference ends at.
|
|
197
|
+
*/
|
|
198
|
+
endVerse: import_zod.default.number().optional().meta({
|
|
199
|
+
description: 'The verse that the verse reference ends at. This is used for references that span multiple verses, such as "GEN 1:1-1:3". In this case, the endVerse would be 3.'
|
|
200
|
+
})
|
|
201
|
+
}).meta({
|
|
202
|
+
id: "VerseRef",
|
|
203
|
+
description: 'Defines a schema for verse references. A verse reference is a string that contains a book ID, chapter number, and verse number, such as "GEN 1:1". It can also contain an optional content part that comes after the verse reference, such as "GEN 1:1 In the beginning, God created the Heavens and the Earth." It can also contain optional endChapter and endVerse fields for references that span multiple chapters or verses, such as "GEN 1:1-2:3".'
|
|
204
|
+
});
|
|
55
205
|
function parseVerseReference(text) {
|
|
56
206
|
const match = text.match(
|
|
57
207
|
/^\s*([0-9A-Za-z\s]+)[\s\.]+(\d+)[:\.](\d+)(?:-([0-9]+)(?:[\s:\.]([0-9]+))?)?/
|
|
@@ -269,7 +419,9 @@ const KNOWN_SKIPPED_VERSES = /* @__PURE__ */ new Set([
|
|
|
269
419
|
]);
|
|
270
420
|
// Annotate the CommonJS export names for ESM import in node:
|
|
271
421
|
0 && (module.exports = {
|
|
422
|
+
BookIdSchema,
|
|
272
423
|
KNOWN_SKIPPED_VERSES,
|
|
424
|
+
VerseRefSchema,
|
|
273
425
|
getBookId,
|
|
274
426
|
getFirstNonEmpty,
|
|
275
427
|
getTranslationId,
|