-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathindex.js
185 lines (150 loc) · 6.21 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/**
* Handlebars Helper: {{compose}}
* https://github.com/helpers/handlebars-helper-compose
*
* Copyright (c) 2014 Jon Schlinkert, contributors.
* https://github.com/jonschlinkert
* Licensed under the MIT license.
*/
'use strict';
// Node.js modules
var path = require('path');
// node_modules
var file = require('fs-utils');
var glob = require('globule');
var marked = require('marked');
var extras = require('marked-extras');
var matter = require('gray-matter');
var _str = require('underscore.string');
var _ = require('lodash');
module.exports.register = function (Handlebars, options, params) {
var grunt = params.grunt;
var opts = options || {};
opts.compose = opts.compose || {};
var customMarkedOpts = _.extend(options.marked || {});
extras.init(customMarkedOpts);
var markedOpts = _.defaults(customMarkedOpts, extras.markedDefaults);
// The {{compose}} helper
Handlebars.registerHelper('compose', function(context, options) {
options = _.extend(context, options);
var hash = options.hash || {};
// Default options
options = _.extend({glob: {}, sep: '\n'}, options, opts.compose, hash);
var i = 0;
var result = '';
var data;
var ctx = _.extend(grunt.config.data, opts, this);
// Define marked.js options
marked.setOptions(markedOpts);
// Add some src variables to the context
ctx.dir = path.dirname(ctx.page.src || '');
ctx.base = file.basename(ctx.page.src);
var patterns = grunt.config.process(options.src);
if(!Array.isArray(patterns)) {
patterns = [patterns];
}
options.cwd = grunt.task.current.files[0].orig.cwd || options.cwd;
var cwd = path.join.bind(null, options.cwd || '');
// Recalculate cwd after ctx.dir has been added to context
if(options.hash.cwd) {
cwd = path.join.bind(null, grunt.config.process(options.hash.cwd));
}
/**
* Accepts two objects (a, b),
* @param {Object} a
* @param {Object} b
* @return {Number} returns 1 if (a >= b), otherwise -1
*/
var compareFn = function (a, b) {
var opts = _.extend({sortOrder: 'asc', sortBy: 'index'}, options);
a = a.data[opts.sortBy];
b = b.data[opts.sortBy];
var result = 0;
result = a > b ? 1 : a < b ? -1 : 0;
if(opts.sortOrder.toLowerCase() === 'desc') {
return result * -1;
}
return result;
};
patterns.forEach(function (pattern) {
var files = glob.find(cwd(pattern), options.glob);
if(options.filter) {
files = options.filter(files);
}
var src = files.map(function (filepath) {
i += 1;
var content = matter.read(filepath).content || '';
var metadata = matter.read(filepath).context || {};
/**
* Process context from last to first, with #1 winning over other contexts.
* 1. `context` : The given context (second parameter)
* 2. `metadata` : YAML front matter of the partial
* 3. `opts.data[name]` : JSON/YAML data file defined in Assemble options.data
* with a basename matching the name of the partial, e.g
* {{include 'foo'}} => foo.json
* 4. `this` : Typically either YAML front matter of the "inheriting" page,
* layout, block expression, or "parent" helper wrapping this helper
* 5. `opts` : Custom properties defined in Assemble options
* 6. `grunt.config.data`: Data from grunt.config.data
* (e.g. pkg: grunt.file.readJSON('package.json'))
*/
var basename = file.basename(filepath);
ctx = _.extend(ctx, opts.data[ctx.base], opts.data[basename], metadata, context);
// Process config (Lo-Dash) templates
ctx = processContext(grunt, ctx);
// Also process metadata separately so we can use it to extend the `data` object
metadata = processContext(grunt, metadata);
// Inject private variables into block, so that variables on `data`
// are only available in the block helper's child template and
// not in the original context
data = Handlebars.createFrame(ctx.data);
data = _.extend(data, opts.compose, _.omit(metadata, ['assemble', 'pages', 'plugins', '_plugins']));
// Best guess at some useful properties to add to the data context
data.filepath = filepath;
data.basename = basename;
data.filename = file.filename(filepath);
data.pagename = file.filename(filepath);
data.slug = data.slug || _str.slugify(data.basename);
data.id = data.slug;
data.title = data.title || ctx.title || _str.titleize(data.basename);
// Some of these might come in handy for ordering/sorting
// or identifying posts, chapters, etc.
data.index = i;
data.number = i + 1;
data.first = (i === 0);
data.prev = i - 1;
data.next = i + 1;
data.last = (i === (files.length - 1));
// Content from src files
var glob_fn = Handlebars.compile(content);
data.content = glob_fn(ctx).replace(/^\s+/, '');
if((opts.marked && opts.marked.process === true) || hash.markdown) {
data.content = marked(data.content);
}
// Content from inside the block
var output = options.fn(ctx, {data: data});
// Prepend output with the filepath to the original partial
if (options.origin && options.origin === true) {
output = '<!-- ' + filepath + ' -->\n' + output;
}
return {
data: data,
content: output
};
}).sort(options.compare || compareFn).map(function (obj) {
if(options.debug) {file.writeDataSync(options.debug, obj);}
// Return content from src files
return obj.content;
}).join(options.sep);
result += src;
});
return new Handlebars.SafeString(result);
});
/**
* Process templates using grunt.config.data and context
*/
var processContext = function (grunt, context) {
grunt.config.data = _.defaults(context || {}, _.cloneDeep(grunt.config.data));
return grunt.config.process(grunt.config.data);
};
};