Expose method for creating an AST node from an HTML element

This commit is contained in:
Patrick Steele-Idem 2015-12-29 11:01:48 -07:00
parent fb6b436063
commit 2a34d940e4
2 changed files with 107 additions and 80 deletions

View File

@ -8,6 +8,16 @@ var deresolve = require('./util/deresolve');
var UniqueVars = require('./util/UniqueVars');
var PosInfo = require('./util/PosInfo');
var CompileError = require('./CompileError');
var path = require('path');
var Node = require('./ast/Node');
function getTaglibPath(taglibPath) {
if (typeof window === 'undefined') {
return path.relative(process.cwd(), taglibPath);
} else {
return taglibPath;
}
}
class CompileContext {
constructor(src, filename, builder) {
@ -73,6 +83,101 @@ class CompileContext {
getStaticVars() {
return this._staticVars;
}
createNodeForEl(tagName, attributes, argument) {
var elDef;
var builder = this.builder;
if (typeof tagName === 'object') {
elDef = tagName;
tagName = elDef.tagName;
attributes = elDef.attributes;
argument = elDef.argument;
} else {
elDef = {
tagName: tagName,
argument: argument,
attributes: attributes
};
}
ok(typeof tagName === 'string', 'Invalid "tagName"');
ok(attributes == null || Array.isArray(attributes), 'Invalid "attributes"');
if (!attributes) {
attributes = elDef.attributes = [];
}
var node;
var elNode = builder.htmlElement(elDef);
var taglibLookup = this.taglibLookup;
var tagDef = taglibLookup.getTag(tagName);
if (tagDef) {
var nodeFactoryFunc = tagDef.getNodeFactory();
if (nodeFactoryFunc) {
node = nodeFactoryFunc(elNode, this);
if (!(node instanceof Node)) {
throw new Error('Invalid node returned from node factory for tag "' + tagName + '".');
}
}
}
if (!node) {
node = elNode;
}
node.pos = elDef.pos;
var foundAttrs = {};
// Validate the attributes
attributes.forEach((attr) => {
let attrName = attr.name;
let attrDef = taglibLookup.getAttribute(tagName, attrName);
if (!attrDef) {
if (tagDef) {
// var isAttrForTaglib = compiler.taglibs.isTaglib(attrUri);
//Tag doesn't allow dynamic attributes
this.addError({
node: node,
message: 'The tag "' + tagName + '" in taglib "' + getTaglibPath(tagDef.taglibId) + '" does not support attribute "' + attrName + '"'
});
}
return;
}
attr.def = attrDef;
foundAttrs[attrName] = true;
});
if (tagDef) {
// Add default values for any attributes. If an attribute has a declared
// default value and the attribute was not found on the element
// then add the attribute with the specified default value
tagDef.forEachAttribute((attrDef) => {
var attrName = attrDef.name;
if (attrDef.hasOwnProperty('defaultValue') && !foundAttrs.hasOwnProperty(attrName)) {
attributes.push({
name: attrName,
value: builder.literal(attrDef.defaultValue)
});
} else if (attrDef.required === true) {
// TODO Only throw an error if there is no data argument provided (just HTML attributes)
if (!foundAttrs.hasOwnProperty(attrName)) {
this.addError({
node: node,
message: 'The "' + attrName + '" attribute is required for tag "' + tagName + '" in taglib "' + getTaglibPath(tagDef.taglibId) + '".'
});
}
}
});
}
return node;
}
}
module.exports = CompileContext;

View File

@ -1,7 +1,5 @@
'use strict';
var ok = require('assert').ok;
var path = require('path');
var Node = require('./ast/Node');
var COMPILER_ATTRIBUTE_HANDLERS = {
whitespace: function(attr, compilerOptions) {
@ -27,15 +25,6 @@ function parseExpression(expression) {
return expression;
}
function getTaglibPath(taglibPath) {
if (typeof window === 'undefined') {
return path.relative(process.cwd(), taglibPath);
} else {
return taglibPath;
}
}
class Parser {
constructor(parserImpl) {
ok(parserImpl, '"parserImpl" is required');
@ -118,11 +107,10 @@ class Parser {
this.prevTextNode = null;
var node;
var elDef = {
tagName: tagName,
argument: argument,
pos: el.pos,
attributes: attributes.map((attr) => {
var isLiteral = false;
@ -145,73 +133,7 @@ class Parser {
})
};
var elNode = builder.htmlElement(elDef);
var taglibLookup = context.taglibLookup;
var tagDef = taglibLookup.getTag(tagName);
if (tagDef) {
var nodeFactoryFunc = tagDef.getNodeFactory();
if (nodeFactoryFunc) {
node = nodeFactoryFunc(elNode, context);
if (!(node instanceof Node)) {
throw new Error('Invalid node returned from node factory for tag "' + tagName + '".');
}
}
}
if (!node) {
node = elNode;
}
node.pos = el.pos;
var foundAttrs = {};
// Validate the attributes
attributes.forEach((attr) => {
let attrName = attr.name;
let attrDef = taglibLookup.getAttribute(tagName, attrName);
if (!attrDef) {
if (tagDef) {
// var isAttrForTaglib = compiler.taglibs.isTaglib(attrUri);
//Tag doesn't allow dynamic attributes
context.addError({
node: node,
message: 'The tag "' + tagName + '" in taglib "' + getTaglibPath(tagDef.taglibId) + '" does not support attribute "' + attrName + '"'
});
}
return;
}
attr.def = attrDef;
foundAttrs[attrName] = true;
});
if (tagDef) {
// Add default values for any attributes. If an attribute has a declared
// default value and the attribute was not found on the element
// then add the attribute with the specified default value
tagDef.forEachAttribute(function (attrDef) {
var attrName = attrDef.name;
if (attrDef.hasOwnProperty('defaultValue') && !foundAttrs.hasOwnProperty(attrName)) {
attributes.push({
name: attrName,
value: builder.literal(attrDef.defaultValue)
});
} else if (attrDef.required === true) {
// TODO Only throw an error if there is no data argument provided (just HTML attributes)
if (!foundAttrs.hasOwnProperty(attrName)) {
context.addError({
node: node,
message: 'The "' + attrName + '" attribute is required for tag "' + tagName + '" in taglib "' + getTaglibPath(tagDef.taglibId) + '".'
});
}
}
});
}
var node = this.context.createNodeForEl(elDef);
this.parentNode.appendChild(node);