Skip to content
Browse files

first commit

  • Loading branch information...
0 parents commit 971718f4b8985921b391bf41137fd7702b4d20e6 @tasogarepg committed Feb 3, 2012
Showing with 438 additions and 0 deletions.
  1. +13 −0 .gitignore
  2. +77 −0 README.md
  3. +164 −0 lib/node-pages.js
  4. +13 −0 package.json
  5. +3 −0 test/mocha.opts
  6. +168 −0 test/node-pages.test.js
13 .gitignore
@@ -0,0 +1,13 @@
+# common
+.DS_Store
+
+# backup
+*~
+*.bak
+
+# IDE
+.project
+.settings
+
+# Node.js
+node_modules
77 README.md
@@ -0,0 +1,77 @@
+# node-pages
+
+A simple template engine.
+Cacheable and Trackable.
+You can make breakpoints on the debugger.
+
+## Installation
+
+ $ npm install node-pages
+
+## Example
+template1.npg
+
+ <? if (arg) { ?>
+ <p><?= arg.name ?></p>
+ <? } ?>
+
+example.js
+
+ var pages = require('node-pages').newInstance({
+ srcPath : '/path/to/template1.npg';
+ });
+
+ var arg = {name : 'tasogare'};
+ var str = pages.render(arg);
+
+## Custom tags
+
+Custom tags (e.g. &lt;% %&gt; )
+
+ var pages = require('node-pages').newInstance({
+ openWord : '<%',
+ closeWord : '%>',
+ srcPath : '/path/to/template2.npg';
+ });
+
+template2.npg
+
+ <% if (arg) { %>
+ <p><%= arg.name %></p>
+ <% } %>
+
+## Custom render arg name
+
+Custom render arg name (e.g. it )
+
+ var pages = require('node-pages').newInstance({
+ renderArgName : 'it',
+ srcPath : '/path/to/template3.npg';
+ });
+
+ var arg = {name : 'tasogare'};
+ var str = pages.render(arg);
+
+template3.npg
+
+ <? if (it) { ?>
+ <p><?= it.name ?></p>
+ <? } ?>
+
+
+## Html escaped and unescaped
+
+ * Escaped with `<?= val ?>`
+ * Unescaped with `<?- val ?>`
+
+## Options
+
+ - `srcPath` Template file path (absolute path)
+ - `openWord` Open tag. ("<?" is default.)
+ - `closeWord` Close tag. ("?>" is default.)
+ - `renderArgName` Render arg name. ("arg" is default.)
+ - `workDir` Directory of compiled js file. ("node-pages/lib/work" is default.)
+
+## License
+
+The MIT License
164 lib/node-pages.js
@@ -0,0 +1,164 @@
+'use strict';
+var fs = require('fs');
+var path = require('path');
+
+module.exports = Pages;
+
+function Pages(conf) {
+ conf = conf || {};
+
+ this.openWord = conf.openWord || Pages.DEFAULT_OPEN_WORD;
+ this.closeWord = conf.closeWord || Pages.DEFAULT_CLOSE_WORD;
+ this.renderArgName = conf.renderArgName || Pages.DEFAULT_RENDER_ARG_NAME;
+ this.workDir = conf.workDir || Pages.DEFAULT_WORK_DIR;
+ this.srcPath = null;
+ this.dstPath = null;
+ this.name = null;
+
+ if (conf.srcPath) {
+ this.loadFileSync(conf.srcPath);
+ }
+}
+
+Pages.newInstance = function(conf) {
+ return new Pages(conf);
+};
+
+Pages.DEFAULT_OPEN_WORD = '<?';
+Pages.DEFAULT_CLOSE_WORD = '?>';
+Pages.DEFAULT_RENDER_ARG_NAME = 'arg';
+Pages.DEFAULT_WORK_DIR = path.join(__dirname, 'work');
+Pages.cache = [];
+
+Pages.prototype.reloadFileSync = function() {
+ if (!this.srcPath) return;
+ this.clear();
+ this.loadFileSync(this.srcPath);
+};
+
+Pages.prototype.clear = function() {
+ this.deleteCache();
+ this.deleteModule();
+};
+
+Pages.prototype.loadFileSync = function(srcPath) {
+ if (!srcPath) return;
+ this.srcPath = require.resolve(srcPath);
+ this.name = this.makeName(this.srcPath);
+ this.dstPath = this.makeDstPath(this.workDir, this.name);
+ if (this.isCached()) return;
+ this.setCache(
+ this.setModuleSync(
+ this.createJsFileSync(
+ this.parse(
+ stripBOM(fs.readFileSync(this.srcPath, 'utf8'))))));
+};
+
+Pages.prototype.makeName = function(filePath) {
+ return filePath && String(filePath).
+ replace(/[\/\\]/g, '__').
+ replace(/[\.:]/g, '-');
+};
+
+Pages.prototype.makeDstPath = function(workDir, name) {
+ return path.join(workDir, name) + '.js';
+};
+
+Pages.prototype.setCache = function(mdl, name) {
+ name = name || this.name;
+ if (name && mdl && mdl.render) {
+ Pages.cache[name] = mdl.render;
+ }
+};
+
+Pages.prototype.deleteCache = function(name) {
+ name = name || this.name;
+ if (name && Pages.cache[name]) {
+ delete Pages.cache[name];
+ }
+};
+
+Pages.prototype.isCached = function(name) {
+ name = name || this.name;
+ return !!Pages.cache[name];
+};
+
+Pages.prototype.setModuleSync = function(filePath) {
+ filePath = filePath || this.dstPath;
+ var mdl = null;
+ try {
+ mdl = require(filePath);
+ } catch(e) {
+ throw new Error(filePath + "\n" + e.stack);
+ }
+ return mdl;
+};
+
+Pages.prototype.deleteModule = function(filePath) {
+ filePath = filePath || this.dstPath;
+ if (filePath && require.cache[filePath]) {
+ delete require.cache[filePath];
+ }
+};
+
+Pages.prototype.createJsFileSync = function(buf, name, workDir) {
+ name = name || this.name;
+ workDir = workDir || this.workDir;
+ if (!fs.existsSync(workDir)) {
+ fs.mkdirSync(workDir, parseInt('755', 8));
+ }
+ var filePath = this.makeDstPath(workDir, name);
+ fs.writeFileSync(filePath, buf);
+ return filePath;
+};
+
+Pages.prototype.parse = function(src) {
+ src = this.closeWord + src + this.openWord;
+
+ var buf = "exports.render = function(" + this.renderArgName + "){\n var renderStr = '';\n";
+ for (var i=0, len=src.length; i<len;) {
+ var start, end, str;
+ start = src.indexOf(this.closeWord, i) + this.closeWord.length;
+ end = src.indexOf(this.openWord, start);
+ if (end == -1) break;
+ str = src.substring(start, end).
+ replace(/\\/g, '\\\\').
+ replace(/'/g, "\\'").
+ replace(/\t/g, ' ').
+ replace(/\r/g, '').
+ replace(/\n/g, '\\n');
+ buf += " renderStr += '" + str + "';\n";
+
+ start = end + this.openWord.length;
+ end = src.indexOf(this.closeWord, start);
+ if (end == -1) break;
+ str = src.substring(start, end);
+ switch (str.substring(0, 1)) {
+ case '-':
+ str = str.substring(1);
+ buf += " renderStr += " + str + ";\n";
+ break;
+ case '=':
+ str = str.substring(1);
+ buf += " renderStr += escape(" + str + ");\n";
+ break;
+ default:
+ buf += str + "\n";
+ }
+ i = end;
+ }
+ buf += " return renderStr;\n};\n";
+ buf += "function escape(str){\n" +
+ " return str && str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n" +
+ "}\n";
+ return buf;
+};
+
+Pages.prototype.render = function(arg) {
+ var func = Pages.cache[this.name];
+ return func ? func(arg) : '';
+};
+
+function stripBOM(dat) {
+ return (dat.charCodeAt(0) === 0xFEFF) ? dat.slice(1) : dat;
+}
13 package.json
@@ -0,0 +1,13 @@
+{
+ "name": "node-pages",
+ "version": "0.1.0",
+ "description": "A simple template engine. Cacheable and Trackable.",
+ "keywords": ["template", "view"],
+ "author": "tasogarepg <tasogare.pg@gmail.com>",
+ "main": "./lib/node-pages.js",
+ "engines": { "node": ">= 0.7.1 < 0.8.0" },
+ "dependencies": {},
+ "devDependencies": {
+ "mocha": "*"
+ }
+}
3 test/mocha.opts
@@ -0,0 +1,3 @@
+--ui bdd
+--reporter spec
+--timeout 5000
168 test/node-pages.test.js
@@ -0,0 +1,168 @@
+var assert = require('assert');
+var fs = require('fs');
+
+var Pages = require('../lib/node-pages.js');
+
+var templateFile = __dirname + '/template.npg';
+
+var pages = null;
+
+describe('node-pages', function() {
+ after(function() {
+ fs.unlinkSync(templateFile);
+ fs.rmdirSync(Pages.DEFAULT_WORK_DIR);
+ });
+
+ afterEach(function() {
+ if (pages) {
+ pages.clear();
+ fs.unlinkSync(pages.dstPath);
+ pages = null;
+ }
+ });
+
+ it('output string', function() {
+ fs.writeFileSync(templateFile, 'abcd');
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ assert.equal(pages.render(), 'abcd');
+ });
+
+ it('output back-slashes', function() {
+ fs.writeFileSync(templateFile, '\\');
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ assert.equal(pages.render(), '\\');
+ });
+
+ it('output double-quotes', function() {
+ fs.writeFileSync(templateFile, '"');
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ assert.equal(pages.render(), '"');
+ });
+
+ it('output single-quotes', function() {
+ fs.writeFileSync(templateFile, "'");
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ assert.equal(pages.render(), "'");
+ });
+
+ it('output tab', function() {
+ fs.writeFileSync(templateFile, '\t');
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ assert.equal(pages.render(), ' ');
+ });
+
+ it('output \\r', function() {
+ fs.writeFileSync(templateFile, '\r');
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ assert.equal(pages.render(), '');
+ });
+
+ it('output \\n', function() {
+ fs.writeFileSync(templateFile, '1\n2');
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ assert.equal(pages.render(), '1\n2');
+ });
+
+ it('output unescaped val', function() {
+ fs.writeFileSync(templateFile, '<?- arg.str ?>');
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ var arg = {str : '&<>"'};
+ assert.equal(pages.render(arg), '&<>"');
+ });
+
+ it('output html escaped val', function() {
+ fs.writeFileSync(templateFile, '<?= arg.str ?>');
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ var arg = {str : '&<>"'};
+ assert.equal(pages.render(arg), '&amp;&lt;&gt;&quot;');
+ });
+
+ it('composite template', function() {
+ fs.writeFileSync(templateFile,
+ '<? if(arg.str1 == arg.str2){ ?><?= arg.str1 ?><? }else{ ?><?= arg.str2 ?><? } ?>');
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ var arg = {str1 : '1', str2 : '2'};
+ assert.equal(pages.render(arg), '2');
+ });
+
+ it('nested', function() {
+ var templateFile1 = __dirname + '/template1.npg';
+ fs.writeFileSync(templateFile1,
+ '<html><body><?- arg ?></body></html>');
+ var pages1 = Pages.newInstance({
+ srcPath : templateFile1
+ });
+ var templateFile2 = __dirname + '/template2.npg';
+ fs.writeFileSync(templateFile2,
+ '<p><?= arg.name ?></p>');
+ var pages2 = Pages.newInstance({
+ srcPath : templateFile2
+ });
+ var arg = {name : 'tasogare'};
+ assert.equal(pages1.render(pages2.render(arg)),
+ '<html><body><p>tasogare</p></body></html>');
+ pages1.clear();
+ pages2.clear();
+ fs.unlinkSync(pages1.dstPath);
+ fs.unlinkSync(pages2.dstPath);
+ fs.unlinkSync(templateFile1);
+ fs.unlinkSync(templateFile2);
+ });
+
+ it('cached', function() {
+ fs.writeFileSync(templateFile, 'first');
+ pages = Pages.newInstance({
+ srcPath : templateFile
+ });
+ assert.equal(pages.render(), 'first');
+ fs.writeFileSync(templateFile, 'second');
+ var pages2 = Pages.newInstance({
+ srcPath : templateFile
+ });
+ assert.equal(pages2.render(), 'first');
+ });
+
+ it('custom tags', function() {
+ fs.writeFileSync(templateFile,
+ '<% if(arg.str1 == arg.str2){ %><%= arg.str1 %><% }else{ %><%= arg.str2 %><% } %>');
+ pages = Pages.newInstance({
+ openWord : '<%',
+ closeWord : '%>',
+ srcPath : templateFile
+ });
+ var arg = {str1 : '1', str2 : '2'};
+ assert.equal(pages.render(arg), '2');
+ });
+
+ it('custom arg name', function() {
+ fs.writeFileSync(templateFile,
+ '<? if(it.str1 == it.str2){ ?><?= it.str1 ?><? }else{ ?><?= it.str2 ?><? } ?>');
+ pages = Pages.newInstance({
+ renderArgName : 'it',
+ srcPath : templateFile
+ });
+ var arg = {str1 : '1', str2 : '2'};
+ assert.equal(pages.render(arg), '2');
+ });
+
+});

0 comments on commit 971718f

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