├── fixtures ├── a.txt ├── b.txt ├── c.txt ├── a.hbs ├── b.hbs ├── c.hbs ├── a.tmpl ├── b.tmpl └── c.tmpl ├── .gitattributes ├── .travis.yml ├── .editorconfig ├── .gitignore ├── gulpfile.js ├── utils.js ├── LICENSE ├── examples ├── app.js └── collection.js ├── .verb.md ├── package.json ├── .eslintrc.json ├── index.js ├── README.md └── test.js /fixtures/a.txt: -------------------------------------------------------------------------------- 1 | This is AAA -------------------------------------------------------------------------------- /fixtures/b.txt: -------------------------------------------------------------------------------- 1 | This is BBB -------------------------------------------------------------------------------- /fixtures/c.txt: -------------------------------------------------------------------------------- 1 | This is CCC -------------------------------------------------------------------------------- /fixtures/a.hbs: -------------------------------------------------------------------------------- 1 | --- 2 | title: AAA 3 | --- 4 |

{{title}}

5 |

{{name}}

-------------------------------------------------------------------------------- /fixtures/b.hbs: -------------------------------------------------------------------------------- 1 | --- 2 | title: BBB 3 | --- 4 |

{{title}}

5 |

{{name}}

-------------------------------------------------------------------------------- /fixtures/c.hbs: -------------------------------------------------------------------------------- 1 | --- 2 | title: CCC 3 | --- 4 |

{{title}}

5 |

{{name}}

-------------------------------------------------------------------------------- /fixtures/a.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | title: AAA 3 | --- 4 |

<%= title %>

5 |

<%= name %>

-------------------------------------------------------------------------------- /fixtures/b.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | title: BBB 3 | --- 4 |

<%= title %>

5 |

<%= name %>

-------------------------------------------------------------------------------- /fixtures/c.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | title: BBB 3 | --- 4 |

<%= title %>

5 |

<%= name %>

