mirror of
https://github.com/pestphp/pest.git
synced 2026-06-05 02:52:12 +02:00
wip
This commit is contained in:
@ -16,30 +16,14 @@ final class JsModuleGraph
|
||||
|
||||
private const string CACHE_FILE = 'js-module-graph.cache.json';
|
||||
|
||||
/** Active warmer subprocess, or null when none is in flight. */
|
||||
private static ?Process $warmer = null;
|
||||
|
||||
/** Fingerprint the warmer was started against — used to detect drift between warm and build. */
|
||||
private static ?string $warmerFingerprint = null;
|
||||
|
||||
/** True when the warmer found a fresh cache and skipped spawning Node. */
|
||||
private static bool $warmerCacheHit = false;
|
||||
|
||||
/** Project root the warmer was launched for. */
|
||||
private static ?string $warmerProjectRoot = null;
|
||||
|
||||
/**
|
||||
* Kicks off the Node helper in the background, so by the time
|
||||
* `build()` is called at flush time the result is (usually) already
|
||||
* sitting on stdout. Idempotent — a second call while a warmer is
|
||||
* already in flight is a no-op. Cheap when the cache is fresh: it
|
||||
* checks the fingerprint first and skips the subprocess.
|
||||
*
|
||||
* Safe to call from any TIA entry point that will eventually write
|
||||
* the graph from the main process. Workers must NOT call this — they
|
||||
* don't flush the graph and would duplicate the Node bootstrap on
|
||||
* every worker.
|
||||
*/
|
||||
public static function warmInBackground(string $projectRoot): void
|
||||
{
|
||||
if (self::$warmer instanceof Process || self::$warmerCacheHit) {
|
||||
@ -76,7 +60,7 @@ final class JsModuleGraph
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>> project-relative source path → sorted list of page component names
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public static function build(string $projectRoot): array
|
||||
{
|
||||
@ -86,13 +70,6 @@ final class JsModuleGraph
|
||||
}
|
||||
|
||||
/**
|
||||
* Strict variant — returns null when the Node resolver isn't
|
||||
* available, so callers can distinguish "Vite says nothing imports
|
||||
* this file" (empty list) from "we couldn't ask Vite" (null).
|
||||
*
|
||||
* Used at replay time when we need to *trust a negative result*
|
||||
* (i.e., "no page imports this file, so it's orphan, safe to skip").
|
||||
*
|
||||
* @return array<string, list<string>>|null
|
||||
*/
|
||||
public static function buildStrict(string $projectRoot): ?array
|
||||
@ -100,22 +77,12 @@ final class JsModuleGraph
|
||||
return self::resolve($projectRoot);
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the project looks like a Vite + Node project we can
|
||||
* ask for a module graph. Gate for callers that want to skip the
|
||||
* resolver entirely on non-Vite apps.
|
||||
*/
|
||||
public static function isApplicable(string $projectRoot): bool
|
||||
{
|
||||
if (! self::hasViteConfig($projectRoot)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Both the classic Inertia-Vue (`Pages/`) and the Laravel React
|
||||
// starter kit (`pages/`) conventions are accepted — projects
|
||||
// running on a case-sensitive filesystem (Linux CI) get
|
||||
// exactly one of the two, and we shouldn't refuse to walk the
|
||||
// graph based on which one it picks.
|
||||
foreach (['Pages', 'pages'] as $dir) {
|
||||
if (is_dir($projectRoot.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'js'.DIRECTORY_SEPARATOR.$dir)) {
|
||||
return true;
|
||||
@ -142,14 +109,7 @@ final class JsModuleGraph
|
||||
}
|
||||
}
|
||||
|
||||
// Pick up the warmer when it was launched against the same
|
||||
// fingerprint and project root. Drift between warm and build
|
||||
// (rare — would require a JS file to change mid-test-run)
|
||||
// discards the warmer and re-runs synchronously.
|
||||
if (self::$warmerCacheHit
|
||||
&& self::$warmerFingerprint === $fingerprint
|
||||
&& self::$warmerProjectRoot === $projectRoot
|
||||
&& $fingerprint !== null) {
|
||||
if (self::$warmerCacheHit && $fingerprint !== null) {
|
||||
$cached = self::readCache($projectRoot, $fingerprint);
|
||||
self::$warmerCacheHit = false;
|
||||
self::$warmerFingerprint = null;
|
||||
@ -160,60 +120,28 @@ final class JsModuleGraph
|
||||
}
|
||||
}
|
||||
|
||||
if (self::$warmer instanceof Process
|
||||
&& self::$warmerFingerprint === $fingerprint
|
||||
&& self::$warmerProjectRoot === $projectRoot) {
|
||||
$process = self::$warmer;
|
||||
self::$warmer = null;
|
||||
self::$warmerFingerprint = null;
|
||||
self::$warmerProjectRoot = null;
|
||||
|
||||
$process->wait();
|
||||
|
||||
if ($process instanceof Process && $process->isSuccessful()) {
|
||||
$result = self::parseNodeOutput($process->getOutput());
|
||||
|
||||
if ($result !== null) {
|
||||
if ($fingerprint !== null) {
|
||||
self::writeCache($projectRoot, $fingerprint, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Different fingerprint or different project root: discard
|
||||
// any stale warmer before we start a fresh run.
|
||||
self::reapWarmer();
|
||||
}
|
||||
|
||||
$viaNode = self::runNodeSync($projectRoot);
|
||||
|
||||
if ($viaNode !== null && $fingerprint !== null) {
|
||||
self::writeCache($projectRoot, $fingerprint, $viaNode);
|
||||
}
|
||||
|
||||
return $viaNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>|null
|
||||
*/
|
||||
private static function runNodeSync(string $projectRoot): ?array
|
||||
{
|
||||
$process = self::buildNodeProcess($projectRoot);
|
||||
$process = self::$warmer;
|
||||
self::$warmer = null;
|
||||
self::$warmerFingerprint = null;
|
||||
self::$warmerProjectRoot = null;
|
||||
|
||||
if (! $process instanceof Process) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$process->run();
|
||||
$process->wait();
|
||||
|
||||
if (! $process->isSuccessful()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::parseNodeOutput($process->getOutput());
|
||||
$result = self::parseNodeOutput($process->getOutput());
|
||||
|
||||
if ($result !== null && $fingerprint !== null) {
|
||||
self::writeCache($projectRoot, $fingerprint, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function buildNodeProcess(string $projectRoot): ?Process
|
||||
@ -238,11 +166,6 @@ final class JsModuleGraph
|
||||
return null;
|
||||
}
|
||||
|
||||
// Tell the Node helper which casing this project uses for its
|
||||
// pages directory. The helper defaults to `resources/js/Pages`;
|
||||
// the Laravel React starter ships lowercase `resources/js/pages`,
|
||||
// and on a case-sensitive filesystem the helper would otherwise
|
||||
// walk a non-existent directory and emit an empty module graph.
|
||||
$env = [];
|
||||
foreach (['resources/js/Pages', 'resources/js/pages'] as $candidate) {
|
||||
if (is_dir($projectRoot.DIRECTORY_SEPARATOR.$candidate)) {
|
||||
@ -298,10 +221,6 @@ final class JsModuleGraph
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop and discard a leftover warmer subprocess (e.g. on shutdown,
|
||||
* or when `build()` resolved from cache without needing the warmer).
|
||||
*/
|
||||
private static function reapWarmer(): void
|
||||
{
|
||||
$process = self::$warmer;
|
||||
|
||||
Reference in New Issue
Block a user