devwizardhq/laravel-enumify

Auto-generate TypeScript enums and types from Laravel PHP enums with an Artisan command and Vite integration.

Fund package maintenance!
DevWizard

Installs: 52

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/devwizardhq/laravel-enumify

v1.0.0 2026-01-16 01:11 UTC

This package is auto-updated.

Last update: 2026-01-17 07:37:51 UTC


README

Latest Version on Packagist GitHub Tests Action Status Total Downloads NPM Version NPM Downloads

Auto-generate TypeScript enums and maps from Laravel PHP enums, with Vite integration.

Laravel Enumify keeps frontend TypeScript enums in sync with backend PHP enums automatically. It generates files before Vite compiles, so imports never fail and no runtime fetching is needed.

Features

  • 🔄 Automatic Sync – Runs during npm run dev and npm run build via the Vite plugin
  • 🧭 Wayfinder-Level DX – One install command to scaffold everything
  • 🏷️ Labels Supportlabel() or static labels() become TS maps
  • 🎨 Custom Methods – Public zero-arg scalar methods become TS maps
  • 📦 Barrel Exports – Optional index.ts for clean imports
  • Smart Caching – Only regenerate changed files using hashes
  • 🔒 Git-Friendly.gitkeep and strict .gitignore patterns supported

Requirements

  • PHP 8.2+
  • Laravel 10, 11, or 12
  • Node.js 18+
  • Vite 4, 5, 6, or 7

Package Links

Installation

1) Install the Laravel package

composer require devwizardhq/laravel-enumify

2) Run the install command

php artisan enumify:install

This will:

  • Create resources/js/enums/
  • Create resources/js/enums/.gitkeep
  • Print the .gitignore lines to add (and offer to append them)
  • Offer to publish the config file

3) Configure Vite

If the installer successfully installed the plugin, you just need to add it to your vite.config.js:

import { defineConfig } from "vite";
import laravel from "laravel-vite-plugin";
import enumify from "@devwizard/vite-plugin-enumify";

export default defineConfig({
    plugins: [
        enumify(),
        laravel({
            input: ["resources/js/app.ts"],
            refresh: true,
        }),
    ],
});

If the automatic installation skipped or failed, manually install the plugin:

npm install @devwizard/vite-plugin-enumify --save-dev
# or
pnpm add -D @devwizard/vite-plugin-enumify
# or
yarn add -D @devwizard/vite-plugin-enumify

Usage

Basic Enum

// app/Enums/OrderStatus.php
<?php

namespace App\Enums;

enum OrderStatus: string
{
    case PENDING = 'pending';
    case PROCESSING = 'processing';
    case SHIPPED = 'shipped';
    case DELIVERED = 'delivered';
}

Generated TypeScript:

// resources/js/enums/order-status.ts
export const OrderStatus = {
    PENDING: "pending",
    PROCESSING: "processing",
    SHIPPED: "shipped",
    DELIVERED: "delivered",
} as const;

export type OrderStatus = (typeof OrderStatus)[keyof typeof OrderStatus];

export const OrderStatusUtils = {
    options(): OrderStatus[] {
        return Object.values(OrderStatus);
    },
};

With Labels & Custom Methods

enum CampusStatus: string
{
    case ACTIVE = 'active';
    case SUSPENDED = 'suspended';
    case INACTIVE = 'inactive';

    public function label(): string
    {
        return match ($this) {
            self::ACTIVE => 'Active',
            self::SUSPENDED => 'Suspended',
            self::INACTIVE => 'Inactive',
        };
    }

    public function color(): string
    {
        return match ($this) {
            self::ACTIVE => 'green',
            self::SUSPENDED => 'red',
            self::INACTIVE => 'gray',
        };
    }

    public function isActive(): bool
    {
        return $this === self::ACTIVE;
    }
}

Generated TypeScript:

export const CampusStatus = {
    ACTIVE: "active",
    SUSPENDED: "suspended",
    INACTIVE: "inactive",
} as const;

export type CampusStatus = (typeof CampusStatus)[keyof typeof CampusStatus];

export const CampusStatusUtils = {
    label(status: CampusStatus): string {
        switch (status) {
            case CampusStatus.ACTIVE:
                return "Active";
            case CampusStatus.SUSPENDED:
                return "Suspended";
            case CampusStatus.INACTIVE:
                return "Inactive";
        }
    },

    color(status: CampusStatus): string {
        switch (status) {
            case CampusStatus.ACTIVE:
                return "green";
            case CampusStatus.SUSPENDED:
                return "red";
            case CampusStatus.INACTIVE:
                return "gray";
        }
    },

    isActive(status: CampusStatus): boolean {
        return status === CampusStatus.ACTIVE;
    },

    options(): CampusStatus[] {
        return Object.values(CampusStatus);
    },
};

Frontend Usage

import { CampusStatus, CampusStatusUtils } from "@/enums/campus-status";

const status: CampusStatus = CampusStatus.ACTIVE;

// Get label
console.log(CampusStatusUtils.label(status)); // 'Active'

