Socket
Book a DemoInstallSign in
Socket

@jackens/nnn

Package Overview
Dependencies
Maintainers
1
Versions
74
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@jackens/nnn

Jackens’ JavaScript helpers.

latest
npmnpm
Version
2025.9.9
Version published
Weekly downloads
52
-89.17%
Maintainers
1
Weekly downloads
 
Created
Source

nnn

A collection of Jackens’ JavaScript helper utilities.

Installation

bun i @jackens/nnn

or

npm i @jackens/nnn

Usage

import { «something» } from '@jackens/nnn'

or

import { «something» } from './node_modules/@jackens/nnn/nnn.js'

Exports

  • CNode: Argument type for the c helper.
  • CRoot: Argument type for the c helper.
  • EscapeMap: Argument type accepted by the escape_values and escape helpers.
  • FiniteNumber: number type excluding ±Infinity and NaN.
  • HArgs: Argument type for the h and s helpers.
  • HArgs1: Argument type for the h and s helpers.
  • c: A minimal JS-to-CSS (CSS‑in‑JS) helper.
  • csv_parse: A tiny CSV parsing helper.
  • escape: Escapes interpolated template values using the provided escape_map.
  • escape_values: Escapes array values using the provided escape_map.
  • fix_typography: Applies Polish‑specific typographic corrections.
  • h: A lightweight HyperScript-style helper for creating and modifying HTMLElements (see also s).
  • has_own: A replacement to the in operator (not to be confused with for-in).
  • is_array: Checks whether the argument is an array.
  • is_finite_number: Checks whether the argument is a finite number (excluding ±Infinity and NaN).
  • is_number: Checks whether the argument is a number.
  • is_record: Checks whether the argument is a plain object record.
  • is_string: Checks whether the argument is a string.
  • js_on_parse: JSON.parse with “JavaScript turned on”.
  • nanolight: Generic syntax highlighting helper (see also nanolight_js).
  • nanolight_js: JavaScript syntax highlighting helper (built on nanolight).
  • omit: Runtime implementation of TypeScript’s Omit (see also pick).
  • pick: Runtime implementation of TypeScript’s Pick (see also omit).
  • pl_ural: Chooses the appropriate Polish noun form based on a numeric value.
  • pro: A Proxy-based helper that safely creates nested structures on access and allows deep assignment without guards.
  • s: A lightweight HyperScript-style helper for creating and modifying SVGElements (see also h).
  • svg_use: Shorthand for: s('svg', ['use', { 'xlink:href': '#' + id }], ...args).
  • uuid_v1: Generates a UUID v1 (time-based) identifier.

CNode

type CNode = {
    [attribute_or_selector: string]: string | number | CNode | undefined;
};

Argument type for the c helper.

CRoot

type CRoot = Record<PropertyKey, CNode>;

Argument type for the c helper.

EscapeMap

type EscapeMap = Map<unknown, (value?: unknown) => string>;

Argument type accepted by the escape_values and escape helpers.

FiniteNumber

type FiniteNumber = number & {
    readonly [IS_FINITE]: true;
};

number type excluding ±Infinity and NaN.

HArgs

type HArgs = [string | Node, ...HArgs1[]];

Argument type for the h and s helpers.

HArgs1

type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string | number | HArgs;

Argument type for the h and s helpers.

c

const c: (root: CRoot, splitter?: string) => string;

A minimal JS-to-CSS (CSS‑in‑JS) helper.

The root object describes a hierarchy of CSS rules.

  • Keys whose values are not objects are treated as CSS property names; their values become property values. The concatenation of parent keys produces a selector.
  • For every key, the substring starting with the splitter (default: $$) to the end is ignored (e.g. src$$1src, @font-face$$1@font-face).
  • In property keys, uppercase letters are converted to lowercase and prefixed with - (e.g. fontFamilyfont-family); underscores are converted to - (e.g. font_familyfont-family).
  • Commas inside selector keys cause them to expand into multiple selectors (e.g. {div:{margin:1,'.a,.b,.c':{margin:2}}}div{margin:1}div.a,div.b,div.c{margin:2}).
  • Top-level keys beginning with @ (at-rules) are not concatenated with parent keys.

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).to.deep.equal(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).to.deep.equal(expected)
const actual = 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 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).to.deep.equal(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).to.deep.equal(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).to.deep.equal(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).to.deep.equal(expected)

