npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reharik/graphql-codegen-smart-enum

v0.3.8

Published

GraphQL Code Generator plugin for SmartEnum generation from schema enums

Downloads

304

Readme

@reharik/graphql-codegen-smart-enum

GraphQL Code Generator plugin that turns your schema's enum types into @reharik/smart-enum definitions. You define enums in your SDL; codegen produces type-safe smart-enum objects with lookup methods, display strings, and full TypeScript inference — no hand-authored enum files to keep in sync with your schema.

What it generates

Given this schema:

"""
Payment processing status
"""
enum PaymentStatus {
  """
  Waiting for payment
  """
  PENDING
  """
  Payment completed successfully
  """
  PAID
  """
  Payment was canceled
  """
  CANCELED @deprecated(reason: "Use VOIDED")
  """
  Payment was voided
  """
  VOIDED
}

enum SortDirection {
  ASC
  DESC
}

The plugin emits:

import { enumeration, type Enumeration } from '@reharik/smart-enum';

const paymentStatusInput = {
  pending: { display: 'Waiting for payment' },
  paid: { display: 'Payment completed successfully' },
  canceled: {
    display: 'Payment was canceled',
    deprecated: true,
    deprecationReason: 'Use VOIDED',
  },
  voided: { display: 'Payment was voided' },
} as const;

const sortDirectionInput = ['asc', 'desc'] as const;

export type PaymentStatus = Enumeration<typeof PaymentStatus>;
export type SortDirection = Enumeration<typeof SortDirection>;

export const PaymentStatus = enumeration<typeof paymentStatusInput>(
  'PaymentStatus',
  { input: paymentStatusInput },
);
export const SortDirection = enumeration<typeof sortDirectionInput>(
  'SortDirection',
  { input: sortDirectionInput },
);

Enum values with descriptions get object input with display metadata. Plain enums without descriptions or deprecations get the compact array form. Deprecated values always force object input so the deprecated flag is preserved.

All member keys are camelCased from the GraphQL value name (IN_REVIEWinReview). If camelCasing causes a collision within an enum, codegen fails with a clear error.

Install

npm install @reharik/smart-enum
npm install -D @reharik/graphql-codegen-smart-enum @graphql-codegen/cli graphql

@reharik/smart-enum is a runtime dependency (generated files import it). The codegen plugin and CLI are dev-only.

Configuration

codegen.ts

import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  schema: './schema.graphql',
  generates: {
    // Standard TypeScript types (optional, from @graphql-codegen/typescript)
    './src/generated/graphql-types.ts': {
      plugins: ['typescript'],
    },
    // Smart-enum definitions
    './src/generated/graphql-smart-enums.ts': {
      plugins: ['@reharik/graphql-codegen-smart-enum'],
      config: {
        emitDescriptionsAsDisplay: true,
      },
    },
  },
};

export default config;

Config options

| Option | Type | Default | Description | | --------------------------- | ---------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | emitDescriptionsAsDisplay | boolean | true | Use GraphQL enum value descriptions as the display field. When false, only enums with deprecated values or @enumMeta directives get object input. | | enumClassSuffix | string | '' | Suffix appended to generated enum names (e.g. 'Enum'PaymentStatusEnum). | | skipEnums | string[] | — | GraphQL enum type names to exclude from output. | | externalEnums | Record<string, string> | — | Map of GraphQL enum type names to import paths for hand-authored enums. Each named enum must also appear in skipEnums. The plugin imports each one and includes it in the generated enumRegistry so patchSchemaEnumSerializers and friends can find it. The plugin does not re-export them as named exports — consumers continue to import hand-authored enums from their original location. |

Enum metadata with @enumMeta

For richer metadata than what GraphQL descriptions provide, use the @enumMeta directive on enum values.

Setup

Define the directive and its input type in your schema:

input EnumMetaProp {
  name: String!
  value: String!
}

directive @enumMeta(
  display: String
  shortDisplay: String
  description: String
  sortOrder: Int
  props: [EnumMetaProp!]
) on ENUM_VALUE

Usage

enum ClaimStatus {
  OPEN @enumMeta(display: "Open", sortOrder: 1)
  IN_REVIEW
    @enumMeta(
      display: "In Review"
      shortDisplay: "Review"
      description: "Waiting for adjudication"
      sortOrder: 2
    )
  CLOSED @enumMeta(display: "Closed", sortOrder: 3)
}

Field resolution order

Each field resolves with these fallbacks:

| Field | Priority | | -------------- | ------------------------------------------------------------------------------------------------ | | display | @enumMeta(display:) → GraphQL value description → derived from key (IN_REVIEWIn Review) | | description | @enumMeta(description:) → GraphQL value description → omitted | | shortDisplay | @enumMeta(shortDisplay:) → omitted | | sortOrder | @enumMeta(sortOrder:) → omitted |

Custom key-value props

Attach arbitrary string properties to enum members:

enum AlbumSortBy {
  CREATED_AT @enumMeta(props: [{ name: "column", value: "created_at" }])
  TITLE @enumMeta(props: [{ name: "column", value: "title" }])
}

Generates:

const albumSortByInput = {
  createdAt: { column: 'created_at' },
  title: { column: 'title' },
} as const;

When @enumMeta on a value only supplies props (no display, shortDisplay, description, or sortOrder), the plugin does not emit a derived display field — even when emitDescriptionsAsDisplay is true.

Prop names that aren't valid JS identifiers use computed keys (e.g. ["weird-key"]: 'value'). Duplicate prop names and reserved names (key, value, display, deprecated, index, etc.) are rejected at codegen time.

Important: @enumMeta and schema pipelines

@enumMeta directives are read from the schema AST. If your pipeline uses printSchema() anywhere (which strips custom directives from enum values), the metadata will be silently lost.

If you use @graphql-codegen/schema-ast to emit a .graphql file that other packages consume, enable includeDirectives: true:

generates:
  ./schema.graphql:
    plugins:
      - schema-ast
    config:
      includeDirectives: true

Without this, @enumMeta directives are dropped from the output file.

Hand-authored enums

Sometimes you want to hand-author an enum — to add custom methods, derive props at runtime, or wrap a third-party value object. List those enum names in skipEnums so the plugin doesn't generate them. But the generated enumRegistry barrel still needs to include them, otherwise the server-side patchSchemaEnumSerializers won't be able to find them when GraphQL calls parseValue on a request argument — and the resolver will receive a raw string instead of a smart-enum instance.

Use externalEnums to bridge the gap:

config:
  skipEnums:
    - ReactionEmoji
    - ViewerOperation
  externalEnums:
    ReactionEmoji: '../hand-authored/reactions'
    ViewerOperation: '../hand-authored/viewerOperations'

The plugin will emit imports for each hand-authored enum and include them in the registry:

import { ReactionEmoji } from '../hand-authored/reactions';
import { ViewerOperation } from '../hand-authored/viewerOperations';

// ... generated enums ...

export const enumRegistry = {
  // ... generated enums ...
  ReactionEmoji,
  ViewerOperation,
} as const;

The registry key is always the GraphQL type name. The plugin does not re-export hand-authored enums as named exports — consumers continue to import them from their original location.

Local development

When developing the plugin locally, reference the built output directly:

generates: {
  './src/generated/graphql-smart-enums.ts': {
    plugins: ['./path/to/dist/index.js'],
  },
}

Related packages

| Package | Purpose | | ------------------------------------------------------------------------------------ | -------------------------------------------- | | @reharik/smart-enum | Core smart-enum library (runtime dependency) | | @reharik/smart-enum-knex | Knex query-level enum revival |

License

MIT