Skip to content

Commit 91ccce6

Browse files
adrumpushpak1300taylorotwell
authored
[1.x] Add support for markdown files (#319)
* add support for markdown files * Update README.md Signed-off-by: Pushpak Chhajed <[email protected]> * feat: add tests and handling for Blade templates and Markdown in GuidelineComposer Signed-off-by: Pushpak Chhajed <[email protected]> * refactor guideline path handling Signed-off-by: Pushpak Chhajed <[email protected]> * Update README.md --------- Signed-off-by: Pushpak Chhajed <[email protected]> Co-authored-by: Pushpak Chhajed <[email protected]> Co-authored-by: Pushpak Chhajed <[email protected]> Co-authored-by: Taylor Otwell <[email protected]>
1 parent ffbbb9b commit 91ccce6

File tree

7 files changed

+124
-21
lines changed

7 files changed

+124
-21
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ You may also automate this process by adding it to your Composer "post-update-cm
145145

146146
## Adding Custom AI Guidelines
147147

148-
To augment Laravel Boost with your own custom AI guidelines, add `.blade.php` files to your application's `.ai/guidelines/*` directory. These files will automatically be included with Laravel Boost's guidelines when you run `boost:install`.
148+
To augment Laravel Boost with your own custom AI guidelines, add `.blade.php` or `.md` files to your application's `.ai/guidelines/*` directory. These files will automatically be included with Laravel Boost's guidelines when you run `boost:install`.
149149

150150
### Overriding Boost AI Guidelines
151151

src/Install/GuidelineComposer.php

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -244,14 +244,38 @@ protected function guidelinesDir(string $dirPath, bool $thirdParty = false): arr
244244
$finder = Finder::create()
245245
->files()
246246
->in($dirPath)
247-
->name('*.blade.php');
247+
->name('*.blade.php')
248+
->name('*.md');
248249
} catch (DirectoryNotFoundException) {
249250
return [];
250251
}
251252

252253
return array_map(fn (SplFileInfo $file): array => $this->guideline($file->getRealPath(), $thirdParty), iterator_to_array($finder));
253254
}
254255

256+
protected function renderContent(string $content, string $path): string
257+
{
258+
$isBladeTemplate = str_ends_with($path, '.blade.php');
259+
260+
if (! $isBladeTemplate) {
261+
return $content;
262+
}
263+
264+
// Temporarily replace backticks and PHP opening tags with placeholders before Blade processing
265+
// This prevents Blade from trying to execute PHP code examples and supports inline code
266+
$placeholders = [
267+
'`' => '___SINGLE_BACKTICK___',
268+
'<?php' => '___OPEN_PHP_TAG___',
269+
];
270+
271+
$content = str_replace(array_keys($placeholders), array_values($placeholders), $content);
272+
$rendered = Blade::render($content, [
273+
'assist' => $this->guidelineAssist,
274+
]);
275+
276+
return str_replace(array_values($placeholders), array_keys($placeholders), $rendered);
277+
}
278+
255279
/**
256280
* @return array{content: string, name: string, description: string, path: ?string, custom: bool, third_party: bool}
257281
*/
@@ -272,18 +296,8 @@ protected function guideline(string $path, bool $thirdParty = false): array
272296
$content = file_get_contents($path);
273297
$content = $this->processBoostSnippets($content);
274298

275-
// Temporarily replace backticks and PHP opening tags with placeholders before Blade processing
276-
// This prevents Blade from trying to execute PHP code examples and supports inline code
277-
$placeholders = [
278-
'`' => '___SINGLE_BACKTICK___',
279-
'<?php' => '___OPEN_PHP_TAG___',
280-
];
299+
$rendered = $this->renderContent($content, $path);
281300

282-
$content = str_replace(array_keys($placeholders), array_values($placeholders), $content);
283-
$rendered = Blade::render($content, [
284-
'assist' => $this->guidelineAssist,
285-
]);
286-
$rendered = str_replace(array_values($placeholders), array_keys($placeholders), $rendered);
287301
$rendered = str_replace(array_keys($this->storedSnippets), array_values($this->storedSnippets), $rendered);
288302

289303
$this->storedSnippets = []; // Clear for next use
@@ -298,7 +312,7 @@ protected function guideline(string $path, bool $thirdParty = false): array
298312

