Skip to content
This repository was archived by the owner on Aug 8, 2025. It is now read-only.

Commit 6d3ce85

Browse files
committed
init
0 parents  commit 6d3ce85

25 files changed

+8535
-0
lines changed

.editorconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
insert_final_newline = true
7+
indent_style = space
8+
indent_size = 4
9+
trim_trailing_whitespace = true
10+
11+
[*.md]
12+
trim_trailing_whitespace = false
13+
14+
[*.yml]
15+
indent_size = 2

.gitattributes

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
* text=auto eol=lf
2+
/.github export-ignore
3+
.scrutinizer.yml export-ignore
4+
BACKERS.md export-ignore
5+
CONTRIBUTING.md export-ignore
6+
CHANGELOG.md export-ignore

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/vendor
2+
/.idea
3+
/.vscode
4+
/.vagrant
5+
.phpunit.result.cache
6+
/example

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# PHP-SaaS Cli
2+
3+
The command line interface for the PHP-SaaS project.
4+
5+
## Installation
6+
7+
```bash
8+
composer global require php-saas/cli
9+
```
10+
11+
## Usage
12+
13+
```bash
14+
php-saas new <project-name>
15+
```

app/Actions/Composer.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace App\Actions;
4+
5+
class Composer
6+
{
7+
public function setup(string $path): void
8+
{
9+
exec("composer install --working-dir={$path}");
10+
}
11+
}

app/Actions/Frontend.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace App\Actions;
4+
5+
use Illuminate\Support\Facades\File;
6+
use RuntimeException;
7+
8+
class Frontend
9+
{
10+
public function setup(string $path, string $stack): void
11+
{
12+
$renames = [
13+
'resources/js-%s' => 'resources/js',
14+
'vite-%s.config.ts' => 'vite.config.ts',
15+
'tsconfig-%s.json' => 'tsconfig.json',
16+
'eslint-%s.config.js' => 'eslint.config.js',
17+
'package-%s.json' => 'package.json',
18+
];
19+
20+
foreach ($renames as $from => $to) {
21+
$from = $path . '/' . sprintf($from, strtolower($stack));
22+
$to = $path . '/' . sprintf($to, strtolower($stack));
23+
24+
if (!File::exists($from)) {
25+
throw new RuntimeException("File {$from} does not exist. Please check the template.");
26+
}
27+
28+
File::move($from, $to);
29+
}
30+
}
31+
32+
public function cleanup(string $path): void
33+
{
34+
File::deleteDirectory($path . '/resources/js-vue');
35+
File::deleteDirectory($path . '/resources/js-react');
36+
}
37+
}

app/Actions/Git.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace App\Actions;
4+
5+
use Illuminate\Support\Facades\File;
6+
use RuntimeException;
7+
8+
class Git
9+
{
10+
public function clone(string $directory): void
11+
{
12+
if (is_dir($directory)) {
13+
throw new RuntimeException("Directory {$directory} already exists. Please choose a different name or remove the existing directory.");
14+
}
15+
16+
$repositoryUrl = '[email protected]:php-saas/php-saas.git';
17+
18+
exec("git clone {$repositoryUrl} {$directory}");
19+
}
20+
21+
public function cleanup(string $path): void
22+
{
23+
File::deleteDirectory($path . '/.github');
24+
File::deleteDirectory($path . '/.git');
25+
File::delete($path . '/.gitignore');
26+
File::move($path . '/.gitignore.final', $path . '/.gitignore');
27+
}
28+
}

app/Actions/Tests.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace App\Actions;
4+
5+
use Illuminate\Support\Facades\File;
6+
use RuntimeException;
7+
8+
class Tests
9+
{
10+
public function setup(string $path, string $stack): void
11+
{
12+
$renames = [
13+
'tests-%s' => 'tests',
14+
];
15+
16+
foreach ($renames as $from => $to) {
17+
$from = $path . '/' . sprintf($from, strtolower($stack));
18+
$to = $path . '/' . sprintf($to, strtolower($stack));
19+
20+
if (!File::exists($from)) {
21+
throw new RuntimeException("File {$from} does not exist. Please check the template.");
22+
}
23+
24+
File::move($from, $to);
25+
}
26+
}
27+
28+
public function cleanup(string $path, string $stack): void
29+
{
30+
File::deleteDirectory($path . '/tests-pest');
31+
File::deleteDirectory($path . '/tests-phpunit');
32+
33+
switch ($stack) {
34+
case 'Pest':
35+
exec('composer remove phpunit/phpunit --no-interaction --working-dir=' . $path);
36+
break;
37+
case 'PHPUnit':
38+
exec('composer remove pestphp/pest --no-interaction --working-dir=' . $path);
39+
break;
40+
default:
41+
}
42+
}
43+
}

