serve-handler
Advanced tools
Comparing version 2.0.0 to 2.1.0
{ | ||
"name": "serve-handler", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "The routing foundation of `serve` and static deployments on Now", | ||
@@ -9,3 +9,5 @@ "main": "src/index.js", | ||
"lint": "zeit-eslint --ext .jsx,.js .", | ||
"lint-staged": "git diff --diff-filter=ACMRT --cached --name-only '*.js' '*.jsx' | xargs zeit-eslint" | ||
"lint-staged": "git diff --diff-filter=ACMRT --cached --name-only '*.js' '*.jsx' | xargs zeit-eslint", | ||
"build-views": "dottojs -s ./src -d ./src", | ||
"prepublish": "yarn run build-views" | ||
}, | ||
@@ -20,5 +22,10 @@ "repository": "zeit/serve-handler", | ||
"license": "MIT", | ||
"files": [ | ||
"./src/*.js" | ||
], | ||
"devDependencies": { | ||
"@zeit/eslint-config-node": "0.2.13", | ||
"@zeit/git-hooks": "0.1.4", | ||
"commander": "2.15.1", | ||
"dot": "1.1.2", | ||
"eslint": "4.19.1" | ||
@@ -35,2 +42,3 @@ }, | ||
"dependencies": { | ||
"bytes": "3.0.0", | ||
"fast-url-parser": "1.1.3", | ||
@@ -37,0 +45,0 @@ "fs-extra": "6.0.1", |
@@ -53,2 +53,3 @@ # serve-handler | ||
- [headers](#headers-array) (set custom headers) | ||
- [directoryListing](#trailingslash-boolean) (disable directory listing or restrict it to certain paths) | ||
- [trailingSlash](#trailingslash-boolean) (remove or add trailing slashes to all paths) | ||
@@ -170,2 +171,17 @@ | ||
### directoryListing (Boolean|Array) | ||
For paths are not files, but directories, the package will automatically render a good-looking list of all the files and directories contained inside that directory. | ||
If you'd like to disable this for all paths, set this option to `false`. Furthermore, you can also restrict it to certain directory paths if you want: | ||
```json | ||
{ | ||
"directoryListing": [ | ||
"/assets/**", | ||
"/!assets/private" | ||
] | ||
} | ||
``` | ||
### trailingSlash (Boolean) | ||
@@ -193,4 +209,5 @@ | ||
await handler(request, response, null, { | ||
stat(path) {}, | ||
createReadStream(path) {}, | ||
stat(path) {} | ||
readdir(path) {} | ||
}); | ||
@@ -197,0 +214,0 @@ ``` |
142
src/index.js
@@ -11,9 +11,14 @@ // Native | ||
const mime = require('mime/lite'); | ||
const bytes = require('bytes'); | ||
// Other | ||
const template = require('./directory.js'); | ||
const getHandlers = methods => { | ||
const {stat, createReadStream} = fs; | ||
const {stat, createReadStream, readdir} = fs; | ||
return Object.assign({ | ||
stat, | ||
createReadStream | ||
createReadStream, | ||
readdir | ||
}, methods); | ||
@@ -158,3 +163,3 @@ }; | ||
const getHeaders = async (handlers, customHeaders = [], relativePath, stats) => { | ||
const getHeaders = async (customHeaders = [], relativePath, stats) => { | ||
const related = {}; | ||
@@ -184,12 +189,12 @@ | ||
const applicableForCleanUrl = (decodedPath, cleanUrls) => { | ||
let matches = false; | ||
const applicable = (decodedPath, configEntry, negative) => { | ||
let matches = negative ? false : true; | ||
if (typeof cleanUrls !== 'undefined') { | ||
matches = (cleanUrls === true); | ||
if (typeof configEntry !== 'undefined') { | ||
matches = (configEntry === !negative); | ||
if (!matches && Array.isArray(cleanUrls)) { | ||
if (!matches && Array.isArray(configEntry)) { | ||
// This is much faster than `.some` | ||
for (let index = 0; index < cleanUrls.length; index++) { | ||
const source = cleanUrls[index]; | ||
for (let index = 0; index < configEntry.length; index++) { | ||
const source = configEntry[index]; | ||
@@ -251,2 +256,93 @@ if (sourceMatches(source, decodedPath)) { | ||
const renderDirectory = async (current, relativePath, absolutePath, {readdir, stat}) => { | ||
let files = await readdir(absolutePath); | ||
for (const file of files) { | ||
const filePath = path.resolve(absolutePath, file); | ||
const details = path.parse(filePath); | ||
const stats = await stat(filePath); | ||
details.relative = path.join(relativePath, details.base); | ||
if (stats.isDirectory()) { | ||
details.base += '/'; | ||
} else { | ||
details.ext = details.ext.split('.')[1] || 'txt'; | ||
details.size = bytes(stats.size, {unitSeparator: ' '}); | ||
} | ||
details.title = details.base; | ||
files[files.indexOf(file)] = details; | ||
} | ||
const directory = path.join(path.basename(current), relativePath, '/'); | ||
const pathParts = directory.split(path.sep); | ||
// Sort to list directories first, then sort alphabetically | ||
files = files.sort((a, b) => { | ||
const aIsDir = a.base.endsWith('/'); | ||
const bIsDir = b.base.endsWith('/'); | ||
if (aIsDir && !bIsDir) { | ||
return -1; | ||
} | ||
if (bIsDir && !aIsDir) { | ||
return 1; | ||
} | ||
if (a.base > b.base) { | ||
return 1; | ||
} | ||
if (a.base < b.base) { | ||
return -1; | ||
} | ||
return 0; | ||
}); | ||
// Add parent directory to the head of the sorted files array | ||
if (absolutePath.indexOf(`${current}/`) > -1) { | ||
const directoryPath = [...pathParts]; | ||
directoryPath.shift(); | ||
files.unshift({ | ||
base: '..', | ||
relative: path.join(...directoryPath, '..'), | ||
title: path.join(...pathParts.slice(0, -2), '/') | ||
}); | ||
} | ||
const paths = []; | ||
pathParts.pop(); | ||
for (const part in pathParts) { | ||
if (!{}.hasOwnProperty.call(pathParts, part)) { | ||
continue; | ||
} | ||
let before = 0; | ||
const parents = []; | ||
while (before <= part) { | ||
parents.push(pathParts[before]); | ||
before++; | ||
} | ||
parents.shift(); | ||
paths.push({ | ||
name: pathParts[part], | ||
url: parents.join('/') | ||
}); | ||
} | ||
return template({ | ||
files, | ||
directory, | ||
paths | ||
}); | ||
}; | ||
module.exports = async (request, response, config = {}, methods = {}) => { | ||
@@ -258,3 +354,3 @@ const cwd = process.cwd(); | ||
const decodedPath = decodeURIComponent(url.parse(request.url).pathname); | ||
const cleanUrl = applicableForCleanUrl(decodedPath, config.cleanUrls); | ||
const cleanUrl = applicable(decodedPath, config.cleanUrls, true); | ||
const redirect = shouldRedirect(decodedPath, config, cleanUrl); | ||
@@ -312,3 +408,3 @@ | ||
const headers = await getHeaders(handlers, config.headers, relativePath, stats); | ||
const headers = await getHeaders(config.headers, relativePath, stats); | ||
@@ -322,4 +418,24 @@ if (stats.isFile()) { | ||
const canList = applicable(decodedPath, config.directoryListing, false); | ||
if (!canList) { | ||
response.statusCode = 404; | ||
response.end('Not Found'); | ||
return; | ||
} | ||
let directory = null; | ||
try { | ||
directory = await renderDirectory(current, relativePath, absolutePath, handlers); | ||
} catch (err) { | ||
response.statusCode = 500; | ||
response.end(err.message); | ||
return; | ||
} | ||
response.statusCode = 200; | ||
response.end('Directory'); | ||
response.end(directory); | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
336
230
0
18857
7
5
4
+ Addedbytes@3.0.0
+ Addedbytes@3.0.0(transitive)