csv_parse

const csv_parse: (csv: string, separator?: string) => string[][];

A tiny CSV parsing helper.

Usage Examples

const text = `"aaa
""aaa""
aaa",bbb, "ccc,ccc"
"xxx,xxx", "yyy
yyy",zzz
 42 , "42" , 17

`
expect(csv_parse(text)).to.deep.equal([
  ['aaa\n"aaa"\naaa', 'bbb', 'ccc,ccc'],
  ['xxx,xxx', 'yyy\nyyy', 'zzz'],
  [' 42 ', '42', ' 17']
])

escape

const escape: (escape_map: EscapeMap, template: TemplateStringsArray, ...values: unknown[]) => string;

Escapes interpolated template values using the provided escape_map.

Usage Examples

const escape_map: EscapeMap = new Map([
  [undefined, () => 'NULL'],
  [Array, (values: unknown[]) => escape_values(escape_map, values).join(', ')],
  [Boolean, (value: boolean) => `b'${+value}'`],
  [Date, (value: Date) => `'${value.toISOString().replace(/^(.+)T(.+)\..*$/, '$1 $2')}'`],
  [Number, (value: number) => `${value}`],
  [String, (value: string) => `'${value.replace(/'/g, "''")}'`]
])

const sql = escape.bind(null, escape_map)

const actual = sql`
  SELECT *
  FROM table_name
  WHERE column_name IN (${[true, null, undefined, 42, '42', "4'2", /42/, new Date(323325000000)]})`

const expected = `
  SELECT *
  FROM table_name
  WHERE column_name IN (b'1', NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00')`

expect(actual).to.deep.equal(expected)

escape_values

const escape_values: (escape_map: EscapeMap, values: unknown[]) => string[];

Escapes array values using the provided escape_map.

fix_typography

const fix_typography: (node: Node) => void;

Applies Polish‑specific typographic corrections.

Usage Examples

const p = h('p', 'Pchnąć w tę łódź jeża lub ośm skrzyń fig (zob. https://pl.wikipedia.org/wiki/Pangram).')

fix_typography(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).')

h

const h: {
    <T extends keyof HTMLElementTagNameMap>(tag: T, ...args1: HArgs1[]): HTMLElementTagNameMap[T];
    <N extends Node>(node: N, ...args1: HArgs1[]): N;
    (tag_or_node: string | Node, ...args1: HArgs1[]): Node;
};

A lightweight HyperScript-style helper for creating and modifying HTMLElements (see also s).

  • If the first argument is a string, it is treated as the tag name to create.
  • If the first argument is a Node, that node is modified.
  • Object arguments map attributes/properties:
    • Keys starting with $ set element properties (without $).
    • Other keys set attributes via setAttribute.
    • Attributes with value false are removed via removeAttribute.
  • null / undefined are ignored.
  • Node arguments are appended.
  • string / number arguments become Text nodes.
  • H_Args arrays are processed recursively.

Usage Examples

const b = h('b')

expect(b.outerHTML).to.deep.equal('<b></b>')

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

h(b, i)

expect(i.outerHTML).to.deep.equal('<i>text</i>')
expect(b.outerHTML).to.deep.equal('<b><i>text</i></b>')

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

