Skip to content

Commit 74b6332

Browse files
committed
Remove completed stub directive TODOs and add tests for them
1 parent 337d5b9 commit 74b6332

3 files changed

Lines changed: 102 additions & 27 deletions

File tree

docs/todo/blade.md

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,6 @@ actions that generate new code need to be aware of Blade structure.
6262

6363
Remaining work in this phase:
6464

65-
### Stub directives
66-
67-
These directives are recognized by the preprocessor but currently
68-
produce generic output rather than the semantic PHP they should:
69-
70-
- `@auth`/`@endauth`, `@guest`/`@endguest`, `@env(...)`/`@endenv`,
71-
`@production`/`@endproduction`, `@once`/`@endonce`
72-
`if (true):` / `endif;`
73-
- `@csrf`, `@method(...)`, `@push`/`@endpush`, `@prepend`/`@endprepend`,
74-
`@stack(...)`, `@yield(...)`, `@section(...)`/`@endsection`/`@show`,
75-
`@extends(...)`, `@include(...)` and variants, `@includeIf(...)`,
76-
`@includeWhen(...)`, `@includeUnless(...)`, `@includeFirst(...)`,
77-
`@each(...)``/* @directive */`
78-
7965
### `$loop` variable injection
8066

8167
The preprocessor injects `$errors` and `$__env` in the prologue, but
@@ -88,12 +74,7 @@ $loop = (object)[];
8874

8975
### Additional test coverage
9076

91-
- Directives with implicit vars (`@error`, `@session`, `@context`)
92-
- Stub directives (`@auth`, `@guest`, `@csrf`, etc.)
93-
- Verbatim regions
94-
- `$loop->` inside a `@foreach`
95-
- `$value` inside `@session` block
96-
- `$message` inside `@error` block
77+
- `$loop->` inside a `@foreach` (blocked on `$loop` variable injection)
9778

9879
---
9980

