run(function ($type, $line) use ($logger) { $logger->line($line); }); if ($process->isSuccessful()) { $logger->success(implode(' ', $command)); return $process; } $logger->error(implode(' ', $command)); $logger->error('Error output: '.trim($process->getErrorOutput())); $logger->error('Chisel: Your project may be in a partially-modified state — review the output above before continuing.'); return $process; }, ); if (! $process->isSuccessful()) { exit($process->getExitCode()); } } function chiselSkipsNode(): bool { return filter_var( $_ENV['LARAVEL_INSTALLER_NO_NODE'] ?? $_SERVER['LARAVEL_INSTALLER_NO_NODE'] ?? getenv('LARAVEL_INSTALLER_NO_NODE'), FILTER_VALIDATE_BOOL, ); } function chiselRemoveNpmPackages(Chisel $c, string ...$packages): void { if (! chiselSkipsNode()) { $c->npm()->remove(...$packages); return; } foreach ($packages as $package) { $c->file('package.json')->removeLinesContaining('"'.$package.'":'); } } /** * Framework-specific filenames are supplied by the sibling chisel-paths.php * that ships with each Inertia kit (React/Svelte/Vue). After build both files * land in the project root. * * @var array{ * login: string, * register: string, * welcome: string, * profile: string, * security: string, * verify_email: string, * two_factor_challenge: string, * confirm_password: string, * auth_types: string, * two_factor_files: list, * two_factor_otp_package: ?string, * passkey_files: list, * } $paths */ $paths = require __DIR__.'/chisel-paths.php'; return Chisel::script(__DIR__) ->questions([ Question::multiselect( name: 'auth_features', label: 'Which authentication features would you like to enable?', options: [ 'email-verification' => 'Email verification', 'registration' => 'Registration', '2fa' => 'Two-factor authentication', 'passkeys' => 'Passkeys', 'password-confirmation' => 'Password confirmation', ], default: ['email-verification', 'registration', '2fa', 'passkeys', 'password-confirmation'], hint: 'Use space to select, enter to confirm.', ), ]) ->selected( 'auth_features', 'registration', then: function (Chisel $c) use ($paths) { $c->files( 'config/fortify.php', 'app/Providers/FortifyServiceProvider.php', $paths['login'], $paths['welcome'], )->removeSectionMarkers('registration'); }, else: function (Chisel $c) use ($paths) { $c->file('config/fortify.php')->removeSection('registration'); $c->files( 'app/Providers/FortifyServiceProvider.php', $paths['login'], $paths['welcome'], )->removeSection('registration'); $c->files( 'app/Actions/Fortify/CreateNewUser.php', 'app/Http/Responses/RegisterResponse.php', $paths['register'], 'tests/Feature/Auth/RegistrationTest.php', )->delete(); }, ) ->selected( 'auth_features', 'email-verification', then: function (Chisel $c) use ($paths) { $c->files( 'config/fortify.php', $paths['profile'], 'app/Providers/FortifyServiceProvider.php', )->removeSectionMarkers('email-verification'); }, else: function (Chisel $c) use ($paths) { $c->php('app/Models/User.php') ->removeImport('Illuminate\Contracts\Auth\MustVerifyEmail') ->removeInterface('MustVerifyEmail'); $c->files( 'config/fortify.php', 'app/Providers/FortifyServiceProvider.php', $paths['profile'], )->removeSection('email-verification'); $c->files( 'app/Http/Responses/VerifyEmailResponse.php', $paths['verify_email'], 'tests/Feature/Auth/EmailVerificationTest.php', 'tests/Feature/Auth/VerificationNotificationTest.php', )->delete(); }, ) ->selected( 'auth_features', '2fa', then: function (Chisel $c) use ($paths) { $c->files( 'app/Models/User.php', 'database/factories/UserFactory.php', $paths['security'], $paths['auth_types'], 'config/fortify.php', 'app/Providers/FortifyServiceProvider.php', 'app/Http/Controllers/Settings/SecurityController.php', )->removeSectionMarkers('2fa'); }, else: function (Chisel $c) use ($paths) { $c->php('app/Models/User.php') ->removeImport('Laravel\Fortify\TwoFactorAuthenticatable') ->removeTrait('TwoFactorAuthenticatable'); $c->files( 'app/Models/User.php', 'database/factories/UserFactory.php', 'config/fortify.php', 'app/Providers/FortifyServiceProvider.php', 'app/Http/Controllers/Settings/SecurityController.php', $paths['security'], $paths['auth_types'], )->removeSection('2fa'); if ($paths['two_factor_otp_package'] !== null) { chiselRemoveNpmPackages($c, $paths['two_factor_otp_package']); } $c->files(...[ $paths['two_factor_challenge'], ...$paths['two_factor_files'], 'database/migrations/2025_08_14_170933_add_two_factor_columns_to_users_table.php', 'tests/Feature/Auth/TwoFactorChallengeTest.php', ])->delete(); }, ) ->selected( 'auth_features', 'passkeys', then: function (Chisel $c) use ($paths) { $c->files( 'config/fortify.php', 'app/Providers/FortifyServiceProvider.php', 'app/Http/Controllers/Settings/SecurityController.php', 'routes/settings.php', 'tests/Feature/Auth/AuthenticationTest.php', 'tests/Feature/Settings/SecurityTest.php', $paths['auth_types'], $paths['security'], $paths['login'], $paths['confirm_password'], )->removeSectionMarkers('passkeys'); }, else: function (Chisel $c) use ($paths) { $c->php('app/Models/User.php') ->removeImport('Laravel\Fortify\PasskeyAuthenticatable') ->removeImport('Laravel\Fortify\Contracts\PasskeyUser') ->removeTrait('PasskeyAuthenticatable') ->removeInterface('PasskeyUser'); $c->files( 'config/fortify.php', 'app/Providers/FortifyServiceProvider.php', 'app/Http/Controllers/Settings/SecurityController.php', 'routes/settings.php', 'tests/Feature/Auth/AuthenticationTest.php', 'tests/Feature/Settings/SecurityTest.php', $paths['auth_types'], $paths['security'], $paths['login'], $paths['confirm_password'], )->removeSection('passkeys'); chiselRemoveNpmPackages($c, '@laravel/passkeys'); $c->files(...[ ...$paths['passkey_files'], 'app/Http/Responses/PasskeyLoginResponse.php', 'database/migrations/2024_01_01_000000_create_passkeys_table.php', ])->delete(); }, ) ->selectedAny( 'auth_features', ['2fa', 'passkeys'], then: function (Chisel $c) use ($paths) { $c->file($paths['security']) ->removeSectionMarkers('2fa-or-passkeys'); }, else: function (Chisel $c) use ($paths) { $c->file($paths['security']) ->removeSection('2fa-or-passkeys'); }, ) ->selected( 'auth_features', 'password-confirmation', then: function (Chisel $c) { $c->files( 'app/Providers/FortifyServiceProvider.php', 'routes/settings.php', 'tests/Feature/Settings/SecurityTest.php', )->removeSectionMarkers('password-confirmation'); }, else: function (Chisel $c) use ($paths) { $c->file('config/fortify.php') ->replace("'confirmPassword' => true,", "'confirmPassword' => false,"); $c->files( 'app/Providers/FortifyServiceProvider.php', 'routes/settings.php', 'tests/Feature/Settings/SecurityTest.php', )->removeSection('password-confirmation'); $c->files( $paths['confirm_password'], 'tests/Feature/Auth/PasswordConfirmationTest.php', )->delete(); }, ) ->apply(function (Chisel $c): void { $c->file('composer.json') ->removeLinesContaining('"@php artisan install:features --ansi"'); chiselRun(['composer', 'lint'], 'Composer Lint'); chiselRun(['php', 'artisan', 'wayfinder:generate', '--with-form', '--no-interaction'], 'Generate Wayfinder Resources'); if (! chiselSkipsNode()) { $c->npm()->run('check:fix'); } $c->files( 'app/Console/Commands/InstallFeaturesCommand.php', 'chisel.php', 'chisel-paths.php', )->delete(); });