From 68011665fdaa42c5bfbf80903cbff1bccf98569c Mon Sep 17 00:00:00 2001 From: Kengo Watanabe <121782456+nabeken5@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:32:30 +0900 Subject: [PATCH] docs: Add JSDoc (#1916) * docs: Add JSDoc for App * docs: Add JSDoc for Routing * docs: Add JSDoc for Context * docs: Add JSDoc for HonoRequest * docs: Add JSDoc for Exception * add .header JSDoc & getPath JSDoc (`nabeken5:feat/add-jsdoc#1`) * add .header JSDoc & getPath JSDoc * fix: Apply the format to JSDoc --------- Thanks @EdamAme-x Co-authored-by: Kengo Watanabe <121782456+nabeken5@users.noreply.github.com> * refactor: format * feat: run denoify --------- Co-authored-by: Ame_x <121654029+EdamAme-x@users.noreply.github.com> --- deno_dist/context.ts | 177 ++++++++++++++++++++++++++++++++++++ deno_dist/hono-base.ts | 79 ++++++++++++++++ deno_dist/http-exception.ts | 18 ++++ deno_dist/request.ts | 161 ++++++++++++++++++++++++++++++++ src/context.ts | 177 ++++++++++++++++++++++++++++++++++++ src/hono-base.ts | 79 ++++++++++++++++ src/http-exception.ts | 18 ++++ src/request.ts | 161 ++++++++++++++++++++++++++++++++ 8 files changed, 870 insertions(+) diff --git a/deno_dist/context.ts b/deno_dist/context.ts index 728d15d9..44cbf375 100644 --- a/deno_dist/context.ts +++ b/deno_dist/context.ts @@ -95,10 +95,37 @@ export class Context< P extends string = any, I extends Input = {} > { + /** + * `.req` is the instance of {@link HonoRequest}. + */ req: HonoRequest
+ /** + * `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers. + * @example + * ```ts + * // Environment object for Cloudflare Workers + * app.get('*', async c => { + * const counter = c.env.COUNTER + * }) + * ``` + * @see https://hono.dev/api/context#env + */ env: E['Bindings'] = {} private _var: E['Variables'] = {} finalized: boolean = false + /** + * `.error` can get the error object from the middleware if the Handler throws an error. + * @example + * ```ts + * app.use('*', async (c, next) => { + * await next() + * if (c.error) { + * // do something... + * } + * }) + * ``` + * @see https://hono.dev/api/context#error + */ error: Error | undefined = undefined #status: StatusCode = 200 @@ -122,6 +149,9 @@ export class Context< } } + /** + * @see https://hono.dev/api/context#event + */ get event(): FetchEventLike { if (this.#executionCtx && 'respondWith' in this.#executionCtx) { return this.#executionCtx @@ -130,6 +160,9 @@ export class Context< } } + /** + * @see https://hono.dev/api/context#executionctx + */ get executionCtx(): ExecutionContext { if (this.#executionCtx) { return this.#executionCtx as ExecutionContext @@ -138,6 +171,9 @@ export class Context< } } + /** + * @see https://hono.dev/api/context#res + */ get res(): Response { this.#isFresh = false return (this.#res ||= new Response('404 Not Found', { status: 404 })) @@ -163,6 +199,40 @@ export class Context< this.finalized = true } + /** + * `.render()` can create a response within a layout. + * @example + * ```ts + * app.get('/', (c) => { + * return c.render('Hello!') + * }) + * ``` + * @see https://hono.dev/api/context#render-setrenderer + */ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-explicit-any + render: Renderer = (...args: any[]) => this.renderer(...args) + + /** + * `.setRenderer()` can set the layout in the custom middleware. + * @example + * ```tsx + * app.use('*', async (c, next) => { + * c.setRenderer((content) => { + * return c.html( + * + *
+ *{content}
+ * + * + * ) + * }) + * await next() + * }) + * ``` + * @see https://hono.dev/api/context#render-setrenderer + */ // @ts-expect-error It is unknown how many arguments the renderer will receive. // eslint-disable-next-line @typescript-eslint/no-explicit-any render: Renderer = (...args: any[]) => this.renderer(...args) @@ -174,6 +244,20 @@ export class Context< this.renderer = renderer } + /** + * `.header()` can set headers. + * @example + * ```ts + * app.get('/welcome', (c) => { + * // Set headers + * c.header('X-Message', 'Hello!') + * c.header('Content-Type', 'text/plain') + * + * return c.body('Thank you for coming') + * }) + * ``` + * @see https://hono.dev/api/context#body + */ header = (name: string, value: string | undefined, options?: { append?: boolean }): void => { // Clear the header if (value === undefined) { @@ -218,15 +302,46 @@ export class Context< this.#status = status } + /** + * `.set()` can set the value specified by the key. + * @example + * ```ts + * app.use('*', async (c, next) => { + * c.set('message', 'Hono is cool!!') + * await next() + * }) + * ``` + * @see https://hono.dev/api/context#set-get +``` + */ set: Set{ + /** + * `.raw` can get the raw Request object. + * @example + * ```ts + * // For Cloudflare Workers + * app.post('/', async (c) => { + * const metadata = c.req.raw.cf?.hostMetadata? + * ... + * }) + * ``` + * @see https://hono.dev/api/request#raw + */ raw: Request #validatedData: { [K in keyof ValidationTargets]?: {} } // Short name of validatedData #matchResult: Result<[unknown, RouterRoute]> routeIndex: number = 0 + /** + * `.path` can get the pathname of the request. + * @example + * ```ts + * app.get('/about/me', (c) => { + * const pathname = c.req.path // `/about/me` + * }) + * ``` + * @see https://hono.dev/api/request#path + */ path: string bodyCache: BodyCache = {} @@ -44,6 +66,16 @@ export class HonoRequest
{
this.#validatedData = {}
}
+ /**
+ * `.req.param()` gets the path parameters.
+ * @example
+ * ```ts
+ * const name = c.req.param('name')
+ * // or all parameters at once
+ * const { id, comment_id } = c.req.param()
+ * ```
+ * @see https://hono.dev/api/routing#path-parameter
+ */
param {
}
}
+ /**
+ * `.query()` can get querystring parameters.
+ * @example
+ * ```ts
+ * // Query params
+ * app.get('/search', (c) => {
+ * const query = c.req.query('q')
+ * })
+ *
+ * // Get all params at once
+ * app.get('/search', (c) => {
+ * const { q, limit, offset } = c.req.query()
+ * })
+ * ```
+ * @see https://hono.dev/api/request#query
+ */
query(key: string): string | undefined
query(): Record {
return headerData
}
+ /**
+ * `.parseBody()` can parse Request body of type `multipart/form-data` or `application/x-www-form-urlencoded`
+ * @example
+ * ```ts
+ * app.post('/entry', async (c) => {
+ * const body = await c.req.parseBody()
+ * })
+ * ```
+ * @see https://hono.dev/api/request#parsebody
+ */
async parseBody {
return (bodyCache[key] = raw[key]())
}
+ /**
+ * `.json()` can parse Request body of type `application/json`
+ * @example
+ * ```ts
+ * app.post('/entry', async (c) => {
+ * const body = await c.req.json()
+ * })
+ * ```
+ * @see https://hono.dev/api/request#json
+ */
json {
return this.#validatedData[target] as unknown
}
+ /**
+ * `.url()` can get the request url strings.
+ * @example
+ * ```ts
+ * app.get('/about/me', (c) => {
+ * const url = c.req.url // `http://localhost:8787/about/me`
+ * ...
+ * })
+ * ```
+ * @see https://hono.dev/api/request#url
+ */
get url() {
return this.raw.url
}
+ /**
+ * `.method()` can get the method name of the request.
+ * @example
+ * ```ts
+ * app.get('/about/me', (c) => {
+ * const method = c.req.method // `GET`
+ * })
+ * ```
+ * @see https://hono.dev/api/request#method
+ */
get method() {
return this.raw.method
}
+ /**
+ * `.matchedRoutes()` can return a matched route in the handler
+ * @example
+ * ```ts
+ * app.use('*', async function logger(c, next) {
+ * await next()
+ * c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
+ * const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')
+ * console.log(
+ * method,
+ * ' ',
+ * path,
+ * ' '.repeat(Math.max(10 - path.length, 0)),
+ * name,
+ * i === c.req.routeIndex ? '<- respond from here' : ''
+ * )
+ * })
+ * })
+ * ```
+ * @see https://hono.dev/api/request#matchedroutes
+ */
get matchedRoutes(): RouterRoute[] {
return this.#matchResult[0].map(([[, route]]) => route)
}
+ /**
+ * `routePath()` can retrieve the path registered within the handler
+ * @example
+ * ```ts
+ * app.get('/posts/:id', (c) => {
+ * return c.json({ path: c.req.routePath })
+ * })
+ * ```
+ * @see https://hono.dev/api/request#routepath
+ */
get routePath(): string {
return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path
}
diff --git a/src/context.ts b/src/context.ts
index 4048cd92..f5dc2525 100644
--- a/src/context.ts
+++ b/src/context.ts
@@ -95,10 +95,37 @@ export class Context<
P extends string = any,
I extends Input = {}
> {
+ /**
+ * `.req` is the instance of {@link HonoRequest}.
+ */
req: HonoRequest
+ /**
+ * `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers.
+ * @example
+ * ```ts
+ * // Environment object for Cloudflare Workers
+ * app.get('*', async c => {
+ * const counter = c.env.COUNTER
+ * })
+ * ```
+ * @see https://hono.dev/api/context#env
+ */
env: E['Bindings'] = {}
private _var: E['Variables'] = {}
finalized: boolean = false
+ /**
+ * `.error` can get the error object from the middleware if the Handler throws an error.
+ * @example
+ * ```ts
+ * app.use('*', async (c, next) => {
+ * await next()
+ * if (c.error) {
+ * // do something...
+ * }
+ * })
+ * ```
+ * @see https://hono.dev/api/context#error
+ */
error: Error | undefined = undefined
#status: StatusCode = 200
@@ -122,6 +149,9 @@ export class Context<
}
}
+ /**
+ * @see https://hono.dev/api/context#event
+ */
get event(): FetchEventLike {
if (this.#executionCtx && 'respondWith' in this.#executionCtx) {
return this.#executionCtx
@@ -130,6 +160,9 @@ export class Context<
}
}
+ /**
+ * @see https://hono.dev/api/context#executionctx
+ */
get executionCtx(): ExecutionContext {
if (this.#executionCtx) {
return this.#executionCtx as ExecutionContext
@@ -138,6 +171,9 @@ export class Context<
}
}
+ /**
+ * @see https://hono.dev/api/context#res
+ */
get res(): Response {
this.#isFresh = false
return (this.#res ||= new Response('404 Not Found', { status: 404 }))
@@ -163,6 +199,40 @@ export class Context<
this.finalized = true
}
+ /**
+ * `.render()` can create a response within a layout.
+ * @example
+ * ```ts
+ * app.get('/', (c) => {
+ * return c.render('Hello!')
+ * })
+ * ```
+ * @see https://hono.dev/api/context#render-setrenderer
+ */
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ render: Renderer = (...args: any[]) => this.renderer(...args)
+
+ /**
+ * `.setRenderer()` can set the layout in the custom middleware.
+ * @example
+ * ```tsx
+ * app.use('*', async (c, next) => {
+ * c.setRenderer((content) => {
+ * return c.html(
+ *
+ * {content} {
+ /**
+ * `.raw` can get the raw Request object.
+ * @example
+ * ```ts
+ * // For Cloudflare Workers
+ * app.post('/', async (c) => {
+ * const metadata = c.req.raw.cf?.hostMetadata?
+ * ...
+ * })
+ * ```
+ * @see https://hono.dev/api/request#raw
+ */
raw: Request
#validatedData: { [K in keyof ValidationTargets]?: {} } // Short name of validatedData
#matchResult: Result<[unknown, RouterRoute]>
routeIndex: number = 0
+ /**
+ * `.path` can get the pathname of the request.
+ * @example
+ * ```ts
+ * app.get('/about/me', (c) => {
+ * const pathname = c.req.path // `/about/me`
+ * })
+ * ```
+ * @see https://hono.dev/api/request#path
+ */
path: string
bodyCache: BodyCache = {}
@@ -44,6 +66,16 @@ export class HonoRequest {
this.#validatedData = {}
}
+ /**
+ * `.req.param()` gets the path parameters.
+ * @example
+ * ```ts
+ * const name = c.req.param('name')
+ * // or all parameters at once
+ * const { id, comment_id } = c.req.param()
+ * ```
+ * @see https://hono.dev/api/routing#path-parameter
+ */
param {
}
}
+ /**
+ * `.query()` can get querystring parameters.
+ * @example
+ * ```ts
+ * // Query params
+ * app.get('/search', (c) => {
+ * const query = c.req.query('q')
+ * })
+ *
+ * // Get all params at once
+ * app.get('/search', (c) => {
+ * const { q, limit, offset } = c.req.query()
+ * })
+ * ```
+ * @see https://hono.dev/api/request#query
+ */
query(key: string): string | undefined
query(): Record {
return headerData
}
+ /**
+ * `.parseBody()` can parse Request body of type `multipart/form-data` or `application/x-www-form-urlencoded`
+ * @example
+ * ```ts
+ * app.post('/entry', async (c) => {
+ * const body = await c.req.parseBody()
+ * })
+ * ```
+ * @see https://hono.dev/api/request#parsebody
+ */
async parseBody {
return (bodyCache[key] = raw[key]())
}
+ /**
+ * `.json()` can parse Request body of type `application/json`
+ * @example
+ * ```ts
+ * app.post('/entry', async (c) => {
+ * const body = await c.req.json()
+ * })
+ * ```
+ * @see https://hono.dev/api/request#json
+ */
json {
return this.#validatedData[target] as unknown
}
+ /**
+ * `.url()` can get the request url strings.
+ * @example
+ * ```ts
+ * app.get('/about/me', (c) => {
+ * const url = c.req.url // `http://localhost:8787/about/me`
+ * ...
+ * })
+ * ```
+ * @see https://hono.dev/api/request#url
+ */
get url() {
return this.raw.url
}
+ /**
+ * `.method()` can get the method name of the request.
+ * @example
+ * ```ts
+ * app.get('/about/me', (c) => {
+ * const method = c.req.method // `GET`
+ * })
+ * ```
+ * @see https://hono.dev/api/request#method
+ */
get method() {
return this.raw.method
}
+ /**
+ * `.matchedRoutes()` can return a matched route in the handler
+ * @example
+ * ```ts
+ * app.use('*', async function logger(c, next) {
+ * await next()
+ * c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
+ * const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')
+ * console.log(
+ * method,
+ * ' ',
+ * path,
+ * ' '.repeat(Math.max(10 - path.length, 0)),
+ * name,
+ * i === c.req.routeIndex ? '<- respond from here' : ''
+ * )
+ * })
+ * })
+ * ```
+ * @see https://hono.dev/api/request#matchedroutes
+ */
get matchedRoutes(): RouterRoute[] {
return this.#matchResult[0].map(([[, route]]) => route)
}
+ /**
+ * `routePath()` can retrieve the path registered within the handler
+ * @example
+ * ```ts
+ * app.get('/posts/:id', (c) => {
+ * return c.json({ path: c.req.routePath })
+ * })
+ * ```
+ * @see https://hono.dev/api/request#routepath
+ */
get routePath(): string {
return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path
}