Lightweight, modern utility library for node, browser, and quickjs
- Simple and lightweight
- First-class support for node, browser, and quickjs
- Native ESM
- Treeshakeable by default
- Support for all iterable types
- Support for async iterables and async iterator functions
- Nominal type system
- 0 dependencies
A lightweight, modern utility library. This package is different from other utility libraries in that it avoids duck-typing and implements a simple nominal type system at it's core. This allows for very fast and accurate type checking. Information about types is also cached at start up and used to provide advanced functionalities like iteration support for every iterable type, including async iterables, generators, and async iterator functions.
You can read more about the design philosophy in the docs.
Add lo as a dependency and install via npm
npm install lo@danmasta/lo --saveInstall a specific version
npm install lo@danmasta/lo#semver:^v0.0.0 --saveSee documentation regarding git dependencies here
Import functions
import lo, { each, map } from 'lo';This library also exposes a browser entrypoint, which excludes functions that depend on node specific APIs, and includes some browser specific types. If you use a bundler it should be able to automatically resolve the browser entrypoint. If you want to explicity import it, you can do that too:
import lo from 'lo/browser';This package defines specific collection types for iteration. By default, if it is not a collection type, it is iterated as a one-object collection.
The current collection types are defined as:
ArrayMapSetTypedArray(and subtypesInt8Array,Uint8Array, etc)Array IteratorString IteratorMap IteratorSet IteratorRegExp String IteratorIteratorAsyncIteratorGeneratorAsyncGeneratorURLSearchParamsHeadersFormDataReadableStream
Buffer
NodeListHTMLCollectionDOMTokenListFileListNamedNodeMap
When using iteration functions like: each, map, tap, some, every, filter, remove, reduce, transform, etc, the default mode is to iterate as a collection. This means they will iterate on collections only, and not on the properties of a single object. For iterating the properties of a single object, you can use the functions forIn and forOwn.
This means if you pass a single object instead of a collection type, it will treat the object as a one-object collection and iterate one time:
import { each } from 'lo';
let obj = { a: true, b: false };
each(obj, (val, key) => {
console.log(key, val);
});
// 0 { a: true, b: false }Each iteration function has the following signature and accepts a trailing options object:
method(obj, fn, arg?, { entries, notNil })Functions like take/drop, reduce/transform, and flatMap accept an extra positional argument
The entries option is false by default, and non-collection values are iterated as a one-object collection. If you want to iterate the properties/entries of a single object, you can set the entries option to true:
import { each } from 'lo';
let obj = { a: true, b: false };
each(obj, (val, key) => {
console.log(key, val);
}, { entries: true });
// a true
// b falseAll iteration functions support every iterable type including: Array, Map, Set, Iterator, Generator, etc.
Functions to iterate the properties of individual objects and iterables: forIn, and forOwn.
Functions for iterating collections: each, map, tap, some, every, filter, remove, drop, take, reduce, transform, find, flatMap, and iterate.
The forEach function is an alias for each. It iterates any collection type, including async iterables like Streams, and supports the same options.
All iteration functions can be stopped early by returning the BREAK symbol:
import { map, BREAK } from 'lo';
let arr = [1, 2, 3, 4];
map(arr, val => {
return val % 3 === 0 ? BREAK : val * 2;
});
// [2, 4]A common task during iteration is checking for nil (null or undefined) values. This package has support for filtering nil values for various iteration functions. It will ignore nil values before the iterator function is called. It will also filter return values for functions that return, such as map, some, every, etc. To use, pass the notNil option:
import { map } from 'lo';
let arr = [1, undefined, 2, 3, null];
map(arr, val => {
return val % 3 === 0 ? undefined : val;
}, { notNil: true });
// [1, 2]All iteration functions support nil filtering
Every iteration function also supports both async iterables and async iterator functions. You don't need to do anything special, just use them like normal:
import { map } from 'lo';
async function* list () {
yield 1;
yield 2;
yield 3;
}
await map(list(), async val => {
return val * 2;
});
// [2, 4, 6]QuickJS is a small, embeddable javascript engine written in C that supports the latest ECMAScript specification including modules, async/await, iterators, generators, proxies, etc. It can also be used to compile and package javascript code into standalone executables.
This library includes first-class support for QuickJS. The lo/qjs entrypoint runs natively on the engine with no polyfills or bundling required. It uses the engine's own primitives (qjs:os, qjs:std) to provide things like console, argv, env, and cwd. So you can write tooling and CLIs with Lo and QuickJS, and compile straight to standalone executables.
import { console, argv, env } from 'lo/qjs';The library also ships an optional compatibility layer for running scripts written with node APIs. This isn't meant to be a complete polyfill of node, but it does cover some of the more common modules:
consoleeventsfsmoduleospathprocess
To use them, point your bundler at the Lo polyfills directory for node imports. You can see an example in the docs.
A list of functions and some documentation can be found here
Tests are currently run using mocha and chai. To execute tests run make test. To generate unit test coverage reports run make coverage
If you have any questions feel free to get in touch