xpm
Version:
The xPack project manager command line tool
224 lines (212 loc) • 7.97 kB
text/typescript
/*
* This file is part of the xPack project (http://xpack.github.io).
* Copyright (c) 2021-2026 Liviu Ionescu. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software
* for any purpose is hereby granted, under the terms of the MIT license.
*
* If a copy of the license was not distributed with this file, it can
* be obtained from https://opensource.org/license/mit.
*/
// ============================================================================
/**
* Determines whether a value is a JavaScript primitive.
*
* @remarks
* In JavaScript, primitives are immutable values that are not objects:
* `string`, `number`, `bigint`, `boolean`, `undefined`, and `symbol`.
* This function
* also treats null as a primitive, following JavaScript's `typeof` behavior
* despite null being technically an object type.
*
* Returns `true` for: `string`, `number`, `bigint`, `boolean`, `undefined`,
* `symbol`, and `null`.
*
* Returns `false` for: objects, arrays, functions, and class instances.
*
* Useful for distinguishing between value types and reference types when
* processing JSON data or validating configuration inputs.
*
* @param value - The value to test.
* @returns `true` if the value is a primitive or `null`, `false` otherwise.
*/
export function isPrimitive(value: unknown): boolean {
return (
(typeof value !== 'object' && typeof value !== 'function') || value === null
)
}
/**
* Determines whether a value is a string.
*
* @remarks
* This function acts as a TypeScript type guard, narrowing the type to
* `string` within conditional blocks. This enables safe string operations
* without type assertions.
*
* Example usage:
* ```typescript
* if (isString(value)) {
* // TypeScript knows value is a string here
* const length = value.length;
* }
* ```
*
* Only returns `true` for primitive string values, not String objects
* created with `new String()`.
*
* @param value - The value to test.
* @returns `true` if the value is a string, `false` otherwise.
*/
export function isString(value: unknown): boolean {
return typeof value === 'string'
}
/**
* Determines whether a value is a finite number.
*
* @remarks
* This function validates that a value is both a number type and
* mathematically finite, excluding `NaN`, `Infinity`, and `-Infinity`.
* This is essential when validating numeric configuration values or
* processing JSON data where numeric fields must contain valid,
* computable values.
*
* Returns `true` for: all finite numeric values, including `0`, negative
* numbers, and floating-point numbers.
*
* Returns `false` for: `NaN`, `Infinity`, `-Infinity`, strings, objects,
* arrays, and any non-number types.
*
* Only returns `true` for primitive number values, not Number objects
* created with `new Number()`.
*
* The finiteness check is crucial for mathematical operations and ensures
* that numeric properties in `package.json` or configuration files contain
* usable values rather than special numeric constants that could cause
* unexpected behaviour in calculations.
*
* @param value - The value to test.
* @returns `true` if the value is a finite number, `false` otherwise.
*/
export function isNumber(value: unknown): boolean {
return typeof value === 'number' && isFinite(value)
}
/**
* Determines whether a value is a boolean.
*
* @remarks
* Tests for primitive boolean values (`true` or `false`). Useful when
* validating configuration options or parsing JSON where boolean flags
* need to be distinguished from truthy/falsy values.
*
* Only returns `true` for the primitive boolean values `true` and `false`,
* not for boolean objects created with `new Boolean()`.
*
* Note: This does not check for truthy or falsy values - it only returns
* `true` for actual boolean primitives. Use standard JavaScript truthiness
* checks for conditional logic.
*
* @param value - The value to test.
* @returns `true` if the value is a boolean, `false` otherwise.
*/
export function isBoolean(value: unknown): boolean {
return typeof value === 'boolean'
}
/**
* Determines whether a value is a non-array object.
*
* @remarks
* This function distinguishes between objects and arrays, which is crucial
* when processing JSON structures where both use the object type but require
* different handling.
*
* Returns `true` for: plain objects, class instances, null and other
* object types.
*
* Returns `false` for: arrays, primitives, and functions.
*
* Note: Arrays in JavaScript are objects, so this function explicitly
* excludes them using `Array.isArray()`. Use {@link isJsonObject} for
* stricter JSON object validation that also excludes `undefined` and `null`.
*
* @param value - The value to test.
* @returns `true` if the value is a non-array object, `false` otherwise.
*/
export function isObject(value: unknown): boolean {
return typeof value === 'object' && !Array.isArray(value)
}
// ----------------------------------------------------------------------------
/**
* Determines whether a value is a JSON object.
*
* @remarks
* Validates that a value represents a JSON object, which is stricter than
* JavaScript's general object type. This is essential when working with
* parsed JSON data or `package.json` structures.
*
* Returns `true` for: plain objects (non-null,
* non-primitive, non-array values).
*
* Returns `false` for: undefined, null, primitives (string, number,
* boolean, etc.), and arrays.
*
* This is the primary validation function for JSON objects in the <b>xpm</b>
* codebase, used extensively when parsing `package.json` sections like
* `xpack.properties`, `xpack.buildConfigurations`, etc.
*
* @param value - The value to test.
* @returns `true` if the value is a JSON object, `false` otherwise.
*/
export function isJsonObject(value: unknown): boolean {
return value !== undefined && !isPrimitive(value) && !Array.isArray(value)
}
/**
* Determines whether a value is a JSON array.
*
* @remarks
* Validates that a value represents a JSON array, excluding `undefined`.
* This ensures the value is a proper array that could have been parsed
* from JSON.
*
* Returns `true` for: any array, including empty arrays.
*
* Returns `false` for: undefined, null, objects, and primitives.
*
* The undefined check is important for distinguishing between optional
* properties that are missing (undefined) versus properties that are
* explicitly empty arrays. This is common in package.json where array
* fields may be absent or present but empty.
*
* @param value - The value to test.
* @returns `true` if the value is a JSON array, `false` otherwise.
*/
export function isJsonArray(value: unknown): boolean {
return value !== undefined && Array.isArray(value)
}
/**
* Determines whether a value is a non-empty JSON object.
*
* @remarks
* Combines JSON object validation with a non-empty check, ensuring the
* object has at least one property. This is useful for validating
* configuration sections that must contain data to be meaningful.
*
* Returns `true` for: objects with one or more enumerable properties.
*
* Returns `false` for: empty objects `{}`, undefined, null, primitives,
* arrays, and any non-object types.
*
* The enumerable properties check uses `Object.keys()`, which only counts
* own enumerable string-keyed properties, not inherited properties or
* symbol-keyed properties.
*
* Common use cases include validating `xpack.actions`, `xpack.dependencies`,
* and other `package.json` sections where an empty object would be
* meaningless.
*
* @param value - The value to test.
* @returns `true` if the value is a non-empty JSON object, `false` otherwise.
*/
export function isNonEmptyJsonObject(value: unknown): boolean {
return isJsonObject(value) && Object.keys(value as object).length > 0
}
// ----------------------------------------------------------------------------