Socket
Socket
Sign inDemoInstall

ssi

Package Overview
Dependencies
6
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.0 to 0.1.1

.travis.yml

325

index.js

@@ -7,4 +7,5 @@

var INCLUDE_VIRTUAL = new RegExp(/<!--#include virtual="(.+?)" -->/g);
var INCLUDE_FILE = new RegExp(/<!--#include file="(.+?)" -->/g);
var DIRECTIVE_MATCHER = /<!--#([a-z]+)([ ]+([a-z]+)="(.+?)")* -->/g;
var ATTRIBUTE_MATCHER = /([a-z]+)="(.+?)"/g;
var EXPRESSION_MATCHER = /\$\{(.+?)\}/g;

@@ -14,2 +15,272 @@ (function() {

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 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);
switch (directiveName) {
case "if":
return this._handleIf(attributes);
case "elif":
return this._handleElseIf(attributes);
case "else":
return this._handleElse();
case "endif":
return this._handleEndIf(currentFile, variables);
case "set":
return this._handleSet(attributes);
case "include":
return this._handleInclude(attributes, currentFile);
}
return {error: "Could not find parse directive #" + directiveName};
},
/* Private Methods */
_parseAttributes: function(directive) {
var attributes = [];
directive.replace(ATTRIBUTE_MATCHER, function(attribute, name, value) {
attributes.push({name: name, value: value});
});
return attributes;
},
_parseExpression: function(expression, variables) {
var instance = this;
expression = expression.replace(EXPRESSION_MATCHER, function(variable, variableName) {
// Either return the variable value or the original expression if it doesn't exist
if (variables[variableName] !== undefined) {
// Escape all double quotes and wrap the value in double quotes
return instance._wrap(variables[variableName]);
}
return variable;
});
if (expression.match(EXPRESSION_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];
if (attribute.name === "file") {
return {output: this.ioUtils.readFileSync(currentFile, attribute.value)};
} else if (attribute.name === "virtual") {
return {output: this.ioUtils.readVirtualSync(attribute.value)};
}
}
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(), variables).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) {

@@ -20,2 +291,5 @@ this.inputDirectory = inputDirectory;

this.matcher = matcher;
this.ioUtils = new IOUtils(this.documentRoot);
this.directiveHandler = new DirectiveHandler(this.ioUtils);
};

@@ -33,6 +307,6 @@

var contents = fs.readFileSync(input, {encoding: "utf8"});
contents = this.parse(input, contents);
var data = this.parse(input, contents);
var output = input.replace(this.inputDirectory, this.outputDirectory);
this._writeFile(output, contents);
this.ioUtils.writeFileSync(output, data.contents);
}

@@ -43,38 +317,23 @@ },

var instance = this;
var variables = {};
contents = contents.replace(INCLUDE_VIRTUAL, function(match, virtual) {
return instance._readVirtual(virtual);
});
contents = contents.replace(DIRECTIVE_MATCHER, function(directive, directiveName) {
var data = instance.directiveHandler.handleDirective(directive, directiveName, filename, variables);
contents = contents.replace(INCLUDE_FILE, function(match, file) {
return instance._readFile(filename, file);
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;
},
return {contents: contents, variables: variables};
}
/* Private Methods */
_readVirtual: function(virtual) {
var filename = path.resolve(this.documentRoot, virtual);
return fs.readFileSync(filename, {encoding: "utf8"});
},
_readFile: function(currentFile, file) {
var filename = path.resolve(path.dirname(currentFile), file);
return fs.readFileSync(filename, {encoding: "utf8"});
},
_writeFile: function(filename, contents) {
var directory = path.dirname(filename);
if (!fs.existsSync(directory)) {
// If the file's directory doesn't exists, recusively create it
mkdirp.sync(directory);
}
fs.writeFileSync(filename, contents, {encoding: "utf8"});
}
};

@@ -81,0 +340,0 @@

8

package.json
{
"name": "ssi",
"version": "0.1.0",
"version": "0.1.1",
"description": "Server Side Includes for NodeJS",

@@ -22,6 +22,8 @@ "author": "Glenn Nelson <glenn@hexcoder.us> (glenn@hexcoder.us)",

},
"devDependencies": {},
"devDependencies": {
"mocha": "~1.10.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "mocha"
}
}
node-ssi
========
[![Build Status](https://travis-ci.org/67726e/node-ssi.png)](https://travis-ci.org/67726e/node-ssi)
Server Side Includes for NodeJS
__Note:__ The current version of ssi does all IO synchronously. Further development plans include writing methods asynchronously and migrating current methods to conform to Node conventions for synchronous methods.
### Supported Instructions
```html
<!--#include virtual="" -->
<!--#include file="" -->
<!--#set var="" value="" -->
<!--#if expr="" -->
<!--#elif expr="" -->
<!--#else -->
<!--#endif -->
```
### Installation
```bash
npm install ssi
```
### Usage
```javascript
var ssi = require("ssi");
var inputDirectory = "/tmp/test";
var outputDirectory = "/tmp/output";
var matcher = "/**/*.shtml";
var includes = new ssi(inputDirectory, outputDirectory, matcher);
includes.compile();
```
### Methods
#### parse(filename, contents)
_filename_ `String` path to the file
_contents_ `String` Contents of the file to be parsed
Method returns the parsed contents
#### compile()
Method parses all of the files found by the matcher in the input directory, and writes the files to the output directory with identical names and directory structure.
### License
MIT

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc