From 815cfc9f7f1660c0a11fd0b6bbe7cfd1d35ece26 Mon Sep 17 00:00:00 2001 From: Seth Kinast Date: Tue, 14 Apr 2015 18:22:41 -0700 Subject: [PATCH] Improve CommonJS modules Compiling a template as a CommonJS module returns a function that must be hydrated with a Dust: var tmpl = require('./my/tmpl.js')(dust); Once you have a handle on that object, you can either invoke it directly: tmpl(context, function(err, out) { ... }) tmpl(context).on('data', ...) Or you can pass it to `dust.render`: dust.render(tmpl, ...) Or you can access the compiled template if you need to do something with it: tmpl.template; // function body_0(chunk, context, bodies, params) { ... } --- Gruntfile.js | 15 ++++++- lib/compiler.js | 31 +++++++------- lib/dust.js | 13 +++++- test/jasmine-test/spec/cjsSpec.js | 59 +++++++++++++++++++++++++++ test/jasmine-test/spec/cli/cliSpec.js | 1 + 5 files changed, 102 insertions(+), 17 deletions(-) create mode 100644 test/jasmine-test/spec/cjsSpec.js diff --git a/Gruntfile.js b/Gruntfile.js index 7542ac48..aa58cc2b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -181,6 +181,17 @@ module.exports = function(grunt) { } }, jasmine_nodejs: { + cjs: { + specs: ['test/jasmine-test/spec/cjsSpec.js'], + options: { + reporters: { + console: { + colors: false, + verbose: false + } + } + } + }, dustc: { specs: ['test/jasmine-test/spec/cli/*'], options: { @@ -286,11 +297,11 @@ module.exports = function(grunt) { grunt.registerTask('build', ['clean:build', 'shell:buildParser', 'buildLib', 'uglify']); //test tasks - grunt.registerTask('testNode', ['shell:oldTests']); + grunt.registerTask('testNode', ['jasmine_nodejs:cjs', 'shell:oldTests']); grunt.registerTask('testRhino', ['build', 'shell:testRhino']); grunt.registerTask('testPhantom', ['build', 'jasmine:testProd']); grunt.registerTask('testCli', ['build', 'jasmine_nodejs:dustc']); - grunt.registerTask('test', ['build', 'jasmine:testProd', 'jasmine_nodejs:dustc', 'shell:oldTests', 'shell:testRhino', 'jasmine:coverage']); + grunt.registerTask('test', ['build', 'jasmine:testProd', 'jasmine_nodejs:dustc', 'jasmine_nodejs:cjs', 'shell:oldTests', 'shell:testRhino', 'jasmine:coverage']); //task for debugging in browser grunt.registerTask('dev', ['build', 'jasmine:testDev:build', 'connect:testServer','log:testClient', 'watch:lib']); diff --git a/lib/compiler.js b/lib/compiler.js index 88485f3d..038c3874 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -163,7 +163,8 @@ escapedName = dust.escapeJs(name), AMDName = name? '"' + escapedName + '",' : '', compiled = 'function(dust){', - entry = compiler.compileNode(context, ast); + entry = compiler.compileNode(context, ast), + iife; if(name) { compiled += 'dust.register("' + escapedName + '",' + entry + ');'; @@ -173,12 +174,17 @@ compileBodies(context) + 'return ' + entry + '}'; + iife = '(' + compiled + '(dust));'; + if(dust.config.amd) { return 'define(' + AMDName + '["dust.core"],' + compiled + ');'; } else if(dust.config.cjs) { - return 'module.exports=' + compiled; + return 'module.exports=function(dust){' + + 'var tmpl=' + iife + + 'var f=' + loaderFor().toString() + ';' + + 'f.template=tmpl;return f}'; } else { - return '(' + compiled + '(dust));'; + return iife; } } @@ -432,21 +438,18 @@ function renderSource(source, context, callback) { var tmpl = dust.loadSource(dust.compile(source)); - if(callback) { - return dust.render(tmpl, context, callback); - } else { - return dust.stream(tmpl, context); - } + return loaderFor(tmpl)(context, callback); } function compileFn(source, name) { var tmpl = dust.loadSource(dust.compile(source, name)); - return function(context, callback) { - if(callback) { - return dust.render(tmpl, context, callback); - } else { - return dust.stream(tmpl, context); - } + return loaderFor(tmpl); + } + + function loaderFor(tmpl) { + return function load(ctx, cb) { + var fn = cb ? 'render' : 'stream'; + return dust[fn](tmpl, ctx, cb); }; } diff --git a/lib/dust.js b/lib/dust.js index e61331c6..94ada69a 100644 --- a/lib/dust.js +++ b/lib/dust.js @@ -121,7 +121,18 @@ dust.cache = {}; } - var tmpl = dust.isTemplateFn(nameOrTemplate) ? nameOrTemplate : dust.cache[nameOrTemplate]; + var tmpl; + if(typeof nameOrTemplate === 'function' && nameOrTemplate.template) { + // Sugar away CommonJS module templates + tmpl = nameOrTemplate.template; + } else if(dust.isTemplateFn(nameOrTemplate)) { + // Template functions passed directly + tmpl = nameOrTemplate; + } else { + // Load a template with this name from cache + tmpl = dust.cache[nameOrTemplate]; + } + if (tmpl) { return tmpl(chunk, Context.wrap(context, tmpl.templateName)); } else { diff --git a/test/jasmine-test/spec/cjsSpec.js b/test/jasmine-test/spec/cjsSpec.js new file mode 100644 index 00000000..911e56df --- /dev/null +++ b/test/jasmine-test/spec/cjsSpec.js @@ -0,0 +1,59 @@ +/*global describe,expect,it,beforeEach */ +/*jshint evil:true*/ +var dust = require('../../../'); + +function load(tmpl) { + dust.config.cjs = true; + return eval(dust.compile(tmpl, 'hello'))(dust); +} + +describe('CommonJS template', function() { + var template = "Hello {world}!", + context = { world: "world" }, + rendered = "Hello world!", + templateName = 'hello', + tmpl; + + beforeEach(function() { + tmpl = load(template); + }); + + it('can be invoked to render', function() { + tmpl(context, function(err, out) { + expect(out).toEqual(rendered); + }); + }); + + it('can be invoked to stream', function(done) { + tmpl(context).on('data', function(out) { + expect(out).toEqual(rendered); + done(); + }); + }); + + it('can be passed to dust.render', function() { + dust.render(tmpl, context, function(err, out) { + expect(out).toEqual(rendered); + }); + }); + + it('can be passed to dust.stream', function(done) { + dust.stream(tmpl, context).on('data', function(out) { + expect(out).toEqual(rendered); + done(); + }); + }); + + it('has a template property that can be passed to dust.render', function() { + expect(tmpl.template.templateName).toEqual(templateName); + dust.render(tmpl.template, context, function(err, out) { + expect(out).toEqual(rendered); + }); + }); + + it('has a name that can be passed to dust.render', function() { + dust.render(templateName, context, function(err, out) { + expect(out).toEqual(rendered); + }); + }); +}); diff --git a/test/jasmine-test/spec/cli/cliSpec.js b/test/jasmine-test/spec/cli/cliSpec.js index 8d421ba5..39624058 100644 --- a/test/jasmine-test/spec/cli/cliSpec.js +++ b/test/jasmine-test/spec/cli/cliSpec.js @@ -1,3 +1,4 @@ +/*global describe,expect,it,afterEach */ var path = require('path'), fs = require('fs'), exec = require('child_process').exec;