(
text: string,
options: ParseOptions = {},
)
| 142 | * @return The parsed object |
| 143 | */ |
| 144 | export function parse<T extends object>( |
| 145 | text: string, |
| 146 | options: ParseOptions = {}, |
| 147 | ): T { |
| 148 | if (typeof text !== "string") { |
| 149 | throw new SyntaxError(`Unexpected token ${text} in INI at line 0`); |
| 150 | } |
| 151 | |
| 152 | const root = {} as T; |
| 153 | let object: object = root; |
| 154 | let sectionName: string | undefined; |
| 155 | |
| 156 | let lineNumber = 0; |
| 157 | for (let line of readTextLines(text)) { |
| 158 | line = line.trim(); |
| 159 | lineNumber += 1; |
| 160 | |
| 161 | // skip empty lines |
| 162 | if (line === "") continue; |
| 163 | |
| 164 | // skip comment |
| 165 | if (isComment(line)) continue; |
| 166 | |
| 167 | if (isSection(line, lineNumber)) { |
| 168 | sectionName = SECTION_REGEXP.exec(line)?.groups?.name; |
| 169 | if (!sectionName) { |
| 170 | throw new SyntaxError( |
| 171 | `Unexpected empty section name at line ${lineNumber}`, |
| 172 | ); |
| 173 | } |
| 174 | |
| 175 | object = {}; |
| 176 | Object.defineProperty(root, sectionName, { |
| 177 | value: object, |
| 178 | writable: true, |
| 179 | enumerable: true, |
| 180 | configurable: true, |
| 181 | }); |
| 182 | |
| 183 | continue; |
| 184 | } |
| 185 | |
| 186 | const groups = KEY_VALUE_REGEXP.exec(line)?.groups; |
| 187 | |
| 188 | if (!groups) { |
| 189 | throw new SyntaxError( |
| 190 | `Unexpected token ${line[0]} in INI at line ${lineNumber}`, |
| 191 | ); |
| 192 | } |
| 193 | |
| 194 | const { key, value } = groups as { key: string; value: string }; |
| 195 | if (!key.length) { |
| 196 | throw new SyntaxError(`Unexpected empty key name at line ${lineNumber}`); |
| 197 | } |
| 198 | |
| 199 | const parsedValue = parseValue(key, value); |
| 200 | let val = parsedValue as unknown; |
| 201 | if (options.reviver) val = options.reviver(key, parsedValue, sectionName); |
no test coverage detected