From 0ce7f07739be8e5eb86ea2220ca161d5ee79dcd0 Mon Sep 17 00:00:00 2001 From: queckezz Date: Wed, 29 Jan 2014 21:12:19 +0100 Subject: [PATCH 1/6] rework --- index.js | 129 +++++++++++-------------------- package.json | 8 +- runtime.js | 209 --------------------------------------------------- 3 files changed, 47 insertions(+), 299 deletions(-) delete mode 100644 runtime.js diff --git a/index.js b/index.js index 8324273..1230e3f 100644 --- a/index.js +++ b/index.js @@ -1,116 +1,73 @@ -var fs = require('fs') - , jade = require('jade') - , path = require('path') - , debug = require('debug')('component-jade'); - - /** - * Replace Jade files with compiled Javascript files. - * - * @param {Builder|Object} builder (or options) + * Module depencenies. */ -module.exports = function (builder) { - if ('function' == typeof builder.build) return plugin(builder); - - var options = builder; - return function (builder) { - plugin(builder, options); - }; -}; - +var basename = require('path').basename; +var extname = require('path').extname; +var debug = require('debug')('component-jade'); +var jade = require('jade'); /** - * Apply the plugin. - * - * @param {Builder} builder - * @param {Object} options (optional) + * Export `templates`. */ -function plugin (builder, options) { - options || (options = {}); - - // Before processing any scripts, convert `.jade` files to Javascript. - builder.hook('before scripts', jadeCompiler(options)); - - if (options.html) return; // don't need the runtime - - // Add the runtime.js to our top-level package's `scripts` array. - debug('adding jade-runtime.js to %s', builder.config.name); - // Add our runtime to the builder, and add a require call for our runtime, - // so it's global for all future template functions. - var runtime = fs.readFileSync(__dirname + '/runtime.js', 'utf8'); - builder.addFile('scripts', 'jade-runtime.js', runtime); - builder.append('require("' + builder.config.name + '/jade-runtime");\n'); -} - +module.exports = templates; /** - * Create a Jade compiler function with `options`. + * Compile jade templates. * - * @param {Object} options (optional) - * @return {Function} + * @param {Bool} plain */ -function jadeCompiler (options) { - options || (options = {}); - - return function (pkg, callback) { - - // Grab our Jade templates. - if (!pkg.config.templates) return callback(); - var files = pkg.config.templates.filter(filterJade); +function templates (plain) { + return function (build, done) { + setImmediate(done); + build.map('templates', function(file, conf){ + if (!file.contents) return; + if ('.jade' != extname(file.filename)) return; + debug('compiling: %s', conf.path()); - files.forEach(function (file) { - debug('compiling: %s', pkg.path(file)); - - // Read and compile our Jade. - var fullPath = pkg.path(file); - var string = fs.readFileSync(fullPath, 'utf8'); - var method = options.html ? 'render' : 'compileClient'; - var compiled = jade[method](string, { + var opts = { compileDebug: false, - filename: fullPath - }); - - if (options.html) compiled = escapeHtml(compiled); + filename: conf.path() + } - // Add our new compiled version to the package, with the same name. - pkg.removeFile('templates', file); - file = file.slice(0, file.length - 5) + '.js'; - pkg.addFile('scripts', file, 'module.exports = ' + compiled); + if (plain) return html(file, opts); + return template(file, opts); }); - - callback(); - }; + } } - /** - * Escape an HTML `string` when we're outputting straight HTML. + * Compile `file` to an html string. * - * @param {String} string - * @return {String} + * @param {String} file + * @param {Function} done */ -function escapeHtml (string) { - string = string - .replace(/[\\"']/g, '\\$&') // escape slashes and quotes - .replace(/\u0000/g, '\\0') - .replace(/\n/g,'\'+\n\''); // line breaks should be concatenated - string = '\'' + string + '\''; - return string; +function html (file, opts) { + console.log('html'); + file.filename = basename(file.filename, '.jade') + '.html'; + file.contents = jade.render(file.contents, opts); + return file; } - /** - * Filter for `.jade` files. + * Compile `file` to a reusable template function. * - * @param {String} filename - * @return {Boolean} + * @param {String} file + * @param {Function} done */ -function filterJade (filename) { - return path.extname(filename) === '.jade'; +function template (file, opts) { + console.log('template'); + var fn = jade.compileClient(file.contents, opts); + file.filename = basename(file.filename, '.jade') + '.js'; + + file.contents = 'var jade = require(\'jade-runtime\');' + + 'module.exports = ' + + fn.toString(); + + return file; } diff --git a/package.json b/package.json index 4ee0a1b..5e4866e 100644 --- a/package.json +++ b/package.json @@ -13,11 +13,11 @@ "url": "git://github.com/segmentio/component-jade.git" }, "dependencies": { - "debug": "~0.7.4", - "jade": "~1.1.4" + "debug": "~0.7.4" }, - "devDependencies" : { - "component-builder" : "~0.11.2" + "devDependencies": { + "jade": "~1.1.5", + "component-builder": "~0.12.0" }, "main": "index.js" } diff --git a/runtime.js b/runtime.js deleted file mode 100644 index 2c61761..0000000 --- a/runtime.js +++ /dev/null @@ -1,209 +0,0 @@ - -jade = (function(exports){ -'use strict'; - -/** - * Merge two attribute objects giving precedence - * to values in object `b`. Classes are special-cased - * allowing for arrays and merging/joining appropriately - * resulting in a string. - * - * @param {Object} a - * @param {Object} b - * @return {Object} a - * @api private - */ - -exports.merge = function merge(a, b) { - if (arguments.length === 1) { - var attrs = a[0]; - for (var i = 1; i < a.length; i++) { - attrs = merge(attrs, a[i]); - } - return attrs; - } - var ac = a['class']; - var bc = b['class']; - - if (ac || bc) { - ac = ac || []; - bc = bc || []; - if (!Array.isArray(ac)) ac = [ac]; - if (!Array.isArray(bc)) bc = [bc]; - a['class'] = ac.concat(bc).filter(nulls); - } - - for (var key in b) { - if (key != 'class') { - a[key] = b[key]; - } - } - - return a; -}; - -/** - * Filter null `val`s. - * - * @param {*} val - * @return {Boolean} - * @api private - */ - -function nulls(val) { - return val != null && val !== ''; -} - -/** - * join array as classes. - * - * @param {*} val - * @return {String} - */ -exports.joinClasses = joinClasses; -function joinClasses(val) { - return Array.isArray(val) ? val.map(joinClasses).filter(nulls).join(' ') : val; -} - -/** - * Render the given classes. - * - * @param {Array} classes - * @param {Array.} escaped - * @return {String} - */ -exports.cls = function cls(classes, escaped) { - var buf = []; - for (var i = 0; i < classes.length; i++) { - if (escaped && escaped[i]) { - buf.push(exports.escape(joinClasses([classes[i]]))); - } else { - buf.push(joinClasses(classes[i])); - } - } - var text = joinClasses(buf); - if (text.length) { - return ' class="' + text + '"'; - } else { - return ''; - } -}; - -/** - * Render the given attribute. - * - * @param {String} key - * @param {String} val - * @param {Boolean} escaped - * @param {Boolean} terse - * @return {String} - */ -exports.attr = function attr(key, val, escaped, terse) { - if ('boolean' == typeof val || null == val) { - if (val) { - return ' ' + (terse ? key : key + '="' + key + '"'); - } else { - return ''; - } - } else if (0 == key.indexOf('data') && 'string' != typeof val) { - return ' ' + key + "='" + JSON.stringify(val).replace(/'/g, ''') + "'"; - } else if (escaped) { - return ' ' + key + '="' + exports.escape(val) + '"'; - } else { - return ' ' + key + '="' + val + '"'; - } -}; - -/** - * Render the given attributes object. - * - * @param {Object} obj - * @param {Object} escaped - * @return {String} - */ -exports.attrs = function attrs(obj, terse){ - var buf = []; - - var keys = Object.keys(obj); - - if (keys.length) { - for (var i = 0; i < keys.length; ++i) { - var key = keys[i] - , val = obj[key]; - - if ('class' == key) { - if (val = joinClasses(val)) { - buf.push(' ' + key + '="' + val + '"'); - } - } else { - buf.push(exports.attr(key, val, false, terse)); - } - } - } - - return buf.join(''); -}; - -/** - * Escape the given string of `html`. - * - * @param {String} html - * @return {String} - * @api private - */ - -exports.escape = function escape(html){ - var result = String(html) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"'); - if (result === '' + html) return html; - else return result; -}; - -/** - * Re-throw the given `err` in context to the - * the jade in `filename` at the given `lineno`. - * - * @param {Error} err - * @param {String} filename - * @param {String} lineno - * @api private - */ - -exports.rethrow = function rethrow(err, filename, lineno, str){ - if (!(err instanceof Error)) throw err; - if ((typeof window != 'undefined' || !filename) && !str) { - err.message += ' on line ' + lineno; - throw err; - } - try { - str = str || require('fs').readFileSync(filename, 'utf8') - } catch (ex) { - rethrow(err, null, lineno) - } - var context = 3 - , lines = str.split('\n') - , start = Math.max(lineno - context, 0) - , end = Math.min(lines.length, lineno + context); - - // 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 || 'Jade') + ':' + lineno - + '\n' + context + '\n\n' + err.message; - throw err; -}; - - return exports; - -})({}); From f521236a0b1b961cdbd3aa0bbd6228d5ea565c47 Mon Sep 17 00:00:00 2001 From: queckezz Date: Wed, 29 Jan 2014 21:54:06 +0100 Subject: [PATCH 2/6] update api to match native plugins --- index.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 1230e3f..eeebb10 100644 --- a/index.js +++ b/index.js @@ -17,15 +17,16 @@ module.exports = templates; /** * Compile jade templates. * - * @param {Bool} plain + * @param {Bool} options */ -function templates (plain) { +function templates (type, options) { return function (build, done) { setImmediate(done); - build.map('templates', function(file, conf){ + build.map(type, function(file, conf){ if (!file.contents) return; if ('.jade' != extname(file.filename)) return; + if (!options) options = {}; debug('compiling: %s', conf.path()); var opts = { @@ -33,7 +34,7 @@ function templates (plain) { filename: conf.path() } - if (plain) return html(file, opts); + if (options.string) return html(file, opts); return template(file, opts); }); } @@ -47,8 +48,7 @@ function templates (plain) { */ function html (file, opts) { - console.log('html'); - file.filename = basename(file.filename, '.jade') + '.html'; + file.filename = basename(file.filename) + '.html'; file.contents = jade.render(file.contents, opts); return file; } @@ -61,7 +61,6 @@ function html (file, opts) { */ function template (file, opts) { - console.log('template'); var fn = jade.compileClient(file.contents, opts); file.filename = basename(file.filename, '.jade') + '.js'; From 0be4d1e2feeefc4ec5780cadb2ebb982f83d7fce Mon Sep 17 00:00:00 2001 From: queckezz Date: Wed, 29 Jan 2014 21:56:12 +0100 Subject: [PATCH 3/6] add examples --- example/builder.js | 12 - example/index.js | 4 - {example => examples/string}/build.js | 223 +++++----- examples/string/builder.js | 40 ++ {example => examples/string}/component.json | 5 +- {example => examples/string}/index.html | 2 +- examples/string/index.js | 3 + examples/string/template.jade | 3 + examples/template/build.js | 422 +++++++++++++++++++ examples/template/builder.js | 40 ++ examples/template/component.json | 13 + examples/template/index.html | 11 + examples/template/index.js | 5 + {example => examples/template}/template.jade | 2 +- 14 files changed, 659 insertions(+), 126 deletions(-) delete mode 100644 example/builder.js delete mode 100644 example/index.js rename {example => examples/string}/build.js (61%) create mode 100644 examples/string/builder.js rename {example => examples/string}/component.json (68%) rename {example => examples/string}/index.html (95%) create mode 100644 examples/string/index.js create mode 100644 examples/string/template.jade create mode 100644 examples/template/build.js create mode 100644 examples/template/builder.js create mode 100644 examples/template/component.json create mode 100644 examples/template/index.html create mode 100644 examples/template/index.js rename {example => examples/template}/template.jade (84%) diff --git a/example/builder.js b/example/builder.js deleted file mode 100644 index 2254a96..0000000 --- a/example/builder.js +++ /dev/null @@ -1,12 +0,0 @@ -var Builder = require('component-builder') - , fs = require('fs') - , jade = require('../'); - -var builder = new Builder(__dirname); - -builder.use(jade); - -builder.build(function(err, res){ - if (err) console.log(err); - fs.writeFileSync('example/build.js', res.require + res.js); -}); \ No newline at end of file diff --git a/example/index.js b/example/index.js deleted file mode 100644 index 3405ee8..0000000 --- a/example/index.js +++ /dev/null @@ -1,4 +0,0 @@ -var template = require('./template') - , html = template({ youAreUsingJade : true }); - -document.body.innerHTML = html; \ No newline at end of file diff --git a/example/build.js b/examples/string/build.js similarity index 61% rename from example/build.js rename to examples/string/build.js index f466571..4abffc2 100644 --- a/example/build.js +++ b/examples/string/build.js @@ -26,10 +26,14 @@ function require(path, parent, orig) { // perform real require() // by invoking the module's // registered function - if (!module.exports) { - module.exports = {}; - module.client = module.component = true; - module.call(this, module.exports, require.relative(resolved), module); + if (!module._resolving && !module.exports) { + var mod = {}; + mod.exports = {}; + mod.client = mod.component = true; + module._resolving = true; + module.call(this, mod.exports, require.relative(resolved), mod); + delete module._resolving; + module.exports = mod.exports; } return module.exports; @@ -63,7 +67,6 @@ require.aliases = {}; require.resolve = function(path) { if (path.charAt(0) === '/') path = path.slice(1); - var index = path + '/index.js'; var paths = [ path, @@ -76,10 +79,7 @@ require.resolve = function(path) { for (var i = 0; i < paths.length; i++) { var path = paths[i]; if (require.modules.hasOwnProperty(path)) return path; - } - - if (require.aliases.hasOwnProperty(index)) { - return require.aliases[index]; + if (require.aliases.hasOwnProperty(path)) return require.aliases[path]; } }; @@ -200,45 +200,13 @@ require.relative = function(parent) { return localRequire; }; require.register("example/index.js", function(exports, require, module){ -var template = require('./template') - , html = template({ youAreUsingJade : true }); - -document.body.innerHTML = html; -}); -require.register("example/jade-runtime.js", function(exports, require, module){ - -jade = (function(exports){ -/*! - * Jade - runtime - * Copyright(c) 2010 TJ Holowaychuk - * MIT Licensed - */ - -/** - * Lame Array.isArray() polyfill for now. - */ -if (!Array.isArray) { - Array.isArray = function(arr){ - return '[object Array]' == Object.prototype.toString.call(arr); - }; -} - -/** - * Lame Object.keys() polyfill for now. - */ +var template = require('./template.html') +document.body.appendChild(template) -if (!Object.keys) { - Object.keys = function(obj){ - var arr = []; - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - arr.push(key); - } - } - return arr; - } -} +}); +require.register("visionmedia-jade/lib/runtime.js", function(exports, require, module){ +'use strict'; /** * Merge two attribute objects giving precedence @@ -253,6 +221,13 @@ if (!Object.keys) { */ exports.merge = function merge(a, b) { + if (arguments.length === 1) { + var attrs = a[0]; + for (var i = 1; i < a.length; i++) { + attrs = merge(attrs, a[i]); + } + return attrs; + } var ac = a['class']; var bc = b['class']; @@ -261,9 +236,7 @@ exports.merge = function merge(a, b) { bc = bc || []; if (!Array.isArray(ac)) ac = [ac]; if (!Array.isArray(bc)) bc = [bc]; - ac = ac.filter(nulls); - bc = bc.filter(nulls); - a['class'] = ac.concat(bc).join(' '); + a['class'] = ac.concat(bc).filter(nulls); } for (var key in b) { @@ -278,57 +251,103 @@ exports.merge = function merge(a, b) { /** * Filter null `val`s. * - * @param {Mixed} val - * @return {Mixed} + * @param {*} val + * @return {Boolean} * @api private */ function nulls(val) { - return val != null; + return val != null && val !== ''; } +/** + * join array as classes. + * + * @param {*} val + * @return {String} + */ +exports.joinClasses = joinClasses; +function joinClasses(val) { + return Array.isArray(val) ? val.map(joinClasses).filter(nulls).join(' ') : val; +} + +/** + * Render the given classes. + * + * @param {Array} classes + * @param {Array.} escaped + * @return {String} + */ +exports.cls = function cls(classes, escaped) { + var buf = []; + for (var i = 0; i < classes.length; i++) { + if (escaped && escaped[i]) { + buf.push(exports.escape(joinClasses([classes[i]]))); + } else { + buf.push(joinClasses(classes[i])); + } + } + var text = joinClasses(buf); + if (text.length) { + return ' class="' + text + '"'; + } else { + return ''; + } +}; + +/** + * Render the given attribute. + * + * @param {String} key + * @param {String} val + * @param {Boolean} escaped + * @param {Boolean} terse + * @return {String} + */ +exports.attr = function attr(key, val, escaped, terse) { + if ('boolean' == typeof val || null == val) { + if (val) { + return ' ' + (terse ? key : key + '="' + key + '"'); + } else { + return ''; + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + return ' ' + key + "='" + JSON.stringify(val).replace(/'/g, ''') + "'"; + } else if (escaped) { + return ' ' + key + '="' + exports.escape(val) + '"'; + } else { + return ' ' + key + '="' + val + '"'; + } +}; + /** * Render the given attributes object. * * @param {Object} obj * @param {Object} escaped * @return {String} - * @api private */ +exports.attrs = function attrs(obj, terse){ + var buf = []; -exports.attrs = function attrs(obj, escaped){ - var buf = [] - , terse = obj.terse; - - delete obj.terse; - var keys = Object.keys(obj) - , len = keys.length; + var keys = Object.keys(obj); - if (len) { - buf.push(''); - for (var i = 0; i < len; ++i) { + if (keys.length) { + for (var i = 0; i < keys.length; ++i) { var key = keys[i] , val = obj[key]; - if ('boolean' == typeof val || null == val) { - if (val) { - terse - ? buf.push(key) - : buf.push(key + '="' + key + '"'); + if ('class' == key) { + if (val = joinClasses(val)) { + buf.push(' ' + key + '="' + val + '"'); } - } else if (0 == key.indexOf('data') && 'string' != typeof val) { - buf.push(key + "='" + JSON.stringify(val) + "'"); - } else if ('class' == key && Array.isArray(val)) { - buf.push(key + '="' + exports.escape(val.join(' ')) + '"'); - } else if (escaped && escaped[key]) { - buf.push(key + '="' + exports.escape(val) + '"'); } else { - buf.push(key + '="' + val + '"'); + buf.push(exports.attr(key, val, false, terse)); } } } - return buf.join(' '); + return buf.join(''); }; /** @@ -340,11 +359,13 @@ exports.attrs = function attrs(obj, escaped){ */ exports.escape = function escape(html){ - return String(html) - .replace(/&(?!(\w+|\#\d+);)/g, '&') + var result = String(html) + .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); + if (result === '' + html) return html; + else return result; }; /** @@ -357,11 +378,18 @@ exports.escape = function escape(html){ * @api private */ -exports.rethrow = function rethrow(err, filename, lineno){ - if (!filename) throw err; - +exports.rethrow = function rethrow(err, filename, lineno, str){ + if (!(err instanceof Error)) throw err; + if ((typeof window != 'undefined' || !filename) && !str) { + err.message += ' on line ' + lineno; + throw err; + } + try { + str = str || require('fs').readFileSync(filename, 'utf8') + } catch (ex) { + rethrow(err, null, lineno) + } var context = 3 - , str = require('fs').readFileSync(filename, 'utf8') , lines = str.split('\n') , start = Math.max(lineno - context, 0) , end = Math.min(lines.length, lineno + context); @@ -382,30 +410,11 @@ exports.rethrow = function rethrow(err, filename, lineno){ throw err; }; - return exports; - -})({}); }); -require.register("example/template.js", function(exports, require, module){ -module.exports = function anonymous(locals, attrs, escape, rethrow, merge) { -attrs = attrs || jade.attrs; escape = escape || jade.escape; rethrow = rethrow || jade.rethrow; merge = merge || jade.merge; -var buf = []; -with (locals || {}) { -var interp; -buf.push('

Jade - node template engine

'); -if ( youAreUsingJade) -{ -buf.push('

You are amazing

'); -} -else -{ -buf.push('

Get on it!

'); -} -buf.push('
'); -} -return buf.join(""); -} +require.register("example/template.jade.html", function(exports, require, module){ +module.exports = ''; }); -require.alias("example/index.js", "example/index.js"); - -require("example/jade-runtime"); +require.alias("visionmedia-jade/lib/runtime.js", "example/deps/jade/lib/runtime.js"); +require.alias("visionmedia-jade/lib/runtime.js", "example/deps/jade/index.js"); +require.alias("visionmedia-jade/lib/runtime.js", "jade/index.js"); +require.alias("example/index.js", "example/index.js");require.alias("example/index.js", "example/index.js"); \ No newline at end of file diff --git a/examples/string/builder.js b/examples/string/builder.js new file mode 100644 index 0000000..cfe1280 --- /dev/null +++ b/examples/string/builder.js @@ -0,0 +1,40 @@ + +/** + * Module dependencies. + */ + +var Builder = require('component-builder'); +var write = require('fs').writeFileSync; +var jade = require('../..'); +var commonjs = Builder.commonjs; +var concat = Builder.concat; +var link = Builder.symlink; + +/** + * Build. + */ + +var builder = Builder(__dirname); + +// Build scripts. +builder.use(commonjs('scripts')); +builder.use(concat('scripts')); + +// Build templates. +builder.use(jade('templates', { string: true })); +builder.use(commonjs('templates')); +builder.use(concat('templates')); + +var file = __dirname + + '/build.js'; + +// Build. +builder.build(function (err, build) { + if (err) throw err; + + var js = build.requirejs; + js += build.scripts; + js += build.templates; + js += build.aliases; + write(file, js); +}); diff --git a/example/component.json b/examples/string/component.json similarity index 68% rename from example/component.json rename to examples/string/component.json index adf17b5..315c18c 100644 --- a/example/component.json +++ b/examples/string/component.json @@ -3,8 +3,11 @@ "scripts": [ "index.js" ], + "dependencies": { + "visionmedia/jade" : "*" + }, "templates": [ "template.jade" ], "main": "index.js" -} \ No newline at end of file +} diff --git a/example/index.html b/examples/string/index.html similarity index 95% rename from example/index.html rename to examples/string/index.html index fa974e9..99700b6 100644 --- a/example/index.html +++ b/examples/string/index.html @@ -8,4 +8,4 @@ require('example'); - \ No newline at end of file + diff --git a/examples/string/index.js b/examples/string/index.js new file mode 100644 index 0000000..7b7cc98 --- /dev/null +++ b/examples/string/index.js @@ -0,0 +1,3 @@ + +var template = require('./template.html') +document.body.appendChild(template) diff --git a/examples/string/template.jade b/examples/string/template.jade new file mode 100644 index 0000000..b83fceb --- /dev/null +++ b/examples/string/template.jade @@ -0,0 +1,3 @@ + +div(id='Navigation Navigation--stacked') + span: i(class='Logo Logo--small') diff --git a/examples/template/build.js b/examples/template/build.js new file mode 100644 index 0000000..1f77896 --- /dev/null +++ b/examples/template/build.js @@ -0,0 +1,422 @@ + +/** + * Require the given path. + * + * @param {String} path + * @return {Object} exports + * @api public + */ + +function require(path, parent, orig) { + var resolved = require.resolve(path); + + // lookup failed + if (null == resolved) { + orig = orig || path; + parent = parent || 'root'; + var err = new Error('Failed to require "' + orig + '" from "' + parent + '"'); + err.path = orig; + err.parent = parent; + err.require = true; + throw err; + } + + var module = require.modules[resolved]; + + // perform real require() + // by invoking the module's + // registered function + if (!module._resolving && !module.exports) { + var mod = {}; + mod.exports = {}; + mod.client = mod.component = true; + module._resolving = true; + module.call(this, mod.exports, require.relative(resolved), mod); + delete module._resolving; + module.exports = mod.exports; + } + + return module.exports; +} + +/** + * Registered modules. + */ + +require.modules = {}; + +/** + * Registered aliases. + */ + +require.aliases = {}; + +/** + * Resolve `path`. + * + * Lookup: + * + * - PATH/index.js + * - PATH.js + * - PATH + * + * @param {String} path + * @return {String} path or null + * @api private + */ + +require.resolve = function(path) { + if (path.charAt(0) === '/') path = path.slice(1); + + var paths = [ + path, + path + '.js', + path + '.json', + path + '/index.js', + path + '/index.json' + ]; + + for (var i = 0; i < paths.length; i++) { + var path = paths[i]; + if (require.modules.hasOwnProperty(path)) return path; + if (require.aliases.hasOwnProperty(path)) return require.aliases[path]; + } +}; + +/** + * Normalize `path` relative to the current path. + * + * @param {String} curr + * @param {String} path + * @return {String} + * @api private + */ + +require.normalize = function(curr, path) { + var segs = []; + + if ('.' != path.charAt(0)) return path; + + curr = curr.split('/'); + path = path.split('/'); + + for (var i = 0; i < path.length; ++i) { + if ('..' == path[i]) { + curr.pop(); + } else if ('.' != path[i] && '' != path[i]) { + segs.push(path[i]); + } + } + + return curr.concat(segs).join('/'); +}; + +/** + * Register module at `path` with callback `definition`. + * + * @param {String} path + * @param {Function} definition + * @api private + */ + +require.register = function(path, definition) { + require.modules[path] = definition; +}; + +/** + * Alias a module definition. + * + * @param {String} from + * @param {String} to + * @api private + */ + +require.alias = function(from, to) { + if (!require.modules.hasOwnProperty(from)) { + throw new Error('Failed to alias "' + from + '", it does not exist'); + } + require.aliases[to] = from; +}; + +/** + * Return a require function relative to the `parent` path. + * + * @param {String} parent + * @return {Function} + * @api private + */ + +require.relative = function(parent) { + var p = require.normalize(parent, '..'); + + /** + * lastIndexOf helper. + */ + + function lastIndexOf(arr, obj) { + var i = arr.length; + while (i--) { + if (arr[i] === obj) return i; + } + return -1; + } + + /** + * The relative require() itself. + */ + + function localRequire(path) { + var resolved = localRequire.resolve(path); + return require(resolved, parent, path); + } + + /** + * Resolve relative to the parent. + */ + + localRequire.resolve = function(path) { + var c = path.charAt(0); + if ('/' == c) return path.slice(1); + if ('.' == c) return require.normalize(p, path); + + // resolve deps by returning + // the dep in the nearest "deps" + // directory + var segs = parent.split('/'); + var i = lastIndexOf(segs, 'deps') + 1; + if (!i) i = 0; + path = segs.slice(0, i + 1).join('/') + '/deps/' + path; + return path; + }; + + /** + * Check if module is defined at `path`. + */ + + localRequire.exists = function(path) { + return require.modules.hasOwnProperty(localRequire.resolve(path)); + }; + + return localRequire; +}; +require.register("example/index.js", function(exports, require, module){ + +var template = require('./template.html'); +document.innerHTML = template({ + youAreUsingJade : true +}); + +}); +require.register("visionmedia-jade/lib/runtime.js", function(exports, require, module){ +'use strict'; + +/** + * Merge two attribute objects giving precedence + * to values in object `b`. Classes are special-cased + * allowing for arrays and merging/joining appropriately + * resulting in a string. + * + * @param {Object} a + * @param {Object} b + * @return {Object} a + * @api private + */ + +exports.merge = function merge(a, b) { + if (arguments.length === 1) { + var attrs = a[0]; + for (var i = 1; i < a.length; i++) { + attrs = merge(attrs, a[i]); + } + return attrs; + } + var ac = a['class']; + var bc = b['class']; + + if (ac || bc) { + ac = ac || []; + bc = bc || []; + if (!Array.isArray(ac)) ac = [ac]; + if (!Array.isArray(bc)) bc = [bc]; + a['class'] = ac.concat(bc).filter(nulls); + } + + for (var key in b) { + if (key != 'class') { + a[key] = b[key]; + } + } + + return a; +}; + +/** + * Filter null `val`s. + * + * @param {*} val + * @return {Boolean} + * @api private + */ + +function nulls(val) { + return val != null && val !== ''; +} + +/** + * join array as classes. + * + * @param {*} val + * @return {String} + */ +exports.joinClasses = joinClasses; +function joinClasses(val) { + return Array.isArray(val) ? val.map(joinClasses).filter(nulls).join(' ') : val; +} + +/** + * Render the given classes. + * + * @param {Array} classes + * @param {Array.} escaped + * @return {String} + */ +exports.cls = function cls(classes, escaped) { + var buf = []; + for (var i = 0; i < classes.length; i++) { + if (escaped && escaped[i]) { + buf.push(exports.escape(joinClasses([classes[i]]))); + } else { + buf.push(joinClasses(classes[i])); + } + } + var text = joinClasses(buf); + if (text.length) { + return ' class="' + text + '"'; + } else { + return ''; + } +}; + +/** + * Render the given attribute. + * + * @param {String} key + * @param {String} val + * @param {Boolean} escaped + * @param {Boolean} terse + * @return {String} + */ +exports.attr = function attr(key, val, escaped, terse) { + if ('boolean' == typeof val || null == val) { + if (val) { + return ' ' + (terse ? key : key + '="' + key + '"'); + } else { + return ''; + } + } else if (0 == key.indexOf('data') && 'string' != typeof val) { + return ' ' + key + "='" + JSON.stringify(val).replace(/'/g, ''') + "'"; + } else if (escaped) { + return ' ' + key + '="' + exports.escape(val) + '"'; + } else { + return ' ' + key + '="' + val + '"'; + } +}; + +/** + * Render the given attributes object. + * + * @param {Object} obj + * @param {Object} escaped + * @return {String} + */ +exports.attrs = function attrs(obj, terse){ + var buf = []; + + var keys = Object.keys(obj); + + if (keys.length) { + for (var i = 0; i < keys.length; ++i) { + var key = keys[i] + , val = obj[key]; + + if ('class' == key) { + if (val = joinClasses(val)) { + buf.push(' ' + key + '="' + val + '"'); + } + } else { + buf.push(exports.attr(key, val, false, terse)); + } + } + } + + return buf.join(''); +}; + +/** + * Escape the given string of `html`. + * + * @param {String} html + * @return {String} + * @api private + */ + +exports.escape = function escape(html){ + var result = String(html) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + if (result === '' + html) return html; + else return result; +}; + +/** + * Re-throw the given `err` in context to the + * the jade in `filename` at the given `lineno`. + * + * @param {Error} err + * @param {String} filename + * @param {String} lineno + * @api private + */ + +exports.rethrow = function rethrow(err, filename, lineno, str){ + if (!(err instanceof Error)) throw err; + if ((typeof window != 'undefined' || !filename) && !str) { + err.message += ' on line ' + lineno; + throw err; + } + try { + str = str || require('fs').readFileSync(filename, 'utf8') + } catch (ex) { + rethrow(err, null, lineno) + } + var context = 3 + , lines = str.split('\n') + , start = Math.max(lineno - context, 0) + , end = Math.min(lines.length, lineno + context); + + // 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 || 'Jade') + ':' + lineno + + '\n' + context + '\n\n' + err.message; + throw err; +}; + +}); +require.register("example/template.js", function(exports, require, module){ +module.exports = 'var jade = require(\'jade-runtime\');module.exports = function template(locals) {\nvar buf = [];\nvar jade_mixins = {};\nvar locals_ = (locals || {}),youAreUsingJade = locals_.youAreUsingJade;\nbuf.push("

Jade - node template engine

");\nif ( youAreUsingJade)\n{\nbuf.push("

You are amazing

");\n}\nelse\n{\nbuf.push("

Get on it!

");\n}\nbuf.push("
");;return buf.join("");\n}'; +}); +require.alias("visionmedia-jade/lib/runtime.js", "example/deps/jade/lib/runtime.js"); +require.alias("visionmedia-jade/lib/runtime.js", "example/deps/jade/index.js"); +require.alias("visionmedia-jade/lib/runtime.js", "jade/index.js"); +require.alias("example/index.js", "example/index.js");require.alias("example/index.js", "example/index.js"); \ No newline at end of file diff --git a/examples/template/builder.js b/examples/template/builder.js new file mode 100644 index 0000000..32e6254 --- /dev/null +++ b/examples/template/builder.js @@ -0,0 +1,40 @@ + +/** + * Module dependencies. + */ + +var Builder = require('component-builder'); +var write = require('fs').writeFileSync; +var jade = require('../..'); +var commonjs = Builder.commonjs; +var concat = Builder.concat; +var link = Builder.symlink; + +/** + * Build. + */ + +var builder = Builder(__dirname); + +// Build scripts. +builder.use(commonjs('scripts')); +builder.use(concat('scripts')); + +// Build templates. +builder.use(jade('templates')); +builder.use(commonjs('templates')); +builder.use(concat('templates')); + +var file = __dirname + + '/build.js'; + +// Build. +builder.build(function (err, build) { + if (err) throw err; + + var js = build.requirejs; + js += build.scripts; + js += build.templates; + js += build.aliases; + write(file, js); +}); diff --git a/examples/template/component.json b/examples/template/component.json new file mode 100644 index 0000000..315c18c --- /dev/null +++ b/examples/template/component.json @@ -0,0 +1,13 @@ +{ + "name": "example", + "scripts": [ + "index.js" + ], + "dependencies": { + "visionmedia/jade" : "*" + }, + "templates": [ + "template.jade" + ], + "main": "index.js" +} diff --git a/examples/template/index.html b/examples/template/index.html new file mode 100644 index 0000000..99700b6 --- /dev/null +++ b/examples/template/index.html @@ -0,0 +1,11 @@ + + + component-jade example + + + + + + diff --git a/examples/template/index.js b/examples/template/index.js new file mode 100644 index 0000000..48d89fd --- /dev/null +++ b/examples/template/index.js @@ -0,0 +1,5 @@ + +var template = require('./template.html'); +document.innerHTML = template({ + youAreUsingJade : true +}); diff --git a/example/template.jade b/examples/template/template.jade similarity index 84% rename from example/template.jade rename to examples/template/template.jade index f5aa124..ef96319 100644 --- a/example/template.jade +++ b/examples/template/template.jade @@ -3,4 +3,4 @@ h1 Jade - node template engine if youAreUsingJade p You are amazing else - p Get on it! \ No newline at end of file + p Get on it! From 8f1d1c84b2b62231d14d88bc13d3b8d5c61b2624 Mon Sep 17 00:00:00 2001 From: queckezz Date: Thu, 30 Jan 2014 13:29:44 +0100 Subject: [PATCH 4/6] cleanup --- index.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index eeebb10..1ed3a4d 100644 --- a/index.js +++ b/index.js @@ -3,9 +3,9 @@ * Module depencenies. */ +var debug = require('debug')('component-jade'); var basename = require('path').basename; var extname = require('path').extname; -var debug = require('debug')('component-jade'); var jade = require('jade'); /** @@ -17,16 +17,19 @@ module.exports = templates; /** * Compile jade templates. * - * @param {Bool} options + * @param {String} type + * @param {Object} options (optional) */ function templates (type, options) { + if (typeof type != 'string') throw new Error('you need to set a type'); + if (!options) options = {}; + return function (build, done) { setImmediate(done); build.map(type, function(file, conf){ if (!file.contents) return; if ('.jade' != extname(file.filename)) return; - if (!options) options = {}; debug('compiling: %s', conf.path()); var opts = { @@ -44,7 +47,7 @@ function templates (type, options) { * Compile `file` to an html string. * * @param {String} file - * @param {Function} done + * @param {Object} opts */ function html (file, opts) { @@ -57,12 +60,12 @@ function html (file, opts) { * Compile `file` to a reusable template function. * * @param {String} file - * @param {Function} done + * @param {Object} opts */ function template (file, opts) { var fn = jade.compileClient(file.contents, opts); - file.filename = basename(file.filename, '.jade') + '.js'; + file.filename = basename(file.filename) + '.js'; file.contents = 'var jade = require(\'jade-runtime\');' + 'module.exports = ' From 4cb05238e4e3eddb85783f2d58cc854cf008054a Mon Sep 17 00:00:00 2001 From: queckezz Date: Thu, 30 Jan 2014 13:31:42 +0100 Subject: [PATCH 5/6] add tests --- Makefile | 19 ++++++- package.json | 3 +- test/fixtures/string/component.json | 6 ++ test/fixtures/string/in.jade | 1 + test/fixtures/string/out.js | 3 + test/fixtures/template/component.json | 6 ++ test/fixtures/template/in.jade | 1 + test/fixtures/template/out.js | 3 + test/index.js | 82 +++++++++++++++++++++++++++ 9 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/string/component.json create mode 100644 test/fixtures/string/in.jade create mode 100644 test/fixtures/string/out.js create mode 100644 test/fixtures/template/component.json create mode 100644 test/fixtures/template/in.jade create mode 100644 test/fixtures/template/out.js create mode 100644 test/index.js diff --git a/Makefile b/Makefile index e1927dd..7627c9e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,19 @@ +mocha = ./node_modules/.bin/mocha + +test: + @$(mocha) \ + --require assert \ + --reporter spec \ + --bail + example: - node example/builder - open example/index.html + cd examples/string/ && component install && cd - + node examples/string/builder + open examples/string/index.html + + cd examples/template/ && component install && cd - + node examples/template/builder + open examples/template/index.html -.PHONY: example \ No newline at end of file +.PHONY: test example diff --git a/package.json b/package.json index 5e4866e..bf535c0 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ }, "devDependencies": { "jade": "~1.1.5", - "component-builder": "~0.12.0" + "component-builder": "~0.12.0", + "mocha": "~1.17.1" }, "main": "index.js" } diff --git a/test/fixtures/string/component.json b/test/fixtures/string/component.json new file mode 100644 index 0000000..3e784a3 --- /dev/null +++ b/test/fixtures/string/component.json @@ -0,0 +1,6 @@ +{ + "name": "fixture-string", + "templates": [ + "in.jade" + ] +} diff --git a/test/fixtures/string/in.jade b/test/fixtures/string/in.jade new file mode 100644 index 0000000..4b8cbb7 --- /dev/null +++ b/test/fixtures/string/in.jade @@ -0,0 +1 @@ +p jade diff --git a/test/fixtures/string/out.js b/test/fixtures/string/out.js new file mode 100644 index 0000000..95c89af --- /dev/null +++ b/test/fixtures/string/out.js @@ -0,0 +1,3 @@ +require.register("fixture-string/in.jade.html", function(exports, require, module){ +module.exports = '

jade

'; +}); diff --git a/test/fixtures/template/component.json b/test/fixtures/template/component.json new file mode 100644 index 0000000..046f80a --- /dev/null +++ b/test/fixtures/template/component.json @@ -0,0 +1,6 @@ +{ + "name": "fixture-template", + "templates": [ + "in.jade" + ] +} diff --git a/test/fixtures/template/in.jade b/test/fixtures/template/in.jade new file mode 100644 index 0000000..87dfaa8 --- /dev/null +++ b/test/fixtures/template/in.jade @@ -0,0 +1 @@ +p hello #{name} diff --git a/test/fixtures/template/out.js b/test/fixtures/template/out.js new file mode 100644 index 0000000..f61465a --- /dev/null +++ b/test/fixtures/template/out.js @@ -0,0 +1,3 @@ +require.register("fixture-template/in.jade.js", function(exports, require, module){ +module.exports = 'var jade = require(\'jade-runtime\');module.exports = function template(locals) {\nvar buf = [];\nvar jade_mixins = {};\nvar locals_ = (locals || {}),name = locals_.name;\nbuf.push("

hello " + (jade.escape((jade.interp = name) == null ? \'\' : jade.interp)) + "

");;return buf.join("");\n}'; +}); diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..bee777a --- /dev/null +++ b/test/index.js @@ -0,0 +1,82 @@ + +var Builder = require('component-builder'); +var commonjs = Builder.commonjs; +var concat = Builder.concat +var exec = require('child_process').exec; +var assert = require('assert'); +var jade = require('..'); +var fs = require('fs'); + +/** + * Api. + */ + +describe('component-jade', function () { + describe('string', function () { + beforeEach(function () { + builder = Builder(__dirname + '/fixtures/string'); + + builder + .use(jade('templates', { string: true })) + .use(commonjs('templates')) + .use(concat('templates')); + }); + + it('should have ext `.jade.html`', function (done) { + builder.build(function (err, build) { + if (err) throw err; + assert.notEqual(build.templates.search('.jade.html'), -1); + done(); + }); + }); + + it('should convert jade to a string', function (done) { + builder.build(function (err, build) { + if (err) throw err; + var input = build.templates.trim(); + var output = read('/string' + '/out.js').trim(); + assert.equal(input, output); + done(); + }); + }); + }) + + describe('template', function () { + beforeEach(function () { + builder = Builder(__dirname + '/fixtures/template'); + + builder + .use(jade('templates')) + .use(commonjs('templates')) + .use(concat('templates')) + }); + + it('should have `ext` .jade.js', function (done) { + builder.build(function (err, build) { + if (err) throw err; + assert.notEqual(build.templates.search('.jade.js'), -1); + done(); + }); + }); + + it('should convert jade to a template', function (done) { + builder.build(function (err, build) { + if (err) throw err; + var input = build.templates.trim(); + var output = read('/template' + '/out.js').trim(); + assert.equal(input, output); + done(); + }); + }); + }); +}); + +/** + * Read fixture. + * + * @param {String} fixture + */ + +function read (fixture) { + return fs.readFileSync(__dirname + '/fixtures' + fixture, 'utf-8'); +} From 7215212e49e2dc498205be4db0dd8bb0a320ae4c Mon Sep 17 00:00:00 2001 From: queckezz Date: Thu, 30 Jan 2014 13:48:42 +0100 Subject: [PATCH 6/6] update docs --- README.md | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 75b6b87..560f176 100644 --- a/README.md +++ b/README.md @@ -21,28 +21,41 @@ Use the plugin during your build process: ```js - var fs = require('fs') - , Builder = require('component-builder') - , jade = require('component-jade'); - + var Builder = require('component-builder'); + var write = require('fs').readFileSync; + var jade = require('component-jade'); var builder = new Builder(__dirname); - builder.use(jade); + builder + .use(Builder.commonjs('scripts')) + .use(Builder.concat('scripts')) + + // Transpile jade templates to html strings. + .use(jade('templates', { string: true })) + + .use(Builder.commonjs('templates')) + .use(Builder.concat('templates')) - builder.build(function(err, res){ + .build(function(err, build) { if (err) throw err; - fs.writeFileSync('build/build.js', res.require + res.js); - if (res.css) fs.writeFileSync('build/build.css', res.css); + + var js = build.requirejs; + js += build.scripts; + js += build.templates; + js += build.aliases; + write(file, js); }); ``` And then require the files in your Javascript: ```js - var tip = require('tip') - , template = require('template'); + var template = require('template.jade'); + var tip = require('tip'); ``` + __Note:__ You need to add jade and the runtime yourself. For a full working example take a look at `./examples`. + # License (The MIT License) @@ -66,4 +79,4 @@ 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. \ No newline at end of file +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.