Skip to content

Commit 74e520d

Browse files
committed
feat: #6 lint route URL
1 parent 6e3901c commit 74e520d

File tree

5 files changed

+149
-1
lines changed

5 files changed

+149
-1
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ php artisan lint:publish
1717

1818
## use
1919

20+
lint code:
21+
2022
```shell
2123
php artisan lint:check .
2224
php artisan lint:check app/ tests/
@@ -25,3 +27,11 @@ php artisan lint:staged
2527
```
2628

2729
The default standard is PSR-12, feel free to change the `phpcs.xml`.
30+
31+
lint route URL:
32+
33+
```shell
34+
php artisan lint:route
35+
```
36+
37+
Slug(kebab-case) standard: lowercase ASCII letters, digits, and hyphens (a-z, 0–9, -)

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"minimum-stability": "stable",
3131
"require": {
3232
"laravel/framework": ">=v6.18.38",
33-
"squizlabs/php_codesniffer": "^3.5"
33+
"squizlabs/php_codesniffer": "^3.5",
34+
"ext-json": "*"
3435
},
3536
"require-dev": {
3637
"orchestra/testbench": ">=v4.8.0"

src/LintRouteCommand.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace LaravelFans\Lint;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Support\Facades\Artisan;
7+
use Illuminate\Support\Facades\File;
8+
use Illuminate\Support\Str;
9+
10+
class LintRouteCommand extends Command
11+
{
12+
/**
13+
* The name and signature of the console command.
14+
*
15+
* @var string
16+
*/
17+
protected $signature = 'lint:route
18+
{--file=auto : route list json file}';
19+
20+
/**
21+
* The console command description.
22+
*
23+
* @var string
24+
*/
25+
protected $description = 'Lint routes';
26+
27+
/**
28+
* Execute the console command.
29+
*
30+
* @return int
31+
*/
32+
public function handle()
33+
{
34+
if ($this->option('file') == 'auto') {
35+
Artisan::call('route:list', ['--json' => true]);
36+
$json = Artisan::output();
37+
} else {
38+
$json = File::get($this->option('file'));
39+
}
40+
$routes = json_decode($json, true);
41+
foreach ($routes as $route) {
42+
if ($route['uri'] == '/') {
43+
continue;
44+
}
45+
$this->info($route['uri']);
46+
if (Str::endsWith($route['uri'], '/')) {
47+
// https://docs.geostandaarden.nl/api/API-Designrules/#api-48-leave-off-trailing-slashes-from-api-endpoints
48+
$this->error('API-48: Leave off trailing slashes from API endpoints');
49+
return 1;
50+
}
51+
$tmp = explode('/', ltrim($route['uri'], '/'));
52+
foreach ($tmp as $piece) {
53+
// ignore variable
54+
if (preg_match('/^\{[^{]+\}$/', $piece)) {
55+
continue;
56+
}
57+
if (!preg_match('/^[a-z0-9]+(\-[a-z0-9]+)*$/', $piece)) {
58+
$this->error('user-friendly URL should follow domain rules: '
59+
. 'lowercase ASCII letters, digits, and hyphens (a-z, 0–9, -)');
60+
return 1;
61+
}
62+
}
63+
}
64+
65+
return 0;
66+
}
67+
}

src/LintServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public function boot()
1717
$this->commands([
1818
LintCheckCommand::class,
1919
LintPublishCommand::class,
20+
LintRouteCommand::class,
2021
LintStagedCommand::class,
2122
]);
2223
}
@@ -32,6 +33,7 @@ public function provides()
3233
return [
3334
LintCheckCommand::class,
3435
LintPublishCommand::class,
36+
LintRouteCommand::class,
3537
LintStagedCommand::class,
3638
];
3739
}

tests/LintRouteCommandTest.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace LaravelFans\Lint\Tests;
4+
5+
use Illuminate\Support\Facades\File;
6+
7+
class LintRouteCommandTest extends TestCase
8+
{
9+
public function testBadRoute()
10+
{
11+
$laravelPath = __DIR__ . '/../vendor/orchestra/testbench-core/laravel';
12+
File::put($laravelPath . '/route.json', json_encode([
13+
[
14+
'method' => 'GET',
15+
'uri' => 'user/',
16+
],
17+
]));
18+
$this->artisan('lint:route', ['--file' => $laravelPath . '/route.json'])
19+
->assertExitCode(1);
20+
21+
File::put($laravelPath . '/route.json', json_encode([
22+
[
23+
'method' => 'GET',
24+
'uri' => 'api/user/',
25+
],
26+
]));
27+
$this->artisan('lint:route', ['--file' => $laravelPath . '/route.json'])
28+
->assertExitCode(1);
29+
30+
File::put($laravelPath . '/route.json', json_encode([
31+
[
32+
'method' => 'GET',
33+
'uri' => 'user_profile',
34+
],
35+
]));
36+
$this->artisan('lint:route', ['--file' => $laravelPath . '/route.json'])
37+
->assertExitCode(1);
38+
}
39+
40+
public function testGoodRoute()
41+
{
42+
$laravelPath = __DIR__ . '/../vendor/orchestra/testbench-core/laravel';
43+
File::put($laravelPath . '/route.json', json_encode([
44+
[
45+
'method' => 'GET',
46+
'uri' => '/',
47+
],
48+
[
49+
'method' => 'GET',
50+
'uri' => 'api/user',
51+
],
52+
[
53+
'method' => 'GET',
54+
'uri' => '/user',
55+
],
56+
[
57+
'method' => 'GET|HEAD',
58+
'uri' => 'api/photos/{photo}/comments/{comment}/edit',
59+
],
60+
[
61+
'method' => 'GET|HEAD',
62+
'uri' => 'api/photos/{photo_id}/comments/{id}',
63+
],
64+
]));
65+
$this->artisan('lint:route', ['--file' => $laravelPath . '/route.json'])
66+
->assertExitCode(0);
67+
}
68+
}

0 commit comments

Comments
 (0)