Prettier everything up
This commit is contained in:
@ -1,8 +1,11 @@
|
||||
function checkBlacklist(blacklist) {
|
||||
return function (req, res, next) {
|
||||
return function(req, res, next) {
|
||||
// Do not allow packages that have been blacklisted.
|
||||
if (blacklist.includes(req.packageName)) {
|
||||
res.status(403).type('text').send(`Package "${req.packageName}" is blacklisted`)
|
||||
res
|
||||
.status(403)
|
||||
.type('text')
|
||||
.send(`Package "${req.packageName}" is blacklisted`)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
|
@ -4,27 +4,28 @@ const getFileContentType = require('../utils/getFileContentType')
|
||||
|
||||
const e = React.createElement
|
||||
|
||||
const formatTime = (time) =>
|
||||
new Date(time).toISOString()
|
||||
const formatTime = time => new Date(time).toISOString()
|
||||
|
||||
const DirectoryListing = ({ dir, entries }) => {
|
||||
const rows = entries.map(({ file, stats }, index) => {
|
||||
const isDir = stats.isDirectory()
|
||||
const href = file + (isDir ? '/' : '')
|
||||
|
||||
return (
|
||||
e('tr', { key: file, className: index % 2 ? 'odd' : 'even' },
|
||||
e('td', null, e('a', { title: file, href }, file)),
|
||||
e('td', null, isDir ? '-' : getFileContentType(file)),
|
||||
e('td', null, isDir ? '-' : prettyBytes(stats.size)),
|
||||
e('td', null, isDir ? '-' : formatTime(stats.mtime))
|
||||
)
|
||||
return e(
|
||||
'tr',
|
||||
{ key: file, className: index % 2 ? 'odd' : 'even' },
|
||||
e('td', null, e('a', { title: file, href }, file)),
|
||||
e('td', null, isDir ? '-' : getFileContentType(file)),
|
||||
e('td', null, isDir ? '-' : prettyBytes(stats.size)),
|
||||
e('td', null, isDir ? '-' : formatTime(stats.mtime))
|
||||
)
|
||||
})
|
||||
|
||||
if (dir !== '/')
|
||||
rows.unshift(
|
||||
e('tr', { key: '..', className: 'odd' },
|
||||
e(
|
||||
'tr',
|
||||
{ key: '..', className: 'odd' },
|
||||
e('td', null, e('a', { title: 'Parent directory', href: '../' }, '..')),
|
||||
e('td', null, '-'),
|
||||
e('td', null, '-'),
|
||||
@ -32,18 +33,22 @@ const DirectoryListing = ({ dir, entries }) => {
|
||||
)
|
||||
)
|
||||
|
||||
return (
|
||||
e('table', null,
|
||||
e('thead', null,
|
||||
e('tr', null,
|
||||
e('th', null, 'Name'),
|
||||
e('th', null, 'Type'),
|
||||
e('th', null, 'Size'),
|
||||
e('th', null, 'Last Modified')
|
||||
)
|
||||
),
|
||||
e('tbody', null, rows)
|
||||
)
|
||||
return e(
|
||||
'table',
|
||||
null,
|
||||
e(
|
||||
'thead',
|
||||
null,
|
||||
e(
|
||||
'tr',
|
||||
null,
|
||||
e('th', null, 'Name'),
|
||||
e('th', null, 'Type'),
|
||||
e('th', null, 'Size'),
|
||||
e('th', null, 'Last Modified')
|
||||
)
|
||||
),
|
||||
e('tbody', null, rows)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -13,34 +13,41 @@ s.onchange = function () {
|
||||
}
|
||||
`
|
||||
|
||||
const byVersion = (a, b) =>
|
||||
semver.lt(a, b) ? -1 : (semver.gt(a, b) ? 1 : 0)
|
||||
const byVersion = (a, b) => (semver.lt(a, b) ? -1 : semver.gt(a, b) ? 1 : 0)
|
||||
|
||||
const IndexPage = ({ packageInfo, version, dir, entries }) => {
|
||||
const versions = Object.keys(packageInfo.versions).sort(byVersion)
|
||||
const options = versions.map(v => (
|
||||
const options = versions.map(v =>
|
||||
e('option', { key: v, value: v }, `${packageInfo.name}@${v}`)
|
||||
))
|
||||
)
|
||||
|
||||
return (
|
||||
e('html', null,
|
||||
e('head', null,
|
||||
e('meta', { charSet: 'utf-8' }),
|
||||
e('title', null, `Index of ${dir}`),
|
||||
e('style', { dangerouslySetInnerHTML: { __html: IndexPageStyle } })
|
||||
),
|
||||
e('body', null,
|
||||
e('div', { className: 'content-wrapper' },
|
||||
e('div', { className: 'version-wrapper' },
|
||||
e('select', { id: 'version', defaultValue: version }, options)
|
||||
),
|
||||
e('h1', null, `Index of ${dir}`),
|
||||
e('script', { dangerouslySetInnerHTML: { __html: IndexPageScript } }),
|
||||
e('hr'),
|
||||
e(DirectoryListing, { dir, entries }),
|
||||
e('hr'),
|
||||
e('address', null, `${packageInfo.name}@${version}`)
|
||||
)
|
||||
return e(
|
||||
'html',
|
||||
null,
|
||||
e(
|
||||
'head',
|
||||
null,
|
||||
e('meta', { charSet: 'utf-8' }),
|
||||
e('title', null, `Index of ${dir}`),
|
||||
e('style', { dangerouslySetInnerHTML: { __html: IndexPageStyle } })
|
||||
),
|
||||
e(
|
||||
'body',
|
||||
null,
|
||||
e(
|
||||
'div',
|
||||
{ className: 'content-wrapper' },
|
||||
e(
|
||||
'div',
|
||||
{ className: 'version-wrapper' },
|
||||
e('select', { id: 'version', defaultValue: version }, options)
|
||||
),
|
||||
e('h1', null, `Index of ${dir}`),
|
||||
e('script', { dangerouslySetInnerHTML: { __html: IndexPageScript } }),
|
||||
e('hr'),
|
||||
e(DirectoryListing, { dir, entries }),
|
||||
e('hr'),
|
||||
e('address', null, `${packageInfo.name}@${version}`)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -13,18 +13,18 @@ function getBasename(file) {
|
||||
/**
|
||||
* File extensions to look for when automatically resolving.
|
||||
*/
|
||||
const FindExtensions = [ '', '.js', '.json' ]
|
||||
const FindExtensions = ['', '.js', '.json']
|
||||
|
||||
/**
|
||||
* Resolves a path like "lib/file" into "lib/file.js" or "lib/file.json"
|
||||
* depending on which one is available, similar to require('lib/file').
|
||||
*/
|
||||
function findFile(base, useIndex, callback) {
|
||||
FindExtensions.reduceRight(function (next, ext) {
|
||||
FindExtensions.reduceRight(function(next, ext) {
|
||||
const file = base + ext
|
||||
|
||||
return function () {
|
||||
fs.stat(file, function (error, stats) {
|
||||
return function() {
|
||||
fs.stat(file, function(error, stats) {
|
||||
if (error) {
|
||||
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') {
|
||||
next()
|
||||
@ -32,7 +32,11 @@ function findFile(base, useIndex, callback) {
|
||||
callback(error)
|
||||
}
|
||||
} else if (useIndex && stats.isDirectory()) {
|
||||
findFile(path.join(file, 'index'), false, function (error, indexFile, indexStats) {
|
||||
findFile(path.join(file, 'index'), false, function(
|
||||
error,
|
||||
indexFile,
|
||||
indexStats
|
||||
) {
|
||||
if (error) {
|
||||
callback(error)
|
||||
} else if (indexFile) {
|
||||
@ -55,14 +59,20 @@ function findFile(base, useIndex, callback) {
|
||||
* trailing slash.
|
||||
*/
|
||||
function fetchFile(req, res, next) {
|
||||
getPackageInfo(req.packageName, function (error, packageInfo) {
|
||||
getPackageInfo(req.packageName, function(error, packageInfo) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
return res.status(500).type('text').send(`Cannot get info for package "${req.packageName}"`)
|
||||
return res
|
||||
.status(500)
|
||||
.type('text')
|
||||
.send(`Cannot get info for package "${req.packageName}"`)
|
||||
}
|
||||
|
||||
if (packageInfo == null || packageInfo.versions == null)
|
||||
return res.status(404).type('text').send(`Cannot find package "${req.packageName}"`)
|
||||
return res
|
||||
.status(404)
|
||||
.type('text')
|
||||
.send(`Cannot find package "${req.packageName}"`)
|
||||
|
||||
req.packageInfo = packageInfo
|
||||
|
||||
@ -70,10 +80,13 @@ function fetchFile(req, res, next) {
|
||||
// A valid request for a package we haven't downloaded yet.
|
||||
req.packageConfig = req.packageInfo.versions[req.packageVersion]
|
||||
|
||||
getPackage(req.packageConfig, function (error, outputDir) {
|
||||
getPackage(req.packageConfig, function(error, outputDir) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
res.status(500).type('text').send(`Cannot fetch package ${req.packageSpec}`)
|
||||
res
|
||||
.status(500)
|
||||
.type('text')
|
||||
.send(`Cannot fetch package ${req.packageSpec}`)
|
||||
} else {
|
||||
req.packageDir = outputDir
|
||||
|
||||
@ -84,12 +97,18 @@ function fetchFile(req, res, next) {
|
||||
// They want an ES module. Try "module", "jsnext:main", and "/"
|
||||
// https://github.com/rollup/rollup/wiki/pkg.module
|
||||
if (!filename)
|
||||
filename = req.packageConfig.module || req.packageConfig['jsnext:main'] || '/'
|
||||
filename =
|
||||
req.packageConfig.module ||
|
||||
req.packageConfig['jsnext:main'] ||
|
||||
'/'
|
||||
} else if (filename) {
|
||||
// They are requesting an explicit filename. Only try to find an
|
||||
// index file if they are NOT requesting an HTML directory listing.
|
||||
useIndex = filename[filename.length - 1] !== '/'
|
||||
} else if (req.query.main && typeof req.packageConfig[req.query.main] === 'string') {
|
||||
} else if (
|
||||
req.query.main &&
|
||||
typeof req.packageConfig[req.query.main] === 'string'
|
||||
) {
|
||||
// They specified a custom ?main field.
|
||||
filename = req.packageConfig[req.query.main]
|
||||
} else if (typeof req.packageConfig.unpkg === 'string') {
|
||||
@ -104,23 +123,46 @@ function fetchFile(req, res, next) {
|
||||
filename = req.packageConfig.main || '/'
|
||||
}
|
||||
|
||||
findFile(path.join(req.packageDir, filename), useIndex, function (error, file, stats) {
|
||||
if (error)
|
||||
console.error(error)
|
||||
findFile(path.join(req.packageDir, filename), useIndex, function(
|
||||
error,
|
||||
file,
|
||||
stats
|
||||
) {
|
||||
if (error) console.error(error)
|
||||
|
||||
if (file == null)
|
||||
return res.status(404).type('text').send(`Cannot find module "${filename}" in package ${req.packageSpec}`)
|
||||
return res
|
||||
.status(404)
|
||||
.type('text')
|
||||
.send(
|
||||
`Cannot find module "${filename}" in package ${
|
||||
req.packageSpec
|
||||
}`
|
||||
)
|
||||
|
||||
filename = file.replace(req.packageDir, '')
|
||||
|
||||
if (req.query.main != null || getBasename(req.filename) !== getBasename(filename)) {
|
||||
if (
|
||||
req.query.main != null ||
|
||||
getBasename(req.filename) !== getBasename(filename)
|
||||
) {
|
||||
// Need to redirect to the module file so relative imports resolve
|
||||
// correctly. Cache module redirects for 1 minute.
|
||||
delete req.query.main
|
||||
res.set({
|
||||
'Cache-Control': 'public, max-age=60',
|
||||
'Cache-Tag': 'redirect,module-redirect'
|
||||
}).redirect(302, createPackageURL(req.packageName, req.packageVersion, filename, createSearch(req.query)))
|
||||
res
|
||||
.set({
|
||||
'Cache-Control': 'public, max-age=60',
|
||||
'Cache-Tag': 'redirect,module-redirect'
|
||||
})
|
||||
.redirect(
|
||||
302,
|
||||
createPackageURL(
|
||||
req.packageName,
|
||||
req.packageVersion,
|
||||
filename,
|
||||
createSearch(req.query)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
req.filename = filename
|
||||
req.stats = stats
|
||||
@ -131,21 +173,47 @@ function fetchFile(req, res, next) {
|
||||
})
|
||||
} else if (req.packageVersion in req.packageInfo['dist-tags']) {
|
||||
// Cache tag redirects for 1 minute.
|
||||
res.set({
|
||||
'Cache-Control': 'public, max-age=60',
|
||||
'Cache-Tag': 'redirect,tag-redirect'
|
||||
}).redirect(302, createPackageURL(req.packageName, req.packageInfo['dist-tags'][req.packageVersion], req.filename, req.search))
|
||||
res
|
||||
.set({
|
||||
'Cache-Control': 'public, max-age=60',
|
||||
'Cache-Tag': 'redirect,tag-redirect'
|
||||
})
|
||||
.redirect(
|
||||
302,
|
||||
createPackageURL(
|
||||
req.packageName,
|
||||
req.packageInfo['dist-tags'][req.packageVersion],
|
||||
req.filename,
|
||||
req.search
|
||||
)
|
||||
)
|
||||
} else {
|
||||
const maxVersion = semver.maxSatisfying(Object.keys(req.packageInfo.versions), req.packageVersion)
|
||||
const maxVersion = semver.maxSatisfying(
|
||||
Object.keys(req.packageInfo.versions),
|
||||
req.packageVersion
|
||||
)
|
||||
|
||||
if (maxVersion) {
|
||||
// Cache semver redirects for 1 minute.
|
||||
res.set({
|
||||
'Cache-Control': 'public, max-age=60',
|
||||
'Cache-Tag': 'redirect,semver-redirect'
|
||||
}).redirect(302, createPackageURL(req.packageName, maxVersion, req.filename, req.search))
|
||||
res
|
||||
.set({
|
||||
'Cache-Control': 'public, max-age=60',
|
||||
'Cache-Tag': 'redirect,semver-redirect'
|
||||
})
|
||||
.redirect(
|
||||
302,
|
||||
createPackageURL(
|
||||
req.packageName,
|
||||
maxVersion,
|
||||
req.filename,
|
||||
req.search
|
||||
)
|
||||
)
|
||||
} else {
|
||||
res.status(404).type('text').send(`Cannot find package ${req.packageSpec}`)
|
||||
res
|
||||
.status(404)
|
||||
.type('text')
|
||||
.send(`Cannot find package ${req.packageSpec}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -19,9 +19,8 @@ function queryIsKnown(query) {
|
||||
function sanitizeQuery(query) {
|
||||
const saneQuery = {}
|
||||
|
||||
Object.keys(query).forEach(function (param) {
|
||||
if (isKnownQueryParam(param))
|
||||
saneQuery[param] = query[param]
|
||||
Object.keys(query).forEach(function(param) {
|
||||
if (isKnownQueryParam(param)) saneQuery[param] = query[param]
|
||||
})
|
||||
|
||||
return saneQuery
|
||||
@ -54,13 +53,21 @@ function packageURL(req, res, next) {
|
||||
|
||||
// Do not allow invalid URLs.
|
||||
if (url == null)
|
||||
return res.status(403).type('text').send(`Invalid URL: ${req.url}`)
|
||||
return res
|
||||
.status(403)
|
||||
.type('text')
|
||||
.send(`Invalid URL: ${req.url}`)
|
||||
|
||||
const nameErrors = validateNPMPackageName(url.packageName).errors
|
||||
|
||||
// Do not allow invalid package names.
|
||||
if (nameErrors)
|
||||
return res.status(403).type('text').send(`Invalid package name: ${url.packageName} (${nameErrors.join(', ')})`)
|
||||
return res
|
||||
.status(403)
|
||||
.type('text')
|
||||
.send(
|
||||
`Invalid package name: ${url.packageName} (${nameErrors.join(', ')})`
|
||||
)
|
||||
|
||||
req.packageName = url.packageName
|
||||
req.packageVersion = url.packageVersion
|
||||
|
@ -18,36 +18,43 @@ const AutoIndex = !process.env.DISABLE_INDEX
|
||||
const MaximumDepth = 128
|
||||
|
||||
const FileTransforms = {
|
||||
expand: function (file, dependencies, callback) {
|
||||
expand: function(file, dependencies, callback) {
|
||||
const options = {
|
||||
plugins: [
|
||||
unpkgRewrite(dependencies)
|
||||
]
|
||||
plugins: [unpkgRewrite(dependencies)]
|
||||
}
|
||||
|
||||
babel.transformFile(file, options, function (error, result) {
|
||||
babel.transformFile(file, options, function(error, result) {
|
||||
callback(error, result && result.code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send the file, JSON metadata, or HTML directory listing.
|
||||
*/
|
||||
function serveFile(req, res, next) {
|
||||
if (req.query.meta != null) {
|
||||
// Serve JSON metadata.
|
||||
getMetadata(req.packageDir, req.filename, req.stats, MaximumDepth, function (error, metadata) {
|
||||
getMetadata(req.packageDir, req.filename, req.stats, MaximumDepth, function(
|
||||
error,
|
||||
metadata
|
||||
) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
res.status(500).type('text').send(`Cannot generate metadata for ${req.packageSpec}${req.filename}`)
|
||||
res
|
||||
.status(500)
|
||||
.type('text')
|
||||
.send(
|
||||
`Cannot generate metadata for ${req.packageSpec}${req.filename}`
|
||||
)
|
||||
} else {
|
||||
// Cache metadata for 1 year.
|
||||
res.set({
|
||||
'Cache-Control': 'public, max-age=31536000',
|
||||
'Cache-Tag': 'meta'
|
||||
}).send(metadata)
|
||||
res
|
||||
.set({
|
||||
'Cache-Control': 'public, max-age=31536000',
|
||||
'Cache-Tag': 'meta'
|
||||
})
|
||||
.send(metadata)
|
||||
}
|
||||
})
|
||||
} else if (req.stats.isFile()) {
|
||||
@ -56,39 +63,52 @@ function serveFile(req, res, next) {
|
||||
|
||||
let contentType = getFileContentType(file)
|
||||
|
||||
if (contentType === 'text/html')
|
||||
contentType = 'text/plain' // We can't serve HTML because bad people :(
|
||||
if (contentType === 'text/html') contentType = 'text/plain' // We can't serve HTML because bad people :(
|
||||
|
||||
if (contentType === 'application/javascript' && req.query.module != null) {
|
||||
// Serve a JavaScript module.
|
||||
const dependencies = Object.assign({},
|
||||
const dependencies = Object.assign(
|
||||
{},
|
||||
req.packageConfig.peerDependencies,
|
||||
req.packageConfig.dependencies
|
||||
)
|
||||
|
||||
FileTransforms.expand(file, dependencies, function (error, code) {
|
||||
FileTransforms.expand(file, dependencies, function(error, code) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
const debugInfo = error.constructor.name + ': ' + error.message.replace(/^.*?\/unpkg-.+?\//, `/${req.packageSpec}/`) + '\n\n' + error.codeFrame
|
||||
res.status(500).type('text').send(`Cannot generate module for ${req.packageSpec}${req.filename}\n\n${debugInfo}`)
|
||||
const debugInfo =
|
||||
error.constructor.name +
|
||||
': ' +
|
||||
error.message.replace(/^.*?\/unpkg-.+?\//, `/${req.packageSpec}/`) +
|
||||
'\n\n' +
|
||||
error.codeFrame
|
||||
res
|
||||
.status(500)
|
||||
.type('text')
|
||||
.send(
|
||||
`Cannot generate module for ${req.packageSpec}${
|
||||
req.filename
|
||||
}\n\n${debugInfo}`
|
||||
)
|
||||
} else {
|
||||
// Cache modules for 1 year.
|
||||
res.set({
|
||||
'Content-Type': contentType,
|
||||
'Content-Length': Buffer.byteLength(code),
|
||||
'Cache-Control': 'public, max-age=31536000',
|
||||
'Cache-Tag': 'file,js-file,js-module'
|
||||
}).send(code)
|
||||
res
|
||||
.set({
|
||||
'Content-Type': contentType,
|
||||
'Content-Length': Buffer.byteLength(code),
|
||||
'Cache-Control': 'public, max-age=31536000',
|
||||
'Cache-Tag': 'file,js-file,js-module'
|
||||
})
|
||||
.send(code)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// Serve some other static file.
|
||||
const tags = [ 'file' ]
|
||||
const tags = ['file']
|
||||
|
||||
const ext = path.extname(req.filename).substr(1)
|
||||
|
||||
if (ext)
|
||||
tags.push(`${ext}-file`)
|
||||
if (ext) tags.push(`${ext}-file`)
|
||||
|
||||
// Cache files for 1 year.
|
||||
res.set({
|
||||
@ -96,13 +116,13 @@ function serveFile(req, res, next) {
|
||||
'Content-Length': req.stats.size,
|
||||
'Cache-Control': 'public, max-age=31536000',
|
||||
'Last-Modified': req.stats.mtime.toUTCString(),
|
||||
'ETag': etag(req.stats),
|
||||
ETag: etag(req.stats),
|
||||
'Cache-Tag': tags.join(',')
|
||||
})
|
||||
|
||||
const stream = fs.createReadStream(file)
|
||||
|
||||
stream.on('error', function (error) {
|
||||
stream.on('error', function(error) {
|
||||
console.error(`Cannot send file ${req.packageSpec}${req.filename}`)
|
||||
console.error(error)
|
||||
res.sendStatus(500)
|
||||
@ -112,20 +132,36 @@ function serveFile(req, res, next) {
|
||||
}
|
||||
} else if (AutoIndex && req.stats.isDirectory()) {
|
||||
// Serve an HTML directory listing.
|
||||
getIndexHTML(req.packageInfo, req.packageVersion, req.packageDir, req.filename, function (error, html) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
res.status(500).type('text').send(`Cannot generate index page for ${req.packageSpec}${req.filename}`)
|
||||
} else {
|
||||
// Cache HTML directory listings for 1 minute.
|
||||
res.set({
|
||||
'Cache-Control': 'public, max-age=60',
|
||||
'Cache-Tag': 'index'
|
||||
}).send(html)
|
||||
getIndexHTML(
|
||||
req.packageInfo,
|
||||
req.packageVersion,
|
||||
req.packageDir,
|
||||
req.filename,
|
||||
function(error, html) {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
res
|
||||
.status(500)
|
||||
.type('text')
|
||||
.send(
|
||||
`Cannot generate index page for ${req.packageSpec}${req.filename}`
|
||||
)
|
||||
} else {
|
||||
// Cache HTML directory listings for 1 minute.
|
||||
res
|
||||
.set({
|
||||
'Cache-Control': 'public, max-age=60',
|
||||
'Cache-Tag': 'index'
|
||||
})
|
||||
.send(html)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
} else {
|
||||
res.status(403).type('text').send(`Cannot serve ${req.packageSpec}${req.filename}; it's not a file`)
|
||||
res
|
||||
.status(403)
|
||||
.type('text')
|
||||
.send(`Cannot serve ${req.packageSpec}${req.filename}; it's not a file`)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ function createCache(keyPrefix) {
|
||||
}
|
||||
|
||||
function get(key, callback) {
|
||||
db.get(createKey(key), function (error, value) {
|
||||
db.get(createKey(key), function(error, value) {
|
||||
callback(error, value && JSON.parse(value))
|
||||
})
|
||||
}
|
||||
|
@ -1,16 +1,19 @@
|
||||
function createMutex(doWork) {
|
||||
const mutex = {}
|
||||
|
||||
return function (key, payload, callback) {
|
||||
return function(key, payload, callback) {
|
||||
if (mutex[key]) {
|
||||
mutex[key].push(callback)
|
||||
} else {
|
||||
mutex[key] = [ function () {
|
||||
delete mutex[key]
|
||||
}, callback ]
|
||||
mutex[key] = [
|
||||
function() {
|
||||
delete mutex[key]
|
||||
},
|
||||
callback
|
||||
]
|
||||
|
||||
doWork(payload, function (error, value) {
|
||||
mutex[key].forEach(function (callback) {
|
||||
doWork(payload, function(error, value) {
|
||||
mutex[key].forEach(function(callback) {
|
||||
callback(error, value)
|
||||
})
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
function createSearch(query) {
|
||||
const params = []
|
||||
|
||||
Object.keys(query).forEach(function (param) {
|
||||
Object.keys(query).forEach(function(param) {
|
||||
if (query[param] === '') {
|
||||
params.push(param) // Omit the trailing "=" from param=
|
||||
} else {
|
||||
|
@ -8,16 +8,16 @@ const IndexPage = require('../components/IndexPage')
|
||||
const e = React.createElement
|
||||
|
||||
function getEntries(dir) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs.readdir(dir, function (error, files) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fs.readdir(dir, function(error, files) {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
resolve(
|
||||
Promise.all(
|
||||
files.map(file => getFileStats(path.join(dir, file)))
|
||||
).then(function (statsArray) {
|
||||
return statsArray.map(function (stats, index) {
|
||||
).then(function(statsArray) {
|
||||
return statsArray.map(function(stats, index) {
|
||||
return { file: files[index], stats }
|
||||
})
|
||||
})
|
||||
|
@ -6,20 +6,27 @@ const getFileStats = require('./getFileStats')
|
||||
const getFileType = require('./getFileType')
|
||||
|
||||
function getEntries(dir, file, maximumDepth) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs.readdir(path.join(dir, file), function (error, files) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fs.readdir(path.join(dir, file), function(error, files) {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
resolve(
|
||||
Promise.all(
|
||||
files.map(function (f) {
|
||||
files.map(function(f) {
|
||||
return getFileStats(path.join(dir, file, f))
|
||||
})
|
||||
).then(function (statsArray) {
|
||||
return Promise.all(statsArray.map(function (stats, index) {
|
||||
return getMetadataRecursive(dir, path.join(file, files[index]), stats, maximumDepth - 1)
|
||||
}))
|
||||
).then(function(statsArray) {
|
||||
return Promise.all(
|
||||
statsArray.map(function(stats, index) {
|
||||
return getMetadataRecursive(
|
||||
dir,
|
||||
path.join(file, files[index]),
|
||||
stats,
|
||||
maximumDepth - 1
|
||||
)
|
||||
})
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -32,12 +39,12 @@ function formatTime(time) {
|
||||
}
|
||||
|
||||
function getIntegrity(file) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs.readFile(file, function (error, data) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fs.readFile(file, function(error, data) {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
resolve(SRIToolbox.generate({ algorithms: [ 'sha384' ] }, data))
|
||||
resolve(SRIToolbox.generate({ algorithms: ['sha384'] }, data))
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -53,7 +60,7 @@ function getMetadataRecursive(dir, file, stats, maximumDepth) {
|
||||
}
|
||||
|
||||
if (stats.isFile()) {
|
||||
return getIntegrity(path.join(dir, file)).then(function (integrity) {
|
||||
return getIntegrity(path.join(dir, file)).then(function(integrity) {
|
||||
metadata.integrity = integrity
|
||||
return metadata
|
||||
})
|
||||
@ -62,16 +69,19 @@ function getMetadataRecursive(dir, file, stats, maximumDepth) {
|
||||
if (!stats.isDirectory() || maximumDepth === 0)
|
||||
return Promise.resolve(metadata)
|
||||
|
||||
return getEntries(dir, file, maximumDepth).then(function (files) {
|
||||
return getEntries(dir, file, maximumDepth).then(function(files) {
|
||||
metadata.files = files
|
||||
return metadata
|
||||
})
|
||||
}
|
||||
|
||||
function getMetadata(baseDir, path, stats, maximumDepth, callback) {
|
||||
getMetadataRecursive(baseDir, path, stats, maximumDepth).then(function (metadata) {
|
||||
getMetadataRecursive(baseDir, path, stats, maximumDepth).then(function(
|
||||
metadata
|
||||
) {
|
||||
callback(null, metadata)
|
||||
}, callback)
|
||||
},
|
||||
callback)
|
||||
}
|
||||
|
||||
module.exports = getMetadata
|
||||
|
@ -26,7 +26,7 @@ function ignoreSymlinks(file, headers) {
|
||||
}
|
||||
|
||||
function extractResponse(response, outputDir) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
const extract = tar.extract(outputDir, {
|
||||
readable: true, // All dirs/files should be readable.
|
||||
map: stripNamePrefix,
|
||||
@ -44,24 +44,24 @@ function extractResponse(response, outputDir) {
|
||||
function fetchAndExtract(tarballURL, outputDir) {
|
||||
console.log(`info: Fetching ${tarballURL} and extracting to ${outputDir}`)
|
||||
|
||||
return fetch(tarballURL).then(function (response) {
|
||||
return fetch(tarballURL).then(function(response) {
|
||||
return extractResponse(response, outputDir)
|
||||
})
|
||||
}
|
||||
|
||||
const fetchMutex = createMutex(function (payload, callback) {
|
||||
const fetchMutex = createMutex(function(payload, callback) {
|
||||
const { tarballURL, outputDir } = payload
|
||||
|
||||
fs.access(outputDir, function (error) {
|
||||
fs.access(outputDir, function(error) {
|
||||
if (error) {
|
||||
if (error.code === 'ENOENT' || error.code === 'ENOTDIR') {
|
||||
// ENOENT or ENOTDIR are to be expected when we haven't yet
|
||||
// fetched a package for the first time. Carry on!
|
||||
mkdirp(outputDir, function (error) {
|
||||
mkdirp(outputDir, function(error) {
|
||||
if (error) {
|
||||
callback(error)
|
||||
} else {
|
||||
fetchAndExtract(tarballURL, outputDir).then(function () {
|
||||
fetchAndExtract(tarballURL, outputDir).then(function() {
|
||||
callback()
|
||||
}, callback)
|
||||
}
|
||||
@ -80,7 +80,7 @@ function getPackage(packageConfig, callback) {
|
||||
const tarballURL = packageConfig.dist.tarball
|
||||
const outputDir = createTempPath(packageConfig.name, packageConfig.version)
|
||||
|
||||
fetchMutex(tarballURL, { tarballURL, outputDir }, function (error) {
|
||||
fetchMutex(tarballURL, { tarballURL, outputDir }, function(error) {
|
||||
callback(error, outputDir)
|
||||
})
|
||||
}
|
||||
|
@ -20,9 +20,9 @@ function fetchPackageInfo(packageName) {
|
||||
|
||||
return fetch(url, {
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
Accept: 'application/json'
|
||||
}
|
||||
}).then(function (res) {
|
||||
}).then(function(res) {
|
||||
return res.status === 404 ? null : res.json()
|
||||
})
|
||||
}
|
||||
@ -31,32 +31,35 @@ const PackageNotFound = 'PackageNotFound'
|
||||
|
||||
// This mutex prevents multiple concurrent requests to
|
||||
// the registry for the same package info.
|
||||
const fetchMutex = createMutex(function (packageName, callback) {
|
||||
fetchPackageInfo(packageName).then(function (value) {
|
||||
if (value == null) {
|
||||
// Cache 404s for 5 minutes. This prevents us from making
|
||||
// unnecessary requests to the registry for bad package names.
|
||||
// In the worst case, a brand new package's info will be
|
||||
// available within 5 minutes.
|
||||
PackageInfoCache.set(packageName, PackageNotFound, 300, function () {
|
||||
callback(null, value)
|
||||
})
|
||||
} else {
|
||||
// Cache valid package info for 1 minute.
|
||||
PackageInfoCache.set(packageName, value, 60, function () {
|
||||
callback(null, value)
|
||||
const fetchMutex = createMutex(function(packageName, callback) {
|
||||
fetchPackageInfo(packageName).then(
|
||||
function(value) {
|
||||
if (value == null) {
|
||||
// Cache 404s for 5 minutes. This prevents us from making
|
||||
// unnecessary requests to the registry for bad package names.
|
||||
// In the worst case, a brand new package's info will be
|
||||
// available within 5 minutes.
|
||||
PackageInfoCache.set(packageName, PackageNotFound, 300, function() {
|
||||
callback(null, value)
|
||||
})
|
||||
} else {
|
||||
// Cache valid package info for 1 minute.
|
||||
PackageInfoCache.set(packageName, value, 60, function() {
|
||||
callback(null, value)
|
||||
})
|
||||
}
|
||||
},
|
||||
function(error) {
|
||||
// Do not cache errors.
|
||||
PackageInfoCache.del(packageName, function() {
|
||||
callback(error)
|
||||
})
|
||||
}
|
||||
}, function (error) {
|
||||
// Do not cache errors.
|
||||
PackageInfoCache.del(packageName, function () {
|
||||
callback(error)
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
function getPackageInfo(packageName, callback) {
|
||||
PackageInfoCache.get(packageName, function (error, value) {
|
||||
PackageInfoCache.get(packageName, function(error, value) {
|
||||
if (error || value != null) {
|
||||
callback(error, value === PackageNotFound ? null : value)
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user