Comparing version 0.1.2 to 0.1.3
360
index.js
var fs = require("fs"); | ||
var path = require("path"); | ||
var mkdirp = require("mkdirp"); | ||
var glob = require("glob"); | ||
var SSI = require("./lib/SSI"); | ||
var DIRECTIVE_MATCHER = /<!--#([a-z]+)([ ]+([a-z]+)="(.+?)")* -->/g; | ||
var ATTRIBUTE_MATCHER = /([a-z]+)="(.+?)"/g; | ||
var INTERPOLATION_MATCHER = /\$\{(.+?)\}/g; | ||
(function() { | ||
"use strict"; | ||
var mergeSimpleObject = function() { | ||
var output = {}; | ||
for (var i = 0; i < arguments.length; i++) { | ||
var argument = arguments[i]; | ||
for (var key in argument) { | ||
if (argument.hasOwnProperty(key)) { | ||
output[key] = argument[key]; | ||
} | ||
} | ||
} | ||
return output; | ||
var ssi = function(inputDirectory, outputDirectory, matcher) { | ||
this.parser = new SSI(inputDirectory, outputDirectory, matcher); | ||
}; | ||
var IOUtils = function(documentRoot) { | ||
this.documentRoot = documentRoot; | ||
}; | ||
IOUtils.prototype = { | ||
/* Public Methods */ | ||
readFileSync: function(currentFile, includeFile) { | ||
var filename = path.resolve(path.dirname(currentFile), includeFile); | ||
return fs.readFileSync(filename, {encoding: "utf8"}); | ||
}, | ||
readVirtualSync: function(includeFile) { | ||
var filename = path.resolve(this.documentRoot, includeFile); | ||
return fs.readFileSync(filename, {encoding: "utf8"}); | ||
}, | ||
writeFileSync: function(filename, contents) { | ||
var directory = path.dirname(filename); | ||
if (!fs.existsSync(directory)) { | ||
// If the file's directory doesn't exists, recursively create it | ||
mkdirp.sync(directory); | ||
} | ||
fs.writeFileSync(filename, contents, {encoding: "utf8"}); | ||
} | ||
/* Private Methods */ | ||
}; | ||
var Conditional = function(expression) { | ||
this.expression = expression; | ||
this.directives = []; | ||
}; | ||
Conditional.prototype = { | ||
getExpression: function() { | ||
return this.expression; | ||
}, | ||
getDirectives: function() { | ||
return this.directives; | ||
}, | ||
addDirective: function(directive) { | ||
this.directives.push(directive); | ||
} | ||
}; | ||
var DirectiveHandler = function(ioUtils) { | ||
this.ioUtils = ioUtils; | ||
this.conditionals = []; | ||
this.currentConditional = undefined; | ||
}; | ||
DirectiveHandler.prototype = { | ||
/* Public Methods */ | ||
handleDirective: function(directive, directiveName, currentFile, variables) { | ||
if (this._inConditional()) { | ||
if (!this._isConditional(directiveName)) { | ||
this.currentConditional.addDirective(directive); | ||
return {output: ""}; | ||
} | ||
} | ||
var attributes = this._parseAttributes(directive); | ||
function interpolate() { | ||
for (var i = 0; i < attributes.length; i++) { | ||
var attribute = attributes[i]; | ||
attribute.name = this._interpolate(attribute.name, variables, false); | ||
attribute.value = this._interpolate(attribute.value, variables, false); | ||
} | ||
} | ||
switch (directiveName) { | ||
case "if": | ||
interpolate.apply(this); | ||
return this._handleIf(attributes); | ||
case "elif": | ||
interpolate.apply(this); | ||
return this._handleElseIf(attributes); | ||
case "else": | ||
interpolate.apply(this); | ||
return this._handleElse(); | ||
case "endif": | ||
interpolate.apply(this); | ||
return this._handleEndIf(currentFile, variables); | ||
case "set": | ||
interpolate.apply(this); | ||
return this._handleSet(attributes); | ||
case "include": | ||
interpolate.apply(this); | ||
return this._handleInclude(attributes, currentFile); | ||
} | ||
return {error: "Could not find parse directive #" + directiveName}; | ||
}, | ||
/* Private Methods */ | ||
_interpolate: function(string, variables, shouldWrap) { | ||
var instance = this; | ||
return string.replace(INTERPOLATION_MATCHER, function(variable, variableName) { | ||
var value; | ||
// Either return the variable value or the original expression if it doesn't exist | ||
if (variables[variableName] !== undefined) { | ||
value = variables[variableName]; | ||
} else if (process.env[variableName] !== undefined) { | ||
value = process.env[variableName]; | ||
} | ||
if (value !== undefined) { | ||
if (shouldWrap) { | ||
// Escape all double quotes and wrap the value in double quotes | ||
return instance._wrap(variables[variableName]); | ||
} | ||
return value; | ||
} | ||
return variable; | ||
}); | ||
}, | ||
_parseAttributes: function(directive) { | ||
var attributes = []; | ||
directive.replace(ATTRIBUTE_MATCHER, function(attribute, name, value) { | ||
attributes.push({name: name, value: value}); | ||
}); | ||
return attributes; | ||
}, | ||
_parseExpression: function(expression) { | ||
if (expression.match(INTERPOLATION_MATCHER)) { | ||
return {error: "Could not resolve all variables"} | ||
} | ||
// Return a boolean for the truthiness of the expression | ||
return {truthy: !!eval(expression)}; | ||
}, | ||
_wrap: function(value) { | ||
if (this._shouldWrap(value)) { | ||
return "\"" + value.toString().replace(/"/g, "\\\"") + "\""; | ||
} | ||
return value; | ||
}, | ||
_shouldWrap: function(value) { | ||
var type = typeof value; | ||
return (type !== "boolean" && type !== "number"); | ||
}, | ||
_handleSet: function(attributes) { | ||
if (attributes.length === 2 && attributes[0].name === "var" && | ||
attributes[1].name === "value") { | ||
return {variables: [{ | ||
name: attributes[0].value, | ||
value: attributes[1].value | ||
}]}; | ||
} | ||
return {error: "Directive #set did not contain a 'var' and 'value' attribute"}; | ||
}, | ||
_handleInclude: function(attributes, currentFile) { | ||
if (attributes.length === 1) { | ||
var attribute = attributes[0]; | ||
var output = undefined; | ||
if (attribute.name === "file") { | ||
output = this.ioUtils.readFileSync(currentFile, attribute.value); | ||
} else if (attribute.name === "virtual") { | ||
output = this.ioUtils.readVirtualSync(attribute.value); | ||
} | ||
// TODO: Parse included file here | ||
return {output: output}; | ||
} | ||
return {error: "Directive #include did not contain a 'file' or 'virtual' attribute"}; | ||
}, | ||
_handleIf: function(attributes) { | ||
if (attributes.length === 1 && attributes[0].name === "expr") { | ||
// Create a new conditional, put it on the stack and assign as current conditional | ||
var conditional = new Conditional(attributes[0].value); | ||
this.conditionals.push(conditional); | ||
this.currentConditional = conditional; | ||
return {output: ""}; | ||
} | ||
return {error: "If does not have a single 'expr' attribute"}; | ||
}, | ||
_handleElseIf: function(attributes) { | ||
if (attributes.length === 1 && attributes[0].name === "expr") { | ||
if (!this._inConditional()) { | ||
return {error: "Elif while not inside of If block"}; | ||
} | ||
var conditional = new Conditional(attributes[0].value); | ||
this.conditionals.push(conditional); | ||
this.currentConditional = conditional; | ||
return {output: ""}; | ||
} | ||
return {error: "Elif does not have a single 'expr' attribute"}; | ||
}, | ||
_handleElse: function() { | ||
if (!this._inConditional()) { | ||
return {error: "Else while not inside of If block"}; | ||
} | ||
// As a hack, just provide an always true expression | ||
var conditional = new Conditional("true"); | ||
this.conditionals.push(conditional); | ||
this.currentConditional = conditional; | ||
return {output: ""}; | ||
}, | ||
_handleEndIf: function(currentFile, pageVariables) { | ||
if (!this._inConditional()) { | ||
return {error: "Endif while not inside of If block"}; | ||
} | ||
for (var i = 0; i < this.conditionals.length; i++) { | ||
var conditional = this.conditionals[i]; | ||
var variables = {}; | ||
// Find the first conditional that is true | ||
if (this._parseExpression(conditional.getExpression()).truthy) { | ||
var directiveHandler = new DirectiveHandler(this.ioUtils); | ||
var output = {output: "", variables: {}}; | ||
// Iterate over the directives contained by the conditional, and parse them | ||
for (var j = 0; j < conditional.getDirectives().length; j++) { | ||
var directive = conditional.getDirectives()[j]; | ||
// We can assume this matches the directive format | ||
var directiveName = DIRECTIVE_MATCHER.exec(directive)[1]; | ||
var results = directiveHandler.handleDirective(directive, directiveName, currentFile, | ||
mergeSimpleObject(variables, pageVariables)); | ||
output.output += results.output || ""; | ||
output.variables = mergeSimpleObject(output.variables, results.variables || {}); | ||
} | ||
this.conditionals = []; | ||
this.currentConditional = undefined; | ||
return output; | ||
} | ||
} | ||
this.conditionals = []; | ||
this.currentConditional = undefined; | ||
return {output: ""}; | ||
}, | ||
_isConditional: function(directive) { | ||
return (directive === "if" || directive === "elif" || directive === "else" || directive === "endif"); | ||
}, | ||
_inConditional: function() { | ||
return this.conditionals.length > 0; | ||
} | ||
}; | ||
var ssi = function(inputDirectory, outputDirectory, matcher) { | ||
this.inputDirectory = inputDirectory; | ||
this.documentRoot = inputDirectory; | ||
this.outputDirectory = outputDirectory; | ||
this.matcher = matcher; | ||
this.ioUtils = new IOUtils(this.documentRoot); | ||
this.directiveHandler = new DirectiveHandler(this.ioUtils); | ||
}; | ||
ssi.prototype = { | ||
/* Public Methods */ | ||
compile: function() { | ||
var files = glob.sync(this.inputDirectory + this.matcher); | ||
for (var i = 0; i < files.length; i++) { | ||
var input = files[i]; | ||
var contents = fs.readFileSync(input, {encoding: "utf8"}); | ||
var data = this.parse(input, contents); | ||
var output = input.replace(this.inputDirectory, this.outputDirectory); | ||
this.ioUtils.writeFileSync(output, data.contents); | ||
} | ||
return this.parser.compile(); | ||
}, | ||
parse: function(filename, contents) { | ||
var instance = this; | ||
var variables = {}; | ||
contents = contents.replace(DIRECTIVE_MATCHER, function(directive, directiveName) { | ||
var data = instance.directiveHandler.handleDirective(directive, directiveName, filename, variables); | ||
if (data.error) throw data.error; | ||
for (var key in data.variables) { | ||
if (data.variables.hasOwnProperty(key)) { | ||
variables[data.variables[key].name] = data.variables[key].value; | ||
} | ||
} | ||
return (data && data.output) || ""; | ||
}); | ||
return {contents: contents, variables: variables}; | ||
return this.parser.parse(filename, contents); | ||
} | ||
/* Private Methods */ | ||
}; | ||
@@ -371,2 +22,1 @@ | ||
})(); | ||
{ | ||
"name": "ssi", | ||
"version": "0.1.2", | ||
"version": "0.1.3", | ||
"description": "Server Side Includes for NodeJS", | ||
@@ -5,0 +5,0 @@ "author": "Glenn Nelson <glenn@hexcoder.us> (glenn@hexcoder.us)", |
var assert = require("assert"); | ||
var ssi = require("../index.js"); | ||
var SSI = require("../lib/SSI"); | ||
describe("testing conditional directives", function() { | ||
var parser = new ssi("", "", ""); | ||
var parser = new SSI("", "", ""); | ||
@@ -23,2 +23,3 @@ it("#if should evaluate to true", function() { | ||
var results = parser.parse("", conditional); | ||
assert.equal("elif", results.variables["elif"]); | ||
@@ -25,0 +26,0 @@ }); |
var fs = require("fs"); | ||
var assert = require("assert"); | ||
var ssi = require("../index.js"); | ||
function buildIncludeFile(file) { | ||
return "<!--#include file=\"" + file + "\" -->"; | ||
} | ||
describe("#include file", function() { | ||
@@ -13,13 +11,9 @@ var parser = new ssi("test/html", "/tmp/out", ""); | ||
it("file-same should contain \"LEVEL2\"", function() { | ||
var results = parser.parse("test/html/level1/level2/file-same.shtml", buildIncludeFile("level2.html")); | ||
var filename = "test/html/level1/level2/file-same.shtml"; | ||
var contents = fs.readFileSync(filename, {encoding: "utf8"}); | ||
var results = parser.parse(filename, contents); | ||
assert.equal("LEVEL2\n", results.contents); | ||
assert.equal("LEVEL2\n\n", results.contents); | ||
}); | ||
it("file-relative should contain \"LEVEL1\"", function() { | ||
var results = parser.parse("test/html/level1/level2/file-relative.shtml", buildIncludeFile("../level1.html")); | ||
assert.equal("LEVEL1\n", results.contents); | ||
}); | ||
}); | ||
var fs = require("fs"); | ||
var assert = require("assert"); | ||
var ssi = require("../index.js"); | ||
function buildIncludeVirtual(virtual) { | ||
return "<!--#include virtual=\"" + virtual + "\" -->"; | ||
} | ||
describe("#include virtual", function() { | ||
var parser = new ssi("test/html", "", ""); | ||
it("virtual-same should contain \"ROOT\"", function() { | ||
var results = parser.parse("test/html/level1/level2/virtual-same.shtml", buildIncludeVirtual("root.html")); | ||
it("virtual-same should contain \"LEVEL2\"", function() { | ||
var filename = "test/html/level1/level2/virtual-same.shtml"; | ||
var contents = fs.readFileSync(filename, {encoding: "utf8"}); | ||
var results = parser.parse(filename, contents); | ||
assert.equal("ROOT\n", results.contents); | ||
assert.equal("LEVEL2\n\n", results.contents); | ||
}); | ||
it("virtual-relative should contain \"LEVEL1\"", function() { | ||
var results = parser.parse("test/html/level1/level2/virtual-relative.shtml", buildIncludeVirtual("level1/level1.html")); | ||
var filename = "test/html/level1/level2/virtual-relative.shtml"; | ||
var contents = fs.readFileSync(filename, {encoding: "utf8"}); | ||
var results = parser.parse(filename, contents); | ||
assert.equal("LEVEL1\n", results.contents); | ||
assert.equal("LEVEL1\n\n", results.contents); | ||
}); | ||
it("virtual-absolute should contain \"ROOT\"", function() { | ||
var filename = "test/html/level1/level2/virtual-absolute.shtml"; | ||
var contents = fs.readFileSync(filename, {encoding: "utf8"}); | ||
var results = parser.parse(filename, contents); | ||
assert.equal("ROOT\n\n", results.contents); | ||
}); | ||
}); | ||
var assert = require("assert"); | ||
var ssi = require("../index.js"); | ||
var SSI = require("../lib/SSI.js"); | ||
describe("testing conditional expressions", function() { | ||
var parser = new ssi("", "", "").directiveHandler; | ||
var parser = new SSI("", "", "").directiveHandler; | ||
var variables = { | ||
@@ -8,0 +8,0 @@ "true": true, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
22100
26
529
9
2