JSPM

  • Created
  • Published
  • Downloads 15231
  • Score
    100M100P100Q118164F
  • License pemrouz.mit-license.org

Lean JavaScript Utilities as Micro-libraries

Package Exports

  • utilise
  • utilise/all
  • utilise/append
  • utilise/attr
  • utilise/body
  • utilise/by
  • utilise/chainable
  • utilise/client
  • utilise/clone
  • utilise/colorfill
  • utilise/copy
  • utilise/datum
  • utilise/debounce
  • utilise/def
  • utilise/el
  • utilise/emitterify
  • utilise/err
  • utilise/extend
  • utilise/falsy
  • utilise/file
  • utilise/first
  • utilise/flatten
  • utilise/fn
  • utilise/from
  • utilise/grep
  • utilise/group
  • utilise/has
  • utilise/header
  • utilise/identity
  • utilise/includes
  • utilise/is
  • utilise/key
  • utilise/keys
  • utilise/last
  • utilise/lo
  • utilise/log
  • utilise/matches
  • utilise/noop
  • utilise/not
  • utilise/objectify
  • utilise/once
  • utilise/owner
  • utilise/parse
  • utilise/pause
  • utilise/perf
  • utilise/prepend
  • utilise/promise
  • utilise/proxy
  • utilise/raw
  • utilise/rebind
  • utilise/replace
  • utilise/resourcify
  • utilise/sel
  • utilise/send
  • utilise/str
  • utilise/to
  • utilise/unique
  • utilise/values
  • utilise/via
  • utilise/wrap

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (utilise) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Lean JavaScript Utilities as Micro-libraries

  • This is not another pure functional utility library. These are just a set of common useful patterns that have evolved working with JS.

  • Unfortunately the existing popular libraries (Underscore, Lodash, Ramda, Trine, is, etc) don't result in readable code that is easy to reason about. Instead of putting the data in the first, second, third argument or context variable, this library favours lambda's (partially applied functions) that complement the native Array operations, not compete with them.

For a flavour of what I mean, see:

// clean all js files in current dir, except reserved, logging before deletion
fs.readdirSync(__dirname)
  .filter(includes('js'))
  .filter(not(is.in(core)))
  .map(prepend(__dirname+'/'))
  .map(log('deleting'))
  .map(fs.unlinkSync)
// from mutation observer, redraw all added nodes that are custom elements
mutations
  .map(key('addedNodes'))
  .map(to.arr)
  .reduce(flatten)
  .filter(by('nodeName', includes('-')))
  .map(ripple.draw)
  • Each function is in it's own repo. This library just has an automated link to all of them. This has a few benefits:

    • You can npm i utilise and finally write imports like <org>/<repo> in each file.
    • You don't have to make up esoteric names due to lack of <org>/<repo> in npm.
    • You can use utilise.js to import everything (in your application).
    • You don't have to load a 0.5MB utility library just to use one function.
    • You can be a lot smarter with dead code elimination, even if you include the base file (but not use everything).
  • There is no spraying your code with _. everywhere, since these functions are largely first-class additions to the language that make your code a lot more fluent.

  • These are mostly stable, a few new ones may still need experimenting with to get the API right.

  • Each micro-library is only just a few lines.

  • Each micro-library has 100% tests. See badges below.

  • All browsers (IE >= 9) + node/iojs are supported. Tests are run on real browsers using popper (to be open-sourced soon).

  • There is no polyfilling done here. Recommend using polyfill.io where needed. Some libraries will fail tests (like promise) which wraps native functions like Promise, unless you shim first.

API Reference

Please also refer to the respective test.js for more cases and examples.

Coverage Status Build all

Select all elements based on a CSS selector, piercing shadow boundaries if the browser supports it.

all('li.class')

Narrow search space by also passing in a node

all('li.class', ul)

Coverage Status Build append

Append something to a string

['lorem', 'ipsum']
  .map(append('-foo')) // returns ['lorem-foo', 'ipsum-foo']

