A simple parser for Valve's KeyValue text file format (VDF) https://developer.valvesoftware.com/wiki/KeyValues. Written in JavaScript (TypeScript).
Support both returning an object OR piping readable stream to the parser streaming out key-value pairs.
Pick your favorite package manager. For example using npm:
npm install @hinw/vdf-parserimport{VdfParser}from'@hinw/vdf-parser';// file content: "key" { "nested_key" "value" }"constfilePath='input/sample.vdf';constparser=newVdfParser();constresult=awaitparser.parseFile(filePath);// assert.assertEqual(result, { key: { nested_key: 'value' }});importstreamfrom'node:stream';import{VdfParser}from'@hinw/vdf-parser';constreadStream=stream.Readable.from(`"key" { "nested_key" "value" }"`);constparser=newVdfParser();constresult=awaitparser.parseStream(readStream);// assert.assertEqual(result, { key: { nested_key: 'value' }});import{VdfParser}from'@hinw/vdf-parser';constinput=`"key" { "nested_key" "value" }"`;constparser=newVdfParser();constresult=awaitparser.parseText(input);// assert.assertEqual(result, { key: { nested_key: 'value' }});importfsfrom'node:fs'import{VdfParser}from'@hinw/vdf-parser';// file content: "key" { "nested_key" "value" }"constfilePath='input/sample.vdf';constparser=newVdfParser();constfileStream=fs.createReadStream(filePath)constparserStream=fileStream.pipe(parser)forawait(constpairofparserStream){// assert.assertEqual(pair, { keyParts: ['key', 'nested_key'], value: 'value' });}// You can build the object from the stream using async iterator interface:constfileStream=fs.createReadStream(filePath)constparserStream=fileStream.pipe(parser)constresult=awaitparser.condensePairsAsync(parserStream)// Or you can store the pairs somewhere, and build it afterwards with normal iterator interface:constfileStream=fs.createReadStream(filePath)constparserStream=fileStream.pipe(parser)constpairs=awaitArray.fromAsync(parserStream)constresult=parser.condensePairs(pairs)By default, the parser will handle escape sequence. To disable this behavior, you can set disableEscape to true.
import{VdfParser}from'@hinw/vdf-parser';constinput=`"\\"quoted key\\"" "value"`;constparser=newVdfParser({disableEscape: true});constresult=awaitparser.parseText(input);// assert.assertEqual(result, { '\\"quoted key\\"': 'value' });If the values of the duplicated keys are all maps, they will be merged together
import{VdfParser}from'@hinw/vdf-parser';constinput=`"key" { "nested_key" "value" }" "key" { "nested_key_2" "value" }"`;constparser=newVdfParser({useLatestValue: true});constresult=awaitparser.parseText(input);// assert.assertEqual(result, { key: { nested_key: 'value', nested_key_2: 'value' }});By default, the parser will use the earliest seen value for the duplicated keys.
import{VdfParser}from'@hinw/vdf-parser';constinput=`"key" { "nested_key" "value" }" "key" "value"`;constparser=newVdfParser();constresult=awaitparser.parseText(input);// assert.assertEqual(result, { key: { nested_key: 'value' }});However, you can set useLatestValue to true to use the latest seen value instead.
import{VdfParser}from'@hinw/vdf-parser';constinput=`"key" { "nested_key" "value" }" "key" "value"`;constparser=newVdfParser({useLatestValue: true});constresult=awaitparser.parseText(input);// assert.assertEqual(result, { key: 'value' });Since the VDF specification does not contain any type info, it would be a guess work to convert some value to a certain type. To keep this library simple, it would not provide an option to do auto type detection / conversion. In theory if the user is interested in using the values of a VDF they should know the schema of the file well and it is the responsibiliy for the user to convert the types instead of this library doing the guess work.