shelving
Version:
Toolkit for using data in JavaScript.
80 lines (79 loc) • 3.16 kB
JavaScript
import { RequiredError } from "../error/RequiredError.js";
import { isObject } from "./object.js";
/** Log an error to the console. */
export function logError(reason) {
console.error(reason);
}
/** Is an unknown value an `Error` instance? */
export function isError(v) {
return typeof Error.isError === "function" ? Error.isError(v) : v instanceof Error;
}
/** Return the string message from an unknown value, or return `undefined` if it could not be found. */
export function getMessage(input) {
return typeof input === "string" ? input : isObject(input) && typeof input.message === "string" ? input.message : undefined;
}
/** Require a message from an unknown value, or throw `RequiredError` if it could not be found. */
export function requireMessage(input, caller = requireMessage) {
const message = getMessage(input);
if (message === undefined)
throw new RequiredError("Message is required", { received: input, caller });
return message;
}
/**
* Split a string message into lines, look for prefixes like `name:`, and return a dictionary of those named messages.
* - Full messages strings can have multiple lines separated by `\n` newline.
* - Named messages are extracted into their own entries in the dictionary.
* - Unnamed messages are combined into a single entry with the key `""` (empty string).
*/
export function splitMessage(input) {
const messages = requireMessage(input, splitMessage).split("\n");
const output = {};
for (const line of messages) {
const i = line.indexOf(": ");
if (i >= 0) {
const name = line.slice(0, i).trim();
const message = line.slice(i + 2).trim();
if (!message.length)
continue;
if (Object.hasOwn(output, name))
output[name] += `\n${message}`;
else
output[name] = message;
}
else {
const message = line.trim();
if (!message.length)
continue;
if (Object.hasOwn(output, ""))
output[""] += `\n${message}`;
else
output[""] = message;
}
}
return output;
}
/**
* Join a dictionary of named messages back into a single string.
* - The `""` (empty string) key is emitted as unnamed lines.
* - Named messages are emitted as `name: message`, one line per message line.
* - Empty lines are skipped and each emitted line is trimmed to match `splitMessage()` semantics.
*/
export function joinMessage(input) {
const output = [];
for (const [name, message] of Object.entries(input)) {
for (const line of message.split("\n")) {
const value = line.trim();
if (!value.length)
continue;
output.push(name ? `${name}: ${value}` : value);
}
}
return output.join("\n");
}
/**
* Name a message by applying a `name: ` prefix to it.
* - Assumes each line in the message is a separate error, so each line has the same prefix applied.
*/
export function getNamedMessage(name, message) {
return `${name}: ${message.split("\n").join(`\n${name}: `)}`;
}