Coverage Status Build args

Cherry-pick arguments to pass to function by index. This is useful when iterating a list, and invoking a function which may be confused if passed the index and array arguments.

['lorem.txt', 'ipsum.txt']
  .map(args(0)(fs.createWriteStream))

This would fail without args since the second argument (index) would try to be read as the encoding.

You can pick out more than one argument using an array instead of a number.

Coverage Status Build attr

Get or set value of element attribute.

attr('key')(el)          // returns value for attribute key
attr('key', 'value')(el) // adds [key=value]
attr('key', false)(el)   // removes attribute key

Coverage Status Build body

Get the value of a resource from a ripple instance

body(ripple)('users')

This is used internally to avoid any type-specific convienience defaults. Always returns undefined if the resource does not exist. You should probably use ripple('resource') to get the value of a resource in your application.

Coverage Status Build by

Checks if a property matches a value

users = [ { name: 'foo' }, { name: 'bar' } ]
users
  .filter(by('name', 'foo'))

If the second value parameter is a function, you can run custom logic against each property (default is ==)

nodes
  .filter(by('nodeName', isCustomElement)) // checks if has '-' in tag

It is common to sometimes see filtering empty values via .filter(Boolean). If only one parameter is given, it filters out objects that do not have a value for the property.

nodes
  .filter(by('prop')) // removes object that do not have a value for prop

Coverage Status Build chainable

Takes a function, and returns a new function that evaluates the original function and also returns it again ignoring what the evaluated function returns

ripple('key', value)                // this normally registers a resource, and returns the value
ripple.resource = chainable(ripple) // ripple.resource now registers a resource, but returns ripple again

ripple 
  .resource('foo', 'bar')
  .resource('lorem', 'ipsum')

NB: I think this will be deprecated in favour of the more generic proxy function that is used to alter return values

Coverage Status Build client

Simple variable: Am I on the server or browser?

// browser
client == true

// server
client == false

Useful for isomorphic apps/libs, and also deadcode elimination.

Coverage Status Build clone

Returns a new deep copy of an object

copied = clone(original)

Coverage Status Build colorfill

Adds color to strings, standardising behaviour across server/client

require('colorfill')

'foo'.red  // server, returns string in red
'foo'.red  // client, returns string

Coverage Status Build copy

Copies properties from one object to another

keys(from)
  .filter(not(is.in(private)))
  .map(copy(from, to))

Coverage Status Build datum

Returns the D3 datum for a node. Useful in lists as you cannot d3.select(node).datum()

nodes
  .map(datum)

Coverage Status Build debounce

Returns a debounced function. Specify time in ms, or defaults to 100ms.

debounced = debounce(fn)
// or
debounced = debounce(200)(fn)

Coverage Status Build def

Defines a property, if does not already exist, returning the value

def(object, prop, value[, writable])  // returns value

Coverage Status Build el

Creates a node from a CSS selector

el(div.foo.bar[lorem=ipsum]) // returns <el class="foo bar" lorem="ipsum" />

Coverage Status Build emitterify

Enhance any object with .on, .once and .emit

var o = emitterify({}) 
o.on('event')             // get listeners for event
o.on('event', fn)         // set listener on arbitrary event
o.once('event', fn)       // set listener that is only called once
o.on('event.ns', fn)      // set listener for event.namespace, unique listener per namespace
o.emit('event', payload)  // emit event with optional payload
o.emit('event', [array])  // emit event with optional multiple arguments

Coverage Status Build err

Lightweight scoped version of console.error with a prefix, useful for per module identification

err = err('[module/prefix]')
err('something went wrong!') // [module/prefix] something went wrong

Coverage Status Build extend

Extends an object with properties from another, not overwriting properties by default

to = { foo: 1 }
from = { foo: 2, bar: 3 }
extend(to)(from)  // to == { foo: 1, bar: 3 }

Coverage Status Build falsy

Function that returns false

shouldIContinue = falsy // when executed, returns false

