feat(ssg): enhance conbined hooks
We are considering making it easier for users to specify hooks without having to be aware of their placement by ultimately passing a list of each hook to either plugins or hooks. As a preliminary step, it is essential to enable each hook to combine multiple hooks. Without this capability, implementing such a patch for extension would be challenging.
Combining Hook
You can enhance your site generation process by combining multiple hooks. While this approach can be applied to any type of hook (BeforeRequestHook, AfterResponseHook, AfterGenerateHook), the example below demonstrates combining multiple BeforeRequestHook instances to achieve fine-grained request filtering.
Example:
Combining BeforeRequestHook. First, define two hooks: one to allow requests for certain paths, and another to deny requests for specific paths.
// Hook to allow requests only to specified paths
const filterPathsBeforeRequestHook = (allowedPaths: string | string[]): BeforeRequestHook => {
const baseURL = 'http://localhost';
return async (req: Request): Promise<Request | false> => {
const paths = Array.isArray(allowedPaths) ? allowedPaths : [allowedPaths];
const pathname = new URL(req.url, baseURL).pathname;
return paths.some(path => pathname === path || pathname.startsWith(`${path}/`)) ? req : false;
};
};
// Hook to deny requests to specified paths
const denyPathsBeforeRequestHook = (deniedPaths: string | string[]): BeforeRequestHook => {
const baseURL = 'http://localhost';
return async (req: Request): Promise<Request | false> => {
const paths = Array.isArray(deniedPaths) ? deniedPaths : [deniedPaths];
const pathname = new URL(req.url, baseURL).pathname;
return !paths.some(path => pathname === path || pathname.startsWith(`${path}/`)) ? req : false;
};
};
Combine these hooks to filter requests by allowing them for /public path, but denying them for /public/secret.
toSSG(app, fs, {
beforeRequestHook: [
filterPathsBeforeRequestHook(['/public']), // Allow only '/public'
denyPathsBeforeRequestHook(['/public/secret']) // Deny '/public/secret'
],
})
The author should do the following, if applicable
- [x] Add tests
- [x] Run tests
- [x]
bun denoifyto generate files for Deno - [x]
bun run format:fix && bun run lint:fixto format the code
Hi @watany-dev
Sorry to be late for my comment. I think this is good! But I want to know, is the main point of this PR that makes the hooks allow receiving the array?
export interface ToSSGOptions {
dir?: string
beforeRequestHook?: BeforeRequestHook | BeforeRequestHook[] // <---
afterResponseHook?: AfterResponseHook | AfterResponseHook[] // <---
afterGenerateHook?: AfterGenerateHook | AfterGenerateHook[] // <---
concurrency?: number
extensionMap?: Record<string, string>
}
I'd like to know the options of others. cc: @nakasyou @usualoma @sor4chi
Nice feature! This is the natural behavior expected by users and I hope it is supported.
Hey @watany-dev
I think there is no problem. It can be merged!