Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Composer Installer Laravel Package

liberusoftware/composer-installer

A Composer installer plugin for Liberu Software packages that provides custom install paths and package-type routing, helping Liberu modules, themes, and extensions land in the expected Laravel directories and integrate smoothly via Composer.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Composer installer plugin, not a Laravel service provider — it never touches your config/app.php. It hijacks Composer's install path resolution so packages of the supported types land in modules/ and themes/ instead of vendor/.

  1. Require it as a real dependency (plugins belong in require, not require-dev):

    composer require <vendor>/<installer-package>
    
  2. Let Composer trust it. On Composer 2.2+ you'll be prompted; otherwise add it manually:

    { "config": { "allow-plugins": { "<vendor>/<installer-package>": true } } }
    
  3. Verify activation before trusting it:

    composer install -vvv | grep -i plugin
    

    In 1.1.0 the package ships extra.plugin-modifies-install-path: true, so it should activate at the very start of the run. If you don't see that, Composer will resolve paths as if every package lives in vendor/ and your modules/ tree will churn.

  4. First use case: composer require <vendor>/<some-module> and confirm modules/<Name>/ appears (not vendor/<vendor>/<some-module>). The package's type in its own composer.json decides the target directory — read src/LiberuInstaller.php first to see the exact type → path mapping your version supports, since that is the one thing you cannot guess from the docs.

Implementation Patterns

Module/theme authoring. Your package's composer.json is the whole contract:

{ "name": "acme/blog-module", "type": "liberu-module", "require": {} }

The name drives the target directory name. Keep the name and the intended directory in sync — 1.1.0 now enforces that.

Local development with path repositories. Standard pattern still works, and is where you'll hit the collision check most often:

"repositories": [{ "type": "path", "url": "../blog-module", "options": { "symlink": true } }]

Renaming a package (new failure mode in 1.1.0). Treat a rename as a two-step migration, scripted so CI can't get it wrong:

rm -rf modules/OldName themes/OldTheme
composer update acme/new-name

Testing your own code that depends on layout. LiberuInstaller now accepts an optional root directory, defaulting to getcwd(). Use it in your own tests to assert paths without touching the repo:

$installer = new LiberuInstaller(__DIR__ . '/fixtures/project');
$path = $installer->getInstallPath($package); // asserts modules/... etc.

CI. Never run composer install --no-plugins in a pipeline that then tests against modules/ — the plugin is skipped and the tree is silently wrong. Assert layout explicitly rather than assuming success:

composer install --no-interaction
test -f modules/Blog/composer.json

Extending. The supported types live in the installer's path-computation method; adding a type is a small change plus a Pest test. Mirror the 1.1.0 suite structure (path computation, type mapping, name validation, traversal rejection, collision) if you fork.

Gotchas and Tips

  • allow-plugins is silent by default. Skipped plugins produce no error, just packages in vendor/. If modules/ looks stale after a composer update, check this before anything else.
  • plugin-modifies-install-path needs Composer 2.2+. On older versions the key is ignored — you get the old, later activation and the "unexplained churn in tracked package directories" the release notes describe. Pin composer-runtime-api/Composer version in CI images.
  • The collision check reads the working tree, not the process. A target directory whose composer.json names a different package aborts the install. That is exactly right for catching renames, but it also means a half-finished or hand-created directory blocks you. Inspect modules/<Target>/composer.json and delete if stale.
  • Absent/unreadable/nameless composer.json blocks nothing. A directory with no composer.json won't protect itself — writes proceed. Don't rely on the check to guard hand-curated folders; commit them or use your own guard.
  • Name validation is strict. Traversal (../) and absolute paths in package names are rejected outright. If a composer require fails with a path error, suspect the package name, not your repository layout.
  • Root is the working directory. Running Composer from a subdirectory or with --working-dir moves what the installer considers root. The new injectable root argument mirrors this behaviour — it isn't a new default.
  • Removed Composer\Plugin\Capable. Harmless for consumers; only relevant if you subclassed Plugin and overrode getCapabilities().
  • Debugging: composer install -vvv for plugin activation order; vendor/composer/installed.json shows the resolved install-path per package, which is the fastest way to see where the installer actually put something.
  • Commit before you upgrade. modules/ and themes/ are tracked in this layout, so a botched install is a git diff you can read — and git checkout is your rollback.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle