@jackens/nnn
v2026.4.15
Published
Jackens’ JavaScript helpers.
Maintainers
Readme
nnn
A collection of Jackens’ JavaScript helper utilities (version: 2026.4.15).
Installation
bun i @jackens/nnnor
npm i @jackens/nnnUsage
import {
c,
csvParse,
fixPlTypography,
h,
hasOwn,
isArray,
isFiniteNumber,
isInteger,
isNumber,
isRecord,
isString,
jsOnParse,
monokai,
nanolightTs,
newEscape,
newNounForm,
newTokenizer,
omit,
pick,
rwd,
s,
svgUse,
uuidV1,
vivify,
} from '@jackens/nnn' // or './node_modules/@jackens/nnn/nnn.js'Exports
CNode: Represents a CSS rule node for thechelper.CRoot: Represents the root CSS object for thechelper.HArgs: Tuple argument type for thehandshelpers.HArgs1: Single argument type for thehandshelpers.c: A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.csvParse: Parses a CSV string into a two-dimensional array of strings.fixPlTypography: Applies Polish-specific typographic corrections to a DOM subtree.h: A lightweight HyperScript-style helper for creating and modifyingHTMLElements (see alsos).hasOwn: Checks whether an object has the specified key as its own property.isArray: Checks whether the argument is an array.isFiniteNumber: Checks whether the argument is a finite number (excludes±InfinityandNaN).isInteger: Checks whether the argument is an integer number.isNumber: Checks whether the argument is of typenumber(includesNaNand±Infinity).isRecord: Checks whether the argument is a plain object (notnulland not an array).isString: Checks whether the argument is a string.jsOnParse: Parses JSON with support for handler-based value transformation (“JavaScript ON”).monokai: A Monokai-inspired color scheme for use with thechelper andnanolightTstokenizer.nanolightTs: A TypeScript/JavaScript syntax highlighting tokenizer built usingnewTokenizer.newEscape: Creates a tag function for escaping interpolated values in template literals.newNounForm: Creates a function that returns the appropriate noun form based on a numeric value usingIntl.PluralRules.newTokenizer: A helper for building simple tokenizers (see alsonanolightTs).omit: Creates a new object excluding the specified keys from the source object.pick: Creates a new object containing only the specified keys from the source object.rwd: A responsive web design helper that generates CSS rules for a grid-like layout.s: A lightweight HyperScript-style helper for creating and modifyingSVGElements (see alsoh).svgUse: Shorthand for creating an SVG element with a<use>child referencing an icon by ID.uuidV1: Generates a UUID v1 (time-based) identifier.vivify: A Proxy-based helper for auto-vivification of nested object structures.
CNode
type CNode = {
[attributeOrSelector: string]: string | number | CNode | undefined;
};Represents a CSS rule node for the c helper.
Keys are CSS properties or nested selectors.
CRoot
type CRoot = Record<PropertyKey, CNode>;Represents the root CSS object for the c helper.
Keys are top-level selectors or at-rules.
HArgs
type HArgs = [string | Node, ...HArgs1[]];Tuple argument type for the h and s
helpers.
HArgs1
type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string | number | HArgs;Single argument type for the h and s
helpers.
c
const c: (root: CRoot, splitter?: string) => string;A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.
root
An object describing CSS rules. Keys are selectors or at-rules; values are either CSS property values or nested rule objects.
splitter
A delimiter used to create unique keys (default: '$$').
The substring from splitter to the end of a key
is ignored (e.g., src$$1 → src).
Returns
A CSS string representing the compiled rules.
Remarks
- Keys whose values are primitives (
string|number) are treated as CSS properties. - In property keys, uppercase letters
become lowercase with a
-prefix (e.g.,fontFamily→font-family); underscores become hyphens (e.g.,font_family→font-family). - Comma-separated selector keys
expand into multiple selectors
(e.g.,
{ div: { '.a,.b': { margin: 1 } } }→div.a,div.b{margin:1}). - Top-level keys starting with
@(at-rules) are not concatenated with child selectors.
Usage Examples
const actual1 = c(
{
a: {
color: 'red',
margin: 1,
'.c': { margin: 2, padding: 2 },
padding: 1,
},
},
)
const expected1 = `
a{
color:red;
margin:1
}
a.c{
margin:2;
padding:2
}
a{
padding:1
}`.replace(/\n\s*/g, '')
expect(actual1).to.equal(expected1)
const actual2 = c(
{
a: {
'.b': {
color: 'red',
margin: 1,
'.c': { margin: 2, padding: 2 },
padding: 1,
},
},
},
)
const expected2 = `
a.b{
color:red;
margin:1
}
a.b.c{
margin:2;
padding:2
}
a.b{
padding:1
}`.replace(/\n\s*/g, '')
expect(actual2).to.equal(expected2)
const actual3 = c(
{
'@font-face$$1': {
fontFamily: 'Jackens',
src$$1: 'url(otf/jackens.otf)',
src$$2: "url(otf/jackens.otf) format('opentype')," +
"url(svg/jackens.svg) format('svg')",
font_weight: 'normal',
'font-style': 'normal',
},
'@font-face$$2': {
font_family: 'C64',
src: 'url(fonts/C64_Pro_Mono-STYLE.woff)',
},
'@keyframes spin': {
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' },
},
div: {
border: 'solid red 1px',
'.c1': { 'background-color': '#000' },
' .c1': { background_color: 'black' },
'.c2': { backgroundColor: 'rgb(0,0,0)' },
},
'@media(min-width:200px)': {
div: { margin: 0, padding: 0 },
span: { color: '#000' },
},
},
)
const expected3 = `
@font-face{
font-family:Jackens;
src:url(otf/jackens.otf);
src:url(otf/jackens.otf) format('opentype'),url(svg/jackens.svg) format('svg');
font-weight:normal;
font-style:normal
}
@font-face{
font-family:C64;
src:url(fonts/C64_Pro_Mono-STYLE.woff)
}
@keyframes spin{
0%{
transform:rotate(0deg)
}
100%{
transform:rotate(360deg)
}
}
div{
border:solid red 1px
}
div.c1{
background-color:#000
}
div .c1{
background-color:black
}
div.c2{
background-color:rgb(0,0,0)
}
@media(min-width:200px){
div{
margin:0;
padding:0
}
span{
color:#000
}
}`.replace(/\n\s*/g, '')
expect(actual3).to.equal(expected3)
const actual4 = c(
{
a: {
'.b,.c': {
margin: 1,
'.d': {
margin: 2,
},
},
},
},
)
const expected4 = `
a.b,a.c{
margin:1
}
a.b.d,a.c.d{
margin:2
}`.replace(/\n\s*/g, '')
expect(actual4).to.equal(expected4)
const actual5 = c(
{
'.b,.c': {
margin: 1,
'.d': {
margin: 2,
},
},
},
)
const expected5 = `
.b,.c{
margin:1
}
.b.d,.c.d{
margin:2
}`.replace(/\n\s*/g, '')
expect(actual5).to.equal(expected5)
const actual6 = c(
{
'.a,.b': {
margin: 1,
'.c,.d': {
margin: 2,
},
},
},
)
const expected6 = `
.a,.b{
margin:1
}
.a.c,.a.d,.b.c,.b.d{
margin:2
}`.replace(/\n\s*/g, '')
expect(actual6).to.equal(expected6)csvParse
const csvParse: (csv: string, separator?: string) => string[][];Parses a CSV string into a two-dimensional array of strings.
Supports quoted fields with escaped double quotes ("").
Carriage returns are normalized.
csv
The CSV string to parse.
separator
The field delimiter (default: ',').
Returns
A 2D array where each inner array represents a row of fields.
Usage Examples
const text = `"aaa
""aaa""
aaa",bbb, "ccc,ccc"
"xxx,xxx", "yyy
yyy",zzz
42 , "42" , 17
`
expect(csvParse(text)).to.deep.equal(
[
['aaa\n"aaa"\naaa', 'bbb', 'ccc,ccc'],
['xxx,xxx', 'yyy\nyyy', 'zzz'],
[' 42 ', '42', ' 17'],
],
)fixPlTypography
const fixPlTypography: (node: Node) => void;Applies Polish-specific typographic corrections to a DOM subtree.
This function prevents orphaned conjunctions (single-letter words like “a”, “i”, “o”, “u”, “w”, “z”) from appearing at the end of a line by wrapping them with the following word in a non-breaking span. It also inserts zero-width spaces after slashes and dots to allow line breaks.
node
The root DOM node to process.
All descendant text nodes are corrected recursively,
except those inside IFRAME, NOSCRIPT, PRE,
SCRIPT, STYLE, or TEXTAREA elements.
Usage Examples
const p = h('p',
'Pchnąć w tę łódź jeża lub ośm skrzyń fig ' +
'(zob. https://pl.wikipedia.org/wiki/Pangram).',
['br'],
['b', 'Zażółć gęślą jaźń.'],
)
fixPlTypography(p)
expect(p.innerHTML).to.deep.equal(
'Pchnąć ' +
'<span style="white-space:nowrap">w </span>' +
'tę łódź jeża lub ośm skrzyń fig ' +
'(zob. https://\u200Bpl.\u200Bwikipedia.\u200Borg/' +
'\u200Bwiki/\u200BPangram).' +
'<br>' +
'<b>Zażółć gęślą jaźń.</b>')h
const h: {
<T extends keyof HTMLElementTagNameMap>(tag: T, ...args1: HArgs1[]): HTMLElementTagNameMap[T];
<N extends Node>(node: N, ...args1: HArgs1[]): N;
(tagOrNode: string | Node, ...args1: HArgs1[]): Node;
};A lightweight HyperScript-style helper
for creating and modifying HTMLElements
(see also s).
tagOrNode
If a string, it is treated as the tag name
for a new element.
If a Node, that node is modified in place.
args
Additional arguments processed as follows:
Object: maps attributes/properties. Keys starting with$set element properties (without the$prefix); other keys set attributes viasetAttribute. A value offalseremoves the attribute.null/undefined: ignored.Node: appended as a child.string/number: converted to aTextnode and appended.HArgsarray: processed recursively.
Returns
The created or modified HTMLElement.
Usage Examples
const b = h('b')
expect(b.outerHTML).to.equal('<b></b>')
const i = h('i', 'text')
h(b, i)
expect(i.outerHTML).to.equal('<i>text</i>')
expect(b.outerHTML).to.equal('<b><i>text</i></b>')
h(i, { $className: 'some class' })
expect(i.outerHTML)
.to.equal('<i class="some class">text</i>')
expect(b.outerHTML)
.to.equal('<b><i class="some class">text</i></b>')
expect(h('span', 'text').outerHTML)
.to.equal('<span>text</span>')
expect(h('span', { $innerText: 'text' }).outerHTML)
.to.equal('<span>text</span>')
expect(h('span', '42').outerHTML)
.to.equal('<span>42</span>')
expect(h('span', 42).outerHTML).to.equal('<span>42</span>')
expect(h('div', { style: 'margin:0;padding:0' }).outerHTML)
.to.equal('<div style="margin:0;padding:0"></div>')
expect(h('div', { $style: 'margin:0;padding:0' }).outerHTML)
.to.equal(
'<div style="margin: 0px; padding: 0px;"></div>',
)
expect(
h(
'div',
{ $style: { margin: 0, padding: 0 } },
).outerHTML,
).to.equal('<div style="margin: 0px; padding: 0px;"></div>')
const input1 = h('input', { value: 42 })
const input2 = h('input', { $value: '42' })
expect(input1.value).to.equal('42')
expect(input2.value).to.equal('42')
expect(input1.outerHTML).to.equal('<input value="42">')
expect(input2.outerHTML).to.equal('<input>')
const checkbox1 = h(
'input',
{ type: 'checkbox', checked: true },
)
const checkbox2 = h(
'input',
{ type: 'checkbox', $checked: true },
)
expect(checkbox1.checked).to.be.true
expect(checkbox2.checked).to.be.true
expect(checkbox1.outerHTML)
.to.equal('<input type="checkbox" checked="">')
expect(checkbox2.outerHTML)
.to.equal('<input type="checkbox">')
const div = h('div')
expect(div.key).to.be.undefined
h(div, { $key: { one: 1 } })
expect(div.key).to.deep.equal({ one: 1 })
h(div, { $key: { two: 2 } })
expect(div.key).to.deep.equal({ one: 1, two: 2 })
const elemWithClass = h('div', { class: 'test' })
expect(elemWithClass.getAttribute('class')).to.equal('test')
const elemWithText = h('div', 'initial')
h(elemWithText, ' more')
expect(elemWithText.outerHTML)
.to.equal('<div>initial more</div>')
const elemWithNested = h('div',
['span', 'hello'],
['b', 'world'])
expect(elemWithNested.outerHTML)
.to.equal('<div><span>hello</span><b>world</b></div>')hasOwn
const hasOwn: (ref: unknown, key: unknown) => boolean;Checks whether an object has the specified key as its own property.
A null-safe wrapper around Object.hasOwn.
ref
The object to check.
key
The property key to look for.
Returns
true if ref is not nullish
and has key as an own property, false otherwise.
Usage Examples
const obj = { 42: null, null: 'k,e,y', 'k,e,y': 42 }
expect(42 in obj).to.be.true
expect(hasOwn(obj, 42)).to.be.true
expect('42' in obj).to.be.true
expect(hasOwn(obj, '42')).to.be.true
expect('null' in obj).to.be.true
expect(hasOwn(obj, 'null')).to.be.true
expect(null in obj).to.be.true
expect(hasOwn(obj, null)).to.be.true
expect('k,e,y' in obj).to.be.true
expect(hasOwn(obj, 'k,e,y')).to.be.true
expect(['k', 'e', 'y'] in obj).to.be.true
expect(hasOwn(obj, ['k', 'e', 'y'])).to.be.true
expect('toString' in obj).to.be.true
expect(hasOwn(obj, 'toString')).to.be.false
expect(() => 'key' in null).to.throw
expect(hasOwn(null, 'key')).to.be.false
expect(() => 'key' in undefined).to.throw
expect(hasOwn(undefined, 'key')).to.be.falseisArray
const isArray: (arg: unknown) => arg is unknown[];Checks whether the argument is an array.
arg
The value to check.
Returns
true if arg is an array, false otherwise.
Usage Examples
expect(isArray([])).to.be.true
const fakeArray1 = Object.create({ constructor: Array })
expect(isArray(fakeArray1)).to.be.false
const fakeArray2 =
Object.create({ [Symbol.toStringTag]: Array.name })
expect(isArray(fakeArray2)).to.be.falseisFiniteNumber
const isFiniteNumber: (arg: unknown) => arg is number;Checks whether the argument is a finite number
(excludes ±Infinity and NaN).
arg
The value to check.
Returns
true if arg is a finite number, false otherwise.
Usage Examples
expect(isFiniteNumber(42)).to.be.true
expect(isFiniteNumber(Number(42))).to.be.true
expect(isFiniteNumber(new Number(42))).to.be.false
expect(isFiniteNumber(NaN)).to.be.false
expect(isFiniteNumber(Infinity)).to.be.falseisInteger
const isInteger: (arg: unknown) => arg is number;Checks whether the argument is an integer number.
arg
The value to check.
Returns
true if arg is an integer number, false otherwise.
Usage Examples
expect(isInteger(42)).to.be.true
expect(isInteger(42.00000000000001)).to.be.false
expect(isInteger(42.000000000000001)).to.be.true
// ^because of loss of precision
expect(isInteger(Number(42))).to.be.true
expect(isInteger(new Number(42))).to.be.false
expect(isInteger(NaN)).to.be.false
expect(isInteger(Infinity)).to.be.falseisNumber
const isNumber: (arg: unknown) => arg is number;Checks whether the argument is of type number
(includes NaN and ±Infinity).
arg
The value to check.
Returns
true if typeof arg === 'number', false otherwise.
Usage Examples
expect(isNumber(42)).to.be.true
expect(isNumber(Number(42))).to.be.true
expect(isNumber(new Number(42))).to.be.false
expect(isNumber(NaN)).to.be.true
expect(isNumber(Infinity)).to.be.trueisRecord
const isRecord: (arg: unknown) => arg is Record<PropertyKey, unknown>;Checks whether the argument is a plain object
(not null and not an array).
arg
The value to check.
Returns
true if arg is a plain object, false otherwise.
Usage Examples
expect(isRecord({})).to.be.true
expect(isRecord([])).to.be.false
expect(isRecord(Object.create(null))).to.be.true
const fakeNumber1 = Object.create({ constructor: Number })
expect(isRecord(fakeNumber1)).to.be.true
const fakeNumber2 =
Object.create({ [Symbol.toStringTag]: Number.name })
expect(isRecord(fakeNumber2)).to.be.true
expect(isRecord(new Number(42))).to.be.true
expect(isRecord(new String('42'))).to.be.true
class FooBar { }
expect(isRecord(new FooBar())).to.be.trueisString
const isString: (arg: unknown) => arg is string;Checks whether the argument is a string.
arg
The value to check.
Returns
true if typeof arg === 'string', false otherwise.
Usage Examples
expect(isString('42')).to.be.true
expect(isString(String('42'))).to.be.true
expect(isString(new String('42'))).to.be.falsejsOnParse
const jsOnParse: (handlers: Record<PropertyKey, Function>, text: string) => any;Parses JSON with support for handler-based value transformation (“JavaScript ON”).
Objects with exactly one property
whose key exists in handlers
and whose value is an array
are replaced by invoking the corresponding handler
with the array elements as arguments.
handlers
An object mapping handler names to functions.
text
The JSON string to parse.
Returns
The parsed value with handler substitutions applied.
Usage Examples
const handlers = {
$add: (a: number, b: number) => a + b,
$hello: (name: string) => `Hello ${name}!`,
$foo: () => 'bar',
}
const actual1 = jsOnParse(handlers, `[
{
"$add": [1, 2]
},
{
"$hello": ["World"]
},
{
"nested": {
"$hello": ["nested World"]
},
"one": 1,
"two": 2
},
{
"$foo": []
},
{
"$foo": ["The parent object does not have exactly one property!"],
"one": 1,
"two": 2
}
]`)
expect(actual1).to.deep.equal(
[
3,
'Hello World!',
{
nested: 'Hello nested World!',
one: 1,
two: 2,
},
'bar',
{
$foo: [
'The parent object does not have ' +
'exactly one property!',
],
one: 1,
two: 2,
},
],
)
const actual2 = jsOnParse(
{ $notFunc: 'handler not being a function' } as any,
'{"$notFunc": [1, 2]}',
)
expect(actual2).to.deep.equal({ $notFunc: [1, 2] })
const actual3 = jsOnParse(
handlers,
'{"$unknown_handler_key": [1, 2]}',
)
expect(actual3)
.to.deep.equal({ $unknown_handler_key: [1, 2] })
const actual4 = jsOnParse(
handlers,
'{"$add": {"not": "array"}}',
)
expect(actual4)
.to.deep.equal({ $add: { not: 'array' } })monokai
const monokai: CRoot;A Monokai-inspired color scheme
for use with the c helper
and nanolightTs tokenizer.
nanolightTs
const nanolightTs: (code: string) => HArgs1[];A TypeScript/JavaScript syntax highlighting tokenizer
built using newTokenizer.
code
The source code string to tokenize.
Returns
An array of HArgs1 elements
suitable for rendering with h.
Usage Examples
const codeJs =
'const answerToLifeTheUniverseAndEverything = ' +
"{ 42: 42 }['42'] /* 42 */"
expect(nanolightTs(codeJs)).to.deep.equal(
[
['span', { class: 'keyword-1' }, 'const'],
' ',
['span',
{ class: 'identifier-4' },
'answerToLifeTheUniverseAndEverything',
],
' ',
['span', { class: 'operator' }, '='],
' ',
['span', { class: 'punctuation' }, '{'],
' ',
['span', { class: 'number' }, '42'],
['span', { class: 'operator' }, ':'],
' ',
['span', { class: 'number' }, '42'],
' ',
['span', { class: 'punctuation' }, '}'],
['span', { class: 'punctuation' }, '['],
['span', { class: 'string' }, "'42'"],
['span', { class: 'punctuation' }, ']'],
' ',
['span', { class: 'comment' }, '/* 42 */'],
],
)newEscape
const newEscape: (escapeFn: (value: any) => string) => (template: TemplateStringsArray, ...values: unknown[]) => string;Creates a tag function for escaping interpolated values in template literals.
escapeFn
A function that takes a value and returns its escaped string representation.
Returns
A tag function that escapes interpolated values using the provided escape map.
Usage Examples
const escapeFn = (value: any): string =>
isArray(value)
? value.map(escapeFn).join(', ')
: value === true || value === false
? `b'${+value}'`
: value instanceof Date
? `'${value.toISOString().replace(/^(.+)T(.+)\..*$/, '$1 $2')}'`
: isFiniteNumber(value)
? `${value}`
: isString(value)
? `'${value.replace(/'/g, "''")}'`
: 'NULL'
const sql = newEscape(escapeFn)
const actual = sql`
SELECT *
FROM table_name
WHERE column_name IN (
${[
true,
null,
undefined,
NaN,
Infinity,
42,
'42',
"4'2",
/42/,
new Date(323325000000),
]}
)`
const expected = `
SELECT *
FROM table_name
WHERE column_name IN (
b'1', NULL, NULL, NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00'
)`
expect(actual).to.equal(expected)newNounForm
const newNounForm: (locale: string, forms: Partial<Record<Intl.LDMLPluralRule, string>>) => (value: number) => string;Creates a function that
returns the appropriate noun form
based on a numeric value using Intl.PluralRules.
Different languages have different plural rules.
The Intl.PluralRules API provides locale-aware
plural category selection.
Possible categories are:
zero: for zero items (used in some languages like Arabic, Latvian)one: for singular (e.g., 1 item)two: for dual (used in some languages like Arabic, Hebrew)few: for small plurals (e.g., 2-4 in Polish)many: for larger plurals (e.g., 5-21 in Polish)other: fallback category (used by all languages)
locale
A BCP 47 language tag (e.g., pl, en).
forms
An object mapping plural categories to noun forms.
Not all categories need to be provided;
if a category is missing,
the function falls back to other,
then to an empty string.
Returns
A function that takes a numeric value and returns the appropriate noun form.
Usage Examples
const auto = newNounForm(
'pl',
{ one: 'auto', few: 'auta', other: 'aut' },
)
expect(auto(0)).to.equal('aut')
expect(auto(1)).to.equal('auto')
expect(auto(17)).to.equal('aut')
expect(auto(42)).to.equal('auta')
const car = newNounForm('en', { one: 'car', other: 'cars' })
expect(car(0)).to.equal('cars')
expect(car(1)).to.equal('car')
expect(car(17)).to.equal('cars')
expect(car(42)).to.equal('cars')
const empty = newNounForm('en', {})
expect(empty(0)).to.equal('')
expect(empty(1)).to.equal('')
expect(empty(17)).to.equal('')
expect(empty(42)).to.equal('')newTokenizer
const newTokenizer: <M, T>(decorator: (chunk: string, metadata?: M) => T, ...specs: [M, string | RegExp][]) => (code: string) => T[];A helper for building simple tokenizers
(see also nanolightTs).
decorator
A function that wraps each matched chunk.
It receives the matched text (chunk)
and optionally the metadata
associated with the pattern that produced the match.
For unmatched text between patterns,
metadata is undefined.
specs
An array of tuples [metadata, pattern] where:
metadata: arbitrary data (e.g., a CSS class name) passed todecoratorwhen the pattern matches.pattern: astringorRegExpto match against the input.
Returns
A tokenizer function that accepts a code string and returns an array of decorated tokens.
Remarks
- Matches starting at an earlier position take precedence.
- Among matches at the same position, the longer one wins.
- Among matches of the same position and length, the one defined earlier wins.
Usage Examples
const tokenizer1 = newTokenizer(
(chunk, metadata) => ({ chunk, metadata }),
['keyword', /\b(if|else|for)\b/],
['string', /"[^"]*"/],
)
const result1 = tokenizer1('if "hello" else "world"')
expect(result1).to.deep.equal(
[
{ chunk: 'if', metadata: 'keyword' },
{ chunk: ' ', metadata: undefined },
{ chunk: '"hello"', metadata: 'string' },
{ chunk: ' ', metadata: undefined },
{ chunk: 'else', metadata: 'keyword' },
{ chunk: ' ', metadata: undefined },
{ chunk: '"world"', metadata: 'string' },
],
)
const tokenizer2 = newTokenizer(
(chunk, metadata) => `${metadata}:${chunk}`,
['tag', 'BEGIN'],
['end', 'END'],
)
const result2 = tokenizer2('aBEGINbENDc')
expect(result2).to.deep.equal(
[
'undefined:a',
'tag:BEGIN',
'undefined:b',
'end:END',
'undefined:c',
],
)
const tokenizer3 = newTokenizer(
(chunk) => chunk,
['test', /test/],
)
const result3 = tokenizer3('')
expect(result3).to.deep.equal([])
const tokenizer4 = newTokenizer(
(chunk, metadata) => ({ chunk, metadata }),
['start', /^test/],
)
const result4 = tokenizer4('test here')
expect(result4).to.deep.equal(
[
{ chunk: 'test', metadata: 'start' },
{ chunk: ' here', metadata: undefined },
],
)
const tokenizer5 = newTokenizer(
(chunk, metadata) => metadata,
['later', /x/],
['earlier', /y/],
)
const result5 = tokenizer5('yx')
expect(result5).to.deep.equal(['earlier', 'later'])
const tokenizer6 = newTokenizer(
(chunk) => chunk,
['short', 'a'],
['long', 'abc'],
)
const result6 = tokenizer6('abc')
expect(result6).to.deep.equal(['abc'])
const tokenizer7 = newTokenizer(
(chunk) => chunk,
['empty', ''],
['word', /\w+/],
)
const result7 = tokenizer7('hello')
expect(result7).to.deep.equal(['hello'])
const tokenizer8 = newTokenizer(
(chunk) => chunk,
['test', /xyz/],
)
const result8 = tokenizer8('abc')
expect(result8).to.deep.equal(['abc'])
const tokenizer9 = newTokenizer(
(_chunk, metadata) => metadata,
['a', 'a'],
['b', 'b'],
)
const result9 = tokenizer9('aabb')
expect(result9).to.deep.equal(['a', 'a', 'b', 'b'])omit
const omit: <T, K extends keyof T>(ref: T, keys: unknown[]) => Omit<T, K>;Creates a new object excluding the specified keys from the source object.
A runtime equivalent
of TypeScript’s Omit<T, K> utility type.
See also pick.
ref
The source object.
keys
An array of keys to exclude from the result.
Returns
A new object without the specified keys.
Usage Examples
const ref = { a: 42, b: '42', c: 17 }
expect(omit(ref, ['c'])).to.deep.equal({ a: 42, b: '42' })pick
const pick: <T, K extends keyof T>(ref: T, keys: unknown[]) => Pick<T, K>;Creates a new object containing only the specified keys from the source object.
A runtime equivalent
of TypeScript’s Pick<T, K> utility type.
See also omit.
ref
The source object.
keys
An array of keys to include in the result.
Returns
A new object with only the specified keys.
Usage Examples
const ref = { a: 42, b: '42', c: 17 }
expect(pick(ref, ['a', 'b'])).to.deep.equal(
{ a: 42, b: '42' },
)rwd
const rwd: (root: CRoot, selector: string, cellWidthPx: number, cellHeightPx: number, ...specs: [number, number?, number?][]) => void;A responsive web design helper that generates CSS rules for a grid-like layout.
root
The CSS root object to populate (see c).
selector
The CSS selector for the grid item.
cellWidthPx
The base cell width in pixels.
cellHeightPx
The base cell height in pixels.
specs
An array of breakpoint specifications, each a tuple of:
maxWidth: maximum number of cells per row (defines the viewport breakpoint).width(optional, default1): number of horizontal cells the element spans.height(optional, default1): number of vertical cells the element spans.
Usage Examples
const style: CRoot = {
body: {
margin: 0,
},
'.r6': {
border: 'solid red 1px',
'.no-border': {
border: 'none',
},
},
}
rwd(style, '.r6', 200, 50, [6], [3], [1, 1, 2])
expect(style).to.deep.equal(
{
body: {
margin: 0,
},
'.r6': {
border: 'solid red 1px',
'.no-border': {
border: 'none',
},
boxSizing: 'border-box',
display: 'block',
float: 'left',
width: '100%',
height: '100px',
},
'@media(min-width:600px)': {
'.r6': {
width: 'calc(100% / 3)',
height: '50px',
},
},
'@media(min-width:1200px)': {
'.r6': {
width: 'calc(50% / 3)',
height: '50px',
},
},
},
)s
const s: {
<T extends keyof SVGElementTagNameMap>(tag: T, ...args1: HArgs1[]): SVGElementTagNameMap[T];
<N extends Node>(node: N, ...args1: HArgs1[]): N;
(tagOrNode: string | Node, ...args1: HArgs1[]): Node;
};A lightweight HyperScript-style helper
for creating and modifying SVGElements
(see also h).
tagOrNode
If a string, it is treated as the tag name
for a new element.
If a Node, that node is modified in place.
args
Additional arguments processed as follows:
Object: maps attributes/properties. Keys starting with$set element properties (without the$prefix); other keys set attributes viasetAttributeNS. A value offalseremoves the attribute.null/undefined: ignored.Node: appended as a child.string/number: converted to aTextnode and appended.HArgsarray: processed recursively.
Returns
The created or modified SVGElement.
Usage Examples
const XLINK_NS = 'http://www.w3.org/1999/xlink'
const svg1 = s('svg', { 'xlink:href': true })
expect(svg1.getAttributeNS(XLINK_NS, 'href')).to.equal('')
const svg2 = s('svg', { 'xlink:href': false })
expect(svg2.getAttributeNS(XLINK_NS, 'href')).to.be.null
const svg3 = s(
'svg',
{ 'xlink:href': 'http://example.com' },
)
expect(svg3.getAttributeNS(XLINK_NS, 'href'))
.to.equal('http://example.com')
const svg4 = s('svg', { 'xlink:title': 42 })
expect(svg4.getAttributeNS(XLINK_NS, 'title'))
.to.equal('42')svgUse
const svgUse: (id: string, ...args: HArgs1[]) => SVGSVGElement;Shorthand for creating an SVG element
with a <use> child referencing an icon by ID.
Equivalent to:
s('svg', ['use', { 'xlink:href': '#' + id }], ...args).
id
The ID of the symbol to reference
(without the # prefix).
args
Additional arguments passed to the outer <svg> element.
Returns
An SVGSVGElement containing a <use> element.
Usage Examples
const XLINK_NS = 'http://www.w3.org/1999/xlink'
const svg = svgUse('icon-home')
expect(svg.children.length).to.equal(1)
const useElement = svg.children[0]
expect(useElement.getAttributeNS(XLINK_NS, 'href'))
.to.equal('#icon-home')
const svgWithViewBox =
svgUse('icon-star', { viewBox: '0 0 24 24' })
expect(svgWithViewBox.getAttribute('viewBox'))
.to.equal('0 0 24 24')
const useViewBox = svgWithViewBox.children[0]
expect(useViewBox.getAttributeNS(XLINK_NS, 'href'))
.to.equal('#icon-star')
const svgWithClass =
svgUse('icon-menu', { class: 'icon-btn' })
expect(svgWithClass.getAttribute('class'))
.to.equal('icon-btn')
expect(svgWithClass.children.length).to.equal(1)
const useClass = svgWithClass.children[0]
expect(useClass.getAttributeNS(XLINK_NS, 'href'))
.to.equal('#icon-menu')
const svgWithMultipleAttrs = svgUse(
'icon-settings',
{ width: 24, height: 24, class: 'icon' },
)
expect(svgWithMultipleAttrs.getAttribute('width'))
.to.equal('24')
expect(svgWithMultipleAttrs.getAttribute('height'))
.to.equal('24')
expect(svgWithMultipleAttrs.getAttribute('class'))
.to.equal('icon')
const useMultiple = svgWithMultipleAttrs.children[0]
expect(useMultiple.getAttributeNS(XLINK_NS, 'href'))
.to.equal('#icon-settings')uuidV1
const uuidV1: (date?: Date, node?: string) => string;Generates a UUID v1 (time-based) identifier.
date
The date to use for the timestamp portion (default: current date/time).
node
A hexadecimal string for the node portion
(default: random).
Must match /^[0-9a-f]*$/;
it is trimmed to the last 12 characters
and left-padded with zeros if shorter.
Returns
A UUID v1 string in the standard format
xxxxxxxx-xxxx-1xxx-xxxx-xxxxxxxxxxxx.
Usage Examples
for (let i = 1; i <= 22136; ++i) {
const uuid = uuidV1()
if (i === 1) {
expect(uuid.split('-')[3]).to.equal('8001')
}
if (i === 4095) {
expect(uuid.split('-')[3]).to.equal('8fff')
}
if (i === 4096) {
expect(uuid.split('-')[3]).to.equal('9000')
}
if (i === 9029) {
expect(uuid.split('-')[3]).to.equal('a345')
}
if (i === 13398) {
expect(uuid.split('-')[3]).to.equal('b456')
}
if (i === 16384) {
expect(uuid.split('-')[3]).to.equal('8000')
}
if (i === 17767) {
expect(uuid.split('-')[3]).to.equal('8567')
}
}
expect(uuidV1(new Date(), '000123456789abc').split('-')[4])
.to.equal('123456789abc')
expect(uuidV1(new Date(), '123456789').split('-')[4])
.to.equal('000123456789')
expect(
uuidV1(new Date(323325000000))
.startsWith('c1399400-9a71-11bd'),
)
.to.be.truevivify
const vivify: (ref: unknown) => any;A Proxy-based helper for auto-vivification of nested object structures.
Accessing, assigning, or deleting any nested property
on the returned proxy
automatically creates intermediate objects
(or arrays for numeric-string keys matching
^(0|[1-9]\d*)$) as needed,
allowing deep operations without explicit null checks.
Intermediates of the last level
in a get-only property chain are NOT auto-created.
For example, vivify(ref).one.two will create
ref.one as {}, but will NOT create ref.one.two.
Only when a deeper access, assignment, or deletion occurs
(e.g. delete vivify(ref).one.two.three
or vivify(ref).one.two.three = 4)
will ref.one.two be materialized.
When traversal reaches a primitive value,
no auto-creation happens;
the primitive’s own property is returned instead
(e.g. accessing .toString.name on a number yields
'toString' without modifying the underlying structure).
Deletion on a non-existing intermediate path
will auto-create intermediates up to the parent
of the deleted key
(e.g. delete vivify(ref).one.two.three
will create ref.one.two as {} if it does not exist).
ref
The root object to wrap.
Returns
A proxy that auto-creates nested objects/arrays on property access.
Usage Examples
const ref: any = {}
vivify(ref).one.two[3][4]
expect(ref).to.deep.equal(
{
one: {
two: [undefined, undefined, undefined, []],
},
},
)
vivify(ref).one.two[3][4] = 5
expect(ref).to.deep.equal(
{
one: {
two: [
undefined,
undefined,
undefined,
[undefined, undefined, undefined, undefined, 5],
],
},
},
)
vivify(ref).one.two[3].length = 1
expect(ref).to.deep.equal(
{
one: {
two: [undefined, undefined, undefined, [undefined]],
},
},
)
vivify(ref).one.two[3] = 4
expect(ref).to.deep.equal(
{
one: {
two: [undefined, undefined, undefined, 4],
},
},
)
expect(vivify(ref).one.two.length).to.equal(4)
expect(ref).to.deep.equal(
{
one: {
two: [undefined, undefined, undefined, 4],
},
},
)
vivify(ref).one.two = 3
expect(ref).to.deep.equal({ one: { two: 3 } })
vivify(ref).one.two = undefined
expect(ref).to.deep.equal({ one: { two: undefined } })
delete vivify(ref).one.two
expect(ref).to.deep.equal({ one: {} })
expect(vivify(ref).one.toString instanceof Function)
.to.be.true
expect(vivify(ref).one.toString.name).to.equal('toString')
expect(ref).to.deep.equal({ one: {} })
delete vivify(ref).one.two.three
expect(ref).to.deep.equal({ one: { two: {} } })
vivify(ref).one.two.three.four
expect(ref).to.deep.equal({ one: { two: { three: {} } } })
vivify(ref).one.two[3]
expect(ref).to.deep.equal({ one: { two: { three: {} } } })
vivify(ref).one.two.three.four = 5
expect(ref)
.to.deep.equal({ one: { two: { three: { four: 5 } } } })
expect(vivify(ref).one.two.three.four.toString.name)
.to.equal('toString')
expect(ref)
.to.deep.equal({ one: { two: { three: { four: 5 } } } })
const u = undefined
vivify(u).one.two = 3
expect(u).to.be.undefined
const n = null
delete vivify(n).one.two.three
expect(n).to.be.nullLicense
The MIT License (MIT)
Copyright (c) 2016+ Jackens
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
