Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: yaml

- name: Validate composer.json and composer.lock
run: composer validate --strict
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Install using composer:
composer require utopia-php/detector
```

Requires the `yaml` extension — see [System Requirements](#system-requirements).

Init in your application:
```php
<?php
Expand Down Expand Up @@ -125,6 +127,7 @@ Framework Adapters:
|---------|---------|
| Astro | ✅ |
| Flutter | ✅ |
| Jaspr | ✅ |
| NextJs | ✅ |
| Nuxt | ✅ |
| Remix | ✅ |
Expand Down Expand Up @@ -152,7 +155,13 @@ Rendering Adapters:

## System Requirements

Utopia Detector requires PHP 8.0 or later. We recommend using the latest PHP version whenever possible.
Utopia Detector requires PHP 8.0 or later with the `json` and `yaml` extensions. We recommend using the latest PHP version whenever possible.

`json` ships with PHP. `yaml` is a PECL extension, used to read `pubspec.yaml` manifests:

```bash
pecl install yaml
```


## Contributing
Expand Down
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
}
},
"require": {
"php": ">=8.0"
"php": ">=8.0",
"ext-json": "*",
"ext-yaml": "*"
Comment thread
Meldiron marked this conversation as resolved.
},
"require-dev": {
"phpunit/phpunit": "^9.4",
Expand Down
6 changes: 4 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions src/Detection/Framework/Flutter.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ public function getFiles(): array
return ['pubspec.yaml', 'pubspec.lock'];
}

/**
* @return array<string>
*/
public function getPackages(): array
{
return ['flutter'];
}

public function getInstallCommand(): string
{
return '';
Expand Down
67 changes: 67 additions & 0 deletions src/Detection/Framework/Jaspr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Utopia\Detector\Detection\Framework;

/**
* Not a Flutter derivative, but detected from the same pubspec files. Extending
* Flutter makes Jaspr the more specific match, so it wins once a jaspr package
* shows up and loses an ambiguous pubspec whatever order the options were added in.
*/
class Jaspr extends Flutter
{
public function getName(): string
{
return 'jaspr';
Comment thread
greptile-apps[bot] marked this conversation as resolved.
}

/**
* @return array<string>
*/
public function getPackages(): array
{
return \array_merge(['jaspr', 'jaspr_builder'], parent::getPackages());
}

public function getInstallCommand(): string
{
return 'dart pub get';
}

public function getBuildCommand(): string
{
return 'jaspr build';
}

public function getOutputDirectory(): string
{
return './build/jaspr';
}

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

/**
* Reads `jaspr.mode`, which builds a server executable in `server` mode
* and web assets in `static` and `client` mode.
*/
public function getAdapter(string $configContent): string
{
$pubspec = @\yaml_parse($configContent);
$config = \is_array($pubspec) ? ($pubspec['jaspr'] ?? null) : null;

if (! \is_array($config)) {
return '';
}

return match ($config['mode'] ?? null) {
'server' => 'ssr',
'static', 'client' => 'static',
default => '',
};
Comment thread
Meldiron marked this conversation as resolved.
}
}
1 change: 1 addition & 0 deletions src/Detection/Rendering/SSR.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class SSR extends Rendering
'analog' => ['server/index.mjs'],
'tanstack-start' => ['server/server.js', 'server/index.mjs'],
'flutter' => [],
'jaspr' => ['app'],
'lynx' => [],
];

Expand Down
45 changes: 42 additions & 3 deletions src/Detector/Framework.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ class Framework extends Detector
public const INPUT_FILE = 'file';
public const INPUT_PACKAGES = 'packages';

private const DEPENDENCY_SECTIONS = [
'dependencies',
'devDependencies',
'peerDependencies',
'optionalDependencies',
'dev_dependencies',
'dependency_overrides',
];

/**
* @var array<FrameworkDetection>
*/
Expand Down Expand Up @@ -46,7 +55,7 @@ public function detect(): ?FrameworkDetection
$files = array_map(fn ($input) => $input['content'], $files);

$packages = array_filter($this->inputs, fn ($input) => $input['type'] === self::INPUT_PACKAGES);
$packages = array_map(fn ($input) => $input['content'], $packages);
$packages = array_map(fn ($input) => $this->dependencies($input['content']), $packages);

// List of frameworks with count of matches
$frameworkMatches = [];
Expand All @@ -63,9 +72,9 @@ public function detect(): ?FrameworkDetection

foreach ($this->options as $detector) {
// Check package-based detection
foreach ($packages as $packageJson) {
foreach ($packages as $dependencies) {
foreach ($detector->getPackages() as $packageNeeded) {
if (str_contains($packageJson, '"'.$packageNeeded.'"')) {
if (\in_array($packageNeeded, $dependencies, true)) {
$frameworkMatches[$detector->getName()] += 1;
}
}
Expand Down Expand Up @@ -125,4 +134,34 @@ public function detect(): ?FrameworkDetection

return null;
}

/**
* Reads declared dependency names out of a `package.json` or a `pubspec.yaml`.
*
* @return array<string>
*/
protected function dependencies(string $manifest): array
{
$parsed = \json_decode($manifest, true);

if (! \is_array($parsed)) {
$parsed = @\yaml_parse($manifest);
}

if (! \is_array($parsed)) {
return [];
}

$dependencies = [];

foreach (self::DEPENDENCY_SECTIONS as $section) {
$packages = $parsed[$section] ?? null;

if (\is_array($packages)) {
$dependencies = \array_merge($dependencies, \array_map('strval', \array_keys($packages)));
}
}

return $dependencies;
}
}
Loading
Loading