This commit is contained in:
nuno maduro
2026-05-02 17:11:49 +01:00
parent 1c7c9754fd
commit f247dd8e7b
4 changed files with 0 additions and 264 deletions

View File

@ -20,7 +20,6 @@
"php": "^8.3.0",
"brianium/paratest": "^7.20.0",
"composer/xdebug-handler": "^3.0.5",
"fidry/cpu-core-counter": "^1.3",
"nunomaduro/collision": "^8.9.4",
"nunomaduro/termwind": "^2.4.0",
"pestphp/pest-plugin": "^4.0.0",

View File

@ -1,234 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Plugins\Tia;
/**
* @internal
*/
final class JsImportParser
{
private const array PAGE_EXTENSIONS = ['vue', 'tsx', 'jsx', 'svelte'];
private const array RESOLVABLE_EXTENSIONS = ['vue', 'tsx', 'jsx', 'svelte', 'ts', 'js', 'mjs', 'mts'];
private const string JS_DIR = 'resources/js';
/**
* @return array<string, list<string>>
*/
public static function parse(string $projectRoot): array
{
$jsRoot = $projectRoot.DIRECTORY_SEPARATOR.self::JS_DIR;
$pagesRoot = null;
foreach (['resources/js/Pages', 'resources/js/pages'] as $candidate) {
$abs = $projectRoot.DIRECTORY_SEPARATOR.$candidate;
if (is_dir($abs)) {
$pagesRoot = $abs;
break;
}
}
if ($pagesRoot === null) {
return [];
}
$reverse = [];
foreach (self::collectPages($pagesRoot) as $pageAbs) {
$component = self::componentName($pagesRoot, $pageAbs);
if ($component === null) {
continue;
}
$visited = [];
self::collectTransitive($pageAbs, $projectRoot, $jsRoot, $visited);
foreach (array_keys($visited) as $depAbs) {
if ($depAbs === $pageAbs) {
continue;
}
$rel = str_replace(DIRECTORY_SEPARATOR, '/', substr($depAbs, strlen($projectRoot) + 1));
$reverse[$rel][$component] = true;
}
}
$out = [];
foreach ($reverse as $path => $components) {
$names = array_keys($components);
sort($names);
$out[$path] = $names;
}
ksort($out);
return $out;
}
/**
* @return list<string>
*/
private static function collectPages(string $pagesRoot): array
{
$out = [];
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($pagesRoot, \FilesystemIterator::SKIP_DOTS),
);
foreach ($iterator as $fileInfo) {
if (! $fileInfo->isFile()) {
continue;
}
$ext = strtolower((string) $fileInfo->getExtension());
if (in_array($ext, self::PAGE_EXTENSIONS, true)) {
$out[] = $fileInfo->getPathname();
}
}
return $out;
}
private static function componentName(string $pagesRoot, string $pageAbs): ?string
{
$rel = str_replace(DIRECTORY_SEPARATOR, '/', substr($pageAbs, strlen($pagesRoot) + 1));
$dot = strrpos($rel, '.');
if ($dot === false) {
return null;
}
$name = substr($rel, 0, $dot);
return $name === '' ? null : $name;
}
/**
* @param array<string, true> $visited
*/
private static function collectTransitive(string $fileAbs, string $projectRoot, string $jsRoot, array &$visited): void
{
if (isset($visited[$fileAbs])) {
return;
}
$visited[$fileAbs] = true;
$source = self::loadSource($fileAbs);
if ($source === null) {
return;
}
foreach (self::extractImports($source) as $spec) {
$resolved = self::resolveImport($spec, $fileAbs, $jsRoot);
if ($resolved === null) {
continue;
}
if (! is_file($resolved)) {
continue;
}
self::collectTransitive($resolved, $projectRoot, $jsRoot, $visited);
}
}
private static function loadSource(string $fileAbs): ?string
{
$content = @file_get_contents($fileAbs);
if ($content === false) {
return null;
}
if (str_ends_with(strtolower($fileAbs), '.vue')) {
$scripts = [];
if (preg_match_all('/<script[^>]*>(.*?)<\/script>/si', $content, $m) !== false) {
foreach ($m[1] as $block) {
$scripts[] = $block;
}
}
return implode("\n", $scripts);
}
return $content;
}
/**
* @return list<string>
*/
private static function extractImports(string $source): array
{
$stripped = preg_replace('#//[^\n]*#', '', $source) ?? $source;
$stripped = preg_replace('#/\*.*?\*/#s', '', $stripped) ?? $stripped;
$specs = [];
if (preg_match_all('/\bimport\s+(?:[^\'"()]*?\s+from\s+)?[\'"]([^\'"]+)[\'"]/', $stripped, $matches) !== false) {
foreach ($matches[1] as $spec) {
$specs[] = $spec;
}
}
if (preg_match_all('/\bimport\(\s*[\'"]([^\'"]+)[\'"]\s*\)/', $stripped, $matches) !== false) {
foreach ($matches[1] as $spec) {
$specs[] = $spec;
}
}
return $specs;
}
private static function resolveImport(string $spec, string $importerAbs, string $jsRoot): ?string
{
if ($spec === '' || $spec[0] === '.' || $spec[0] === '/') {
return self::resolveRelative($spec, $importerAbs);
}
if (str_starts_with($spec, '@/') || str_starts_with($spec, '~/')) {
$tail = substr($spec, 2);
return self::withExtension($jsRoot.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $tail));
}
return null;
}
private static function resolveRelative(string $spec, string $importerAbs): ?string
{
if ($spec === '' || $spec[0] === '/') {
return null;
}
$base = dirname($importerAbs);
$path = $base.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $spec);
return self::withExtension($path);
}
private static function withExtension(string $path): ?string
{
if (is_file($path)) {
return realpath($path) ?: $path;
}
foreach (self::RESOLVABLE_EXTENSIONS as $ext) {
$candidate = $path.'.'.$ext;
if (is_file($candidate)) {
return realpath($candidate) ?: $candidate;
}
}
foreach (self::RESOLVABLE_EXTENSIONS as $ext) {
$candidate = $path.DIRECTORY_SEPARATOR.'index.'.$ext;
if (is_file($candidate)) {
return realpath($candidate) ?: $candidate;
}
}
return null;
}
}

View File

@ -118,14 +118,6 @@ final readonly class SourceScope
return false;
}
/**
* @return list<string>
*/
public function includes(): array
{
return $this->includes;
}
/**
* @return list<string>
*/

View File

@ -1,21 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Support;
use Fidry\CpuCoreCounter\CpuCoreCounter;
/**
* @internal
*/
final class Cpu
{
/**
* @param int<1, max> $fallback
*/
public static function cores(int $fallback = 4): int
{
return (new CpuCoreCounter)->getCountWithFallback($fallback);
}
}