laranail/package-tools

Runtime base library for building Laravel packages — fluent Package builder + abstract PackageServiceProvider with attribute-driven discovery, package-tools.doctor, and isolation testing.

Maintainers

Package info

github.com/laranail/package-tools

Homepage

Documentation

pkg:composer/laranail/package-tools

Statistics

Installs: 6

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.3.0 2026-06-27 09:09 UTC

This package is auto-updated.

Last update: 2026-06-30 14:48:44 UTC


README

Latest version on Packagist Tests Static analysis License: MIT

Runtime base library for building Laravel packages.

A fluent Package builder and an abstract PackageServiceProvider your package extends, in the spirit of spatie/laravel-package-tools. On top of that base it adds attribute-driven discovery, a set of package-tools.* Artisan commands, abstract HTTP controllers, and a testing harness.

Status: v1.2.0 — namespaced config with publishable overrides, a consolidated fluent builder (config/views/components/assets/routes/middleware/events/commands/seeders) with singular and array-batch forms, publishFile()/publishDirectory(), attribute-driven discovery, abstract HTTP controllers, a testing harness, four Artisan commands, and runtime services.

Contents

Requirements

  • PHP ^8.4.1 (8.4 and 8.5 are supported; CI tests the full 8.4 / 8.5 matrix). The .1 floor comes from laranail/console (a dependency), which requires PHP ^8.4.1.
  • Laravel ^13.0
  • For development: Pest ^3.0, Testbench ^11.0, Larastan ^3.0

Installation

composer require laranail/package-tools

The service provider is auto-discovered, so the package-tools.* commands are available as soon as the package is installed.

Upgrading across a breaking release? See UPGRADING.md.

Quick start

Extend PackageServiceProvider and describe your package in configurePackage():

use Simtabi\Laranail\Package\Tools\Package;
use Simtabi\Laranail\Package\Tools\PackageServiceProvider;

final class FooServiceProvider extends PackageServiceProvider
{
    public function configurePackage(Package $package): void
    {
        $package
            ->name('vendor/foo')
            ->hasConfigFile()
            ->hasViews()
            ->hasMigration('create_foos_table')
            ->hasInstallCommand(fn ($command) => $command
                ->publishConfigFile()
                ->askToRunMigrations())
            ->discoversWithAttributes()
            ->hasDoctorCheck(FooHealthCheck::class);
    }
}

A complete, runnable example lives in docs/examples/.

Declarative registration & batch helpers

Beyond the core builder, Package exposes hooks for child providers, validation rules, about sections, doctor checks, and namespaced asset publishing — each with a batch sibling so you pass one array instead of chaining repeated calls:

$package
    ->name('vendor/foo')
    // Config files — the single call already accepts an array.
    ->hasConfigFile(['foo', 'foo-features'])
    // Register child / feature service providers (array form).
    ->hasChildProviders([Bar\BarServiceProvider::class, Baz\BazServiceProvider::class])
    // Opt-in route-middleware aliases (the canonical batch entry point).
    ->registerMiddlewareAliases(['foo.auth' => EnsureFoo::class])
    // Custom validator rules: `name => Class` or `name => [Class, message]`.
    ->hasValidationRules([
        'strong_password' => [StrongPassword::class, 'The :attribute is too weak.'],
    ])
    // `php artisan about` sections: label => callable.
    ->hasAboutSections(['Foo' => fn (): array => ['Version' => Foo::VERSION]])
    // Doctor checks (batch).
    ->hasDoctorChecks([FooHealthCheck::class, BarHealthCheck::class])
    // Publish an individual FILE or DIRECTORY under a namespaced `::` tag.
    ->publishFile('config/foo-data.php', config_path('foo-data.php'), 'data')   // tag: vendor::foo-data
    ->publishDirectory('stubs', base_path('stubs/vendor/foo'), 'stubs');        // tag: vendor::foo-stubs

The singular forms (hasValidationRule(), hasAboutSection(), hasDoctorCheck(), registerMiddlewareAlias(), sharesDataWithAllViews($name, $value)) remain for one-off registration.

Testing published config overrides

