npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

easy-keyvalues

v1.2.7

Published

Parse Valve KeyValues Format and easy to use in nodejs or browser.

Downloads

529

Readme

easy-keyvalues

CI CodeQL npm tag support tag support tag

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.

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 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

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.

License

MIT License