Public
Script
Readme

Diesel

Diesel is a lightweight data manipulation library inspired by Tom Wright's Dasel, designed for easy querying and transformation of various data formats such as JSON, YAML, CSV, and TOML. It allows users to select, update, and delete data using a simple selector syntax.

Heavily adapted from https://github.com/TomWright/dasel

Features

  • Multi-format Support: Works with JSON, YAML, CSV, and TOML.
  • Dynamic Selectors: Use conditions to filter data dynamically.
  • Function Support: Built-in functions for data manipulation (e.g., length, sum, avg).
  • Easy Integration: Can be used in both Deno and Val Town environments.

Usage

import Diesel from "https://esm.town/v/yawnxyz/diesel";

async function main() {
  const jsonData = `
  {
    "users": [
      {"id": 1, "name": "Alice", "age": 30},
      {"id": 2, "name": "Bob", "age": 25},
      {"id": 3, "name": "Charlie", "age": 35}
    ],
    "settings": {
      "theme": "dark",
      "notifications": true
    }
  }
  `;****

  const diesel = new Diesel(jsonData, 'json');
  try {
    console.log("All data:", await diesel.select(''));
    console.log("All users:", await diesel.select('users'));
    console.log("First user's name:", await diesel.select('users.[0].name'));
    console.log("Users over 30:", await diesel.select('users.(age>30)'));
    
    await diesel.put('settings.theme', 'light');
    console.log("Updated settings:", await diesel.select('settings'));
    
    // await diesel.delete('users.[1]');
    // console.log("Users after deletion:", await diesel.select('users'));
    
    console.log("Data in YAML format:");
    console.log(await diesel.convert('yaml'));
    
    console.log("Data in TOML format:");
    console.log(await diesel.convert('toml'));
    
    console.log("Number of users:", await diesel.select('users.length()'));
    console.log("User names in uppercase:", await diesel.select('users.[*].name.toUpper()'));
  } catch (error) {
    console.error("An error occurred:", error);
  }
}

main();

Installation

To use Diesel, simply import it in your Deno project as shown in the usage example.

License

This project is licensed under the MIT License.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { parse as parseYAML, stringify as stringifyYAML } from "https://deno.land/std/yaml/mod.ts";
// import * as jsonMod from "https://deno.land/std@0.224.0/json/mod.ts";
// import * as csv from "npm:csvtojson";
// import { readCSV, readCSVRows, writeCSV } from "jsr:@vslinko/csv";
// import { parse as parseCSV, stringify as stringifyCSV } from "https://deno.land/std/csv/mod.ts";
import csv from "npm:csvtojson";
// import { CSVtoJSON } from "https://deno.land/x/data_format_converter@v1.2.0/mod.ts";
import { parse as parseTOML, stringify as stringifyTOML } from "https://deno.land/std/toml/mod.ts";
type DataFormat = 'object' | 'json' | 'yaml' | 'csv' | 'toml';
type SelectorType = 'property' | 'index' | 'search' | 'dynamic' | 'append' | 'function';
interface Selector {
type: SelectorType;
value: string | number;
conditions?: Condition[];
args?: string[];
}
interface Condition {
property: string;
operator: string;
value: any;
}
class SelectorParser {
parse(selectorString: string): Selector[] {
const selectors: Selector[] = [];
const parts = this.splitSelector(selectorString);
for (const part of parts) {
if (part.startsWith('.')) {
selectors.push(this.parseProperty(part));
} else if (part.startsWith('[') && part.endsWith(']')) {
selectors.push(this.parseIndex(part));
} else if (part.startsWith('(') && part.endsWith(')')) {
selectors.push(this.parseDynamic(part));
} else if (part === '[]') {
selectors.push(this.parseAppend());
} else if (part.includes('(')) {
selectors.push(this.parseFunction(part));
} else {
// Handle top-level properties without a leading dot
selectors.push(this.parseProperty(`.${part}`));
}
}
return selectors;
}
private splitSelector(selector: string): string[] {
const parts: string[] = [];
let current = '';
let depth = 0;
let inQuotes = false;
for (const char of selector) {
if (char === '"' && depth === 0) {
inQuotes = !inQuotes;
}
if (!inQuotes) {
if (char === '(' || char === '[') depth++;
if (char === ')' || char === ']') depth--;
}
if (char === '.' && depth === 0 && !inQuotes && current) {
parts.push(current);
current = '';
} else {
current += char;
}
}
if (current) parts.push(current);
// If the selector doesn't start with '.', add the first part as is
if (selector[0] !== '.') {
return parts;
}
// Otherwise, add the dot to each part
return parts.map(part => part.startsWith('.') ? part : `.${part}`);
}
private parseProperty(part: string): Selector {
return { type: 'property', value: part.slice(1) };
}
private parseIndex(part: string): Selector {
const inner = part.slice(1, -1);
if (inner === '*') {
return { type: 'index', value: 'all' };
}
const index = parseInt(inner, 10);
if (isNaN(index)) {
throw new Error(`Invalid index: ${inner}`);
}
return { type: 'index', value: index };
}
private parseDynamic(part: string): Selector {
const inner = part.slice(1, -1);
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
September 2, 2024