UNPKG

@jackens/nnn

Version:

Jackens’ JavaScript helpers.

1,365 lines (974 loc) 32.8 kB
# nnn A collection of Jackens’ JavaScript helper utilities (version: `2026.2.27`). ## Installation ```sh bun i @jackens/nnn ``` or ```sh npm i @jackens/nnn ``` ## Usage ```js 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`](#CNode): Represents a CSS rule node for the [`c`](#c) helper. Keys are CSS properties or nested selectors. - [`CRoot`](#CRoot): Represents the root CSS object for the [`c`](#c) helper. Keys are top-level selectors or at-rules. - [`HArgs`](#HArgs): Tuple argument type for the [`h`](#h) and [`s`](#s) helpers. - [`HArgs1`](#HArgs1): Single argument type for the [`h`](#h) and [`s`](#s) helpers. - [`c`](#c): A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string. - [`csvParse`](#csvParse): Parses a CSV string into a two-dimensional array of strings. - [`fixPlTypography`](#fixPlTypography): Applies Polish-specific typographic corrections to a DOM subtree. - [`h`](#h): A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `HTMLElement`s (see also [`s`](#s)). - [`hasOwn`](#hasOwn): Checks whether an object has the specified key as its own property. - [`isArray`](#isArray): Checks whether the argument is an array. - [`isFiniteNumber`](#isFiniteNumber): Checks whether the argument is a finite number (excludes `±Infinity` and `NaN`). - [`isInteger`](#isInteger): Checks whether the argument is an integer number. - [`isNumber`](#isNumber): Checks whether the argument is of type `number` (includes `NaN` and `±Infinity`). - [`isRecord`](#isRecord): Checks whether the argument is a plain object (not `null` and not an array). - [`isString`](#isString): Checks whether the argument is a string. - [`jsOnParse`](#jsOnParse): Parses JSON with support for handler-based value transformation (“JavaScript ON”). - [`monokai`](#monokai): A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolightTs`](#nanolightTs) tokenizer. - [`nanolightTs`](#nanolightTs): A TypeScript/JavaScript syntax highlighting tokenizer built using [`newTokenizer`](#newTokenizer). - [`newEscape`](#newEscape): Creates a tag function for escaping interpolated values in template literals. - [`newNounForm`](#newNounForm): Creates a function that returns the appropriate noun form based on a numeric value using `Intl.PluralRules`. - [`newTokenizer`](#newTokenizer): A helper for building simple tokenizers (see also [`nanolightTs`](#nanolightTs)). - [`omit`](#omit): Creates a new object excluding the specified keys from the source object. - [`pick`](#pick): Creates a new object containing only the specified keys from the source object. - [`rwd`](#rwd): A responsive web design helper that generates CSS rules for a grid-like layout. - [`s`](#s): A lightweight [HyperScript](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `SVGElement`s (see also [`h`](#h)). - [`svgUse`](#svgUse): Shorthand for creating an SVG element with a `<use>` child referencing an icon by ID. - [`uuidV1`](#uuidV1): Generates a UUID v1 (time-based) identifier. - [`vivify`](#vivify): A Proxy-based helper for auto-vivification of nested object structures. ### CNode ```ts type CNode = { [attributeOrSelector: string]: string | number | CNode | undefined; }; ``` Represents a CSS rule node for the [`c`](#c) helper. Keys are CSS properties or nested selectors. ### CRoot ```ts type CRoot = Record<PropertyKey, CNode>; ``` Represents the root CSS object for the [`c`](#c) helper. Keys are top-level selectors or at-rules. ### HArgs ```ts type HArgs = [string | Node, ...HArgs1[]]; ``` Tuple argument type for the [`h`](#h) and [`s`](#s) helpers. ### HArgs1 ```ts type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string | number | HArgs; ``` Single argument type for the [`h`](#h) and [`s`](#s) helpers. ### c ```ts 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 ```ts 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 ```ts 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 ```ts 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 ```ts 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 ```ts 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 ```ts 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](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `HTMLElement`s (see also [`s`](#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 via `setAttribute`. A value of `false` removes the attribute. - `null`/`undefined`: ignored. - `Node`: appended as a child. - `string`/`number`: converted to a `Text` node and appended. - [`HArgs`](#HArgs) array: processed recursively. #### Returns The created or modified `HTMLElement`. #### Usage Examples ```ts 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 }) ``` ### hasOwn ```ts 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 ```ts 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.false ``` ### isArray ```ts 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 ```ts expect(isArray([])).to.be.true expect(isArray(Object.create({ constructor: Array }))).to.be.false expect(isArray(Object.create({ [Symbol.toStringTag]: Array.name }))).to.be.false ``` ### isFiniteNumber ```ts 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 ```ts 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.false ``` ### isInteger ```ts 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 ```ts 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.false ``` ### isNumber ```ts 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 ```ts 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.true ``` ### isRecord ```ts 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 ```ts expect(isRecord({})).to.be.true expect(isRecord([])).to.be.false expect(isRecord(Object.create(null))).to.be.true expect(isRecord(Object.create({ constructor: Number }))).to.be.true expect(isRecord(Object.create({ [Symbol.toStringTag]: Number.name }))).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.true ``` ### isString ```ts 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 ```ts expect(isString('42')).to.be.true expect(isString(String('42'))).to.be.true expect(isString(new String('42'))).to.be.false ``` ### jsOnParse ```ts 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 ```ts const handlers = { $add: (a: number, b: number) => a + b, $hello: (name: string) => `Hello ${name}!`, $foo: () => 'bar' } const actual = 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 } ]`) const expected = [ 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 } ] expect(actual).to.deep.equal(expected) ``` ### monokai ```ts const monokai: CRoot; ``` A Monokai-inspired color scheme for use with the [`c`](#c) helper and [`nanolightTs`](#nanolightTs) tokenizer. ### nanolightTs ```ts const nanolightTs: (code: string) => HArgs1[]; ``` A TypeScript/JavaScript syntax highlighting tokenizer built using [`newTokenizer`](#newTokenizer). #### code The source code string to tokenize. #### Returns An array of [`HArgs1`](#HArgs1) elements suitable for rendering with [`h`](#h). #### Usage Examples ```ts 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 ```ts 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 ```ts 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 ```ts 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 ```ts 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 ```ts 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`](#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 to `decorator` when the pattern matches. - `pattern`: a `string` or `RegExp` to match against the input. #### Returns A tokenizer function that accepts a code string and returns an array of decorated tokens. #### Remarks 1. Matches starting at an earlier position take precedence. 2. Among matches at the same position, the longer one wins. 3. Among matches of the same position and length, the one defined earlier wins. ### omit ```ts 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`](#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 ```ts const ref = { a: 42, b: '42', c: 17 } expect(omit(ref, ['c'])).to.deep.equal({ a: 42, b: '42' }) ``` ### pick ```ts const pick: <T, K extends keyof T>(ref: T, keys: K[]) => 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`](#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 ```ts const ref = { a: 42, b: '42', c: 17 } expect(pick(ref, ['a', 'b'])).to.deep.equal({ a: 42, b: '42' }) ``` ### rwd ```ts 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`](#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, default `1`): number of horizontal cells the element spans. - `height` (optional, default `1`): number of vertical cells the element spans. #### Usage Examples ```ts 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 ```ts 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](https://github.com/hyperhype/hyperscript)-style helper for creating and modifying `SVGElement`s (see also [`h`](#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 via `setAttributeNS`. A value of `false` removes the attribute. - `null`/`undefined`: ignored. - `Node`: appended as a child. - `string`/`number`: converted to a `Text` node and appended. - [`HArgs`](#HArgs) array: processed recursively. #### Returns The created or modified `SVGElement`. ### svgUse ```ts 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. ### uuidV1 ```ts 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 ```ts 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.true ``` ### vivify ```ts 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 ```ts 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.null ``` ## License 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.