src/blade/directives.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,12 @@ pub fn translate_directive(directive: &str) -> String {
121121
"unless" => "if(!".to_string(),
122122
"else" => "else:".to_string(),
123123
"endif" | "endforeach" | "endfor" | "endwhile" | "endunless" | "endisset" | "endempty"
124-
| "endswitch" | "endforelse" | "endsession" | "endcontext" | "enderror" => {
124+
| "endswitch" | "endforelse" | "endsession" | "endcontext" | "enderror"
125+
| "endauth" | "endguest" | "endproduction" | "endenv" | "endonce" => {
125126
let mapped = match directive {
126127
"endunless" | "endisset" | "endempty" | "endsession" | "endcontext"
127-
| "enderror" => "endif",
128+
| "enderror" | "endauth" | "endguest" | "endproduction" | "endenv"
129+
| "endonce" => "endif",
128130
"endforelse" => "endif",
129131
other => other,
130132
};

src/blade/preprocessor.rs

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,23 @@ pub fn preprocess(content: &str) -> (String, BladeSourceMap) {
145145
replacement = " if (true) ".to_string();
146146
next_mode = Mode::SkipArgs(": $message = '';");
147147
paren_depth = 0;
148+
} else if matches!(
149+
directive,
150+
"auth" | "guest" | "production" | "env" | "once"
151+
) {
152+
// These are conditional blocks: if args present, skip them;
153+
// if no args, emit directly.
154+
let after_dir: String =
155+
rest_str[directive.len()..].chars().collect();
156+
let after_trimmed = after_dir.trim_start();
157+
if after_trimmed.starts_with('(') {
158+
replacement = " if (true) ".to_string();
159+
next_mode = Mode::SkipArgs(":");
160+
paren_depth = 0;
161+
} else {
162+
replacement = " if (true): ".to_string();
163+
next_mode = Mode::Html;
164+
}
148165
} else if matches!(
149166
directive,
150167
"if" | "elseif"
@@ -175,11 +192,6 @@ pub fn preprocess(content: &str) -> (String, BladeSourceMap) {
175192
| "slot"
176193
| "props"
177194
| "aware"
178-
| "auth"
179-
| "guest"
180-
| "production"
181-
| "env"
182-
| "once"
183195
| "fragment"
184196
| "hasSection"
185197
| "sectionMissing"
@@ -550,4 +562,84 @@ mod tests {
550562
php2
551563
);
552564
}
565+
566+
#[test]
567+
fn test_preprocess_stub_directives() {
568+
// @csrf should produce a comment (no-args directive)
569+
let content = "@csrf\n";
570+
let (php, _) = preprocess(content);
571+
assert!(
572+
php.contains("/* @csrf */"),
573+
"@csrf should become a comment: {}",
574+
php
575+
);
576+
577+
// @auth without args should produce if (true):
578+
let content = "@auth\n<p>logged in</p>\n@endauth\n";
579+
let (php, _) = preprocess(content);
580+
assert!(php.contains("if (true):"), "@auth should produce if (true):: {}", php);
581+
assert!(php.contains("endif;"), "@endauth should produce endif;: {}", php);
582+
583+
// @auth with args should also produce if (true):
584+
let content = "@auth('admin')\n<p>admin</p>\n@endauth\n";
585+
let (php, _) = preprocess(content);
586+
assert!(php.contains("if (true)"), "@auth('admin') should produce if (true): {}", php);
587+
assert!(php.contains("endif;"), "@endauth should produce endif;: {}", php);
588+
589+
// @guest without args
590+
let content = "@guest\n<p>guest</p>\n@endguest\n";
591+
let (php, _) = preprocess(content);
592+
assert!(php.contains("if (true):"), "@guest should produce if (true):: {}", php);
593+
assert!(php.contains("endif;"), "@endguest should produce endif;: {}", php);
594+
595+
// @production (never takes args)
596+
let content = "@production\n<p>prod</p>\n@endproduction\n";
597+
let (php, _) = preprocess(content);
598+
assert!(php.contains("if (true):"), "@production should produce if (true):: {}", php);
599+
assert!(php.contains("endif;"), "@endproduction should produce endif;: {}", php);
600+
601+
// @env with args
602+
let content = "@env('local')\n<p>local</p>\n@endenv\n";
603+
let (php, _) = preprocess(content);
604+
assert!(php.contains("if (true)"), "@env should produce if (true): {}", php);
605+
assert!(php.contains("endif;"), "@endenv should produce endif;: {}", php);
606+
607+
// @once without args
608+
let content = "@once\n<script>app.js</script>\n@endonce\n";
609+
let (php, _) = preprocess(content);
610+
assert!(php.contains("if (true):"), "@once should produce if (true):: {}", php);
611+
assert!(php.contains("endif;"), "@endonce should produce endif;: {}", php);
612+
}
613+
614+
#[test]
615+
fn test_preprocess_session_value_accessible() {
616+
// $value should be accessible inside @session block
617+
let content = "@session('status')\n{{ $value }}\n@endsession\n";
618+
let (php, _) = preprocess(content);
619+
assert!(php.contains("$value = '';"), "should declare $value: {}", php);
620+
// The $value echo should appear after the declaration
621+
let val_decl = php.find("$value = '';").unwrap();
622+
// Find last occurrence of $value (the echo usage)
623+
let val_echo = php.rfind("$value").unwrap();
624+
assert!(
625+
val_echo > val_decl,
626+
"$value usage should come after declaration: {}",
627+
php
628+
);
629+
}
630+
631+
#[test]
632+
fn test_preprocess_error_message_accessible() {
633+
// $message should be accessible inside @error block
634+
let content = "@error('email')\n{{ $message }}\n@enderror\n";
635+
let (php, _) = preprocess(content);
636+
assert!(php.contains("$message = '';"), "should declare $message: {}", php);
637+
let msg_decl = php.find("$message = '';").unwrap();
638+
let msg_echo = php.rfind("$message").unwrap();
639+
assert!(
640+
msg_echo > msg_decl,
641+
"$message usage should come after declaration: {}",
642+
php
643+
);
644+
}
553645
}

0 commit comments

Comments
 (0)