diff --git a/README.md b/README.md index b87c7c8..ae62173 100755 --- a/README.md +++ b/README.md @@ -7,24 +7,26 @@ A simple router for Deno. ## Usage ### Basics + ```ts -import Router from '@bearmetal/router'; +import Router from "@bearmetal/router"; const router = new Router(); router - .route('/users') - .get((ctx) => { - return new Response('GET /users'); + .route("/users") + .get((req, ctx) => { + return new Response("GET /users"); }) - .post((ctx) => { - return new Response('POST /users'); + .post((req, ctx) => { + return new Response("POST /users"); }); -Deno.serve(router.handle) +Deno.serve(router.handle); ``` ### Middleware + ```ts ... @@ -37,6 +39,7 @@ router.use('/users', async (ctx, next) => { ``` ### Nested Routers + ```ts ... @@ -56,3 +59,45 @@ router.use('/users', nestedRouter); ... ``` +### Static Files + +```ts +... +router.serveDirectory('dirname', '/url-root') // files from 'dirname' directory will be available at '/url-root/filename' + +// To automatically locate index.html pages: +router.serveDirectory('dirWithIndexHtml', '/indexes', {showIndex: true}); +// Will also generate an index if there is no index.html present in the directory +... +``` + +### File-based Routing + +```ts +import { FileRouter } from "@bearmetal/router"; + +const router = new FileRouter("dirname"); +Deno.listen(router.handle); + +// dirname/index.ts - will be accessible at '/' +export default function (req, ctx) { + return new Response("Hello, world!"); +} + +// dirname/methods.ts - will be accessible at '/methods' +export const handlers = { + get() { + return new Response("Hello, world"); + }, + post(req, ctx) { + const data = doDataOp(req.body); + return new Response(data); + }, +}; + +// dirname/nestedRouter.ts - will be accessible at '/nestedRouter' +import { router } from "@bearmetal/router"; + +const router = new Router(); +export default router; +``` diff --git a/deno.json b/deno.json index e0b44b4..2c50863 100755 --- a/deno.json +++ b/deno.json @@ -1,9 +1,8 @@ { "name": "@bearmetal/router", "description": "A simple router for Deno", - "version": "0.1.3", + "version": "0.2.0", "stable": true, - "repository": "https://github.com/emmaos/bearmetal", "files": [ "mod.ts", "README.md", @@ -13,8 +12,11 @@ ".": "./mod.ts", "./types": "./types.ts" }, + "exclude": [ + ".vscode/" + ], "imports": { "@std/assert": "jsr:@std/assert@^1.0.7", "@std/testing": "jsr:@std/testing@^1.0.4" } -} +} \ No newline at end of file diff --git a/file_router.ts b/file_router.ts index 63d0258..b053cd7 100755 --- a/file_router.ts +++ b/file_router.ts @@ -1,6 +1,5 @@ import Router from "./router.ts"; import type { Handler, RouteConfigurator } from "./types.ts"; -import { NotFound } from "./util/response.ts"; function crawl(dir: string, callback: (path: string) => void) { for (const entry of Deno.readDirSync(dir)) { @@ -15,6 +14,15 @@ function crawl(dir: string, callback: (path: string) => void) { } } +/** + * @example + * ```ts + * import {FileRouter} from "@bearmetal/router" + * + * const router = new FileRouter("dirName"); + * Deno.listen(router.handle); + * ``` + */ export class FileRouter extends Router { constructor(root: string) { super(); @@ -22,16 +30,16 @@ export class FileRouter extends Router { let relativePath = path.replace(root, ""); if (path.endsWith(".ts") || path.endsWith(".js")) { relativePath = relativePath.replace(/\.[tj]s/, ""); - const asdf = await import(path); + const handlers = await import(path); - if (asdf.default) { - asdf.default instanceof Router - ? this.use(relativePath, asdf.default) - : this.route(relativePath).get(asdf.default); + if (handlers.default) { + handlers.default instanceof Router + ? this.use(relativePath, handlers.default) + : this.route(relativePath).get(handlers.default); } - if (asdf.handlers) { - for (const [method, handler] of Object.entries(asdf.handlers)) { + if (handlers.handlers) { + for (const [method, handler] of Object.entries(handlers.handlers)) { this.route(relativePath)[method as keyof RouteConfigurator]( handler as Handler, ); @@ -48,314 +56,11 @@ export class FileRouter extends Router { } }); } - - serveDirectory(dir: string, root: string, opts?: { showIndex: boolean }) { - this.route(root + "*").get(async (_req, ctx) => { - const { showIndex } = opts ?? { showIndex: false }; - - let normalizedPath = (dir + "/" + - ctx.url.pathname.replace(new RegExp("^" + root), "")).trim().replace( - "//", - "/", - ); - - normalizedPath = normalizedPath.replace( - /\/\s?$/, - "", - ); - - let fileInfo: Deno.FileInfo; - - try { - fileInfo = await Deno.stat(normalizedPath); - } catch (error) { - if (error instanceof Deno.errors.NotFound) { - return NotFound(); - } else { - throw error; - } - } - - if (fileInfo.isDirectory) { - if (!showIndex) return NotFound(); - normalizedPath += "/index.html"; - } - - try { - const file = await Deno.readFile(normalizedPath); - return new Response(file); - } catch (e) { - if (e instanceof Deno.errors.NotFound) { - return showIndex ? generateIndex(normalizedPath) : NotFound(); - } - throw e; - } - }); - } -} - -async function generateIndex(dir: string) { - dir = dir.replace(/index\.html$/, ""); - const items: Deno.DirEntry[] = []; - - for await (const entry of Deno.readDir(dir)) { - items.push(entry); - } - - const fileIcon = ` - -`; - const folderIcon = ` - -`; - - const template = ` - - -
- - -