86 lines
2.9 KiB
TypeScript
86 lines
2.9 KiB
TypeScript
import { createServer, Server } from 'http';
|
|
import { parse as urlParse } from 'url';
|
|
import { open, close, readFile, fstat } from 'fs';
|
|
import { parse, join } from 'path';
|
|
|
|
namespace server {
|
|
export const mimes = {
|
|
'.html': 'text/html',
|
|
'.ico': 'image/x-icon',
|
|
'.js': 'text/javascript',
|
|
'.json': 'application/json',
|
|
'.css': 'text/css',
|
|
'.png': 'image/png',
|
|
'.jpg': 'image/jpeg',
|
|
'.webp': 'image/webp',
|
|
};
|
|
}
|
|
|
|
class Jean {
|
|
private workingDir: string;
|
|
|
|
/**
|
|
* Jean static file server its only purpose is serving SPA and images
|
|
* with the lowest impact possible.
|
|
* @param workingDir sets the root directory automatically trying index.html
|
|
* If specified the file in addition to the directory it will serve the
|
|
* file directly.
|
|
* *e.g* new Jean(path.join(__dirname, 'dist')) will try
|
|
* index.html from the dist directory;
|
|
* @author me :D
|
|
*/
|
|
|
|
constructor(workingDir: string) {
|
|
this.workingDir = workingDir;
|
|
}
|
|
|
|
/**
|
|
* Create a static file server
|
|
* @returns an instance of a standard NodeJS http.Server
|
|
*/
|
|
public createServer(): Server {
|
|
return createServer((req, res) => {
|
|
// parse the current given url
|
|
const parsedUrl = urlParse(req.url, false)
|
|
// extract the pathname and guard it with the working dir
|
|
let pathname = join(this.workingDir, `.${parsedUrl.pathname}`);
|
|
// extract the file extension
|
|
const ext = parse(pathname).ext;
|
|
|
|
// open the file or directory and fetch its descriptor
|
|
open(pathname, 'r', (err, fd) => {
|
|
// whoops, not found, send a 404
|
|
if (err) {
|
|
res.statusCode = 404;
|
|
res.end(`File ${pathname} not found!`);
|
|
return;
|
|
}
|
|
// something's gone wrong it's not a file or a directory
|
|
fstat(fd, (err, stat) => {
|
|
if (err) {
|
|
res.statusCode = 500;
|
|
res.end(err);
|
|
}
|
|
// try file index.html
|
|
if (stat.isDirectory()) {
|
|
pathname = join(pathname, 'index.html')
|
|
}
|
|
// read the file
|
|
readFile(pathname, (err, data) => {
|
|
if (err) {
|
|
res.statusCode = 500;
|
|
res.end(`Error reading the file: ${err}`);
|
|
} else {
|
|
// infer it's extension otherwise it's the index.html
|
|
res.setHeader('Content-type', server.mimes[ext] || 'text/html');
|
|
res.end(data);
|
|
close(fd);
|
|
}
|
|
});
|
|
})
|
|
});
|
|
})
|
|
}
|
|
}
|
|
|
|
export default Jean; |