Installation
Quick Take
Examples
- Classify individual characters
- Parse typed, short and repeatable CLI flags
- Assemble CLI help text
- Deeply clone a cyclic object graph
- Detect and resolve line endings
- Test whether a value exists
- Find overlapping substring occurrences
- Match a string against literal and regular-expression choices
- Intersect arrays in the first array's order
- Reject a character that is not a digit
- Omit keys and clone retained values
- Check own properties safely
- Distinguish plain objects from other object values
- Remove selected array values without mutating the input
- Remove one trailing slash
- Sort strings with the shared comparator
- Replace part of a string
- Reuse shared text symbols and HTML tag sets
- Check common JavaScript value types
- Deduplicate values while preserving order
- Match wildcard allow-lists and deny-lists
Idea
The small helpers that nearly every Codsen package needs: character classification, type guards, a few array and object utilities, EOL detection, wildcard matching and a CLI argument parser. They live here so that each of the other packages can depend on one place instead of on lodash, meow and friends.
Everything is a named export, so bundlers can tree-shake whatever you don’t use:
import { isNumberChar } from "codsen-utils";
console.log(isNumberChar("z"));
// => false
console.log(isNumberChar("0"));
// => true
This package is aimed at Codsen’s own programs first. The API is stable, but it grows to fit what those programs need.
API — character constants
Named constants for the characters which are easy to mistype and hard to spot in source code:
| Export | Character | Description |
|---|---|---|
leftSingleQuote | ||
leftSingleQuote | ‘ | U+2018 |
rightSingleQuote | ||
rightSingleQuote | ’ | U+2019, also an apostrophe |
leftDoubleQuote | ||
leftDoubleQuote | “ | U+201C |
rightDoubleQuote | ||
rightDoubleQuote | ” | U+201D |
rawNDash | ||
rawNDash | – | en dash, U+2013 |
rawMDash | ||
rawMDash | — | em dash, U+2014 |
rawNbsp | ||
rawNbsp | | non-breaking space, U+00A0 |
ellipsis | ||
ellipsis | … | U+2026 |
hairspace | ||
hairspace | | U+200A |
thinSpace | ||
thinSpace | | U+2009 |
singlePrime | ||
singlePrime | ′ | U+2032, for feet and minutes |
doublePrime | ||
doublePrime | ″ | U+2033, for inches and seconds |
rawReplacementMark | ||
rawReplacementMark | � | U+FFFD |
multiplicationSign | ||
multiplicationSign | × | U+00D7 |
backslash | ||
backslash | \ | escapes cleanly in template literals |
backtick | ||
backtick | ` | escapes cleanly in template literals |
import { rawMDash, rightSingleQuote } from "codsen-utils";
console.log(`John${rightSingleQuote}s`);
// => "John’s"
API — character sets
| Export | Type | Contents |
|---|---|---|
punctuationCharsType: string[] | ||
punctuationChars | string[] | [".", ",", ";", "!", "?"] — the sentence-ending and clause-separating characters |
voidTagsType: string[] | ||
voidTags | string[] | The 14 HTML void elements — area, base, br and so on |
inlineTagsType: Set<string> | ||
inlineTags | Set<string> | The 55 HTML inline elements — a, abbr, b, span and so on. It’s a Set, so use .has() |
import { inlineTags, voidTags } from "codsen-utils";
console.log(inlineTags.has("span"));
// => true
console.log(voidTags.includes("br"));
// => true
API — character checks
Each takes an unknown value and returns a boolean. None of them throw — a wrong type is simply false, so you can feed them str[i - 1] without checking whether the index exists.
| Function | Returns true when the input is |
|---|---|
isNumberChar(value) | |
isNumberChar(value) | a string whose first character is 0-9 |
isLetter(value) | |
isLetter(value) | a single character which is a letter in any alphabet — a, ž and я all pass |
isLatinLetter(value) | |
isLatinLetter(value) | a string whose first character is A-Z or a-z only |
isLowercaseLetter(value) | |
isLowercaseLetter(value) | a single lowercase letter |
isUppercaseLetter(value) | |
isUppercaseLetter(value) | a single uppercase letter |
isWhitespaceChar(value) | |
isWhitespaceChar(value) | a string whose first character is whitespace, including the non-breaking space |
isQuote(value) | |
isQuote(value) | ", ', or any of the four curly quotes listed above |
isCurrencyChar(value) | |
isCurrencyChar(value) | a single-character currency symbol — $, £, €, ¥ and so on |
isCurrencySymbol(value) | |
isCurrencySymbol(value) | any known currency symbol, including multi-character ones such as CHF, BZ$ and B/. |
isCurrencyChar() and isCurrencySymbol() differ only in length. Use the first when walking a string character by character, and the second when you already have a whole token:
import { isCurrencyChar, isCurrencySymbol } from "codsen-utils";
console.log(isCurrencyChar("CHF"));
// => false
console.log(isCurrencySymbol("CHF"));
// => true
API — type checks
| Function | Returns true when |
|---|---|
isStr(something) | |
isStr(something) | it’s a string. Narrows to string |
isNum(something) | |
isNum(something) | it’s a number. Narrows to number |
isInt(something) | |
isInt(something) | it’s an integer. Narrows to number |
isBool(something) | |
isBool(something) | it’s a boolean. Narrows to boolean |
isNull(something) | |
isNull(something) | it’s null. Narrows to null |
isRegExp(something) | |
isRegExp(something) | it’s a RegExp. Narrows to RegExp |
isPlainObject(value) | |
isPlainObject(value) | it’s an object literal, Object.create(null), or new Object() — but not an array, null or a class instance. Narrows to JSONObject |
existy(x) | |
existy(x) | it’s neither null nor undefined. Note 0 and "" are “existy” |
import { existy, isPlainObject } from "codsen-utils";
console.log([isPlainObject({}), isPlainObject([]), isPlainObject(null)]);
// => [true, false, false]
console.log([existy(0), existy(null), existy(undefined)]);
// => [true, false, false]
API — array and object helpers
| Function | Description |
|---|---|
deepClone(value) | |
deepClone(value) | Clones nested data without retaining object or collection references. Generic — the return type matches the input |
uniq(input) | |
uniq(input) | Returns a shallow copy of the array with only unique elements |
pullAll(input, remove) | |
pullAll(input, remove) | Returns a copy of input without the elements listed in remove. Unlike the lodash equivalent, it does not mutate the input |
intersection(a, b) | |
intersection(a, b) | Returns the elements present in both arrays. An alternative to lodash.intersection |
omit(obj, keysToRemove) | |
omit(obj, keysToRemove) | Returns a copy of the plain object without the named keys. An alternative to lodash.omit |
hasOwnProp(obj, prop) | |
hasOwnProp(obj, prop) | Safe Object.hasOwn() — inherited properties such as toString return false |
compareFn(a, b) | |
compareFn(a, b) | A localeCompare-based compare function to pass to Array.prototype.sort() |
import { compareFn, omit, pullAll, uniq } from "codsen-utils";
console.log(pullAll([1, 2, 3, 4], [2, 4]));
// => [1, 3]
console.log(uniq([1, 1, 2, 3, 3]));
// => [1, 2, 3]
console.log(omit({ a: 1, b: 2, c: 3 }, ["b"]));
// => { a: 1, c: 3 }
console.log(["b", "a", "C"].sort(compareFn));
// => ["a", "b", "C"]
API — string helpers
| Function | Description |
|---|---|
stringSplice(str, index, count, add) | |
stringSplice(str, index, count, add) | Replaces count characters starting at index with the string add, and returns the new string |
findAllIdx(value, substring) | |
findAllIdx(value, substring) | Returns an array of the indexes of every occurrence of substring |
removeTrailingSlash(value) | |
removeTrailingSlash(value) | Drops a single trailing / and trims the result. Anything which is not a string is passed through untouched |
import { findAllIdx, removeTrailingSlash, stringSplice } from "codsen-utils";
console.log(stringSplice("abcdef", 2, 3, "XY"));
// => "abXYf"
console.log(findAllIdx("scissors", "s"));
// => [0, 3, 4, 7]
console.log(removeTrailingSlash("a/b/"));
// => "a/b"
API — includes()
The function includes() is imported like this:
Like Array.prototype.includes(), but the array can hold a mix of strings and regexes, and it’s matched against a string. It’s a friendly API — it will not throw if the inputs are wrong:
import { includes } from "codsen-utils";
console.log(includes(["a", /^b/], "bcd"));
// => true
console.log(includes(["a"], "z"));
// => false
API — match()
The function match() is imported like this:
Matches a whole string against one or more wildcard patterns:
| Input argument | Type | Obligatory | Description |
|---|---|---|---|
inputType: String Obligatory: yes | |||
input | String | yes | String to match. |
patternsType: String or array of strings Obligatory: yes | |||
patterns | String or array of strings | yes | One pattern or an array of them. |
optionsType: Plain object Obligatory: no | |||
options | Plain object | no | Optional Options Object. |
| Key | Type | Default | Description |
|---|---|---|---|
caseSensitiveMatchType: Boolean Default: false | |||
caseSensitiveMatch | Boolean | false | Set to true to match letter case exactly. |
The rules are:
- Patterns are anchored — they must consume the whole input, not a part of it.
*stands for zero or more characters, and it does cross line breaks.- A leading
!negates a pattern. Any negative pattern which matches vetoes the result outright, no matter what the positive ones did. - Given only negative patterns, anything they don’t catch passes.
- An empty pattern array matches nothing.
\escapes the character after it, so\*means a literal asterisk and\\means a literal backslash.- Matching walks code points, not UTF-16 code units, so a wildcard can never consume half of a surrogate pair.
import { match } from "codsen-utils";
console.log(match("index.js", ["*.js", "!*.test.js"]));
// => true
console.log(match("index.test.js", ["*.js", "!*.test.js"]));
// => false
console.log(match("A.JS", ["*.js"], { caseSensitiveMatch: true }));
// => false
API — end of line
| Function | Description |
|---|---|
detectEol(str) | |
detectEol(str) | Reports the line ending used in the string, checking for "\r\n" first, then "\n", then "\r". Returns undefined if the string has none |
resolveEolSetting(str, eolSetting, defaultEolChar) | |
resolveEolSetting(str, eolSetting, defaultEolChar) | Turns an "lf"/"crlf"/"cr" setting into the actual character. When eolSetting is missing, it falls back to what detectEol() found, then to defaultEolChar (which itself defaults to "\n"). It throws if defaultEolChar is not one of the three EOL characters |
import { detectEol, resolveEolSetting } from "codsen-utils";
console.log(JSON.stringify(detectEol("a\r\nb")));
// => "\r\n"
console.log(JSON.stringify(resolveEolSetting("a\r\nb", "lf")));
// => "\n"
console.log(JSON.stringify(resolveEolSetting("a\r\nb", null)));
// => "\r\n"
That pairing is the point: resolveEolSetting() lets a program offer an explicit “write CRLF” option while still honouring whatever the input file already used when the caller says nothing.
API — codsenCLI()
The function codsenCLI() is imported like this:
An in-house stand-in for meow. It parses argv the way a CLI expects: flags with values, short flags, bundles, --no- negation, camelCase names and a -- escape hatch. It prints help or version on request.
| Input argument | Type | Obligatory | Description |
|---|---|---|---|
helpTextType: String Obligatory: no | |||
helpText | String | no | What --help prints. |
optionsType: Plain object Obligatory: no | |||
options | Plain object | no | Flag schema and the consuming program’s package.json. |
The Options Object has the following shape:
| Key | Type | Default | Description |
|---|---|---|---|
pkgType: Plain object Default: undefined | |||
pkg | Plain object | undefined | The consuming program’s package.json contents. |
flagsType: Plain object Default: undefined | |||
flags | Plain object | undefined | Flag schema, keyed by the flag’s camelCase name. See the table below. |
argvType: Array of strings Default: process.argv.slice(2) | |||
argv | Array of strings | process.argv.slice(2) | The arguments to parse. |
descriptionType: String or falseDefault: pkg.description | |||
description | String or false | pkg.description | Printed above the help text. Set to false to omit it. |
versionType: String Default: pkg.version | |||
version | String | pkg.version | The version to print for --version. |
helpIndentType: Number Default: 2 | |||
helpIndent | Number | 2 | How many spaces the help text is indented by. |
autoHelpType: Boolean Default: true | |||
autoHelp | Boolean | true | Print help and exit when the only argument is --help. |
autoVersionType: Boolean Default: true | |||
autoVersion | Boolean | true | Print version and exit when the only argument is --version. |
booleanDefaultType: Boolean Default: false | |||
booleanDefault | Boolean | false | Value given to declared boolean flags the user didn’t pass. Set to undefined to leave them unset. |
Each entry under flags describes one flag:
| Key | Type | Description |
|---|---|---|
typeType: "boolean", "string", "number" | ||
type | "boolean", "string", "number" | Coercion applied to whatever the user typed. If undeclared, the value is passed through as a raw string (or true for a bare flag). |
shortFlagType: String | ||
shortFlag | String | Single-letter alias, for example p to serve --pad as -p. |
defaultType: Any | ||
default | Any | Used when the flag is absent from argv. |
isMultipleType: Boolean | ||
isMultiple | Boolean | Collect every occurrence into an array instead of last-one-wins. |
The function returns a plain object:
| Returned object’s key | Type | Description |
|---|---|---|
inputType: Array of strings | ||
input | Array of strings | Positional arguments, in the order they were given. |
flagsType: Plain object | ||
flags | Plain object | Parsed flags, keyed by their camelCase names. |
pkgType: Plain object | ||
pkg | Plain object | The package.json contents you passed in. |
helpType: String | ||
help | String | The assembled help text, ready to print. |
showHelpType: Function | ||
showHelp | Function | Prints the help text, then exits (code 2 unless told otherwise). |
showVersionType: Function | ||
showVersion | Function | Prints the version, then exits with code 0. |
For example:
import { codsenCLI } from "codsen-utils";
const cli = codsenCLI("Usage: mytool <file>", {
pkg: { name: "mytool", version: "1.2.3", description: "Does a thing" },
flags: {
pad: { type: "boolean", shortFlag: "p" },
name: { type: "string", default: "anon" },
tag: { type: "string", isMultiple: true },
},
argv: ["a.txt", "-p", "--tag", "x", "--tag", "y"],
});
console.log(cli.input);
// => ["a.txt"]
console.log(cli.flags);
// => { pad: true, tag: ["x", "y"], name: "anon" }
--no- negation and the -- escape hatch work as you’d expect:
codsenCLI("h", { flags: { pad: { type: "boolean", default: true } }, argv: ["--no-pad"] }).flags;
// => { pad: false }
codsenCLI("h", { flags: {}, argv: ["a", "--", "--b"] }).input;
// => ["a", "--b"]
API — version
You can import version:
API — types
This package is written in TypeScript and exports the following types:
| Type | Description |
|---|---|
JSONValueType: JSONValue | |
JSONValue | Any JSON-representable value — string, number, boolean, null, JSONObject or JSONArray |
JSONObjectType: JSONObject | |
JSONObject | An object whose values are all JSONValues |
JSONArrayType: JSONArray | |
JSONArray | An array of JSONValues |
JsonObjectType: JsonObject | |
JsonObject | A mapped-type variant of JSONObject where every key is optional |
ObjType: Obj | |
Obj | An alias of JSONObject, kept for the packages which already import it under that name |
EolCharType: EolChar | |
EolChar | "\n", "\r\n" or "\r" — what detectEol() and resolveEolSetting() deal in |
EolSettingType: EolSetting | |
EolSetting | "lf", "crlf" or "cr" — what a user-facing EOL option accepts |
MatchOptionsType: MatchOptions | |
MatchOptions | The Optional Options Object of match() |
CliFlagTypeType: CliFlagType | |
CliFlagType | "boolean", "string" or "number" — what a flag’s value gets coerced to, once parsed |
CliFlagType: CliFlag | |
CliFlag | One entry of the codsenCLI() flag schema |
CliPkgType: CliPkg | |
CliPkg | The package.json shape codsenCLI() reads name, version, description and bin from |
CliOptionsType: CliOptions | |
CliOptions | The Options Object of codsenCLI() |
CliResultType: CliResult | |
CliResult | What codsenCLI() returns |
import type { CliFlag, CliFlagType, CliOptions, CliPkg, CliResult, EolChar, EolSetting, JSONArray, JSONObject, JSONValue, JsonObject, MatchOptions, Obj } from "codsen-utils";