refactor: susun semula struktur folder — Laravel source ke src/
This commit is contained in:
21
vendor/laravel/roster/LICENSE.md
vendored
Normal file
21
vendor/laravel/roster/LICENSE.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Taylor Otwell
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
74
vendor/laravel/roster/README.md
vendored
Normal file
74
vendor/laravel/roster/README.md
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
# Laravel Roster
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/roster/actions"><img src="https://github.com/laravel/roster/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/roster"><img src="https://img.shields.io/packagist/dt/laravel/roster" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/roster"><img src="https://img.shields.io/packagist/v/laravel/roster" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/roster"><img src="https://img.shields.io/packagist/l/laravel/roster" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## Introduction
|
||||
|
||||
Laravel Roster detects which Laravel ecosystem packages are in use within a project, and gives you an easy to use API to work with that data.
|
||||
|
||||
|
||||
## Installation
|
||||
To get started, install Roster via Composer:
|
||||
|
||||
```bash
|
||||
composer require laravel/roster --dev
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
**Scan a directory**
|
||||
|
||||
Get a roster of installed packages by scanning a directory:
|
||||
|
||||
```php
|
||||
use Laravel\Roster\Roster;
|
||||
|
||||
$roster = Roster::scan($directory);
|
||||
```
|
||||
|
||||
**Query the roster**
|
||||
```php
|
||||
use Laravel\Roster\Packages;
|
||||
|
||||
// Get all packages
|
||||
$roster->packages();
|
||||
|
||||
// Get only packages that will be used in production
|
||||
$roster->packages()->production();
|
||||
|
||||
// Packages that are only used for dev
|
||||
$roster->packages()->dev();
|
||||
|
||||
// Check if a package is in use
|
||||
$roster->uses(Packages::INERTIA);
|
||||
|
||||
// Check if a particular version of a package is in use
|
||||
$roster->usesVersion(Packages::INERTIA, '2.0.0', '>=');
|
||||
|
||||
// Detect which JavaScript package manager is in use
|
||||
$packageManager = $roster->nodePackageManager();
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to Roster! The contribution guide can be found in
|
||||
the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by
|
||||
the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
Please review [our security policy](https://github.com/laravel/roster/security/policy) on how to report security
|
||||
vulnerabilities.
|
||||
|
||||
## License
|
||||
|
||||
Laravel Roster is open-sourced software licensed under the [MIT license](LICENSE.md).
|
||||
70
vendor/laravel/roster/composer.json
vendored
Normal file
70
vendor/laravel/roster/composer.json
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "laravel/roster",
|
||||
"description": "Detect packages & approaches in use within a Laravel project",
|
||||
"keywords": [
|
||||
"dev",
|
||||
"laravel"
|
||||
],
|
||||
"homepage": "https://github.com/laravel/roster",
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/roster/issues",
|
||||
"source": "https://github.com/laravel/roster"
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"illuminate/console": "^11.0|^12.0|^13.0",
|
||||
"illuminate/contracts": "^11.0|^12.0|^13.0",
|
||||
"illuminate/routing": "^11.0|^12.0|^13.0",
|
||||
"illuminate/support": "^11.0|^12.0|^13.0",
|
||||
"symfony/yaml": "^7.2|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "^1.14",
|
||||
"mockery/mockery": "^1.6",
|
||||
"orchestra/testbench": "^9.0|^10.0|^11.0",
|
||||
"pestphp/pest": "^3.0|^4.1",
|
||||
"phpstan/phpstan": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Roster\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Roster\\RosterServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"lint": [
|
||||
"vendor/bin/pint",
|
||||
"vendor/bin/phpstan"
|
||||
],
|
||||
"test": [
|
||||
"vendor/bin/pest"
|
||||
],
|
||||
"check": [
|
||||
"@composer lint",
|
||||
"@composer test"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
20
vendor/laravel/roster/src/Approach.php
vendored
Normal file
20
vendor/laravel/roster/src/Approach.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster;
|
||||
|
||||
use Laravel\Roster\Enums\Approaches;
|
||||
|
||||
class Approach
|
||||
{
|
||||
public function __construct(protected Approaches $approach) {}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return $this->approach->name;
|
||||
}
|
||||
|
||||
public function approach(): Approaches
|
||||
{
|
||||
return $this->approach;
|
||||
}
|
||||
}
|
||||
39
vendor/laravel/roster/src/Console/ScanCommand.php
vendored
Normal file
39
vendor/laravel/roster/src/Console/ScanCommand.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Laravel\Roster\Roster;
|
||||
|
||||
class ScanCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'roster:scan {directory}';
|
||||
|
||||
protected $description = 'Detect packages & approaches in use and output as JSON';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$directory = $this->argument('directory');
|
||||
if (! is_string($directory)) {
|
||||
$this->error('Pass a directory');
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
if (! is_dir($directory) || ! is_readable($directory)) {
|
||||
$this->error("Directory '{$directory}' isn't a directory");
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$roster = Roster::scan($directory);
|
||||
$this->line($roster->json());
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
10
vendor/laravel/roster/src/Enums/Approaches.php
vendored
Normal file
10
vendor/laravel/roster/src/Enums/Approaches.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Enums;
|
||||
|
||||
enum Approaches: string
|
||||
{
|
||||
case ACTION = 'action';
|
||||
case DDD = 'ddd';
|
||||
case MODULAR = 'modular';
|
||||
}
|
||||
14
vendor/laravel/roster/src/Enums/Ides.php
vendored
Normal file
14
vendor/laravel/roster/src/Enums/Ides.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Enums;
|
||||
|
||||
enum Ides: string
|
||||
{
|
||||
case PHPSTORM = 'phpstorm';
|
||||
case CURSOR = 'cursor';
|
||||
case WINDSURF = 'windsurf';
|
||||
case VSCODE = 'vscode';
|
||||
case CLAUDE_CODE = 'claudecode';
|
||||
case CODEX = 'codex';
|
||||
case OPENCODE = 'opencode';
|
||||
}
|
||||
26
vendor/laravel/roster/src/Enums/NodePackageManager.php
vendored
Normal file
26
vendor/laravel/roster/src/Enums/NodePackageManager.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Enums;
|
||||
|
||||
use Laravel\Roster\Scanners\BunPackageLock;
|
||||
use Laravel\Roster\Scanners\NpmPackageLock;
|
||||
use Laravel\Roster\Scanners\PnpmPackageLock;
|
||||
use Laravel\Roster\Scanners\YarnPackageLock;
|
||||
|
||||
enum NodePackageManager: string
|
||||
{
|
||||
case NPM = 'npm';
|
||||
case PNPM = 'pnpm';
|
||||
case YARN = 'yarn';
|
||||
case BUN = 'bun';
|
||||
|
||||
public function scanner(string $path): NpmPackageLock|PnpmPackageLock|YarnPackageLock|BunPackageLock
|
||||
{
|
||||
return match ($this) {
|
||||
self::NPM => new NpmPackageLock($path),
|
||||
self::PNPM => new PnpmPackageLock($path),
|
||||
self::YARN => new YarnPackageLock($path),
|
||||
self::BUN => new BunPackageLock($path),
|
||||
};
|
||||
}
|
||||
}
|
||||
9
vendor/laravel/roster/src/Enums/PackageSource.php
vendored
Normal file
9
vendor/laravel/roster/src/Enums/PackageSource.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Enums;
|
||||
|
||||
enum PackageSource: string
|
||||
{
|
||||
case COMPOSER = 'composer';
|
||||
case NPM = 'npm';
|
||||
}
|
||||
62
vendor/laravel/roster/src/Enums/Packages.php
vendored
Normal file
62
vendor/laravel/roster/src/Enums/Packages.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Enums;
|
||||
|
||||
enum Packages: string
|
||||
{
|
||||
// BACKEND
|
||||
case AI = 'ai';
|
||||
case BOOST = 'boost';
|
||||
case BREEZE = 'breeze';
|
||||
case CASHIER = 'cashier';
|
||||
case DUSK = 'dusk';
|
||||
case ENVOY = 'envoy';
|
||||
case FILAMENT = 'filament';
|
||||
case FOLIO = 'folio';
|
||||
case FORTIFY = 'fortify';
|
||||
case FLUXUI_FREE = 'flux_free';
|
||||
case FLUXUI_PRO = 'flux_pro';
|
||||
case HORIZON = 'horizon';
|
||||
case INERTIA_LARAVEL = 'inertia-laravel';
|
||||
case LARASTAN = 'larastan';
|
||||
case LARAVEL = 'laravel';
|
||||
case LIVEWIRE = 'livewire';
|
||||
case MCP = 'mcp';
|
||||
case NIGHTWATCH = 'nightwatch';
|
||||
case NOVA = 'nova';
|
||||
case OCTANE = 'octane';
|
||||
case PAIL = 'pail';
|
||||
case PASSPORT = 'passport';
|
||||
case PENNANT = 'pennant';
|
||||
case PEST = 'pest';
|
||||
case PHPUNIT = 'phpunit';
|
||||
case PINT = 'pint';
|
||||
case PROMPTS = 'prompts';
|
||||
case PULSE = 'pulse';
|
||||
case RECTOR = 'rector';
|
||||
case REVERB = 'reverb';
|
||||
case SAIL = 'sail';
|
||||
case SANCTUM = 'sanctum';
|
||||
case SCOUT = 'scout';
|
||||
case SOCIALITE = 'socialite';
|
||||
case STATAMIC = 'statamic';
|
||||
case TELESCOPE = 'telescope';
|
||||
case VOLT = 'volt';
|
||||
case WAYFINDER = 'wayfinder';
|
||||
case ZIGGY = 'ziggy';
|
||||
|
||||
// NPM
|
||||
case ALPINEJS = 'alpinejs';
|
||||
case ECHO = 'laravel-echo';
|
||||
case ECHO_REACT = 'echo-react';
|
||||
case ECHO_VUE = 'echo-vue';
|
||||
case ESLINT = 'eslint';
|
||||
case INERTIA_REACT = 'inertia-react';
|
||||
case INERTIA_SVELTE = 'inertia-svelte';
|
||||
case INERTIA_VUE = 'inertia-vue';
|
||||
case PRETTIER = 'prettier';
|
||||
case REACT = 'react';
|
||||
case TAILWINDCSS = 'tailwindcss';
|
||||
case VUE = 'vue';
|
||||
case WAYFINDER_VITE = 'wayfinder_vite';
|
||||
}
|
||||
115
vendor/laravel/roster/src/Package.php
vendored
Normal file
115
vendor/laravel/roster/src/Package.php
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster;
|
||||
|
||||
use Laravel\Roster\Enums\Packages;
|
||||
use Laravel\Roster\Enums\PackageSource;
|
||||
|
||||
class Package
|
||||
{
|
||||
protected bool $direct = false;
|
||||
|
||||
protected string $constraint = '';
|
||||
|
||||
protected ?PackageSource $source = null;
|
||||
|
||||
public function __construct(
|
||||
protected Packages $package,
|
||||
protected string $packageName,
|
||||
protected string $version,
|
||||
protected bool $dev = false,
|
||||
protected ?string $path = null
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
public function setDev(bool $dev = true): self
|
||||
{
|
||||
$this->dev = $dev;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDirect(bool $direct = true): self
|
||||
{
|
||||
$this->direct = $direct;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setConstraint(string $constraint = ''): self
|
||||
{
|
||||
$this->constraint = $constraint;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSource(PackageSource $source): self
|
||||
{
|
||||
$this->source = $source;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPath(string $path): self
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return $this->package->name;
|
||||
}
|
||||
|
||||
public function package(): Packages
|
||||
{
|
||||
return $this->package;
|
||||
}
|
||||
|
||||
public function version(): string
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function direct(): bool
|
||||
{
|
||||
return $this->direct;
|
||||
}
|
||||
|
||||
public function indirect(): bool
|
||||
{
|
||||
return ! $this->direct;
|
||||
}
|
||||
|
||||
public function constraint(): string
|
||||
{
|
||||
return $this->constraint;
|
||||
}
|
||||
|
||||
public function majorVersion(): string
|
||||
{
|
||||
return explode('.', $this->version)[0];
|
||||
}
|
||||
|
||||
public function isDev(): bool
|
||||
{
|
||||
return $this->dev;
|
||||
}
|
||||
|
||||
public function source(): ?PackageSource
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
public function path(): ?string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function rawName(): string
|
||||
{
|
||||
return $this->packageName;
|
||||
}
|
||||
}
|
||||
21
vendor/laravel/roster/src/PackageCollection.php
vendored
Normal file
21
vendor/laravel/roster/src/PackageCollection.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* @extends Collection<int, Package>
|
||||
*/
|
||||
class PackageCollection extends Collection
|
||||
{
|
||||
public function dev(): static
|
||||
{
|
||||
return $this->filter(fn (Package $package) => $package->isDev());
|
||||
}
|
||||
|
||||
public function production(): static
|
||||
{
|
||||
return $this->filter(fn (Package $package) => ! $package->isDev());
|
||||
}
|
||||
}
|
||||
165
vendor/laravel/roster/src/Roster.php
vendored
Normal file
165
vendor/laravel/roster/src/Roster.php
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Roster\Enums\Approaches;
|
||||
use Laravel\Roster\Enums\NodePackageManager;
|
||||
use Laravel\Roster\Enums\Packages;
|
||||
use Laravel\Roster\Scanners\Composer;
|
||||
use Laravel\Roster\Scanners\DirectoryStructure;
|
||||
use Laravel\Roster\Scanners\PackageLock;
|
||||
|
||||
/**
|
||||
* Package and approach detection service for Laravel projects.
|
||||
*
|
||||
* Scans composer.lock, package-lock.json, and directory structure to identify
|
||||
* packages and development approaches in use.
|
||||
*/
|
||||
class Roster
|
||||
{
|
||||
/**
|
||||
* @var Collection<int, \Laravel\Roster\Approach>
|
||||
*/
|
||||
protected Collection $approaches;
|
||||
|
||||
protected PackageCollection $packages;
|
||||
|
||||
protected ?NodePackageManager $nodePackageManager = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->approaches = collect();
|
||||
$this->packages = new PackageCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function add(Package|Approach $item): self
|
||||
{
|
||||
return match (get_class($item)) {
|
||||
Package::class => $this->addPackage($item),
|
||||
Approach::class => $this->addApproach($item),
|
||||
default => throw new \InvalidArgumentException('Unexpected match value'),
|
||||
};
|
||||
}
|
||||
|
||||
public function uses(Packages|Approaches $item): bool
|
||||
{
|
||||
return $this->findItem($item) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function usesVersion(Packages $package, string $version, string $operator = '='): bool
|
||||
{
|
||||
if (! preg_match('/[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,}/', $version)) {
|
||||
throw new \InvalidArgumentException('SEMVER required');
|
||||
}
|
||||
|
||||
$validOperators = ['<', '<=', '>', '>=', '==', '=', '!=', '<>'];
|
||||
if (! in_array($operator, $validOperators)) {
|
||||
throw new \InvalidArgumentException('Invalid operator');
|
||||
}
|
||||
|
||||
$package = $this->findItem($package);
|
||||
if (is_null($package)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var \Laravel\Roster\Package $package */
|
||||
return version_compare($package->version(), $version, $operator);
|
||||
}
|
||||
|
||||
protected function findItem(Packages|Approaches $item): Package|Approach|null
|
||||
{
|
||||
return match (get_class($item)) {
|
||||
Packages::class => $this->package($item),
|
||||
Approaches::class => $this->approach($item),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
protected function addPackage(Package $package): self
|
||||
{
|
||||
$this->packages->push($package);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function addApproach(Approach $approach): self
|
||||
{
|
||||
$this->approaches->push($approach);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, \Laravel\Roster\Approach>
|
||||
*/
|
||||
public function approaches(): Collection
|
||||
{
|
||||
return $this->approaches;
|
||||
}
|
||||
|
||||
public function packages(): PackageCollection
|
||||
{
|
||||
return $this->packages;
|
||||
}
|
||||
|
||||
public function package(Packages $package): ?Package
|
||||
{
|
||||
return $this->packages->first(fn (Package $item) => $item->package()->value === $package->value);
|
||||
}
|
||||
|
||||
public function approach(Approaches $approach): ?Approach
|
||||
{
|
||||
return $this->approaches->first(fn (Approach $item) => $item->approach()->value === $approach->value);
|
||||
}
|
||||
|
||||
public function nodePackageManager(): ?NodePackageManager
|
||||
{
|
||||
return $this->nodePackageManager;
|
||||
}
|
||||
|
||||
public function json(): string
|
||||
{
|
||||
return json_encode([
|
||||
'approaches' => $this->approaches->map(fn (Approach $approach) => [
|
||||
'name' => $approach->name(),
|
||||
])->toArray(),
|
||||
'packages' => $this->packages->map(fn (Package $package) => [
|
||||
'name' => $package->name(),
|
||||
'version' => $package->version(),
|
||||
'source' => $package->source()?->value,
|
||||
'path' => $package->path(),
|
||||
])->toArray(),
|
||||
'nodePackageManager' => $this->nodePackageManager?->value,
|
||||
], JSON_PRETTY_PRINT) ?: '{}';
|
||||
}
|
||||
|
||||
public static function scan(?string $basePath = null): self
|
||||
{
|
||||
$roster = new self;
|
||||
$basePath = ($basePath ?? base_path()).DIRECTORY_SEPARATOR;
|
||||
|
||||
(new Composer($basePath.'composer.lock'))
|
||||
->scan()
|
||||
->each(fn ($item) => $roster->add($item));
|
||||
|
||||
$packageLock = new PackageLock($basePath);
|
||||
|
||||
$packageLock->scan()
|
||||
->each(fn ($item) => $roster->add($item));
|
||||
|
||||
(new DirectoryStructure($basePath))
|
||||
->scan()
|
||||
->each(fn ($item) => $roster->add($item));
|
||||
|
||||
$roster->nodePackageManager = $packageLock->detect();
|
||||
|
||||
return $roster;
|
||||
}
|
||||
}
|
||||
28
vendor/laravel/roster/src/RosterServiceProvider.php
vendored
Normal file
28
vendor/laravel/roster/src/RosterServiceProvider.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class RosterServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any package services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->registerCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the package's commands.
|
||||
*/
|
||||
protected function registerCommands(): void
|
||||
{
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->commands([
|
||||
Console\ScanCommand::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
186
vendor/laravel/roster/src/Scanners/BasePackageScanner.php
vendored
Normal file
186
vendor/laravel/roster/src/Scanners/BasePackageScanner.php
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Scanners;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Laravel\Roster\Approach;
|
||||
use Laravel\Roster\Enums\Approaches;
|
||||
use Laravel\Roster\Enums\Packages;
|
||||
use Laravel\Roster\Enums\PackageSource;
|
||||
use Laravel\Roster\Package;
|
||||
|
||||
abstract class BasePackageScanner
|
||||
{
|
||||
/**
|
||||
* Map of package names to enums
|
||||
*
|
||||
* @var array<string, Packages|Approaches>
|
||||
*/
|
||||
protected array $map = [
|
||||
'alpinejs' => Packages::ALPINEJS,
|
||||
'eslint' => Packages::ESLINT,
|
||||
'@inertiajs/react' => Packages::INERTIA_REACT,
|
||||
'@inertiajs/svelte' => Packages::INERTIA_SVELTE,
|
||||
'@inertiajs/vue3' => Packages::INERTIA_VUE,
|
||||
'laravel-echo' => Packages::ECHO,
|
||||
'@laravel/echo-react' => Packages::ECHO_REACT,
|
||||
'@laravel/echo-vue' => Packages::ECHO_VUE,
|
||||
'@laravel/vite-plugin-wayfinder' => Packages::WAYFINDER_VITE,
|
||||
'prettier' => Packages::PRETTIER,
|
||||
'react' => Packages::REACT,
|
||||
'tailwindcss' => Packages::TAILWINDCSS,
|
||||
'vue' => Packages::VUE,
|
||||
];
|
||||
|
||||
/** @var array<string, array{constraint: string, isDev: bool}>|null */
|
||||
protected ?array $directPackages = null;
|
||||
|
||||
public function __construct(protected string $path) {}
|
||||
|
||||
/**
|
||||
* Returns the expected lock file name
|
||||
*/
|
||||
abstract protected function lockFile(): string;
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection<int, \Laravel\Roster\Package|\Laravel\Roster\Approach>
|
||||
*/
|
||||
abstract public function scan(): Collection;
|
||||
|
||||
/**
|
||||
* Check if the scanner can handle the given path
|
||||
*/
|
||||
public function canScan(): bool
|
||||
{
|
||||
return file_exists($this->lockFilePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file path of the lock file
|
||||
*/
|
||||
protected function lockFilePath(): string
|
||||
{
|
||||
return $this->path.$this->lockFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process dependencies and add them to the mapped items collection
|
||||
*
|
||||
* @param array<string, string> $dependencies
|
||||
* @param Collection<int, Package|Approach> $mappedItems
|
||||
* @param ?callable $versionCb - callback to override version
|
||||
*/
|
||||
protected function processDependencies(array $dependencies, Collection $mappedItems, bool $isDev, ?callable $versionCb = null): void
|
||||
{
|
||||
$directPackages = $this->direct();
|
||||
|
||||
foreach ($dependencies as $packageName => $version) {
|
||||
$mappedPackage = $this->map[$packageName] ?? null;
|
||||
if (is_null($mappedPackage)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_null($versionCb)) {
|
||||
$version = $versionCb($packageName, $version);
|
||||
}
|
||||
|
||||
$direct = false;
|
||||
$constraint = $version;
|
||||
$packageIsDev = $isDev;
|
||||
|
||||
if (array_key_exists($packageName, $directPackages)) {
|
||||
$direct = true;
|
||||
$constraint = $directPackages[$packageName]['constraint'];
|
||||
$packageIsDev = $directPackages[$packageName]['isDev'];
|
||||
}
|
||||
|
||||
$niceVersion = preg_replace('/[^0-9.]/', '', $version) ?? '';
|
||||
$mappedItems->push(match (get_class($mappedPackage)) {
|
||||
Packages::class => (new Package($mappedPackage, $packageName, $niceVersion, $packageIsDev))->setDirect($direct)->setConstraint($constraint)->setSource(PackageSource::NPM)->setPath($this->computePath($packageName)),
|
||||
Approaches::class => new Approach($mappedPackage),
|
||||
default => throw new \InvalidArgumentException('Unsupported mapping')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns direct dependencies as defined in package.json
|
||||
*
|
||||
* @return array<string, array{constraint: string, isDev: bool}>
|
||||
*/
|
||||
protected function direct(): array
|
||||
{
|
||||
if ($this->directPackages !== null) {
|
||||
return $this->directPackages;
|
||||
}
|
||||
|
||||
$this->directPackages = [];
|
||||
$filename = $this->path.'package.json';
|
||||
|
||||
if (! file_exists($filename) || ! is_readable($filename)) {
|
||||
return $this->directPackages;
|
||||
}
|
||||
|
||||
$contents = file_get_contents($filename);
|
||||
if ($contents === false) {
|
||||
return $this->directPackages;
|
||||
}
|
||||
|
||||
$json = json_decode($contents, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE || ! is_array($json)) {
|
||||
return $this->directPackages;
|
||||
}
|
||||
|
||||
foreach ((array) ($json['dependencies'] ?? []) as $name => $constraint) {
|
||||
$this->directPackages[$name] = [
|
||||
'constraint' => $constraint,
|
||||
'isDev' => false,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ((array) ($json['devDependencies'] ?? []) as $name => $constraint) {
|
||||
$this->directPackages[$name] = [
|
||||
'constraint' => $constraint,
|
||||
'isDev' => true,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->directPackages;
|
||||
}
|
||||
|
||||
protected function computePath(string $packageName): string
|
||||
{
|
||||
$basePath = realpath($this->path) ?: $this->path;
|
||||
|
||||
return $basePath.DIRECTORY_SEPARATOR.'node_modules'.DIRECTORY_SEPARATOR
|
||||
.str_replace('/', DIRECTORY_SEPARATOR, $packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common file validation logic
|
||||
*/
|
||||
protected function validateFile(string $path, string $type = 'Package'): ?string
|
||||
{
|
||||
if (! file_exists($path)) {
|
||||
Log::warning("Failed to scan $type: $path");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! is_readable($path)) {
|
||||
Log::warning("File not readable: $path");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$contents = file_get_contents($path);
|
||||
if ($contents === false) {
|
||||
Log::warning("Failed to read $type: $path");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
}
|
||||
58
vendor/laravel/roster/src/Scanners/BunPackageLock.php
vendored
Normal file
58
vendor/laravel/roster/src/Scanners/BunPackageLock.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Scanners;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class BunPackageLock extends BasePackageScanner
|
||||
{
|
||||
protected function lockFile(): string
|
||||
{
|
||||
return 'bun.lock';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection<int, \Laravel\Roster\Package|\Laravel\Roster\Approach>
|
||||
*/
|
||||
public function scan(): Collection
|
||||
{
|
||||
$mappedItems = collect();
|
||||
$lockFilePath = $this->lockFilePath();
|
||||
|
||||
$contents = $this->validateFile($lockFilePath);
|
||||
if ($contents === null) {
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
// Remove trailing commas before decoding
|
||||
/** @var string $contents */
|
||||
$contents = preg_replace('/,\s*([]}])/m', '$1', $contents);
|
||||
$json = json_decode($contents, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE || ! is_array($json)) {
|
||||
Log::warning('Failed to decode Package: '.$lockFilePath.'. '.json_last_error_msg());
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
/** @var array<string, array<string, mixed>> $json */
|
||||
if (! isset($json['workspaces']['']) || ! isset($json['packages'])) {
|
||||
Log::warning('Malformed bun.lock');
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
/** @var array<string, mixed> $workspace */
|
||||
$workspace = $json['workspaces'][''];
|
||||
|
||||
/** @var array<string, string> $dependencies */
|
||||
$dependencies = $workspace['dependencies'] ?? [];
|
||||
/** @var array<string, string> $devDependencies */
|
||||
$devDependencies = $workspace['devDependencies'] ?? [];
|
||||
|
||||
$this->processDependencies($dependencies, $mappedItems, false);
|
||||
$this->processDependencies($devDependencies, $mappedItems, true);
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
}
|
||||
216
vendor/laravel/roster/src/Scanners/Composer.php
vendored
Normal file
216
vendor/laravel/roster/src/Scanners/Composer.php
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Scanners;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Laravel\Roster\Approach;
|
||||
use Laravel\Roster\Enums\Approaches;
|
||||
use Laravel\Roster\Enums\Packages;
|
||||
use Laravel\Roster\Enums\PackageSource;
|
||||
use Laravel\Roster\Package;
|
||||
|
||||
class Composer
|
||||
{
|
||||
protected string $vendorDir = 'vendor';
|
||||
|
||||
/**
|
||||
* Map of composer package names to enums
|
||||
*
|
||||
* @var array<string, Packages|Approaches>
|
||||
*/
|
||||
protected array $map = [
|
||||
'filament/filament' => Packages::FILAMENT,
|
||||
'inertiajs/inertia-laravel' => Packages::INERTIA_LARAVEL,
|
||||
'larastan/larastan' => Packages::LARASTAN,
|
||||
'laravel/ai' => Packages::AI,
|
||||
'laravel/boost' => Packages::BOOST,
|
||||
'laravel/breeze' => Packages::BREEZE,
|
||||
'laravel/cashier' => Packages::CASHIER,
|
||||
'laravel/dusk' => Packages::DUSK,
|
||||
'laravel/envoy' => Packages::ENVOY,
|
||||
'laravel/folio' => Packages::FOLIO,
|
||||
'laravel/fortify' => Packages::FORTIFY,
|
||||
'laravel/framework' => Packages::LARAVEL,
|
||||
'laravel/horizon' => Packages::HORIZON,
|
||||
'laravel/mcp' => Packages::MCP,
|
||||
'laravel/nightwatch' => Packages::NIGHTWATCH,
|
||||
'laravel/nova' => Packages::NOVA,
|
||||
'laravel/octane' => Packages::OCTANE,
|
||||
'laravel/pail' => Packages::PAIL,
|
||||
'laravel/passport' => Packages::PASSPORT,
|
||||
'laravel/pennant' => Packages::PENNANT,
|
||||
'laravel/pint' => Packages::PINT,
|
||||
'laravel/prompts' => Packages::PROMPTS,
|
||||
'laravel/pulse' => Packages::PULSE,
|
||||
'laravel/reverb' => Packages::REVERB,
|
||||
'laravel/sail' => Packages::SAIL,
|
||||
'laravel/sanctum' => Packages::SANCTUM,
|
||||
'laravel/scout' => Packages::SCOUT,
|
||||
'laravel/socialite' => Packages::SOCIALITE,
|
||||
'laravel/telescope' => Packages::TELESCOPE,
|
||||
'laravel/wayfinder' => Packages::WAYFINDER,
|
||||
'livewire/flux' => Packages::FLUXUI_FREE,
|
||||
'livewire/flux-pro' => Packages::FLUXUI_PRO,
|
||||
'livewire/livewire' => Packages::LIVEWIRE,
|
||||
'livewire/volt' => Packages::VOLT,
|
||||
'pestphp/pest' => Packages::PEST,
|
||||
'phpunit/phpunit' => Packages::PHPUNIT,
|
||||
'rector/rector' => Packages::RECTOR,
|
||||
'statamic/cms' => Packages::STATAMIC,
|
||||
'tightenco/ziggy' => Packages::ZIGGY,
|
||||
];
|
||||
|
||||
/** @var array<string, array{constraint: string, isDev: bool}> */
|
||||
protected array $directPackages = [];
|
||||
|
||||
/**
|
||||
* @param string $path - composer.lock
|
||||
*/
|
||||
public function __construct(protected string $path) {}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection<int, \Laravel\Roster\Package|\Laravel\Roster\Approach>
|
||||
*/
|
||||
public function scan(): Collection
|
||||
{
|
||||
$mappedItems = collect([]);
|
||||
|
||||
if (! file_exists($this->path)) {
|
||||
Log::warning('Failed to scan Composer: '.$this->path);
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
if (! is_readable($this->path)) {
|
||||
Log::warning('File not readable: '.$this->path);
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
$contents = file_get_contents($this->path);
|
||||
if ($contents === false) {
|
||||
Log::warning('Failed to read Composer: '.$this->path);
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
$json = json_decode($contents, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE || ! is_array($json)) {
|
||||
Log::warning('Failed to decode Composer: '.$this->path.'. '.json_last_error_msg());
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
if (! array_key_exists('packages', $json)) {
|
||||
Log::warning('Malformed composer.lock');
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
$this->directPackages = $this->direct();
|
||||
$packages = $json['packages'] ?? [];
|
||||
$devPackages = $json['packages-dev'] ?? [];
|
||||
|
||||
$this->processPackages($packages, $mappedItems, false);
|
||||
$this->processPackages($devPackages, $mappedItems, true);
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns direct dependencies as defined in composer.json
|
||||
*
|
||||
* @return array<string, array{constraint: string, isDev: bool}>
|
||||
* */
|
||||
protected function direct(): array
|
||||
{
|
||||
$packages = [];
|
||||
$filename = realpath(dirname($this->path)).DIRECTORY_SEPARATOR.'composer.json';
|
||||
if (file_exists($filename) === false || is_readable($filename) === false) {
|
||||
return $packages;
|
||||
}
|
||||
|
||||
$json = file_get_contents($filename);
|
||||
if ($json === false) {
|
||||
return $packages;
|
||||
}
|
||||
|
||||
$json = json_decode($json, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE || ! is_array($json)) {
|
||||
return $packages;
|
||||
}
|
||||
|
||||
$config = $json['config'] ?? [];
|
||||
$this->vendorDir = is_array($config) && isset($config['vendor-dir']) && is_string($config['vendor-dir'])
|
||||
? $config['vendor-dir']
|
||||
: 'vendor';
|
||||
|
||||
foreach ((array) ($json['require'] ?? []) as $name => $constraint) {
|
||||
$packages[$name] = [
|
||||
'constraint' => $constraint,
|
||||
'isDev' => false,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ((array) ($json['require-dev'] ?? []) as $name => $constraint) {
|
||||
$packages[$name] = [
|
||||
'constraint' => $constraint,
|
||||
'isDev' => true,
|
||||
];
|
||||
}
|
||||
|
||||
return $packages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process packages and add them to the mapped items collection
|
||||
*
|
||||
* @param array<int, array<string, string>> $packages
|
||||
* @param Collection<int, Package|Approach> $mappedItems
|
||||
* @return Collection<int, Package|Approach>
|
||||
*/
|
||||
private function processPackages(array $packages, Collection $mappedItems, bool $isDev): Collection
|
||||
{
|
||||
foreach ($packages as $package) {
|
||||
$packageName = $package['name'] ?? '';
|
||||
$version = $package['version'] ?? '';
|
||||
$mappedPackage = $this->map[$packageName] ?? null;
|
||||
$direct = false;
|
||||
$constraint = $version;
|
||||
|
||||
if (is_null($mappedPackage)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_key_exists($packageName, $this->directPackages) === true) {
|
||||
$direct = true;
|
||||
$constraint = $this->directPackages[$packageName]['constraint'];
|
||||
}
|
||||
|
||||
$niceVersion = preg_replace('/[^0-9.]/', '', $version) ?? '';
|
||||
$mappedItems->push(match (get_class($mappedPackage)) {
|
||||
Packages::class => (new Package($mappedPackage, $packageName, $niceVersion, $isDev))->setDirect($direct)->setConstraint($constraint)->setSource(PackageSource::COMPOSER)->setPath($this->computePath($packageName)),
|
||||
Approaches::class => new Approach($mappedPackage),
|
||||
default => throw new \InvalidArgumentException('Unsupported mapping')
|
||||
});
|
||||
}
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
private function computePath(string $packageName): string
|
||||
{
|
||||
$vendorPath = str_replace('/', DIRECTORY_SEPARATOR, $this->vendorDir);
|
||||
|
||||
if (DIRECTORY_SEPARATOR === '/' && str_starts_with($vendorPath, DIRECTORY_SEPARATOR)
|
||||
|| DIRECTORY_SEPARATOR === '\\' && preg_match('/^[A-Za-z]:[\\\\\\/]/', $vendorPath)) {
|
||||
return $vendorPath.DIRECTORY_SEPARATOR
|
||||
.str_replace('/', DIRECTORY_SEPARATOR, $packageName);
|
||||
}
|
||||
|
||||
return realpath(dirname($this->path)).DIRECTORY_SEPARATOR
|
||||
.$vendorPath.DIRECTORY_SEPARATOR
|
||||
.str_replace('/', DIRECTORY_SEPARATOR, $packageName);
|
||||
}
|
||||
}
|
||||
36
vendor/laravel/roster/src/Scanners/DirectoryStructure.php
vendored
Normal file
36
vendor/laravel/roster/src/Scanners/DirectoryStructure.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Scanners;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Roster\Approach;
|
||||
use Laravel\Roster\Enums\Approaches;
|
||||
|
||||
class DirectoryStructure
|
||||
{
|
||||
public function __construct(protected string $path) {}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection<int, \Laravel\Roster\Package|\Laravel\Roster\Approach>
|
||||
*/
|
||||
public function scan(): Collection
|
||||
{
|
||||
$items = collect();
|
||||
|
||||
$actions = $this->path.DIRECTORY_SEPARATOR.'/app/'.DIRECTORY_SEPARATOR.'Actions';
|
||||
if (is_dir($actions)) {
|
||||
$items->push(new Approach(Approaches::ACTION));
|
||||
}
|
||||
|
||||
$domains = $this->path.DIRECTORY_SEPARATOR.'/app/'.DIRECTORY_SEPARATOR.'Domains';
|
||||
if (is_dir($domains)) {
|
||||
$items->push(new Approach(Approaches::DDD));
|
||||
}
|
||||
|
||||
if (is_dir($this->path.DIRECTORY_SEPARATOR.'modules') || is_dir($this->path.DIRECTORY_SEPARATOR.'Modules') || is_dir($this->path.DIRECTORY_SEPARATOR.'app-modules')) {
|
||||
$items->push(new Approach(Approaches::MODULAR));
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
59
vendor/laravel/roster/src/Scanners/NpmPackageLock.php
vendored
Normal file
59
vendor/laravel/roster/src/Scanners/NpmPackageLock.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Scanners;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class NpmPackageLock extends BasePackageScanner
|
||||
{
|
||||
protected function lockFile(): string
|
||||
{
|
||||
return 'package-lock.json';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection<int, \Laravel\Roster\Package|\Laravel\Roster\Approach>
|
||||
*/
|
||||
public function scan(): Collection
|
||||
{
|
||||
$mappedItems = collect();
|
||||
$lockFilePath = $this->lockFilePath();
|
||||
|
||||
$contents = $this->validateFile($lockFilePath);
|
||||
if ($contents === null) {
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
$json = json_decode($contents, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE || ! is_array($json)) {
|
||||
Log::warning('Failed to decode Package: '.$lockFilePath.'. '.json_last_error_msg());
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
if (! array_key_exists('packages', $json)) {
|
||||
Log::warning('Malformed package-lock');
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
$dependencies = $json['packages']['']['dependencies'] ?? [];
|
||||
$devDependencies = $json['packages']['']['devDependencies'] ?? [];
|
||||
$packages = array_filter($json['packages'], fn ($key) => $key !== '', ARRAY_FILTER_USE_KEY);
|
||||
|
||||
$versionCb = function (string $packageName, string $version) use ($packages): string {
|
||||
$key = "node_modules/{$packageName}";
|
||||
if (array_key_exists($key, $packages)) {
|
||||
return $packages[$key]['version'];
|
||||
}
|
||||
|
||||
return $version;
|
||||
};
|
||||
|
||||
$this->processDependencies($dependencies, $mappedItems, false, $versionCb);
|
||||
$this->processDependencies($devDependencies, $mappedItems, true, $versionCb);
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
}
|
||||
41
vendor/laravel/roster/src/Scanners/PackageLock.php
vendored
Normal file
41
vendor/laravel/roster/src/Scanners/PackageLock.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Scanners;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Roster\Enums\NodePackageManager;
|
||||
|
||||
class PackageLock
|
||||
{
|
||||
/**
|
||||
* @param string $path - Base path to scan for lock files (package-lock.json, pnpm-lock.yaml, yarn.lock, ...)
|
||||
*/
|
||||
public function __construct(protected string $path) {}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection<int, \Laravel\Roster\Package|\Laravel\Roster\Approach>
|
||||
*/
|
||||
public function scan(): Collection
|
||||
{
|
||||
foreach (NodePackageManager::cases() as $case) {
|
||||
$scanner = $case->scanner($this->path);
|
||||
if ($scanner->canScan()) {
|
||||
return $scanner->scan();
|
||||
}
|
||||
}
|
||||
|
||||
return collect();
|
||||
}
|
||||
|
||||
public function detect(): NodePackageManager
|
||||
{
|
||||
foreach (NodePackageManager::cases() as $case) {
|
||||
$scanner = $case->scanner($this->path);
|
||||
if ($scanner->canScan()) {
|
||||
return $case;
|
||||
}
|
||||
}
|
||||
|
||||
return NodePackageManager::NPM;
|
||||
}
|
||||
}
|
||||
70
vendor/laravel/roster/src/Scanners/PnpmPackageLock.php
vendored
Normal file
70
vendor/laravel/roster/src/Scanners/PnpmPackageLock.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Scanners;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class PnpmPackageLock extends BasePackageScanner
|
||||
{
|
||||
protected function lockFile(): string
|
||||
{
|
||||
return 'pnpm-lock.yaml';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection<int, \Laravel\Roster\Package|\Laravel\Roster\Approach>
|
||||
*/
|
||||
public function scan(): Collection
|
||||
{
|
||||
$mappedItems = collect();
|
||||
$lockFilePath = $this->lockFilePath();
|
||||
|
||||
$contents = $this->validateFile($lockFilePath, 'PNPM lock');
|
||||
if ($contents === null) {
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var array<string, mixed> $parsed */
|
||||
$parsed = Yaml::parse($contents);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Failed to parse YAML: '.$e->getMessage());
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
/** @var array<string, string> $dependencies */
|
||||
$dependencies = [];
|
||||
/** @var array<string, string> $devDependencies */
|
||||
$devDependencies = [];
|
||||
|
||||
/** @var array<string, array<string, mixed>> $importers */
|
||||
$importers = $parsed['importers'] ?? [];
|
||||
$root = $importers['.'] ?? [];
|
||||
/** @var array<string, array<string, mixed>> $rootDependencies */
|
||||
$rootDependencies = $root['dependencies'] ?? [];
|
||||
/** @var array<string, array<string, mixed>> $rootDevDependencies */
|
||||
$rootDevDependencies = $root['devDependencies'] ?? [];
|
||||
|
||||
foreach ($rootDependencies as $name => $data) {
|
||||
if (isset($data['version'])) {
|
||||
$dependencies[$name] = $data['version'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rootDevDependencies as $name => $data) {
|
||||
if (isset($data['version'])) {
|
||||
$devDependencies[$name] = $data['version'];
|
||||
}
|
||||
}
|
||||
|
||||
/** @var array<string, string> $dependencies */
|
||||
/** @var array<string, string> $devDependencies */
|
||||
$this->processDependencies($dependencies, $mappedItems, false);
|
||||
$this->processDependencies($devDependencies, $mappedItems, true);
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
}
|
||||
95
vendor/laravel/roster/src/Scanners/YarnPackageLock.php
vendored
Normal file
95
vendor/laravel/roster/src/Scanners/YarnPackageLock.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Laravel\Roster\Scanners;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Roster\Approach;
|
||||
use Laravel\Roster\Package;
|
||||
|
||||
class YarnPackageLock extends BasePackageScanner
|
||||
{
|
||||
private const YARN_V1_HEADER = '/^("?)(@[^@"\/]+\/[^@"]+|[^@"]+)(@[^:"]+)?\1:$/';
|
||||
|
||||
private const YARN_V4_HEADER = '/^"(@?[^@"]+(?:\/[^@"]+)?)@npm:[^"]*":\s*$/';
|
||||
|
||||
private const YARN_V1_VERSION = '/^version\s+"([^"]+)"$/';
|
||||
|
||||
private const YARN_V4_VERSION = '/^version:\s+(.+)$/';
|
||||
|
||||
protected function lockFile(): string
|
||||
{
|
||||
return 'yarn.lock';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, Package|Approach>
|
||||
*/
|
||||
public function scan(): Collection
|
||||
{
|
||||
$mappedItems = collect();
|
||||
$lockFilePath = $this->lockFilePath();
|
||||
|
||||
$contents = $this->validateFile($lockFilePath, 'Yarn lock');
|
||||
if ($contents === null) {
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
$dependencies = [];
|
||||
$lines = explode("\n", $contents);
|
||||
$currentPackage = null;
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
|
||||
if ($line === '' || str_starts_with($line, '#')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$packageName = $this->parsePackageHeader($line);
|
||||
|
||||
if ($packageName !== null) {
|
||||
$currentPackage = $packageName;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$version = $this->parseVersion($line);
|
||||
|
||||
if ($currentPackage !== null && $version !== null) {
|
||||
$dependencies[$currentPackage] = $version;
|
||||
$currentPackage = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Yarn lock does not distinguish devDependencies :/
|
||||
$this->processDependencies($dependencies, $mappedItems, false);
|
||||
|
||||
return $mappedItems;
|
||||
}
|
||||
|
||||
private function parsePackageHeader(string $line): ?string
|
||||
{
|
||||
if (preg_match(self::YARN_V1_HEADER, $line, $matches)) {
|
||||
return $matches[2];
|
||||
}
|
||||
|
||||
if (preg_match(self::YARN_V4_HEADER, $line, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function parseVersion(string $line): ?string
|
||||
{
|
||||
if (preg_match(self::YARN_V1_VERSION, $line, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
if (preg_match(self::YARN_V4_VERSION, $line, $matches)) {
|
||||
return trim($matches[1]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user