var fs = require('fs'); var ok = require('assert').ok; var nodePath = require('path'); var Taglib = require('./Taglib'); var cache = {}; var forEachEntry = require('raptor-util').forEachEntry; var raptorRegexp = require('raptor-regexp'); var tagDefFromCode = require('./tag-def-from-code'); function removeDashes(str) { return str.replace(/-([a-z])/g, function (match, lower) { return lower.toUpperCase(); }); } function invokeHandlers(config, handlers, path) { if (!config) { throw new Error('"config" argument is required'); } if (typeof config !== 'object') { throw new Error('Object expected for ' + path); } for (var k in config) { if (config.hasOwnProperty(k)) { var value = config[k]; k = removeDashes(k); var handler = handlers[k]; if (!handler) { throw new Error('Invalid option of "' + k + '" for ' + path + '. Allowed: ' + Object.keys(handlers).join(', ')); } try { handler(value); } catch(e) { if (!e.invokeHandlerError) { var error = new Error('Error while applying option of "' + k + '" for ' + path + '. Exception: ' + (e.stack || e)); error.invokeHandlerError = e; throw error; } else { throw e; } } } } if (handlers._end) { try { handlers._end(); } catch(e) { if (!e.invokeHandlerError) { var error = new Error('Error for option ' + path + '. Exception: ' + (e.stack || e)); error.invokeHandlerError = e; throw error; } else { throw e; } } } } function buildAttribute(attr, attrProps, path) { invokeHandlers(attrProps, { type: function(value) { attr.type = value; }, targetProperty: function(value) { attr.targetProperty = value; }, defaultValue: function(value) { attr.defaultValue = value; }, namespace: function(value) { attr.namespace = value; }, pattern: function(value) { if (value === true) { var patternRegExp = raptorRegexp.simple(attr.name); attr.pattern = patternRegExp; } }, allowExpressions: function(value) { attr.allowExpressions = value; }, preserveName: function(value) { attr.preserveName = value; } }, path); return attr; } function handleAttributes(value, parent, path) { forEachEntry(value, function(attrName, attrProps) { var parts = attrName.split(':'); var namespace = null; var localName = null; if (parts.length === 2) { namespace = parts[0]; localName = parts[1]; } else if (parts.length === 1) { localName = attrName; } else { throw new Error('Invalid attribute name: ' + attrName); } var attr = new Taglib.Attribute(namespace, localName); if (attrProps == null) { attrProps = { type: 'string' }; } else if (typeof attrProps === 'string') { attrProps = { type: attrProps }; } buildAttribute(attr, attrProps, '"' + attrName + '" attribute as part of ' + path); parent.addAttribute(attr); }); } function buildTag(tagObject, path, taglib, dirname) { ok(tagObject); ok(typeof path === 'string'); ok(taglib); ok(typeof dirname === 'string'); var tag = new Taglib.Tag(taglib); invokeHandlers(tagObject, { name: function(value) { tag.name = value; }, renderer: function(value) { var ext = nodePath.extname(value); if (ext === '') { value += '.js'; } var path = nodePath.resolve(dirname, value); if (!fs.existsSync(path)) { throw new Error('Renderer at path "' + path + '" does not exist.'); } tag.renderer = path; }, template: function(value) { var path = nodePath.resolve(dirname, value); if (!fs.existsSync(path)) { throw new Error('Template at path "' + path + '" does not exist.'); } tag.template = path; }, attributes: function(value) { handleAttributes(value, tag, path); }, nodeClass: function(value) { var path = nodePath.resolve(dirname, value); if (!fs.existsSync(path)) { throw new Error('Node module at path "' + path + '" does not exist.'); } tag.nodeClass = path; }, transformer: function(value) { var transformer = new Taglib.Transformer(); if (typeof value === 'string') { value = { path: value }; } invokeHandlers(value, { path: function(value) { var ext = nodePath.extname(value); if (ext === '') { value += '.js'; } var path = nodePath.resolve(dirname, value); if (!fs.existsSync(path)) { throw new Error('Transformer at path "' + path + '" does not exist.'); } transformer.path = path; }, before: function(value) { transformer.before = value; }, after: function(value) { transformer.after = value; }, name: function(value) { transformer.name = value; } }, 'transformer in ' + path); ok(transformer.path, '"path" is required for transformer'); tag.addTransformer(transformer); }, 'var': function(value) { tag.addNestedVariable({ name: value }); }, vars: function(value) { if (value) { value.forEach(function(varName) { tag.addNestedVariable({ name: varName }); }); } }, importVar: function(value) { forEachEntry(value, function(varName, varValue) { var importedVar = { targetProperty: varName }; var expression = varValue; if (!expression) { expression = varName; } else if (typeof expression === 'object') { expression = expression.expression; } if (!expression) { throw new Error('Invalid "import-var": ' + require('util').inspect(varValue)); } importedVar.expression = expression; tag.addImportedVariable(importedVar); }); } }, path); return tag; } /** * @param {String} tagsConfigPath path to tag definition file * @param {String} tagsConfigDirname path to directory of tags config file (should be path.dirname(tagsConfigPath)) * @param {String} dir the path to directory to scan * @param {String} taglib the taglib that is being loaded */ function scanTagsDir(tagsConfigPath, tagsConfigDirname, dir, taglib) { dir = nodePath.resolve(tagsConfigDirname, dir); var children = fs.readdirSync(dir); for (var i=0, len=children.length; i tag in ' + taglib.id; } var tag = buildTag(tagObject, path, taglib, tagDirname); if (tag.name === undefined) { tag.name = tagName; } taglib.addTag(tag); }); }, tagsDir: function(dir) { if (Array.isArray(dir)) { for (var i = 0; i < dir.length; i++) { scanTagsDir(path, dirname, dir[i], taglib); } } else { scanTagsDir(path, dirname, dir, taglib); } } }, path); taglib.id = path; cache[path] = taglib; return taglib; } function loadTaglibXml(taglibXml, path) { var TaglibXmlLoader = require('./TaglibXmlLoader'); var taglib = TaglibXmlLoader.load(taglibXml, path); taglib.id = path; return taglib; } function loadTaglibXmlFromFile(path) { var src = fs.readFileSync(path, {encoding: 'utf8'}); return loadTaglibXml(src, path); } exports.load = load; exports.loadTaglibXml = loadTaglibXml; exports.loadTaglibXmlFromFile = loadTaglibXmlFromFile;