import { useDocument } from '@terrace/core' import { createStringReader } from '@terrace/core/readers/js-string' const lines = [ `name @terrace/core`, `version 0.0.1`, `randomthing test`, `license MIT`, `license GPL`, `exports`, ` .`, ` import ./dist/index.js`, ` require ./dist/index.cjs`, ` ./parser`, ` import ./dist/parser.js`, ` require ./dist/parser.cjs`, ``, ` ./document`, ` import ./dist/document.js`, ` require ./dist/document.cjs`, ``, ` ./readers/node-readline`, ` import ./dist/readers/node-readline.js`, ` require ./dist/readers/node-readline.cjs`, ``, ` ./readers/js-string`, ` import ./dist/readers/js-string.js`, ` require ./dist/readers/js-string.cjs`, `scripts`, ` test vitest ./src`, ` build vite build`, `devDependencies`, ` vite ^3.2.3`, ` vitest ^0.24.5`, ``, `authors`, ` author`, ` name Joshua Bemenderfer`, ` email josh@thederf.com`, ``, ` Further comments below. As I will now demonstrate, there is no simple`, ` even if embedded`, ` way of dealing with this problem.`, ` author`, ` name Second Person`, ` email second@secondperson.com`, ` More text,`, ` across two lines.`, `list`, ` - item1`, ` - item2` ] // Schema // name tail // version tail // license tail // exports object // #any object // import tail // require tail // scripts object // #any tail // devDependencies // #any tail // author object // name tail // email tail // #text async function main() { const { head, tail, next, match, level, line } = useDocument(createStringReader(lines)) const structure = {} async function kvObject(handle) { const obj = {} const l = level() while (await next(l)) { if (!head()) continue obj[head()] = handle ? await handle(level()) : tail().trim() } return obj } while (await next()) { if (match('name')) structure.name = tail().trim() if (match('version')) structure.version = tail().trim() if (match('exports')) structure.exports = await kvObject(async l => { const obj = {} while (await next(l)) { if (match('import')) obj.import = tail().trim() if (match('require')) obj.require = tail().trim() } return obj }) if (match('scripts')) structure.scripts = await kvObject() if (match('devDependencies')) structure.devDependencies = await kvObject() if (match('author')) { if (!structure.authors) structure.authors = [] const author = {} structure.authors.push(author) const l = level() while (await next(l)) { if (!head()) continue if (match('name')) author.name = tail().trim() else if (match('email')) author.email = tail().trim() else { if (!author['#text']) author['#text'] = [line(l + 1)] // Loop through all remaining lines to avoid re-matching name or email above. while(await next(l)) { author['#text'].push(line(l + 1)) } } } } } console.dir(structure, { depth: null }) } main()