chore(deps): upgrade nwidart/laravel-modules to v13 via invoiceshelf/modules ^3.0

Pulls invoiceshelf/modules ^3.0 from packagist — a thin extension package on
top of current upstream nwidart/laravel-modules ^13.0 — replacing the stale
2021-era invoiceshelf/modules ^1.0 fork that bundled its own copy of nwidart.

The host app's autoloader now resolves Nwidart\Modules\* from
vendor/nwidart/laravel-modules and InvoiceShelf\Modules\* from
vendor/invoiceshelf/modules. Existing imports of Nwidart\Modules\Facades\Module
keep working unchanged.

config/modules.php is republished from upstream v13 with two
InvoiceShelf-specific overrides:

- activators.file.statuses-file kept at storage/app/modules_statuses.json so
  existing installations don't lose track of which modules are enabled when
  the config is republished (upstream v13 defaults to base_path()).
- New lang/menu and lang/settings entries in stubs.files / stubs.replacements
  that pair with the custom module:make stubs shipped from the package.

Wires wikimedia/composer-merge-plugin (a transitive dependency of nwidart) so
each module's nested composer.json autoload mapping is merged into the host
autoloader at composer dump-autoload time. This is what makes a module
generated via php artisan module:make MyModule actually loadable. The plugin
is added to allow-plugins and configured via extra.merge-plugin.include.

Drops the stale Modules\\: Modules/ PSR-4 fallback root from autoload — it
didn't match nwidart's app/-prefixed module layout and was always broken for
generated modules.
This commit is contained in:
Darko Gjorgjijoski
2026-04-09 00:27:16 +02:00
parent 1fb5886d06
commit 61e1efd81b
4 changed files with 339 additions and 226 deletions

View File

