💭

laravel12のlaravel newの正体と内部で動く create-project の仕組み (larave/installer)

に公開

laravel(installer)

https://packagist.org/packages/laravel/installer

ドキュメントを読む限りLaravel 12 では、laravel new が公式に唯一案内されている新規アプリケーション作成方法になっているように見える。従来のcreate-projectlaravel.build式でプロジェクトを作成する事に関しては全く案内されなくなっている。がしかし、これは結局内部では実はcomposer create-projectを行っているのであるが、方式を統一して、なるべくlaravel12のstarter kitを使わせたいための誘導という意味が強いのであろう。

dockerでlaravel installerを展開する方法

ここでphp.newや、システムにインストールされたphp等を使わないでdockerで展開する方法を記しておく。

docker run --rm -it -v "$PWD":/app -w /app composer bash

でcomposerイメージを取ってきてシェルを起動する。さらに

composer -n global require laravel/installer &&
export PATH="$PATH:/tmp/vendor/bin" &&
laravel --version

と入力するとlaravelコマンドが使えるようになる


Laravel Installer 5.23.0が入っている

これでlaravelコマンドが使えるのでlaravel newなどサブコマンドが自由に使える環境となる。
今回はそのサブコマンドを掘り下げていく。

laravel 5.23.0のコマンド一覧

このコマンドは基本的にlaravel newするためだけにあるといっても過言ではないのでnewされたときのヘルプをオプションに絞って見ていこう、なお、以下のヘルプの説明はAIによる翻訳を含む。

