( pathname: string, preferredEol: LineEnding, autoGuessEncoding: boolean = true, trimTrailingNewline: number = 2, autoNormalizeLineEndings: boolean = false )
| 89 | * Reads the contents of a markdown file. |
| 90 | */ |
| 91 | export const loadMarkdownFile = async( |
| 92 | pathname: string, |
| 93 | preferredEol: LineEnding, |
| 94 | autoGuessEncoding: boolean = true, |
| 95 | trimTrailingNewline: number = 2, |
| 96 | autoNormalizeLineEndings: boolean = false |
| 97 | ): Promise<MarkdownDocumentRaw> => { |
| 98 | // TODO: Use streams to not buffer the file multiple times and only guess |
| 99 | // encoding on the first 256/512 bytes. |
| 100 | |
| 101 | const buffer = await fsPromises.readFile(path.resolve(pathname)) |
| 102 | |
| 103 | const encoding = guessEncoding(buffer, autoGuessEncoding) |
| 104 | const supported = iconv.encodingExists(encoding.encoding) |
| 105 | if (!supported) { |
| 106 | throw new Error(`"${encoding.encoding}" encoding is not supported.`) |
| 107 | } |
| 108 | |
| 109 | let markdown = iconv.decode(buffer, encoding.encoding) |
| 110 | |
| 111 | // Detect line ending |
| 112 | const isLf = LF_LINE_ENDING_REG.test(markdown) |
| 113 | const isCrlf = CRLF_LINE_ENDING_REG.test(markdown) |
| 114 | const isMixedLineEndings = isLf && isCrlf |
| 115 | const isUnknownEnding = !isLf && !isCrlf |
| 116 | let lineEnding: LineEnding = preferredEol |
| 117 | if (isLf && !isCrlf) { |
| 118 | lineEnding = 'lf' |
| 119 | } else if (isCrlf && !isLf) { |
| 120 | lineEnding = 'crlf' |
| 121 | } |
| 122 | |
| 123 | let adjustLineEndingOnSave = false |
| 124 | |
| 125 | if (isMixedLineEndings || isUnknownEnding || lineEnding !== 'lf') { |
| 126 | markdown = convertLineEndings(markdown, 'lf') |
| 127 | // MarkText always uses LF internally. If the user did not request LF line |
| 128 | // endings, we need to adjust on save. |
| 129 | adjustLineEndingOnSave = !autoNormalizeLineEndings && lineEnding !== 'lf' |
| 130 | } |
| 131 | |
| 132 | // Detect final newline |
| 133 | if (trimTrailingNewline === 2) { |
| 134 | if (!markdown) { |
| 135 | trimTrailingNewline = 3 |
| 136 | } else { |
| 137 | const lastIndex = markdown.length - 1 |
| 138 | if (lastIndex >= 1 && markdown[lastIndex] === '\n' && markdown[lastIndex - 1] === '\n') { |
| 139 | trimTrailingNewline = 2 |
| 140 | } else if (markdown[lastIndex] === '\n') { |
| 141 | trimTrailingNewline = 1 |
| 142 | } else { |
| 143 | trimTrailingNewline = 0 |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | const filename = path.basename(pathname) |
no test coverage detected