Skip to content

Commit 2d2104f

Browse files
authored
fix: asterisk wildcard generates as invalid key without quotes (#44)
* fix: asterisk wildcard generates as invalid key without quotes * fix: using json_encode() for both the key (when quoted) and the value, which properly escapes quotes, backslashes, newlines, unicode, and any other special characters
1 parent bd8baa5 commit 2d2104f

File tree

2 files changed

+37
-1
lines changed

2 files changed

+37
-1
lines changed

src/Commands/TypeScriptCommand.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,8 @@ private function collectCapabilities(DefinitionDiscoverer $discoverer): array
303303
* - "article:view" → ["ArticlePermissions", "VIEW"]
304304
* - "user.create" → ["UserPermissions", "CREATE"]
305305
* - "admin" → ["Roles", "ADMIN"]
306+
* - "article:*" → ["ArticlePermissions", "*"]
307+
* - "*" → ["Permissions", "*"]
306308
*
307309
* @return array{0: string, 1: string}
308310
*/
@@ -328,6 +330,14 @@ private function parseNameToGroupAndConst(string $name, string $defaultGroup): a
328330
return [$defaultGroup, $constName];
329331
}
330332

333+
/**
334+
* Check if a string is a valid JavaScript/TypeScript identifier.
335+
*/
336+
private function isValidIdentifier(string $name): bool
337+
{
338+
return (bool) preg_match('/^[a-zA-Z_$][a-zA-Z0-9_$]*$/', $name);
339+
}
340+
331341
/**
332342
* Group definitions by their source class.
333343
*
@@ -364,7 +374,11 @@ private function generateConstObject(string $groupName, array $items): string
364374
$lines = ["export const {$groupName} = {"];
365375

366376
foreach ($items as $constName => $value) {
367-
$lines[] = " {$constName}: \"{$value}\",";
377+
// Quote keys that aren't valid identifiers (e.g., wildcards like "*")
378+
// Use json_encode for proper escaping of special characters
379+
$key = $this->isValidIdentifier($constName) ? $constName : json_encode($constName);
380+
$escapedValue = json_encode($value);
381+
$lines[] = " {$key}: {$escapedValue},";
368382
}
369383

370384
$lines[] = '} as const;';

tests/Feature/Commands/TypeScriptCommandTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,28 @@
8383
expect($content)->toContain('export type Role');
8484
});
8585

86+
it('quotes wildcard permissions as valid TypeScript property keys', function () {
87+
config(['mandate.code_first.enabled' => false]);
88+
89+
// Create wildcard permissions in the database
90+
OffloadProject\Mandate\Models\Permission::create(['name' => '*']);
91+
OffloadProject\Mandate\Models\Permission::create(['name' => 'article:*']);
92+
93+
$outputFile = $this->outputPath.'/mandate.ts';
94+
95+
$this->artisan('mandate:typescript', ['--output' => $outputFile])
96+
->assertSuccessful();
97+
98+
$content = file_get_contents($outputFile);
99+
100+
// Wildcard keys should be quoted
101+
expect($content)->toContain('"*": "*"');
102+
expect($content)->toContain('"*": "article:*"');
103+
104+
// Should NOT contain unquoted * as a key
105+
expect($content)->not->toMatch('/^\s+\*:/m');
106+
});
107+
86108
it('uses as const for objects', function () {
87109
$outputFile = $this->outputPath.'/mandate.ts';
88110

0 commit comments

Comments
 (0)