app/Commands/InspireCommand.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace App\Commands;
4+
5+
use Illuminate\Console\Scheduling\Schedule;
6+
use LaravelZero\Framework\Commands\Command;
7+
8+
use function Termwind\render;
9+
10+
class InspireCommand extends Command
11+
{
12+
/**
13+
* The signature of the command.
14+
*
15+
* @var string
16+
*/
17+
protected $signature = 'inspire {name=Artisan}';
18+
19+
/**
20+
* The description of the command.
21+
*
22+
* @var string
23+
*/
24+
protected $description = 'Display an inspiring quote';
25+
26+
/**
27+
* Execute the console command.
28+
*/
29+
public function handle(): void
30+
{
31+
render(<<<'HTML'
32+
<div class="py-1 ml-2">
33+
<div class="px-1 bg-blue-300 text-black">Laravel Zero</div>
34+
<em class="ml-1">
35+
Simplicity is the ultimate sophistication.
36+
</em>
37+
</div>
38+
HTML);
39+
}
40+
41+
/**
42+
* Define the command's schedule.
43+
*/
44+
public function schedule(Schedule $schedule): void
45+
{
46+
// $schedule->command(static::class)->everyMinute();
47+
}
48+
}

app/Commands/NewCommand.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
namespace App\Commands;
4+
5+
use App\Actions\Composer;
6+
use App\Actions\Frontend;
7+
use App\Actions\Git;
8+
use App\Actions\Tests;
9+
use Illuminate\Console\Command;
10+
use Illuminate\Support\Facades\File;
11+
use function Laravel\Prompts\select;
12+
13+
class NewCommand extends Command
14+
{
15+
protected $signature = 'new {name}';
16+
17+
protected $description = 'Create a new application';
18+
19+
protected string $path = '';
20+
21+
protected string $frontend = '';
22+
23+
protected string $tests = '';
24+
25+
protected string $projects = '';
26+
27+
protected string $billing = '';
28+
29+
protected string $apiTokens = '';
30+
31+
public function handle(): void
32+
{
33+
$name = $this->argument('name');
34+
35+
$this->path = getcwd() . '/' . $name;
36+
37+
$this->collectInputs();
38+
39+
app(Git::class)->clone($name);
40+
app(Frontend::class)->setup($this->path, $this->frontend);
41+
app(Tests::class)->setup($this->path, $this->tests);
42+
app(Composer::class)->setup($this->path);
43+
44+
$this->cleanup();
45+
46+
$this->info("Application '{$name}' created successfully.");
47+
}
48+
49+
private function collectInputs(): void
50+
{
51+
$this->frontend = 'React';
52+
$this->tests = 'Pest';
53+
$this->projects = 'Projects';
54+
$this->billing = 'Cashier Paddle';
55+
$this->apiTokens = 'Yes';
56+
57+
return;
58+
$this->frontend = select("Which frontend stack would you like to use?", [
59+
'React',
60+
'Vue',
61+
]);
62+
$this->tests = select("Which testing framework would you like to use?", [
63+
'PHPUnit',
64+
'Pest',
65+
]);
66+
$this->projects = select("Do you want Projects, Organizations or Teams?", [
67+
'Projects',
68+
'Organizations',
69+
'Teams',
70+
'None',
71+
]);
72+
$this->billing = select("Which payment provider do you want for Billing?", [
73+
'Cashier Paddle',
74+
'Cashier Stripe',
75+
'None',
76+
]);
77+
$this->apiTokens = select("Do you want to include API tokens?", [
78+
'Yes',
79+
'No',
80+
]);
81+
}
82+
83+
private function cleanup(): void
84+
{
85+
app(Git::class)->cleanup($this->path);
86+
app(Frontend::class)->cleanup($this->path);
87+
app(Tests::class)->cleanup($this->path, $this->tests);
88+
89+
File::delete($this->path . '/use.sh');
90+
}
91+
}

0 commit comments

Comments
 (0)