mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Code comments and other code cleanup
This commit is contained in:
parent
9116bb7009
commit
2c95b1ed20
@ -30,9 +30,7 @@ TypeConverter.convert = function (value, targetType, allowExpressions) {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (targetType === 'expression') {
|
||||
if (targetType === 'expression' || targetType === 'object' || targetType === 'array') {
|
||||
if (value === '') {
|
||||
value = 'null';
|
||||
}
|
||||
|
||||
@ -150,6 +150,10 @@ module.exports = makeClass({
|
||||
|
||||
nestedTag.isNestedTag = true;
|
||||
|
||||
if (!nestedTag.targetProperty) {
|
||||
nestedTag.targetProperty = nestedTag.name;
|
||||
}
|
||||
|
||||
this.nestedTags[nestedTag.name] = nestedTag;
|
||||
},
|
||||
forEachNestedTag: function (callback, thisObj) {
|
||||
|
||||
@ -10,18 +10,50 @@ function AttrHandlers(attr){
|
||||
}
|
||||
|
||||
AttrHandlers.prototype = {
|
||||
/**
|
||||
* The attribute type. One of the following:
|
||||
* - string (the default)
|
||||
* - expression (a JavaScript expression)
|
||||
* - number
|
||||
* - integer
|
||||
* - int
|
||||
* - boolean
|
||||
* - float
|
||||
* - double
|
||||
* - object
|
||||
* - array
|
||||
*
|
||||
*/
|
||||
type: function(value) {
|
||||
var attr = this.attr;
|
||||
attr.type = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* The name of the target property to use when mapping
|
||||
* the attribute to a property on the target object.
|
||||
*/
|
||||
targetProperty: function(value) {
|
||||
var attr = this.attr;
|
||||
attr.targetProperty = value;
|
||||
},
|
||||
/**
|
||||
* The "default-value" property allows a default value
|
||||
* to be provided when the attribute is not declared
|
||||
* on the custom tag.
|
||||
*/
|
||||
defaultValue: function(value) {
|
||||
var attr = this.attr;
|
||||
attr.defaultValue = value;
|
||||
},
|
||||
/**
|
||||
* The "pattern" property allows the attribute
|
||||
* to be matched based on a simplified regular expression.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* "pattern": "myprefix-*"
|
||||
*/
|
||||
pattern: function(value) {
|
||||
var attr = this.attr;
|
||||
if (value === true) {
|
||||
@ -29,29 +61,80 @@ AttrHandlers.prototype = {
|
||||
attr.pattern = patternRegExp;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If "allow-expressions" is set to true (the default) then
|
||||
* the the attribute value will be parsed to find any dynamic
|
||||
* parts.
|
||||
*/
|
||||
allowExpressions: function(value) {
|
||||
var attr = this.attr;
|
||||
attr.allowExpressions = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* By default, the Marko compiler maps an attribute
|
||||
* to a property by removing all dashes from the attribute
|
||||
* name and converting each character after a dash to
|
||||
* an uppercase character (e.g. "my-attr" --> "myAttr").
|
||||
*
|
||||
* Setting "preserve-name" to true will prevent this from
|
||||
* happening for the attribute.
|
||||
*/
|
||||
preserveName: function(value) {
|
||||
var attr = this.attr;
|
||||
attr.preserveName = value;
|
||||
},
|
||||
/**
|
||||
* Declares an attribute as required. Currently, this is
|
||||
* not enforced and is only used for documentation purposes.
|
||||
*
|
||||
* Example:
|
||||
* "required": true
|
||||
*/
|
||||
required: function(value) {
|
||||
var attr = this.attr;
|
||||
attr.required = value === true;
|
||||
},
|
||||
/**
|
||||
* This is the opposite of "preserve-name" and will result
|
||||
* in dashes being removed from the attribute if set to true.
|
||||
*/
|
||||
removeDashes: function(value) {
|
||||
var attr = this.attr;
|
||||
attr.removeDashes = value === true;
|
||||
},
|
||||
/**
|
||||
* The description of the attribute. Only used for documentation.
|
||||
*/
|
||||
description: function() {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* The "set-flag" property allows a "flag" to be added to a Node instance
|
||||
* at compile time if the attribute is found on the node. This is helpful
|
||||
* if an attribute uses a pattern and a transformer wants to have a simple
|
||||
* check to see if the Node has an attribute that matched the pattern.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* "set-flag": "myCustomFlag"
|
||||
*
|
||||
* A Node instance can be checked if it has a flag set as shown below:
|
||||
*
|
||||
* if (node.hasFlag('myCustomFlag')) { ... }
|
||||
*
|
||||
*
|
||||
*/
|
||||
setFlag: function(value) {
|
||||
var attr = this.attr;
|
||||
attr.setFlag = value;
|
||||
},
|
||||
/**
|
||||
* An attribute can be marked for ignore. Ignored attributes
|
||||
* will be ignored during compilation.
|
||||
*/
|
||||
ignore: function(value) {
|
||||
var attr = this.attr;
|
||||
if (value === true) {
|
||||
|
||||
@ -28,19 +28,71 @@ function removeDashes(str) {
|
||||
});
|
||||
}
|
||||
|
||||
function handleVar(tag, value, path) {
|
||||
var nestedVariable;
|
||||
|
||||
function TagHandlers(tag, dirname, path) {
|
||||
if (typeof value === 'string') {
|
||||
nestedVariable = {
|
||||
name: value
|
||||
};
|
||||
} else {
|
||||
nestedVariable = {};
|
||||
|
||||
propertyHandlers(value, {
|
||||
|
||||
name: function(value) {
|
||||
nestedVariable.name = value;
|
||||
},
|
||||
|
||||
nameFromAttribute: function(value) {
|
||||
nestedVariable.nameFromAttribute = value;
|
||||
}
|
||||
|
||||
}, path);
|
||||
|
||||
if (!nestedVariable.name && !nestedVariable.nameFromAttribute) {
|
||||
throw new Error('The "name" or "name-from-attribute" attribute is required for a nested variable');
|
||||
}
|
||||
}
|
||||
|
||||
tag.addNestedVariable(nestedVariable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We load tag definition using this class. Properties in the taglib
|
||||
* definition (which is just a JavaScript object with properties)
|
||||
* are mapped to handler methods in an instance of this type.
|
||||
*
|
||||
* @param {Tag} tag The initially empty Tag instance that we populate
|
||||
* @param {String} dirname The full file system path associated with the tag being loaded
|
||||
* @param {String} path An informational path associated with this tag (used for error reporting)
|
||||
*/
|
||||
function TagHandlers(tag, dirname, path, taglib) {
|
||||
this.tag = tag;
|
||||
this.dirname = dirname;
|
||||
this.path = path;
|
||||
this.taglib = taglib;
|
||||
}
|
||||
|
||||
TagHandlers.prototype = {
|
||||
/**
|
||||
* The tag name
|
||||
* @param {String} value The tag name
|
||||
*/
|
||||
name: function(value) {
|
||||
var tag = this.tag;
|
||||
tag.name = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* The path to the renderer JS module to use for this tag.
|
||||
*
|
||||
* NOTE: We use the equivalent of require.resolve to resolve the JS module
|
||||
* and use the tag directory as the "from".
|
||||
*
|
||||
* @param {String} value The renderer path
|
||||
*/
|
||||
renderer: function(value) {
|
||||
var tag = this.tag;
|
||||
var dirname = this.dirname;
|
||||
@ -48,6 +100,12 @@ TagHandlers.prototype = {
|
||||
|
||||
tag.renderer = path;
|
||||
},
|
||||
|
||||
/**
|
||||
* A tag can use a renderer or a template to do the rendering. If
|
||||
* a template is provided then the value should be the path to the
|
||||
* template to use to render the custom tag.
|
||||
*/
|
||||
template: function(value) {
|
||||
var tag = this.tag;
|
||||
var dirname = this.dirname;
|
||||
@ -59,12 +117,32 @@ TagHandlers.prototype = {
|
||||
|
||||
tag.template = path;
|
||||
},
|
||||
|
||||
/**
|
||||
* An Object where each property maps to an attribute definition.
|
||||
* The property key will be the attribute name and the property value
|
||||
* will be the attribute definition. Example:
|
||||
* {
|
||||
* "attributes": {
|
||||
* "foo": "string",
|
||||
* "bar": "expression"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
attributes: function(value) {
|
||||
var tag = this.tag;
|
||||
var path = this.path;
|
||||
|
||||
handleAttributes(value, tag, path);
|
||||
},
|
||||
|
||||
/**
|
||||
* A custom tag can be mapped to a compile-time Node that gets
|
||||
* added to the parsed Abstract Syntax Tree (AST). The Node can
|
||||
* then generate custom JS code at compile time. The value
|
||||
* should be a path to a JS module that gets resolved using the
|
||||
* equivalent of require.resolve(path)
|
||||
*/
|
||||
nodeClass: function(value) {
|
||||
var tag = this.tag;
|
||||
var dirname = this.dirname;
|
||||
@ -72,10 +150,22 @@ TagHandlers.prototype = {
|
||||
var path = resolve(value, dirname);
|
||||
tag.nodeClass = path;
|
||||
},
|
||||
/**
|
||||
* If the "preserve-whitespace" property is set to true then
|
||||
* all whitespace nested below the custom tag in a template
|
||||
* will be stripped instead of going through the normal whitespace
|
||||
* removal rules.
|
||||
*/
|
||||
preserveWhitespace: function(value) {
|
||||
var tag = this.tag;
|
||||
tag.preserveWhitespace = !!value;
|
||||
},
|
||||
|
||||
/**
|
||||
* If a custom tag has an associated transformer then the transformer
|
||||
* will be called on the compile-time Node. The transformer can manipulate
|
||||
* the AST using the DOM-like API to change how the code gets generated.
|
||||
*/
|
||||
transformer: function(value) {
|
||||
var tag = this.tag;
|
||||
var dirname = this.dirname;
|
||||
@ -84,11 +174,19 @@ TagHandlers.prototype = {
|
||||
var transformer = new Taglib.Transformer();
|
||||
|
||||
if (typeof value === 'string') {
|
||||
// The value is a simple string type
|
||||
// so treat the value as the path to the JS
|
||||
// module for the transformer
|
||||
value = {
|
||||
path: value
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The transformer is a complex type and we need
|
||||
* to process each property to load the Transformer
|
||||
* definition.
|
||||
*/
|
||||
propertyHandlers(value, {
|
||||
path: function(value) {
|
||||
var path = resolve(value, dirname);
|
||||
@ -119,12 +217,48 @@ TagHandlers.prototype = {
|
||||
tag.addTransformer(transformer);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "var" property is used to declared nested variables that get
|
||||
* added as JavaScript variables at compile time.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* "var": "myScopedVariable",
|
||||
*
|
||||
* "var": {
|
||||
* "name": "myScopedVariable"
|
||||
* }
|
||||
*
|
||||
* "var": {
|
||||
* "name-from-attribute": "var"
|
||||
* }
|
||||
*/
|
||||
'var': function(value) {
|
||||
var tag = this.tag;
|
||||
tag.addNestedVariable({
|
||||
name: value
|
||||
});
|
||||
handleVar(this.tag, value, '"var" in tag ' + this.path);
|
||||
},
|
||||
/**
|
||||
* The "vars" property is equivalent to the "var" property
|
||||
* except that it expects an array of nested variables.
|
||||
*/
|
||||
vars: function(value) {
|
||||
var tag = this.tag;
|
||||
var self = this;
|
||||
|
||||
if (value) {
|
||||
value.forEach(function(v, i) {
|
||||
handleVar(tag, v, '"vars"[' + i + '] in tag ' + self.path);
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The "body-function" property" allows the nested body content to be mapped
|
||||
* to a function at compile time. The body function gets mapped to a property
|
||||
* of the tag renderer at render time. The body function can have any number
|
||||
* of parameters.
|
||||
*
|
||||
* Example:
|
||||
* - "body-function": "_handleBody(param1, param2, param3)"
|
||||
*/
|
||||
bodyFunction: function(value) {
|
||||
var tag = this.tag;
|
||||
var parts = bodyFunctionRegExp.exec(value);
|
||||
@ -149,44 +283,27 @@ TagHandlers.prototype = {
|
||||
|
||||
tag.setBodyFunction(functionName, params);
|
||||
},
|
||||
/**
|
||||
* The "body-property" property can be used to map the body content
|
||||
* to a String property on the renderer's input object.
|
||||
*
|
||||
* Example:
|
||||
* "body-property": "label"
|
||||
*/
|
||||
bodyProperty: function(value) {
|
||||
var tag = this.tag;
|
||||
tag.setBodyProperty(value);
|
||||
},
|
||||
vars: function(value) {
|
||||
var tag = this.tag;
|
||||
if (value) {
|
||||
value.forEach(function(v, i) {
|
||||
var nestedVariable;
|
||||
|
||||
if (typeof v === 'string') {
|
||||
nestedVariable = {
|
||||
name: v
|
||||
};
|
||||
} else {
|
||||
nestedVariable = {};
|
||||
|
||||
propertyHandlers(v, {
|
||||
|
||||
name: function(value) {
|
||||
nestedVariable.name = value;
|
||||
},
|
||||
|
||||
nameFromAttribute: function(value) {
|
||||
nestedVariable.nameFromAttribute = value;
|
||||
}
|
||||
|
||||
}, 'var at index ' + i);
|
||||
|
||||
if (!nestedVariable.name && !nestedVariable.nameFromAttribute) {
|
||||
throw new Error('The "name" or "name-from-attribute" attribute is required for a nested variable');
|
||||
}
|
||||
}
|
||||
|
||||
tag.addNestedVariable(nestedVariable);
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The "import-var" property can be used to add a property to the
|
||||
* input object of the tag renderer whose value is determined by
|
||||
* a JavaScript expression.
|
||||
*
|
||||
* Example:
|
||||
* "import-var": {
|
||||
* "myTargetProperty": "data.myCompileTimeJavaScriptExpression",
|
||||
* }
|
||||
*/
|
||||
importVar: function(value) {
|
||||
var tag = this.tag;
|
||||
forEachEntry(value, function(varName, varValue) {
|
||||
@ -211,12 +328,42 @@ TagHandlers.prototype = {
|
||||
tag.addImportedVariable(importedVar);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* The tag type.
|
||||
*/
|
||||
type: function(value) {
|
||||
var tag = this.tag;
|
||||
tag.type = value;
|
||||
},
|
||||
/**
|
||||
* Declare a nested tag.
|
||||
*
|
||||
* Example:
|
||||
* {
|
||||
* ...
|
||||
* "nested-tags": {
|
||||
* "tab": {
|
||||
* "target-property": "tabs",
|
||||
* "isRepeated": true
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
nestedTags: function(value) {
|
||||
var tagPath = this.path;
|
||||
var taglib = this.taglib;
|
||||
var dirname = this.dirname;
|
||||
var tag = this.tag;
|
||||
|
||||
forEachEntry(value, function(nestedTagName, nestedTagDef) {
|
||||
var nestedTag = loadTag(
|
||||
nestedTagDef,
|
||||
nestedTagName + ' of ' + tagPath,
|
||||
taglib,
|
||||
dirname);
|
||||
nestedTag.name = nestedTagName;
|
||||
tag.addNestedTag(nestedTag);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -239,7 +386,7 @@ function loadTag(tagProps, path, taglib, dirname) {
|
||||
};
|
||||
}
|
||||
|
||||
var tagHandlers = new TagHandlers(tag, dirname, path);
|
||||
var tagHandlers = new TagHandlers(tag, dirname, path, taglib);
|
||||
|
||||
// We add a handler for any properties that didn't match
|
||||
// one of the default property handlers. This is used to
|
||||
@ -288,10 +435,12 @@ function loadTag(tagProps, path, taglib, dirname) {
|
||||
tagProps[k] = value[k];
|
||||
delete value[k];
|
||||
} else {
|
||||
// The property is not a shorthand attribute or shorthand
|
||||
// tag so move it over to either the tag definition
|
||||
// or the attribute definition or both the tag definition
|
||||
// and attribute definition.
|
||||
var propNameDashes = removeDashes(k);
|
||||
|
||||
|
||||
|
||||
if (loader.tagLoader.isSupportedProperty(propNameDashes) &&
|
||||
loader.attributeLoader.isSupportedProperty(propNameDashes)) {
|
||||
// Move over all of the properties that are associated with a tag
|
||||
@ -365,6 +514,8 @@ function loadTag(tagProps, path, taglib, dirname) {
|
||||
taglib,
|
||||
dirname);
|
||||
|
||||
// We use the '[]' suffix to indicate that a nested tag
|
||||
// can be repeated
|
||||
var isNestedTagRepeated = false;
|
||||
if (part.endsWith('[]')) {
|
||||
isNestedTagRepeated = true;
|
||||
|
||||
@ -55,6 +55,15 @@ function handleTag(taglibHandlers, tagName, path) {
|
||||
taglib.addTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* We load a taglib definion using this class. Properties in the taglib
|
||||
* definition (which is just a JavaScript object with properties)
|
||||
* are mapped to handler methods in an instance of this type.
|
||||
*
|
||||
*
|
||||
* @param {Taglib} taglib The initially empty Taglib instance that we will populate
|
||||
* @param {String} path The file system path to the taglib that we are loading
|
||||
*/
|
||||
function TaglibHandlers(taglib, path) {
|
||||
ok(taglib);
|
||||
ok(path);
|
||||
@ -66,12 +75,40 @@ function TaglibHandlers(taglib, path) {
|
||||
|
||||
TaglibHandlers.prototype = {
|
||||
attributes: function(value) {
|
||||
// The value of the "attributes" property will be an object
|
||||
// where each property maps to an attribute definition. Since these
|
||||
// attributes are on the taglib they will be "global" attribute
|
||||
// defintions.
|
||||
//
|
||||
// The property key will be the attribute name and the property value
|
||||
// will be the attribute definition. Example:
|
||||
// {
|
||||
// "attributes": {
|
||||
// "foo": "string",
|
||||
// "bar": "expression"
|
||||
// }
|
||||
// }
|
||||
var taglib = this.taglib;
|
||||
var path = this.path;
|
||||
|
||||
handleAttributes(value, taglib, path);
|
||||
},
|
||||
tags: function(tags) {
|
||||
// The value of the "tags" property will be an object
|
||||
// where each property maps to an attribute definition. The property
|
||||
// key will be the tag name and the property value
|
||||
// will be the tag definition. Example:
|
||||
// {
|
||||
// "tags": {
|
||||
// "foo": {
|
||||
// "attributes": { ... }
|
||||
// },
|
||||
// "bar": {
|
||||
// "attributes": { ... }
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
for (var tagName in tags) {
|
||||
if (tags.hasOwnProperty(tagName)) {
|
||||
handleTag(this, tagName, tags[tagName]);
|
||||
@ -79,6 +116,12 @@ TaglibHandlers.prototype = {
|
||||
}
|
||||
},
|
||||
tagsDir: function(dir) {
|
||||
// The "tags-dir" property is used to supporting scanning
|
||||
// of a directory to discover custom tags. Scanning a directory
|
||||
// is a much simpler way for a developer to create custom tags.
|
||||
// Only one tag is allowed per directory and the directory name
|
||||
// corresponds to the tag name. We only search for directories
|
||||
// one level deep.
|
||||
var taglib = this.taglib;
|
||||
var path = this.path;
|
||||
var dirname = this.dirname;
|
||||
@ -93,6 +136,14 @@ TaglibHandlers.prototype = {
|
||||
},
|
||||
|
||||
taglibImports: function(imports) {
|
||||
// The "taglib-imports" property allows another taglib to be imported
|
||||
// into this taglib so that the tags defined in the imported taglib
|
||||
// will be part of this taglib.
|
||||
//
|
||||
// NOTE: If a taglib import refers to a package.json file then we read
|
||||
// the package.json file and automatically import *all* of the
|
||||
// taglibs from the installed modules found in the "dependencies"
|
||||
// section
|
||||
var taglib = this.taglib;
|
||||
var dirname = this.dirname;
|
||||
|
||||
@ -127,6 +178,8 @@ TaglibHandlers.prototype = {
|
||||
},
|
||||
|
||||
textTransformer: function(value) {
|
||||
// Marko allows a "text-transformer" to be registered. The provided
|
||||
// text transformer will be called for any static text found in a template.
|
||||
var taglib = this.taglib;
|
||||
var path = this.path;
|
||||
var dirname = this.dirname;
|
||||
@ -150,8 +203,20 @@ TaglibHandlers.prototype = {
|
||||
ok(transformer.path, '"path" is required for transformer');
|
||||
|
||||
taglib.addTextTransformer(transformer);
|
||||
},
|
||||
'*': function(name, value) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.loadTaglib = function(path) {
|
||||
var taglibProps = taglibReader.readTaglib(path);
|
||||
|
||||
var taglib = new Taglib(path);
|
||||
taglib.addInputFile(path);
|
||||
|
||||
var taglibHandlers = new TaglibHandlers(taglib, path);
|
||||
|
||||
// We register a wildcard handler to handle "@my-attr" and "<my-tag>"
|
||||
// properties (shorthand syntax)
|
||||
taglibHandlers['*'] = function(name, value) {
|
||||
var taglib = this.taglib;
|
||||
var path = this.path;
|
||||
|
||||
@ -169,16 +234,7 @@ TaglibHandlers.prototype = {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.loadTaglib = function(path) {
|
||||
var taglibProps = taglibReader.readTaglib(path);
|
||||
|
||||
var taglib = new Taglib(path);
|
||||
taglib.addInputFile(path);
|
||||
|
||||
var taglibHandlers = new TaglibHandlers(taglib, path);
|
||||
};
|
||||
|
||||
propertyHandlers(taglibProps, taglibHandlers, path);
|
||||
|
||||
|
||||
@ -12,10 +12,12 @@
|
||||
"target-property": "className"
|
||||
}
|
||||
},
|
||||
"@footer <footer>": {
|
||||
"@class": {
|
||||
"type": "string",
|
||||
"target-property": "className"
|
||||
"nested-tags": {
|
||||
"footer": {
|
||||
"@class": {
|
||||
"type": "string",
|
||||
"target-property": "className"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user