Skip to content
17 changes: 17 additions & 0 deletions src/Detection/Framework.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,21 @@ abstract public function getInstallCommand(): string;
abstract public function getBuildCommand(): string;

abstract public function getOutputDirectory(): string;

/**
* @return array<string> Config files to read for adapter detection, in priority order.
*/
public function getConfigFiles(): array
{
return [];
}

/**
* Detect the adapter ('ssr' or 'static') from config file content.
* Returns empty string if detection is not possible.
*/
public function getAdapter(string $configContent): string
{
return '';
}
}
19 changes: 19 additions & 0 deletions src/Detection/Framework/Astro.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,23 @@ public function getOutputDirectory(): string
{
return './dist';
}

/**
* @return array<string>
*/
public function getConfigFiles(): array
{
return ['astro.config.mjs', 'astro.config.js', 'astro.config.ts'];
}

public function getAdapter(string $configContent): string
{
$stripped = \preg_replace('/(?<!:)\/\/[^\n]*/', '', $configContent) ?? $configContent;

if (\preg_match('/\boutput\s*:\s*[\x27\x22\x60](?:server|hybrid)[\x27\x22\x60]/', $stripped)) {
return 'ssr';
}

return 'static';
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
}
13 changes: 13 additions & 0 deletions src/Detection/Framework/Remix.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,17 @@ public function getOutputDirectory(): string
{
return './build';
}

/**
* @return array<string>
*/
public function getConfigFiles(): array
{
return ['package.json'];
}

public function getAdapter(string $configContent): string
{
return 'ssr';
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
}
15 changes: 15 additions & 0 deletions src/Detection/Framework/SvelteKit.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,19 @@ public function getOutputDirectory(): string
{
return './build';
}

/**
* @return array<string>
*/
public function getConfigFiles(): array
{
return ['svelte.config.js', 'svelte.config.mjs', 'svelte.config.ts', 'package.json'];
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Comment thread
greptile-apps[bot] marked this conversation as resolved.

public function getAdapter(string $configContent): string
{
$stripped = \preg_replace('/(?<!:)\/\/[^\n]*/', '', $configContent) ?? $configContent;

return \str_contains($stripped, '@sveltejs/adapter-static') ? 'static' : 'ssr';
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
}
19 changes: 19 additions & 0 deletions src/Detection/Framework/TanStackStart.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,23 @@ public function getOutputDirectory(): string
{
return './.output';
}

/**
* @return array<string>
*/
public function getConfigFiles(): array
{
return ['vite.config.ts', 'vite.config.js', 'vite.config.mjs'];
}

public function getAdapter(string $configContent): string
{
$stripped = \preg_replace('/(?<!:)\/\/[^\n]*/', '', $configContent) ?? $configContent;

if (!\preg_match('/\bprerender\b/', $stripped) || \preg_match('/\bprerender[\x27\x22]?\s*:\s*false\b/', $stripped)) {
return 'ssr';
}

return 'static';
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
}
52 changes: 52 additions & 0 deletions tests/unit/DetectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -798,4 +798,56 @@ public function testFrameworkEdgeCases(string $assertion, array $files, string $

$this->assertSame($framework, $detection->getName(), $assertion);
}

public function testTanStackStartAdapterDetection(): void
{
$fw = new TanStackStart();

$this->assertSame('ssr', $fw->getAdapter('export default defineConfig({ plugins: [tanstackStart()] })'));
$this->assertSame('static', $fw->getAdapter('export default defineConfig({ plugins: [tanstackStart({ prerender: { routes: [\'/\'] } })] })'));
$this->assertSame('ssr', $fw->getAdapter('export default defineConfig({ plugins: [tanstackStart({ prerender: false })] })'));
$this->assertSame('ssr', $fw->getAdapter('export default defineConfig({ plugins: [tanstackStart({ "prerender": false })] })'));
$this->assertSame('ssr', $fw->getAdapter('// prerender: true' . "\n" . 'export default defineConfig({})'));
$this->assertSame('static', $fw->getAdapter('server: { url: "https://example.com" },' . "\n" . 'prerender: { routes: [\'/\'] }'));
$this->assertNotEmpty($fw->getConfigFiles());
}

public function testSvelteKitAdapterDetection(): void
{
$fw = new SvelteKit();

$this->assertSame('ssr', $fw->getAdapter('import adapter from \'@sveltejs/adapter-auto\'; export default { kit: { adapter: adapter() } }'));
$this->assertSame('static', $fw->getAdapter('import adapter from \'@sveltejs/adapter-static\'; export default { kit: { adapter: adapter() } }'));
$this->assertSame('static', $fw->getAdapter('{"dependencies":{"@sveltejs/adapter-static":"^3.0.0"}}'));
$this->assertSame('ssr', $fw->getAdapter('// import adapter from \'@sveltejs/adapter-static\'' . "\n" . 'import adapter from \'@sveltejs/adapter-auto\''));
$this->assertContains('package.json', $fw->getConfigFiles());
$this->assertNotEmpty($fw->getConfigFiles());
}

public function testAstroAdapterDetection(): void
{
$fw = new Astro();

$this->assertSame('static', $fw->getAdapter('export default defineConfig({ integrations: [] })'));
$this->assertSame('ssr', $fw->getAdapter('export default defineConfig({ output: \'server\', adapter: node({ mode: \'standalone\' }) })'));
$this->assertSame('ssr', $fw->getAdapter('export default defineConfig({ output: "server" })'));
$this->assertSame('ssr', $fw->getAdapter('export default defineConfig({ output: \'hybrid\' })'));
$this->assertSame('ssr', $fw->getAdapter('export default defineConfig({ output : \'server\' })'));
$this->assertSame('static', $fw->getAdapter('// output: \'server\'' . "\n" . 'export default defineConfig({})'));
$this->assertSame('ssr', $fw->getAdapter('site: "https://example.com",' . "\n" . 'output: "server"'));
$this->assertSame('ssr', $fw->getAdapter('export default defineConfig({ output: `server` })'));
$this->assertSame('ssr', $fw->getAdapter('export default defineConfig({ output: `hybrid` })'));
$this->assertNotEmpty($fw->getConfigFiles());
}

public function testRemixAdapterDetection(): void
{
$fw = new Remix();

$this->assertSame('ssr', $fw->getAdapter('{"dependencies":{"@remix-run/react":"^2.0.0"}}'));
$this->assertSame('ssr', $fw->getAdapter('{"dependencies":{"@remix-run/serve":"^2.0.0"}}'));
$this->assertSame('ssr', $fw->getAdapter('{"dependencies":{"@remix-run/node":"^2.0.0"}}'));
$this->assertSame('ssr', $fw->getAdapter(''));
$this->assertContains('package.json', $fw->getConfigFiles());
}
}
Loading