オプション 説明
--dev 最新の「開発版(development release)」をインストールする
--git Git リポジトリを初期化する
--branch=BRANCH 新しいリポジトリで作成するブランチ名(デフォルト: main
--github[=GITHUB] 新しい GitHub リポジトリを作成する(デフォルト: false
--organization=ORG 指定した GitHub 組織にリポジトリを作成する
--database=DB 使用するデータベースドライバを指定(選択肢: mysql, mariadb, pgsql, sqlite, sqlsrv
--react React Starter Kit をインストール
--vue Vue Starter Kit をインストール
--livewire Livewire Starter Kit をインストール
--livewire-class-components Livewire のスタンドアロン・クラスコンポーネントを生成
--workos 認証に WorkOS を使用
--no-authentication 認証スキャフォールドを生成しない
--pest Pest テストフレームワークをインストール
--phpunit PHPUnit テストフレームワークをインストール
--npm NPM でフロントエンド依存関係をインストール&ビルド
--pnpm PNPM で依存関係をインストール&ビルド
--bun Bun で依存関係をインストール&ビルド
--yarn Yarn で依存関係をインストール&ビルド
--boost Laravel Boost をインストールし、AI コーディング支援を強化
--using=PACKAGE コミュニティ製 Starter Kit パッケージを指定してインストール
-f, --force 対象ディレクトリが存在しても強制的にインストール
-h, --help 指定コマンドのヘルプを表示。コマンドがない場合はコマンド一覧を表示
--silent 出力を一切表示しない
-q, --quiet エラーのみ表示(その他の出力を抑制)
-V, --version このアプリケーションのバージョンを表示
--ansi / --no-ansi ANSI 出力を強制(または無効化)
-n, --no-interaction 対話的な質問を行わない
-v, -vv, -vvv, --verbose メッセージの詳細度を上げる(1: 通常, 2: 詳細, 3: デバッグ)

このように --boost がしれっと追加されている(後で解説)

オプションの内部を見ていく

ここからはソースを交えてどのような動作になるのか深く理解してみよう。

react/vue/livewireの選択が実際どのように行われているか

これはlaravelインストーラー

src/NewCommand.php
        $starterKit = $this->getStarterKit($input);

        if ($starterKit) {
            $createProjectCommand = $composer." create-project {$starterKit} \"{$directory}\" --stability=dev";

            if ($this->usingLaravelStarterKit($input) && $input->getOption('livewire-class-components')) {
                $createProjectCommand = str_replace(" {$starterKit} ", " {$starterKit}:dev-components ", $createProjectCommand);
            }

の部分で

    protected function getStarterKit(InputInterface $input): ?string
    {
        if ($input->getOption('no-authentication')) {
            return match (true) {
                $input->getOption('react') => 'laravel/blank-react-starter-kit',
                $input->getOption('vue') => 'laravel/blank-vue-starter-kit',
                $input->getOption('livewire') => 'laravel/blank-livewire-starter-kit',
                default => $input->getOption('using'),
            };
        }

        return match (true) {
            $input->getOption('react') => 'laravel/react-starter-kit',
            $input->getOption('vue') => 'laravel/vue-starter-kit',
            $input->getOption('livewire') => 'laravel/livewire-starter-kit',
            default => $input->getOption('using'),
        };
    }

により認証のある/なしもあるが、ある場合

composer create-project laravel/react-starter-kit "{directory}" --stability=dev

とかが呼びこまれているだけであるので、これを自分で持ってきても同じ(ただしlivewireはもう1段階選択肢--livewire-class-componentsがあるが、割愛)

workosオプション

composer create-project laravel/react-starter-kit:dev-workos "directory" --stability=dev

のようにdev-workosブランチを明示的に指定している。

--no-authenticationオプション

これは実は結構あやうくてブランチでなくてリポジトリが変わる。たとえばreactだと

composer create-project laravel/blank-react-starter-kit "directory" --stability=dev

このようにlaravel/blank-react-starter-kitという全く別のリポジトリが指定される

https://github.com/laravel/blank-react-starter-kit

見ての通りリポジトリが全然アクティブじゃないので可能なら触らない方がよさそう。機能がシンプルすぎるのでもう完成しきっているという事なのかもしれないが...

git/githubオプション

git init -q
git add .
git commit -q -m "Set up a fresh Laravel app"
git branch -M {branch}

みたい展開が自動で行われる。--githubが付いているとghコマンドでさらに手厚くされる。正直これは自分でやってもいいだろうというレベルなので、敢えて付ける必要があるのかは疑問。

--databaseオプション

SQLiteの場合:

DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1  (コメントアウト)
# DB_PORT=3306
# DB_DATABASE=laravel

MySQL/MariaDBの場合:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel

というようなのをセットアップする。ローカルにデーターベースを展開している場合、多少の補助にはなるが結局ユーザー名やパスワードなど追加の指定が必要になるから、正直、この段階で付ける意味があるとは思えない。

--pestと--phpunitオプション

これらは排他的に動作するのでどちらかしか使えない。なお両方指定された場合、--pestが優先される。create-projectするとphpunitで組まれたものが展開されるのだが、--pestが指定されるとその展開されたものに一手間加えて変換しているのである。

composer remove phpunit/phpunit --dev --no-update
composer require pestphp/pest pestphp/pest-plugin-laravel --no-update --dev
composer update

# Pest初期化
php ./vendor/bin/pest --init # tests/Pest.phpファイルを生成

## Pest Driftによるテスト変換
composer require pestphp/pest-plugin-drift --dev
php ./vendor/bin/pest --drift
composer remove pestphp/pest-plugin-drift --dev

あと細かい変換処理が入る。

--npm --pnpm --bun --yarn について

パッケージマネージャーによるフロントエンドパッケージのインストール処理に関するオプションであり、以下のように排他的に選択される

if ($input->getOption('pnpm')) {
    return [NodePackageManager::PNPM, true];
}
if ($input->getOption('bun')) {
    return [NodePackageManager::BUN, true];
}
if ($input->getOption('yarn')) {
    return [NodePackageManager::YARN, true];
}
if ($input->getOption('npm')) {
    return [NodePackageManager::NPM, true];
}

ソースコードにおいてはこんな感じになっているので全部付けるとifの順に処理される。インストール完了後に以下が引かれるだけ。

  • pnpm: pnpm install && pnpm build
  • bun: bun install && bun run build
  • yarn: yarn install && yarn build
  • npm: npm install && npm run build

カスタムスターターキットオプション --using[=USING]

これはちょっと詳細が複雑なので後述

AI Boostオプション --boost

最近のインストーラーの目玉であるが

composer require laravel/boost --dev
php artisan boost:install

という処理を行うのがメインであるが一部追加処理がある。

なお、これは対話型でも現われてくる。ここではlaravel newを行い対話型インストーラーを起動すると以下のように

 ┌ Do you want to install Laravel Boost to improve AI assisted coding? ┐
 │ ● Yes / ○ No                                                        │
 └─────────────────────────────────────────────────────────────────────┘

という選択肢が現われる。これをYesにするとある程度インストール作業が終わった最後の方で突然

Using version ^1.6 for laravel/boost

 ██████╗   ██████╗   ██████╗  ███████╗ ████████╗
 ██╔══██╗ ██╔═══██╗ ██╔═══██╗ ██╔════╝ ╚══██╔══╝
 ██████╔╝ ██║   ██║ ██║   ██║ ███████╗    ██║
 ██╔══██╗ ██║   ██║ ██║   ██║ ╚════██║    ██║
 ██████╔╝ ╚██████╔╝ ╚██████╔╝ ███████║    ██║
 ╚═════╝   ╚═════╝   ╚═════╝  ╚══════╝    ╚═╝

  ✦ Laravel Boost :: Install :: We Must Ship ✦

 Let's give Laravel a Boost

 ┌ Which third-party AI guidelines do you want to install? ─────┐
 │ › ◻ laravel/fortify (~311 tokens) Laravel Fortify            │
 └──────────────────────────────────────────────────────────────┘
  You can add or remove them later by running this command again

のような画面が表れる。これはphp artisan boost:installが行われて

laravel/fortifyのガイドラインをinstallするか?

と聞かれているわけであるが、laravel boostの知識が無いとそもそも全く必要ないので、これはインストールする時に無効にするべきだ。後からでももちろん導入は可能

sail

この辺は一切面倒を見てくれないため、欲しいなら自分で入れる

カスタムスターターキット

これは2つの形式がある

1. Composerパッケージ形式(デフォルト)

laravel new myapp --using=vendor/package-name

これは実質以下が実行される

composer create-project vendor/package-name "myapp" --stability=dev

すなわち、composerに登録済みスターターキットを使う場合だ。しかし現時点でcomposer(packagist)に登録されているもので勢いがあるのは少ない。要件は以下の通り

  • Packagist(または他のComposerリポジトリ)に公開されている
  • composer.jsonのtypeがproject
  • composer create-projectで使用可能

例えば以下のように調査する事はできる

$ composer search react-starter-kit --only-name --type=project
amdadulhaq/react-starter-kit
aronpc/react-starter-kit-auth
arthurydalgo/laravext-react-starter-kit
axyr/laravel-react-starter-kit
benbjurstrom/otpz-react-starter-kit
chargebee/laravel-react-starter-kit
f1nng/laravel-react-starter-kit
fabppl/laravel-teams-react-starter-kit
feature-ninja/react-starter-kit
ghijk/laravel-react-starter-kit
hichemtab-tech/forked-from-react-starter-kit
jengo/react-starter-kit
laravel-uis/socialite-ui-react-starter-kit
laravel/blank-react-starter-kit
laravel/react-starter-kit
mitantsoa/laravel-react-starter-kit
nervcomputer/react-starter-kit
pixelsprout/chorus-react-starter-kit
shipfastlabs/modern-react-starter-kit
shipfastlabs/modern-react-starter-kit-auth
shipfastlabs/modern-react-starter-kit-workos
simaocurado/blank-tweaked-laravel-react-starter-kit
stereshko/react-starter-kit
zarchp/react-starter-kit

2. Git URL形式(tiged使用)

以下のようにして指定する

laravel new myapp --using=https://github.com/user/repo
laravel new myapp --using=git://github.com/user/repo

たとえば

laravel new myapp --using=https://github.com/user/repo

すると

npx tiged@latest https://github.com/user/repo "myapp" && cd "myapp" && composer install

のようにtigedで取れるものであれば何でも取れる

  • laravel/で始まらない(公式スターターキット以外)
  • ://を含む(URLとして認識)
#### tiged とは

`tiged` は `degit` の TypeScript 実装で、主な目的は 「テンプレートとしてリポジトリ内容をコピーする」 こと。

実際の処理の流れ
* 指定したリポジトリ(GitHub, GitLab, Bitbucket など)から最新コミット(`HEAD`)のスナップショットを取得。
* **.git/** ディレクトリを含めずに展開(=履歴なしのコピー)。
* 結果として、指定ディレクトリにファイルを展開して終了。

つまり、「clone + 履歴削除」とほぼ同じ最終状態を一発で作るものである。

packagistに登録するまでもないプライベートなリポジトリーから展開するのに向いている

以上を踏まえてlaravelコマンドを「使わない」選択

ここまで見てきたようにlaravel newはusingを使わないのであれば実際の所composer create-projectが内部でコールされているに過ぎない。たとえば

laravel new myapp --react --phpunit

は実際には

git clone https://github.com/laravel/react-starter-kit.git myapp2

と比較すると

$ diff -r myapp myapp2/
myapp のみに存在: .env
myapp2/ のみに存在: .git
myapp2/.github/workflows のみに存在: browser-tests.yml
myapp2/ のみに存在: README.md
myapp/bootstrap/cache のみに存在: packages.php
myapp/bootstrap/cache のみに存在: services.php
diff -r myapp/composer.json myapp2/composer.json
97c97
< }
\ ファイル末尾に改行がありません
---
> }
myapp のみに存在: composer.lock
myapp/database のみに存在: database.sqlite
myapp のみに存在: vendor

このようにほとんど変わっていない

npx tiged@latest https://github.com/laravel/react-starter-kit "myapp3" && cd "myapp3" && composer install

すればほぼ同等になる

$ diff -ru myapp myapp3
myapp のみに存在: .env
diff -ru myapp/composer.json myapp3/composer.json
--- myapp/composer.json 2025-11-03 16:41:14.474138883 +0900
+++ myapp3/composer.json        2025-10-30 02:04:32.000000000 +0900
@@ -94,4 +94,4 @@
     },
     "minimum-stability": "stable",
     "prefer-stable": true
-}
\ ファイル末尾に改行がありません
+}
myapp/database のみに存在: database.sqlite
diff -ru myapp/vendor/composer/installed.php myapp3/vendor/composer/installed.php
--- myapp/vendor/composer/installed.php 2025-11-03 16:41:10.190167164 +0900
+++ myapp3/vendor/composer/installed.php        2025-11-03 16:46:11.456171449 +0900
@@ -1,8 +1,8 @@
 <?php return array(
     'root' => array(
         'name' => 'laravel/react-starter-kit',
-        'pretty_version' => 'dev-main',
-        'version' => 'dev-main',
+        'pretty_version' => '1.0.0+no-version-set',
+        'version' => '1.0.0.0',
         'reference' => null,
         'type' => 'project',
         'install_path' => __DIR__ . '/../../',
@@ -455,8 +455,8 @@
             'dev_requirement' => false,
         ),
         'laravel/react-starter-kit' => array(
-            'pretty_version' => 'dev-main',
-            'version' => 'dev-main',
+            'pretty_version' => '1.0.0+no-version-set',
+            'version' => '1.0.0.0',
             'reference' => null,
             'type' => 'project',
             'install_path' => __DIR__ . '/../../',

ただし .env の生成など差はある。pint変換などの作業があるが、大体何を行っているのか把握できているならlaravel newが面倒という場合これでもそれほど問題にはならないだろう。

例: react版をupstreamにして開発したい場合


my-react-starter-kit

ここではgithubにmy-react-starter-kitを作成する

git clone https://github.com/catatsumuri/react-starter-kit.git my-react-starter-kit
cd my-react-starter-kit

で空のリポジトリを取得したら

git remote add upstream https://github.com/laravel/react-starter-kit.git
git fetch upstream

すると

$ git remote add upstream https://github.com/laravel/react-starter-kit.git
git fetch upstream
remote: Enumerating objects: 2597, done.
remote: Counting objects: 100% (45/45), done.
remote: Compressing objects: 100% (35/35), done.
remote: Total 2597 (delta 17), reused 12 (delta 10), pack-reused 2552 (from 3)
Receiving objects: 100% (2597/2597), 1.07 MiB | 21.50 MiB/s, done.
Resolving deltas: 100% (1391/1391), done.
From https://github.com/laravel/react-starter-kit
 * [new branch]      fix_ci     -> upstream/fix_ci
 * [new branch]      main       -> upstream/main
 * [new branch]      preview    -> upstream/preview
 * [new branch]      workos     -> upstream/workos
 * [new tag]         v1.0.0     -> v1.0.0
 * [new tag]         v1.0.1     -> v1.0.1

となるので

git merge upstream/main

とすれば

$ ls
README.md  components.json  eslint.config.js   public     tests
app        composer.json    package-lock.json  resources  tsconfig.json
artisan    config           package.json       routes     vite.config.ts
bootstrap  database         phpunit.xml        storage

このように内容が揃うため、あとは自分のリポジトリにpushするなるするとよいだろう。

Discussion