diff --git a/lib/exporter/dojov1.js b/lib/exporter/dojov1.js index b9a3a06..dd047f7 100644 --- a/lib/exporter/dojov1.js +++ b/lib/exporter/dojov1.js @@ -1,4 +1,46 @@ -define([ '../Module', '../Value', './util', '../console', '../node!fs' ], function (Module, Value, util, console, fs) { +define([ + 'dojo/string', + '../Module', + '../Value', + './util', + '../console', + 'dojo/node!fs', + 'dojo/node!../marked/lib/marked.js' +], function (stringUtil, Module, Value, util, console, fs, marked) { + /** + * Parses a markdown-enabled block of text into HTML, including the custom block extension for code samples. + * @param text Markdown text. + * @returns {string} HTML. + */ + function parseMarkdown(/**string*/ text) { + var tokens = marked.lexer(text); + + for (var index = 0, token; (token = tokens[index]); ++index) { + if (token.type === 'paragraph' && /^\s*\|\s*/m.test(token.text)) { + var newTokens = [], + lastToken = {}; + + token.text.split('\n').forEach(function (line) { + var lineType = /^\s*\|/.test(line) ? 'code' : 'paragraph'; + + line = line.replace(/^\s*\|/, ''); + + if (lineType === lastToken.type) { + lastToken.text += (lastToken.text.length ? '\n' : '') + line; + } + else { + newTokens.push((lastToken = { type: lineType, text: line })); + } + }); + + tokens.splice.apply(tokens, [ index, 1 ].concat(newTokens)); + index += newTokens.length - 1; + } + } + + return marked.parser(tokens); + } + /** * Takes information from metadata stored alongside a Value and adds it to the output. * @param node The node to add metadata to. @@ -11,7 +53,7 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi for (var metadataName in { summary: 1, description: 1 }) { if (metadata.hasOwnProperty(metadataName) && metadata[metadataName]) { - node.createNode(metadataName).childNodes.push(metadata[metadataName]); + node.createNode(metadataName).childNodes.push(parseMarkdown(metadata[metadataName])); } } @@ -29,11 +71,54 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi var examplesNode = node.createNode('examples'); for (var i = 0, j = metadata.examples.length; i < j; ++i) { - examplesNode.createNode('example').childNodes.push(metadata.examples[i]); + examplesNode.createNode('example').childNodes.push(parseMarkdown(metadata.examples[i])); } } } + /** + * 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 = '\n* ${key}${type}${summary}', + annotationObject = metadata.type, + additionalDescription = ''; + + 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 ? ': ' + parseMarkdown(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 +152,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, @@ -76,7 +164,7 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi for (var metadataName in { summary: 1, description: 1 }) { if (parameter.metadata.hasOwnProperty(metadataName) && parameter.metadata[metadataName]) { - parameterNode.createNode(metadataName).childNodes.push(parameter.metadata[metadataName]); + parameterNode.createNode(metadataName).childNodes.push(parseMarkdown(parameter.metadata[metadataName])); } } } @@ -88,7 +176,7 @@ define([ '../Module', '../Value', './util', '../console', '../node!fs' ], functi for (i = 0; (returnValue = property.returns[i]); ++i) { if (returnValue.metadata.summary) { - propertyNode.createNode('return-description').childNodes.push(returnValue.metadata.summary); + propertyNode.createNode('return-description').childNodes.push(parseMarkdown(returnValue.metadata.summary)); break; } } @@ -105,6 +193,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..9834891 100644 --- a/lib/processor/dojodoc.js +++ b/lib/processor/dojodoc.js @@ -1,44 +1,8 @@ define([ - 'dojo/_base/lang', - 'dojo/string', - '../Value', '../env', - './util', - 'dojo/node!../marked/lib/marked.js' -], function (lang, stringUtil, Value, env, util, marked) { - /** - * Parses a markdown-enabled block of text into HTML, including the custom block extension for code samples. - * @param text Markdown text. - * @returns {string} HTML. - */ - function parseMarkdown(/**string*/ text) { - var tokens = marked.lexer(text); - - for (var index = 0, token; (token = tokens[index]); ++index) { - if (token.type === 'paragraph' && /^\s*\|\s*/m.test(token.text)) { - var newTokens = [], - lastToken = {}; - - token.text.split('\n').forEach(function (line) { - var lineType = /^\s*\|/.test(line) ? 'code' : 'paragraph'; - - line = line.replace(/^\s*\|/, ''); - - if (lineType === lastToken.type) { - lastToken.text += (lastToken.text.length ? '\n' : '') + line; - } - else { - newTokens.push((lastToken = { type: lineType, text: line })); - } - }); - - tokens.splice.apply(tokens, [ index, 1 ].concat(newTokens)); - index += newTokens.length - 1; - } - } - - return marked.parser(tokens); - } + '../Value', + './util' +], function (env, Value, util) { /** * Mixes in metadata safely, ignoring empty keys and concatenating instead of overriding arrays in order to @@ -50,7 +14,7 @@ define([ 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 (typeof source[k] !== 'string' || trim(source[k])) { @@ -113,18 +77,15 @@ 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; @@ -139,34 +100,8 @@ define([ // 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); + // attach annotation to metadata as the type so the exporter can process it + metadata.type = annotationObject; } /** @@ -306,20 +241,6 @@ 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); - } - } - return metadata; }