npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jackens/nnn

v2026.9.9

Published

Jackens’ JavaScript helpers.

Readme

nnn

A collection of Jackens’ JavaScript helper utilities (version: 2026.9.9).

Installation

bun i @jackens/nnn

or

npm i @jackens/nnn

Usage

import {
  CNode,
  HArgs,
  HArgs1,
  c,
  createEscape,
  createTokenizer,
  csvParse,
  fixPlTypography,
  h,
  monokai,
  nanolightTs,
  rwd,
  s,
  svgUse,
  uuidV1,
} from '@jackens/nnn' // or './node_modules/@jackens/nnn/nnn.js'

Exports

  • CNode: Represents a CSS rule node for the c helper. Keys are CSS properties or nested selectors.
  • HArgs: Tuple argument type for the h and s helpers.
  • HArgs1: Single argument type for the h and s helpers.
  • c: A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.
  • createEscape: Creates a tag function for escaping interpolated values in template literals.
  • createTokenizer: A helper for building simple tokenizers (see also nanolightTs).
  • 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 modifying HTMLElements (see also s).
  • monokai: A Monokai-inspired color scheme for use with the c helper and nanolightTs tokenizer.
  • nanolightTs: A TypeScript/JavaScript syntax highlighting tokenizer built using createTokenizer.
  • rwd: A responsive web design helper that generates CSS rules for a grid-like layout.
  • s: A lightweight HyperScript-style helper for creating and modifying SVGElements (see also h).
  • svgUse: Shorthand for creating an SVG element with a <use> child referencing an icon by ID.
  • uuidV1: Generates a UUID v1 (time-based) identifier.

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.

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: (node: CNode, splitter?: string) => string;

A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.

node

An object describing CSS rules. Keys are selectors or at-rules; values are either CSS property values or nested rule objects. A selector key may contain comma-separated alternatives (e.g. '.b,.c'); when nested, each alternative is combined with each parent alternative (cartesian product), producing a comma-joined selector list (e.g. 'a.b,a.c').

splitter

A delimiter used to create unique keys (default: '$$'). The substring from splitter to the end of a key is ignored (e.g., src$$1src).

Returns

A CSS string representing the compiled rules.

Usage Examples

const actual = c({
  a: {
    color: 'red',
    margin: 1,
    '.c': { margin: 2, padding: 2 },
    padding: 1,
  },
})

const expected = `
  a{
    color:red;
    margin:1
  }
  a.c{
    margin:2;
    padding:2
  }
  a{
    padding:1
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)
const actual = c({
  a: {
    '.b': {
      color: 'red',
      margin: 1,
      '.c': { margin: 2, padding: 2 },
      padding: 1,
    },
  },
})

const expected = `
  a.b{
    color:red;
    margin:1
  }
  a.b.c{
    margin:2;
    padding:2
  }
  a.b{
    padding:1
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)
const actual = c({
  '@font-face$$1': {
    'font-family': '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': { 'background-color': 'rgb(0,0,0)' },
  },
  '@media(min-width:200px)': {
    div: { margin: 0, padding: 0 },
    span: { color: '#000' },
  },
})

const expected = `
  @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(actual).toBe(expected)
const actual = c({
  a: {
    '.b,.c': {
      margin: 1,
      '.d': {
        margin: 2,
      },
    },
  },
})

const expected = `
  a.b,a.c{
    margin:1
  }
  a.b.d,a.c.d{
    margin:2
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)
const actual = c({
  '.b,.c': {
    margin: 1,
    '.d': {
      margin: 2,
    },
  },
})

