Skip to content

normaliseStats ​

Format project stats into a normalized array for display. Converts raw numeric stats into human-readable format with proper labels.

Note: normalizeStats is available as a deprecated alias for normaliseStats. Prefer normaliseStats in new code.

Signature ​

tsx
function normaliseStats(
  stats: Record<string, unknown>,
  type: ProjectType
): NormalizedStat[]

Parameters ​

ParameterTypeDescription
statsRecord<string, unknown>Raw stats object
typeProjectTypeProject type for context

Returns ​

NormalizedStat[] - Array of normalized stat objects

Types ​

tsx
interface NormalizedStat {
  label: string
  value: string | number
  unit?: string
}

Behavior ​

Transforms raw stats into display-friendly format. Each stat is conditionally included only if it has a value.

Note: Although the type parameter is accepted, the function does not filter stats by project type — it processes all stat keys present in the stats object regardless of the type value. The "Available for" labels in each section below describe which project types populate those stats upstream during normalisation, not what normalizeStats filters on.

Number Formatting ​

Numbers are formatted with suffixes:

  • ≥ 1,000,000 → X.XM (e.g., 1234567 → 1.2M)
  • ≥ 1,000 → X.XK (e.g., 1234 → 1.2K)
  • < 1,000 → raw number (e.g., 56 → 56)

Date Formatting ​

Dates are formatted as Mon DD, YYYY (e.g., Jan 15, 2024).

All Output Labels ​

GitHub Stats ​

Stat KeyLabelFormatExample Output
starsStarsNumber (K/M suffix){ label: 'Stars', value: '1.2K' }
forksForksNumber (K/M suffix){ label: 'Forks', value: '56' }

Available for: github, hybrid

NPM Stats ​

Stat KeyLabelFormatExample Output
downloadsDownloadsNumber (K/M suffix) + "month" unit{ label: 'Downloads', value: '1.2M', unit: 'month' }
versionVersionPrefixed with "v"{ label: 'Version', value: 'v1.2.3' }

Available for: npm, hybrid

Product Hunt Stats ​

Stat KeyLabelFormatExample Output
upvotesUpvotesNumber (K/M suffix){ label: 'Upvotes', value: '456' }
commentsCommentsNumber (K/M suffix){ label: 'Comments', value: '89' }
launchDateLaunchedFormatted date{ label: 'Launched', value: 'Jan 15, 2024' }

Available for: product-hunt

YouTube Stats ​

Stat KeyLabelFormatExample Output
subscribersSubscribersNumber (K/M suffix){ label: 'Subscribers', value: '125.4K' }
viewsViewsNumber (K/M suffix){ label: 'Views', value: '2.5M' }

Note: latestVideoTitle, latestVideoUrl, and latestVideoPublishedAt are NOT included in normalized stats — they are displayed separately by components as links.

Available for: youtube

Gumroad Stats ​

Stat KeyLabelFormatExample Output
formattedRevenueRevenueString (pass-through){ label: 'Revenue', value: '$1,234.56' }
salesCountSalesNumber (K/M suffix){ label: 'Sales', value: '456' }
subscriberCountSubscribersNumber (K/M suffix){ label: 'Subscribers', value: '89' }

Available for: gumroad

Lemon Squeezy Stats ​

Stat KeyLabelFormatExample Output
formattedMRRMRRString (pass-through){ label: 'MRR', value: '$1,234.56' }
orderCountOrdersNumber (K/M suffix){ label: 'Orders', value: '234' }
customerCountCustomersNumber (K/M suffix){ label: 'Customers', value: '156' }

Available for: lemonsqueezy

Dev.to Stats ​

Stat KeyLabelFormatExample Output
articleCountArticlesNumber (K/M suffix){ label: 'Articles', value: '45' }
totalViewsViewsNumber (K/M suffix){ label: 'Views', value: '125.4K' }
totalReactionsReactionsNumber (K/M suffix){ label: 'Reactions', value: '67' }

Available for: devto

Example ​

tsx
import { normaliseStats } from '@manningworks/projex'

// GitHub project
const githubStats = normaliseStats(
  { stars: 1234, forks: 56 },
  'github'
)
// [
//   { label: 'Stars', value: '1.2K' },
//   { label: 'Forks', value: '56' },
// ]

// YouTube channel
const youtubeStats = normaliseStats(
  { subscribers: 125400, views: 2500000 },
  'youtube'
)
// [
//   { label: 'Subscribers', value: '125.4K' },
//   { label: 'Views', value: '2.5M' },
// ]

// Gumroad product
const gumroadStats = normaliseStats(
  { formattedRevenue: '$5,678.90', salesCount: 234, subscriberCount: 89 },
  'gumroad'
)
// [
//   { label: 'Revenue', value: '$5,678.90' },
//   { label: 'Sales', value: '234' },
//   { label: 'Subscribers', value: '89' },
// ]

Usage with Components ​

tsx
import { normaliseStats } from '@manningworks/projex'

function StatList({ project }) {
  const stats = normaliseStats(project.stats || {}, project.type)
  
  return (
    <ul>
      {stats.map((stat) => (
        <li key={stat.label}>
          <strong>{stat.label}:</strong> {stat.value}
          {stat.unit && <span>/{stat.unit}</span>}
        </li>
      ))}
    </ul>
  )
}

Skipped Fields ​

The following stats properties are intentionally NOT included in normalized output because they are displayed differently by components:

PropertyReason
latestVideoTitleDisplayed as a link, not a stat
latestVideoUrlUsed as the URL for the link
latestVideoPublishedAtUsed for date sorting, not display