diff --git a/lib/exporter/dojov1.js b/lib/exporter/dojov1.js
index b9a3a06..cd5e244 100644
--- a/lib/exporter/dojov1.js
+++ b/lib/exporter/dojov1.js
@@ -1,4 +1,12 @@
-define([ '../Module', '../Value', './util', '../console', '../node!fs' ], function (Module, Value, util, console, fs) {
+define([
+ 'dojo/string',
+ '../Module',
+ '../Value',
+ './util',
+ '../console',
+ 'dojo/node!fs',
+], function (stringUtil, Module, Value, util, console, fs) {
+
/**
* Takes information from metadata stored alongside a Value and adds it to the output.
* @param node The node to add metadata to.
@@ -34,6 +42,66 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi
}
}
+ /**
+ * Given metadata with a type annotation, attempt to resolve the annotated type as an object and (hackily) apply
+ * information about the object’s default properties to the metadata description property.
+ */
+ function processTypeAnnotation(/**Object*/ metadata) {
+ if (!metadata.type || typeof metadata.type === 'string') {
+ return;
+ }
+
+ var propertyTemplate = '
${key}${type}${summary}',
+ annotationObject = metadata.type,
+ additionalDescription = '';
+
+ if (annotationObject.relatedModule) {
+ metadata.type = annotationObject.relatedModule.id;
+ return;
+ }
+
+ metadata.type = 'Object';
+ additionalDescription += '' + (metadata.description ?
+ 'The following properties are supported:' :
+ 'An object with the following properties:') + '
';
+
+ (function readProperties(object) {
+ var propertyMetadata,
+ properties = object.properties,
+ k;
+
+ // if the annotationObject is a function, we don't want to pick up any properties apart
+ // from what's on the prototype.
+ if (object.type === 'function') {
+ if (_hasOwnProperty.call(properties, 'prototype')) {
+ readProperties(properties.prototype);
+ }
+ return;
+ }
+
+ for (k in properties) {
+ if (_hasOwnProperty.call(properties, k)) {
+ // Type descriptor could be a plain JS object, or could be a constructor. It is often the
+ // latter.
+ if (k === 'prototype') {
+ readProperties(properties[k]);
+ }
+ // Filter out built-ins and constructor properties which come from dojo/_base/declare
+ else if (k !== 'constructor' && properties[k].file) {
+ propertyMetadata = properties[k].metadata;
+ additionalDescription += stringUtil.substitute(propertyTemplate, {
+ key: k,
+ type: propertyMetadata.type ? ' (' + propertyMetadata.type + (propertyMetadata.isOptional ? ', optional' : '') + ')' : '',
+ summary: propertyMetadata.summary ? ': ' + propertyMetadata.summary : ''
+ });
+ }
+ }
+ }
+ }(annotationObject));
+
+ metadata.description = (metadata.description || '') + additionalDescription + '
';
+ }
+
/**
* Takes an array of return Values and processes it for return types, discarding all
* duplicates, and applies the resulting list of properties to the node given in returnsNode.
@@ -67,6 +135,9 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi
i;
for (i = 0; (parameter = property.parameters[i]); ++i) {
+ if (typeof parameter.metadata.type !== 'string') {
+ processTypeAnnotation(parameter.metadata);
+ }
parameterType = parameter.metadata.type || parameter.type || 'unknown';
parameterNode = parametersNode.createNode('parameter', {
name: parameter.name,
@@ -105,6 +176,9 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi
propertyNode;
function makePropertyObject(name, value) {
+ if (typeof value.metadata.type !== 'string') {
+ processTypeAnnotation(value.metadata);
+ }
var object = {
name: name,
scope: scope,
diff --git a/lib/processor/dojodoc.js b/lib/processor/dojodoc.js
index 5c0ad8a..26acfe7 100644
--- a/lib/processor/dojodoc.js
+++ b/lib/processor/dojodoc.js
@@ -45,14 +45,22 @@ define([
* avoid accidentally overwriting data that already exists with empty data.
*/
function mixinMetadata(destination, source) {
- for (var k in source) {
+ var raw,
+ k;
+
+ for (k in source) {
if (_hasOwnProperty.call(source, k) && source[k]) {
if (source[k] instanceof Array && destination[k] instanceof Array) {
destination[k] = destination[k].concat(source[k]);
}
- else if (k === 'type') {
+ else if (k === 'type' && typeof source[k] === 'string') {
destination[k] = source[k].replace(optionalTypeRe, '');
}
+ else if (k === 'raw') {
+ // this keeps the mixin/override semantics of raw the same as the metadata itself
+ raw = destination[k];
+ raw ? mixinMetadata(raw, source[k]) : destination[k] = source[k];
+ }
else if (typeof source[k] !== 'string' || trim(source[k])) {
destination[k] = source[k];
}
@@ -113,60 +121,24 @@ define([
}
/**
- * Given metadata with a type annotation, attempt to resolve the annotated type as an object and (hackily) apply
- * information about the object’s default properties to the metadata description property.
- * TODO: This should really end up happening in the dojov1 exporter instead.
+ * Given metadata with a type annotation, attempt to resolve the annotated type as an object and
+ * provide that object to the exporter as the type property of the metadata.
*/
function processTypeAnnotation(/**Object*/ metadata) {
if (!metadata.type) {
return;
}
- var propertyTemplate = '\n* ${key}${type}${summary}',
- annotationObject = env.scope.getVariable(metadata.type.replace(/[^\w$\.]+$/g, '').split('.')),
- additionalDescription = '';
+ var annotationObject = env.scope.getVariable(metadata.type.replace(/[^\w$\.]+$/g, '').split('.'));
if (!annotationObject || annotationObject.type === Value.TYPE_UNDEFINED || /* not a built-in */ !annotationObject.file) {
return;
}
- if (annotationObject.relatedModule) {
- metadata.type = annotationObject.relatedModule.id;
- return;
- }
-
- // TODO: The fact that evaluate exists on annotation objects seems to indicate that we’re failing to
- // evaluate all function expressions; this might be an issue
- annotationObject.evaluate && annotationObject.evaluate();
-
- metadata.type = 'Object';
- additionalDescription += metadata.description ?
- '\n\nThe following properties are supported:\n' :
- 'An object with the following properties:\n';
-
- (function readProperties(object) {
- var propertyMetadata;
- for (var k in object.properties) {
- if (_hasOwnProperty.call(object.properties, k)) {
- // Type descriptor could be a plain JS object, or could be a constructor. It is often the
- // latter.
- if (k === 'prototype') {
- readProperties(object.properties[k]);
- }
- // Filter out built-ins and constructor properties which come from dojo/_base/declare
- else if (k !== 'constructor' && object.properties[k].file) {
- propertyMetadata = object.properties[k].metadata;
- additionalDescription += stringUtil.substitute(propertyTemplate, {
- key: k,
- type: propertyMetadata.type ? ' (' + propertyMetadata.type + (propertyMetadata.isOptional ? ', optional' : '') + ')' : '',
- summary: propertyMetadata.summary ? ': ' + propertyMetadata.summary : ''
- });
- }
- }
- }
- }(annotationObject));
-
- metadata.description = (metadata.description || '') + parseMarkdown(additionalDescription);
+ // defer resolving this to do it in the exporter. annotationObject might be the return
+ // value of the module currently being processed and in that case it won't have been tagged
+ // with a relatedModule yet.
+ metadata.type = annotationObject;
}
/**
@@ -194,6 +166,47 @@ define([
}
}
+ /**
+ * Processes metadata to parse the markdown of certain properties
+ * @param metadata Metadata object to parse for markdown
+ */
+ function processMetadata(/**Object*/ metadata) {
+ var example,
+ i,
+ k,
+ raw = metadata.raw = {
+ summary: metadata.summary,
+ description: metadata.description,
+ // handle examples in the loop below to avoid redundant iteration. also, by
+ // recursing for properties and returns, they will get their own raw objects.
+ };
+
+ // The style guide says `summary` isn’t markdown, only `description` is markdown, but everyone sticks markdown
+ // in the summary anyway, so handle it as markdown too
+ metadata.summary = parseMarkdown(metadata.summary || '');
+ metadata.description = parseMarkdown(metadata.description || '');
+
+ if (metadata.examples) {
+ raw.examples = [];
+ for (i = 0; (example = metadata.examples[i]); ++i) {
+ raw.examples[i] = example;
+ metadata.examples[i] = parseMarkdown(example);
+ }
+ }
+
+ if (metadata.properties) {
+ for (k in metadata.properties) {
+ if (_hasOwnProperty.call(metadata.properties, k)) {
+ processMetadata(metadata.properties[k]);
+ }
+ }
+ }
+
+ if (metadata.returns) {
+ processMetadata(metadata.returns);
+ }
+ }
+
/**
* Processes a dojodoc multi-line comment block, which consists of key lines that identify the metadata and
* subsequent indented lines containing the actual metadata.
@@ -306,19 +319,7 @@ define([
}
}
- // The style guide says `summary` isn’t markdown, only `description` is markdown, but everyone sticks markdown
- // in the summary anyway, so handle it as markdown too
- metadata.summary = parseMarkdown(metadata.summary);
- metadata.description = parseMarkdown(metadata.description);
- for (var i = 0, example; (example = metadata.examples[i]); ++i) {
- metadata.examples[i] = parseMarkdown(example);
- }
-
- for (var k in metadata.properties) {
- if (_hasOwnProperty.call(metadata.properties, k)) {
- metadata.properties[k].summary = parseMarkdown(metadata.properties[k].summary);
- }
- }
+ processMetadata(metadata);
return metadata;
}
@@ -369,8 +370,11 @@ define([
candidate = /^[^\n]*\/\/(.*?)\n/.exec(util.getSourceForRange(value.raw.range));
if (candidate) {
- metadata = { type: trim(candidate[1]) };
+ value.evaluated.metadata.type = trim(candidate[1]);
+ processTypeAnnotation(value.evaluated.metadata);
}
+ // if we get here, we have no need to mixin any metadata
+ return;
}
// Function or object body