Comparing version 1.0.0 to 2.0.0
148
index.js
'use strict'; | ||
const path = require('path'); | ||
const crypto = require('crypto'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const url = require('url'); | ||
const ignoredDirectories = require('ignore-by-default').directories(); | ||
const send = require('send'); | ||
function buildVersionHash(directory, root, versions) { | ||
// Walks the directory tree, finding files, generating a version hash | ||
const files = fs.readdirSync(directory); | ||
const staticify = (root, options) => { | ||
const MAX_AGE = 1000 * 60 * 60 * 24 * 365; // 1 year in milliseconds | ||
root = root || directory; | ||
versions = versions || {}; | ||
const setOptions = opts => { | ||
opts = opts || {}; | ||
files.forEach(file => { | ||
const filePath = path.posix.join(directory, file); | ||
const stat = fs.statSync(filePath); | ||
let defaultOptions = { | ||
includeAll: opts.includeAll || false, | ||
shortHash: opts.shortHash || true, | ||
sendOptions: opts.sendOptions || {} | ||
}; | ||
if (stat.isDirectory()) { | ||
buildVersionHash(filePath, root, versions); // Whee! | ||
} else if (stat.isFile()) { | ||
const fileStr = fs.readFileSync(filePath, 'utf8'); | ||
const hash = crypto.createHash('md5') | ||
.update(fileStr, 'utf8') | ||
.digest('hex') | ||
.slice(0, 7); | ||
defaultOptions = Object.assign(defaultOptions, opts); | ||
versions[`/${path.posix.relative(root, filePath)}`] = hash; | ||
return defaultOptions; | ||
}; | ||
const opts = setOptions(options); | ||
// Walks the directory tree, finding files, generating a version hash | ||
const buildVersionHash = (directory, root, vers) => { | ||
root = root || directory; | ||
vers = vers || {}; | ||
if (opts.includeAll === false && ignoredDirectories.some(d => directory.includes(d))) { | ||
return; | ||
} | ||
}); | ||
return versions; | ||
} | ||
const files = fs.readdirSync(directory); | ||
function stripVersion(p) { | ||
// index.<hash>.js -> index.js | ||
const fileName = path.basename(p); | ||
const fileNameParts = fileName.split('.'); | ||
files.forEach(file => { | ||
const filePath = path.posix.join(directory, file); | ||
const stat = fs.statSync(filePath); | ||
if (fileNameParts.length >= 3 && | ||
fileNameParts[fileNameParts.length - 2].length === 7 && | ||
/^[0-9a-f]{7}$/i.exec(fileNameParts[fileNameParts.length - 2])[0] === fileNameParts[fileNameParts.length - 2] | ||
) { | ||
const stripped = fileNameParts.slice(0, fileNameParts.length - 2); | ||
if (stat.isDirectory()) { | ||
buildVersionHash(filePath, root, vers); // Whee! | ||
} else if (stat.isFile()) { | ||
const fileStr = fs.readFileSync(filePath, 'utf8'); | ||
let hash = crypto.createHash('md5') | ||
.update(fileStr, 'utf8') | ||
.digest('hex'); | ||
stripped.push(fileNameParts[fileNameParts.length - 1]); | ||
if (opts.shortHash) { | ||
hash = hash.slice(0, 7); | ||
} | ||
return path.join(path.dirname(p), stripped.join('.')); | ||
} | ||
vers[`/${path.posix.relative(root, filePath)}`] = hash; | ||
} | ||
}); | ||
return p; | ||
} | ||
return vers; | ||
}; | ||
module.exports = (root, options) => { | ||
let versions = buildVersionHash(root); | ||
options = options || {}; | ||
function getVersionedPath(p) { | ||
// index.js -> index.<hash>.js | ||
// index.js -> index.<hash>.js | ||
const getVersionedPath = p => { | ||
if (!versions[p]) { | ||
@@ -71,17 +78,36 @@ return p; | ||
return path.posix.join(path.dirname(p), fileNameParts.join('.')); | ||
} | ||
}; | ||
function serve(req) { | ||
// index.<hash>.js -> index.js | ||
const stripVersion = p => { | ||
const HASH_LEN = opts.shortHash === true ? 7 : 32; | ||
const fileName = path.basename(p); | ||
const fileNameParts = fileName.split('.'); | ||
const re = new RegExp(`^[0-9a-f]{${HASH_LEN}}$`, 'i'); | ||
if (fileNameParts.length >= 3 && | ||
fileNameParts[fileNameParts.length - 2].length === HASH_LEN && | ||
re.exec(fileNameParts[fileNameParts.length - 2])[0] === fileNameParts[fileNameParts.length - 2] | ||
) { | ||
const stripped = fileNameParts.slice(0, fileNameParts.length - 2); | ||
stripped.push(fileNameParts[fileNameParts.length - 1]); | ||
return path.join(path.dirname(p), stripped.join('.')); | ||
} | ||
return p; | ||
}; | ||
const serve = req => { | ||
const filePath = stripVersion(url.parse(req.url).pathname); | ||
const MAX_AGE = 1000 * 60 * 60 * 24 * 365; // 1 year | ||
return send(req, filePath, { | ||
maxage: filePath === req.url ? 0 : MAX_AGE, | ||
index: options.index || 'index.html', | ||
ignore: options.hidden, | ||
root | ||
}); | ||
} | ||
opts.sendOptions.maxAge = filePath === req.url ? 0 : (opts.sendOptions.maxAge ? opts.sendOptions.maxAge : MAX_AGE); | ||
opts.sendOptions.root = root; | ||
function middleware(req, res, next) { | ||
return send(req, filePath, opts.sendOptions); | ||
}; | ||
const middleware = (req, res, next) => { | ||
if (req.method !== 'GET' && req.method !== 'HEAD') { | ||
@@ -99,17 +125,13 @@ return next(); | ||
.pipe(res); | ||
} | ||
}; | ||
function replacePaths(fileContents) { | ||
const urls = Object.keys(versions); | ||
const replacePaths = fileContents => { | ||
return Object.keys(versions).reduce((f, url) => { | ||
return f.replace(url, getVersionedPath(url)); | ||
}, fileContents); | ||
}; | ||
urls.forEach(url => { | ||
fileContents = fileContents.replace(url, getVersionedPath(url)); | ||
}); | ||
return fileContents; | ||
} | ||
function refresh() { | ||
const refresh = () => { | ||
versions = buildVersionHash(root); | ||
} | ||
}; | ||
@@ -126,1 +148,3 @@ return { | ||
}; | ||
module.exports = staticify; |
{ | ||
"name": "staticify", | ||
"version": "1.0.0", | ||
"description": "A better static asset handler for node.js", | ||
"version": "2.0.0", | ||
"description": "A better static asset handler for Node.js / express.js", | ||
"main": "index.js", | ||
@@ -29,7 +29,8 @@ "author": "Rakesh Pai <rakeshpai@errorception.com>", | ||
"dependencies": { | ||
"send": "~0.16.1" | ||
"ignore-by-default": "^1.0.1", | ||
"send": "^0.16.1" | ||
}, | ||
"devDependencies": { | ||
"mocha": "~4.0.1", | ||
"should": "~13.1.3", | ||
"mocha": "^4.0.1", | ||
"should": "^13.1.3", | ||
"xo": "^0.18.2" | ||
@@ -40,3 +41,8 @@ }, | ||
"rules": { | ||
"capitalized-comments": "off" | ||
"capitalized-comments": "off", | ||
"spaced-comment": "off", | ||
"func-style": [ | ||
"error", | ||
"expression" | ||
] | ||
}, | ||
@@ -43,0 +49,0 @@ "overrides": [ |
@@ -9,3 +9,3 @@ # staticify | ||
A better static asset handler for node.js / express.js | ||
A better static asset handler for Node.js / express.js. | ||
@@ -31,5 +31,5 @@ Provides helpers to add a version identifier to your static asset's public URLs, and to remove the hash before serving the file from the file system. | ||
```javascript | ||
var path = require("path"); | ||
var staticify = require("staticify")(path.join(__dirname, "public")); | ||
```js | ||
var path = require('path'); | ||
var staticify = require('staticify')(path.join(__dirname, 'public')); | ||
@@ -48,2 +48,25 @@ ... | ||
## Options | ||
### includeAll | ||
Include all files when scanning the public directory. By default, the directories from [ignore-by-default](https://github.com/novemberborn/ignore-by-default/blob/master/index.js) are ignored. | ||
* Type: Boolean | ||
* Default: `false` | ||
### shortHash | ||
Generate a short (7-digit) md5 hash instead of the full (32-digit) one. | ||
* Type: Boolean | ||
* Default: `true` | ||
### sendOptions | ||
* Type: Object | ||
* Default: `sendOptions: { maxAge: '1y' }` for hashed assets or `maxAge: 0` for non-hashed assets. | ||
You can pass any [send](https://github.com/pillarjs/send) options; used in `middleware` and `serve` functions. | ||
## Usage | ||
@@ -59,5 +82,5 @@ | ||
```javascript | ||
var path = require("path"); | ||
var statificy = require("staticify")(path.join(__dirname, "public")); | ||
```js | ||
var path = require('path'); | ||
var statificy = require('staticify')(path.join(__dirname, 'public')); | ||
``` | ||
@@ -71,3 +94,3 @@ | ||
```javascript | ||
```js | ||
staticify.getVersionedPath('/path/to/file.ext'); // --> /path/to/file.<md5 of the contents of file.ext>.ext | ||
@@ -84,4 +107,4 @@ ``` | ||
```javascript | ||
app.use(staticify.middleware) // `app` is your express instance | ||
```js | ||
app.use(staticify.middleware); // `app` is your express instance | ||
``` | ||
@@ -93,4 +116,4 @@ | ||
```javascript | ||
staticify.replacePaths("body { background: url('/index.js') }"); | ||
```js | ||
staticify.replacePaths('body { background: url("/index.js") }'); | ||
``` | ||
@@ -100,3 +123,3 @@ | ||
```javascript | ||
```js | ||
"body { background: url('/index.d766c4a983224a3696bc4913b9e47305.js') }" | ||
@@ -111,3 +134,3 @@ ``` | ||
```javascript | ||
```js | ||
staticify.stripVersion('/path/to/file.ae2b1fca515949e5d54fb22b8ed95575.ext'); // --> /path/to/file.ext | ||
@@ -124,6 +147,10 @@ ``` | ||
Handles an incoming request for the file. Internally calls `.stripVersion` to strip the version identifier, and serves the file with a `maxage` of a year, using [send](https://github.com/tj/send). Returns a stream that can be `.pipe`d to a http response stream. | ||
Handles an incoming request for the file. Internally calls `.stripVersion` to strip the version identifier, and serves the file with a `maxAge` of one year, using [send](https://github.com/pillarjs/send). Returns a stream that can be `.pipe`d to a http response stream. See [here](https://github.com/pillarjs/send#options) for the options you can pass. | ||
```javascript | ||
staticify.serve(req).pipe(res) | ||
```js | ||
staticify.serve(req, { | ||
sendOptions: { | ||
maxAge: 3600 * 1000 // milliseconds | ||
} | ||
}).pipe(res); | ||
``` | ||
@@ -130,0 +157,0 @@ |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
10387
111
151
0
2
+ Addedignore-by-default@^1.0.1
+ Addedignore-by-default@1.0.1(transitive)
Updatedsend@^0.16.1