plandek-utils/ts-clean-key

View on GitHub
src/index.ts

Summary

Maintainability
C
1 day
Test Coverage
A
100%
import trim from "lodash.trim";
import parameterize from "parameterize";

export { processedSafeKey, safeKeyToOriginal } from "./safe-keys";

/**
 * @internal
 * @ignore
 */
const EMPTY = "";

/**
 * @internal
 * @ignore
 */
const MANY_DASHES = /[-]+/g;

/**
 * @internal
 * @ignore
 */
const DASH = "-";

/**
 * regex to be used for detecting invalid chars for `cleanKeySimple`
 *
 * @see cleanKeySimple()
 */
export const INVALID_CHARS = /[^0-9a-z\-_]/g;

/**
 * regex to be used for detecting invalid chars for `cleanKeySimpleCI`
 *
 * @see cleanKeySimpleCI()
 */
export const INVALID_CHARS_CI = /[^0-9A-Za-z\-_]/g;

/**
 * regex to be used for detecting invalid chars for `cleanKeySimpleWithDots`
 *
 * @see cleanKeySimpleWithDots()
 */
export const INVALID_CHARS_WITH_DOTS = /[^0-9a-z\-_\.]/g;

/**
 * regex to be used for detecting invalid chars for `cleanKeySimpleCIWithDots`
 *
 * @see cleanKeySimpleCIWithDots()
 */
export const INVALID_CHARS_CI_WITH_DOTS = /[^0-9A-Za-z\-_\.]/g;

/**
 * regex to be used for detecting invalid chars for `cleanKeySimpleWithSpecials`
 *
 * @see cleanKeySimpleWithSpecials()
 */
