This commit is contained in:
nuno maduro
2026-05-01 22:55:38 +01:00
parent e59b99cd73
commit 21efbc3107
4 changed files with 79 additions and 30 deletions

View File

@ -10,6 +10,7 @@ const PAGE_EXTENSIONS = new Set(['.vue', '.tsx', '.jsx', '.svelte'])
const PROJECT_ROOT = resolve(process.argv[2] ?? process.cwd()) const PROJECT_ROOT = resolve(process.argv[2] ?? process.cwd())
const PAGES_REL = (process.env.TIA_VITE_PAGES_DIR ?? 'resources/js/Pages').replace(/\\/g, '/') const PAGES_REL = (process.env.TIA_VITE_PAGES_DIR ?? 'resources/js/Pages').replace(/\\/g, '/')
const TIMEOUT_MS = Number.parseInt(process.env.TIA_VITE_TIMEOUT_MS ?? '20000', 10) const TIMEOUT_MS = Number.parseInt(process.env.TIA_VITE_TIMEOUT_MS ?? '20000', 10)
const CONCURRENCY = Math.max(1, Number.parseInt(process.env.TIA_VITE_CONCURRENCY ?? '16', 10))
async function loadVite() { async function loadVite() {
const projectRequire = createRequire(join(PROJECT_ROOT, 'package.json')) const projectRequire = createRequire(join(PROJECT_ROOT, 'package.json'))
@ -77,42 +78,70 @@ async function main() {
pageComponentCache.set(page, componentNameFor(page, pagesDir)) pageComponentCache.set(page, componentNameFor(page, pagesDir))
} }
const projectRootPosix = PROJECT_ROOT.split(sep).join('/')
const pageEntries = pages.map((pagePath) => ({
pagePath,
pageComponent: pageComponentCache.get(pagePath),
pageUrl: '/' + posix.relative(projectRootPosix, pagePath.split(sep).join('/')),
}))
try { try {
for (const pagePath of pages) { let cursor = 0
const pageComponent = pageComponentCache.get(pagePath) const workers = Array.from({ length: Math.min(CONCURRENCY, pageEntries.length) }, async () => {
const pageUrl = '/' + posix.relative( while (true) {
PROJECT_ROOT.split(sep).join('/'), const i = cursor++
pagePath.split(sep).join('/'), if (i >= pageEntries.length) return
) const { pageUrl } = pageEntries[i]
try {
try { await server.transformRequest(pageUrl, { ssr: false })
await server.transformRequest(pageUrl, { ssr: false }) } catch {
} catch { // ignore, handled below when we look up the module
continue }
} }
})
await Promise.all(workers)
const transitiveCache = new Map()
const computeTransitive = (mod, stack) => {
const key = mod.file ?? mod.id
if (!key) return null
const cached = transitiveCache.get(key)
if (cached) return cached
if (stack.has(key)) return null // cycle: let the originating frame fold us in
stack.add(key)
const acc = new Set()
for (const imported of mod.importedModules) {
const id = imported.file ?? imported.id
if (!id) continue
if (id.startsWith('\0')) continue
if (id.startsWith(PROJECT_ROOT)) {
const rel = relative(PROJECT_ROOT, id).split(sep).join('/')
acc.add(rel)
}
const childKey = id
if (stack.has(childKey)) continue
const child = computeTransitive(imported, stack)
if (child) for (const r of child) acc.add(r)
}
stack.delete(key)
transitiveCache.set(key, acc)
return acc
}
for (const { pageComponent, pageUrl } of pageEntries) {
const pageModule = await server.moduleGraph.getModuleByUrl(pageUrl, false) const pageModule = await server.moduleGraph.getModuleByUrl(pageUrl, false)
if (!pageModule) continue if (!pageModule) continue
const visited = new Set() const reachable = computeTransitive(pageModule, new Set())
const queue = [pageModule] if (!reachable) continue
while (queue.length) {
const mod = queue.shift()
for (const imported of mod.importedModules) {
const id = imported.file ?? imported.id
if (!id || visited.has(id)) continue
visited.add(id)
if (id.startsWith('\0')) continue for (const rel of reachable) {
if (!id.startsWith(PROJECT_ROOT)) continue const bucket = reverse.get(rel) ?? new Set()
bucket.add(pageComponent)
const rel = relative(PROJECT_ROOT, id).split(sep).join('/') reverse.set(rel, bucket)
const bucket = reverse.get(rel) ?? new Set()
bucket.add(pageComponent)
reverse.set(rel, bucket)
queue.push(imported)
}
} }
} }
} finally { } finally {

View File

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

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Pest\Plugins\Tia; namespace Pest\Plugins\Tia;
use Pest\Support\Cpu;
use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process; use Symfony\Component\Process\Process;
@ -166,7 +167,7 @@ final class JsModuleGraph
return null; return null;
} }
$env = []; $env = ['TIA_VITE_CONCURRENCY' => (string) max(4, min(32, Cpu::cores() * 2))];
foreach (['resources/js/Pages', 'resources/js/pages'] as $candidate) { foreach (['resources/js/Pages', 'resources/js/pages'] as $candidate) {
if (is_dir($projectRoot.DIRECTORY_SEPARATOR.$candidate)) { if (is_dir($projectRoot.DIRECTORY_SEPARATOR.$candidate)) {
$env['TIA_VITE_PAGES_DIR'] = $candidate; $env['TIA_VITE_PAGES_DIR'] = $candidate;

18
src/Support/Cpu.php Normal file
View File

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