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

@firebrandtech/jexlate

v1.3.0

Published

[![Tests](https://github.com/FirebrandTech/jexlate/actions/workflows/tests.yml/badge.svg)](https://github.com/FirebrandTech/jexlate/actions/workflows/tests.yml)

Downloads

106

Readme

Tests

Jexlate

Format and validate JSON data with ease (oh... and Jexl). Jexlate uses Jexl to allow for complex tranformations and translations able to be stored in a JSON object.

A prime use-case would be to store transformations in a database without needing to maintain them in source code. This allows for dynamic transformations to be applied to data without needing to redeploy the application code.

Installation

npm i jexlate
# or
yarn add jexlate

Usage

Jexlate is a JSON transformation library that uses Jexl to transform data from a source object to a target object format. A very simple example is shown below, simply changing 2 keys to CamelCase, and create a new key with the full name concatenated from the first and last name:

import { Jexlate } from 'jexlate';

// Define a transformation template
const template = {
  FirstName: {
    from: 'first_name',
  },
  LastName: {
    from: 'last_name',
  },
  FullName: {
    from: 'first_name + " " + last_name',
  },
};

// Instantiate a new Jexlate instance
const jexlate = new Jexlate(template, options);

// Transform data
const data = {
  first_name: 'John',
  last_name: 'Doe',
};

const transformedData = jexlate.transform(data);

/**
 * transformedData:
 * {
 *   FirstName: 'John',
 *   LastName: 'Doe',
 *   FullName: 'John Doe',
 * }
 */

Options

The Jexlate constructor accepts an optional options object as the second argument. The following options are available:

const options = {
  transforms: {
    uppercase: (value) => value.toUpperCase(),
    // 'first_name|uppercase' => 'JOHN'
  },
  functions: {
    concat: (...args) => args.join(''),
    // 'concat(first_name, " ", last_name)' => 'John Doe'
  },
  binaryOps: {
    add: {
      precedence: 1,
      fn: (left, right) => left + right,
    },
    // '10 add 5' => 15
  },
};

Streams

Jexlate also supports transforming streams of data. This is useful for transforming large datasets without needing to load the entire dataset into memory.

import { Jexlate } from 'jexlate';

// Define template and options...

const jexlate = new Jexlate(template, options);

// Create a Jexlate transform stream
const jexlateTransformStream = jexlate.stream();

// Open a readable stream and pipe it through the Jexlate transform stream

const readableStream = someReadableStream();
const writableStream = someWritableStream();

// Pipe the readable stream through the Jexlate transform stream and then to the writable stream
readableStream.pipe(jexlateTransformStream).pipe(writableStream);

Stream Options

The stream method accepts an optional options object as the first argument. The following options are available:

const options = {
  onError: string<throw | collect>
  errorCollector: []
};

By default the stream will throw on any validation or processing errors. If you want to continue the stream and collect errors, you can set onError to collect.

An optional errorCollector array can be passed to collect errors when onError is set to collect.

Operations and Configation

Basic Transformations

const template = {
  FirstName: {
    from: 'first_name',
  },
  LastName: {
    from: 'last_name',
  },
  FullName: {
    from: 'first_name + " " + last_name',
  },
};

Nested Onjects

Templates can use dot-notation to access nested objects in the source data.

const data = {
  company: {
    name: 'Acme Inc.',
    streetAddress: '123 Main St.',
  },
};

const template = {
  Company: {
    Name: {
      from: 'company.name',
    },
    Address: {
      Street: {
        from: 'company.streetAddress',
      },
    },
  },
};

/**
 * transformedData:
 * {
 *   Company: {
 *     Name: 'Acme Inc.',
 *     Address: {
 *       Street: '123 Main St.',
 *     },
 *   },
 * }
 */

Arrays

Arrays can be transformed by using the [] syntax in the from value.

const data = {
  users: [{ name: 'John Doe' }, { name: 'Jane Doe' }],
};

const template = {
  Users: {
    from: 'users[]',
    values: {
      Name: {
        from: 'name',
      },
    },
  },
};

/**
 * transformedData:
 * {
 *   Users: [
 *     { Name: 'John Doe' },
 *     { Name: 'Jane Doe' },
 *   ],
 * }
 */

Conditional Transformations

Jexlate supports conditional transformations using the if key in the template.

const data = {
  age: 25,
};

const template = {
  IsAdult: {
    from: 'age',
    if: 'age >= 18',
  },
  CanRetire: {
    from: 'age',
    if: 'age >= 65',
  },
};

/**
 * transformedData:
 * {
 *   IsAdult: true,
 *   CanRetire: false,
 * }
 */

Required Properties

Jexlate can be configured to require certain properties to be present in the source data. If a required property is missing, an error will be thrown. This will also throw if a condition or transformation fails or returns false or undefined

const template = {
  FirstName: {
    from: 'first_name',
    required: true,
  },
  LastName: {
    from: 'last_name',
    required: true,
  },
};

const data = {
  first_name: 'John',
};

// Throws an error because 'last_name' is missing
const transformedData = jexlate.transform(data);

// JSON contained in the error object:
// { required: ['last_name'] }

Validation

Jexlate can be configured to validate the transformed data using a JSON schema. If the transformed data does not match the schema, an error will be thrown. Validation uses Jexl syntax, functions, and binary operators.

const template = {
  Age: {
    from: 'age',
    validate: 'age > 25',
  },
};

const data = {
  age: 24,
};

// Throws an error because 'age' is less than 25
const transformedData = jexlate.transform(data);

// JSON contained in the error object:
// { invalid: [{ test: 'age > 25', value: 24 }] }

Static Values

Jexl will attempt to evaluate the from value as an expression. If you want to use a static value, you can wrap the value one of the functions below:

const template = {
  StringValue: {
    from: 'value(string)',
  },
  NumberValue: {
    from: 'number(43)',
  },
  IsTrue: {
    from: 'boolean(true)',
  },
  IsFalse: {
    from: 'boolean(false)',
  },
  NullValue: {
    from: 'null()',
  },
};

Type Coercion

Jexlate will attempt to coerce values to the correct type based on the from value. If you want to force a value to be a specific type, you can use the following functions:

const template = {
  AgeAsString: {
    from: 'age',
    as: 'string', // 'string' | 'number' | 'boolean' | 'json'
  },
};

const data = {
  age: 25,
};

// transformedData: { AgeAsString: '25' }

...

Development

Install Dependencies

yarn

Run Tests

yarn test
# or
yarn test:watch

Build

yarn build
yarn build:types