hono icon indicating copy to clipboard operation
hono copied to clipboard

feat(ssg): enhance conbined hooks

Open watany-dev opened this issue 1 year ago • 2 comments

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 denoify to generate files for Deno
  • [x] bun run format:fix && bun run lint:fix to format the code

watany-dev avatar May 16 '24 08:05 watany-dev

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

yusukebe avatar May 22 '24 15:05 yusukebe

Nice feature! This is the natural behavior expected by users and I hope it is supported.

usualoma avatar May 22 '24 20:05 usualoma

Hey @watany-dev

I think there is no problem. It can be merged!

yusukebe avatar May 24 '24 08:05 yusukebe