@bonniernews/local-esi
Advanced tools
Comparing version 0.14.0 to 1.0.0
80
index.js
"use strict"; | ||
const transformHtml = require("./lib/transformHtml"); | ||
const ESIListener = require("./lib/ESIListener"); | ||
const ESIEvaluator = require("./lib/ESIEvaluator"); | ||
const ListenerContext = require("./lib/ListenerContext"); | ||
const {asStream, transform, createESIParser} = require("./lib/transformHtml"); | ||
const redirectCodes = [301, 302, 303, 307, 308]; | ||
module.exports = localEsi; | ||
module.exports.createStream = streaming; | ||
module.exports.createParser = createParser; | ||
module.exports.htmlWriter = require("./lib/htmlWriter"); | ||
function localEsi(html, req, res, next) { | ||
const context = ListenerContext(req, res); | ||
const listener = ESIListener(context); | ||
transformHtml(html, listener, (err, parsed) => { | ||
const context = ListenerContext(req); | ||
let completed = false; | ||
context.on("set_response_code", (statusCode, body) => { | ||
completed = true; | ||
res.status(statusCode).send(body === undefined ? "" : body); | ||
}); | ||
context.on("add_header", (name, value) => { | ||
res.set(name, value); | ||
}); | ||
context.once("set_redirect", (statusCode, location) => { | ||
completed = true; | ||
res.redirect(location); | ||
}); | ||
const listener = ESIEvaluator(context); | ||
return transform(html, listener, (err, parsed) => { | ||
if (err) return next(err); | ||
if (context.redirected) { | ||
return; | ||
} | ||
if (context.replacement) { | ||
return res.send(context.replacement); | ||
} | ||
res.send(parsed); | ||
if (!completed) res.send(parsed); | ||
// return next && next(null, parsed); | ||
}); | ||
} | ||
module.exports = localEsi; | ||
function streaming(req) { | ||
const context = ListenerContext(req); | ||
const listener = ESIEvaluator(context); | ||
const pipeline = asStream(listener); | ||
context.emitter = pipeline; | ||
let responseCode; | ||
const headers = {}; | ||
pipeline.once("set_response_code", onResponseCode); | ||
pipeline.once("add_header", onAddHeader); | ||
pipeline.once("set_redirect", close); | ||
return pipeline; | ||
function onResponseCode(int, body) { | ||
responseCode = int; | ||
if (int > 399 || body) return close(); | ||
if (headers.location && redirectCodes.includes(int)) pipeline.emit("set_redirect", responseCode, headers.location); | ||
} | ||
function onAddHeader(name, value) { | ||
const headerName = name.toLowerCase(); | ||
headers[headerName] = value; | ||
if (headerName === "location" && redirectCodes.includes(responseCode)) pipeline.emit("set_redirect", responseCode, value); | ||
} | ||
function close() { | ||
pipeline.removeListener("set_response_code", onResponseCode); | ||
pipeline.removeListener("add_header", onAddHeader); | ||
pipeline.removeListener("set_redirect", close); | ||
pipeline.destroy(); | ||
} | ||
} | ||
function createParser(req) { | ||
const context = ListenerContext(req); | ||
const listener = ESIEvaluator(context); | ||
return createESIParser(listener); | ||
} |
@@ -0,1 +1,2 @@ | ||
/* eslint-disable camelcase */ | ||
"use strict"; | ||
@@ -18,3 +19,2 @@ const esiExpressionParser = require("./esiExpressionParser"); | ||
}, | ||
// eslint-disable-next-line camelcase | ||
base64_decode([arg]) { | ||
@@ -27,3 +27,2 @@ const string = getFunc(arg.type)(arg); | ||
}, | ||
// eslint-disable-next-line camelcase | ||
base64_encode([arg]) { | ||
@@ -36,3 +35,2 @@ const string = getFunc(arg.type)(arg); | ||
}, | ||
// eslint-disable-next-line camelcase | ||
url_encode([arg]) { | ||
@@ -45,14 +43,15 @@ const string = getFunc(arg.type)(arg); | ||
}, | ||
// eslint-disable-next-line camelcase | ||
add_header([name, value]) { | ||
context.res.set(getFunc(name.type)(name), getFunc(value.type)(value)); | ||
context.emit("add_header", getFunc(name.type)(name), getFunc(value.type)(value)); | ||
}, | ||
// eslint-disable-next-line camelcase | ||
set_redirect([location]) { | ||
context.res.redirect(getFunc(location.type)(location)); | ||
context.emit("set_redirect", 302, getFunc(location.type)(location)); | ||
context.redirected = true; | ||
}, | ||
// eslint-disable-next-line camelcase | ||
set_response_code([code]) { | ||
context.res.status(getFunc(code.type)(code)); | ||
set_response_code([code, body]) { | ||
if (body) { | ||
return context.emit("set_response_code", getFunc(code.type)(code), getFunc(body.type)(body)); | ||
} | ||
context.emit("set_response_code", getFunc(code.type)(code)); | ||
}, | ||
@@ -87,3 +86,2 @@ substr([arg1, arg2, arg3]) { | ||
}, | ||
// eslint-disable-next-line camelcase | ||
http_time([seconds]) { | ||
@@ -90,0 +88,0 @@ const secondsInt = parseInt(getFunc(seconds.type)(seconds)); |
"use strict"; | ||
module.exports = function ListenerContext(req, res) { | ||
const {EventEmitter} = require("events"); | ||
module.exports = function ListenerContext(req, res, emitter) { | ||
const buildHeaderVariables = (headers) => { | ||
@@ -14,3 +15,5 @@ if (!headers) return {}; | ||
return { | ||
emitter = emitter || new EventEmitter(); | ||
const context = { | ||
assigns: Object.assign(buildHeaderVariables(req && req.headers), { | ||
@@ -31,3 +34,20 @@ "HTTP_COOKIE": req.cookies || {}, | ||
tags: [], | ||
get emitter() { | ||
return emitter; | ||
}, | ||
set emitter(value) { | ||
emitter = value; | ||
}, | ||
on(...args) { | ||
emitter.on(...args); | ||
}, | ||
once(...args) { | ||
emitter.once(...args); | ||
}, | ||
emit(...args) { | ||
emitter.emit(...args); | ||
} | ||
}; | ||
return context; | ||
}; |
"use strict"; | ||
const HtmlParser = require("atlas-html-stream"); | ||
const {Readable, Transform} = require("stream"); | ||
const ESIParser = require("./ESIParser"); | ||
const pump = require("pump"); // replace with stream.pipeline when upping to node 10 | ||
const pumpify = require("pumpify"); | ||
const {Readable} = require("stream"); | ||
const htmlWriter = require("./htmlWriter"); | ||
class HtmlTransformer extends Transform { | ||
constructor(filter, options) { | ||
options = Object.assign({}, options, {objectMode: true}); | ||
super(options); | ||
this.filter = filter; | ||
} | ||
module.exports = { | ||
transform, | ||
asStream, | ||
convert, | ||
createESIParser, | ||
}; | ||
_transform(obj, encoding, next) { | ||
const self = this; | ||
this.filter(obj, (err, chunk) => { | ||
if (err) return next(err); | ||
if (chunk) self.push(chunk); | ||
next(); | ||
}); | ||
} | ||
} | ||
module.exports = function parse(html, {onopentag, ontext, onclosetag}, onFinish) { | ||
function transform(html, listener, onFinish) { | ||
const bufferStream = convert(html); | ||
const htmlParser = new HtmlParser({ preserveWS: true }); | ||
const transform = new HtmlTransformer(filter); | ||
return new Promise((resolve, reject) => { | ||
let data = ""; | ||
bufferStream.pipe(htmlParser).pipe(transform) | ||
.once("error", (err) => (onFinish || reject)(err)) | ||
.on("data", (chunk) => { | ||
data += chunk; | ||
}) | ||
.on("finish", () => { | ||
if (onFinish) onFinish(null, data); | ||
resolve(data); | ||
}); | ||
pump(bufferStream, asStream(listener), htmlWriter(), (err) => { | ||
if (onFinish) { | ||
onFinish(err, data); | ||
return resolve(); | ||
} | ||
if (err) return reject(err); | ||
resolve(data); | ||
}).on("data", (chunk) => { | ||
data += chunk; | ||
}); | ||
}); | ||
} | ||
function filter({name, data, text}, next) { | ||
if (text) { | ||
return ontext(text, next); | ||
} else if (name && data) { | ||
return onopentag(name, data, next); | ||
} else { | ||
return onclosetag(name, next); | ||
} | ||
} | ||
}; | ||
function asStream(listener) { | ||
const htmlParser = new HtmlParser({ preserveWS: true }); | ||
const esiParser = createESIParser(listener); | ||
return pumpify.obj(htmlParser, esiParser); | ||
} | ||
@@ -78,1 +65,15 @@ function convert(buf, chunkSize) { | ||
} | ||
function createESIParser({onopentag, ontext, onclosetag}) { | ||
return new ESIParser(filter); | ||
function filter({name, data, text}, next) { | ||
if (text) { | ||
return ontext(text, next); | ||
} else if (name && data) { | ||
return onopentag(name, data, next); | ||
} else { | ||
return onclosetag(name, next); | ||
} | ||
} | ||
} |
{ | ||
"name": "@bonniernews/local-esi", | ||
"version": "0.14.0", | ||
"version": "1.0.0", | ||
"description": "Local Edge Side Includes parser", | ||
@@ -16,4 +16,5 @@ "main": "index.js", | ||
"chai": "^4.2.0", | ||
"eslint": "^6.4.0", | ||
"mocha": "^6.2.0", | ||
"chronokinesis": "^2.0.1", | ||
"eslint": "^6.5.1", | ||
"mocha": "^6.2.1", | ||
"nock": "^11.3.5" | ||
@@ -23,3 +24,4 @@ }, | ||
"atlas-html-stream": "^1.2.0", | ||
"chronokinesis": "^2.0.1", | ||
"pump": "^3.0.0", | ||
"pumpify": "^2.0.0", | ||
"request": "^2.88.0" | ||
@@ -26,0 +28,0 @@ }, |
@@ -8,4 +8,9 @@ Local-ESI | ||
# Example Express route: | ||
# API | ||
- `localEsi(req, res[, callback])`: returns ESI evaluated markup in callback or as promised | ||
- `localEsi.createStream(req)`: returns pipable object stream with ESI evaluated content | ||
## `localEsi(req, res[, callback])` | ||
```javascript | ||
@@ -24,1 +29,26 @@ "use strict"; | ||
``` | ||
## `localEsi.createStream(req[, res])` | ||
Used as object stream. | ||
```javascript | ||
"use strict"; | ||
const localEsi = require("@bonniernews/local-esi"); | ||
module.exports = (req, res, next) => { | ||
const esiPipeline = localEsi.createStream(req); | ||
res.render("index", { data: "a" }) | ||
.pipe(esiPipeline) | ||
.on("error", next) | ||
.on("set_redirect", (statusCode, location) => res.redirect(statusCode, location)); | ||
}; | ||
``` | ||
## Events | ||
- `error`: an error occured | ||
- `set_response_code`: send status code and optional body | ||
- `add_header`: set header name and value | ||
- `set_redirect`: redirect with status code and location |
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
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
32656
12
937
0
53
4
5
1
+ Addedpump@^3.0.0
+ Addedpumpify@^2.0.0
+ Addedduplexify@4.1.3(transitive)
+ Addedend-of-stream@1.4.4(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedpump@3.0.2(transitive)
+ Addedpumpify@2.0.1(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedstream-shift@1.0.3(transitive)
+ Addedstring_decoder@1.3.0(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedwrappy@1.0.2(transitive)
- Removedchronokinesis@^2.0.1
- Removedchronokinesis@2.0.1(transitive)