const expected = `
  .b,.c{
    margin:1
  }
  .b.d,.c.d{
    margin:2
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)
const actual = c({
  '.a,.b': {
    margin: 1,
    '.c,.d': {
      margin: 2,
    },
  },
})

const expected = `
  .a,.b{
    margin:1
  }
  .a.c,.a.d,.b.c,.b.d{
    margin:2
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)
const actual = c({
  ':root$$monokai': {
    '--': {
      bg: '#faf4f2',
      fg: '#29242a',
      comment: '#918c8e',
      identifier: {
        1: '#7058be',
        2: '#269d69',
        3: '#1c8ca8',
        4: '#29242a',
      },
      keyword: {
        1: '#e14775',
        2: '#7058be',
        3: '#1c8ca8',
      },
      number: '#7058be',
      operator: '#e14775',
      punctuation: '#918c8e',
      string: '#cc7a0a',
    },
  },
  '@media only screen and (prefers-color-scheme: dark)$$monokai':
    {
      ':root': {
        '--': {
          bg: '#2d2a2e',
          fg: '#fcfcfa',
          comment: '#727072',
          identifier: {
            1: '#ae81ff',
            2: '#a9dc76',
            3: '#66d9ef',
            4: '#fcfcfa',
          },
          keyword: {
            1: '#ff6188',
            2: '#ae81ff',
            3: '#66d9ef',
          },
          number: '#ae81ff',
          operator: '#ff6188',
          punctuation: '#727072',
          string: '#ffd866',
        },
      },
    },
})

const expected = `
  :root{
    --bg:#faf4f2;
    --fg:#29242a;
    --comment:#918c8e;
    --identifier1:#7058be;
    --identifier2:#269d69;
    --identifier3:#1c8ca8;
    --identifier4:#29242a;
    --keyword1:#e14775;
    --keyword2:#7058be;
    --keyword3:#1c8ca8;
    --number:#7058be;
    --operator:#e14775;
    --punctuation:#918c8e;
    --string:#cc7a0a
  }
  @media only screen and (prefers-color-scheme: dark){
    :root{
      --bg:#2d2a2e;
      --fg:#fcfcfa;
      --comment:#727072;
      --identifier1:#ae81ff;
      --identifier2:#a9dc76;
      --identifier3:#66d9ef;
      --identifier4:#fcfcfa;
      --keyword1:#ff6188;
      --keyword2:#ae81ff;
      --keyword3:#66d9ef;
      --number:#ae81ff;
      --operator:#ff6188;
      --punctuation:#727072;
      --string:#ffd866
    }
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

createEscape

const createEscape: (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 function.

Usage Examples

const escapeFn = (value: any): string =>
  Array.isArray(value)
    ? value.map(escapeFn).join(', ')
    : value === true || value === false
      ? `b'${+value}'`
      : value instanceof Date
        ? `'${value
            .toISOString()
            .replace(/^(.+)T(.+)\..*$/, '$1 $2')}'`
        : Number.isFinite(value)
          ? `${value}`
          : typeof value === 'string'
            ? `'${value.replace(/'/g, "''")}'`
            : 'NULL'

const sql = createEscape(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).toBe(expected)

createTokenizer

const createTokenizer: <M, T>(decorator: (chunk: string, metadata?: M) => T, ...specs: [M, string | RegExp][]) => (code: string) => T[];

A helper for building simple tokenizers (see also nanolightTs).

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.

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.

Usage Examples

const tokenizer = createTokenizer(
  (chunk, metadata) => ({ chunk, metadata }),
  ['keyword', /\b(if|else|for)\b/],
  ['string', /"[^"]*"/]
)

const result = tokenizer('if "hello" else "world"')

