Parse Valve KeyValues Format and easy to use in nodejs or browser.
npm install easy-keyvalues




!coverage
!coverage
!coverage
!coverage
The reason for writing a library is that I want to edit KeyValues text, format the text when saving,
and keep the comments, support auto load #base on KeyValues, etc.
> I completely rewrote the library to not be compatible with previous versions, so the version
> starts from 1.0.0.
``shell`
npm i easy-keyvalues
or
yarn add easy-keyvalues
The BOM will be automatically removed when load this format file.
- Retain comments
- Support Node.js and browser
- Support auto load #base
`ts
import {
KeyValues,
} from 'easy-keyvalues';
KeyValues.Load(file: string): Promise
KeyValues.Save(): Promise
KeyValues.Save(otherFile: string): Promise
`
`ts`
// Parse KeyValues text
const kv = await KeyValues.Load('/path/to/file.txt');
console.log(kv.toString());
| Property | Type | Description |
| -------- | :---------: | ------------------------------------------------------------------------------------------------------------------------------------- |
| value | string | It represents the value of KeyValues, which is always of type string and does not convert numeric strings to number during parsing. |
| children | KeyValues[] | It represents the children of KeyValues, which means that this kv is an object and the children are its properties. |
| parent | KeyValues | It represents the parent node to which the KeyValues belong; the root node has no parent. |
> Note that value and children are mutually exclusive, one of them exists and the other is
> undefined, which can be determined by HasChildren()
Related Methods:
`js`
GetChildren(): Readonly
GetChildCount(): number
GetFirstChild(): KeyValues | undefined
GetLastChild(): KeyValues | undefined
GetValue(): string // When value is undefined return empty string
HasChildren(): boolean
GetParent(): KeyValues | undefined
The KeyValues returned after parsing by KeyValues.Parse() is a root node, whose methodIsRoot() will return true and is forced to have children.
The purpose of this library is to edit the KV, so after loading #base it does not merge all the#base
KeyValues nodes in into the parent node, but keeps the KeyValues node #base, which is thechildren
root node of the file, and its are all the children of the root node.
Example
`js
/*
KeyValues.txt
#base "npc/file01.txt"
#base "npc/file02.txt"
"DOTAAbilities"
{
"ability01"
{
"BaseClass" "ability_datadriven"
"AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_POINT"
}
}
*/
const root = await KeyValues.Load(join(__dirname, 'KeyValues.txt'));
// Get path
baseList[0].GetBaseFilePath(); // npc/file01.txt
baseList[0].filename; // {__dirname}/npc/file01.txt
// Calling Save will automatically save the #base file
KeyValues.Save();
// Note that if other file path are specified, the base kv will be created together with the relative paths.
KeyValues.Save(join(__dirname, 'otherPath/KeyValues.txt'));
`
`js
// Create the root node, which is a static method
KeyValues.CreateRoot(): KeyValues
// Only create KeyValues when children exist, otherwise an error will be thrown
// Return new KeyValues
CreateChild(key: string, value: string | KeyValues[]): KeyValues
// Returns self, such as SetValue('a').GetValue()
SetValue(v: string | KeyValues[]): this
`
Example
`js
/*
"Table"
{
"Item" "item_0001"
"Item" "item_0002"
}
*/
const root = KeyValues.CreateRoot();
const kv = root.CreateChild('Table', []);
kv.CreateChild('Item', 'item_0001');
kv.CreateChild('Item', 'item_0002');
// or
root.CreateChild('Table', [new KeyValues('Item', 'item_0001'), new KeyValues('Item', 'item_0002')]);
// or
kv.SetValue([new KeyValues('Item', 'item_0001'), new KeyValues('Item', 'item_0002')]);
`
`js
// Called only when children exist in KeyValues, otherwise an error will be thrown
// Add KeyValues to the end of children
Append(child: KeyValues): this
// Called only when children exist in KeyValues, otherwise an error will be thrown
// Add KeyValues to the specified location of children
Insert(child: KeyValues, index: number): this
// Delete the specified key or KeyValues from children
// Return deleted KeyValues
Delete(child: string | KeyValues): KeyValues | undefined
// This function is used to release the KeyValues and unlink the nodes,
// i.e. remove self from the parent node, and set the parent to undefined
Free(): this
`
> Note that the KeyValues of SetValue, Append, Insert will change the parent
Example
`js
const root = KeyValues.CreateRoot();
const kv = root.CreateChild('Table', []);
kv.Append(new KeyValues('Item', 'item_0001'));
kv.Append(new KeyValues('Item', 'item_0002'));
kv.Delete('Item')?.GetValue(); // item_0001
`
`js
// Find a KeyValues
Find(
callback: (kv: KeyValues, i: number, parent: KeyValues) => boolean
): KeyValues | undefined
// Find multiple KeyValues
FindAll(
callback: (kv: KeyValues, i: number, parent: KeyValues) => boolean
): KeyValues[]
// Find a KeyValues
FindKey(key: string): KeyValues | undefined
// Find multiple KeyValues
FindAllKeys(...keys: string[]): KeyValues[]
// Traversing the KeyValues tree
FindTraverse(
callback: (kv: KeyValues, i: number, parent: KeyValues) => boolean
): KeyValues | undefined
`
Example
`js`
// For example, the above Table
kv.FindAllKeys('Item'); // [KeyValues('Item', 'item_0001'), KeyValues('Item', 'item_0002')]
Easy to convert to JSON.
`js`
// Return an object
kv.toObject();
Compared to KeyValues, KeyValues3 has multiple data types, a format similar to JSON, and relatively
more complex code than KeyValues. The code is also much more complex than KeyValues.
Reference https://developer.valvesoftware.com/wiki/Dota_2_Workshop_Tools/KeyValues3
- Retain comments
- Support Node.js and browser
- Friendly data type inference
`ts
import {
KeyValues3,
} from 'easy-keyvalues';
KeyValues3.Load(file: string): Promise
KeyValues3.Save(): Promise
KeyValues3.Save(otherFile: string): Promise
`
`ts`
// Parse KeyValues3
const kv3 = await KeyValues3.Load('/path/to/file.txt');
console.log(kv3.toString());
| KeyValues3 Type | Javascript Type | Description |
| --------------- | --------------- | ------------------------------------------------------------------- |
| String | string | KV3 supports multi-line strings with """ as the beginning and end |toFixed(6)
| Boolean | boolean | true or false |
| Int | number | integer |
| Double | number | When formatting as a string use |IKV3Value[]
| Array | Array | Array,Type: |KeyValues3[]
| Object | Object | Object,Type: |resource:"example"
| Feature | string | soundevent:"example" |subclass: {}
| FeatureObject | Object | |
> Note that when parsing Int and Double, they are only parsed as Double if they contain a fractional
> part, otherwise they are treated as Int
The root node of KeyValues3 is a bit special in that it contains a file header and its value is
fixed to Object, with the following basic format:
``
{
}
- Static property KeyValues3.CommonHeader is default header
- Static method KeyValues3.CreateRoot(): KeyValues3 Create root node
- Method GetHeader(): string | undefined Get file header
- Method IsRoot(): boolean Determine if it is the root node
`js`
// Called only when the value of KeyValues is Object, otherwise an error is thrown
// Create KeyValues3 to Object
// Return created KeyValues3
CreateObjectValue(key: string, value: IKV3Value): KeyValues3
KeyValues3 values are a class, the interface is IKV3Value and the base class is KV3BaseValue.
`ts`
interface IKV3Value {
Comments: KeyValues3Comments;
GetValue(): any;
GetOwner(): KeyValues3 | undefined;
SetOwner(owner: KeyValues3 | undefined): void;
IsBoolean(): this is ValueBoolean;
IsInt(): this is ValueInt;
IsDouble(): this is ValueDouble;
IsString(): this is ValueString;
IsFeature(): this is ValueFeature;
IsFeatureObject(): this is ValueFeatureObject;
IsArray(): this is ValueArray;
IsObject(): this is ValueObject;
Format(): string;
}
`js`
KeyValues3.String( initValue?: string )
KeyValues3.Boolean( initValue?: boolean )
KeyValues3.Int( initValue?: number )
KeyValues3.Double( initValue?: number )
KeyValues3.Array( initValue?: IKV3Value[] )
KeyValues3.Object( initValue?: KeyValues3[] )
KeyValues3.Feature( feature: string, value?: string )
KeyValues3.FeatureObject( feature: string, initValue?: KeyValues3[] )
Example
`js
const root = KeyValues3.CreateRoot();
root.CreateObjectValue('a', KeyValues3.String('string'));
root.CreateObjectValue('b', KeyValues3.Boolean(false));
root.CreateObjectValue('c', KeyValues3.Int(0));
root.CreateObjectValue('d', KeyValues3.Double(0.0));
root.CreateObjectValue('e', KeyValues3.Array([]));
root.CreateObjectValue('f', KeyValues3.Object([]));
root.CreateObjectValue('g', KeyValues3.Feature('resource', 'path/to/file.vpcf'));
root.CreateObjectValue(
'h',
KeyValues3.FeatureObject('subclass', [new KeyValues3('child', KeyValues3.String('value'))]),
);
KeyValues3.Array([KeyValues3.String('one'), KeyValues3.String('two'), KeyValues3.String('three')]);
const obj = KeyValues3.Object([
new KeyValues3('a', KeyValues3.String('one')),
new KeyValues3('b', KeyValues3.Int(2)),
]);
obj.Create('c', KeyValues3.Boolean(true));
`
- KeyValues3 Object
`js`
// Append to the end of Object
Append(...values: KeyValues3[]): this
// Insert into Object at the specified location
Insert(index: number, ...values: KeyValues3[]): this
// Deletes KeyValues3 from the child node and returns the deleted KeyValues3
Delete(v: string | KeyValues3): KeyValues3
- KeyValues3 Array
`js`
// Append to the end of Array
Append(...values: IKV3Value[]): this
// Insert into Array at the specified location
Insert(index: number, ...values: IKV3Value[]): this
// Delete the IKV3Value in the child node
Delete(v: IKV3Value): this
- KeyValues3 Object
`js
// Find a KeyValues3
Find(
callback: (kv: KeyValues3, i: number, parent: ValueObject) => boolean
): KeyValues3 | undefined
// Find a KeyValues3
FindKey(key: string): KeyValues3 | undefined
// Find multiple KeyValues3
FindAll(
callback: (kv: KeyValues3, i: number, parent: ValueObject) => boolean
): KeyValues3[]
// Find multiple KeyValues3
FindAllKeys(...keys: string[]): KeyValues3[]
`
> These methods are also present in the KeyValues3 method and can be called when the value is an
> Object
Easy to convert to JSON.
`js`
// Return an object or array
kv3.toObject();
| KeyValues3 Type | Javascript Value | Format after |
| --------------- | ------------------------------- | ------------------------------------------- |
| String | this is string | "this is string" |this is string \n second line
| String | | """
this is string
second line
""" |
| Boolean | true | true |
| Int | 5 | 5 |
| Double | 2.5 | 2.500000 |
The library is already adapted for nodejs, but due to the complexity of the browser environment, no
browser adapters are provided, you can refer to src/node.ts and src/adapter.ts for adaptations.
Both KeyValues and KeyValues3 support the ID property, which is provided bycreateKeyValuesID() in the adapter and by default returns the empty character If you need this ID,createKeyValuesID()`. The ID exists to support cross-threaded operation like scenario.
just rewrite