Move utilities into middleware/utils
This commit is contained in:
parent
2d03ed9be6
commit
7408b24adf
|
@ -1,49 +0,0 @@
|
||||||
const fs = require('fs')
|
|
||||||
const mime = require('mime')
|
|
||||||
|
|
||||||
mime.define({
|
|
||||||
'text/plain': [
|
|
||||||
'license',
|
|
||||||
'readme',
|
|
||||||
'changes',
|
|
||||||
'authors',
|
|
||||||
'makefile',
|
|
||||||
'ts',
|
|
||||||
'flow'
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const TextFiles = /\/?(\.[a-z]*rc|\.git[a-z]*|\.[a-z]*ignore)$/i
|
|
||||||
|
|
||||||
function getContentType(file) {
|
|
||||||
return TextFiles.test(file) ? 'text/plain' : mime.lookup(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStats(file) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
fs.lstat(file, (error, stats) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
resolve(stats)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFileType(stats) {
|
|
||||||
if (stats.isFile()) return 'file'
|
|
||||||
if (stats.isDirectory()) return 'directory'
|
|
||||||
if (stats.isBlockDevice()) return 'blockDevice'
|
|
||||||
if (stats.isCharacterDevice()) return 'characterDevice'
|
|
||||||
if (stats.isSymbolicLink()) return 'symlink'
|
|
||||||
if (stats.isSocket()) return 'socket'
|
|
||||||
if (stats.isFIFO()) return 'fifo'
|
|
||||||
return 'unknown'
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getContentType,
|
|
||||||
getStats,
|
|
||||||
getFileType
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
const { getContentType, getStats, getFileType } = require('./FileUtils')
|
|
||||||
|
|
||||||
it('gets a content type of text/plain for LICENSE|README|CHANGES|AUTHORS|Makefile', () => {
|
|
||||||
expect(getContentType('LICENSE')).toBe('text/plain')
|
|
||||||
expect(getContentType('README')).toBe('text/plain')
|
|
||||||
expect(getContentType('CHANGES')).toBe('text/plain')
|
|
||||||
expect(getContentType('AUTHORS')).toBe('text/plain')
|
|
||||||
expect(getContentType('Makefile')).toBe('text/plain')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('gets a content type of text/plain for .*rc files', () => {
|
|
||||||
expect(getContentType('.eslintrc')).toBe('text/plain')
|
|
||||||
expect(getContentType('.babelrc')).toBe('text/plain')
|
|
||||||
expect(getContentType('.anythingrc')).toBe('text/plain')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('gets a content type of text/plain for .git* files', () => {
|
|
||||||
expect(getContentType('.gitignore')).toBe('text/plain')
|
|
||||||
expect(getContentType('.gitanything')).toBe('text/plain')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('gets a content type of text/plain for .*ignore files', () => {
|
|
||||||
expect(getContentType('.eslintignore')).toBe('text/plain')
|
|
||||||
expect(getContentType('.anythingignore')).toBe('text/plain')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('gets a content type of text/plain for .ts files', () => {
|
|
||||||
expect(getContentType('app.ts')).toBe('text/plain')
|
|
||||||
expect(getContentType('app.d.ts')).toBe('text/plain')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('gets a content type of text/plain for .flow files', () => {
|
|
||||||
expect(getContentType('app.js.flow')).toBe('text/plain')
|
|
||||||
})
|
|
|
@ -1,41 +0,0 @@
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const React = require('react')
|
|
||||||
const ReactDOMServer = require('react-dom/server')
|
|
||||||
const IndexPage = require('./components/IndexPage')
|
|
||||||
const { getStats } = require('./FileUtils')
|
|
||||||
|
|
||||||
const e = React.createElement
|
|
||||||
|
|
||||||
const getEntries = (dir) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
fs.readdir(dir, (error, files) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
resolve(
|
|
||||||
Promise.all(
|
|
||||||
files.map(file => getStats(path.join(dir, file)))
|
|
||||||
).then(
|
|
||||||
statsArray => statsArray.map(
|
|
||||||
(stats, index) => ({ file: files[index], stats })
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const DOCTYPE = '<!DOCTYPE html>'
|
|
||||||
|
|
||||||
const generateIndexPage = (props) =>
|
|
||||||
DOCTYPE + ReactDOMServer.renderToStaticMarkup(e(IndexPage, props))
|
|
||||||
|
|
||||||
const generateDirectoryIndexHTML = (packageInfo, version, baseDir, dir, callback) =>
|
|
||||||
getEntries(path.join(baseDir, dir))
|
|
||||||
.then(entries => generateIndexPage({ packageInfo, version, dir, entries }))
|
|
||||||
.then(html => callback(null, html), callback)
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
generateDirectoryIndexHTML
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const csso = require('csso')
|
|
||||||
|
|
||||||
const minifyCSS = (css) =>
|
|
||||||
csso.minify(css).css
|
|
||||||
|
|
||||||
const readCSS = (...args) =>
|
|
||||||
minifyCSS(fs.readFileSync(path.resolve(...args), 'utf8'))
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
minifyCSS,
|
|
||||||
readCSS
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
const React = require('react')
|
const React = require('react')
|
||||||
const prettyBytes = require('pretty-bytes')
|
const prettyBytes = require('pretty-bytes')
|
||||||
const { getContentType } = require('../FileUtils')
|
const getFileContentType = require('../utils/getFileContentType')
|
||||||
|
|
||||||
const e = React.createElement
|
const e = React.createElement
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ const DirectoryListing = ({ dir, entries }) => {
|
||||||
return (
|
return (
|
||||||
e('tr', { key: file, className: index % 2 ? 'odd' : 'even' },
|
e('tr', { key: file, className: index % 2 ? 'odd' : 'even' },
|
||||||
e('td', null, e('a', { title: file, href }, file)),
|
e('td', null, e('a', { title: file, href }, file)),
|
||||||
e('td', null, isDir ? '-' : getContentType(file)),
|
e('td', null, isDir ? '-' : getFileContentType(file)),
|
||||||
e('td', null, isDir ? '-' : prettyBytes(stats.size)),
|
e('td', null, isDir ? '-' : prettyBytes(stats.size)),
|
||||||
e('td', null, isDir ? '-' : formatTime(stats.mtime))
|
e('td', null, isDir ? '-' : formatTime(stats.mtime))
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const React = require('react')
|
const React = require('react')
|
||||||
const semver = require('semver')
|
const semver = require('semver')
|
||||||
const DirectoryListing = require('./DirectoryListing')
|
const DirectoryListing = require('./DirectoryListing')
|
||||||
const { readCSS } = require('../StyleUtils')
|
const readCSS = require('../utils/readCSS')
|
||||||
|
|
||||||
const e = React.createElement
|
const e = React.createElement
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const qs = require('querystring')
|
const qs = require('querystring')
|
||||||
const etag = require('etag')
|
const etag = require('etag')
|
||||||
const { generateDirectoryIndexHTML } = require('./IndexUtils')
|
const getFileContentType = require('./utils/getFileContentType')
|
||||||
const { getContentType } = require('./FileUtils')
|
const getIndexHTML = require('./utils/getIndexHTML')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically generate HTML pages that show package contents.
|
* Automatically generate HTML pages that show package contents.
|
||||||
|
@ -11,7 +11,7 @@ const { getContentType } = require('./FileUtils')
|
||||||
const AutoIndex = !process.env.DISABLE_INDEX
|
const AutoIndex = !process.env.DISABLE_INDEX
|
||||||
|
|
||||||
function sendFile(res, file, stats) {
|
function sendFile(res, file, stats) {
|
||||||
let contentType = getContentType(file)
|
let contentType = getFileContentType(file)
|
||||||
|
|
||||||
if (contentType === 'text/html')
|
if (contentType === 'text/html')
|
||||||
contentType = 'text/plain' // We can't serve HTML because bad people :(
|
contentType = 'text/plain' // We can't serve HTML because bad people :(
|
||||||
|
@ -52,7 +52,7 @@ function serveFile(req, res, next) {
|
||||||
// TODO: use res.sendFile instead of our own sendFile?
|
// TODO: use res.sendFile instead of our own sendFile?
|
||||||
sendFile(res, path.join(req.packageDir, req.file), req.stats)
|
sendFile(res, path.join(req.packageDir, req.file), req.stats)
|
||||||
} else if (AutoIndex && req.stats.isDirectory()) {
|
} else if (AutoIndex && req.stats.isDirectory()) {
|
||||||
generateDirectoryIndexHTML(req.packageInfo, req.packageVersion, req.packageDir, req.file, function (error, html) {
|
getIndexHTML(req.packageInfo, req.packageVersion, req.packageDir, req.file, function (error, html) {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
res.status(500).type('text').send(`Cannot generate index page for ${req.packageSpec}${req.filename}`)
|
res.status(500).type('text').send(`Cannot generate index page for ${req.packageSpec}${req.filename}`)
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
const Metadata = require('./MetadataUtils')
|
const getMetadata = require('./utils/getMetadata')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum recursion depth for ?meta listings.
|
* Maximum recursion depth for meta listings.
|
||||||
*/
|
*/
|
||||||
const MaximumDepth = 128
|
const MaximumDepth = 128
|
||||||
|
|
||||||
function serveMetadata(req, res) {
|
function serveMetadata(req, res) {
|
||||||
Metadata.get(req.packageDir, req.file, req.stats, MaximumDepth, function (error, metadata) {
|
getMetadata(req.packageDir, req.file, req.stats, MaximumDepth, function (error, metadata) {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(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}`)
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
const mime = require('mime')
|
||||||
|
|
||||||
|
mime.define({
|
||||||
|
'text/plain': [
|
||||||
|
'license',
|
||||||
|
'readme',
|
||||||
|
'changes',
|
||||||
|
'authors',
|
||||||
|
'makefile',
|
||||||
|
'ts',
|
||||||
|
'flow'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const TextFiles = /\/?(\.[a-z]*rc|\.git[a-z]*|\.[a-z]*ignore)$/i
|
||||||
|
|
||||||
|
function getFileContentType(file) {
|
||||||
|
return TextFiles.test(file) ? 'text/plain' : mime.lookup(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = getFileContentType
|
|
@ -0,0 +1,34 @@
|
||||||
|
const getFileContentType = require('./getFileContentType')
|
||||||
|
|
||||||
|
it('gets a content type of text/plain for LICENSE|README|CHANGES|AUTHORS|Makefile', () => {
|
||||||
|
expect(getFileContentType('LICENSE')).toBe('text/plain')
|
||||||
|
expect(getFileContentType('README')).toBe('text/plain')
|
||||||
|
expect(getFileContentType('CHANGES')).toBe('text/plain')
|
||||||
|
expect(getFileContentType('AUTHORS')).toBe('text/plain')
|
||||||
|
expect(getFileContentType('Makefile')).toBe('text/plain')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gets a content type of text/plain for .*rc files', () => {
|
||||||
|
expect(getFileContentType('.eslintrc')).toBe('text/plain')
|
||||||
|
expect(getFileContentType('.babelrc')).toBe('text/plain')
|
||||||
|
expect(getFileContentType('.anythingrc')).toBe('text/plain')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gets a content type of text/plain for .git* files', () => {
|
||||||
|
expect(getFileContentType('.gitignore')).toBe('text/plain')
|
||||||
|
expect(getFileContentType('.gitanything')).toBe('text/plain')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gets a content type of text/plain for .*ignore files', () => {
|
||||||
|
expect(getFileContentType('.eslintignore')).toBe('text/plain')
|
||||||
|
expect(getFileContentType('.anythingignore')).toBe('text/plain')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gets a content type of text/plain for .ts files', () => {
|
||||||
|
expect(getFileContentType('app.ts')).toBe('text/plain')
|
||||||
|
expect(getFileContentType('app.d.ts')).toBe('text/plain')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gets a content type of text/plain for .flow files', () => {
|
||||||
|
expect(getFileContentType('app.js.flow')).toBe('text/plain')
|
||||||
|
})
|
|
@ -0,0 +1,15 @@
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
function getFileStats(file) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fs.lstat(file, (error, stats) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error)
|
||||||
|
} else {
|
||||||
|
resolve(stats)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = getFileStats
|
|
@ -0,0 +1,12 @@
|
||||||
|
function getFileType(stats) {
|
||||||
|
if (stats.isFile()) return 'file'
|
||||||
|
if (stats.isDirectory()) return 'directory'
|
||||||
|
if (stats.isBlockDevice()) return 'blockDevice'
|
||||||
|
if (stats.isCharacterDevice()) return 'characterDevice'
|
||||||
|
if (stats.isSymbolicLink()) return 'symlink'
|
||||||
|
if (stats.isSocket()) return 'socket'
|
||||||
|
if (stats.isFIFO()) return 'fifo'
|
||||||
|
return 'unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = getFileType
|
|
@ -0,0 +1,42 @@
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
const React = require('react')
|
||||||
|
const ReactDOMServer = require('react-dom/server')
|
||||||
|
const getFileStats = require('./getFileStats')
|
||||||
|
const IndexPage = require('../components/IndexPage')
|
||||||
|
|
||||||
|
const e = React.createElement
|
||||||
|
|
||||||
|
function getEntries(dir) {
|
||||||
|
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) {
|
||||||
|
return { file: files[index], stats }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const DOCTYPE = '<!DOCTYPE html>'
|
||||||
|
|
||||||
|
function createHTML(props) {
|
||||||
|
return DOCTYPE + ReactDOMServer.renderToStaticMarkup(e(IndexPage, props))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIndexHTML(packageInfo, version, baseDir, dir, callback) {
|
||||||
|
getEntries(path.join(baseDir, dir))
|
||||||
|
.then(entries => createHTML({ packageInfo, version, dir, entries }))
|
||||||
|
.then(html => callback(null, html), callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = getIndexHTML
|
|
@ -1,7 +1,9 @@
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const SRIToolbox = require('sri-toolbox')
|
const SRIToolbox = require('sri-toolbox')
|
||||||
const { getContentType, getStats, getFileType } = require('./FileUtils')
|
const getFileContentType = require('./getFileContentType')
|
||||||
|
const getFileStats = require('./getFileStats')
|
||||||
|
const getFileType = require('./getFileType')
|
||||||
|
|
||||||
function getEntries(dir, file, maximumDepth) {
|
function getEntries(dir, file, maximumDepth) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
|
@ -12,7 +14,7 @@ function getEntries(dir, file, maximumDepth) {
|
||||||
resolve(
|
resolve(
|
||||||
Promise.all(
|
Promise.all(
|
||||||
files.map(function (f) {
|
files.map(function (f) {
|
||||||
return getStats(path.join(dir, file, f))
|
return getFileStats(path.join(dir, file, f))
|
||||||
})
|
})
|
||||||
).then(function (statsArray) {
|
).then(function (statsArray) {
|
||||||
return Promise.all(statsArray.map(function (stats, index) {
|
return Promise.all(statsArray.map(function (stats, index) {
|
||||||
|
@ -44,7 +46,7 @@ function getIntegrity(file) {
|
||||||
function getMetadataRecursive(dir, file, stats, maximumDepth) {
|
function getMetadataRecursive(dir, file, stats, maximumDepth) {
|
||||||
const metadata = {
|
const metadata = {
|
||||||
lastModified: formatTime(stats.mtime),
|
lastModified: formatTime(stats.mtime),
|
||||||
contentType: getContentType(file),
|
contentType: getFileContentType(file),
|
||||||
path: file,
|
path: file,
|
||||||
size: stats.size,
|
size: stats.size,
|
||||||
type: getFileType(stats)
|
type: getFileType(stats)
|
||||||
|
@ -72,6 +74,4 @@ function getMetadata(baseDir, path, stats, maximumDepth, callback) {
|
||||||
}, callback)
|
}, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = getMetadata
|
||||||
get: getMetadata
|
|
||||||
}
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
const fs = require('fs')
|
||||||
|
const path = require('path')
|
||||||
|
const csso = require('csso')
|
||||||
|
|
||||||
|
function readCSS(...args) {
|
||||||
|
return csso.minify(fs.readFileSync(path.resolve(...args), 'utf8')).css
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = readCSS
|
Loading…
Reference in New Issue