Skip to content
Browse files

Initial commit.

  • Loading branch information...
0 parents commit f4655d5d20fd864c796b55d6f18133ee203bfbb6 @jaredhanson committed Jul 18, 2012
Showing with 381 additions and 0 deletions.
  1. +5 −0 .gitignore
  2. +15 −0 .npmignore
  3. +20 −0 LICENSE
  4. +8 −0 Makefile
  5. +29 −0 README.md
  6. +19 −0 lib/element.js
  7. +196 −0 lib/index.js
  8. +29 −0 package.json
  9. +2 −0 test/fixtures/doc.ltxb
  10. +2 −0 test/fixtures/hello.ltxb
  11. +56 −0 test/index-test.js
5 .gitignore
@@ -0,0 +1,5 @@
+# Mac OS X
+.DS_Store
+# Node.js
+node_modules
+npm-debug.log
15 .npmignore
@@ -0,0 +1,15 @@
+# Project
+README.md
+Makefile
+docs/
+examples/
+test/
+
+# Mac OS X
+.DS_Store
+# Node.js
+.npmignore
+node_modules/
+npm-debug.log
+# Git
+.git*
20 LICENSE
@@ -0,0 +1,20 @@
+(The MIT License)
+
+Copyright (c) 2012 Jared Hanson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 Makefile
@@ -0,0 +1,8 @@
+NODE = node
+TEST = ./node_modules/.bin/vows
+TESTS ?= test/*-test.js
+
+test:
+ @NODE_ENV=test NODE_PATH=lib $(TEST) $(TEST_FLAGS) $(TESTS)
+
+.PHONY: test
29 README.md
@@ -0,0 +1,29 @@
+# ltxb
+
+
+## Credits
+
+ - [Jared Hanson](http://github.com/jaredhanson)
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2012 Jared Hanson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 lib/element.js
@@ -0,0 +1,19 @@
+/**
+ * Set element name to `val`.
+ *
+ * The function is used to extend `ltx.Element` with a chainable mechanism to
+ * set an element's name.
+ *
+ * The root element within a Less-Than XML builder file will be extended with
+ * this function. This is the recommended way of setting a root element's name,
+ * since the element is not initialized directly by code, but rather by the
+ * template engine.
+ *
+ * @param {String} val
+ * @return {Element} for chaining
+ * @api public
+ */
+exports.n = function(val) {
+ this.name = val;
+ return this;
+}
196 lib/index.js
@@ -0,0 +1,196 @@
+/**
+ * Module dependencies.
+ */
+var fs = require('fs')
+ , ltx = require('ltx')
+ , element = require('./element');
+
+
+/**
+ * Render a Less-Than XML builder file at the given `path` and callback
+ * `fn(err, str)`.
+ *
+ * @param {String} path
+ * @param {Object|Function} options or callback
+ * @param {Function} fn
+ * @api public
+ */
+function renderFile(path, options, fn) {
+ if ('function' == typeof options) {
+ fn = options, options = {};
+ }
+
+ try {
+ options.filename = path;
+ var str = options.cache
+ ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8'))
+ : fs.readFileSync(path, 'utf8');
+ exports.render(str, options, fn);
+ } catch (err) {
+ fn(err);
+ }
+}
+
+
+/**
+ * Export `renderFile` as module function.
+ *
+ * @api public
+ */
+exports = module.exports = renderFile;
+
+/**
+ * Template function cache.
+ */
+exports.cache = {};
+
+
+/**
+ * Factory function to create new `Element`.
+ *
+ * @return {ltx.Element}
+ * @api private
+ */
+function factory() {
+ var el = new ltx.Element();
+ el.n = element.n;
+ return el
+}
+
+/**
+ * Parse the given `str` of Less-Than XML builder and return a function body.
+ *
+ * @param {String} str
+ * @param {Object} options
+ * @return {String}
+ * @api private
+ */
+function parse(str, options) {
+ var doc = options.document || 'xml'
+ , js = str;
+
+ return ''
+ + 'var ' + doc + ' = factory();\n'
+ + (options.self
+ ? 'var self = locals || {};\n' + js
+ : 'with (locals || {}) {\n' + js + '\n}\n')
+ + 'return ' + doc + '.toString()';
+}
+
+/**
+ * Re-throw the given `err` in context to the `str` of Less-Than XML builder,
+ * `filename`, and `lineno`.
+ *
+ * @param {Error} err
+ * @param {String} str
+ * @param {String} filename
+ * @param {String} lineno
+ * @api private
+ */
+function rethrow(err, str, filename, lineno) {
+ var context = 3
+ , lines = str.split('\n')
+ , start = Math.max(lineno - context, 0)
+ , end = Math.min(lines.length, lineno + context);
+
+ // NOTE: Due to the fact that this "template" is not being parsed line by
+ // line, `lineno` here is always 1. If there is an elegant apprach to
+ // determining the offending line, it is TBD; despite this, the error is
+ // made more useful by including the context and filename.
+
+ // Error context
+ var context = lines.slice(start, end).map(function(line, i){
+ var curr = i + start + 1;
+ return (curr == lineno ? ' >> ' : ' ')
+ + curr
+ + '| '
+ + line;
+ }).join('\n');
+
+ // Alter exception message
+ err.path = filename;
+ err.message = (filename || 'ltxb') + ':' + lineno
+ + '\n' + context + '\n\n' + err.message;
+ throw err;
+}
+
+/**
+ * Compile a `Function` representation of the given Less-Than XML builder `str`.
+ *
+ * Options:
+ *
+ * - `compileDebug` when `false` debugging code is stripped from the compiled template
+ *
+ * @param {String} str
+ * @param {Options} options
+ * @return {Function}
+ * @api public
+ */
+exports.compile = function(str, options) {
+ options = options || {};
+
+ var input = JSON.stringify(str)
+ , filename = options.filename
+ ? JSON.stringify(options.filename)
+ : 'undefined'
+ , fn;
+
+ if (options.compileDebug !== false) {
+ fn = [
+ 'var __stack = { lineno: 1, input: ' + input + ', filename: ' + filename + ' };'
+ , 'try {'
+ , parse(str, options)
+ , '} catch (err) {'
+ , ' rethrow(err, __stack.input, __stack.filename, __stack.lineno);'
+ , '}'
+ ].join('\n');
+ } else {
+ fn = parse(str, options);
+ }
+
+ fn = new Function('locals, rethrow, factory', fn);
+ return function(locals) {
+ return fn(locals, rethrow, factory);
+ };
+}
+
+/**
+ * Render the given `str` of Less-Than XML builder and invoke the callback
+ * `fn(err, str)`.
+ *
+ * Options:
+ *
+ * - `cache` enable template caching
+ * - `filename` filename required for caching
+ *
+ * @param {String} str
+ * @param {Object|Function} options or fn
+ * @param {Function} fn
+ * @api public
+ */
+exports.render = function(str, options, fn) {
+ if ('function' == typeof options) {
+ fn = options, options = {};
+ }
+
+ // cache requires .filename
+ if (options.cache && !options.filename) {
+ return fn(new Error('the "filename" option is required for caching'));
+ }
+
+ try {
+ var path = options.filename;
+ var tmpl = options.cache
+ ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options))
+ : exports.compile(str, options);
+ fn(null, tmpl(options));
+ } catch (err) {
+ fn(err);
+ }
+}
+
+
+/**
+ * Express support.
+ */
+exports.__express = renderFile;
29 package.json
@@ -0,0 +1,29 @@
+{
+ "name": "ltxb",
+ "version": "0.0.0",
+ "description": "Less-Than XML builder templates for Node.js.",
+ "keywords": ["xml", "template", "view", "engine", "express"],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/jaredhanson/ltxb.git"
+ },
+ "bugs": {
+ "url": "http://github.com/jaredhanson/ltxb/issues"
+ },
+ "author": { "name": "Jared Hanson", "email": "jaredhanson@gmail.com", "url": "http://www.jaredhanson.net/" },
+ "licenses": [ {
+ "type": "MIT",
+ "url": "http://www.opensource.org/licenses/MIT"
+ } ],
+ "main": "./lib",
+ "dependencies": {
+ "ltx": "0.2.x"
+ },
+ "devDependencies": {
+ "vows": "0.6.x"
+ },
+ "scripts": {
+ "test": "NODE_PATH=lib node_modules/.bin/vows test/*-test.js"
+ },
+ "engines": { "node": ">= 0.4.0" }
+}
2 test/fixtures/doc.ltxb
@@ -0,0 +1,2 @@
+doc.n('foo')
+ .c('bar')
2 test/fixtures/hello.ltxb
@@ -0,0 +1,2 @@
+xml.n('greetings')
+ .c('hello', {'name': name })
56 test/index-test.js
@@ -0,0 +1,56 @@
+var vows = require('vows');
+var assert = require('assert');
+var ltxb = require('index');
+
+
+vows.describe('ltxb').addBatch({
+
+ 'module': {
+ 'should export module function': function () {
+ assert.isFunction(ltxb);
+ },
+ 'should export compile and render function': function () {
+ assert.isFunction(ltxb.compile);
+ assert.isFunction(ltxb.render);
+ },
+ 'should support express': function () {
+ assert.isFunction(ltxb.__express);
+ assert.strictEqual(ltxb.__express, ltxb);
+ },
+ },
+
+ 'render file': {
+ topic: function() {
+ var self = this;
+ var options = { name: 'world' };
+ ltxb(__dirname + '/fixtures/hello.ltxb', options, function(err, res) {
+ self.callback(err, res);
+ });
+ },
+
+ 'should not error' : function(err, res) {
+ assert.isNull(err);
+ },
+ 'should render correctly' : function(err, res) {
+ assert.equal(res, '<greetings><hello name="world"/></greetings>');
+ },
+ },
+
+ 'render file with document option': {
+ topic: function() {
+ var self = this;
+ var options = { document: 'doc' };
+ ltxb(__dirname + '/fixtures/doc.ltxb', options, function(err, res) {
+ self.callback(err, res);
+ });
+ },
+
+ 'should not error' : function(err, res) {
+ assert.isNull(err);
+ },
+ 'should render correctly' : function(err, res) {
+ assert.equal(res, '<foo><bar/></foo>');
+ },
+ },
+
+}).export(module);

0 comments on commit f4655d5

Please sign in to comment.
Something went wrong with that request. Please try again.