├── .editorconfig
├── .gitignore
├── .prettierignore
├── .prettierrc
├── license
├── package.json
├── readme.md
├── src
├── bin.js
├── index.js
├── injectCode.js
├── listen.js
├── logMessage.js
├── mimeTypes.js
└── utils
│ ├── addClient.js
│ ├── error.js
│ ├── getFilePath.js
│ ├── getIp.js
│ ├── index.js
│ ├── injectContent.js
│ ├── log.js
│ ├── mimeType.js
│ ├── setRoot.js
│ ├── show404.js
│ ├── showError.js
│ ├── showFile.js
│ └── styles.js
└── test
├── favicon.ico
├── images
├── apple-touch-icon.png
├── google-touch-icon.png
├── icon.svg
└── mask-icon.svg
├── index.html
├── manifest.json
└── scripts
└── main.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = tab
5 | indent_size = 4
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.yml]
12 | indent_style = space
13 | indent_size = 2
14 |
15 | [*.md]
16 | indent_style = space
17 | trim_trailing_whitespace = false
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | package-lock.json
4 | yarn.lock
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | test
2 | src/mimeTypes.js
3 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "none",
4 | "arrowParens": "avoid",
5 | "printWidth": 90
6 | }
7 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) Antoine Boulanger (https://github.com/ABXlink)
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted, provided that the above
7 | copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-serve",
3 | "version": "1.0.1",
4 | "description": "🍛 Ultralight http server with live reload. [CLI + API]",
5 | "repository": "nativew/serve",
6 | "author": "Antoine Boulanger (https://github.com/antoineboulanger)",
7 | "license": "ISC",
8 | "exports": "./src/index.js",
9 | "main": "src/index.js",
10 | "type": "module",
11 | "bin": {
12 | "create-serve": "src/bin.js"
13 | },
14 | "scripts": {
15 | "format": "prettier --write --ignore-unknown '**/*'",
16 | "start": "node src/bin.js test"
17 | },
18 | "devDependencies": {
19 | "prettier": "^2.2.1"
20 | },
21 | "files": [
22 | "src"
23 | ],
24 | "keywords": [
25 | "serve",
26 | "cli",
27 | "command-line",
28 | "http",
29 | "dev",
30 | "development",
31 | "server",
32 | "livereload",
33 | "live",
34 | "reload",
35 | "hot",
36 | "refresh"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Serve 🍛
4 |
5 | Ultralight http server with live reload.
6 | CLI + API
7 |
8 |
9 |
10 | ### Simple CLI and API
11 |
12 | ### With live reload
13 |
14 | ### Light and modern
15 |
16 | ### No dependencies
17 |
18 |
19 |
20 | ### One command
21 |
22 | ```zsh
23 | npm init serve
24 | ```
25 |
26 |
27 |
28 | ### Or one function
29 |
30 | ```js
31 | import serve from 'create-serve';
32 |
33 | serve.start();
34 | ```
35 |
36 |
37 |
38 | ### To start 🍛
39 |
40 |
41 |
42 | ### CLI
43 |
44 | By default, it serves `public` if the folder exists, otherwise root `/`.
45 | Or you can specify a different folder.
46 |
47 | ```zsh
48 | npm init serve [folder]
49 | ```
50 |
51 |
52 |
53 | ### API
54 |
55 | ```js
56 | import serve from 'create-serve';
57 |
58 | serve.start({
59 | port: 7000,
60 | root: '.',
61 | live: true
62 | });
63 | ```
64 |
65 |
66 |
67 | ### Live reload
68 |
69 | ```js
70 | serve.update();
71 | ```
72 |
73 |
74 |
75 | ### Use any file watcher
76 |
77 |
78 |
79 | [Chokidar](https://github.com/paulmillr/chokidar)
80 |
81 | ```js
82 | import serve from 'create-serve';
83 | import chokidar from 'chokidar';
84 |
85 | serve.start();
86 |
87 | chokidar.watch('.').on('change', () => {
88 | serve.update();
89 | });
90 | ```
91 |
92 |
93 |
94 | [esbuild](https://esbuild.github.io/api/#watch)
95 |
96 | Use the official wrapper for esbuild's watch → [esbuild-serve](https://github.com/nativew/esbuild-serve)
97 |
98 |
99 |
100 | ### Log
101 |
102 | Import the util functions to log updates with colours.
103 |
104 | ```js
105 | import serve, { error, log } from 'create-serve';
106 |
107 | serve.update();
108 |
109 | hasError
110 | ? error('× Failed') // Red
111 | : log('✓ Updated'); // Green
112 | ```
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/src/bin.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import { start } from './index.js';
3 |
4 | const root = process.argv[2];
5 |
6 | start({ live: false, root });
7 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import http from 'http';
2 | import fs from 'fs';
3 | import path from 'path';
4 | import listen from './listen.js';
5 | import {
6 | addClient,
7 | error,
8 | getFilePath,
9 | injectContent,
10 | log,
11 | mimeType,
12 | setRoot,
13 | show404,
14 | showError,
15 | showFile
16 | } from './utils/index.js';
17 |
18 | export let options = {
19 | port: 7000,
20 | root: '.',
21 | live: true
22 | };
23 |
24 | export const defaultRoot = './public';
25 | export const encoding = 'utf-8';
26 | export const eventSource = '/create-serve';
27 | export const clients = [];
28 |
29 | export const start = (startOptions = {}) => {
30 | Object.assign(options, startOptions);
31 | options.root = setRoot();
32 |
33 | const { live } = options;
34 | const server = http.createServer((request, response) => {
35 | if (live && request.url == eventSource) {
36 | const client = addClient(response);
37 |
38 | return clients.push(client);
39 | }
40 |
41 | const filePath = getFilePath(request);
42 | const extension = path.extname(filePath).toLowerCase().slice(1);
43 | const contentType = mimeType(extension) || 'application/octet-stream';
44 | const isHtml = contentType == 'text/html';
45 | const encode = isHtml ? 'utf8' : null;
46 |
47 | fs.readFile(filePath, encode, (error, content) => {
48 | if (error) {
49 | if (error.code == 'ENOENT') {
50 | return show404(response);
51 | }
52 |
53 | return showError(response, error);
54 | }
55 |
56 | if (live && isHtml) content = injectContent(content);
57 |
58 | return showFile(response, content, contentType);
59 | });
60 | });
61 |
62 | listen(server);
63 | };
64 |
65 | export const update = () => {
66 | clients.forEach(response => response.write('data: update\n\n'));
67 | clients.length = 0;
68 | };
69 |
70 | export { error, log };
71 | export default { start, update };
72 |
--------------------------------------------------------------------------------
/src/injectCode.js:
--------------------------------------------------------------------------------
1 | import { eventSource } from './index.js';
2 |
3 | const injectCode = () => `
4 |
7 | `;
8 |
9 | export default injectCode;
10 |
--------------------------------------------------------------------------------
/src/listen.js:
--------------------------------------------------------------------------------
1 | import logMessage from './logMessage.js';
2 | import { options } from './index.js';
3 |
4 | const listen = (server, serverPort = options.port) => {
5 | server
6 | .listen(serverPort, () => {
7 | const currentPort = server.address().port;
8 |
9 | logMessage(currentPort);
10 | })
11 | .once('error', () => {
12 | server.removeAllListeners('listening');
13 | listen(server, 0);
14 | });
15 | };
16 |
17 | export default listen;
18 |
--------------------------------------------------------------------------------
/src/logMessage.js:
--------------------------------------------------------------------------------
1 | import { log, error, getIp } from './utils/index.js';
2 | import { options } from './index.js';
3 |
4 | const logMessage = currentPort => {
5 | const { port } = options;
6 |
7 | log('\nServing 🍛\n');
8 | log(`Local → http://localhost:${currentPort}\n`);
9 | log(`Network → http://${getIp()}:${currentPort}\n`);
10 | if (currentPort != port) error(`Port ${port} was in use.\n`);
11 | };
12 |
13 | export default logMessage;
14 |
--------------------------------------------------------------------------------
/src/mimeTypes.js:
--------------------------------------------------------------------------------
1 | export default {"application/andrew-inset":["ez"],"application/applixware":["aw"],"application/atom+xml":["atom"],"application/atomcat+xml":["atomcat"],"application/atomdeleted+xml":["atomdeleted"],"application/atomsvc+xml":["atomsvc"],"application/atsc-dwd+xml":["dwd"],"application/atsc-held+xml":["held"],"application/atsc-rsat+xml":["rsat"],"application/bdoc":["bdoc"],"application/calendar+xml":["xcs"],"application/ccxml+xml":["ccxml"],"application/cdfx+xml":["cdfx"],"application/cdmi-capability":["cdmia"],"application/cdmi-container":["cdmic"],"application/cdmi-domain":["cdmid"],"application/cdmi-object":["cdmio"],"application/cdmi-queue":["cdmiq"],"application/cu-seeme":["cu"],"application/dash+xml":["mpd"],"application/davmount+xml":["davmount"],"application/docbook+xml":["dbk"],"application/dssc+der":["dssc"],"application/dssc+xml":["xdssc"],"application/ecmascript":["ecma","es"],"application/emma+xml":["emma"],"application/emotionml+xml":["emotionml"],"application/epub+zip":["epub"],"application/exi":["exi"],"application/fdt+xml":["fdt"],"application/font-tdpfr":["pfr"],"application/geo+json":["geojson"],"application/gml+xml":["gml"],"application/gpx+xml":["gpx"],"application/gxf":["gxf"],"application/gzip":["gz"],"application/hjson":["hjson"],"application/hyperstudio":["stk"],"application/inkml+xml":["ink","inkml"],"application/ipfix":["ipfix"],"application/its+xml":["its"],"application/java-archive":["jar","war","ear"],"application/java-serialized-object":["ser"],"application/java-vm":["class"],"application/javascript":["js","mjs"],"application/json":["json","map"],"application/json5":["json5"],"application/jsonml+json":["jsonml"],"application/ld+json":["jsonld"],"application/lgr+xml":["lgr"],"application/lost+xml":["lostxml"],"application/mac-binhex40":["hqx"],"application/mac-compactpro":["cpt"],"application/mads+xml":["mads"],"application/manifest+json":["webmanifest"],"application/marc":["mrc"],"application/marcxml+xml":["mrcx"],"application/mathematica":["ma","nb","mb"],"application/mathml+xml":["mathml"],"application/mbox":["mbox"],"application/mediaservercontrol+xml":["mscml"],"application/metalink+xml":["metalink"],"application/metalink4+xml":["meta4"],"application/mets+xml":["mets"],"application/mmt-aei+xml":["maei"],"application/mmt-usd+xml":["musd"],"application/mods+xml":["mods"],"application/mp21":["m21","mp21"],"application/mp4":["mp4s","m4p"],"application/mrb-consumer+xml":["*xdf"],"application/mrb-publish+xml":["*xdf"],"application/msword":["doc","dot"],"application/mxf":["mxf"],"application/n-quads":["nq"],"application/n-triples":["nt"],"application/node":["cjs"],"application/octet-stream":["bin","dms","lrf","mar","so","dist","distz","pkg","bpk","dump","elc","deploy","exe","dll","deb","dmg","iso","img","msi","msp","msm","buffer"],"application/oda":["oda"],"application/oebps-package+xml":["opf"],"application/ogg":["ogx"],"application/omdoc+xml":["omdoc"],"application/onenote":["onetoc","onetoc2","onetmp","onepkg"],"application/oxps":["oxps"],"application/p2p-overlay+xml":["relo"],"application/patch-ops-error+xml":["*xer"],"application/pdf":["pdf"],"application/pgp-encrypted":["pgp"],"application/pgp-signature":["asc","sig"],"application/pics-rules":["prf"],"application/pkcs10":["p10"],"application/pkcs7-mime":["p7m","p7c"],"application/pkcs7-signature":["p7s"],"application/pkcs8":["p8"],"application/pkix-attr-cert":["ac"],"application/pkix-cert":["cer"],"application/pkix-crl":["crl"],"application/pkix-pkipath":["pkipath"],"application/pkixcmp":["pki"],"application/pls+xml":["pls"],"application/postscript":["ai","eps","ps"],"application/provenance+xml":["provx"],"application/pskc+xml":["pskcxml"],"application/raml+yaml":["raml"],"application/rdf+xml":["rdf","owl"],"application/reginfo+xml":["rif"],"application/relax-ng-compact-syntax":["rnc"],"application/resource-lists+xml":["rl"],"application/resource-lists-diff+xml":["rld"],"application/rls-services+xml":["rs"],"application/route-apd+xml":["rapd"],"application/route-s-tsid+xml":["sls"],"application/route-usd+xml":["rusd"],"application/rpki-ghostbusters":["gbr"],"application/rpki-manifest":["mft"],"application/rpki-roa":["roa"],"application/rsd+xml":["rsd"],"application/rss+xml":["rss"],"application/rtf":["rtf"],"application/sbml+xml":["sbml"],"application/scvp-cv-request":["scq"],"application/scvp-cv-response":["scs"],"application/scvp-vp-request":["spq"],"application/scvp-vp-response":["spp"],"application/sdp":["sdp"],"application/senml+xml":["senmlx"],"application/sensml+xml":["sensmlx"],"application/set-payment-initiation":["setpay"],"application/set-registration-initiation":["setreg"],"application/shf+xml":["shf"],"application/sieve":["siv","sieve"],"application/smil+xml":["smi","smil"],"application/sparql-query":["rq"],"application/sparql-results+xml":["srx"],"application/srgs":["gram"],"application/srgs+xml":["grxml"],"application/sru+xml":["sru"],"application/ssdl+xml":["ssdl"],"application/ssml+xml":["ssml"],"application/swid+xml":["swidtag"],"application/tei+xml":["tei","teicorpus"],"application/thraud+xml":["tfi"],"application/timestamped-data":["tsd"],"application/toml":["toml"],"application/ttml+xml":["ttml"],"application/ubjson":["ubj"],"application/urc-ressheet+xml":["rsheet"],"application/urc-targetdesc+xml":["td"],"application/voicexml+xml":["vxml"],"application/wasm":["wasm"],"application/widget":["wgt"],"application/winhlp":["hlp"],"application/wsdl+xml":["wsdl"],"application/wspolicy+xml":["wspolicy"],"application/xaml+xml":["xaml"],"application/xcap-att+xml":["xav"],"application/xcap-caps+xml":["xca"],"application/xcap-diff+xml":["xdf"],"application/xcap-el+xml":["xel"],"application/xcap-error+xml":["xer"],"application/xcap-ns+xml":["xns"],"application/xenc+xml":["xenc"],"application/xhtml+xml":["xhtml","xht"],"application/xliff+xml":["xlf"],"application/xml":["xml","xsl","xsd","rng"],"application/xml-dtd":["dtd"],"application/xop+xml":["xop"],"application/xproc+xml":["xpl"],"application/xslt+xml":["*xsl","xslt"],"application/xspf+xml":["xspf"],"application/xv+xml":["mxml","xhvml","xvml","xvm"],"application/yang":["yang"],"application/yin+xml":["yin"],"application/zip":["zip"],"audio/3gpp":["*3gpp"],"audio/adpcm":["adp"],"audio/amr":["amr"],"audio/basic":["au","snd"],"audio/midi":["mid","midi","kar","rmi"],"audio/mobile-xmf":["mxmf"],"audio/mp3":["*mp3"],"audio/mp4":["m4a","mp4a"],"audio/mpeg":["mpga","mp2","mp2a","mp3","m2a","m3a"],"audio/ogg":["oga","ogg","spx","opus"],"audio/s3m":["s3m"],"audio/silk":["sil"],"audio/wav":["wav"],"audio/wave":["*wav"],"audio/webm":["weba"],"audio/xm":["xm"],"font/collection":["ttc"],"font/otf":["otf"],"font/ttf":["ttf"],"font/woff":["woff"],"font/woff2":["woff2"],"image/aces":["exr"],"image/apng":["apng"],"image/avif":["avif"],"image/bmp":["bmp"],"image/cgm":["cgm"],"image/dicom-rle":["drle"],"image/emf":["emf"],"image/fits":["fits"],"image/g3fax":["g3"],"image/gif":["gif"],"image/heic":["heic"],"image/heic-sequence":["heics"],"image/heif":["heif"],"image/heif-sequence":["heifs"],"image/hej2k":["hej2"],"image/hsj2":["hsj2"],"image/ief":["ief"],"image/jls":["jls"],"image/jp2":["jp2","jpg2"],"image/jpeg":["jpeg","jpg","jpe"],"image/jph":["jph"],"image/jphc":["jhc"],"image/jpm":["jpm"],"image/jpx":["jpx","jpf"],"image/jxr":["jxr"],"image/jxra":["jxra"],"image/jxrs":["jxrs"],"image/jxs":["jxs"],"image/jxsc":["jxsc"],"image/jxsi":["jxsi"],"image/jxss":["jxss"],"image/ktx":["ktx"],"image/ktx2":["ktx2"],"image/png":["png"],"image/sgi":["sgi"],"image/svg+xml":["svg","svgz"],"image/t38":["t38"],"image/tiff":["tif","tiff"],"image/tiff-fx":["tfx"],"image/webp":["webp"],"image/wmf":["wmf"],"message/disposition-notification":["disposition-notification"],"message/global":["u8msg"],"message/global-delivery-status":["u8dsn"],"message/global-disposition-notification":["u8mdn"],"message/global-headers":["u8hdr"],"message/rfc822":["eml","mime"],"model/3mf":["3mf"],"model/gltf+json":["gltf"],"model/gltf-binary":["glb"],"model/iges":["igs","iges"],"model/mesh":["msh","mesh","silo"],"model/mtl":["mtl"],"model/obj":["obj"],"model/stl":["stl"],"model/vrml":["wrl","vrml"],"model/x3d+binary":["*x3db","x3dbz"],"model/x3d+fastinfoset":["x3db"],"model/x3d+vrml":["*x3dv","x3dvz"],"model/x3d+xml":["x3d","x3dz"],"model/x3d-vrml":["x3dv"],"text/cache-manifest":["appcache","manifest"],"text/calendar":["ics","ifb"],"text/coffeescript":["coffee","litcoffee"],"text/css":["css"],"text/csv":["csv"],"text/html":["html","htm","shtml"],"text/jade":["jade"],"text/jsx":["jsx"],"text/less":["less"],"text/markdown":["markdown","md"],"text/mathml":["mml"],"text/mdx":["mdx"],"text/n3":["n3"],"text/plain":["txt","text","conf","def","list","log","in","ini"],"text/richtext":["rtx"],"text/rtf":["*rtf"],"text/sgml":["sgml","sgm"],"text/shex":["shex"],"text/slim":["slim","slm"],"text/spdx":["spdx"],"text/stylus":["stylus","styl"],"text/tab-separated-values":["tsv"],"text/troff":["t","tr","roff","man","me","ms"],"text/turtle":["ttl"],"text/uri-list":["uri","uris","urls"],"text/vcard":["vcard"],"text/vtt":["vtt"],"text/xml":["*xml"],"text/yaml":["yaml","yml"],"video/3gpp":["3gp","3gpp"],"video/3gpp2":["3g2"],"video/h261":["h261"],"video/h263":["h263"],"video/h264":["h264"],"video/iso.segment":["m4s"],"video/jpeg":["jpgv"],"video/jpm":["*jpm","jpgm"],"video/mj2":["mj2","mjp2"],"video/mp2t":["ts"],"video/mp4":["mp4","mp4v","mpg4"],"video/mpeg":["mpeg","mpg","mpe","m1v","m2v"],"video/ogg":["ogv"],"video/quicktime":["qt","mov"],"video/webm":["webm"]};
2 |
--------------------------------------------------------------------------------
/src/utils/addClient.js:
--------------------------------------------------------------------------------
1 | export const addClient = response =>
2 | response.writeHead(200, {
3 | 'Content-Type': 'text/event-stream',
4 | 'Cache-Control': 'no-cache',
5 | Connection: 'keep-alive'
6 | });
7 |
--------------------------------------------------------------------------------
/src/utils/error.js:
--------------------------------------------------------------------------------
1 | import { log, styles } from './index.js';
2 |
3 | export const error = message => log(message, styles.red);
4 |
--------------------------------------------------------------------------------
/src/utils/getFilePath.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import { options } from '../index.js';
3 |
4 | export const getFilePath = request => {
5 | const { root } = options;
6 |
7 | if (request.url == '/') return `${root}/index.html`;
8 |
9 | if (!request.url.includes('.')) {
10 | const testFilepath = `${root}/${request.url}.html`;
11 |
12 | if (fs.existsSync(testFilepath)) return testFilepath;
13 | }
14 |
15 | return root + request.url;
16 | };
17 |
--------------------------------------------------------------------------------
/src/utils/getIp.js:
--------------------------------------------------------------------------------
1 | import { networkInterfaces } from 'os';
2 |
3 | export const getIp = () =>
4 | Object.values(networkInterfaces())
5 | .flat()
6 | .find(ip => ip.family == 'IPv4' && !ip.internal).address;
7 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export { addClient } from './addClient.js';
2 | export { error } from './error.js';
3 | export { getFilePath } from './getFilePath.js';
4 | export { getIp } from './getIp.js';
5 | export { injectContent } from './injectContent.js';
6 | export { log } from './log.js';
7 | export { mimeType } from './mimeType.js';
8 | export { setRoot } from './setRoot.js';
9 | export { show404 } from './show404.js';
10 | export { showError } from './showError.js';
11 | export { showFile } from './showFile.js';
12 | export { styles } from './styles.js';
13 |
--------------------------------------------------------------------------------
/src/utils/injectContent.js:
--------------------------------------------------------------------------------
1 | import injectCode from '../injectCode.js';
2 |
3 | export const injectContent = content => {
4 | const index = content.indexOf('
22 |
23 |
24 | ');
5 | const start = content.slice(0, index);
6 | const end = content.slice(index);
7 |
8 | return start + injectCode() + end;
9 | };
10 |
--------------------------------------------------------------------------------
/src/utils/log.js:
--------------------------------------------------------------------------------
1 | import { styles } from './index.js';
2 |
3 | export const log = (message, color = styles.green) =>
4 | console.log(color + message + styles.reset);
5 |
--------------------------------------------------------------------------------
/src/utils/mimeType.js:
--------------------------------------------------------------------------------
1 | import mimeTypes from '../mimeTypes.js';
2 |
3 | export const mimeType = extension =>
4 | Object.keys(mimeTypes).find(key => mimeTypes[key].includes(extension));
5 |
--------------------------------------------------------------------------------
/src/utils/setRoot.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import { options, defaultRoot } from '../index.js';
3 |
4 | export const setRoot = () => {
5 | const { root } = options;
6 | const isRoot = !root || root == '.';
7 |
8 | return isRoot && fs.existsSync(defaultRoot) ? defaultRoot : root;
9 | };
10 |
--------------------------------------------------------------------------------
/src/utils/show404.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import { options, encoding } from '../index.js';
3 |
4 | export const show404 = response => {
5 | fs.readFile(`${options.root}/404.html`, (error, content) => {
6 | response.writeHead(404, { 'Content-Type': 'text/html' });
7 | response.end(content, encoding);
8 | });
9 | };
10 |
--------------------------------------------------------------------------------
/src/utils/showError.js:
--------------------------------------------------------------------------------
1 | export const showError = (response, error) => {
2 | response.writeHead(500);
3 | response.end(`Error ${error.code}\n`);
4 | };
5 |
--------------------------------------------------------------------------------
/src/utils/showFile.js:
--------------------------------------------------------------------------------
1 | import { encoding } from '../index.js';
2 |
3 | export const showFile = (response, content, contentType) => {
4 | response.writeHead(200, { 'Content-Type': contentType });
5 | response.end(content, encoding);
6 | };
7 |
--------------------------------------------------------------------------------
/src/utils/styles.js:
--------------------------------------------------------------------------------
1 | export const styles = {
2 | reset: '\x1b[0m',
3 | red: '\x1b[31m',
4 | green: '\x1b[32m'
5 | };
6 |
--------------------------------------------------------------------------------
/test/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nativew/serve/f0a7aaa866e23ae91e304a49189afac25ee92512/test/favicon.ico
--------------------------------------------------------------------------------
/test/images/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nativew/serve/f0a7aaa866e23ae91e304a49189afac25ee92512/test/images/apple-touch-icon.png
--------------------------------------------------------------------------------
/test/images/google-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nativew/serve/f0a7aaa866e23ae91e304a49189afac25ee92512/test/images/google-touch-icon.png
--------------------------------------------------------------------------------
/test/images/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/test/images/mask-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Serve 🍛
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
20 |
21 |
25 |