forked from merkle-open/nitro-component-handlebars
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
115 lines (110 loc) · 4.23 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
/* eslint-disable prefer-rest-params, complexity */
'use strict';
const assert = require('assert');
const _ = require('lodash');
const EventEmitter = require('events').EventEmitter;
const fs = require('fs');
const path = require('path');
const hbs = require('hbs');
// local lib
const TemplateResolver = require('./lib/template-resolver.js');
const TemplateValidator = require('./lib/template-validator.js');
/**
* Configuration of the component handlebars helper
*
* @param {Object} userConfig configuration
* @returns {function} the configured helper
*/
function ComponentHandlebarsHelper(userConfig) {
assert(typeof userConfig === 'object', 'The config has to be an object.');
assert(typeof userConfig.rootDirectory === 'string', 'Please pass the root directory of the nitro project');
// Default configuration
const config = _.extend({
// The root directory is required - just list it for documentation
rootDirectory: '',
// Schema filename
schemaName: 'pattern.json',
// Is a schema required
useSchema: false,
// Prerender handler to modify the data object
preRenderHandler: (templateRenderData) => templateRenderData,
// Prevalidate handler to modify the schema
preValidateHandler: (schema) => schema,
// Allows to set a custom error handler
errorHandler: (err) => { throw err; },
}, userConfig);
const eventEmitter = new EventEmitter();
/**
* handlebars helper: {{component ComponentName Data Variation}}
*
* Usage (simple)
* {{component "Button" "button-fancy"}}
*
* Usage (with children)
* {{#component "Button"}}Click Me{{/component}}
*
* Usage (passing arguments)
* {{#component "Button" disabled=true}}Click Me{{/component}}
*
* @param {string} componentName the name of the component e.g. "base/atoms/button"
* @param {Object} handlebarsHelperContext the handlebars conext object
* @returns {string} rendered component
*/
return _.extend(function component(componentName, handlebarsHelperContext) {
let componentRenderData = {};
try {
assert(typeof componentName === 'string', 'componentName is required: e.g. {{component "base/atoms/button"}}');
assert(arguments.length === 2, 'syntax error please use {{component "base/atoms/button" setting="value"}}');
const templatePath = TemplateResolver.resolveTemplatePath(componentName, config.rootDirectory);
// All neccessary information to render the component
const componentInformation = {
componentName,
templateFile: templatePath,
templateDirectory: path.dirname(templatePath),
handlebarsHelperContext,
config,
};
// Get template schema
const baseSchema = config.useSchema
? TemplateValidator.getSchema(componentInformation.templateDirectory, config.schemaName)
: null;
// Allow to modify the schema instance
componentInformation.templateSchema = config.preValidateHandler(
baseSchema,
componentInformation
);
// Get template render data
const baseRenderData = TemplateResolver.getComponentRenderData(componentInformation);
// Allow to modify the render data
componentRenderData = config.preRenderHandler(
baseRenderData,
componentInformation
);
// Validate render data
if (config.useSchema) {
TemplateValidator.validate(componentRenderData, componentInformation);
}
const componentTemplate = fs.readFileSync(templatePath).toString();
// Render template
return new hbs.handlebars.SafeString(
hbs.handlebars.compile(componentTemplate)(componentRenderData, handlebarsHelperContext)
);
} catch (err) {
// Try to write an expressive error message
const name = handlebarsHelperContext && handlebarsHelperContext.name;
let errMessage = `${name} (${componentName}) with arguments: ${JSON.stringify(componentRenderData)}` +
` failed because ${err.message}`;
if (handlebarsHelperContext &&
handlebarsHelperContext.data &&
handlebarsHelperContext.data.root &&
handlebarsHelperContext.data.root.filepath) {
const relativePath = path.relative(config.rootDirectory, handlebarsHelperContext.data.root.filepath);
errMessage = `[${relativePath}] - ${errMessage}`;
}
err.message = errMessage;
eventEmitter.emit('error', err);
return config.errorHandler(err, componentName);
}
}, eventEmitter);
}
module.exports = ComponentHandlebarsHelper;