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

cursorify

v0.0.4

Published

It helps to manipulate, complex and deeply nested objects trees.

Downloads

22

Readme

CircleCI

What is it about?

It helps to manipulate, complex and deeply nested objects trees.

How does it work?

Assume you need to create an object tree like the following:

const obj = {
  a: {
    b: {
      c: [
        {d: 1},
        {
          e: {
            f: {g: 2},
            h: {i: 3}
          },
        },
        {l: 4}
      ]
    }
  }
}

Now assume you have to delete the object {l: 4}, delete the key h and override {g: 2} with {k: 2}.

With cursorify you can create cursors to the parts of the object you will need to modify later:

import {createCursors} from 'cursorify';

const cursors = createCursors();

const obj = {
  a: {
    b: {
      c: [
        {d: 1},
        {
          e: {
            f: cursors.push('cursor1',
              {g: 2}
            ),
            h: cursors.push('cursor2',
              {i: 3}
            )
          },
        },
        cursors.push('cursor3',
          {l: 4}
        )
      ]
    }
  }
}

And then use the cursors to easily mutate the object as you intended to:

import {setByCursor, deleteByCursor} from 'cursorify';

deleteByCursor(obj, cursors['cursor3']);

deleteByCursor(obj, cursors['cursor2']);

setByCursors(obj, cursors['cursor1'], {k:2});

/*

  Now obj value is:

  const obj = {
    a: {
      b: {
        c: [
          {d: 1},
          {
            e: {
              f: {k: 2}
            },
          }
        ]
      }
    }
  }

*/

API

import {createCursors, getByCursor, setByCursor, deleteByCursor} from 'cursorify';
  • createCursors() initializes a new empty list of cursors
  • cursors.push(name, value) pushes a new cursor to the list
  • getByCursor(obj, cursor) retrieve a value from obj using the cursor cursor
  • setByCursor(obj, cursor, newValue) overrides a value in obj using the cursor cursor
  • deleteByCursor(obj, cursor) deletes a value in obj using the cursor cursor
    • if the cursor points to an entry into an array deleteByCursor removes that entry from the array
    • if the cursor points to a value into an object deleteByCursor removes the related key from the object

When is this library useful? (A real use case)

Sometimes object trees are structure in a way that is very difficult to modify or get values form them later on.

A real life example is the webpack configuration object tree, modify, or delete a loader from the configuration after its initialization can be very messy and unreliable.

In general overriding a pre existing webpack configuration can be very painful.

Here an example of how a webpack configuration object looks like:

let config = {
  output: {
    path: BUILD,
    filename: 'bundle.js',
    publicPath: '/'
  },
  plugins: [
    new DefinePlugin(ENV),
    new ProgressBarPlugin({clear: false})
  ],
  resolve: {
    modules: [NODE_MODULES, CWD_NODE_MODULES],
    extensions: ['.js', '.jsx', '.json']
  },
  resolveLoader: {
    modules: [NODE_MODULES, CWD_NODE_MODULES]
  },
  module: {
    rules: [
      {
          test: /\.jsx?$/,
          enforce: "pre",
          use: [
            {loader: 'source-map-loader'}
          ]
      },
      {
        test: /\.html$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]'
        }
      },
      {
        test: /\.s?css$/, // alternative *** : ^(?:(?:[^\.\s]+\.)(?!module))+s?css$
        use: [
          {loader: 'style-loader'},
          {loader: 'css-loader'},
          {loader: 'sass-loader'},
        ]
      },
      {
        test: /\.jsx?$/,
        include: [SRC, TESTS],
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              babelrc: false,
              presets: [
                ['babel-preset-es2015',{ "modules": false }], //{ "modules": false } is needed to make react-hot-loader work
                'babel-preset-flow',
                'babel-preset-stage-0',
                'babel-preset-react'
              ],
              plugins: [
                'react-hot-loader/babel'
              ]
            }
          },
        ],
      },
      {
        test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'url-loader',
        options: {
          limit: '10000',
          mimetype: 'image/svg+xml'
        }
      },
      {
        test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
        loader: 'file-loader'
      }
    ]
  }
};

What if you only need to modify the loader for the svg files after the config object has been created? There is no reliable and easy way for you to mutate only that part of the config object.

With cursorify you can name parts of this configuration object so that it will be very easy for you to modify them later on:

import {createCursors} from 'cursorify';

const cursors = createCursors();

let config = {
   output: {
     path: BUILD,
     filename: 'bundle.js',
     publicPath: '/'
   },
   plugins: [
     new DefinePlugin(ENV),
     new ProgressBarPlugin({clear: false})
   ],
   resolve: {
     modules: [NODE_MODULES, CWD_NODE_MODULES],
     extensions: ['.js', '.jsx', '.json']
   },
   resolveLoader: {
     modules: [NODE_MODULES, CWD_NODE_MODULES]
   },
   module: {
     rules: [
       cursors.push('source-map-loader',{
           test: /\.jsx?$/,
           enforce: "pre",
           use: [
             {loader: 'source-map-loader'}
           ]
       }),
       cursors.push('html-loader',{
         test: /\.html$/,
         loader: 'file-loader',
         options: {
           name: '[name].[ext]'
         }
       }),
       cursors.push('style-loader',{
         test: /\.s?css$/, // alternative *** : ^(?:(?:[^\.\s]+\.)(?!module))+s?css$
         use: [
           {loader: 'style-loader'},
           {loader: 'css-loader'},
           {loader: 'sass-loader'},
         ]
       }),
       cursors.push('javascript-loader',{
         test: /\.jsx?$/,
         include: [SRC, TESTS],
         exclude: /(node_modules|bower_components)/,
         use: [
           {
             loader: 'babel-loader',
             options: {
               babelrc: false,
               presets: [
                 ['babel-preset-es2015',{ "modules": false }], //{ "modules": false } is needed to make react-hot-loader work
                 'babel-preset-flow',
                 'babel-preset-stage-0',
                 'babel-preset-react'
               ],
               plugins: [
                 'react-hot-loader/babel'
               ]
             }
           },
         ],
       }),
       cursors.push('svg-loader',{
         test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
         loader: 'url-loader',
         options: {
           limit: '10000',
           mimetype: 'image/svg+xml'
         }
       }),
       cursors.push('eot-loader',{
         test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
         loader: 'file-loader'
       })
     ]
   }
 };

creating cursors doesn't mutate the original object so that you can keep on using it as usual, the advantage is that changing the loaders now is very simple e.g:

//the following code replaces the existing loader for the svg files completely:

import {setByCursor} from 'cursorify';

setByCursor(config, cursors['svg-loader'], {
  test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
  loaders: [
    'svg-inline-loader', {
      loader: 'image-webpack-loader',
      options: {
        svgo: {
          plugins: [
            {
              removeStyleElement: true
            },
            {
              removeXMLNS: true
            },
            {
              removeDimensions: true
            }
          ]
        }
      }
    }
  ]
});

consider providing references to part of your configuration object using cursorify if you are building a tool exposing a complex and deeply nested configuration object.