// Get custom method value
const badgeColor = CampusStatusUtils.color(status); // 'green'

// Check state (boolean method)
if (CampusStatusUtils.isActive(status)) {
    // Allow access
}

// Get all options (e.g., for a dropdown)
const options = CampusStatusUtils.options();

Localization

Enumify supports automatic localization for React and Vue applications using @devwizard/laravel-localizer-react or @devwizard/laravel-localizer-vue.

Prerequisites: Install the appropriate localization package for your framework:

# For React
npm install @devwizard/laravel-localizer-react

# For Vue
npm install @devwizard/laravel-localizer-vue
  1. Configure the mode in config/enumify.php:
'localization' => [
    'mode' => 'react', // 'react' | 'vue' | 'none'
],
  1. Generated TypeScript will export a use{Enum}Utils hook instead of a static object:
import { useLocalizer } from "@devwizard/laravel-localizer-react";

export const CampusStatus = {
    ACTIVE: "active",
    // ...
} as const;

/**
 * CampusStatus enum methods (PHP-style)
 */
export function useCampusStatusUtils() {
    const { __ } = useLocalizer();

    return {
        label(status: CampusStatus): string {
            switch (status) {
                case CampusStatus.ACTIVE:
                    return __("Active");
                // ...
                default:
                    return status;
            }
        },

        options(): CampusStatus[] {
            return Object.values(CampusStatus);
        },
    };
}
  1. Usage in Components:
import { CampusStatus, useCampusStatusUtils } from "@/enums/campus-status";

function MyComponent() {
    const { label, options } = useCampusStatusUtils();

    return (
        <select>
            {options().map((status) => (
                <option value={status}>{label(status)}</option>
            ))}
        </select>
    );
}

This ensures your enums are fully localized on the frontend while respecting React's Rules of Hooks.

Note: If you enable localization mode but disable label generation (generate_label_maps set to false), the generator will create hooks/composables without the useLocalizer import, since labels are the only feature that uses localization. In this case, you'll get a hook that only contains custom methods and the options() method.

Method Conversion Rules

Enumify will convert methods into TypeScript maps when they meet these rules:

  • Public, non-static, zero-argument methods only
  • Return types must be string, int, float, bool, or nullable/union combinations of those
  • Methods without return types or unsupported return types are skipped
  • Map naming: EnumName + MethodName (pluralized for non-boolean methods)
  • Boolean methods also generate a helper function

Labels are handled separately using label() or labels().

Artisan Commands

enumify:install

php artisan enumify:install

enumify:sync

# Standard sync
php artisan enumify:sync

# Force regenerate all files
php artisan enumify:sync --force

# Preview changes without writing
php artisan enumify:sync --dry-run

# Sync only one enum
php artisan enumify:sync --only="App\Enums\OrderStatus"

# Output as JSON
php artisan enumify:sync --format=json

# Suppress console output (useful for Vite)
php artisan enumify:sync --quiet

Configuration

Publish the config file:

php artisan vendor:publish --tag="enumify-config"
// config/enumify.php
return [
    'paths' => [
        'enums' => ['app/Enums'],
        'output' => 'resources/js/enums',
    ],

    'naming' => [
        'file_case' => 'kebab',
    ],

    'features' => [
        'generate_union_types' => true,
        'generate_label_maps' => true,
        'generate_method_maps' => true,
        'generate_index_barrel' => true,
    ],

    'runtime' => [
        'watch' => true,
    ],

    'filters' => [
        'include' => [],
        'exclude' => [],
    ],
];

Generated Output

  • resources/js/enums/*.ts – one file per enum
  • resources/js/enums/index.ts – barrel exports (optional)
  • resources/js/enums/.enumify-manifest.json – hashes, timestamps, versions

The generator uses atomic writes and skips unchanged files for speed.

Local Development (Monorepo)

This repo contains two packages:

  • packages/laravel-enumify (Composer)
  • packages/vite-plugin-enumify (NPM)

Composer path repository

{
    "repositories": [
        {
            "type": "path",
            "url": "./packages/laravel-enumify",
            "options": { "symlink": true }
        }
    ]
}

Vite plugin workspace

You can install the Vite plugin locally with a workspace or a file path:

pnpm add -D ./packages/vite-plugin-enumify
# or
npm install --save-dev ./packages/vite-plugin-enumify

Git Workflow

Recommended workflow for both packages:

  1. Create a feature branch from main
  2. Make changes with focused commits
  3. Run tests/builds locally
  4. Open a PR and ensure CI passes

Release tip: tag releases after merging to main, then publish to Packagist and NPM.

CI

Suggested pipelines:

  • PHP: composer test and composer test-coverage
  • Node: pnpm run build && pnpm run typecheck

Troubleshooting

  • Missing enums folder: run php artisan enumify:install or ensure resources/js/enums/.gitkeep exists.
  • Imports fail during build: ensure the Vite plugin is enabled and runs before laravel().
  • Enums not discovered: check config/enumify.php paths and include/exclude filters.

Changelog

See CHANGELOG.md

License

MIT. See LICENSE.md