Data transformation, serialization, and manipulation utilities including YAML processing, deep object merging, filename sanitization, and various string and object utilities for data handling operations.
Convert objects to YAML format with customizable options.
/**
* Serialize object to YAML string with configuration options
* @param object - Object to serialize to YAML
* @param skipInvalid - Whether to skip invalid values (default: false)
* @param noRefs - Whether to disable references (default: false)
* @returns YAML string representation
*/
function serializeToYaml(
object: any,
skipInvalid?: boolean,
noRefs?: boolean
): string;Usage Examples:
import { serializeToYaml } from "builder-util";
// Basic YAML serialization
const config = {
name: "myapp",
version: "1.0.0",
scripts: {
build: "npm run compile",
test: "jest"
},
dependencies: ["react", "typescript"]
};
const yamlString = serializeToYaml(config);
console.log(yamlString);
// Output:
// name: myapp
// version: 1.0.0
// scripts:
// build: npm run compile
// test: jest
// dependencies:
// - react
// - typescript
// With options
const yamlWithOptions = serializeToYaml(config, true, true);
// Handle objects with circular references
const objectWithRefs = { name: "test" };
objectWithRefs.self = objectWithRefs;
const safeYaml = serializeToYaml(objectWithRefs, false, true);Deep merge objects with special handling for arrays.
/**
* Deep merge objects with array deduplication
* @param target - Target object to merge into
* @param objects - Source objects to merge from
* @returns Merged object of target type
*/
function deepAssign<T>(target: T, ...objects: Array<any>): T;Usage Examples:
import { deepAssign } from "builder-util";
// Basic deep merge
const defaultConfig = {
build: {
target: "es2018",
sourcemap: true
},
plugins: ["plugin1"]
};
const userConfig = {
build: {
minify: true
},
plugins: ["plugin2"]
};
const merged = deepAssign(defaultConfig, userConfig);
// Result: {
// build: {
// target: "es2018",
// sourcemap: true,
// minify: true
// },
// plugins: ["plugin1", "plugin2"] // Arrays are merged and deduplicated
// }
// Multiple source objects
const config1 = { a: 1, nested: { x: 1 } };
const config2 = { b: 2, nested: { y: 2 } };
const config3 = { c: 3, nested: { z: 3 } };
const final = deepAssign(config1, config2, config3);
// Result: { a: 1, b: 2, c: 3, nested: { x: 1, y: 2, z: 3 } }Sanitize filenames and extract complete file extensions.
/**
* Sanitize filename for cross-platform compatibility
* @param s - Filename string to sanitize
* @param normalizeNfd - Whether to normalize NFD (default: false)
* @returns Sanitized filename string
*/
function sanitizeFileName(s: string, normalizeNfd?: boolean): string;
/**
* Get complete file extension including compound extensions
* @param filename - Filename to extract extension from
* @returns Complete extension (e.g., ".tar.gz", ".dmg.blockmap")
*/
function getCompleteExtname(filename: string): string;Usage Examples:
import { sanitizeFileName, getCompleteExtname } from "builder-util";
// Sanitize problematic filenames
const unsafeFilename = 'my<file>name|with?chars*.txt';
const safeFilename = sanitizeFileName(unsafeFilename);
console.log(safeFilename); // "my file name with chars .txt"
// Handle Unicode normalization
const unicodeFilename = "café.txt";
const normalizedFilename = sanitizeFileName(unicodeFilename, true);
// Extract complete extensions
const simpleExt = getCompleteExtname("file.txt"); // ".txt"
const compoundExt = getCompleteExtname("archive.tar.gz"); // ".tar.gz"
const dmgExt = getCompleteExtname("app.dmg.blockmap"); // ".dmg.blockmap"
const noExt = getCompleteExtname("README"); // ""
// Use in build processes
const outputFile = sanitizeFileName(`${appName}-${version}.exe`);
const archiveExtension = getCompleteExtname(inputFile);Convert nested Map structures to plain objects and validate object keys.
/**
* Convert nested Map structures to plain objects recursively
* @param map - RecursiveMap to convert
* @returns Plain object representation
*/
function mapToObject(map: RecursiveMap): any;
/**
* Validate that a key is safe for object property access
* @param key - Key to validate
* @returns True if key is valid, false if dangerous
*/
function isValidKey(key: any): boolean;
type RecursiveMap = Map<any, RecursiveMap | any>;Usage Examples:
import { mapToObject, isValidKey } from "builder-util";
// Convert nested Maps to objects
const nestedMap = new Map([
["config", new Map([
["development", { debug: true }],
["production", { debug: false }]
])],
["version", "1.0.0"]
]);
const plainObject = mapToObject(nestedMap);
// Result: {
// config: {
// development: { debug: true },
// production: { debug: false }
// },
// version: "1.0.0"
// }
// Validate object keys for security
const safeKey = "normalProperty";
const dangerousKey = "__proto__";
console.log(isValidKey(safeKey)); // true
console.log(isValidKey(dangerousKey)); // false
console.log(isValidKey("constructor")); // false
// Safe object property assignment
function safeAssign(obj: any, key: any, value: any) {
if (isValidKey(key)) {
obj[key] = value;
} else {
throw new Error(`Dangerous key: ${key}`);
}
}String manipulation and validation functions.
/**
* Conditional execution utility
* @param value - Value to check and use
* @param task - Function to execute if value is not null/undefined
* @returns Result of task or null if value is nullish
*/
function use<T, R>(value: T | Nullish, task: (value: T) => R): R | null;
/**
* Type guard for empty or whitespace-only strings
* @param s - String to check
* @returns True if string is empty, null, undefined, or only whitespace
*/
function isEmptyOrSpaces(s: string | Nullish): s is "" | Nullish;
/**
* Validate token characters against allowed pattern
* @param token - Token string to validate
* @returns True if token contains only valid characters
*/
function isTokenCharValid(token: string): boolean;
/**
* Check if environment variable represents a true value
* @param value - Environment variable value
* @returns True for "true", "", "1", false otherwise
*/
function isEnvTrue(value: string | Nullish): boolean;
/**
* Detect if running in CI pull request environment
* @returns True if running in a pull request build
*/
function isPullRequest(): boolean;
type Nullish = null | undefined;Usage Examples:
import {
use,
isEmptyOrSpaces,
isTokenCharValid,
isEnvTrue,
isPullRequest
} from "builder-util";
// Conditional execution
const config = loadOptionalConfig();
const processedConfig = use(config, (cfg) => {
return {
...cfg,
processed: true,
timestamp: Date.now()
};
});
// processedConfig is null if config was null/undefined
// String validation
const userInput = " ";
if (isEmptyOrSpaces(userInput)) {
console.log("Input is empty or whitespace only");
}
const token = "abc.def/123-456+789=";
if (isTokenCharValid(token)) {
console.log("Token is valid");
}
// Environment variable handling
const shouldDebug = isEnvTrue(process.env.DEBUG); // true for "true", "1", ""
const isProduction = isEnvTrue(process.env.PRODUCTION); // false for "false", "0", null
// CI detection
if (isPullRequest()) {
console.log("Running in PR environment, skipping deployment");
}Utilities for working with arrays and Maps.
/**
* Add unique value to Map array, creating array if needed
* @param map - Map containing arrays as values
* @param key - Key to add value under
* @param value - Value to add to array
*/
function addValue<K, T>(map: Map<K, Array<T>>, key: K, value: T): void;
/**
* Replace "default" entries in array with default values
* @param inList - Input array that may contain "default" entries
* @param defaultList - Default values to substitute for "default"
* @returns Array with "default" entries replaced
*/
function replaceDefault(inList: Array<string> | Nullish, defaultList: Array<string>): Array<string>;Usage Examples:
import { addValue, replaceDefault } from "builder-util";
// Build collections with unique values
const dependencies = new Map<string, Array<string>>();
addValue(dependencies, "react", "react-dom");
addValue(dependencies, "react", "react-router");
addValue(dependencies, "react", "react-dom"); // Won't duplicate
addValue(dependencies, "vue", "vue-router");
console.log(dependencies);
// Map {
// "react" => ["react-dom", "react-router"],
// "vue" => ["vue-router"]
// }
// Handle default value replacement
const userTargets = ["linux", "default", "win32"];
const defaultTargets = ["darwin", "win32"];
const finalTargets = replaceDefault(userTargets, defaultTargets);
// Result: ["linux", "darwin", "win32", "win32"]
// Note: "default" is replaced with default values
// Handle null/undefined input
const emptyTargets = replaceDefault(null, ["linux", "win32"]);
// Result: ["linux", "win32"]import { deepAssign, serializeToYaml, isEmptyOrSpaces } from "builder-util";
class ConfigManager {
mergeConfigurations(base: any, ...overrides: Array<any>): any {
return deepAssign({}, base, ...overrides);
}
async saveConfig(config: any, filepath: string) {
if (!isEmptyOrSpaces(filepath)) {
const yamlContent = serializeToYaml(config, false, true);
await fs.writeFile(filepath, yamlContent, "utf8");
}
}
}
// Usage
const configManager = new ConfigManager();
const baseConfig = { timeout: 5000, retries: 3 };
const devConfig = { debug: true, timeout: 10000 };
const userConfig = { retries: 5 };
const finalConfig = configManager.mergeConfigurations(
baseConfig,
devConfig,
userConfig
);
await configManager.saveConfig(finalConfig, "config.yml");import { isValidKey, sanitizeFileName, mapToObject } from "builder-util";
class DataProcessor {
processUserData(userData: Map<string, any>): any {
// Convert Map to object safely
const obj = mapToObject(userData);
// Process with key validation
const processed = {};
for (const [key, value] of Object.entries(obj)) {
if (isValidKey(key)) {
processed[key] = this.processValue(value);
}
}
return processed;
}
generateSafeFilename(input: string): string {
return sanitizeFileName(input.trim());
}
}import { isEnvTrue, isPullRequest, use } from "builder-util";
class BuildProcessor {
async process(): Promise<BuildResult> {
const isDevelopment = isEnvTrue(process.env.NODE_ENV);
const isCI = isPullRequest();
const buildOptions = {
minify: !isDevelopment,
sourceMaps: isDevelopment,
skipTests: isCI,
};
// Conditional processing
const testResults = use(buildOptions.skipTests ? null : "test", async () => {
return await this.runTests();
});
return {
buildOptions,
testResults
};
}
}