Comparing version 0.1.7 to 0.1.8
154
ltl.js
@@ -7,2 +7,4 @@ /** | ||
var isBrowser = (typeof window == 'object'); | ||
// Some HTML tags won't have end tags. | ||
@@ -44,2 +46,24 @@ var selfClosePattern = /^(!DOCTYPE|area|base|br|hr|img|input|link|meta|-|\/\/)(\b|$)/; | ||
// When compilation fails, we can re-run in debug mode. | ||
function debug(output, settings) { | ||
var fs = require('fs'); | ||
var dir = process.cwd() + '/.debug'; | ||
var name = (settings.name || 'template').replace(/[\/\\]/g, '__'); | ||
try { | ||
fs.mkdirSync(dir); | ||
} | ||
catch (e) { | ||
// Probably already exists. | ||
} | ||
fs.writeFileSync(dir + '/' + name + '.js', 'module.exports=' + output); | ||
try { | ||
require(dir + '/' + name); | ||
} | ||
catch (e) { | ||
name = (settings.name ? '"' + settings.name + '"' : 'template'); | ||
e.message = 'Ltl failed to compile ' + name + '. ' + e.message; | ||
throw e; | ||
} | ||
} | ||
// Public API. | ||
@@ -49,3 +73,3 @@ var ltl = { | ||
// Allow users to see what version of ltl they're using. | ||
version: '0.1.7', | ||
version: '0.1.8', | ||
@@ -55,3 +79,3 @@ // Store all of the templates that have been compiled. | ||
// Default compile options. | ||
// Default compile settings. | ||
_options: { | ||
@@ -61,3 +85,4 @@ tabWidth: 4, | ||
contextVar: 'c', | ||
partsVar: 'p' | ||
partsVar: 'p', | ||
enableDebug: false | ||
}, | ||
@@ -73,8 +98,13 @@ | ||
// Copy default options. | ||
options = options || {}; | ||
for (var name in this._options) { | ||
if (typeof options[name] == 'undefined') { | ||
options[name] = this._options[name]; | ||
} | ||
// Copy the default options. | ||
var settings = { | ||
tabWidth: this._options.tabWidth, | ||
outputVar: this._options.outputVar, | ||
contextVar: this._options.contextVar, | ||
partsVar: this._options.partsVar, | ||
space: this._options.space, | ||
enableDebug: this._options.enableDebug | ||
}; | ||
for (var name in options) { | ||
settings[name] = options[name]; | ||
} | ||
@@ -84,8 +114,8 @@ | ||
var vars = varCharacters; | ||
vars = vars.replace(options.contextVar, ''); | ||
vars = vars.replace(options.outputVar, ''); | ||
vars = vars.replace(options.partsVar, ''); | ||
vars = vars.replace(settings.contextVar, ''); | ||
vars = vars.replace(settings.outputVar, ''); | ||
vars = vars.replace(settings.partsVar, ''); | ||
if (options.space) { | ||
options.space = escapeBlock(options.space); | ||
if (settings.space) { | ||
settings.space = escapeBlock(settings.space); | ||
} | ||
@@ -105,3 +135,3 @@ | ||
// Be lenient with mixed tabs and spaces, assuming tab width of 4. | ||
var tabReplacement = Array(options.tabWidth + 1).join(' '); | ||
var tabReplacement = Array(settings.tabWidth + 1).join(' '); | ||
code = code.replace(/\t/g, tabReplacement); | ||
@@ -121,3 +151,3 @@ | ||
var tagDepth = 0; | ||
var output = 'var ' + options.outputVar + "='"; | ||
var output = 'var ' + settings.outputVar + "='"; | ||
@@ -133,3 +163,3 @@ var varIndex = 0; | ||
} else { | ||
output += options.outputVar + "+='"; | ||
output += settings.outputVar + "+='"; | ||
} | ||
@@ -156,5 +186,16 @@ mode = textMode; | ||
// Some options should be passed through. | ||
var blockOptions = { | ||
outputVar: settings.outputVar, | ||
contextVar: settings.contextVar, | ||
partsVar: settings.partsVar, | ||
space: settings.space, | ||
enableDebug: settings.enableDebug | ||
}; | ||
// If we're in a "call" block, compile the contents. | ||
if (blockFilter == 'call') { | ||
appendText('html', "'+this['" + blockName + "'].call(this," + options.contextVar + (text ? ',' + ltl.compile(text) : '') + ")+'"); | ||
appendText('html', | ||
"'+this['" + blockName + "'].call(this," + settings.contextVar + | ||
(text ? ',' + ltl.compile(text, blockOptions) : '') + ")+'"); | ||
return; | ||
@@ -166,3 +207,3 @@ } | ||
if (blockFilter == 'set') { | ||
block = ltl.compile(text).toString(); | ||
block = ltl.compile(text, blockOptions).toString(); | ||
} else { | ||
@@ -213,9 +254,9 @@ block = "function(){return '" + escapeBlock(text) + "'}"; | ||
text = trim(text); | ||
if (options.space) { | ||
if (settings.space) { | ||
if (hasHtmlOutput) { | ||
text = '\n' + text; | ||
} | ||
text = text.replace(/\n/g, '\n' + repeat(options.space, tagDepth)); | ||
text = text.replace(/\n/g, '\n' + repeat(settings.space, tagDepth)); | ||
if (blockTag) { | ||
text += '\n' + repeat(options.space, tagDepth - 1); | ||
text += '\n' + repeat(settings.space, tagDepth - 1); | ||
} | ||
@@ -255,4 +296,4 @@ } | ||
} | ||
else if (options.space) { | ||
html = '\\n' + repeat(options.space, tagDepth) + html; | ||
else if (settings.space) { | ||
html = '\\n' + repeat(settings.space, tagDepth) + html; | ||
} | ||
@@ -267,3 +308,3 @@ appendText('html', html); | ||
function transformScript(script) { | ||
var c = options.contextVar; | ||
var c = settings.contextVar; | ||
var found = false; | ||
@@ -340,3 +381,3 @@ | ||
if (!isProperty && !isLoopVar) { | ||
tokens[i] = options.contextVar + '.' + token; | ||
tokens[i] = settings.contextVar + '.' + token; | ||
} | ||
@@ -386,4 +427,11 @@ } | ||
var line = lines[i]; | ||
// Mitigate recursion past 100 deep. | ||
var maxT = 1e2; | ||
// In debug mode, make JS line numbers match Ltl line numbers. | ||
if (settings.enableDebug && i) { | ||
output += mode == 'html' ? "' +\n'" : '\n'; | ||
} | ||
// If the line is all whitespace, ignore it. | ||
@@ -397,2 +445,7 @@ if (!/\S/.test(line)) { | ||
// In debug mode, make indentation match. | ||
if (settings.enableDebug) { | ||
output += line.substr(0, spaces); | ||
} | ||
// If this is our first time seeing leading spaces, that's our tab width. | ||
@@ -404,3 +457,3 @@ if (spaces > 0 && !currentTabWidth) { | ||
// Calculate the number of levels of indentation. | ||
var indent = spaces ? Math.round(spaces / currentTabWidth) : 0; | ||
indent = spaces ? Math.round(spaces / currentTabWidth) : 0; | ||
@@ -432,4 +485,4 @@ // If we're in a block, we can append or close it. | ||
if (controlPattern.test(line)) { | ||
var code = transformScript(line); | ||
appendText('script', code); | ||
var script = transformScript(line); | ||
appendText('script', script); | ||
} | ||
@@ -445,3 +498,3 @@ | ||
if (command == 'get') { | ||
appendText('html', "'+" + options.partsVar + "['" + blockName + "'].call(this," + options.contextVar + ")+'"); | ||
appendText('html', "'+" + settings.partsVar + "['" + blockName + "'].call(this," + settings.contextVar + ")+'"); | ||
hasGets = true; | ||
@@ -611,8 +664,8 @@ } | ||
if (tag == 'html' && !/DOCTYPE/.test(output)) { | ||
html = '<!DOCTYPE html>' + (options.space ? '\\n' : '') + html; | ||
html = '<!DOCTYPE html>' + (settings.space ? '\\n' : '') + html; | ||
} | ||
// Prepend whitespace if requested via options.space. | ||
if (options.space) { | ||
html = repeat(options.space, tagDepth) + html; | ||
// Prepend whitespace if requested via settings.space. | ||
if (settings.space) { | ||
html = repeat(settings.space, tagDepth) + html; | ||
// Prepend a line break if this isn't the first tag. | ||
@@ -641,3 +694,3 @@ if (hasHtmlOutput) { | ||
// Allow same-line tag open/close in options.space mode. | ||
// Allow same-line tag open/close in settings.space mode. | ||
previousTag = tag; | ||
@@ -666,3 +719,3 @@ if (!selfClosePattern.test(tag)) { | ||
// Add the return statement (ending concatenation, where applicable). | ||
appendText('script', 'return ' + options.outputVar); | ||
appendText('script', 'return ' + settings.outputVar); | ||
@@ -677,10 +730,19 @@ if (blockSets.length) { | ||
} | ||
output = 'eval.f=function(' + options.contextVar + (hasGets ? ',' + options.partsVar : '') + '){' + output + '}'; | ||
output = 'function(' + settings.contextVar + (hasGets ? ',' + settings.partsVar : '') + '){' + output + '}'; | ||
// Evaluate the template as a function. | ||
try { | ||
eval(output); | ||
eval('eval.f=' + output); // jshint ignore:line | ||
} | ||
catch (e) { | ||
// Anyone hitting an error here should submit an issue on GitHub. | ||
/* istanbul ignore next */ | ||
throw "Could not save output as a function: " + output; | ||
// If we failed in a dev environment in Node, we can try to debug it. | ||
if (process && settings.enableDebug) { | ||
debug(output, settings); | ||
} | ||
// Otherwise, just fail. | ||
else { | ||
var name = (settings.name ? '"' + settings.name + '"' : 'template'); | ||
e.message = 'Ltl failed to compile ' + name + '. ' + e.message; | ||
throw e; | ||
} | ||
} | ||
@@ -690,4 +752,4 @@ var template = eval.f; | ||
// If there's a name specified, cache the template with that name. | ||
if (options.name) { | ||
this.cache[options.name] = template; | ||
if (settings.name) { | ||
this.cache[settings.name] = template; | ||
} | ||
@@ -699,9 +761,9 @@ | ||
if (typeof window == 'undefined') { | ||
module.exports = ltl; | ||
if (isBrowser) { | ||
window.ltl = ltl; | ||
} | ||
else { | ||
window.ltl = ltl; | ||
module.exports = ltl; | ||
} | ||
})(); |
@@ -14,3 +14,3 @@ { | ||
], | ||
"version": "0.1.7", | ||
"version": "0.1.8", | ||
"main": "ltl", | ||
@@ -17,0 +17,0 @@ "homepage": "http://lighter.io/ltl", |
@@ -35,2 +35,3 @@ # Ltl | ||
* `name` will cause the template to cache at `ltl.templates[name]` | ||
* `space` causes HTML to be indented, using `space` as indentation. | ||
@@ -37,0 +38,0 @@ ### ltl.setOption(name, value) |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
35583
703
336
4