expect(i.outerHTML).to.deep.equal('<i class="some class">text</i>')
expect(b.outerHTML).to.deep.equal('<b><i class="some class">text</i></b>')
expect(h('span', 'text').outerHTML).to.deep.equal('<span>text</span>')
expect(h('span', { $innerText: 'text' }).outerHTML).to.deep.equal('<span>text</span>')
expect(h('span', '42').outerHTML).to.deep.equal('<span>42</span>')
expect(h('span', 42).outerHTML).to.deep.equal('<span>42</span>')
expect(h('div', { style: 'margin:0;padding:0' }).outerHTML)
  .to.deep.equal('<div style="margin:0;padding:0"></div>')
expect(h('div', { $style: 'margin:0;padding:0' }).outerHTML)
  .to.deep.equal('<div style="margin: 0px; padding: 0px;"></div>')
expect(h('div', { $style: { margin: 0, padding: 0 } }).outerHTML)
  .to.deep.equal('<div style="margin: 0px; padding: 0px;"></div>')
const input1 = h('input', { value: 42 })
const input2 = h('input', { $value: '42' })

expect(input1.value).to.deep.equal('42')
expect(input2.value).to.deep.equal('42')

expect(input1.outerHTML).to.deep.equal('<input value="42">')
expect(input2.outerHTML).to.deep.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.deep.equal('<input type="checkbox" checked="">')
expect(checkbox2.outerHTML).to.deep.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 })

has_own

const has_own: (ref: unknown, key: unknown) => boolean;

A replacement to the in operator (not to be confused with for-in).

Usage Examples

const obj = { 42: null, null: 'k,e,y', 'k,e,y': 42 }

expect(42 in obj).to.be.true
expect(has_own(obj, 42)).to.be.true

expect('42' in obj).to.be.true
expect(has_own(obj, '42')).to.be.true

expect('null' in obj).to.be.true
expect(has_own(obj, 'null')).to.be.true

expect(null in obj).to.be.true
expect(has_own(obj, null)).to.be.true

expect('k,e,y' in obj).to.be.true
expect(has_own(obj, 'k,e,y')).to.be.true

expect(['k', 'e', 'y'] in obj).to.be.true
expect(has_own(obj, ['k', 'e', 'y'])).to.be.true

expect('toString' in obj).to.be.true
expect(has_own(obj, 'toString')).to.be.false

expect(() => 'key' in null).to.throw
expect(has_own(null, 'key')).to.be.false

expect(() => 'key' in undefined).to.throw
expect(has_own(undefined, 'key')).to.be.false

is_array

const is_array: (arg: unknown) => arg is unknown[];

Checks whether the argument is an array.

is_finite_number

const is_finite_number: (arg: unknown) => arg is FiniteNumber;

Checks whether the argument is a finite number (excluding ±Infinity and NaN).

is_number

const is_number: (arg: unknown) => arg is number;

Checks whether the argument is a number.

is_record

const is_record: (arg: unknown) => arg is Record<PropertyKey, unknown>;

Checks whether the argument is a plain object record.

is_string

const is_string: (arg: unknown) => arg is string;

Checks whether the argument is a string.

js_on_parse

const js_on_parse: (handlers: Record<PropertyKey, Function>, text: string) => any;

JSON.parse with “JavaScript turned on”.

Objects having exactly one property whose name exists in handlers, i.e.:

{ "«handler_name»": [«params»] }

are replaced with:

handlers['«handler_name»'](...«params»)

Usage Examples

const handlers = {
  $hello: (name: string) => `Hello ${name}!`,
  $foo: () => 'bar'
}