A namespaced config publishes to a nested config/vendor/package.php that Laravel never auto-loads; the provider bridges it back into the dotted key at register time. To test that round-trip reliably (without racing Testbench's getEnvironmentSetUp()), use the AssertsPublishedConfigOverrides trait — it's already mixed into Testing\IsolatedTestCase:

use Simtabi\Laranail\Package\Tools\Testing\AssertsPublishedConfigOverrides;

$this->assertPublishedConfigOverride(
    FooServiceProvider::class, 'vendor.foo',
    ['enabled' => false], 'vendor.foo.enabled', false,
);

What you get

Capability Summary
Fluent Package builder Spatie-compatible surface: name(), hasConfigFile(), hasViews(), hasViewComponents(), hasInertiaComponents(), hasViewComposer(), sharesDataWithAllViews(), hasTranslations(), hasAssets(), hasRoute(), hasMigration(), runsMigrations(), discoversMigrations(), hasCommand(), hasInstallCommand(), publishesServiceProvider().
Attribute discovery discoversWithAttributes() scans src/ for #[AsArtisanCommand], #[AsRoute], and #[AsViewComposer] and registers them for you.
Namespaced config hasNestedConfig() / discoversConfig() mount config sub-folders at dotted keys (config/admin/panel.phpconfig('admin.panel.*')), with an optional in-file __namespace. See config-namespacing.
Artisan commands laranail::package-tools.doctor, .sbom, .audit, .ide-helper (see below).
HTTP controllers Optional WebController / ApiController base classes for package routes.
Testing harness Testing\IsolatedTestCase — Testbench wrapper with in-memory SQLite, sync queue, and schema/command assertions.
Runtime services SystemService, HttpConfigurationService, and ErrorStorageService, bound by the provider and resolvable from the container.

The full reference is in docs/ — see Documentation.

Artisan commands

Registered automatically via package auto-discovery.

Command Purpose Options
php artisan laranail::package-tools.doctor Run every registered DoctorCheck; non-zero exit on any failure. --json, --strict
php artisan laranail::package-tools.sbom Emit a CycloneDX 1.5 JSON SBOM for composer.lock. --output=, --print
php artisan laranail::package-tools.audit Query OSV.dev for advisories in composer.lock; non-zero exit on any advisory. --no-dev, --json, --timeout=
php artisan laranail::package-tools.ide-helper Generate Facade classes from #[AsFacade] contracts with @method docblocks. --source=, --output=

Commands follow the laranail::<package-slug>.<command> naming used across the laranail family (the :: separator is enabled by the package's base command — see CONTRIBUTING.md).

Attribute discovery

use Simtabi\Laranail\Package\Tools\Attributes\AsArtisanCommand;
use Simtabi\Laranail\Package\Tools\Attributes\AsRoute;
use Simtabi\Laranail\Package\Tools\Attributes\AsViewComposer;
use Simtabi\Laranail\Package\Tools\Attributes\AsFacade;

#[AsArtisanCommand(signature: 'foo:run', description: 'Run the foo task')]
class FooCommand extends Command {}

#[AsRoute(method: 'GET', uri: '/foo')]
#[AsRoute(method: 'POST', uri: '/foo', name: 'foo.create', middleware: ['web'])]
class FooController {}

#[AsViewComposer(views: ['layouts.app', 'partials.header'])]
class AppViewComposer {}

See docs/tools/attribute-discovery.md.

Documentation

Hosted at opensource.simtabi.com/package-tools/docs/ (product page: opensource.simtabi.com/package-tools/). The same pages live under docs/:

Guides

  • Installation — requirements, install, auto-registration
  • Configuration — the fluent Package builder and lifecycle hooks
  • Seeding — package + standalone seeding: registry, executor, fluent builder, SeederExecutionStats, events, console output
  • Architecture — how the runtime is put together
  • Services reference — the service and support classes

Tools & features

Examples

  • Runnable examples — a cohesive Acme\Hello package that demonstrates every feature end to end: the fluent builder and lifecycle hooks, all four discovery attributes, namespaced commands, the WebController/ApiController bases, doctor checks, the runtime services and consumer traits, package seeders, the install-command flow, and an IsolatedTestCase test

Local development

bash .scripts/init.sh   # verify PHP >= 8.4, run composer install, smoke-check
composer setup          # alias for .scripts/init.sh
composer test           # run the Pest suite
composer test-coverage  # run with coverage
composer lint           # Pint + PHPStan + Rector (dry run)
composer pint-fix       # apply Pint formatting
composer rector-fix     # apply Rector transformations
composer audit          # composer security audit

.scripts/init.sh is the only shell script in the repo; everything else runs through Composer.

Provenance

The open API surface (hasConfigFile(), hasViews(), the lifecycle hooks, and friends) is intentionally compatible with spatie/laravel-package-tools (MIT). Code is copyright Simtabi LLC; see LICENSE.

Sister packages

Contributing & security

License

MIT. See LICENSE.