Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 233 additions & 0 deletions src/Illuminate/Foundation/Console/VueInstallCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
<?php

namespace Illuminate\Foundation\Console;

use Illuminate\Console\Command;
use Illuminate\Process\Exceptions\ProcessTimedOutException;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Process;
use Symfony\Component\Console\Attribute\AsCommand;

#[AsCommand(name: 'install:vue')]
class VueInstallCommand extends Command
{
use InteractsWithComposerPackages;

/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'install:vue
{--composer=global : Absolute path to the Composer binary which should be used to install packages}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Install and configure Vue.js scaffolding in a fresh Laravel installation';

/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$inertiaMiddlewarePath = $this->laravel->path('Http/Middleware/HandleInertiaRequests.php');

$inertiaConfigPath = $this->laravel->configPath('inertia.php');

if (file_exists($inertiaMiddlewarePath) || file_exists($inertiaConfigPath)) {
$this->components->error('Vue.js scaffolding is already configured.');
} else {
$this->components->info('Install and configure Vue.js scaffolding.');

$this->installInertia();

$this->installZiggy();

$this->ensureDirectoriesExist([
$this->laravel->path('Http/Middleware'),
$this->laravel->resourcePath('js/lib'),
$this->laravel->resourcePath('js/pages'),
$this->laravel->resourcePath('js/types'),
]);

$this->publishFiles([
// inertia files
__DIR__.'/stubs/vue-scaffolding/inertia/HandleInertiaRequests.stub' => $inertiaMiddlewarePath,
__DIR__.'/stubs/vue-scaffolding/inertia/inertia.stub' => $inertiaConfigPath,

// js files
__DIR__.'/stubs/vue-scaffolding/js/lib/utils.stub' => $this->laravel->resourcePath('js/lib/utils.ts'),
__DIR__.'/stubs/vue-scaffolding/js/pages/Welcome.stub' => $this->laravel->resourcePath('js/pages/Welcome.vue'),
__DIR__.'/stubs/vue-scaffolding/js/types/globals.stub' => $this->laravel->resourcePath('js/types/globals.d.ts'),
__DIR__.'/stubs/vue-scaffolding/js/types/index.stub' => $this->laravel->resourcePath('js/types/index.d.ts'),
__DIR__.'/stubs/vue-scaffolding/js/types/ziggy.stub' => $this->laravel->resourcePath('js/types/ziggy.d.ts'),
__DIR__.'/stubs/vue-scaffolding/js/app.stub' => $this->laravel->resourcePath('js/app.ts'),
__DIR__.'/stubs/vue-scaffolding/js/ssr.stub' => $this->laravel->resourcePath('js/ssr.ts'),

// view files
__DIR__.'/stubs/vue-scaffolding/welcome.stub' => $this->laravel->resourcePath('views/welcome.blade.php'),

// routes files
__DIR__.'/stubs/vue-scaffolding/web.stub' => $this->laravel->basePath('routes/web.php'),

// bootstrap files
__DIR__.'/stubs/vue-scaffolding/app.stub' => $this->laravel->basePath('bootstrap/app.php'),

// other files
__DIR__.'/stubs/vue-scaffolding/package.stub' => $this->laravel->basePath('package.json'),
__DIR__.'/stubs/vue-scaffolding/eslint.config.stub' => $this->laravel->basePath('eslint.config.js'),
__DIR__.'/stubs/vue-scaffolding/tsconfig.stub' => $this->laravel->basePath('tsconfig.json'),
__DIR__.'/stubs/vue-scaffolding/vite.config.stub' => $this->laravel->basePath('vite.config.ts'),
]);

$this->deleteFiles([
$this->laravel->resourcePath('js/app.js'),
$this->laravel->resourcePath('js/bootstrap.js'),
$this->laravel->basePath('vite.config.js'),
]);

$this->cleanCssFile();

$this->addDevSsrCommand();

$this->installNodeDependencies();
}
}

/**
* Install Inertia into the application.
*
* @return void
*/
protected function installInertia()
{
$this->components->info('Installing Inertia...');

$this->requireComposerPackages($this->option('composer'), [
'inertiajs/inertia-laravel:^2.0',
]);
}

/**
* Install Ziggy into the application.
*
* @return void
*/
protected function installZiggy()
{
$this->output->newLine();
$this->components->info('Installing Ziggy...');

$this->requireComposerPackages($this->option('composer'), [
'tightenco/ziggy:^2.5',
]);
}

/**
* Ensure that the given directories exist, creating them if necessary.
*
* @param list<string> $directories
* @return void
*/
protected function ensureDirectoriesExist($directories)
{
foreach ($directories as $directory) {
File::ensureDirectoryExists($directory);
}
}

/**
* Publish the given files to their destination paths.
*
* @param array<string, string> $files
* @return void
*/
protected function publishFiles($files)
{
foreach ($files as $source => $destination) {
File::copy($source, $destination);
}
}

