serve-handler
Advanced tools
Comparing version 1.2.0 to 1.3.0
136
lib/index.js
@@ -83,3 +83,3 @@ // Native | ||
const shouldRedirect = (rewrittenPath, {redirects = [], trailingSlash = null}) => { | ||
const shouldRedirect = (decodedPath, {redirects = [], trailingSlash}, cleanUrl) => { | ||
if (redirects.length === 0) { | ||
@@ -90,6 +90,18 @@ return null; | ||
const defaultType = 301; | ||
const matchHTML = /.html|.htm|\/index/g; | ||
let cleanedUrl = false; | ||
// By stripping the HTML parts from the decoded | ||
// path *before* handling the trailing slash, we make | ||
// sure that only *one* redirect occurs if both | ||
// config options are used. | ||
if (cleanUrl && matchHTML.test(decodedPath)) { | ||
decodedPath = decodedPath.replace(matchHTML, ''); | ||
cleanedUrl = true; | ||
} | ||
if (typeof trailingSlash === 'boolean') { | ||
const {ext} = path.parse(rewrittenPath); | ||
const isTrailed = rewrittenPath.endsWith('/'); | ||
const {ext} = path.parse(decodedPath); | ||
const isTrailed = decodedPath.endsWith('/'); | ||
@@ -99,9 +111,9 @@ let target = null; | ||
if (!trailingSlash && isTrailed) { | ||
target = rewrittenPath.slice(0, -1); | ||
target = decodedPath.slice(0, -1); | ||
} else if (trailingSlash && !isTrailed && !ext) { | ||
target = `${rewrittenPath}/`; | ||
target = `${decodedPath}/`; | ||
} | ||
if (rewrittenPath.indexOf('//') > -1) { | ||
target = rewrittenPath.replace(/\/+/g, '/'); | ||
if (decodedPath.indexOf('//') > -1) { | ||
target = decodedPath.replace(/\/+/g, '/'); | ||
} | ||
@@ -117,2 +129,9 @@ | ||
if (cleanedUrl) { | ||
return { | ||
target: decodedPath, | ||
statusCode: defaultType | ||
}; | ||
} | ||
// This is currently the fastest way to | ||
@@ -122,3 +141,3 @@ // iterate over an array | ||
const {source, destination, type} = redirects[index]; | ||
const target = toTarget(source, destination, rewrittenPath); | ||
const target = toTarget(source, destination, decodedPath); | ||
@@ -168,2 +187,68 @@ if (target) { | ||
const applicableForCleanUrl = (decodedPath, cleanUrls) => { | ||
let matches = false; | ||
if (typeof cleanUrls !== 'undefined') { | ||
matches = (cleanUrls === true); | ||
if (!matches && Array.isArray(cleanUrls)) { | ||
// This is much faster than `.some` | ||
for (let index = 0; index < cleanUrls.length; index++) { | ||
const source = cleanUrls[index]; | ||
if (sourceMatches(source, decodedPath)) { | ||
matches = true; | ||
} | ||
} | ||
} | ||
} | ||
return matches; | ||
}; | ||
const getPossiblePaths = (relativePath, extension) => [ | ||
path.join(relativePath, `index${extension}`), | ||
relativePath.endsWith('/') ? relativePath.replace(/\/$/g, extension) : (relativePath + extension) | ||
]; | ||
const findRelated = async (current, relativePath, stat, extension = '.html') => { | ||
const possible = getPossiblePaths(relativePath, extension); | ||
let stats = null; | ||
for (let index = 0; index < possible.length; index++) { | ||
const related = possible[index]; | ||
const absolutePath = path.join(current, related); | ||
try { | ||
stats = await stat(absolutePath); | ||
} catch (err) { | ||
if (err.code !== 'ENOENT') { | ||
throw err; | ||
} | ||
} | ||
if (stats) { | ||
return { | ||
stats, | ||
absolutePath | ||
}; | ||
} | ||
} | ||
if (extension === '.htm') { | ||
return null; | ||
} | ||
// At this point, no `.html` files have been found, so we | ||
// need to check for the existance of `.htm` ones. | ||
const relatedHTM = findRelated(current, relativePath, stat, '.htm'); | ||
if (relatedHTM) { | ||
return relatedHTM; | ||
} | ||
return null; | ||
}; | ||
module.exports = async (request, response, config = {}, methods = {}) => { | ||
@@ -175,4 +260,4 @@ const cwd = process.cwd(); | ||
const decodedPath = decodeURIComponent(url.parse(request.url).pathname); | ||
const relativePath = applyRewrites(decodedPath, config.rewrites); | ||
const redirect = shouldRedirect(decodedPath, config); | ||
const cleanUrl = applicableForCleanUrl(decodedPath, config.cleanUrls); | ||
const redirect = shouldRedirect(decodedPath, config, cleanUrl); | ||
@@ -187,3 +272,5 @@ if (redirect) { | ||
const absolutePath = path.join(current, relativePath); | ||
const relativePath = applyRewrites(decodedPath, config.rewrites); | ||
let absolutePath = path.join(current, relativePath); | ||
let stats = null; | ||
@@ -202,7 +289,26 @@ | ||
if (!stats) { | ||
response.statusCode = 404; | ||
response.end('Not Found'); | ||
if (!stats || stats.isDirectory()) { | ||
if (cleanUrl) { | ||
try { | ||
const related = await findRelated(current, relativePath, handlers.stat); | ||
return; | ||
if (related) { | ||
({stats, absolutePath} = related); | ||
} | ||
} catch (err) { | ||
if (err.code !== 'ENOENT') { | ||
response.statusCode = 500; | ||
response.end(err.message); | ||
return; | ||
} | ||
} | ||
} | ||
if (!stats) { | ||
response.statusCode = 404; | ||
response.end('Not Found'); | ||
return; | ||
} | ||
} | ||
@@ -209,0 +315,0 @@ |
{ | ||
"name": "serve-handler", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "The routing foundation of `serve` and static deployments on Now", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
48230
248