expect(result).toEqual([
  { 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 tokenizer = createTokenizer(
  (chunk, metadata) => `${metadata}:${chunk}`,
  ['tag', 'BEGIN'],
  ['end', 'END']
)

const result = tokenizer('aBEGINbENDc')

expect(result).toEqual([
  'undefined:a',
  'tag:BEGIN',
  'undefined:b',
  'end:END',
  'undefined:c',
])
const tokenizer = createTokenizer(
  chunk => chunk,
  ['test', /test/]
)

const result = tokenizer('')

expect(result).toEqual([])
const tokenizer = createTokenizer(
  (chunk, metadata) => ({ chunk, metadata }),
  ['start', /^test/]
)

const result = tokenizer('test here')

expect(result).toEqual([
  { chunk: 'test', metadata: 'start' },
  { chunk: ' here', metadata: undefined },
])
const tokenizer = createTokenizer(
  (chunk, metadata) => metadata,
  ['later', /x/],
  ['earlier', /y/]
)

const result = tokenizer('yx')

expect(result).toEqual(['earlier', 'later'])
const tokenizer = createTokenizer(
  chunk => chunk,
  ['short', 'a'],
  ['long', 'abc']
)

const result = tokenizer('abc')

expect(result).toEqual(['abc'])
const tokenizer = createTokenizer(
  chunk => chunk,
  ['empty', ''],
  ['word', /\w+/]
)

const result = tokenizer('hello')

expect(result).toEqual(['hello'])
const tokenizer = createTokenizer(
  chunk => chunk,
  ['test', /xyz/]
)

const result = tokenizer('abc')

expect(result).toEqual(['abc'])
const tokenizer = createTokenizer(
  (_chunk, metadata) => metadata,
  ['a', 'a'],
  ['b', 'b']
)

const result = tokenizer('aabb')

expect(result).toEqual(['a', 'a', 'b', 'b'])

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)).toEqual([
  ['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”) and the em dash (“—”) from appearing at the end of a line. It wraps each such character together with the whitespace that follows it in a white-space:nowrap span, removing the only line-break opportunity between it and the next word so the two stay glued together. 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).toEqual(
  '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); if the value is a plain object, it is shallow-merged into the existing property instead of replacing it (useful e.g. for $style). Other keys set attributes: a key prefixed with xlink: sets the attribute via setAttributeNS in the XLink namespace; any other key (without a colon) sets it 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 array: processed recursively.

Returns

The created or modified HTMLElement.

Usage Examples

const b = h('b')

expect(b.outerHTML).toBe('<b></b>')

const i = h('i', 'text')

h(b, i)

expect(i.outerHTML).toBe('<i>text</i>')

expect(b.outerHTML).toBe('<b><i>text</i></b>')

h(i, { $className: 'some class' })

expect(i.outerHTML).toBe('<i class="some class">text</i>')

expect(b.outerHTML).toBe(
  '<b><i class="some class">text</i></b>'
)
expect(h('span', 'text').outerHTML).toBe(
  '<span>text</span>'
)

expect(h('span', { $innerText: 'text' }).outerHTML).toBe(
  '<span>text</span>'
)

expect(h('span', '42').outerHTML).toBe('<span>42</span>')

expect(h('span', 42).outerHTML).toBe('<span>42</span>')
expect(
  h('div', { style: 'margin:0;padding:0' }).outerHTML
).toBe('<div style="margin:0;padding:0"></div>')

expect(
  h('div', { $style: 'margin:0;padding:0' }).outerHTML
).toBe('<div style="margin: 0px; padding: 0px;"></div>')

expect(
  h('div', { $style: { margin: 0, padding: 0 } })
    .outerHTML
).toBe('<div style="margin: 0px; padding: 0px;"></div>')
const input1 = h('input', { value: 42 })
const input2 = h('input', { $value: '42' })

expect(input1.value).toBe('42')
expect(input2.value).toBe('42')

expect(input1.outerHTML).toBe('<input value="42">')
expect(input2.outerHTML).toBe('<input>')
const checkbox1 = h('input', {
  type: 'checkbox',
  checked: true,
})

const checkbox2 = h('input', {
  type: 'checkbox',
  $checked: true,
})

expect(checkbox1.checked).toBe(true)
expect(checkbox2.checked).toBe(true)

expect(checkbox1.outerHTML).toBe(
  '<input type="checkbox" checked="">'
)

expect(checkbox2.outerHTML).toBe(
  '<input type="checkbox">'
)
const div = h('div')

expect(div.key).toBeUndefined()

h(div, { $key: { one: 1 } })

expect(div.key).toEqual({ one: 1 })

h(div, { $key: { two: 2 } })

expect(div.key).toEqual({ one: 1, two: 2 })
const elemWithClass = h('div', { class: 'test' })

expect(elemWithClass.getAttribute('class')).toBe('test')

const elemWithText = h('div', 'initial')

h(elemWithText, ' more')

expect(elemWithText.outerHTML).toBe(
  '<div>initial more</div>'
)

const elemWithNested = h(
  'div',
  ['span', 'hello'],
  ['b', 'world']
)

expect(elemWithNested.outerHTML).toBe(
  '<div><span>hello</span><b>world</b></div>'
)

monokai

const monokai: CNode;

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 createTokenizer.

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)).toEqual([
  ['span', { class: 'keyword1' }, 'const'],
  ' ',
  [
    'span',
    { class: 'identifier4' },
    '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 */'],
])

rwd

const rwd: (root: CNode, 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, default 1): number of horizontal cells the element spans.
  • height (optional, default 1): number of vertical cells the element spans.

Usage Examples

const style: CNode = {
  body: {
    margin: 0,
  },
  '.r6': {
    border: 'solid red 1px',
    '.no-border': {
      border: 'none',
    },
  },
}

rwd(style, '.r6', 200, 50, [6], [3], [1, 1, 2])

expect(style).toEqual({
  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); if the value is a plain object, it is shallow-merged into the existing property instead of replacing it. Other keys set attributes: a key prefixed with xlink: sets the attribute via setAttributeNS in the XLink namespace; any other key (without a colon) sets it 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 array: processed recursively.

Returns

The created or modified SVGElement.

Usage Examples

const svg = s('svg', { 'xlink:href': true })

expect(
  svg.getAttributeNS(
    'http://www.w3.org/1999/xlink',
    'href'
  )
).toBe('')
const svg = s('svg', { 'xlink:href': false })

expect(
  svg.getAttributeNS(
    'http://www.w3.org/1999/xlink',
    'href'
  )
).toBeNull()
const svg = s('svg', {
  'xlink:href': 'http://example.com',
})

expect(
  svg.getAttributeNS(
    'http://www.w3.org/1999/xlink',
    'href'
  )
).toBe('http://example.com')
const svg = s('svg', { 'xlink:title': 42 })

expect(
  svg.getAttributeNS(
    'http://www.w3.org/1999/xlink',
    'title'
  )
).toBe('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 svg = svgUse('icon-home')

expect(svg.children.length).toBe(1)

const useElement = svg.children[0]

expect(
  useElement.getAttributeNS(
    'http://www.w3.org/1999/xlink',
    'href'
  )
).toBe('#icon-home')

const svgWithViewBox = svgUse('icon-star', {
  viewBox: '0 0 24 24',
})

expect(svgWithViewBox.getAttribute('viewBox')).toBe(
  '0 0 24 24'
)

const useViewBox = svgWithViewBox.children[0]

expect(
  useViewBox.getAttributeNS(
    'http://www.w3.org/1999/xlink',
    'href'
  )
).toBe('#icon-star')

const svgWithClass = svgUse('icon-menu', {
  class: 'icon-btn',
})

expect(svgWithClass.getAttribute('class')).toBe(
  'icon-btn'
)

expect(svgWithClass.children.length).toBe(1)

const useClass = svgWithClass.children[0]

expect(
  useClass.getAttributeNS(
    'http://www.w3.org/1999/xlink',
    'href'
  )
).toBe('#icon-menu')

const svgWithMultipleAttrs = svgUse('icon-settings', {
  width: 24,
  height: 24,
  class: 'icon',
})

expect(svgWithMultipleAttrs.getAttribute('width')).toBe(
  '24'
)

expect(svgWithMultipleAttrs.getAttribute('height')).toBe(
  '24'
)

expect(svgWithMultipleAttrs.getAttribute('class')).toBe(
  'icon'
)

const useMultiple = svgWithMultipleAttrs.children[0]

expect(
  useMultiple.getAttributeNS(
    'http://www.w3.org/1999/xlink',
    'href'
  )
).toBe('#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]).toBe('8001')
  }

  if (i === 4095) {
    expect(uuid.split('-')[3]).toBe('8fff')
  }

  if (i === 4096) {
    expect(uuid.split('-')[3]).toBe('9000')
  }

  if (i === 9029) {
    expect(uuid.split('-')[3]).toBe('a345')
  }

  if (i === 13398) {
    expect(uuid.split('-')[3]).toBe('b456')
  }

  if (i === 16384) {
    expect(uuid.split('-')[3]).toBe('8000')
  }

  if (i === 17767) {
    expect(uuid.split('-')[3]).toBe('8567')
  }
}
expect(
  uuidV1(new Date(), '000123456789abc').split('-')[4]
).toBe('123456789abc')

expect(
  uuidV1(new Date(), '123456789').split('-')[4]
).toBe('000123456789')

expect(
  uuidV1(new Date(323325000000)).startsWith(
    'c1399400-9a71-11bd'
  )
).toBe(true)

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.