299313
return [
300314
'content' => trim($rendered),
301-
'name' => str_replace('.blade.php', '', basename($path)),
315+
'name' => str_replace(['.blade.php', '.md'], '', basename($path)),
302316
'description' => $description,
303317
'path' => $path,
304318
'custom' => str_contains($path, $this->customGuidelinePath()),
@@ -326,16 +340,21 @@ protected function processBoostSnippets(string $content): string
326340

327341
protected function prependPackageGuidelinePath(string $path): string
328342
{
329-
$path = preg_replace('/\.blade\.php$/', '', $path);
330-
331-
return str_replace('/', DIRECTORY_SEPARATOR, __DIR__.'/../../.ai/'.$path.'.blade.php');
343+
return $this->prependGuidelinePath($path, __DIR__.'/../../.ai/');
332344
}
333345

334346
protected function prependUserGuidelinePath(string $path): string
335347
{
336-
$path = preg_replace('/\.blade\.php$/', '', $path);
348+
return $this->prependGuidelinePath($path, $this->customGuidelinePath());
349+
}
350+
351+
private function prependGuidelinePath(string $path, string $basePath): string
352+
{
353+
if (! str_ends_with($path, '.md') && ! str_ends_with($path, '.blade.php')) {
354+
$path .= '.blade.php';
355+
}
337356

338-
return str_replace('/', DIRECTORY_SEPARATOR, $this->customGuidelinePath($path.'.blade.php'));
357+
return str_replace('/', DIRECTORY_SEPARATOR, $basePath.$path);
339358
}
340359

341360
protected function guidelinePath(string $path): ?string

tests/Feature/Install/GuidelineComposerTest.php

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Laravel\Roster\Package;
1111
use Laravel\Roster\PackageCollection;
1212
use Laravel\Roster\Roster;
13+
use function Pest\testDirectory;
1314

1415
beforeEach(function (): void {
1516
$this->roster = Mockery::mock(Roster::class);
@@ -268,7 +269,7 @@
268269
$composer = Mockery::mock(GuidelineComposer::class, [$this->roster, $this->herd])->makePartial();
269270
$composer
270271
->shouldReceive('customGuidelinePath')
271-
->andReturnUsing(fn ($path = ''): string => realpath(\Pest\testDirectory('fixtures/.ai/guidelines')).'/'.ltrim((string) $path, '/'));
272+
->andReturnUsing(fn ($path = ''): string => realpath(testDirectory('fixtures/.ai/guidelines')).'/'.ltrim((string) $path, '/'));
272273

273274
expect($composer->compose())
274275
->toContain('=== .ai/custom-rule rules ===')
@@ -291,7 +292,7 @@
291292
$composer = Mockery::mock(GuidelineComposer::class, [$this->roster, $this->herd])->makePartial();
292293
$composer
293294
->shouldReceive('customGuidelinePath')
294-
->andReturnUsing(fn ($path = ''): string => realpath(\Pest\testDirectory('fixtures/.ai/guidelines')).'/'.ltrim((string) $path, '/'));
295+
->andReturnUsing(fn ($path = ''): string => realpath(testDirectory('fixtures/.ai/guidelines')).'/'.ltrim((string) $path, '/'));
295296

296297
$guidelines = $composer->compose();
297298
$overrideStringCount = substr_count((string) $guidelines, 'Thanks though, appreciate you');
@@ -382,3 +383,45 @@
382383
'yarn' => [NodePackageManager::YARN, 'yarn'],
383384
'bun' => [NodePackageManager::BUN, 'bun'],
384385
]);
386+
387+
test('renderContent handles blade and markdown files correctly', function (): void {
388+
$packages = new PackageCollection([
389+
new Package(Packages::LARAVEL, 'laravel/framework', '11.0.0'),
390+
]);
391+
392+
$this->roster->shouldReceive('packages')->andReturn($packages);
393+
$this->nodePackageManager = NodePackageManager::NPM;
394+
395+
$composer = Mockery::mock(GuidelineComposer::class, [$this->roster, $this->herd])->makePartial();
396+
$composer
397+
->shouldReceive('customGuidelinePath')
398+
->andReturnUsing(fn ($path = ''): string => realpath(testDirectory('fixtures/.ai/guidelines')).'/'.ltrim((string) $path, '/'));
399+
400+
$guidelines = $composer->compose();
401+
402+
expect($guidelines)
403+
// Preserves backticks in blade templates
404+
->toContain('=== .ai/test-blade-with-backticks rules ===')
405+
->not->toContain('=== .ai/test-blade-with-backticks.md rules ===')
406+
->toContain('`artisan make:model`')
407+
->toContain('`php artisan migrate`')
408+
->toContain('`Model::query()`')
409+
->toContain('`route(\'home\')`')
410+
->toContain('`config(\'app.name\')`')
411+
// Preserves PHP tags in blade templates
412+
->toContain('=== .ai/test-blade-with-php-tags rules ===')
413+
->not->toContain('=== .ai/test-blade-with-backticks.blade.php rules ===')
414+
->toContain('<?php')
415+
->toContain('namespace App\Models;')
416+
->toContain('class User extends Model')
417+
// Does not process markdown files with blade
418+
->toContain('=== .ai/test-markdown rules ===')
419+
->toContain('# Markdown File Test')
420+
->toContain('This is a plain markdown file')
421+
->toContain('Use `code` in backticks')
422+
->toContain('echo "Hello World";')
423+
// Processes blade variables correctly
424+
->toContain('=== .ai/test-blade-with-assist rules ===')
425+
->toContain('Run `npm install` to install dependencies')
426+
->toContain('Package manager: npm');
427+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Blade with @assist variable
2+
3+
Run `{{ $assist->nodePackageManager() }} install` to install dependencies.
4+
5+
Package manager: {{ $assist->nodePackageManager() }}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Blade Template with Backticks
2+
3+
Use `artisan make:model` to create models.
4+
Run `php artisan migrate` for migrations.
5+
6+
Code example with backticks:
7+
- `Model::query()`
8+
- `route('home')`
9+
- `config('app.name')`
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Blade Template with PHP Tags
2+
3+
Example PHP code:
4+
5+
<?php
6+
7+
namespace App\Models;
8+
9+
class User extends Model
10+
{
11+
// Model code
12+
}
13+
14+
Use this pattern in your code.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Markdown File Test
2+
3+
This is a plain markdown file.
4+
5+
Use `code` in backticks.
6+
7+
Example code:
8+
```php
9+
<?php
10+
echo "Hello World";
11+
```
12+
13+
This should not be processed by Blade.

0 commit comments

Comments
 (0)