@@ -1,7 +1,7 @@
<?php
use Nwidart\Modules\Activators\FileActivator;
use Nwidart\Modules\Commands;
use Nwidart\Modules\Providers\ConsoleServiceProvider;
return [
@@ -13,9 +13,19 @@ return [
| Default module namespace.
|
*/
'namespace' => 'Modules',
/*
|--------------------------------------------------------------------------
| Vapor Maintenance Mode
|--------------------------------------------------------------------------
|
| Indicates if the application is running on Laravel Vapor.
| When enabled, cached services path will be set to a writable location.
|
*/
'vapor_maintenance_mode' => env('VAPOR_MAINTENANCE_MODE', false),
/*
|--------------------------------------------------------------------------
| Module Stubs
@@ -24,35 +34,34 @@ return [
| Default module stubs.
|
*/
'stubs' => [
'enabled' => false,
'path' => base_path('vendor/nwidart/laravel-modules/src/Commands/stubs'),
'files' => [
'routes/web' => 'Routes/web.php',
'routes/api' => 'Routes/api.php',
'views/index' => 'Resources/views/index.blade.php',
'views/master' => 'Resources/views/layouts/master.blade.php',
'scaffold/config' => 'Config/config.php',
'routes/web' => 'routes/web.php',
'routes/api' => 'routes/api.php',
'views/index' => 'resources/views/index.blade.php',
'views/master' => 'resources/views/components/layouts/master.blade.php',
'scaffold/config' => 'config/config.php',
'composer' => 'composer.json',
'resources/scripts/module' => 'Resources/scripts/module.js',
'resources/sass/module' => 'Resources/sass/module.scss',
'resources/scripts/stores/sample-store' => 'Resources/scripts/stores/sample-store.js',
'resources/scripts/views/SamplePage' => 'Resources/scripts/views/SamplePage.vue',
'resources/locales/en' => 'Resources/locales/en.json',
'resources/locales/locales' => 'Resources/locales/locales.js',
'assets/js/app' => 'resources/assets/js/app.js',
'assets/sass/app' => 'resources/assets/sass/app.scss',
'vite' => 'vite.config.js',
'package' => 'package.json',
'postcss.config' => 'postcss.config.js',
'tailwind.config' => 'tailwind.config.js',
'vite.config' => 'vite.config.js',
// InvoiceShelf stubs (shipped by the invoiceshelf/modules package)
// — starter i18n files referenced by the registered menu title and
// settings schema.
'lang/menu' => 'lang/en/menu.php',
'lang/settings' => 'lang/en/settings.php',
],
'replacements' => [
'routes/web' => ['LOWER_NAME', 'STUDLY_NAME'],
'routes/api' => ['LOWER_NAME'],
'webpack' => ['LOWER_NAME'],
'json' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'PROVIDER_NAMESPACE'],
'routes/web' => ['LOWER_NAME', 'STUDLY_NAME', 'PLURAL_LOWER_NAME', 'KEBAB_NAME', 'MODULE_NAMESPACE', 'CONTROLLER_NAMESPACE'],
'routes/api' => ['LOWER_NAME', 'STUDLY_NAME', 'PLURAL_LOWER_NAME', 'KEBAB_NAME', 'MODULE_NAMESPACE', 'CONTROLLER_NAMESPACE'],
'vite' => ['LOWER_NAME', 'STUDLY_NAME', 'KEBAB_NAME'],
'json' => ['LOWER_NAME', 'STUDLY_NAME', 'KEBAB_NAME', 'MODULE_NAMESPACE', 'PROVIDER_NAMESPACE'],
'views/index' => ['LOWER_NAME'],
'views/master' => ['LOWER_NAME', 'STUDLY_NAME'],
'views/master' => ['LOWER_NAME', 'STUDLY_NAME', 'KEBAB_NAME'],
'scaffold/config' => ['STUDLY_NAME'],
'composer' => [
'LOWER_NAME',
@@ -62,205 +71,140 @@ return [
'AUTHOR_EMAIL',
'MODULE_NAMESPACE',
'PROVIDER_NAMESPACE',
'APP_FOLDER_NAME',
],
'resources/scripts/module' => ['LOWER_NAME'],
'resources/scripts/stores/sample-store' => ['LOWER_NAME'],
'vite.config' => ['LOWER_NAME'],
// InvoiceShelf lang stubs — only need the module name tokens.
'lang/menu' => ['STUDLY_NAME', 'LOWER_NAME'],
'lang/settings' => ['STUDLY_NAME', 'LOWER_NAME'],
],
'gitkeep' => true,
],
'paths' => [
/*
|--------------------------------------------------------------------------
| Modules path
|--------------------------------------------------------------------------
|
| This path used for save the generated module. This path also will be added
| automatically to list of scanned folders.
|
*/
'modules' => base_path('Modules'),
/*
|--------------------------------------------------------------------------
| Modules assets path
|--------------------------------------------------------------------------
|
| Here you may update the modules assets path.
|
*/
'assets' => public_path('modules'),
/*
|--------------------------------------------------------------------------
| The migrations path
|--------------------------------------------------------------------------
|
| Where you run 'module:publish-migration' command, where do you publish the
| the migration files?
|
*/
'migration' => base_path('database/migrations'),
/*
|--------------------------------------------------------------------------
| Generator path
|--------------------------------------------------------------------------
| Customise the paths where the folders will be generated.
| Set the generate key to false to not generate that folder
*/
'app_folder' => 'app/',
'generator' => [
'config' => ['path' => 'Config', 'generate' => true],
'command' => ['path' => 'Console', 'generate' => true],
'migration' => ['path' => 'Database/Migrations', 'generate' => true],
'seeder' => ['path' => 'Database/Seeders', 'generate' => true],
'factory' => ['path' => 'Database/factories', 'generate' => true],
'model' => ['path' => 'Entities', 'generate' => true],
'routes' => ['path' => 'Routes', 'generate' => true],
'controller' => ['path' => 'Http/Controllers', 'generate' => true],
'filter' => ['path' => 'Http/Middleware', 'generate' => true],
'request' => ['path' => 'Http/Requests', 'generate' => true],
'provider' => ['path' => 'Providers', 'generate' => true],
'assets' => ['path' => 'Resources/assets', 'generate' => true],
'lang' => ['path' => 'Resources/lang', 'generate' => true],
'views' => ['path' => 'Resources/views', 'generate' => true],
'test' => ['path' => 'Tests/Unit', 'generate' => true],
'test-feature' => ['path' => 'Tests/Feature', 'generate' => true],
'repository' => ['path' => 'Repositories', 'generate' => false],
'event' => ['path' => 'Events', 'generate' => false],
'listener' => ['path' => 'Listeners', 'generate' => false],
'policies' => ['path' => 'Policies', 'generate' => false],
'rules' => ['path' => 'Rules', 'generate' => false],
'jobs' => ['path' => 'Jobs', 'generate' => false],
'emails' => ['path' => 'Emails', 'generate' => false],
'notifications' => ['path' => 'Notifications', 'generate' => false],
'resource' => ['path' => 'Transformers', 'generate' => false],
'component-view' => ['path' => 'Resources/views/components', 'generate' => false],
'component-class' => ['path' => 'View/Components', 'generate' => false],
// app/
'actions' => ['path' => 'app/Actions', 'generate' => false],
'casts' => ['path' => 'app/Casts', 'generate' => false],
'channels' => ['path' => 'app/Broadcasting', 'generate' => false],
'class' => ['path' => 'app/Classes', 'generate' => false],
'command' => ['path' => 'app/Console', 'generate' => false],
'command_replacements' => ['path' => 'app/Console/Replacements', 'generate' => false],
'component-class' => ['path' => 'app/View/Components', 'generate' => false],
'emails' => ['path' => 'app/Emails', 'generate' => false],
'event' => ['path' => 'app/Events', 'generate' => false],
'enums' => ['path' => 'app/Enums', 'generate' => false],
'exceptions' => ['path' => 'app/Exceptions', 'generate' => false],
'jobs' => ['path' => 'app/Jobs', 'generate' => false],
'helpers' => ['path' => 'app/Helpers', 'generate' => false],
'interfaces' => ['path' => 'app/Interfaces', 'generate' => false],
'listener' => ['path' => 'app/Listeners', 'generate' => false],
'model' => ['path' => 'app/Models', 'generate' => false],
'notifications' => ['path' => 'app/Notifications', 'generate' => false],
'observer' => ['path' => 'app/Observers', 'generate' => false],
'policies' => ['path' => 'app/Policies', 'generate' => false],
'provider' => ['path' => 'app/Providers', 'generate' => true],
'repository' => ['path' => 'app/Repositories', 'generate' => false],
'resource' => ['path' => 'app/Transformers', 'generate' => false],
'route-provider' => ['path' => 'app/Providers', 'generate' => true],
'rules' => ['path' => 'app/Rules', 'generate' => false],
'services' => ['path' => 'app/Services', 'generate' => false],
'scopes' => ['path' => 'app/Models/Scopes', 'generate' => false],
'traits' => ['path' => 'app/Traits', 'generate' => false],
// app/Http/
'controller' => ['path' => 'app/Http/Controllers', 'generate' => true],
'filter' => ['path' => 'app/Http/Middleware', 'generate' => false],
'request' => ['path' => 'app/Http/Requests', 'generate' => false],
// config/
'config' => ['path' => 'config', 'generate' => true],
// database/
'factory' => ['path' => 'database/factories', 'generate' => true],
'migration' => ['path' => 'database/migrations', 'generate' => true],
'seeder' => ['path' => 'database/seeders', 'generate' => true],
// lang/
'lang' => ['path' => 'lang', 'generate' => false],
// resource/
'assets' => ['path' => 'resources/assets', 'generate' => true],
'component-view' => ['path' => 'resources/views/components', 'generate' => false],
'views' => ['path' => 'resources/views', 'generate' => true],
'inertia' => ['path' => 'resources/js/Pages', 'generate' => false],
'inertia-components' => ['path' => 'resources/js/Components', 'generate' => false],
// routes/
'routes' => ['path' => 'routes', 'generate' => true],
// tests/
'test-feature' => ['path' => 'tests/Feature', 'generate' => true],
'test-unit' => ['path' => 'tests/Unit', 'generate' => true],
],
],
/*
|--------------------------------------------------------------------------
| Auto Discover of Modules
|--------------------------------------------------------------------------
*/
'auto-discover' => [
'migrations' => true,
'translations' => false,
],
/*
|--------------------------------------------------------------------------
| Package commands
|--------------------------------------------------------------------------
|
| Here you can define which commands will be visible and used in your
| application. If for example you don't use some of the commands provided
| you can simply comment them out.
| Built dynamically from upstream nwidart's default command list. Add new
| commands to the merge() block below.
|
*/
'commands' => [
Commands\CommandMakeCommand::class,
Commands\ComponentClassMakeCommand::class,
Commands\ComponentViewMakeCommand::class,
Commands\ControllerMakeCommand::class,
Commands\DisableCommand::class,
Commands\DumpCommand::class,
Commands\EnableCommand::class,
Commands\EventMakeCommand::class,
Commands\JobMakeCommand::class,
Commands\ListenerMakeCommand::class,
Commands\MailMakeCommand::class,
Commands\MiddlewareMakeCommand::class,
Commands\NotificationMakeCommand::class,
Commands\ProviderMakeCommand::class,
Commands\RouteProviderMakeCommand::class,
Commands\InstallCommand::class,
Commands\ListCommand::class,
Commands\ModuleDeleteCommand::class,
Commands\ModuleMakeCommand::class,
Commands\FactoryMakeCommand::class,
Commands\PolicyMakeCommand::class,
Commands\RequestMakeCommand::class,
Commands\RuleMakeCommand::class,
Commands\MigrateCommand::class,
Commands\MigrateRefreshCommand::class,
Commands\MigrateResetCommand::class,
Commands\MigrateRollbackCommand::class,
Commands\MigrateStatusCommand::class,
Commands\MigrationMakeCommand::class,
Commands\ModelMakeCommand::class,
Commands\PublishCommand::class,
Commands\PublishConfigurationCommand::class,
Commands\PublishMigrationCommand::class,
Commands\PublishTranslationCommand::class,
Commands\SeedCommand::class,
Commands\SeedMakeCommand::class,
Commands\SetupCommand::class,
Commands\UnUseCommand::class,
Commands\UpdateCommand::class,
Commands\UseCommand::class,
Commands\ResourceMakeCommand::class,
Commands\TestMakeCommand::class,
Commands\LaravelModulesV6Migrator::class,
Commands\ComponentClassMakeCommand::class,
Commands\ComponentViewMakeCommand::class,
],
'commands' => ConsoleServiceProvider::defaultCommands()
->merge([
// InvoiceShelf-specific module commands go here
])->toArray(),
/*
|--------------------------------------------------------------------------
| Scan Path
|--------------------------------------------------------------------------
|
| Here you define which folder will be scanned. By default will scan vendor
| directory. This is useful if you host the package in packagist website.
|
*/
'scan' => [
'enabled' => false,
'paths' => [
base_path('vendor/*/*'),
],
],
/*
|--------------------------------------------------------------------------
| Composer File Template
|--------------------------------------------------------------------------
|
| Here is the config for composer.json file, generated by this package
|
*/
'composer' => [
'vendor' => 'nwidart',
'vendor' => env('MODULE_VENDOR', 'invoiceshelf'),
'author' => [
'name' => 'Nicolas Widart',
'email' => 'n.widart@gmail.com',
'name' => env('MODULE_AUTHOR_NAME', 'InvoiceShelf'),
'email' => env('MODULE_AUTHOR_EMAIL', 'hello@invoiceshelf.com'),
],
'composer-output' => false,
],
/*
|--------------------------------------------------------------------------
| Caching
|--------------------------------------------------------------------------
|
| Here is the config for setting up caching feature.
|
*/
'cache' => [
'enabled' => false,
'key' => 'laravel-modules',
'lifetime' => 60,
],
/*
|--------------------------------------------------------------------------
| Choose what laravel-modules will register as custom namespaces.
| Setting one to false will require you to register that part
| in your own Service Provider class.
|--------------------------------------------------------------------------
*/
'register' => [
'translations' => true,
/**
* load files on boot or register method
*
* Note: boot not compatible with asgardcms
*
* @example boot|register
*/
'files' => 'register',
],
@@ -269,9 +213,10 @@ return [
| Activators
|--------------------------------------------------------------------------
|
| You can define new types of activators here, file, database etc. The only
| required parameter is 'class'.
| The file activator will store the activation status in storage/installed_modules
| InvoiceShelf-specific override: keep the statuses file under storage/app/
| so existing installations don't lose track of which modules are enabled
| when this config is republished. Upstream v13 defaults to base_path() but
| the v3 ModuleInstaller already writes here.
*/
'activators' => [
'file' => [
@@ -283,4 +228,13 @@ return [
],
'activator' => 'file',
/*
|--------------------------------------------------------------------------
| Inertia
|--------------------------------------------------------------------------
*/
'inertia' => [
'frontend' => 'vue',
],
];