@nophin/lib0
v0.0.1
Published
> Monorepo of isomorphic utility functions
Downloads
2
Readme
Lib0
Monorepo of isomorphic utility functions
This library is meant to replace all global JavaScript functions with isomorphic module imports. Additionally, it implements several performance-oriented utility modules. Most noteworthy are the binary encoding/decoding modules [lib0/encoding] / [lib0/decoding], the randomized testing framework [lib0/testing], the fast Pseudo Random Number Generator [lib0/PRNG], the small socket.io alternative [lib0/websocket], and the logging module [lib0/logging] that allows colorized logging in all environments. Lib0 has only one dependency, which is also from the author of lib0. If lib0 is transpiled with rollup or webpack, very little code is produced because of the way that it is written. All exports are pure and are removed by transpilers that support dead code elimination. Here is an example of how dead code elemination and mangling optimizes code from lib0:
// How the code is optimized by transpilers:
// lib0/json.js
export const stringify = JSON.stringify
export const parse = JSON.parse
// index.js
import * as json from 'lib0/json'
export const f = (arg1, arg2) => json.stringify(arg1) + json.stringify(arg2)
// compiled with rollup and uglifyjs:
const s=JSON.stringify,f=(a,b)=>s(a)+s(b)
export {f}
Performance resources
Each function in this library is tested thoroughly and is not deoptimized by v8 (except some logging and comparison functions that can't be implemented without deoptimizations). This library implements its own test suite that is designed for randomized testing and inspecting performance issues.
node --trace-deopt
andnode --trace-opt
- https://youtu.be/IFWulQnM5E0 Good intro video
- https://github.com/thlorenz/v8-perf
- https://github.com/thlorenz/deoptigate - A great tool for investigating deoptimizations
- https://github.com/vhf/v8-bailout-reasons - Description of some deopt messages
Code style
The code style might be a bit different from what you are used to. Stay open. Most of the design choices have been thought through. The purpose of this code style is to create code that is optimized by the compiler and that results in small code bundles when used with common module bundlers. Keep that in mind when reading the library.
- No polymorphism!
- Modules should only export pure functions and constants. This way the module bundler can eliminate dead code. The statement
const x = someCondition ? A : B
cannot be eleminated, because it is tied to a condition. - Use Classes for structuring data. Classes are well supported by jsdoc and are immediately optimized by the compiler. I.e. prefer
class Coord { constructor (x, y) { this.x = x; this.y = y} }
instead of{ x: x, y: y }
, because the compiler needs to be assured that the order of properties does not change.{ y: y, x: x }
has a different hidden class than{ x: x, y: y }
, which will lead to code deoptimizations if their use is alternated. - The user of your module should never create data objects with the
new
keyword. Prefer exporting factory functions likeconst createCoordinate = (x, y) => new Coord(x, y)
. - The use of class methods is discouraged, because method names can't be mangled or removed by dead code elimination.
- The only acceptable use of methods is when two objects implement functionality differently.
E.g.
class Duck { eat () { swallow() } }
andclass Cow { eat () { chew() } }
have the same signature, but implement it differently. - Prefer
const
variable declarations. Uselet
only in loops.const
always leads to easier code. - Keep the potential execution stack small and compartmentalized. Nobody wants to debug spaghetti code.
- Give proper names to your functions and ask yourself if you would know what the function does if you saw it in the execution stack.
- Avoid recursion. There is a stack limit in most browsers and not every recursive function is optimized by the compiler.
- Semicolons are superfluous. Lint with https://standardjs.com/
Using lib0
lib0
contains isomorphic modules that work in nodejs, the browser, and other environments. It exports modules as the commonjs
and the new esm module
format.
If possible,
ESM module
import module from 'lib0/[module]' // automatically resolves to lib0/[module].js
CommonJS
require('lib0/[module]') // automatically resolves to lib0/dist/[module].cjs
Manual
Automatically resolving to commonjs
and esm modules
is implemented using conditional exports which is available in node>=v12
. If support for older versions is required, then it is recommended to define the location of the module manually:
import module from 'lib0/[module].js'
// require('lib0/dist/[module].cjs')