UNPKG

@shopify/cli-kit

Version:

A set of utilities, interfaces, and models that are common across all the platform features

407 lines • 11 kB
import { takeRandomFromArray } from './array.js'; import { unstyled } from '../../public/node/output.js'; import { camelCase, capitalCase, constantCase, paramCase, snakeCase, pascalCase } from 'change-case'; const SAFE_RANDOM_BUSINESS_ADJECTIVES = [ 'commercial', 'profitable', 'amortizable', 'branded', 'integrated', 'synergistic', 'consolidated', 'diversified', 'lean', 'niche', 'premium', 'luxury', 'scalable', 'optimized', 'empowered', 'international', 'beneficial', 'fruitful', 'extensive', 'lucrative', 'modern', 'stable', 'strategic', 'adaptive', 'efficient', 'growing', 'sustainable', 'innovative', 'regional', 'specialized', 'focused', 'pragmatic', 'ethical', 'flexible', 'competitive', ]; const SAFE_RANDOM_CREATIVE_ADJECTIVES = [ 'bright', 'impactful', 'stylish', 'colorful', 'modern', 'minimal', 'trendy', 'creative', 'artistic', 'spectacular', 'glamorous', 'luxury', 'retro', 'nostalgic', 'comfy', 'polished', 'fabulous', 'balanced', 'monochrome', 'glitched', 'contrasted', 'elegant', 'textured', 'vibrant', 'harmonious', 'versatile', 'eclectic', 'futuristic', 'idealistic', 'intricate', 'bohemian', 'abstract', 'meticulous', 'refined', 'flamboyant', ]; const SAFE_RANDOM_BUSINESS_NOUNS = [ 'account', 'consumer', 'customer', 'enterprise', 'business', 'venture', 'marketplace', 'revenue', 'vertical', 'portfolio', 'negotiation', 'shipping', 'demand', 'supply', 'growth', 'merchant', 'investment', 'shareholder', 'conversion', 'capital', 'projection', 'upside', 'trade', 'deal', 'merchandise', 'transaction', 'sale', 'franchise', 'subsidiary', 'logistics', 'sponsorship', 'partnership', 'tax', 'policy', 'outsource', 'equity', 'strategy', 'valuation', 'benchmark', 'metrics', 'duplication', ]; const SAFE_RANDOM_CREATIVE_NOUNS = [ 'vibe', 'style', 'moment', 'mood', 'flavor', 'look', 'appearance', 'perspective', 'aspect', 'ambience', 'quality', 'backdrop', 'focus', 'tone', 'inspiration', 'imagery', 'aesthetics', 'palette', 'ornamentation', 'contrast', 'colorway', 'visuals', 'typography', 'composition', 'scale', 'symmetry', 'gradients', 'proportions', 'textures', 'harmony', 'shapes', 'patterns', ]; /** * Generates a random name by combining an adjective and noun. * * @param family - Theme to use for the random name (business or creative). * @returns A random name generated by combining an adjective and noun. */ export function getRandomName(family = 'business') { const mapping = { business: { adjectives: SAFE_RANDOM_BUSINESS_ADJECTIVES, nouns: SAFE_RANDOM_BUSINESS_NOUNS, }, creative: { adjectives: SAFE_RANDOM_CREATIVE_ADJECTIVES, nouns: SAFE_RANDOM_CREATIVE_NOUNS, }, }; return `${takeRandomFromArray(mapping[family].adjectives)}-${takeRandomFromArray(mapping[family].nouns)}`; } /** * Given a string, it returns it with the first letter capitalized. * * @param str - String to capitalize. * @returns String with the first letter capitalized. */ export function capitalize(str) { return str.substring(0, 1).toUpperCase() + str.substring(1); } /** * Given a list of items, it returns a pluralized string based on the amount of items. * * @param items - List of items. * @param plural - Supplier used when the list of items has more than one item. * @param singular - Supplier used when the list of items has a single item. * @param none - Supplier used when the list has no items. * @returns The {@link TokenItem} supplied by the {@link plural}, {@link singular}, or {@link none} functions. */ export function pluralize(items, plural, singular, none) { if (items.length === 1) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return singular(items[0]); } if (items.length > 1) { return plural(items); } if (none) { return none(); } return ''; } /** * Try to convert a string to an int, falling back to undefined if unable to. * * @param maybeInt - String to convert to an int. * @returns The int if it was able to convert, otherwise undefined. */ export function tryParseInt(maybeInt) { let asInt; if (maybeInt !== undefined) { asInt = parseInt(maybeInt, 10); if (isNaN(asInt)) { asInt = undefined; } } return asInt; } /** * Transforms a matrix of strings into a single string with the columns aligned. * * @param lines - Array of rows, where each row is an array of strings (representing columns). * @returns A string with the columns aligned. */ export function linesToColumns(lines) { const widths = []; for (let i = 0; lines[0] && i < lines[0].length; i++) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const columnRows = lines.map((line) => line[i]); widths.push(Math.max(...columnRows.map((row) => unstyled(row).length))); } const paddedLines = lines .map((line) => { return line .map((col, index) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return `${col}${' '.repeat(widths[index] - unstyled(col).length)}`; }) .join(' ') .trimEnd(); }) .join('\n'); return paddedLines; } /** * Given a string, it transforms it to a slug (lowercase, hyphenated, no special chars, trimmed...). * * @param str - String to slugify. * @returns The slugified string. */ export function slugify(str) { return str .toLowerCase() .trim() .replace(/[^\w\s-]/g, '') .replace(/[\s_-]+/g, '-') .replace(/^-+|-+$/g, ''); } /** * Given a string, it returns it with the special regex characters escaped. * More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping. * * @param str - String to escape. * @returns The escaped string. */ export function escapeRegExp(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } /** * Transform a string to camelCase. * * @param input - String to escape. * @returns The escaped string. */ export function camelize(input) { return camelCase(input); } /** * Transform a string to capitalCase. * * @param input - String to transform. * @returns The transformed string. */ export function capitalizeWords(input) { return capitalCase(input); } /** * Transform a string to param-case. * * @param input - String to transform. * @returns The transformed string. */ export function hyphenate(input) { return paramCase(input); } /** * Transform a string to snake_case. * * @param input - String to transform. * @returns The transformed string. */ export function underscore(input) { return snakeCase(input); } /** * Transform a string to CONSTANT_CASE. * * @param input - String to transform. * @returns The transformed string. */ export function constantize(input) { return constantCase(input); } /** * Given a date, return a formatted string like "2021-01-01 12:00:00". * * @param date - Date to format. * @returns The transformed string. */ export function formatDate(date) { const components = date.toISOString().split('T'); const dateString = components[0] ?? date.toDateString(); const timeString = components[1]?.split('.')[0] ?? date.toTimeString(); return `${dateString} ${timeString}`; } /** * Given a date in UTC ISO String format, return a formatted string in local time like "2021-01-01 12:00:00". * * @param dateString - UTC ISO Date String. * @returns The transformed string in local system time. */ export function formatLocalDate(dateString) { const dateObj = new Date(dateString); const localDate = new Date(Date.UTC(dateObj.getFullYear(), dateObj.getMonth(), dateObj.getDate(), dateObj.getHours(), dateObj.getMinutes(), dateObj.getSeconds())); return formatDate(localDate); } /** * Given a list of items, it returns a string with the items joined by commas and the last item joined by "and". * All items are wrapped in double quotes. * For example: ["a", "b", "c"] returns "a", "b" and "c". * * @param items - List of items. * @returns The joined string. */ export function joinWithAnd(items) { if (items.length === 0) return ''; if (items.length === 1) return `"${items[0]}"`; return `${items .slice(0, -1) .map((item) => `"${item}"`) .join(', ')} and "${items[items.length - 1]}"`; } /** * Given a string, it returns the PascalCase form of it. * Eg: "pascal_case" returns "PascalCase". * * @param str - String to PascalCase. * @returns String with all the first letter capitalized with no spaces. */ export function pascalize(str) { return pascalCase(str); } /** * Given a string that represents a list of delimited tokens, it returns the normalized string representing the same * list, without empty elements, sorted, and with no duplicates. * * @param delimitedString - String to normalize. * @param delimiter - Delimiter used to split the string into tokens. * @returns String with the normalized list of tokens. */ export function normalizeDelimitedString(delimitedString, delimiter = ',') { if (!delimitedString) return; const items = delimitedString.split(delimiter).map((value) => value.trim()); const nonEmptyItems = items.filter((value) => value !== ''); const sortedItems = nonEmptyItems.sort(); const uniqueSortedItems = [...new Set(sortedItems)]; return uniqueSortedItems.join(delimiter); } /** * Given two dates, it returns a human-readable string representing the time elapsed between them. * * @param from - Start date. * @param to - End date. * @returns A string like "5 minutes ago" or "2 days ago". */ export function timeAgo(from, to) { const seconds = Math.floor((to.getTime() - from.getTime()) / 1000); if (seconds < 60) return `${formatTimeUnit(seconds, 'second')} ago`; const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${formatTimeUnit(minutes, 'minute')} ago`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${formatTimeUnit(hours, 'hour')} ago`; const days = Math.floor(hours / 24); return `${formatTimeUnit(days, 'day')} ago`; } function formatTimeUnit(count, unit) { return `${count} ${unit}${count === 1 ? '' : 's'}`; } //# sourceMappingURL=string.js.map