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

revive-functions

v0.2.0

Published

JSON reviver, allowing named functions and their parameters to be replaced by their output values

Downloads

1

Readme

A function reviver, as might be used with Javascript's JSON.parse, allowing specified functions to be substituted for their output in a JSON-like structure.

This is useful for allowing set functions to be applied in a JSON-like structure at runtime. For example:

{added: {$add: [2,3]}, unchanged: 4}

...could be converted to...

{added: 5, unchanged: 4}.

An optional data object can be provided, allowing partially applied (curried) functions to use it as their last argument.

Synopsis

npm install revive-functions

import { 
  reviveFunctions,
  reviveFunctionsInObject,
  reviveFunctionsInObjectCurried
} from 'revive-functions'

JSON.parse(jsonString, reviveFunctions(options, data))
reviveFunctionsInObject(options, jsonLikeObject, data)
reviveFunctionsInObjectCurried(options)(jsonLikeObject)(data)
  • options is an object with three keys:

    • functions is an object containing key value pairs of function names and function bodies. Defaults to {}. It can be useful to pass selected functions from libraries such as Ramda, lodash/fp, or date-fns.
    • getFunctionTag is a function which, given a function name (e.g. myfunc), returns a function tag (e.g. $myfunc). Defaults to name=>"$"+name, i.e. using "$" as a function tag.
    • stringifyFirst: Boolean. For reviveFunctionsInObject and reviveFunctionsInObjectCurried, allows explicit determination of whether to run JSON.stringify on the input value, before passing to JSON.parse. Defaults to undefined, meaning this is done on objects only.
    • bindDataToFunction (boolean, default false). Set to true to have data bound to functions, so that it can be referred to as this (in non-arrow functions only).
    • callFunctionsReturnedWithData (boolean, default true). Set to false to NOT use currying with the data argument: i.e. if the function call returns a function, the latter will not be called with the data. Useful if you want to return functions from the reviver without having to use currying.
  • data an optional Javascript value to be passed as a curried last argument to functions

  • jsonLikeObject is a JSON-like Javascript structure, or a string containing valid JSON, in which objects containing function specifications like {$functionname: [param1, param2, ...]} will be substituted with the result of calling the function with the specified parameters (and, optionally, the data object, applied as a last, curried argument). If only one parameter is needed, the array can be omitted (e.g. {$functionname: param1}). Parameters can be generated recursively, from nested function calls (see examples below). The whole JSON-like tree is parsed. Items which are not recognised as function specifications are passed through unchanged, as per normal JSON.parse behaviour.

  • jsonString is a string as would normally be passed to JSON.parse

Return values are as per JSON.parse: generally Javascript data structures, typically objects or arrays. These could be equivalent to valid JSON, but could also include regular JS objects (such as Dates or functions), where these are returned by the functions supplied.

Examples

EXAMPLE 1: Use reviveFunctions directly with JSON.parse (without a data argument):

import { reviveFunctions } from 'revive-functions'

const example1 = JSON.parse(
  `{
    "example":{"$get": ["test", {"test": "example value"} ] }
  }`,
  reviveFunctions({
    functions: {
      get: (prop,x) => x[prop],
    }
  })
)

console.log(JSON.stringify(example1))
// {"example":"example value"}

EXAMPLE 2: Use reviveFunctions directly with JSON.parse with a data argument. Note that getFromData is a curried function, with the data last. When a function has no arguments, use an empty array, either with or without data.

import { reviveFunctions } from 'revive-functions'

const example2 = JSON.parse(
  `{
    "example using data":{"$getFromData": "test"},
    "without data": {"$sayHelloTo": "Steve"},
    "no arguments": {"$firstDay": []},
    "no arguments, with data": {"$firstDayTest": []}
  }`,
  reviveFunctions(
    {
      functions: {
        getFromData: prop => data => data[prop],
        sayHelloTo: name => `Hello ${name}`,
        firstDay: () => new Date(0).toISOString(),      // safest to return string
        firstDayTest: () => ({test}) => new Date(test)  // can also return Date object
      }
    },
    { test: 42 }
  )
)

console.log(JSON.stringify(example2,null,2))
/*
{
  "example using data": 42,
  "without data": "Hello Steve",
  "no arguments": "1970-01-01T00:00:00.000Z",
  "no arguments, with data": "1970-01-01T00:00:00.042Z"
}
*/

EXAMPLE 3: Using reviveFunctionsInObject, which hides the call to JSON.parse. Here functions are injected from ramda and date-fns. Note that functions can be combined in the JSON-like-object ($tomorrow) or in the functions object ($yesterday), possibly using values from data (someWhileAgo). Also note that all non-function-tag elements are passed through unchanged.


import { reviveFunctionsInObject } from 'revive-functions'
import { prop, pipe } from 'ramda'
import { add as dateAdd, format } from 'date-fns/fp/index.js'

const example3 = reviveFunctionsInObject(
  {
    functions: {
      add: (x, y) => x + y,
      get: prop,
      today: () => Date.now(),
      dateAdd,
      format,
      dateOffsetDays: pipe(
        days => dateAdd({ days }, new Date()),
        format('yyyy-MM-dd')
      ),
      negate: x => -x,
    }
  },
  {
    sum: { $add: [2, { $get: 'test' }] },
    twoWaysOfChainingFunctions: {
      tomorrow: { $format: ['yyyy-MM-dd', { $dateAdd: [{ days: 1 }, { $today: [] }] }] },
      yesterday: { $dateOffsetDays: -1 },
      someWhileAgo: { $dateOffsetDays: { $negate: { $get: 'test' } } }
    },
    unchanged: { string: 'other values get passed through', array: [1, 2, 3] }
  },
  { test: 42 }     // data object
)

console.log(JSON.stringify(example3, null, 2))

/*
{
  "sum": 44,
  "twoWaysOfChainingFunctions": {
    "tomorrow": "2022-04-29",
    "yesterday": "2022-04-27",
    "someWhileAgo": "2022-03-17"
  },
  "unchanged": {
    "string": "other values get passed through",
    "array": [1, 2, 3]
  }
}
*/

EXAMPLE 4: Use a curried structure with reviveFunctionsInObjectCurried. Useful for working with an array of objects (collection). Also change the function tag to use "fn::" convention (like AWS).

Specifying stringifyFirst as true (or false) is generally superfluous, but might be useful in some cases, perhaps if the JSON like object might be a plain string.

import { reviveFunctionsInObjectCurried } from 'revive-functions'

const reviver = reviveFunctionsInObjectCurried({
  functions: {
    get: label => x => x[label]
  },
  getFunctionTag: f => 'fn::' + f,
  stringifyFirst: true
}
)({
  something: { 'fn::get': 'test' }
})

const data = [
  { test: 42 },
  { test: 43 }
]

const example4 = data.map(reviver)

console.log(JSON.stringify(example4))
/*
[ 
  { something: 42 }, 
  { something: 43 } 
]
*/

References

See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse for details of JSON.parse and its reviver function parameter.

See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html for an example of this kind of substitution being used in Amazon Web Services (AWS) Cloudformation.

NPM: https://www.npmjs.com/package/revive-functions