* Validate a plugin's hooks.json file. Unlike frontmatter, this one HARD-ERRORS * at runtime (pluginLoader uses .parse() not .safeParse()) — a bad hooks.json * breaks the whole plugin. Surfacing it here is essential.
(filePath: string)
| 644 | * breaks the whole plugin. Surfacing it here is essential. |
| 645 | */ |
| 646 | async function validateHooksJson(filePath: string): Promise<ValidationResult> { |
| 647 | let content: string |
| 648 | try { |
| 649 | content = await readFile(filePath, { encoding: 'utf-8' }) |
| 650 | } catch (e: unknown) { |
| 651 | const code = getErrnoCode(e) |
| 652 | // ENOENT is fine — hooks are optional |
| 653 | if (code === 'ENOENT') { |
| 654 | return { |
| 655 | success: true, |
| 656 | errors: [], |
| 657 | warnings: [], |
| 658 | filePath, |
| 659 | fileType: 'hooks', |
| 660 | } |
| 661 | } |
| 662 | return { |
| 663 | success: false, |
| 664 | errors: [ |
| 665 | { path: 'file', message: `Failed to read file: ${errorMessage(e)}` }, |
| 666 | ], |
| 667 | warnings: [], |
| 668 | filePath, |
| 669 | fileType: 'hooks', |
| 670 | } |
| 671 | } |
| 672 | |
| 673 | let parsed: unknown |
| 674 | try { |
| 675 | parsed = jsonParse(content) |
| 676 | } catch (e) { |
| 677 | return { |
| 678 | success: false, |
| 679 | errors: [ |
| 680 | { |
| 681 | path: 'json', |
| 682 | message: |
| 683 | `Invalid JSON syntax: ${errorMessage(e)}. ` + |
| 684 | `At runtime this breaks the entire plugin load.`, |
| 685 | }, |
| 686 | ], |
| 687 | warnings: [], |
| 688 | filePath, |
| 689 | fileType: 'hooks', |
| 690 | } |
| 691 | } |
| 692 | |
| 693 | const result = PluginHooksSchema().safeParse(parsed) |
| 694 | if (!result.success) { |
| 695 | return { |
| 696 | success: false, |
| 697 | errors: formatZodErrors(result.error), |
| 698 | warnings: [], |
| 699 | filePath, |
| 700 | fileType: 'hooks', |
| 701 | } |
| 702 | } |
| 703 |
no test coverage detected