Skip to content

Commit

Permalink
feat: serve static assets
Browse files Browse the repository at this point in the history
  • Loading branch information
Sorikairox committed Dec 26, 2023
1 parent e8aa447 commit 5c41e9e
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 9 deletions.
3 changes: 2 additions & 1 deletion src/app.ts
@@ -1,4 +1,4 @@
import { Application, serveStatic, MiddlewareHandler } from './deps.ts';
import { Application, MiddlewareHandler } from './deps.ts';
import { FilterExecutor } from './exception/filter/executor.ts';
import { GuardExecutor } from './guard/executor.ts';
import { HookExecutor } from './hook/executor.ts';
Expand All @@ -14,6 +14,7 @@ import { Constructor } from './utils/constructor.ts';
import { PossibleMiddlewareType } from './router/middleware/decorator.ts';
import { globalMiddlewareContainer } from './router/middleware/global-container.ts';
import { ModuleConstructor } from './module/constructor.ts';
import { serveStatic } from './utils/serve-static.ts';
import { cors } from 'https://deno.land/x/hono/middleware.ts'

type CORSOptions = {
Expand Down
3 changes: 1 addition & 2 deletions src/deps.ts
Expand Up @@ -6,5 +6,4 @@ export {
} from 'https://deno.land/std@0.135.0/fmt/colors.ts';
export { Reflect } from 'https://deno.land/x/deno_reflect@v0.2.1/mod.ts';
export { validateObject } from '../validation.ts';
export { Hono as Application, type Context, type MiddlewareHandler } from 'https://deno.land/x/hono/mod.ts'
export { serveStatic } from 'https://deno.land/x/hono/middleware.ts'
export { Hono as Application, type Context, type MiddlewareHandler, type Next } from 'https://deno.land/x/hono/mod.ts'
37 changes: 37 additions & 0 deletions src/utils/filepath.ts
@@ -0,0 +1,37 @@
type FilePathOptions = {
filename: string
root?: string
defaultDocument?: string
}

export const getFilePath = (options: FilePathOptions): string | undefined => {
let filename = options.filename
if (/(?:^|[\/\\])\.\.(?:$|[\/\\])/.test(filename)) return

let root = options.root || ''
const defaultDocument = options.defaultDocument || 'index.html'

if (filename.endsWith('/')) {
// /top/ => /top/index.html
filename = filename.concat(defaultDocument)
} else if (!filename.match(/\.[a-zA-Z0-9]+$/)) {
// /top => /top/index.html
filename = filename.concat('/' + defaultDocument)
}

// /foo.html => foo.html
filename = filename.replace(/^\.?[\/\\]/, '')

// foo\bar.txt => foo/bar.txt
filename = filename.replace(/\\/, '/')

// assets/ => assets
root = root.replace(/\/$/, '')

// ./assets/foo.html => assets/foo.html
let path = root ? root + '/' + filename : filename
path = path.replace(/^\.?\//, '')

return path
}

93 changes: 93 additions & 0 deletions src/utils/get-mime.ts
@@ -0,0 +1,93 @@
export const getMimeType = (filename: string): string | undefined => {
const regexp = /\.([a-zA-Z0-9]+?)$/
const match = filename.match(regexp)
if (!match) return
let mimeType = mimes[match[1]]
if ((mimeType && mimeType.startsWith('text')) || mimeType === 'application/json') {
mimeType += '; charset=utf-8'
}
return mimeType
}

const mimes: Record<string, string> = {
aac: 'audio/aac',
abw: 'application/x-abiword',
arc: 'application/x-freearc',
avi: 'video/x-msvideo',
avif: 'image/avif',
av1: 'video/av1',
azw: 'application/vnd.amazon.ebook',
bin: 'application/octet-stream',
bmp: 'image/bmp',
bz: 'application/x-bzip',
bz2: 'application/x-bzip2',
csh: 'application/x-csh',
css: 'text/css',
csv: 'text/csv',
doc: 'application/msword',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
eot: 'application/vnd.ms-fontobject',
epub: 'application/epub+zip',
gif: 'image/gif',
gz: 'application/gzip',
htm: 'text/html',
html: 'text/html',
ico: 'image/x-icon',
ics: 'text/calendar',
jar: 'application/java-archive',
jpeg: 'image/jpeg',
jpg: 'image/jpeg',
js: 'text/javascript',
json: 'application/json',
jsonld: 'application/ld+json',
map: 'application/json',
mid: 'audio/x-midi',
midi: 'audio/x-midi',
mjs: 'text/javascript',
mp3: 'audio/mpeg',
mp4: 'video/mp4',
mpeg: 'video/mpeg',
mpkg: 'application/vnd.apple.installer+xml',
odp: 'application/vnd.oasis.opendocument.presentation',
ods: 'application/vnd.oasis.opendocument.spreadsheet',
odt: 'application/vnd.oasis.opendocument.text',
oga: 'audio/ogg',
ogv: 'video/ogg',
ogx: 'application/ogg',
opus: 'audio/opus',
otf: 'font/otf',
pdf: 'application/pdf',
php: 'application/php',
png: 'image/png',
ppt: 'application/vnd.ms-powerpoint',
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
rtf: 'application/rtf',
sh: 'application/x-sh',
svg: 'image/svg+xml',
swf: 'application/x-shockwave-flash',
tar: 'application/x-tar',
tif: 'image/tiff',
tiff: 'image/tiff',
ts: 'video/mp2t',
ttf: 'font/ttf',
txt: 'text/plain',
vsd: 'application/vnd.visio',
wasm: 'application/wasm',
webm: 'video/webm',
weba: 'audio/webm',
webp: 'image/webp',
woff: 'font/woff',
woff2: 'font/woff2',
xhtml: 'application/xhtml+xml',
xls: 'application/vnd.ms-excel',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
xml: 'application/xml',
xul: 'application/vnd.mozilla.xul+xml',
zip: 'application/zip',
'3gp': 'video/3gpp',
'3g2': 'video/3gpp2',
'7z': 'application/x-7z-compressed',
gltf: 'model/gltf+json',
glb: 'model/gltf-binary',
}

13 changes: 7 additions & 6 deletions src/utils/serve-static.ts
@@ -1,5 +1,6 @@
import type { Context, Next, getFilepath, getMimeType } from '../deps.ts'

import type { Context, Next } from '../deps.ts'
import { getFilePath } from './filepath.ts'
import { getMimeType } from './get-mime.ts'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const { open } = Deno
Expand All @@ -23,13 +24,13 @@ export const serveStatic = (options: ServeStaticOptions = { root: '' }) => {
let path = getFilePath({
filename: options.rewriteRequestPath ? options.rewriteRequestPath(filename) : filename,
root: options.root,
defaultDocument: DEFAULT_DOCUMENT,
defaultDocument: 'index.html',
})

if (!path) return await next()
path = `./${path}`

path = `/${path}`;

let file

try {
Expand Down

0 comments on commit 5c41e9e

Please sign in to comment.