Coverage Status Build file

Reads and returns a file. Server only.

var template = file('template.html')

Coverage Status Build first

Returns first element in array

first(array)  // returns array[0]

Coverage Status Build flatten

Flattens a 2D array

twoD = [[1], [2], [3]]
oneD = twoD.reduce(flatten)  // [1, 2, 3]

Coverage Status Build fn

Turns a function as a string into a real function

foo = 'function(){ console.log("Hi!") }'
foo = fn(foo)   // returns function(){ console.log("foo") }
foo()           // logs out "Hi!"

Coverage Status Build from

Looks up and returns a property from an object. Useful for converting foreign keys to matching records.

users = [
  { name: 'foo', city: 1 }
, { name: 'bar', city: 2 }
, { name: 'baz', city: 1 }
]

cities: { 1: 'London', 2: 'New York', 3: 'Paris' }

// get a unique list of cities users live in
users
  .map(key('city'))
  .map(from(cities))
  .reduce(unique)     // returns [ 'London', 'New York' ]

from.parent returns the value of a property from the parent datum. Useful if you generate a fixed number of columns, whose values depend on the parent.

processes = [ 
  { name: 'chrome', pid: '123', cpu: '50%' } 
, { name: 'safari', pid: '456', cpu: '50%' } 
]

// generate a list of rows, each with three columns
once('tr', processes)
  ('td', ['name', 'pid', 'cpu'])
    .text(from.parent)
<tr>
  <td>chrome</td><td>123</td></td>50%</td>
  <td>safari</td><td>456</td></td>50%</td>
</tr>

In general you should try to pass each element the data it needs and not reach outside of its own scope.

Coverage Status Build group

Grouped logging using groupCollapsed/groupEnd if it exists, or simple start/end demarcation logs using asterisk if not.

group('category', fn)

Coverage Status Build grep

Conditionally executes a function depending on the regex against its arguments. Returns the original unfiltered function. Useful for higher order modules to conditionally filter out logs of many smaller modules unobtrusively.

