From 21efbc310726ef51bfde3b9f71cddbca5399ad06 Mon Sep 17 00:00:00 2001 From: nuno maduro Date: Fri, 1 May 2026 22:55:38 +0100 Subject: [PATCH] wip --- bin/pest-tia-vite-deps.mjs | 87 ++++++++++++++++++++----------- composer.json | 1 + src/Plugins/Tia/JsModuleGraph.php | 3 +- src/Support/Cpu.php | 18 +++++++ 4 files changed, 79 insertions(+), 30 deletions(-) create mode 100644 src/Support/Cpu.php diff --git a/bin/pest-tia-vite-deps.mjs b/bin/pest-tia-vite-deps.mjs index 93a27e81..4e8805ff 100644 --- a/bin/pest-tia-vite-deps.mjs +++ b/bin/pest-tia-vite-deps.mjs @@ -10,6 +10,7 @@ const PAGE_EXTENSIONS = new Set(['.vue', '.tsx', '.jsx', '.svelte']) const PROJECT_ROOT = resolve(process.argv[2] ?? process.cwd()) 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 CONCURRENCY = Math.max(1, Number.parseInt(process.env.TIA_VITE_CONCURRENCY ?? '16', 10)) async function loadVite() { const projectRequire = createRequire(join(PROJECT_ROOT, 'package.json')) @@ -77,42 +78,70 @@ async function main() { 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 { - for (const pagePath of pages) { - const pageComponent = pageComponentCache.get(pagePath) - const pageUrl = '/' + posix.relative( - PROJECT_ROOT.split(sep).join('/'), - pagePath.split(sep).join('/'), - ) - - try { - await server.transformRequest(pageUrl, { ssr: false }) - } catch { - continue + let cursor = 0 + const workers = Array.from({ length: Math.min(CONCURRENCY, pageEntries.length) }, async () => { + while (true) { + const i = cursor++ + if (i >= pageEntries.length) return + const { pageUrl } = pageEntries[i] + try { + await server.transformRequest(pageUrl, { ssr: false }) + } catch { + // ignore, handled below when we look up the module + } } + }) + 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) if (!pageModule) continue - const visited = new Set() - const queue = [pageModule] - 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) + const reachable = computeTransitive(pageModule, new Set()) + if (!reachable) continue - if (id.startsWith('\0')) continue - if (!id.startsWith(PROJECT_ROOT)) continue - - const rel = relative(PROJECT_ROOT, id).split(sep).join('/') - const bucket = reverse.get(rel) ?? new Set() - bucket.add(pageComponent) - reverse.set(rel, bucket) - - queue.push(imported) - } + for (const rel of reachable) { + const bucket = reverse.get(rel) ?? new Set() + bucket.add(pageComponent) + reverse.set(rel, bucket) } } } finally { diff --git a/composer.json b/composer.json index 842ecd19..b79f1371 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "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", diff --git a/src/Plugins/Tia/JsModuleGraph.php b/src/Plugins/Tia/JsModuleGraph.php index 05492d48..f97e980c 100644 --- a/src/Plugins/Tia/JsModuleGraph.php +++ b/src/Plugins/Tia/JsModuleGraph.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Pest\Plugins\Tia; +use Pest\Support\Cpu; use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\Process; @@ -166,7 +167,7 @@ final class JsModuleGraph return null; } - $env = []; + $env = ['TIA_VITE_CONCURRENCY' => (string) max(4, min(32, Cpu::cores() * 2))]; foreach (['resources/js/Pages', 'resources/js/pages'] as $candidate) { if (is_dir($projectRoot.DIRECTORY_SEPARATOR.$candidate)) { $env['TIA_VITE_PAGES_DIR'] = $candidate; diff --git a/src/Support/Cpu.php b/src/Support/Cpu.php new file mode 100644 index 00000000..9ff5fda1 --- /dev/null +++ b/src/Support/Cpu.php @@ -0,0 +1,18 @@ +getCountWithFallback($fallback); + } +}