/**
* Delete the given files if they exist.
*
* @param list<string> $files
* @return void
*/
protected function deleteFiles($files)
{
foreach ($files as $file) {
File::delete($file);
}
}

/**
* Remove some directives from the "app.css" file.
*
* @return void
*/
protected function cleanCssFile()
{
$filePath = $this->laravel->resourcePath('css/app.css');

$str = "@source '../**/*.blade.php';\n@source '../**/*.js';\n";

if (str_contains(File::get($filePath), $str)) {
File::replaceInFile($str, '', $filePath);
}
}

/**
* Add the `dev:ssr` command to the "composer.json" file.
*
* @return void
*/
protected function addDevSsrCommand()
{
$file = $this->laravel->basePath('composer.json');

$content = json_decode(File::get($file), true);

$content['scripts'] = Arr::add($content['scripts'], 'dev:ssr', [
'npm run build:ssr',
'Composer\\Config::disableProcessTimeout',
'npx concurrently -c "#93c5fd,#c4b5fd,#fb7185,#fdba74" "php artisan serve" "php artisan queue:listen --tries=1" "php artisan pail --timeout=0" "php artisan inertia:start-ssr" --names=server,queue,logs,ssr --kill-others',
]);

File::put(
$file,
json_encode($content, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)."\n"
);
}

/**
* Run the `npm install` command in the base path of the application.
*
* @return void
*/
protected function installNodeDependencies()
{
if (! $this->confirm('Would you like to install the Node dependencies?', default: true)) {
$this->components->warn("Please run the 'npm install' command manually.");

return;
}

try {
$command = Process::timeout(120)
->path($this->laravel->basePath())
->run('npm install');

$this->components->info('Node dependencies installed successfully.');
} catch (ProcessTimedOutException $e) {
$this->components->warn("Node dependency installation failed. Please run the 'npm install' command.");
}
}
}
23 changes: 23 additions & 0 deletions src/Illuminate/Foundation/Console/stubs/vue-scaffolding/app.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

use App\Http\Middleware\HandleInertiaRequests;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets;

return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [
HandleInertiaRequests::class,
AddLinkHeadersForPreloadedAssets::class,
]);
})
->withExceptions(function (Exceptions $exceptions): void {
//
})->create();
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import prettier from 'eslint-config-prettier';
import vue from 'eslint-plugin-vue';

import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript';

export default defineConfigWithVueTs(
vue.configs['flat/essential'],
vueTsConfigs.recommended,
{
ignores: ['vendor', 'node_modules', 'public', 'bootstrap/ssr', 'tailwind.config.js', 'resources/js/components/ui/*'],
},
{
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
},
prettier,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Inspiring;
use Illuminate\Http\Request;
use Inertia\Middleware;
use Tighten\Ziggy\Ziggy;

class HandleInertiaRequests extends Middleware
{
/**
* The root template that's loaded on the first page visit.
*
* @see https://inertiajs.com/server-side-setup#root-template
*
* @var string
*/
protected $rootView = 'app';

/**
* Determines the current asset version.
*
* @see https://inertiajs.com/asset-versioning
*/
public function version(Request $request): ?string
{
return parent::version($request);
}

/**
* Define the props that are shared by default.
*
* @see https://inertiajs.com/shared-data
*
* @return array<string, mixed>
*/
public function share(Request $request): array
{
[$message, $author] = str(Inspiring::quotes()->random())->explode('-');

return [
...parent::share($request),
'name' => config('app.name'),
'quote' => ['message' => trim($message), 'author' => trim($author)],
'auth' => [
'user' => $request->user(),
],
'ziggy' => [
...(new Ziggy)->toArray(),
'location' => $request->url(),
],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

return [

/*
|--------------------------------------------------------------------------
| Server Side Rendering
|--------------------------------------------------------------------------
|
| These options configure if and how Inertia uses Server Side Rendering
| to pre-render every initial visit made to your application's pages
| automatically. A separate rendering service should be available.
|
| See: https://inertiajs.com/server-side-rendering
|
*/

'ssr' => [
'enabled' => true,
'url' => 'http://127.0.0.1:13714',
// 'bundle' => base_path('bootstrap/ssr/ssr.mjs'),
],

/*
|--------------------------------------------------------------------------
| Testing
|--------------------------------------------------------------------------
|
| The values described here are used to locate Inertia components on the
| filesystem. For instance, when using `assertInertia`, the assertion
| attempts to locate the component as a file relative to the paths.
|
*/

'testing' => [
'ensure_pages_exist' => true,

'page_paths' => [
resource_path('js/pages'),
],

'page_extensions' => [
'js',
'jsx',
'svelte',
'ts',
'tsx',
'vue',
],
],

];
Loading