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: 134
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/devwizardhq/laravel-enumify
Requires
- php: ^8.2
- illuminate/contracts: ^10.0||^11.0||^12.0
- laravel/prompts: ^0.3
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
This package is auto-updated.
Last update: 2026-01-25 07:25:42 UTC
README
Auto-generate TypeScript enums from Laravel PHP enums. Refactor hardcoded values and normalize enum keys.
Laravel Enumify keeps frontend TypeScript enums in sync with backend PHP enums automatically. It also scans your codebase for hardcoded enum values and can refactor them to use proper enum references. Includes tools for normalizing enum case names to UPPERCASE.
Features
- 🔄 Automatic Sync – Runs during
npm run devandnpm run buildvia the Vite plugin - 🧭 Wayfinder-Level DX – One install command to scaffold everything
- 🏷️ Labels Support –
label()or staticlabels()become TS maps - 🎨 Custom Methods – Public zero-arg scalar methods become TS maps
- 📦 Barrel Exports – Optional
index.tsfor clean imports - ⚡ Smart Caching – Only regenerate changed files using hashes
- 🔒 Git-Friendly –
.gitkeepand strict.gitignorepatterns supported - 🔧 Refactor Command – Scan and fix hardcoded enum values in your codebase
- 🔠 Key Normalization – Convert enum keys to UPPERCASE and update all references
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
.gitignorelines 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
- Configure the mode in
config/enumify.php:
'localization' => [ 'mode' => 'react', // 'react' | 'vue' | 'none' ],
- 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); }, }; }
- 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
enumify:refactor
Scan your codebase for hardcoded enum values and refactor them to use proper enum references. Only columns with enum casts in models will be refactored. This command also supports normalizing enum case names to UPPERCASE and updating all references throughout your application.
Available Options
| Option | Short | Description |
|---|---|---|
--fix |
-f |
Apply refactoring changes to files |
--dry-run |
-d |
Preview changes without modifying files |
--enum= |
-e |
Target a specific enum class by short name (e.g., OrderStatus) |
--path= |
-p |
Limit scan to a specific directory (e.g., app/Models) |
--interactive |
-i |
Run in interactive mode with guided prompts |
--json |
-j |
Output results in JSON format |
--backup |
Create backups before applying changes | |
--include= |
File patterns to include (e.g., *.php) |
|
--exclude= |
Paths or patterns to exclude from scanning | |
--report= |
Export report to file (formats: json, csv, md) |
|
--detailed |
Show detailed output with code context | |
--normalize-keys |
Convert enum keys to UPPERCASE and fix all references |
Scanning for Hardcoded Values
# Scan and display hardcoded enum values php artisan enumify:refactor # Preview what changes would be made php artisan enumify:refactor --dry-run # Apply fixes with backup php artisan enumify:refactor --fix --backup # Target a specific enum php artisan enumify:refactor --enum=OrderStatus # Limit scan to a directory php artisan enumify:refactor --path=app/Services # Export a markdown report php artisan enumify:refactor --report=refactor-report.md
Note: The refactor command only processes columns that have an enum cast defined in a model. If no cast is available for a column, it will skip refactoring for that column.
Key Normalization (UPPERCASE)
The --normalize-keys flag converts enum case names from any case format to UPPERCASE and updates all references in your codebase:
case Active→case ACTIVEcase pending→case PENDINGcase InProgress→case IN_PROGRESS
# Preview key normalization changes php artisan enumify:refactor --normalize-keys --dry-run # Apply key normalization with backup php artisan enumify:refactor --normalize-keys --fix --backup
Interactive Mode
Run the command interactively with guided prompts:
php artisan enumify:refactor --interactive
This mode allows you to:
- Choose between scan, preview, apply, or normalize modes
- Select which enums to check
- Specify the directory to scan
- Confirm changes before applying
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 enumresources/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:
- Create a feature branch from
main - Make changes with focused commits
- Run tests/builds locally
- 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 testandcomposer test-coverage - Node:
pnpm run build && pnpm run typecheck
Troubleshooting
- Missing enums folder: run
php artisan enumify:installor ensureresources/js/enums/.gitkeepexists. - Imports fail during build: ensure the Vite plugin is enabled and runs before
laravel(). - Enums not discovered: check
config/enumify.phppaths and include/exclude filters.
Changelog
See CHANGELOG.md
License
MIT. See LICENSE.md