// filter out all internal logs from ripple (i.e. that don't start with "[ri/")
unfiltered = grep(console, 'log', /^(?!.*\[ri\/)/)

Coverage Status Build has

Checks if object has property using in keyword

has(object, 'prop')

Coverage Status Build header

Extract the value of a header from a ripple resource

header('content-type')(resource) // returns for example 'application/javascript'

Or if a second parameter is set, check for equality

// filter to get all data resources
resources 
  .filter(header('content-type', 'application/data'))

Coverage Status Build identity

Identity function, returns what it is passed in

identity(5) // returns 5

Coverage Status Build includes

Checks if string or array contains the pattern or element (uses indexOf common to strings and arrays)

// filter down to all javascript files
files
  .filter(includes('.js'))

Coverage Status Build inherit

Inherits parent data

once('div', { name: 'sth' })
  ('li', inherit)
    ('span', key('name'))
      .text(String)
<div>
  <li>
    <span>sth</span>
  </li>
</div>

If a number is provided, returns an array of inherited parent datum

once('div', { name: 'sth' })
  ('li', inherit(3))
    ('span', key('name'))
      .text(String)
<div>
  <li>
    <span>sth</span>
    <span>sth</span>
    <span>sth</span>
  </li>
</div>

Coverage Status Build is

Various basic flavours of checking

is(v)(d)      // equality d == v
is.fn         // function
is.str        // string
is.num        // number
is.obj        // object (includes arrays)
is.lit        // object (excludes arrays)
is.bol        // boolean
is.truthy     // truthy
is.falsy      // falsy
is.arr        // array
is.null       // null
is.def        // undefined
is.in(set)(d) // checks if d in set (string, array or object)

Coverage Status Build join

Replace a foreign key property with the full record or a value from the record

// doctors == [ { name: nick, grade: 1 .. }, .. ]
// ripple('grades') == [ { id: 1, name: 'FY1' }, { id: 2, name: 'SHO' }, .. ]

doctors
  .map(join('shift', 'shifts'))
  .map(join('speciality', 'specialities'))
  .map(join('grade', 'grades.name'))
  .map(join('hospital', 'hospitals.location'))

If the second parameter is a string, it uses that as the ripple resource to look in. You can also use a primitive array outside of a ripple context.

Coverage Status Build key

Powerful versatile operator for accessing/setting key(s)

key('name')(d)                        // returns value of property name from object d
key('details.profile.name')(d)        // returns deep property
key('details', 'foo')(d)              // set property
key('details.profile.name', 'foo')(d) // set deep property
key(['name', 'city.name'])(d)         // returns object with selected keys (can mix from any level)
key()(d)                              // returns object root if key undefined

Accessing deep keys returns undefined if a link is missing, which prevents doing things like:

(((d || {}).details || {}).profile || {}).name

Setting a deep key will create any missing keys it needs as it traverses the path.

If the second value parameter is a function, it evaluates it with the data before setting.

To make dates in all records human-readable with moment for example:

orders = [ { .. }, { .. } ]
orders
  .map(key('date', mo.format('MMM Do YY')))

Coverage Status Build keys

Alias for Object.keys

keys({ foo: 1, bar: 2}) // returns ['foo', 'bar']

Coverage Status Build last

Returns the last element in the array

last(array) // returns array[array.length-1]

Links changes in the attribute of one component to the attribute of another

// when a different day is selected in the calendar, the detail page automatically updates
link('events-calendar[selected-day]', 'event-detail[day]')
<events-calendar selected-day="1-1-1970" />
<event-detail day="1-1-1970"/>

Coverage Status Build lo

Lowercase a string

['A', 'B', 'C'].map(lo) // returns ['a', 'b', 'c']

Coverage Status Build log

Lightweight scoped version of console.log with a prefix, useful for per module identification

log = log('[module/prefix]')
log('something went wrong!') // [module/prefix] something went wrong

Returns the input, so it is useful with intermediary logging whilst iterating over a list

list
  .map(op1)
  .map(log)
  .map(op2)

Coverage Status Build mo

Convenience functions working with moment

dates.map(mo)                     // convert to moment object
dates.map(mo.format('Do MMM YY')) // convert to specific format
dates.map(mo.iso)                 // convert to iso date format

Coverage Status Build noop

Function that does nothing

;(fn || noop)()

Coverage Status Build not

Negates the result of a function

numbers
  .filter(not(isEven))
  .filter(not(is('5')))

Coverage Status Build objectify

Converts an array to an object. Uses name property as key by default if none specified

objectify([
  { name: 'foo', value: 1 }
, { name: 'bar', value: 2 }
], 'name')

/* returns 
{ 
  foo: { name: 'foo', value: 1 }
, bar: { name: 'bar', value: 2 }
} 
*/

Coverage Status Build once

Function for building entirely data-driven idempotent components/UI with D3.

once(node)                        // limit to this node
  ('div', { results: [1, 2, 3] }) // creates one div (with the specified datum)
    ('li', key('results'))        // creates three li (with datum 1, 2, 3 respectively)
      ('a', inherit)              // creates anchor in each li (with parent datum)
        .text(String)             // sets the text in anchor to the datum

The first time you call once(node | string) it essentially selects that element and limits the scope of subsequent operations to that.

Subsequents calls generate a D3 join using the syntax (selector, data). The selector can be:

  • A selector string (foo.bar.baz). Classes are fine too and will be added to the final elements created.
  • A real element, which will be replicated.
  • A function, which will be given parent data, in case you wish to output different (custom) elements based on data.

The data is the same as what you would normally use to generate a join (array of items, or function), with some convenient defaults: if you pass an object, number or boolean it'll be converted to a single-valued array, meaning "create one element with this as the datum". If you pass in a falsy, it defaults to empty array "meaning removing all elements of this type".

The return value is essentially a D3 join selection (enter/update/exit), so you can continue to customise using .text, .classed, .attr, etc. You can also access the elements added via .enter and removed via .exit.

There are two further optional arguments you can use (selector, data[, key[, before]]). The key function has the exact same meaning as normal (how to key data), which D3 defaults to by index. The before parameter can be used to force the insertion before a specific element à la .insert(something, before) as opposed to just .append(something).

Coverage Status Build owner

Either window or global dependeing on executing context

// browser
owner == window

// server
owner == global

Coverage Status Build parse

Equivalent to JSON.parse

Coverage Status Build pause

Actually pauses a stream so you can build up a pipeline, pass it around, attach more pipes, before starting the flow. Server only.

var stream = pause(browserify)
  .pipe(via(minify))
  .pipe(via(deadcode))

addMorePipes(stream)

function addMorePipes(stream){
  stream
    .on('end', doSomething)
    .pipe(file)
    .flow()
}

Coverage Status Build perf

Evaluate how long a function takes in milliseconds.

perf(function(){ .. })  // Evaluates the function, logs the time taken, and returns time in ms

Coverage Status Build prepend

Prepend something to a string

['foo', 'bar']
  .map(prepend('hi-'))  // returns ['hi-foo', 'hi-bar']

Coverage Status Build promise

Convenience functions for working with (native) Promises

var p = promise()                 // creates promise with resolve/reject attached
p.resolve('result')
p.reject('something went wrong')

promise(5)                        // creates promise and resolves to value
promise.args(1)('foo', 'bar')     // creates promise that resolves to argument given
promise.sync(1)('foo', 'bar')     // creates thenable that immediately invokes then callback
promise.noop()                    // creates empty promise
promise.null()                    // creates promise that resolves to null

Coverage Status Build proxy

Proxy a function. It is common to use fn.apply(this, arguments) for proxying. This function allows you to do that, but alter the return value and/or context.

proxy(fn, 5)      // returns a function that invokes fn, but then always returns 5
proxy(fn, 5, {})  // same as above, but also changes context variable

Coverage Status Build raw

Select an element based on a CSS selector, piercing shadow boundaries if the browser supports it.

raw('.foo')

Coverage Status Build rebind

D3 rebind function to rebind accessors. See the docs here.

Coverage Status Build replace

Replace a value in a string

['Hi name', 'Bye name']
  .map(replace('name', 'foo'))

Coverage Status Build resourcify

Returns the specified resource from a ripple instance. Returns an object of resources if multiple specified. Returns undefined if one of the resources not present.

resourcify(ripple)('foo')         // returns body of foo
resourcify(ripple)('foo bar')     // returns { foo: foo-body, bar: bar-body }
resourcify(ripple)('foo bar baz') // returns undefined, since no baz resource

Coverage Status Build sall

Convenience function for d3.selectAll. Returns

sall(parent)(selector)

Parent can be selection/string/node. If no parent, selects globally.

Coverage Status Build sel

Convenience function for d3.select.

sel(string)

Coverage Status Build send

Sends a file on an express route. Server only.

app.get('/file', send('./file'))

Coverage Status Build str

Coerces anything into a string

str(5)                // returns '5'
str({ foo: 5 })       // returns '{ foo: 5 }'
str(undefined)        // returns ''
str(function(){ .. }) // returns 'function(){ .. }'

Coverage Status Build to

Converts to a primitive type (only real arrays)

to.arr(NodeList)
to.arr(arguments)

Coverage Status Build values

Converts an object to array

values({ 
  a: { name: 'foo', value: 1 }
, b: { name: 'bar', value: 2 }
})

/* returns
[ 
  { name: 'foo', value: 1 }
, { name: 'bar', value: 2 }
]
*/

Coverage Status Build via

Buffers output to a stream destination. Useful when you need the whole input rather than chunks. Server only.

stream
  .pipe(via(minify))
  .pipe(via(replace))

Coverage Status Build wrap

Wraps something in a function which returns it when executed

wrapped = wrap(5)
wrapped()          // returns 5