export const INVALID_CHARS_WITH_SPECIALS = /[^0-9a-z\-_\.\/:~\|#]/g;

/**
 * regex to be used for detecting invalid chars for `cleanKeySimpleCIWithSpecials`
 *
 * @see cleanKeySimpleCIWithSpecials()
 */
export const INVALID_CHARS_CI_WITH_SPECIALS = /[^0-9A-Za-z\-_\.\/~:\|#]/g;

/**
 * Removes bad chars for a string key (all except number, lowercase ascii7 letters, dash `-` and underscore `_`)
 *
 * @param s
 */
export function cleanKeySimple(s: string): string {
  return s.replace(INVALID_CHARS, EMPTY);
}

/**
 * Removes bad chars for a string key (all except number, uppercase and lowercase ascii7 letters, dash `-` and underscore `_`)
 *
 * @param s
 */
export function cleanKeySimpleCI(s: string): string {
  return s.replace(INVALID_CHARS_CI, EMPTY);
}

/**
 * Removes bad chars for a string key (all except number, lowercase ascii7 letters, dash `-` and underscore `_` and dot `.`)
 *
 * @param s
 */
export function cleanKeySimpleWithDots(s: string): string {
  return s.replace(INVALID_CHARS_WITH_DOTS, EMPTY);
}

/**
 * Removes bad chars for a string key (all except number, uppercase and lowercase ascii7 letters, dash `-`, underscore `_`, and dot `.`)
 *
 * @param s
 */
export function cleanKeySimpleCIWithDots(s: string): string {
  return s.replace(INVALID_CHARS_CI_WITH_DOTS, EMPTY);
}

/**
 * Removes bad chars for a string key (all except number, lowercase ascii7 letters, dash `-`, underscore `_`, dot `.`, pipes `|`, colons `:`, tildes `~`, hashtags `#`, and slashes `/`)
 *
 * @param s
 */
export function cleanKeySimpleWithSpecials(s: string): string {
  return s.replace(INVALID_CHARS_WITH_SPECIALS, EMPTY);
}

/**
 * Removes bad chars for a string key (all except number, uppercase and lowercase ascii7 letters, dash `-`, underscore `_`, dot `.`, pipes `|`, colons `:`, tildes `~`, hashtags `#`, and slashes `/`)
 *
 * @param s
 */
export function cleanKeySimpleCIWithSpecials(s: string): string {
  return s.replace(INVALID_CHARS_CI_WITH_SPECIALS, EMPTY);
}

/**
 * signals which characters to allow, not taking into account case sensitivity:
 * - `strict` => `0-9`, `a-z`, `-`, and `_`
 * - `dots` => strict + `.`
 * - `specials` => strict + `.`, `|`, `~`, `/`, and `:`
 */
export enum CharAllowanceMode {
  Strict = "strict",
  Dots = "dots",
  Specials = "specials",
}

/**
 * type with the string values of `CharAllowanceMode`
 * @see CharAllowanceMode
 */
export type CharAllowanceModeEnumValues = "strict" | "dots" | "specials";

export type CleanKeyOptions = {
  prependIfNoLetters?: string;
  trimEdgeDashes?: boolean;
  replaceManyDashes?: boolean;
  caseSensitive?: boolean;
  mode?: CharAllowanceModeEnumValues;
};

/**
 * same as `cleanKey` with the `caseSensitive` option to `true`
 *
 * @param s
 * @param opts
 * @see cleanKey
 */
export function cleanKeyCI(s: string, opts: Omit<CleanKeyOptions, "caseSensitive"> = {}): string {
  return run(s, { ...opts, caseSensitive: false });
}

/**
 * same as `cleanKey` with the `mode` option to `dots`
 *
 * @param s
 * @param opts
 * @param opts.replaceManyDashes (default `true`)
 * @param opts.caseSensitive (default `true`)
 * @see cleanKey
 */
export function cleanKeyWithDots(s: string, opts: Omit<CleanKeyOptions, "mode"> = {}): string {
  return run(s, { ...opts, mode: CharAllowanceMode.Dots });
}

/**
 * same as `cleanKey` with the `mode` option to `dots`, and `caseSensitive` to `true`
 *
 * @param s
 * @param opts
 * @param opts.replaceManyDashes (default `true`)
 * @see cleanKey
 */
export function cleanKeyCIWithDots(s: string, opts: Omit<CleanKeyOptions, "caseSensitive" | "mode"> = {}): string {
  return run(s, { ...opts, caseSensitive: false, mode: CharAllowanceMode.Dots });
}

/**
 * same as `cleanKey` with the `mode` option to `specials`
 *
 * @param s
 * @param opts
 * @param opts.replaceManyDashes (default `true`)
 * @param opts.caseSensitive (default `true`)
 * @see cleanKey
 */
export function cleanKeyWithSpecials(s: string, opts: Omit<CleanKeyOptions, "mode"> = {}): string {
  return run(s, { ...opts, mode: CharAllowanceMode.Specials });
}

/**
 * same as `cleanKey` with the `mode` option to `specials`, and `caseSensitive` to `true`
 *
 * @param s
 * @param opts
 * @param opts.replaceManyDashes (default `true`)
 * @see cleanKey
 */
export function cleanKeyCIWithSpecials(s: string, opts: Omit<CleanKeyOptions, "caseSensitive" | "mode"> = {}): string {
  return run(s, { ...opts, caseSensitive: false, mode: CharAllowanceMode.Specials });
}

/**
 * Removes bad chars for a string key (all except number, lowercase ascii7 letters, dash `-` and underscore `_`)
 * It will allow uppercase ascii7 letters if option `caseSensitive` is false
 * If the mode is:
 *  - `strict`: it will allow just the standard set (`0-9`, `a-z`, `-`, and `_`)
 *  - `dots` => strict + `.`
 *  - `specials` => strict + `.`, `|`, `~`, `/`, and `:`
 * It also removes multiple dashes in a row and replaces them for a single dash unless option `replaceManyDashes: false` is given
 * If no letters `a-z` or `A-Z` are found, and the option `prependIfNoLetters` is given, it will prepend it to the original string, and clean again.
 * If `trimEdgeDashes` is true, it will trim the edge dashes (`-`) from the beginning and end of the clean string
 *
 * @param s
 * @param opts
 * @param opts.caseSensitive (default `true`)
 * @param opts.replaceManyDashes (default `true`)
 * @param opts.trimEdgeDashes (default `false`)
 * @param opts.prependIfNoLetters (default `undefined`)
 * @param opts.mode (default `strict`)
 */
export function cleanKey(s: string, opts: CleanKeyOptions = {}): string {
  return run(s, opts);
}

/**
 * @internal
 * @hidden
 */
const ANY_LETTERS = /[a-zA-Z]/;

/**
 * @internal
 * @hidden
 */
function run(s: string, opts: CleanKeyOptions = {}): string {
  const replaceManyDashes = opts.replaceManyDashes ?? true;
  const trimEdgeDashes = opts.trimEdgeDashes ?? false;
  const prependIfNoLetters = opts.prependIfNoLetters ?? "";
  const caseSensitive = opts.caseSensitive ?? true;
  const mode = opts.mode ?? CharAllowanceMode.Strict;

  const cleanString = performClean(s, mode, caseSensitive);

  const value = replaceManyDashes ? cleanString.replace(MANY_DASHES, DASH) : cleanString;
  const valueTrimmed = trimEdgeDashes ? trim(value, "-") : value;
  if (prependIfNoLetters && !ANY_LETTERS.test(valueTrimmed)) {
    return run(`${prependIfNoLetters}${s}`, { ...opts, prependIfNoLetters: undefined });
  }
  return valueTrimmed;
}

/**
 * @internal
 * @hidden
 */
function performClean(s: string, mode: CharAllowanceModeEnumValues, caseSensitive: boolean): string {
  if (mode === CharAllowanceMode.Dots) {
    return caseSensitive ? cleanKeySimpleWithDots(s) : cleanKeySimpleCIWithDots(s);
  }

  if (mode === CharAllowanceMode.Specials) {
    return caseSensitive ? cleanKeySimpleWithSpecials(s) : cleanKeySimpleCIWithSpecials(s);
  }

  return caseSensitive ? cleanKeySimple(s) : cleanKeySimpleCI(s);
}

/**
 * First it trims and parameterizes the original string (using the `parameterize` package), and then it cleans it using the `cleanKey` function
 *
 * @param original
 * @param opts
 * @see cleanKey
 */
export function parameterizeAndClean(original: string, opts: CleanKeyOptions = {}): string {
  const key = parameterize(original.trim());
  return cleanKey(key, opts);
}