const actual = js_on_parse(handlers, `[
  {
    "$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 = [
  '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)

nanolight

const nanolight: (pattern: RegExp, highlighters: ((chunk: string, index: number) => HArgs1)[], code: string) => HArgs1[];

Generic syntax highlighting helper (see also nanolight_js).

nanolight_js

const nanolight_js: (code: string) => HArgs1[];

JavaScript syntax highlighting helper (built on nanolight).

Usage Examples

const code_js = 'const answerToLifeTheUniverseAndEverything = 42'

expect(nanolight_js(code_js)).to.deep.equal([
  ['span', { class: 'keyword' }, 'const'],
  ' ',
  ['span', { class: 'literal' }, 'answerToLifeTheUniverseAndEverything'],
  ' ',
  ['span', { class: 'operator' }, '='],
  ' ',
  ['span', { class: 'number' }, '42']
])

omit

const omit: <T, K extends keyof T>(ref: T, keys: unknown[]) => Omit<T, K>;

Runtime implementation of TypeScript’s Omit (see also pick).

Usage Examples

const obj = { a: 42, b: '42', c: 17 }

expect(omit(obj, ['c'])).to.deep.equal({ a: 42, b: '42' })

pick

const pick: <T, K extends keyof T>(ref: T, keys: K[]) => Pick<T, K>;

Runtime implementation of TypeScript’s Pick (see also omit).

Usage Examples

const obj = { a: 42, b: '42', c: 17 }

expect(pick(obj, ['a', 'b'])).to.deep.equal({ a: 42, b: '42' })

pl_ural

const pl_ural: (singular: string, plural_2: string, plural_5: string, value: number) => string;

Chooses the appropriate Polish noun form based on a numeric value.

Usage Examples

const auto = pl_ural.bind(null, 'auto', 'auta', 'aut')

expect(auto(0)).to.deep.equal('aut')
expect(auto(1)).to.deep.equal('auto')
expect(auto(17)).to.deep.equal('aut')
expect(auto(42)).to.deep.equal('auta')

const car = pl_ural.bind(null, 'car', 'cars', 'cars')

expect(car(0)).to.deep.equal('cars')
expect(car(1)).to.deep.equal('car')
expect(car(17)).to.deep.equal('cars')
expect(car(42)).to.deep.equal('cars')

pro

const pro: (ref: unknown) => any;

A Proxy-based helper that safely creates nested structures on access and allows deep assignment without guards.

Usage Examples

const ref = {}

pro(ref).one.two[3][4] = 1234

expect(ref).to.deep.equal({ one: { two: { 3: { 4: 1234 } } } })

pro(ref).one.two.tree = 123

expect(ref).to.deep.equal({ one: { two: { 3: { 4: 1234 }, tree: 123 } } })

pro(ref).one.two = undefined

expect(ref).to.deep.equal({ one: { two: undefined } })

delete pro(ref).one.two

expect(ref).to.deep.equal({ one: {} })

pro(ref).one.two.three.four

expect(ref).to.deep.equal({ one: { two: { three: { four: {} } } } })

pro(ref).one.two.three.four = 1234

expect(ref).to.deep.equal({ one: { two: { three: { four: 1234 } } } })

s

const s: {
    <T extends keyof SVGElementTagNameMap>(tag: T, ...args1: HArgs1[]): SVGElementTagNameMap[T];
    <N extends Node>(node: N, ...args1: HArgs1[]): N;
    (tag_or_node: string | Node, ...args1: HArgs1[]): Node;
};

A lightweight HyperScript-style helper for creating and modifying SVGElements (see also h).

  • If the first argument is a string, it is treated as the tag name to create.
  • If the first argument is a Node, that node is modified.
  • Object arguments map attributes/properties:
    • Keys starting with $ set element properties (without $).
    • Other keys set attributes via setAttributeNS.
    • Attributes with value false are removed via removeAttributeNS.
  • null / undefined are ignored.
  • Node arguments are appended.
  • string / number arguments become Text nodes.
  • H_Args arrays are processed recursively.

svg_use

const svg_use: (id: string, ...args: HArgs1[]) => SVGSVGElement;

Shorthand for: s('svg', ['use', { 'xlink:href': '#' + id }], ...args).

uuid_v1

const uuid_v1: (date?: Date, node?: string) => string;

Generates a UUID v1 (time-based) identifier.

  • Optional node must match /^[0-9a-f]*$/; it is trimmed to the last 12 characters and left-padded with zeros.

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.

Keywords

c

FAQs

Package last updated on 09 Sep 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts