├── test ├── fixtures │ ├── different.ext │ ├── template.html │ ├── interpolate.html │ └── nested │ │ ├── template.html │ │ └── module.js ├── curl.html ├── dojo.html ├── requirejs.html └── spec │ ├── dojo.js │ ├── requirejs.js │ └── curl.js ├── .gitignore ├── .travis.yml ├── bower.json ├── package.json ├── Gruntfile.coffee ├── loader.js └── README.md /test/fixtures/different.ext: -------------------------------------------------------------------------------- 1 | It works! 2 | -------------------------------------------------------------------------------- /test/fixtures/template.html: -------------------------------------------------------------------------------- 1 | It works! 2 | -------------------------------------------------------------------------------- /test/fixtures/interpolate.html: -------------------------------------------------------------------------------- 1 | {{ msg }} 2 | -------------------------------------------------------------------------------- /test/fixtures/nested/template.html: -------------------------------------------------------------------------------- 1 | It works! 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /test/reports/ 2 | /bower_components/ 3 | /node_modules/ 4 | -------------------------------------------------------------------------------- /test/fixtures/nested/module.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports) { 2 | "use strict"; 3 | 4 | exports.template = require("ldsh!./template"); 5 | }); 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 4 | 5 | branches: 6 | only: 7 | - master 8 | 9 | before_install: 10 | - npm install -gq grunt-cli bower 11 | - bower install 12 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lodash-template-loader", 3 | "version": "0.1.2", 4 | "main": "loader.js", 5 | "dependencies": { 6 | "lodash": "~2.2.0" 7 | }, 8 | "devDependencies": { 9 | "qunit": "~1.12.0", 10 | "requirejs": "~2.1.8", 11 | "curl": "~0.8.2", 12 | "dojo": "~1.9.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lodash-template-loader", 3 | "version": "0.1.2", 4 | "description": "A Lo-Dash template loader plugin for AMD projects.", 5 | "main": "loader.js", 6 | "author": "Tim Branyen (@tbranyen)", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "grunt-contrib-clean": "~0.5.0", 10 | "grunt": "~0.4.1", 11 | "grunt-contrib-jshint": "~0.6.4", 12 | "grunt-contrib-watch": "~0.5.3", 13 | "grunt-clear": "~0.2.1", 14 | "grunt-contrib-connect": "~0.5.0", 15 | "grunt-contrib-qunit": "~0.3.0" 16 | }, 17 | "scripts": { 18 | "test": "grunt", 19 | "postinstall": "bower i -s" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | module.exports = -> 2 | @initConfig 3 | 4 | jshint: ["loader.js"] 5 | 6 | connect: 7 | test: 8 | options: 9 | port: 8000 10 | 11 | watch: 12 | files: ["loader.js", "test/**/*", "Gruntfile.coffee"] 13 | tasks: ["clear", "default"] 14 | 15 | qunit: 16 | test: 17 | options: 18 | urls: [ 19 | "http://localhost:8000/test/requirejs.html" 20 | "http://localhost:8000/test/dojo.html" 21 | "http://localhost:8000/test/curl.html" 22 | ] 23 | 24 | @loadNpmTasks "grunt-contrib-jshint" 25 | @loadNpmTasks "grunt-contrib-watch" 26 | @loadNpmTasks "grunt-contrib-connect" 27 | @loadNpmTasks "grunt-contrib-qunit" 28 | @loadNpmTasks "grunt-clear" 29 | 30 | @registerTask "default", ["jshint", "connect", "qunit"] 31 | -------------------------------------------------------------------------------- /test/curl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Lo-Dash Template Loader: Curl Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Lo-Dash Template Loader: Curl Tests

17 |

18 |
19 |

20 |
    21 |
    test markup
    22 | 23 | 24 | 25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/dojo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Lo-Dash Template Loader: Dojo Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

    Lo-Dash Template Loader: Dojo Tests

    17 |

    18 |
    19 |

    20 |
      21 |
      test markup
      22 | 23 | 24 | 25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/requirejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Lo-Dash Template Loader: RequireJS Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

      Lo-Dash Template Loader: RequireJS Tests

      17 |

      18 |
      19 |

      20 |
        21 |
        test markup
        22 | 23 | 24 | 25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/spec/dojo.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Module: Dojo 3 | * Ensures that the loader loads and functions in Dojo. 4 | * 5 | */ 6 | QUnit.module("dojo"); 7 | 8 | asyncTest("AMD support", 1, function() { 9 | require({ 10 | baseUrl: "../", 11 | 12 | paths: { 13 | lodash: "bower_components/lodash/dist/lodash", 14 | ldsh: "loader", 15 | fixtures: "test/fixtures" 16 | } 17 | }, ["ldsh!fixtures/template"], function(template) { 18 | ok(template(), "It works!"); 19 | 20 | start(); 21 | }); 22 | }); 23 | 24 | asyncTest("change extension", function() { 25 | require({ 26 | lodashLoader: { 27 | ext: ".ext" 28 | } 29 | }, ["ldsh!fixtures/different"], function(template) { 30 | ok(template(), "It works!"); 31 | 32 | start(); 33 | }); 34 | }); 35 | 36 | asyncTest("templateSettings", function() { 37 | require({ 38 | lodashLoader: { 39 | templateSettings: { 40 | "interpolate": /{{([\s\S]+?)}}/g 41 | } 42 | } 43 | }, ["ldsh!fixtures/interpolate"], function(template) { 44 | ok(template({ msg: "It works!" }), "It works!"); 45 | 46 | start(); 47 | }); 48 | }); 49 | 50 | asyncTest("relative paths", 1, function() { 51 | require(["fixtures/nested/module"], function(exports) { 52 | ok(exports.template(), "It works!"); 53 | 54 | start(); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/spec/requirejs.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Module: RequireJS 3 | * Ensures that the loader loads and functions in RequireJS. 4 | * 5 | */ 6 | QUnit.module("requirejs"); 7 | 8 | asyncTest("AMD support", 1, function() { 9 | require.config({ 10 | baseUrl: "../", 11 | 12 | paths: { 13 | lodash: "bower_components/lodash/dist/lodash", 14 | ldsh: "loader", 15 | fixtures: "test/fixtures" 16 | } 17 | }); 18 | 19 | require(["ldsh!fixtures/template"], function(template) { 20 | ok(template(), "It works!"); 21 | 22 | start(); 23 | }); 24 | }); 25 | 26 | asyncTest("change extension", 1, function() { 27 | require({ 28 | lodashLoader: { 29 | ext: ".ext" 30 | } 31 | }, ["ldsh!fixtures/different"], function(template) { 32 | ok(template(), "It works!"); 33 | 34 | start(); 35 | }); 36 | }); 37 | 38 | asyncTest("templateSettings", 1, function() { 39 | require({ 40 | lodashLoader: { 41 | templateSettings: { 42 | "interpolate": /{{([\s\S]+?)}}/g 43 | } 44 | } 45 | }, ["ldsh!fixtures/interpolate"], function(template) { 46 | ok(template({ msg: "It works!" }), "It works!"); 47 | 48 | start(); 49 | }); 50 | }); 51 | 52 | asyncTest("relative paths", 1, function() { 53 | require(["fixtures/nested/module"], function(exports) { 54 | ok(exports.template(), "It works!"); 55 | 56 | start(); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/spec/curl.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Test Module: Curl 3 | * Ensures that the loader loads and functions in Curl. 4 | * 5 | */ 6 | /*global QUnit asyncTest curl ok start */ 7 | QUnit.module("curl"); 8 | 9 | curl.config({ 10 | baseUrl: "../", 11 | 12 | paths: { 13 | "lodash": "bower_components/lodash/dist/lodash", 14 | "ldsh": "loader", 15 | "fixtures": "test/fixtures" 16 | } 17 | }); 18 | 19 | asyncTest("AMD support", 1, function() { 20 | curl(["ldsh!fixtures/template"]).then( 21 | function(template) { 22 | ok(template(), "It works!"); 23 | 24 | start(); 25 | }, 26 | function(ex) { 27 | ok(false, ex.message); 28 | start(); 29 | } 30 | ); 31 | }); 32 | 33 | asyncTest("change extension", function() { 34 | curl({ 35 | lodashLoader: { 36 | ext: ".ext" 37 | } 38 | }, ["ldsh!fixtures/different"]).then( 39 | function(template) { 40 | ok(template(), "It works!"); 41 | 42 | start(); 43 | }, 44 | function(ex) { 45 | ok(false, ex.message); 46 | start(); 47 | } 48 | ); 49 | }); 50 | 51 | asyncTest("templateSettings", function() { 52 | curl({ 53 | lodashLoader: { 54 | templateSettings: { 55 | "interpolate": /{{([\s\S]+?)}}/g 56 | } 57 | } 58 | }, ["ldsh!fixtures/interpolate"]).then( 59 | function(template) { 60 | ok(template({ msg: "It works!" }), "It works!"); 61 | 62 | start(); 63 | }, 64 | function(ex) { 65 | ok(false, ex.message); 66 | start(); 67 | } 68 | ); 69 | }); 70 | 71 | asyncTest("relative paths", 1, function() { 72 | curl(["fixtures/nested/module"], function(exports) { 73 | ok(exports.template(), "It works!"); 74 | 75 | start(); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /loader.js: -------------------------------------------------------------------------------- 1 | /* Lo-Dash Template Loader v0.1.2 2 | * Copyright 2013, Tim Branyen (@tbranyen). 3 | * loader.js may be freely distributed under the MIT license. 4 | */ 5 | (function(global) { 6 | "use strict"; 7 | 8 | // Cache used to map configuration options between load and write. 9 | var buildMap = {}; 10 | 11 | // Alias the correct `nodeRequire` method. 12 | var nodeRequire = typeof requirejs === "function" && requirejs.nodeRequire; 13 | 14 | // If in Node, get access to the filesystem. 15 | if (nodeRequire) { 16 | var fs = nodeRequire("fs"); 17 | } 18 | 19 | // Define the plugin using the CommonJS syntax. 20 | define(function(require, exports) { 21 | var _ = require("lodash"); 22 | 23 | exports.version = "0.1.2"; 24 | 25 | // Invoked by the AMD builder, passed the path to resolve, the require 26 | // function, done callback, and the configuration options. 27 | exports.load = function(name, req, load, config) { 28 | // Dojo provides access to the config object through the req function. 29 | if (!config) { 30 | config = require.rawConfig; 31 | } 32 | 33 | var settings = configure(config); 34 | 35 | // Builds must happen with Node. 36 | if (config.isBuild) { 37 | var path = settings.root + name + settings.ext; 38 | 39 | // Read in the file synchronously, as RequireJS expects, and return the 40 | // contents. Process as a Lo-Dash template. 41 | buildMap[name] = _.template(String(fs.readFileSync(path))); 42 | 43 | return load(); 44 | } 45 | 46 | // Create a basic XHR. 47 | var xhr = new XMLHttpRequest(); 48 | 49 | // Wait for it to load. 50 | xhr.onreadystatechange = function() { 51 | if (xhr.readyState === 4) { 52 | // Process as a Lo-Dash template and cache. 53 | buildMap[name] = _.template(xhr.responseText); 54 | 55 | // Return the compiled template. 56 | load(buildMap[name]); 57 | } 58 | }; 59 | 60 | // Initiate the fetch. 61 | xhr.open("GET", "/" + settings.root + name + settings.ext, true); 62 | xhr.send(null); 63 | }; 64 | 65 | // Also invoked by the AMD builder, this writes out a compatible define 66 | // call that will work with loaders such as almond.js that cannot read 67 | // the configuration data. 68 | exports.write = function(pluginName, moduleName, write) { 69 | var template = buildMap[moduleName].source; 70 | 71 | // Write out the actual definition 72 | write(strDefine(pluginName, moduleName, template)); 73 | }; 74 | 75 | // This is for curl.js/cram.js build-time support. 76 | exports.compile = function(pluginName, moduleName, req, io, config) { 77 | configure(config); 78 | 79 | // Ask cram to fetch the template file (resId) and pass it to `write`. 80 | io.read(moduleName, write, io.error); 81 | 82 | function write(template) { 83 | // Write-out define(id,function(){return{/* template */}}); 84 | io.write(strDefine(pluginName, moduleName, template)); 85 | } 86 | }; 87 | 88 | // Crafts the written definition form of the module during a build. 89 | function strDefine(pluginName, moduleName, template) { 90 | return [ 91 | "define('", pluginName, "!", moduleName, "', ", "[], ", 92 | [ 93 | "function() {", 94 | "return ", template, ";", 95 | "}" 96 | ].join(""), 97 | ");\n" 98 | ].join(""); 99 | } 100 | 101 | function configure(config) { 102 | // Default settings point to the project root and using html files. 103 | var settings = _.extend({ 104 | ext: ".html", 105 | root: config.baseUrl, 106 | templateSettings: {} 107 | }, config.lodashLoader); 108 | 109 | // Set the custom passed in template settings. 110 | _.extend(_.templateSettings, settings.templateSettings); 111 | 112 | return settings; 113 | } 114 | }); 115 | 116 | })(typeof global === "object" ? global : this); 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lo-Dash AMD Template Loader 2 | --------------------------- 3 | 4 | [![Build Status](https://travis-ci.org/tbranyen/lodash-template-loader.png?branch=master)](https://travis-ci.org/tbranyen/lodash-template-loader) 5 | 6 | Created by Tim Branyen [@tbranyen](http://twitter.com/tbranyen) 7 | 8 | RequireJS, Dojo, and Curl are excellent module loaders, but through the 9 | flexibility of plugin architecture they should really be seen as resource 10 | loaders. 11 | 12 | We've come to expect our development environments to be raw and our builds to 13 | be as optimized as possible. This plugin will fetch your Lo-Dash templates 14 | during development and inline them in a production build. 15 | 16 | Unlike [requirejs-tpl](https://github.com/ZeeAgency/requirejs-tpl) it does not 17 | use a hardcoded template function, but instead uses the exact one from your 18 | copy of Lo-Dash. Consistency is a key difference here. 19 | 20 | Almost every single article and tutorial on using client side templates with 21 | AMD, will advocate the use of the RequireJS text! plugin. While this is a fine 22 | tool for loading text, it is not optimized for templates. It requires the 23 | duplicative act of compiling the templates before use in production. 24 | 25 | ### Installing: ### 26 | 27 | This plugin has been registered with Bower, install with: 28 | 29 | ``` bash 30 | bower install lodash-template-loader 31 | ``` 32 | 33 | Alternatively you can download the `loader.js` file and place anywhere in your 34 | project. 35 | 36 | ### Loading the plugin: ### 37 | 38 | ``` javascript 39 | require.config({ 40 | paths: { 41 | // You can change the plugin name to be whatever you want, maybe tpl? 42 | "ldsh": "path/to/lodash-template-loader/loader" 43 | } 44 | }); 45 | ``` 46 | 47 | You must not end the path in `.js` unless you are providing a url. 48 | 49 | Examples: 50 | 51 | * `vendor/libraries/loader` 52 | * `http://cdn.mysite.com/vendor/libraries/loader.js` 53 | 54 | ### Using: ### 55 | 56 | Inside an AMD module you can now load templates like so: 57 | 58 | ``` javascript 59 | // Omit the extension and root path. 60 | define(["ldsh!path/to/template"], function(template) { 61 | var contents = template({ 62 | // Some data. 63 | }); 64 | }); 65 | ``` 66 | 67 | The path to your templates directory can be configured as well as the default 68 | extension to search for. More details below. 69 | 70 | ### Configuring templateSettings: ### 71 | 72 | There are a few default settings in place to make consumption easier. 73 | 74 | The extension appended by default is `.html`. The default root path is your 75 | configuration's `baseUrl`. No `templateSettings` are configured by default. 76 | 77 | To change these options, add the following to your configuration: 78 | 79 | ``` javascript 80 | require.config({ 81 | // The Lo-Dash loader configuration. 82 | lodashLoader: { 83 | // This is the default extension, you can change to whatever you like. 84 | // Setting this to "" will disable automatic extensions. 85 | ext: ".html", 86 | 87 | // The path to where your templates live relative to the `baseUrl`. 88 | root: "/", 89 | 90 | // Globally configured template settings to be applied to any templates 91 | // loaded. This correlates directly to `_.templateSettings`. 92 | templateSettings: {} 93 | } 94 | }); 95 | ``` 96 | 97 | ### What about Underscore? ### 98 | 99 | I've decided to go with the compatible `.source` attribute for obtaining the 100 | template function source string, which makes this plugin work with Underscore 101 | as well. You'll have to manually map the resource identifier which is 102 | explained in detail below. 103 | 104 | ### Mapping Underscore to Lo-Dash: ### 105 | 106 | In order to use Underscore with this plugin, you must map the identifier. 107 | Internally the plugin specifically looks for the identifier `lodash`. In the 108 | configuration simply: 109 | 110 | ``` javascript 111 | require.config({ 112 | // Define a new object literal, named map. 113 | map: { 114 | // Ensure the mapping works globally across any modules using this plugin. 115 | "*": { 116 | // Map the lodash identifier to whatever module you want here. 117 | "lodash": "underscore" 118 | } 119 | } 120 | }); 121 | ``` 122 | 123 | ### Using with Dojo: ### 124 | 125 | Ensure Dojo's loader is in `async` mode: 126 | 127 | ``` html 128 | 129 | ``` 130 | 131 | Set up your configuration: 132 | 133 | ``` javascript 134 | require({ 135 | paths: { 136 | "ldsh": "path/to/loader" 137 | } 138 | }); 139 | ``` 140 | 141 | And Require in your template: 142 | 143 | ``` javascript 144 | require(["ldsh!path/to/template"], function(template) { 145 | var contents = template({ 146 | // Some data. 147 | }); 148 | }); 149 | ``` 150 | 151 | ### Using with Curl: ### 152 | 153 | Set up your configuration: 154 | 155 | ``` javascript 156 | curl.config({ 157 | paths: { 158 | "ldsh": "path/to/loader" 159 | } 160 | }); 161 | ``` 162 | 163 | And Curl in your template: 164 | 165 | ``` javascript 166 | curl(["ldsh!path/to/template"], function(template) { 167 | var contents = template({ 168 | // Some data. 169 | }); 170 | }); 171 | ``` 172 | 173 | ### Running tests ### 174 | 175 | You will need Node.js and Grunt installed to run tests. 176 | 177 | Clone this project, open the directory in a terminal, and execute the following 178 | commands: 179 | 180 | ``` bash 181 | # Install dependencies. 182 | npm i -q 183 | 184 | # Run the tests. 185 | grunt 186 | ``` 187 | 188 | You can also run an http-server in the root and hit the tests directly. Since 189 | XHR is used, tests must be run from a server. 190 | 191 | ### Release notes: ### 192 | 193 | #### 0.1.2 #### 194 | 195 | * Resolved issue with baseUrl concatenation to moduleName. 196 | 197 | #### 0.1.1 #### 198 | 199 | * Hotfix for building in r.js projects. 200 | 201 | #### 0.1.0 #### 202 | 203 | * Open sourced on GitHub. 204 | --------------------------------------------------------------------------------