easy-keyvalues
v1.2.5
Published
Parse Valve KeyValues Format and easy to use in nodejs or browser.
Downloads
218
Maintainers
Readme
easy-keyvalues
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.
Installation
npm i easy-keyvalues
or
yarn add easy-keyvalues
UTF-8 BOM
The BOM will be automatically removed when load this format file.
KeyValues
Features
- Retain comments
- Support Node.js and browser
- Support auto load
#base
Import
import {
KeyValues,
} from 'easy-keyvalues';
KeyValues.Load(file: string): Promise<KeyValues>;
KeyValues.Save(): Promise<void>;
KeyValues.Save(otherFile: string): Promise<void>;
Usages
// Parse KeyValues text
const kv = await KeyValues.Load('/path/to/file.txt');
console.log(kv.toString());
KeyValues's value
and children
| 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:
GetChildren(): Readonly<KeyValues[]> // When children is undefined return empty array
GetChildCount(): number
GetFirstChild(): KeyValues | undefined
GetLastChild(): KeyValues | undefined
GetValue(): string // When value is undefined return empty string
HasChildren(): boolean
GetParent(): KeyValues | undefined
Root Node
The KeyValues
returned after parsing by KeyValues.Parse()
is a root node, whose method
IsRoot()
will return true
and is forced to have children
.
#base
The purpose of this library is to edit the KV, so after loading #base
it does not merge all the
KeyValues nodes in #base
into the parent node, but keeps the KeyValues node #base
, which is the
root node of the file, and its children
are all the children of the root node.
Example
/*
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'));
Create
// 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
/*
"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')]);
Add / Delete
// 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
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
Find
// 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
// For example, the above Table
kv.FindAllKeys('Item'); // [KeyValues('Item', 'item_0001'), KeyValues('Item', 'item_0002')]
Convert to Javascript Object
Easy to convert to JSON.
// Return an object
kv.toObject();
KeyValues3
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
Features
- Retain comments
- Support Node.js and browser
- Friendly data type inference
Import
import {
KeyValues3,
} from 'easy-keyvalues';
KeyValues3.Load(file: string): Promise<KeyValues3>;
KeyValues3.Save(): Promise<void>;
KeyValues3.Save(otherFile: string): Promise<void>;
Usages
// Parse KeyValues3
const kv3 = await KeyValues3.Load('/path/to/file.txt');
console.log(kv3.toString());
Data Types
| KeyValues3 Type | Javascript Type | Description |
| --------------- | --------------- | ------------------------------------------------------------------- |
| String | string | KV3 supports multi-line strings with """
as the beginning and end |
| Boolean | boolean | true or false |
| Int | number | integer |
| Double | number | When formatting as a string usetoFixed(6)
|
| Array | Array | Array,Type:IKV3Value[]
|
| Object | Object | Object,Type:KeyValues3[]
|
| Feature | string | resource:"example"
soundevent:"example"
|
| FeatureObject | Object | subclass: {}
|
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
Root Node
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:
<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
}
Static property
KeyValues3.CommonHeader
is default headerStatic method
KeyValues3.CreateRoot(): KeyValues3
Create root nodeMethod
GetHeader(): string | undefined
Get file headerMethod
IsRoot(): boolean
Determine if it is the root node
Create
// 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
.
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;
}
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
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));
Add / Delete
- KeyValues3 Object
// 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
// 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
Find
- KeyValues3 Object
// 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
Convert to Javascript Object
Easy to convert to JSON.
// Return an object or array
kv3.toObject();
Format
| KeyValues3 Type | Javascript Value | Format after |
| --------------- | ------------------------------- | ------------------------------------------- |
| String | this is string
| "this is string"
|
| String | this is string \n second line
| """this is stringsecond line""" |
| Boolean | true | true |
| Int | 5 | 5 |
| Double | 2.5 | 2.500000 |
Custom adapter
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.
About ID
Both KeyValues
and KeyValues3
support the ID property, which is provided by
createKeyValuesID()
in the adapter and by default returns the empty character If you need this ID,
just rewrite createKeyValuesID()
. The ID exists to support cross-threaded operation like scenario.