-------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text eol=lf 3 | 4 | # binaries 5 | *.ai binary 6 | *.psd binary 7 | *.jpg binary 8 | *.gif binary 9 | *.png binary 10 | *.jpeg binary 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | language: node_js 6 | node_js: 7 | - node 8 | - '6' 9 | - '4' 10 | - '0.12' 11 | - '0.10' 12 | matrix: 13 | allow_failures: 14 | - node_js: '4' 15 | - node_js: '0.12' 16 | - node_js: '0.10' 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{**/{actual,fixtures,expected,templates}/**,*.md}] 12 | trim_trailing_whitespace = false 13 | insert_final_newline = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # always ignore files 2 | *.DS_Store 3 | *.sublime-* 4 | 5 | # test related, or directories generated by tests 6 | test/actual 7 | actual 8 | coverage 9 | .nyc* 10 | 11 | # npm 12 | node_modules 13 | npm-debug.log 14 | 15 | # yarn 16 | yarn.lock 17 | yarn-error.log 18 | 19 | # misc 20 | _gh_pages 21 | _draft 22 | _drafts 23 | bower_components 24 | vendor 25 | temp 26 | tmp 27 | TODO.md 28 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var mocha = require('gulp-mocha'); 5 | var istanbul = require('gulp-istanbul'); 6 | var eslint = require('gulp-eslint'); 7 | 8 | gulp.task('coverage', function() { 9 | return gulp.src(['index.js', 'utils.js']) 10 | .pipe(istanbul({includeUntested: true})) 11 | .pipe(istanbul.hookRequire()); 12 | }); 13 | 14 | gulp.task('mocha', ['coverage'], function() { 15 | return gulp.src('test.js') 16 | .pipe(mocha()) 17 | .pipe(istanbul.writeReports({ 18 | reporters: ['html', 'text', 'text-summary'], 19 | reportOpts: {dir: 'coverage', file: 'summary.txt'} 20 | })); 21 | }); 22 | 23 | gulp.task('eslint', function() { 24 | return gulp.src('*.js') 25 | .pipe(eslint()) 26 | .pipe(eslint.format()); 27 | }); 28 | 29 | gulp.task('default', ['mocha', 'eslint']); 30 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = require('lazy-cache')(require); 4 | 5 | /** 6 | * Lazily required module dependencies 7 | */ 8 | 9 | var fn = require; 10 | require = utils; 11 | require('extend-shallow', 'extend'); 12 | require('file-contents', 'contents'); 13 | require('is-registered'); 14 | require('is-valid-glob'); 15 | require('is-valid-instance'); 16 | require('isobject', 'isObject'); 17 | require('load-templates', 'Loader'); 18 | require = fn; 19 | 20 | /** 21 | * Return true if app is a valid instance 22 | */ 23 | 24 | utils.isValid = function(app) { 25 | if (!utils.isValidInstance(app)) { 26 | return false; 27 | } 28 | if (utils.isRegistered(app, 'assemble-loader')) { 29 | return false; 30 | } 31 | return true; 32 | }; 33 | 34 | /** 35 | * Return true if obj is a View instance 36 | */ 37 | 38 | utils.isView = function(obj) { 39 | return utils.isObject(obj) && (obj.path || obj.contents || obj.isView || obj.isItem); 40 | }; 41 | 42 | /** 43 | * Expose utils 44 | */ 45 | 46 | module.exports = utils; 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2017, Jon Schlinkert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/app.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var matter = require('parser-front-matter'); 4 | var templates = require('templates'); 5 | var loader = require('..'); 6 | 7 | var app = templates() 8 | .use(loader()) 9 | 10 | app.engine('tmpl', require('engine-base')); 11 | app.engine('hbs', require('engine-handlebars')); 12 | 13 | app.renameKey(function(key) { 14 | return path.basename(key); 15 | }); 16 | 17 | app.onLoad(/\.(hbs|tmpl)$/, function(view, next) { 18 | matter.parse(view, next); 19 | }); 20 | 21 | 22 | var posts = app 23 | .create('posts') 24 | .load(path.join(__dirname, '../fixtures/*.hbs')); 25 | 26 | 27 | var pages = app 28 | .create('pages') 29 | .load(path.join(__dirname, '../fixtures/*.tmpl')); 30 | 31 | app.posts.getView('a.hbs') 32 | .set('data.title', 'A') 33 | .set('data.name', 'Halle') 34 | .render(function(err, res) { 35 | if (err) return console.log(err.stack); 36 | console.log(res.content) 37 | }); 38 | 39 | app.posts.getView('b.hbs') 40 | .set('data.name', 'Brooke') 41 | .render(function(err, res) { 42 | if (err) return console.log(err.stack); 43 | console.log(res.content) 44 | }); 45 | 46 | app.pages.getView('a.tmpl') 47 | .set('data.name', 'Brian') 48 | .render(function(err, res) { 49 | if (err) return console.log(err.stack); 50 | console.log(res.content) 51 | }); 52 | -------------------------------------------------------------------------------- /examples/collection.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var matter = require('parser-front-matter'); 4 | var templates = require('templates'); 5 | var loader = require('..'); 6 | 7 | var app = templates(); 8 | 9 | app.engine('tmpl', require('engine-base')); 10 | app.engine('hbs', require('engine-handlebars')); 11 | 12 | app.renameKey(function (key) { 13 | return path.basename(key); 14 | }); 15 | 16 | app.onLoad(/\.(hbs|tmpl)$/, function (view, next) { 17 | matter.parse(view, next); 18 | }); 19 | 20 | var posts = app 21 | .create('posts') 22 | .use(loader(path.join(__dirname, '../fixtures/*.hbs'))) 23 | // .load('fixtures/*.hbs'); 24 | 25 | var pages = app 26 | .create('pages') 27 | .use(loader()) 28 | .load('fixtures/*.tmpl'); 29 | 30 | 31 | app.posts.getView('a.hbs') 32 | .set('data.title', 'A') 33 | .set('data.name', 'Halle') 34 | .render(function (err, res) { 35 | if (err) return console.log(err.stack); 36 | console.log(res.content) 37 | }); 38 | 39 | app.posts.getView('b.hbs') 40 | .set('data.name', 'Brooke') 41 | .render(function (err, res) { 42 | if (err) return console.log(err.stack); 43 | console.log(res.content) 44 | }); 45 | 46 | app.pages.getView('a.tmpl') 47 | .set('data.name', 'Brian') 48 | .render(function (err, res) { 49 | if (err) return console.log(err.stack); 50 | console.log(res.content) 51 | }); 52 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```js 4 | var assemble = require('assemble'); 5 | var loader = require('{%= name %}'); 6 | var app = assemble() 7 | app.use(loader()); 8 | 9 | app.create('pages', {cwd: 'src/pages'}); 10 | app.create('partials', {cwd: 'src/partials'}); 11 | 12 | // you can now load globs of templates onto any collection 13 | app.pages('*.hbs'); 14 | app.partials('*.hbs'); 15 | ``` 16 | 17 | 18 | ## API 19 | 20 | ```js 21 | var assemble = require('assemble'); 22 | var loader = require('{%= name %}'); 23 | var app = assemble() 24 | 25 | // use the plugin 26 | app.use(loader()); 27 | ``` 28 | 29 | Optionally pass glob patterns to the plugin: 30 | 31 | ```js 32 | var app = assemble(); 33 | // this can only be done once when the plugin is registered 34 | app.use(loader('foo/*.hbs')); 35 | 36 | // you can use the `.loadViews()` method any number of times 37 | app.create('pages') 38 | .use(loader('foo/*.hbs')) 39 | .loadViews('bar/*.hbs') 40 | .loadViews('baz/*.hbs') 41 | .loadViews('qux/*.hbs'); 42 | ``` 43 | 44 | ## Collections 45 | 46 | **All collections** 47 | 48 | When the plugin is added to the `app` instance (as in the previous example), a `.load` method will also be added to every collection created. 49 | 50 | ```js 51 | var app = assemble() 52 | .use(loader()); 53 | 54 | // cache views on `app.views.posts` 55 | app.create('posts') 56 | .load('content/*.md'); 57 | 58 | // cache views on `app.views.docs` 59 | app.create('docs') 60 | .load('docs/*.md'); 61 | ``` 62 | 63 | **Specific collections** 64 | 65 | If you only want to add the loader to a specific collection, you can pass the plugin to the `.use` method on the collection. 66 | 67 | ```js 68 | var app = assemble(); 69 | 70 | // `create` returns the collection instance 71 | app.create('posts') 72 | .use(loader()) 73 | .load('content/*.md'); 74 | 75 | // this works too, since `create` adds methods to `app` 76 | // for the collection 77 | app.posts 78 | .load('*.hbs') 79 | .load('*.txt'); 80 | ``` 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assemble-loader", 3 | "description": "Assemble plugin (^0.6.0) for loading globs of views onto custom view collections. Also works with verb or other Templates.js based applications.", 4 | "version": "1.0.5", 5 | "homepage": "https://github.com/assemble/assemble-loader", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "repository": "assemble/assemble-loader", 8 | "bugs": { 9 | "url": "https://github.com/assemble/assemble-loader/issues" 10 | }, 11 | "license": "MIT", 12 | "files": [ 13 | "index.js", 14 | "utils.js" 15 | ], 16 | "main": "index.js", 17 | "engines": { 18 | "node": ">=0.10.0" 19 | }, 20 | "scripts": { 21 | "test": "mocha" 22 | }, 23 | "dependencies": { 24 | "extend-shallow": "^2.0.1", 25 | "file-contents": "^1.0.1", 26 | "is-registered": "^0.1.5", 27 | "is-valid-glob": "^0.3.0", 28 | "is-valid-instance": "^0.2.0", 29 | "isobject": "^3.0.0", 30 | "lazy-cache": "^2.0.2", 31 | "load-templates": "^1.0.2" 32 | }, 33 | "devDependencies": { 34 | "engine-base": "^0.1.2", 35 | "engine-handlebars": "^0.8.0", 36 | "gulp": "^3.9.1", 37 | "gulp-eslint": "^3.0.1", 38 | "gulp-format-md": "^0.1.11", 39 | "gulp-istanbul": "^1.1.1", 40 | "gulp-mocha": "^3.0.1", 41 | "mocha": "^3.2.0", 42 | "parser-front-matter": "^1.6.2", 43 | "should": "^11.2.0", 44 | "templates": "^1.2.4" 45 | }, 46 | "keywords": [ 47 | "assemble", 48 | "assembleplugin", 49 | "blog", 50 | "boilerplate", 51 | "build", 52 | "cli", 53 | "cli-app", 54 | "collection", 55 | "command-line", 56 | "create", 57 | "dev", 58 | "development", 59 | "framework", 60 | "front", 61 | "frontend", 62 | "loader", 63 | "page", 64 | "plugin", 65 | "post", 66 | "project", 67 | "projects", 68 | "scaffold", 69 | "scaffolder", 70 | "scaffolding", 71 | "site", 72 | "template", 73 | "templates", 74 | "view", 75 | "webapp", 76 | "yeoman", 77 | "yo" 78 | ], 79 | "verb": { 80 | "toc": false, 81 | "layout": "default", 82 | "tasks": [ 83 | "readme" 84 | ], 85 | "plugins": [ 86 | "gulp-format-md" 87 | ], 88 | "related": { 89 | "list": [ 90 | "load-templates", 91 | "matched", 92 | "micromatch" 93 | ] 94 | }, 95 | "reflinks": [ 96 | "verb" 97 | ], 98 | "lint": { 99 | "reflinks": true 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaFeatures": { 3 | "modules": true, 4 | "experimentalObjectRestSpread": true 5 | }, 6 | 7 | "env": { 8 | "browser": false, 9 | "es6": true, 10 | "node": true, 11 | "mocha": true 12 | }, 13 | 14 | "globals": { 15 | "document": false, 16 | "navigator": false, 17 | "window": false 18 | }, 19 | 20 | "rules": { 21 | "accessor-pairs": 2, 22 | "arrow-spacing": [2, { "before": true, "after": true }], 23 | "block-spacing": [2, "always"], 24 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 25 | "comma-dangle": [2, "never"], 26 | "comma-spacing": [2, { "before": false, "after": true }], 27 | "comma-style": [2, "last"], 28 | "constructor-super": 2, 29 | "curly": [2, "multi-line"], 30 | "dot-location": [2, "property"], 31 | "eol-last": 2, 32 | "eqeqeq": [2, "allow-null"], 33 | "generator-star-spacing": [2, { "before": true, "after": true }], 34 | "handle-callback-err": [2, "^(err|error)$" ], 35 | "indent": [2, 2, { "SwitchCase": 1 }], 36 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 37 | "keyword-spacing": [2, { "before": true, "after": true }], 38 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 39 | "new-parens": 2, 40 | "no-array-constructor": 2, 41 | "no-caller": 2, 42 | "no-class-assign": 2, 43 | "no-cond-assign": 2, 44 | "no-const-assign": 2, 45 | "no-control-regex": 2, 46 | "no-debugger": 2, 47 | "no-delete-var": 2, 48 | "no-dupe-args": 2, 49 | "no-dupe-class-members": 2, 50 | "no-dupe-keys": 2, 51 | "no-duplicate-case": 2, 52 | "no-empty-character-class": 2, 53 | "no-eval": 2, 54 | "no-ex-assign": 2, 55 | "no-extend-native": 2, 56 | "no-extra-bind": 2, 57 | "no-extra-boolean-cast": 2, 58 | "no-extra-parens": [2, "functions"], 59 | "no-fallthrough": 2, 60 | "no-floating-decimal": 2, 61 | "no-func-assign": 2, 62 | "no-implied-eval": 2, 63 | "no-inner-declarations": [2, "functions"], 64 | "no-invalid-regexp": 2, 65 | "no-irregular-whitespace": 2, 66 | "no-iterator": 2, 67 | "no-label-var": 2, 68 | "no-labels": 2, 69 | "no-lone-blocks": 2, 70 | "no-mixed-spaces-and-tabs": 2, 71 | "no-multi-spaces": 2, 72 | "no-multi-str": 2, 73 | "no-multiple-empty-lines": [2, { "max": 1 }], 74 | "no-native-reassign": 0, 75 | "no-negated-in-lhs": 2, 76 | "no-new": 2, 77 | "no-new-func": 2, 78 | "no-new-object": 2, 79 | "no-new-require": 2, 80 | "no-new-wrappers": 2, 81 | "no-obj-calls": 2, 82 | "no-octal": 2, 83 | "no-octal-escape": 2, 84 | "no-proto": 0, 85 | "no-redeclare": 2, 86 | "no-regex-spaces": 2, 87 | "no-return-assign": 2, 88 | "no-self-compare": 2, 89 | "no-sequences": 2, 90 | "no-shadow-restricted-names": 2, 91 | "no-spaced-func": 2, 92 | "no-sparse-arrays": 2, 93 | "no-this-before-super": 2, 94 | "no-throw-literal": 2, 95 | "no-trailing-spaces": 0, 96 | "no-undef": 2, 97 | "no-undef-init": 2, 98 | "no-unexpected-multiline": 2, 99 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 100 | "no-unreachable": 2, 101 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 102 | "no-useless-call": 0, 103 | "no-with": 2, 104 | "one-var": [0, { "initialized": "never" }], 105 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], 106 | "padded-blocks": [0, "never"], 107 | "quotes": [2, "single", "avoid-escape"], 108 | "radix": 2, 109 | "semi": [2, "always"], 110 | "semi-spacing": [2, { "before": false, "after": true }], 111 | "space-before-blocks": [2, "always"], 112 | "space-before-function-paren": [2, "never"], 113 | "space-in-parens": [2, "never"], 114 | "space-infix-ops": 2, 115 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 116 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], 117 | "use-isnan": 2, 118 | "valid-typeof": 2, 119 | "wrap-iife": [2, "any"], 120 | "yoda": [2, "never"] 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var utils = require('./utils'); 5 | 6 | function loader(patterns, config) { 7 | if (utils.isObject(patterns)) { 8 | config = patterns; 9 | patterns = undefined; 10 | } 11 | 12 | return function plugin(app) { 13 | if (utils.isRegistered(this, 'assemble-loader')) return; 14 | 15 | // register the plugin on an "app" 16 | if (utils.isValidInstance(this)) { 17 | appLoader(this, config); 18 | 19 | // if patterns are passed to the plugin, load them now 20 | if (utils.isValidGlob(patterns)) { 21 | this.load(patterns); 22 | } 23 | } 24 | 25 | // register the plugin on a "collection" 26 | if (utils.isValidInstance(this, ['collection'])) { 27 | collectionLoader(this, config); 28 | 29 | // if patterns are passed to the plugin, load them now 30 | if (utils.isValidGlob(patterns)) { 31 | this.load(patterns); 32 | } 33 | } 34 | 35 | return plugin; 36 | }; 37 | } 38 | 39 | /** 40 | * Adds a `.load` method to the "app" instance for loading views that 41 | * that don't belong to any particular collection. It just returns the 42 | * object of views instead of caching them. 43 | * 44 | * ```js 45 | * var loader = require('assemble-loader'); 46 | * var assemble = require('assemble'); 47 | * var app = assemble(); 48 | * app.use(loader()); 49 | * 50 | * var views = app.load('foo/*.hbs'); 51 | * console.log(views); 52 | * ``` 53 | * @param {Object} `app` application instance (e.g. assemble, verb, etc) 54 | * @param {Object} `config` Settings to use when registering the plugin 55 | * @return {Object} Returns an object of _un-cached_ views, from a glob, string, array of strings, or objects. 56 | * @api public 57 | */ 58 | 59 | function appLoader(app, config) { 60 | app.define('load', load('view', config)); 61 | var fn = app.view; 62 | 63 | app.define('view', function() { 64 | var view = fn.apply(this, arguments); 65 | utils.contents.sync(view); 66 | return view; 67 | }); 68 | } 69 | 70 | /** 71 | * Collection loaders 72 | */ 73 | 74 | function collectionLoader(collection, config) { 75 | collection._addView = collection.addView.bind(collection); 76 | var fn = collection.view; 77 | 78 | collection.define('view', function() { 79 | var view = fn.apply(this, arguments); 80 | utils.contents.sync(view); 81 | return view; 82 | }); 83 | 84 | /** 85 | * Patches the `.addViews` method to support glob patterns. 86 | * 87 | * @param {Object|String} `key` View name or object. 88 | * @param {Object} `value` View options, when key is a string. 89 | * @return {Object} 90 | */ 91 | 92 | collection.define('addView', function(key, value) { 93 | return this._addView.apply(this, arguments); 94 | }); 95 | 96 | /** 97 | * Patches the `.addViews` method to support glob patterns. 98 | * 99 | * @param {Object|String} `key` View name or object. 100 | * @param {Object} `value` View options, when key is a string. 101 | * @return {Object} 102 | */ 103 | 104 | collection.define('addViews', function(key, value) { 105 | this.load.apply(this, arguments); 106 | return this; 107 | }); 108 | 109 | collection.define('loadView', function(filepath, options) { 110 | this.load.apply(this, arguments); 111 | return this; 112 | }); 113 | 114 | collection.define('loadViews', function(patterns, options) { 115 | this.load.apply(this, arguments); 116 | return this; 117 | }); 118 | 119 | collection.define('load', function() { 120 | return load('_addView', config).apply(this, arguments); 121 | }); 122 | } 123 | 124 | /** 125 | * Create options from: 126 | * + `config` - settings passed when registering plugin 127 | * + `app.options` - options set on the instance 128 | * + `options` - options passed when calling a loader method 129 | */ 130 | 131 | function mergeOptions(app, config, options) { 132 | if (utils.isView(options)) options = {}; 133 | var opts = utils.extend({}, config, app.options, options); 134 | opts.cwd = path.resolve(opts.cwd || app.cwd || process.cwd()); 135 | return opts; 136 | } 137 | 138 | /** 139 | * Create a `Loader` instance with a `loaderfn` bound 140 | * to the app or collection instance. 141 | */ 142 | 143 | function createLoader(options, fn) { 144 | var loader = new utils.Loader(options); 145 | return function() { 146 | if (!this.isApp) loader.cache = this.views; 147 | loader.options.loaderFn = fn.bind(this); 148 | loader.load.apply(loader, arguments); 149 | return loader.cache; 150 | }; 151 | } 152 | 153 | /** 154 | * Create a function for loading views using the given 155 | * `method` on the collection or app. 156 | */ 157 | 158 | function load(method, config) { 159 | return function(patterns, options) { 160 | var opts = mergeOptions(this, config, options); 161 | var loader = createLoader(opts, this[method]); 162 | return loader.apply(this, arguments); 163 | }; 164 | } 165 | 166 | /** 167 | * Expose `loader` 168 | */ 169 | 170 | module.exports = loader; 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # assemble-loader [![NPM version](https://img.shields.io/npm/v/assemble-loader.svg?style=flat)](https://www.npmjs.com/package/assemble-loader) [![NPM monthly downloads](https://img.shields.io/npm/dm/assemble-loader.svg?style=flat)](https://npmjs.org/package/assemble-loader) [![NPM total downloads](https://img.shields.io/npm/dt/assemble-loader.svg?style=flat)](https://npmjs.org/package/assemble-loader) [![Linux Build Status](https://img.shields.io/travis/assemble/assemble-loader.svg?style=flat&label=Travis)](https://travis-ci.org/assemble/assemble-loader) 2 | 3 | > Assemble plugin (^0.6.0) for loading globs of views onto custom view collections. Also works with verb or other Templates.js based applications. 4 | 5 | ## Install 6 | 7 | Install with [npm](https://www.npmjs.com/): 8 | 9 | ```sh 10 | $ npm install --save assemble-loader 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```js 16 | var assemble = require('assemble'); 17 | var loader = require('assemble-loader'); 18 | var app = assemble() 19 | app.use(loader()); 20 | 21 | app.create('pages', {cwd: 'src/pages'}); 22 | app.create('partials', {cwd: 'src/partials'}); 23 | 24 | // you can now load globs of templates onto any collection 25 | app.pages('*.hbs'); 26 | app.partials('*.hbs'); 27 | ``` 28 | 29 | ## API 30 | 31 | ```js 32 | var assemble = require('assemble'); 33 | var loader = require('assemble-loader'); 34 | var app = assemble() 35 | 36 | // use the plugin 37 | app.use(loader()); 38 | ``` 39 | 40 | Optionally pass glob patterns to the plugin: 41 | 42 | ```js 43 | var app = assemble(); 44 | // this can only be done once when the plugin is registered 45 | app.use(loader('foo/*.hbs')); 46 | 47 | // you can use the `.loadViews()` method any number of times 48 | app.create('pages') 49 | .use(loader('foo/*.hbs')) 50 | .loadViews('bar/*.hbs') 51 | .loadViews('baz/*.hbs') 52 | .loadViews('qux/*.hbs'); 53 | ``` 54 | 55 | ## Collections 56 | 57 | **All collections** 58 | 59 | When the plugin is added to the `app` instance (as in the previous example), a `.load` method will also be added to every collection created. 60 | 61 | ```js 62 | var app = assemble() 63 | .use(loader()); 64 | 65 | // cache views on `app.views.posts` 66 | app.create('posts') 67 | .load('content/*.md'); 68 | 69 | // cache views on `app.views.docs` 70 | app.create('docs') 71 | .load('docs/*.md'); 72 | ``` 73 | 74 | **Specific collections** 75 | 76 | If you only want to add the loader to a specific collection, you can pass the plugin to the `.use` method on the collection. 77 | 78 | ```js 79 | var app = assemble(); 80 | 81 | // `create` returns the collection instance 82 | app.create('posts') 83 | .use(loader()) 84 | .load('content/*.md'); 85 | 86 | // this works too, since `create` adds methods to `app` 87 | // for the collection 88 | app.posts 89 | .load('*.hbs') 90 | .load('*.txt'); 91 | ``` 92 | 93 | ## About 94 | 95 | ### Related projects 96 | 97 | * [load-templates](https://www.npmjs.com/package/load-templates): Load templates/views using globs, file paths, objects, arrays, or key-value pairs. | [homepage](https://github.com/jonschlinkert/load-templates "Load templates/views using globs, file paths, objects, arrays, or key-value pairs.") 98 | * [matched](https://www.npmjs.com/package/matched): Adds array support to node-glob, sync and async. Also supports tilde expansion (user home) and… [more](https://github.com/jonschlinkert/matched) | [homepage](https://github.com/jonschlinkert/matched "Adds array support to node-glob, sync and async. Also supports tilde expansion (user home) and resolving to global npm modules.") 99 | * [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/jonschlinkert/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.") 100 | 101 | ### Contributing 102 | 103 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). 104 | 105 | ### Building docs 106 | 107 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ 108 | 109 | To generate the readme, run the following command: 110 | 111 | ```sh 112 | $ npm install -g verbose/verb#dev verb-generate-readme && verb 113 | ``` 114 | 115 | ### Running tests 116 | 117 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: 118 | 119 | ```sh 120 | $ npm install && npm test 121 | ``` 122 | 123 | ### Author 124 | 125 | **Jon Schlinkert** 126 | 127 | * [github/jonschlinkert](https://github.com/jonschlinkert) 128 | * [twitter/jonschlinkert](https://twitter.com/jonschlinkert) 129 | 130 | ### License 131 | 132 | Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert). 133 | MIT 134 | 135 | *** 136 | 137 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.4.2, on February 20, 2017._ -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | require('should'); 5 | var path = require('path'); 6 | var assert = require('assert'); 7 | var App = require('templates'); 8 | var Views = App.Views; 9 | var loader = require('./'); 10 | var collection; 11 | var app; 12 | 13 | function res(fp) { 14 | return path.resolve(fp); 15 | } 16 | 17 | describe('loader', function() { 18 | describe('app.load()', function() { 19 | beforeEach(function() { 20 | app = new App(); 21 | }); 22 | 23 | it('should return a function:', function() { 24 | assert.equal(typeof loader(), 'function'); 25 | }); 26 | 27 | it('should decorate `.load` onto the app instance', function() { 28 | app.use(loader()); 29 | var view = app.view({path: 'index.js'}); 30 | 31 | var collection = app.load('*.js', {cwd: __dirname}); 32 | assert(collection.hasOwnProperty(res('index.js'))); 33 | }); 34 | 35 | it('should take options on loader', function() { 36 | app.use(loader({cwd: 'fixtures'})); 37 | var collection = app.load('*.txt'); 38 | assert(collection.hasOwnProperty(res('fixtures/a.txt'))); 39 | }); 40 | 41 | it('should take options on load', function() { 42 | app.use(loader()); 43 | var collection = app.load('*.txt', {cwd: 'fixtures'}); 44 | assert(collection.hasOwnProperty(res('fixtures/a.txt'))); 45 | }); 46 | 47 | it('should use the renameKey options on load', function() { 48 | app.use(loader()); 49 | var collection = app.load('*.txt', { 50 | cwd: 'fixtures', 51 | renameKey: function(file) { 52 | return file.path; 53 | } 54 | }); 55 | assert(collection.hasOwnProperty(res('fixtures/a.txt'))); 56 | }); 57 | }); 58 | 59 | describe('collection.loadViews()', function() { 60 | beforeEach(function() { 61 | app = new App(); 62 | app.on('error', function(err) { 63 | console.log(err); 64 | process.exit(1); 65 | }); 66 | }); 67 | 68 | it('should decorate `loadViews` onto collections', function() { 69 | app.use(loader()); 70 | app.create('pages'); 71 | 72 | app.pages.loadViews('*.js'); 73 | assert(app.views.pages.hasOwnProperty(res('index.js'))); 74 | }); 75 | 76 | it('should use a cwd defined on the collection options:', function() { 77 | app.use(loader()); 78 | app.create('pages') 79 | .option('cwd', 'fixtures') 80 | .loadViews('*.tmpl'); 81 | 82 | assert(app.views.pages.hasOwnProperty(res('fixtures/a.tmpl'))); 83 | }); 84 | 85 | it('should use a cwd defined on create:', function() { 86 | app.use(loader()); 87 | app.create('pages', {cwd: 'fixtures'}) 88 | .loadViews('*.tmpl'); 89 | 90 | assert(app.views.pages.hasOwnProperty(res('fixtures/a.tmpl'))); 91 | }); 92 | 93 | it('should use a cwd defined on create with collection loader', function() { 94 | app.use(loader()); 95 | app.create('pages', {cwd: 'fixtures'}); 96 | app.pages('*.tmpl'); 97 | 98 | assert(app.views.pages.hasOwnProperty(res('fixtures/a.tmpl'))); 99 | }); 100 | }); 101 | 102 | describe('collection loader function', function() { 103 | beforeEach(function() { 104 | app = new App(); 105 | }); 106 | 107 | it('should work as a collection plugin:', function() { 108 | app.create('pages') 109 | .use(loader('*.json')); 110 | assert(app.views.pages.hasOwnProperty(res('package.json'))); 111 | }); 112 | 113 | it('should decorate `load` on the collection:', function() { 114 | app.create('files') 115 | .use(loader()); 116 | 117 | app.files.load('*.json'); 118 | assert(app.views.files.hasOwnProperty(res('package.json'))); 119 | }); 120 | 121 | it('should be chainable:', function() { 122 | app.create('pages') 123 | .use(loader()) 124 | .loadViews('*.json') 125 | .loadViews('*.js'); 126 | 127 | assert(app.views.pages.hasOwnProperty(res('index.js'))); 128 | assert(app.views.pages.hasOwnProperty(res('package.json'))); 129 | }); 130 | 131 | it('should add the `loadViews` method to a collection:', function() { 132 | app.create('pages') 133 | .use(loader()); 134 | 135 | app.pages.loadViews('fixtures/*.txt'); 136 | assert(app.views.pages.hasOwnProperty(res('fixtures/a.txt'))); 137 | }); 138 | 139 | it('collection.loadViews should be chainable', function() { 140 | app.create('pages') 141 | .use(loader()) 142 | .loadViews('*.json') 143 | .loadViews('*.js'); 144 | 145 | assert(app.views.pages.hasOwnProperty(res('index.js'))); 146 | assert(app.views.pages.hasOwnProperty(res('package.json'))); 147 | }); 148 | 149 | it('should patch `addViews` to support globs:', function() { 150 | app.create('pages') 151 | .use(loader()); 152 | 153 | app.pages.addViews('fixtures/*.txt'); 154 | assert(app.views.pages.hasOwnProperty(res('fixtures/a.txt'))); 155 | }); 156 | 157 | it('should load globs with app collection methods:', function() { 158 | app.create('pages') 159 | .use(loader()); 160 | 161 | app.pages('fixtures/*.txt'); 162 | assert(app.views.pages.hasOwnProperty(res('fixtures/a.txt'))); 163 | }); 164 | 165 | it('should load glob arrays with app collection methods:', function() { 166 | app.create('pages') 167 | .use(loader()); 168 | 169 | app.pages(['fixtures/*.txt']); 170 | assert(app.views.pages.hasOwnProperty(res('fixtures/a.txt'))); 171 | }); 172 | 173 | it('should not change native behavior with addView:', function() { 174 | app.create('pages') 175 | .use(loader()); 176 | 177 | app.page('a', {content: '...'}); 178 | app.page('b', {content: '...'}); 179 | app.page('c', {content: '...'}); 180 | 181 | assert(app.views.pages.hasOwnProperty('a')); 182 | assert(app.views.pages.hasOwnProperty('b')); 183 | assert(app.views.pages.hasOwnProperty('c')); 184 | }); 185 | 186 | it('should maintain backwards compatibility with addViews:', function() { 187 | app.create('pages') 188 | .use(loader()); 189 | 190 | app.pages({ 191 | a: {contents: '...'}, 192 | b: {contents: '...'}, 193 | c: {contents: '...'} 194 | }); 195 | 196 | assert(app.views.pages.hasOwnProperty('a')); 197 | assert(app.views.pages.hasOwnProperty('b')); 198 | assert(app.views.pages.hasOwnProperty('c')); 199 | }); 200 | 201 | it('should get the contents for a view:', function() { 202 | app.create('pages') 203 | .use(loader()); 204 | 205 | app.pages(['fixtures/*.txt']); 206 | var page = app.pages.getView('fixtures/a.txt'); 207 | assert.equal(page.contents.toString(), 'This is AAA'); 208 | }); 209 | 210 | it('should take a custom renameKey function on the options:', function() { 211 | app.create('pages') 212 | .use(loader()); 213 | 214 | app.pages.option('renameKey', function(key, file) { 215 | return path.basename(key); 216 | }); 217 | 218 | app.pages.loadViews('fixtures/*.txt'); 219 | assert(app.views.pages.hasOwnProperty('a.txt')); 220 | }); 221 | 222 | it('should keep collection options separate:', function() { 223 | app.use(loader()); 224 | app.create('pages'); 225 | app.create('partials', {viewType: 'partial'}); 226 | app.create('layouts', {viewType: 'layout'}); 227 | 228 | app.on('page', function(view, type) { 229 | assert.equal(view.options.viewType.length, 1); 230 | assert.equal(view.options.viewType[0], 'renderable'); 231 | }); 232 | app.on('layout', function(view, type) { 233 | assert.equal(view.options.viewType.length, 1); 234 | assert.equal(view.options.viewType[0], 'layout'); 235 | }); 236 | app.on('partial', function(view, type) { 237 | assert.equal(view.options.viewType.length, 1); 238 | assert.equal(view.options.viewType[0], 'partial'); 239 | }); 240 | 241 | app.pages.option('renameKey', function(key, file) { 242 | return path.basename(key); 243 | }); 244 | app.layouts.option('renameKey', function(key, file) { 245 | return path.basename(key); 246 | }); 247 | app.partials.option('renameKey', function(key, file) { 248 | return path.basename(key); 249 | }); 250 | 251 | app.pages('fixtures/*.txt'); 252 | app.layouts('fixtures/*.hbs'); 253 | app.partials('fixtures/*.tmpl'); 254 | 255 | assert(app.views.pages.hasOwnProperty('a.txt')); 256 | }); 257 | }); 258 | 259 | describe('loadView', function() { 260 | beforeEach(function() { 261 | collection = new Views({ 262 | renameKey: function(key) { 263 | return path.basename(key); 264 | } 265 | }); 266 | collection.use(loader()); 267 | }); 268 | 269 | it('should load a file and add it to `views`:', function() { 270 | collection.loadView('fixtures/a.tmpl'); 271 | collection.views.should.have.property('a.tmpl'); 272 | }); 273 | 274 | it('should use a cwd defined on the collection:', function() { 275 | collection.option('cwd', 'fixtures'); 276 | collection.loadView('a.tmpl'); 277 | collection.views.should.have.property('a.tmpl'); 278 | }); 279 | 280 | it('should handle files with no extension:', function() { 281 | collection.loadView('LICENSE'); 282 | collection.views.should.have.property('LICENSE'); 283 | }); 284 | }); 285 | 286 | describe('loadViews', function() { 287 | beforeEach(function() { 288 | collection = new Views({ 289 | renameKey: function(key) { 290 | return path.basename(key); 291 | } 292 | }); 293 | collection.use(loader()); 294 | }); 295 | 296 | it('should load a view from a filepath:', function() { 297 | collection.loadViews('fixtures/a.tmpl'); 298 | collection.views.should.have.property('a.tmpl'); 299 | }); 300 | 301 | it('should load views from a glob:', function() { 302 | collection.loadViews('fixtures/*.tmpl'); 303 | collection.views.should.have.property('a.tmpl'); 304 | }); 305 | 306 | it('should load views an array globs:', function() { 307 | collection.loadViews(['fixtures/*.tmpl']); 308 | collection.views.should.have.property('a.tmpl'); 309 | }); 310 | 311 | it('should use a cwd defined on the collection:', function() { 312 | collection.option('cwd', 'fixtures'); 313 | collection.loadViews('*.tmpl'); 314 | collection.views.should.have.property('a.tmpl'); 315 | }); 316 | 317 | it('should handle files with no extension:', function() { 318 | collection.loadViews('LICENSE'); 319 | collection.views.should.have.property('LICENSE'); 320 | }); 321 | }); 322 | }); 323 | --------------------------------------------------------------------------------